Source code for statrl.experiments.parallelruns
import time
import copy
from joblib import Parallel, delayed
from typing import Any, Callable
## Parallelization
[docs]
def multicoreRuns(env: Any, learner: Any, interact: Any, nbReplicates: int, timeHorizon: int, oneRunFunction: Callable[..., Any], root_folder: str) -> tuple[Any, float]:
"""Run one agent for many independent replicates, spread across CPU cores.
Each replicate gets its own deep copy of the environment, agent, and
interaction.
Parameters
----------
env : object
Environment to replicate.
learner : object
Agent to replicate.
interact : statrl.experiments.onerun.Interaction
Interaction loop of the setting.
nbReplicates : int
Number of independent runs.
timeHorizon : int
Number of rounds per run.
oneRunFunction : callable
Function executing one replicate, called as
``oneRunFunction(env, learner, interact, timeHorizon, root_folder)``.
In practice :func:`~statrl.experiments.onerun.oneRunWithDump`.
root_folder : str
Directory the per-replicate dumps are written to.
Returns
-------
scores : list of str
One dump filename per replicate, in the order the jobs were created.
elapsed : float
Mean wall-clock seconds per replicate. Since the runs are concurrent
this is total elapsed time divided by ``nbReplicates``, so it measures
throughput rather than the cost of a single run.
Notes
-----
Uses all available cores (``n_jobs=-1``). Everything passed in must be
picklable, which is why
:class:`~statrl.settings.bandits.stochastic.batch.environment.BatchMAB` accepts a
plain list of batch sizes rather than only a callable.
"""
#FIXME Should be made more general? indep of gymnasium?
#envs.append(gymnasium.make(envRegisterName).unwrapped)
jobs = [
(copy.deepcopy(env), copy.deepcopy(learner), copy.deepcopy(interact), timeHorizon, root_folder)
for _ in range(nbReplicates)
]
t0 = time.time()
scores = Parallel(n_jobs=-1)(delayed(oneRunFunction)(*job) for job in jobs)
elapsed = time.time() - t0
return scores, elapsed / nbReplicates