Run lazyscribe concurrently#

Logging experiments#

Multithreading#

Note

While experimental, we have tested the following functionality against free-threaded python (3.13t, 3.14t).

Lazyscribe provides basic support for concurrent experiment logging. You can reference a lazyscribe.project.Project object across threads and log experiments safely:

import threading

from lazyscribe import Project

project = Project("project.json", mode="w")

def _closure(project, name: str, metric: float):
    with project.log(name) as exp:
        exp.log_metric("metric", metric)

threads = [
    threading.Thread(target=_closure, args=(project, *params))
    for params in [
        ("First experiment", 0.5),
        ("Second experiment", 0.75),
        ("Third experiment", 1.0)
    ]
]

for t in threads:
    t.start()
for t in threads:
    t.join()

project["second-experiment"]  # View the second experiment

Multiprocessing#

Using multiprocessing, we can modify lazyscribe.project.Project. Instead of using shared state, we create new “projects” that we can merge back to the original.

from concurrent.futures import ProcessPoolExecutor

from lazyscribe import Project

project = Project("project.json", mode="w")

def _closure(project, name: str, metric: float):
    with project.log(name) as exp:
        exp.log_metric("metric", metric)

    return project

with ProcessPoolExecutor(max_workers=2) as ppe:
    futures = [
        ppe.submit(_closure, project, *params)
        for params in [
            ("First experiment", 0.5),
            ("Second experiment", 0.75),
            ("Third experiment", 1.0)
        ]
    ]
    outputs_ = [f.result() for f in futures]

project = project.merge(*outputs_)

Logging tests#

Note

While experimental, we have tested the following functionality against free-threaded python (3.13t, 3.14t).

Similar to the functionality described above, lazyscribe supports logging tests in parallel threads:

import threading

from lazyscribe import Project

project = Project("project.json", mode="w")

def _closure(experiment, name: str, metric: float):
    with experiment.log_test(name) as test:
        test.log_metric("metric", metric)

with project.log(name="My experiment") as exp:
    threads = [
        threading.Thread(target=_closure, args=(exp, *params))
        for params in [
            ("Subpopulation 1", 0.5),
            ("Subpopulation 2", 0.75),
            ("Subpopulation 3", 1.0)
        ]
    ]

    for t in threads:
        t.start()
    for t in threads:
        t.join()

project["my-experiment"]  # Look at the experiment and associated tests

It’s important to note that no other experiment or test-level logging functionality is thread-safe.