Experiments

spicefault.experiments.experiment

The experiment: a circuit, its normal variation, faults and operating conditions, simulated as one deterministic set of samples.

Sample dataclass

Definition of one simulation. fault_index 0 is the fault-free circuit.

Realisation dataclass

A sample turned into a netlist, with the values drawn for it.

SampleResult dataclass

A simulated sample. Its draws come from sample_stream(seed, *seed_key).

ExperimentResult dataclass

to_frame()

One row per sample: its definition, its status and the realised parameters.

Experiment dataclass

What is simulated, under which conditions, with which faults and which uncertainty.

Each fault is simulated samples times and the fault-free circuit healthy_samples times (samples by default), under every operating condition. The values of a sample are drawn from a stream that depends only on the seed and on the key of the sample, which seeding defines (see seeding.seed_key): by default (fault index, replica). The result therefore does not depend on the number of workers. The operating condition is never part of the key, so one drawn circuit is observed under all the conditions.

sampling chooses how the circuits of a population are drawn: random, each from its own independent stream; lhs, a Latin hypercube; or sobol, a scrambled Sobol sequence (see experiments.sampling). The last two decide the points of each population jointly, through the quantile function of every variation, and their samples are not independent.

With a design, the circuits are not drawn: each row of the design is a circuit with the parameter values it gives, simulated without and with each fault, under every condition. There are then no variations and no sample counts to give.

waveform_points property

Points of the stored waveforms; None if no condition stores one.

measurement_columns property

The measurement columns of the table: those of every condition, once each.

waveform_for(condition_index)

The waveform stored for a condition: its own, or that of the experiment.

columns_for(condition_index)

The measurement columns that a condition fills.

plan()

Every sample, in a fixed order: by fault (healthy first), replica and condition.

realise(sample)

Draw the circuit, inject the fault, then set the operating condition.

parameter_targets()

The parameters whose value every sample records: those drawn or designed.

faults_overlapping_tolerance()

How many parametric fault conditions lie partly inside the tolerance band.

metadata()

Everything needed to regenerate the samples, as JSON-serialisable data.

from_metadata(metadata, netlist, **overrides) classmethod

Rebuild an experiment from its record and the netlist of its circuit.

What a record cannot carry must be passed again as keyword arguments: custom or joint variations (variations), custom measurements (measurements) and a backend other than the built-in ones (simulator). The netlist must be the one the record was made with.

check_picklable()

Worker processes receive a copy of the experiment, so it must be picklable.

measure(result, condition_index=None)

The measurements of a successful simulation, by name.

nominal(fault=None)

The nominal circuit, simulated and measured under each operating condition.

No variation is drawn: the circuit has the values of its netlist. It is the reference of an experiment, such as the nominal gain against which a gain error is defined. With fault, a fault or the identifier of one of the experiment, it is the nominal circuit with that fault. See evaluate, of which this is the case with no values given.

evaluate(parameters, fault=None)

The circuit with the given parameter values, simulated and measured under each operating condition.

parameters maps (component, parameter) to a value; what is not given keeps the value of the netlist. Nothing is drawn. With fault, a fault or the identifier of one of the experiment, it is injected after the values are set, as in a sample. This is what an external algorithm needs to drive the circuit: an optimiser, or a sensitivity method that supplies its own points.

Returns {condition name: SampleResult}, with the measurements of the condition and the complete simulation result. These are not samples of the plan: sample_id and replica are -1. As in a run, a simulation that fails is returned with its status, not raised.

run(workers=1)

Simulate every sample and keep the results in memory.

A simulation that fails is recorded with its status; it does not stop the run.

spicefault.experiments.design

Designed samples: circuits whose parameter values are chosen, not drawn.

A design is a table of parameter values, one row per circuit. An experiment that is given one simulates those circuits with the same engine as drawn ones, so they are resumable, keep their failed simulations and end in a dataset. What they are not is a random sample: a proportion over chosen circuits is not a probability, and the dataset says that its samples were designed.

Design dataclass

Parameter values of the circuits to simulate.

targets are the (component, parameter) pairs and values one row per circuit, in the order of targets. names optionally labels each row; the label is stored in the column design_point. kind and description say how the design was made and are recorded with the dataset.

row(index)

(values, labels) of one circuit of the design.

from_table(table, kind='table', description='') classmethod

A design from a pandas table whose columns are (component, parameter) pairs, or component alone for a value, or component.parameter.

corners(variations, circuit, limit=1024, nominal=True)

The corners of the band of a set of variations: every combination of the lowest and the highest value of each parameter, and the nominal circuit first.

Each variation must be bounded (a tolerance, a uniform or log-uniform range, a truncated normal). n parameters give 2^n corners; more than limit is refused, since the count doubles with every parameter. Rows are named by the side of each parameter, such as -+-, and nominal.

The extremes of a measurement over the corners are its extremes over the band only if it is monotonic in each parameter there. A response with a maximum or a minimum inside the band (a resonance, a notch, a compensated error) has its worst case where no corner is, and corners will not show it.

spicefault.experiments.campaign

Campaign: an experiment simulated to disk, in chunks, resumable, with every simulation accounted for.

ValidationReport dataclass

Problems found before running. Errors prevent the run; warnings do not.

Campaign

A systematic set of simulations written to a dataset: the fault-free circuit and every fault, samples times each, under every operating condition.

Without faults it is a Monte Carlo study of the healthy population: samples circuits drawn from the variations. With faults, each one is injected into samples drawn circuits (samples_per_fault is the same argument under the name it has always had), and healthy_samples sets the size of the fault-free population. FaultCampaign is this class.

It takes the arguments of Experiment, or an experiment already built through from_experiment. The results go to out_dir as a dataset: samples.parquet, waveforms.npy if a waveform is declared, and manifest.json. waveforms.npy has one row per sample of the conditions that store a waveform; the manifest names them under waveform_conditions when they are not all of them.

validate(simulate=True)

Check that the definitions fit the circuit, without running the campaign.

Every variation is drawn once, and every fault and operating condition is applied to the circuit. With simulate, the nominal fault-free circuit is also simulated under each condition and measured: a campaign whose healthy circuit does not simulate is not worth starting.

run(workers=1, resume=True, chunk=2000, progress=True, validate=True)

Simulate every pending sample and write the dataset; returns its folder.

An interrupted run continues after the last complete chunk when launched again with the same definitions; with resume=False it starts from the beginning. A finished campaign is not run again. Individual failures are recorded and do not stop the run.

status()

Samples completed (simulated successfully), failed and still pending.

summary()

One row per fault: samples simulated, successful, and the count of each status.

failures()

The samples that did not give a usable result, with status and message.

dataset()

The finished campaign as a Dataset.

load(drop_failed=True)

(samples, waveforms, manifest) of the finished campaign.

simulate_sample(sample, experiment)

One row of the dataset, and the waveform if one is stored for its condition.

Nothing that happens here stops the campaign: a simulation that fails, an output that cannot be measured, or an error while building the netlist is recorded in the row with its status. elapsed_s is the only column that is not reproducible.

spicefault.experiments.seeding

Random streams of an experiment.

Every sample has its own stream, derived from the master seed and a key that identifies the sample. Nothing else feeds the draws, so a sample is the same whatever the number of workers, the order of execution or the chunking.

sample_stream(seed, *key)

Independent stream for the sample identified by key, whatever the execution order.

text_key(text)

A stable 64-bit integer for a piece of text, the same on every platform and run.

seed_key(scheme, fault_index, fault_id, replica)

Key of the stream of one sample.

  • positional: (fault index, replica). The stream of a fault depends on its place in the fault list: adding or reordering faults changes the samples of the others.
  • content: (hash of the fault identifier, replica). A fault has the same samples in every experiment that contains it.
  • common: (replica,). Replica r is the same drawn circuit for every fault and for the healthy case (common random numbers): comparisons between conditions are paired, and the conditions are no longer statistically independent.

spicefault.experiments.sampling

Sampling designs that choose the points of a population jointly.

By default every sample of an experiment draws from its own independent stream. A Latin hypercube or a Sobol sequence instead decides the points of a whole population together, so that they cover the space more evenly and a mean is estimated with fewer simulations. The design is a function of the seed, the population and its size only, and a sample reads its row: the result still does not depend on the number of workers or on the order of execution.

The points of such a design are not independent. Intervals that assume independence (binomial, order statistics) are not valid for them; the uncertainty of an estimate comes from independent replications of the whole design, with different seeds.

latin_hypercube(rng, n, d)

[n, d] points in (0, 1): each column has exactly one point in each of the n equal strata, at a random place inside it, and the strata are paired at random between columns.

sobol(rng, n, d)

[n, d] points of a scrambled Sobol sequence, from SciPy.

The sequence is balanced when n is a power of two. Its points depend on the version of SciPy, which is recorded with the experiment.

uniforms(method, seed, key, n, d)

The design of the population identified by key: [n, d] numbers in (0, 1).

record(method)

What a dataset keeps about how its samples were chosen.

spicefault.experiments.engine

Parallel, resumable execution of a list of independent simulation tasks.

The application supplies the tasks and a worker(task, context) function returning (row, waveform): row is a dict of scalars describing the simulation and its results, waveform a 1-D array or None. The worker must be a module-level function and derive any random numbers from the task itself (see seeding.sample_stream), so the results do not depend on the number of processes.

run_chunks(tasks, worker, context, parts_dir, n_points=None, workers=1, chunk=2000, progress=True)

Simulate tasks in chunks, one pair of files per chunk; finished chunks are kept.

This is what makes a long campaign resumable: a chunk is written only once it is complete, and a chunk already on disk is not simulated again. Rows get a sample_id equal to the position of their task. With n_points, waveforms are stored as float32 [n, n_points], with NaN where the worker returned none.

elapsed_so_far(parts_dir)

Seconds spent on the complete chunks of a campaign, over all its runs.

run_campaign(tasks, worker, context, out_dir, config, n_points=None, workers=1, chunk=2000, progress=True, summary=None, files=None, user_metadata=None, waveform_rows=None)

Simulate every task and write the dataset to out_dir.

config is whatever defines the campaign; it must be JSON-serialisable and is written to metadata.json. If a previous run on the same folder and configuration was interrupted, it resumes after the last complete chunk. summary adds application counts, computed from the samples, to the manifest. files are text files to keep with the dataset, by name, such as the source netlist. With waveform_rows, one flag per task, only the waveforms of the flagged tasks are kept in waveforms.npy.