Skip to content

Design studies

Describe, sample, run, and measure design variations; see the guide.

Parameters

Span dataclass

Span(
    name: str,
    low: float,
    high: float,
    nominal: Optional[float] = None,
    sigma: Optional[float] = None,
    unit: Optional[str] = None,
    integer: bool = False,
)

A numeric parameter on the closed interval [low, high].

nominal is the design value (default: the midpoint, rounded down for an integer span); the corner sampler uses it and the random samplers centre a sigma draw on it. sigma (float spans only) makes monte_carlo and latin_hypercube draw from a normal distribution around nominal truncated to [low, high]; without it they draw uniformly. unit is display metadata; when omitted it is inferred from the name's suffix (width_um gives "um"). integer=True keeps every value an int.

name instance-attribute

name: str

low instance-attribute

low: float

high instance-attribute

high: float

nominal class-attribute instance-attribute

nominal: Optional[float] = None

sigma class-attribute instance-attribute

sigma: Optional[float] = None

unit class-attribute instance-attribute

unit: Optional[str] = None

integer class-attribute instance-attribute

integer: bool = False

kind property

kind: str

"span".

normalize

normalize(value: object) -> Union[float, int]

value as this parameter stores it (float, or int for an integer span), refused when it is outside [low, high].

from_unit

from_unit(u: float) -> Union[float, int]

Map u in [0, 1) to a value (the inverse CDF of this parameter's sampling distribution): uniform on [low, high], a normal around nominal truncated to [low, high] when sigma is set, or uniform over the integers of an integer span.

to_dict

to_dict() -> dict

JSON-safe description; from_dict reverses it.

from_dict classmethod

from_dict(data: dict) -> 'Span'

The Span a to_dict description records.

Choices dataclass

Choices(
    name: str,
    values: Tuple[Value, ...],
    nominal: Optional[Value] = None,
    unit: Optional[str] = None,
)

A parameter that takes one of a finite list of values.

The values share one type family: all real numbers, all str or all bool. nominal (default: the first value) is the design value the corner sampler starts from. The random samplers pick every value with equal probability. unit is display metadata, inferred from the name's suffix when omitted.

name instance-attribute

name: str

values instance-attribute

values: Tuple[Value, ...]

nominal class-attribute instance-attribute

nominal: Optional[Value] = None

unit class-attribute instance-attribute

unit: Optional[str] = None

kind property

kind: str

"choices".

family property

family: str

"number", "str" or "bool".

normalize

normalize(value: object) -> Value

The stored value equal to value; refused when value is not one of values. A number matches a number of equal value (1 matches 1.0); a bool never matches a number.

from_unit

from_unit(u: float) -> Value

Map u in [0, 1) to one of values, each with equal probability.

to_dict

to_dict() -> dict

JSON-safe description; from_dict reverses it.

from_dict classmethod

from_dict(data: dict) -> 'Choices'

The Choices a to_dict description records.

Samples

Samples dataclass

Samples(
    parameters: Tuple[Parameter, ...],
    rows: Tuple[Tuple[Value, ...], ...],
    method: str,
    seed: Optional[int] = None,
    options: Mapping[str, Any] = dict(),
)

A frozen set of sample points over parameters.

rows holds one tuple of values per sample, in parameter order, each value normalized by its parameter. Iterating yields one {name: value} dict per row, len() is the row count and samples[i] is row i as a dict. method, seed and options record how the rows were made. Rows may repeat. a + b concatenates two sample sets over the same parameters.

parameters instance-attribute

parameters: Tuple[Parameter, ...]

rows instance-attribute

rows: Tuple[Tuple[Value, ...], ...]

method instance-attribute

method: str

seed class-attribute instance-attribute

seed: Optional[int] = None

options class-attribute instance-attribute

options: Mapping[str, Any] = field(default_factory=dict)

names property

names: Tuple[str, ...]

The parameter names, in order.

sample_names property

sample_names: Tuple[str, ...]

One deterministic name per row, derived from its values (equal rows share a name).

to_dict

to_dict(*, rows: bool = True) -> dict

JSON-safe description; from_dict reverses it. rows=False leaves the rows out (a summary of how they were made).

from_dict classmethod

from_dict(data: Mapping[str, Any]) -> 'Samples'

The Samples a to_dict description (with rows) records; every row is checked against its parameters again.

grid

grid(
    parameters: Sequence[Parameter],
    *,
    num_points: Union[int, Mapping[str, int]] = 5,
) -> Samples

The full factorial grid. Each Span takes num_points evenly spaced values from low to high inclusive (an int, or a {name: int} mapping naming every span; at least 2 each; an integer span keeps the distinct rounded values); each Choices takes all its values. Rows are in row-major order: the last parameter varies fastest.

points

points(
    parameters: Sequence[Parameter],
    rows: Sequence[Mapping[str, Value]],
) -> Samples

An explicit list of sample points: rows is a sequence of {name: value} mappings, each naming every parameter exactly once. Values are checked against their parameter (inside a span, one of the choices).

corners

corners(
    parameters: Sequence[Parameter], *, style: str = "all"
) -> Samples

Process corners. The first row is every parameter at its nominal. style="all" then adds every combination of each parameter's extremes (low and high of a span, every non-nominal value of a choice), in row-major order; style="one_at_a_time" adds each parameter at each extreme in turn with the others at nominal (span: low then high; choice: each non-nominal value in order). A choice with a single value stays at it in every row. Rows equal to the nominal row are not repeated. options records {"style": style}.

monte_carlo

monte_carlo(
    parameters: Sequence[Parameter],
    *,
    num_samples: int,
    seed: int,
) -> Samples

num_samples independent random draws, reproducible from seed. Unit-cube points come from numpy.random.Generator(PCG64(seed)) .random((num_samples, num_parameters)); column j is mapped through parameter j's from_unit.

latin_hypercube

latin_hypercube(
    parameters: Sequence[Parameter],
    *,
    num_samples: int,
    seed: int,
) -> Samples

A Latin hypercube of num_samples points, reproducible from seed: for every parameter, each of the num_samples equal- probability strata of [0, 1) holds exactly one point before the parameter's from_unit maps it. For each parameter in order the generator draws a permutation of the strata, then the offsets inside them; that order is part of the reproducibility contract.

Running

plan

plan(
    samples,
    build,
    *,
    path=None,
    retry_failed=False,
    cloud=False,
    device=None,
) -> StudyPlan

Preview the jobs a study would run without writing or executing.

samples supplies parameter rows and build(params) returns a Simulation for each row. path optionally reads an existing study directory for resume decisions. retry_failed includes failed jobs in to_run. Build and runnability failures count as failed rows; if no row is runnable, the first error is raised. The estimate is local, not a cloud quote. cloud and device apply the same per-row checks as run. This toolkit makes no physics claim about the resulting samples.

run

run(
    samples,
    build,
    *,
    path,
    measure=None,
    cloud=False,
    retry_failed=False,
    max_workers=1,
    progress=None,
    quiet=False,
    solver_path=None,
    device=None,
    timeout=None,
    solver=None,
    max_usd=DEFAULT_MAX_USD,
    max_usd_total=5.0,
    wait_timeout_s=None,
)

Run or resume a study and return one results-table row per sample.

samples contains parameter rows; build(params) makes each row's Simulation and optional measure(result) returns named finite scalars. path stores samples, jobs, measurements and the latest table. Identical solver inputs run once. Succeeded jobs and measurements are reused; failed jobs and measurements are retried only with retry_failed=True. Submitted or timed-out cloud jobs are collected on a call with cloud=True. A local call leaves their submitted or timed-out status unchanged. Without measure, succeeded rows have no stored measurement. A later call with measure reads stored output without running the solver again.

cloud chooses cloud execution; otherwise solver_path selects the local solver. solver selects the cloud build and is recorded with each job and submission; resuming with a different cloud build is refused. On a GPU, solver=None takes the build the directory recorded, so collecting and adding jobs needs no local solver; a directory with none recorded submits its first jobs with the commit of the locally installed solver, as every cloud call does. device=None is the service's only GPU. max_workers, device, timeout (local) and wait_timeout_s (cloud) control the runners. progress receives study event dictionaries; quiet suppresses the one-line stderr job reports. The cloud path quotes every new job before the first submission. Its max_usd per-job ceiling defaults to $5, as for every cloud entry point; max_usd_total defaults to $5 and counts all submitted jobs recorded in this directory, including quotes from failed or locally retried jobs. Submission history is append-only, including ambiguous submit outcomes whose job id is unknown. Those failed jobs require retry_failed=True to submit again; inspect ph.cloud.list_jobs() before retrying. A definite service refusal leaves rows pending with its error. If all new submissions are refused, the table is saved and a CloudError is raised. Both ceilings must be finite and non-negative. If either quote ceiling is exceeded during initial quoting, the new batch is refused before submission. Stale quotes are refreshed before each submission and checked against the remaining budget; a refresh refusal leaves that entry pending. A submitted job is collected without another submission. Cloud completion hooks record status before measurements run. An exclusive directory lock prevents concurrent runs on the same path. This toolkit makes no physics claim about the returned measurements.

StudyPlan dataclass

StudyPlan(
    num_samples: int,
    num_jobs: int,
    num_build_failed: int,
    reused: tuple[str, ...],
    to_run: tuple[str, ...],
    to_collect: tuple[str, ...],
    skipped_failed: tuple[str, ...],
    estimate_usd: float,
)

A read-only preview of a study run.

num_samples counts rows, num_jobs unique solver inputs and num_build_failed rows whose builder failed or whose simulation was not runnable. reused lists succeeded jobs in the directory, to_run jobs that would run or submit, to_collect already submitted cloud jobs awaiting collection and skipped_failed failed jobs held unless retried. estimate_usd sums local cost estimates for to_run and is not a service quote.

num_samples instance-attribute

num_samples: int

num_jobs instance-attribute

num_jobs: int

num_build_failed instance-attribute

num_build_failed: int

reused instance-attribute

reused: tuple[str, ...]

to_run instance-attribute

to_run: tuple[str, ...]

to_collect instance-attribute

to_collect: tuple[str, ...]

skipped_failed instance-attribute

skipped_failed: tuple[str, ...]

estimate_usd instance-attribute

estimate_usd: float

Results

save

save(table: Dataset, file: Union[str, Path]) -> Path

Write table (a build_table dataset) to file as deterministic JSON and return the path. NaN is written as null; dtypes and attrs are recorded so load gives an identical dataset. The write is atomic (a sibling temporary file, then os.replace).

load

load(file: Union[str, Path]) -> Dataset

Read a table written by save. A directory reads its table.json (a study directory).

yield_fraction

yield_fraction(
    table: Dataset,
    criteria: Mapping[
        str, Tuple[Optional[float], Optional[float]]
    ],
    *,
    incomplete: str = "exclude",
) -> YieldEstimate

Count the rows of table whose measured values meet criteria, {name: (low, high)} with inclusive bounds and None for an open side. Rows whose status is not "succeeded" (or that lack a value a criterion names) are incomplete: incomplete="exclude" leaves them out of the denominator, "fail" counts them as failing.

The fraction is a count over the samples given; it is only as good as the sampling and the simulations behind it.

YieldEstimate dataclass

YieldEstimate(
    fraction: float,
    num_pass: int,
    num_evaluated: int,
    num_samples: int,
    num_incomplete: int,
    incomplete: str,
    criteria: Mapping[
        str, Tuple[Optional[float], Optional[float]]
    ],
    interval_95: Tuple[float, float],
)

The share of samples meeting pass criteria, with the counts behind it.

fraction is num_pass / num_evaluated (NaN when nothing was evaluated); num_samples is every row, num_incomplete the rows that did not succeed, and incomplete says whether those were excluded or counted as failing. interval_95 is the Wilson score interval at 95% confidence.

fraction instance-attribute

fraction: float

num_pass instance-attribute

num_pass: int

num_evaluated instance-attribute

num_evaluated: int

num_samples instance-attribute

num_samples: int

num_incomplete instance-attribute

num_incomplete: int

incomplete instance-attribute

incomplete: str

criteria instance-attribute

criteria: Mapping[
    str, Tuple[Optional[float], Optional[float]]
]

interval_95 instance-attribute

interval_95: Tuple[float, float]