research_helpers.sweep
This module provides tools for planning and running parameter sweeps as scheduler job arrays
and collecting the results. Planning, running, and status checks need only the standard library.
collect and aggregate_runs require the sweep extra. See parameter sweeps and cluster jobs.
- research_helpers.sweep.aggregate_runs(run_dirs, *, group_by=None, metrics=None, confidence=0.95)[source]
Combine the results of several runs of one grid, with a confidence interval per metric.
- Parameters:
run_dirs (
Iterable[Path|str]) – the run directories, one per independent draw.group_by (
Sequence[str] |None, default:None) – the columns identifying a parameter setting across runs. Defaults to the swept parameter names on the first run’s manifest.metrics (
Sequence[str] |None, default:None) – the measurements to aggregate. Defaults to every numeric column that is not a grouping column or engine bookkeeping.confidence (
float, default:0.95) – the two-sided confidence level.
- Return type:
DataFrame- Returns:
One row per parameter setting, carrying ‘n_runs’ and, for each metric, its mean, the half-width of the interval, and the standard error.
- Raises:
ValueError – if no run directories were given, if they share no parameter setting, or if ‘group_by’ contains ‘combination_id’.
- research_helpers.sweep.array_spec(indices)[source]
Render task indices the way ‘–array’ takes them, collapsing runs into ranges.
- research_helpers.sweep.collect_results(run_dir, *, write=True, leading=None)[source]
Merge the part files into a table de-duplicating by combination id.
Duplicates are legitimate: a re-queued task can recompute a combination whose result was already flushed. The last record written wins.
- Parameters:
write (
bool, default:True) – also write ‘results.csv’, and ‘results.parquet’ where an engine is installed.leading (
Iterable[str] |None, default:None) – columns to move to the front, so the table reads configuration first and measurements second. Defaults to the run’s own parameters, taken from the manifest. Pass an explicit sequence to override it, or an empty one to leave the order alone.
- Return type:
DataFrame- Returns:
One row per combination.
- research_helpers.sweep.leading_columns(run_dir)[source]
Return the configuration columns of a run, in the order they should be read.
The id first, then the constants, then the swept parameters. Everything else is a measurement.
- research_helpers.sweep.missing_task_indices(run_dir)[source]
Return the 1-based array indices whose slices still hold unfinished combinations.
Slices come from the manifest, not from the width of the array that was submitted, so an array narrower than the manifest plans leaves the tail simply unrun—nothing fails anywhere, the sweep stops short.
- research_helpers.sweep.run_status(run_dir)[source]
Report how much of a planned sweep has finished.
- class research_helpers.sweep.Manifest(grid, combinations, n_tasks=1, metadata=<factory>, notes='', constants=<factory>, tuple_params=<factory>, artefact_key=None)[source]
Bases:
objectThe full description of a planned sweep, written once and read by every task.
- Parameters:
- research_helpers.sweep.combination_id(params)[source]
Return a short, stable id for a parameter combination.
Derived from the values alone, so the same combination keeps the same id however the grid was expanded and on whichever machine.
- research_helpers.sweep.expand_grid(grid, constants=None, *, tuple_params=())[source]
Expand a parameter grid into the full Cartesian product of combinations.
- Parameters:
grid (
dict[str,list[Any]]) – parameter name to the list of values to sweep.constants (
dict[str,Any] |None, default:None) – fixed values added to every combination before it is hashed, e.g. the label of the dataset being swept. Part of the identity, so two datasets do not collide.tuple_params (
Iterable[str], default:()) – parameter names whose values are tuples rather than lists.
- Return type:
- Returns:
One dict per combination, each carrying the constants and a ‘combination_id’. Ordering is deterministic: parameter names sorted, values in the order given.
- Raises:
ValueError – if the grid is empty.
- research_helpers.sweep.restore_tuples(params, tuple_params)[source]
Convert the named list-valued parameters back into tuples, in place.
JSON has no tuple, so a parameter whose value must be a tuple arrives back from the manifest as a list. Identity is unaffected, so this is safe to apply at any point: ‘json.dumps’ renders a tuple and a list to the same array, so a combination hashes the same either way.
- research_helpers.sweep.slice_bounds(n_items, n_tasks, task_index)[source]
Return the [start, end) slice of items belonging to a 1-based array task.
- Parameters:
- Return type:
- Returns:
Start and end indices into the combination list.
- Raises:
ValueError – if ‘task_index’ is outside 1..n_tasks.
- research_helpers.sweep.build_units(run_dirs, *, pending=False)[source]
Return every slice of every sweep, in run directory then task order.
- research_helpers.sweep.chunk(units, per_task, max_array)[source]
Split units into groups small enough for one array of at most ‘max_array’ elements each.
- research_helpers.sweep.expand_run_dirs(patterns)[source]
Expand run directory arguments.
- Parameters:
patterns (
Iterable[str|Path]) – run directories, or patterns matching them.- Return type:
- Returns:
The planned run directories (those holding a manifest), sorted and without duplicates.
- Raises:
FileNotFoundError – if a plain path is not a planned run directory, or a pattern matches none.
- research_helpers.sweep.quarantine(units, *, apply=False, artefacts_dir=ARTEFACTS_DIR)[source]
Set aside every result of these units, so that a pending queue runs them again.
This is for units that two processes ran at once. When a scheduler re-queues an element whose node has stopped answering, the original process can keep running, and the same block then runs twice, from two nodes, appending to the same part files. On network storage that leaves runs of NUL bytes and lines spliced together.
- Parameters:
- Return type:
- Returns:
Counts of ‘units’, ‘part files’, ‘damaged part files’ and ‘artefacts’ found, and of files ‘set aside’.
- research_helpers.sweep.read_plan_list(path)[source]
Read a plan list: one ‘config<TAB>run_dir’ per line, blank lines and ‘#’ comments skipped.
Paths are taken as written, so relative ones resolve against the working directory.
- research_helpers.sweep.read_units(path, element, per_task)[source]
Return the units that an array element runs.
- Parameters:
- Return type:
- Returns:
Lines ‘(element - 1) * per_task + 1’ to ‘element * per_task’ of the file, as units. Empty past the end of the file.
- Raises:
ValueError – if ‘element’ or ‘per_task’ is below 1, or a line is malformed.
- research_helpers.sweep.select_run_dirs(run_dirs, *, where=None, where_not=None)[source]
Keep the sweeps whose manifests match, to queue a campaign in parts.
- Parameters:
where (
Mapping[str,Collection[str]] |None, default:None) – key to accepted values. A sweep is kept only if, for every key, its value is one of these. Keys are looked up in the manifest’s metadata, then its constants, and values are compared as text.where_not (
Mapping[str,Collection[str]] |None, default:None) – key to rejected values. A sweep is dropped if any of its values is one of these.
- Return type:
- Returns:
The run directories kept, in the order given.
- research_helpers.sweep.shuffled(units, seed)[source]
Return the units in a seeded random order, so that each element draws a mix of sweeps.
- research_helpers.sweep.write_queues(units, out, per_task, max_array=None, *, force=False)[source]
Write the queue files for a list of units, named ‘<out>.01.units’, ‘<out>.02.units’, …
An existing queue of the same stem is refused unless ‘force’ is set. A resumed queue takes a new stem. With ‘force’, every file of the old queue is removed first, so that a shorter queue never leaves an old file behind.
- Parameters:
units (
list[Unit]) – the units, in the order the elements should run them.per_task (
int) – units each element runs. Part of the queue: submit it with the same number.max_array (
int|None, default:None) – elements per array, and so per file. Defaults to the project’s ‘max-array’.force (
bool, default:False) – replace an existing queue of this stem.
- Return type:
- Returns:
For each file, its path and the array width to submit it with.
- Raises:
FileExistsError – if a queue of this stem exists and ‘force’ is not set.
- research_helpers.sweep.artefact_path(run_dir, combination_id)[source]
Return the location where a combination’s artefact is written.
- research_helpers.sweep.completed_ids(run_dir)[source]
Return the combination ids already recorded in this run’s parts.
A task killed mid-write leaves a partial final line, which is skipped rather than treated as corruption.
- research_helpers.sweep.jsonable(value)[source]
Convert result values into JSON-serialisable equivalents.
- research_helpers.sweep.read_artefact(run_dir, combination_id)[source]
Read back what a combination put aside.
- Parameters:
- Return type:
- Returns:
Whatever the evaluation returned under the manifest’s ‘artefact_key’.
- Raises:
FileNotFoundError – if this combination recorded no artefact.
- research_helpers.sweep.run_slice(run_dir, evaluate, *, task_index, n_tasks=None, context=None, resume=True, quiet=False, artefact_key=None)[source]
Run this task’s slice of the sweep, appending each result to its own part file.
- Parameters:
run_dir (
Path|str) – the run directory, holding the manifest.evaluate (
Callable[[dict[str,Any],Any],dict[str,Any]]) – called as ‘evaluate(params, context)’ for each combination, returning the measurements to record. The parameters are recorded alongside them automatically.task_index (
int) – 1-based array task index.n_tasks (
int|None, default:None) – total array tasks. Defaults to the manifest’s value.context (
Any, default:None) – whatever the evaluation needs, built once for the task.resume (
bool, default:True) – skip combinations already recorded in this run.quiet (
bool, default:False) – suppress per-combination progress.artefact_key (
str|None, default:None) – the key under which the evaluation returns output too bulky for a results row.
- Return type:
- Returns:
The part file written.
- research_helpers.sweep.task_index_from_env(explicit=None)[source]
Return the array task index, from the argument, the scheduler, or 1 for a local run.
- class research_helpers.sweep.Interval(mean, half_width, standard_error)[source]
Bases:
NamedTupleA mean and the interval around it.
- research_helpers.sweep.confidence_interval(values, confidence=0.95)[source]
Return the mean of ‘values’ and the t-interval around it.
- Parameters:
- Return type:
- Returns:
The mean, the half-width, and the standard error. All three are NaN for no observations, and the last two are NaN for exactly one.
- Raises:
ValueError – if ‘confidence’ is not in (0, 1).
- research_helpers.sweep.student_t_quantile(p, df)[source]
Return t such that P(T <= t) = ‘p’, for Student’s t with ‘df’ degrees of freedom.
- Parameters:
- Return type:
- Returns:
The quantile.
- Raises:
ValueError – if ‘p’ is not in (0, 1), or ‘df’ is not positive.
- class research_helpers.sweep.Sweep(evaluate=None, context=None)[source]
Bases:
objectA parameter sweep: an evaluation, an optional per-task context, and a command line.
- Parameters:
- evaluate(function)[source]
Register the evaluation, called as ‘function(params, context)’ for each combination.
- context(function)[source]
Register what to build once per task, called as ‘function(manifest, run_dir)’.
- property evaluation: Callable[[dict[str, Any], Any], dict[str, Any]]
Return the registered evaluation.
- Raises:
RuntimeError – if none was registered.
- build_context(manifest, run_dir)[source]
Return the per-task context, or None if the sweep registered no context factory.
- plan(run_dir, grid, *, constants=None, metadata=None, notes='', n_tasks=None, max_tasks=None, tuple_params=(), artefact_key=None)[source]
Expand a grid and write the manifest that every task will read.
- Parameters:
run_dir (
Path|str) – directory to hold the manifest, parts and results.grid (
dict[str,list[Any]]) – parameter name to the values to sweep.constants (
dict[str,Any] |None, default:None) – fixed values on every combination, included in its identity.metadata (
dict[str,Any] |None, default:None) – anything a task needs, e.g. a dataset name or a seed count.notes (
str, default:'') – free text recorded with the sweep.n_tasks (
int|None, default:None) – array width. Defaults to the smaller of ‘max_tasks’ and the grid size.max_tasks (
int|None, default:None) – the cap applied when ‘n_tasks’ is not given. Defaults to the project’s ‘max-array’.tuple_params (
Sequence[str], default:()) – parameters whose values are tuples rather than lists.artefact_key (
str|None, default:None) – the key under which the evaluation returns output too bulky for a results row.
- Return type:
- Returns:
The manifest, already written.
- plan_config(config, run_dir, n_tasks=None)[source]
Plan a sweep from a config file, as the ‘plan’ command does.
- research_helpers.sweep.estimate_runtime(manifest, evaluate, context=None, *, samples=DEFAULT_SAMPLES, seed=0)[source]
Time a random sample of combinations.
The sample is random, not strided. ‘research_helpers.sweep.grid.expand_grid’ orders combinations by the Cartesian product over sorted parameter names, so a stride lands on a systematically correlated subset.
- Parameters:
manifest (
Manifest) – the planned sweep.evaluate (
Callable[[dict[str,Any],Any],dict[str,Any]]) – the evaluation, called as for a real task.context (
Any, default:None) – whatever the evaluation needs.samples (
int, default:DEFAULT_SAMPLES) – how many combinations to time.seed (
int, default:0) – seed for choosing the sample.
- Return type:
- Returns:
The timings, sorted ascending.
- research_helpers.sweep.read_config(path)[source]
Read a sweep config from TOML, JSON, or YAML.
TOML and JSON are standard library, YAML needs ‘pyyaml’.
- Parameters:
- Return type:
- Returns:
The parsed config – ‘grid’, and optionally ‘constants’, ‘metadata’, ‘notes’, ‘max_tasks’, ‘tuple_params’, and ‘artefact_key’.
- Raises:
ImportError – for a YAML config when ‘pyyaml’ is not installed.
research_helpers.sweep.stats
- class research_helpers.sweep.stats.Interval(mean, half_width, standard_error)[source]
Bases:
NamedTupleA mean and the interval around it.
- research_helpers.sweep.stats.student_t_quantile(p, df)[source]
Return t such that P(T <= t) = ‘p’, for Student’s t with ‘df’ degrees of freedom.
- Parameters:
- Return type:
- Returns:
The quantile.
- Raises:
ValueError – if ‘p’ is not in (0, 1), or ‘df’ is not positive.
- research_helpers.sweep.stats.confidence_interval(values, confidence=0.95)[source]
Return the mean of ‘values’ and the t-interval around it.
- Parameters:
- Return type:
- Returns:
The mean, the half-width, and the standard error. All three are NaN for no observations, and the last two are NaN for exactly one.
- Raises:
ValueError – if ‘confidence’ is not in (0, 1).