Normal variation¶
Variations describe the healthy population: how the parameters of good circuits differ. They are not faults. A fault is injected into a circuit already drawn from this population.
| Variation | Value drawn |
|---|---|
ToleranceVariation |
Within nominal × (1 ± tolerance), or nominal ± tolerance with relative=False; uniform or truncated normal |
NormalVariation |
Normal around a mean (the nominal value by default), optionally truncated |
LogNormalVariation |
Median × exp(N(0, σ)): positive, multiplicative spread |
UniformVariation |
Uniform between two limits |
LogUniformVariation |
Median times a factor between 1/spread and spread |
FixedVariation |
A set value, no spread |
CustomVariation |
Any function of the random stream and the nominal value |
JointVariation |
Several parameters drawn together, when they depend on each other |
CatalogueVariation |
The type of a part, then its parameters around the medians of that type |
CorrelatedVariation |
Several variations that keep their distributions and vary together |
LotVariation |
Components that share the deviation of their manufacturing lot |
from spicefault.variation import NormalVariation, VariationSet, tolerances
population = tolerances(circuit, {"R": 0.01, "C": 0.05}) # by component kind
population = VariationSet([NormalVariation("R1", 10000, 500), NormalVariation("R2", 10000, 500)])
tighter = population.scaled(0.2) # same random numbers, a fifth of the spread
instance_tolerances gives every instance of a subcircuit the same spread in its
parameters, relative or, for a parameter that is nominally zero, absolute:
from spicefault.variation import instance_tolerances
amplifiers = instance_tolerances(circuit, "opamp", {"vos": (0.5e-3, "absolute"), "aol": 0.5})
A normal variation is not a fault: variations describe the healthy population, and a
fault is injected into a circuit already drawn from it. scaled multiplies every
spread while using the same random numbers, so a study of detectability against
tolerance compares the same circuits at each tolerance level.
Every variation except the custom, joint and catalogue ones also has a quantile
function, variation.quantile(u, nominal): the value at probability u. Sampling does
not use it; the designs that choose their uniform numbers jointly do.
Parameters that are not independent¶
Independent tolerances overstate the spread of a ratio and understate that of a sum. Two variations describe the common cases without a function, so a dataset rebuilds them from its record:
from spicefault.variation import CorrelatedVariation, LotVariation, ToleranceVariation
# two resistors from the same reel: each within 1 %, tracking each other
CorrelatedVariation([ToleranceVariation("R1", 0.01), ToleranceVariation("R2", 0.01)],
correlation=0.9)
# a whole group shifted by its lot (4 %), each part 1 % around it
LotVariation(("R1", "R2", "R3"), lot=0.04, within=0.01)
CorrelatedVariation keeps the distribution of each parameter and joins them with a
Gaussian copula. correlation, one number or the full matrix, is that of the
underlying normals: the correlation of the values equals it only for normal variations,
and their rank correlation is (6/π) asin(correlation/2) for any. Each variation it joins
needs a quantile function. LotVariation draws one deviation for the group and one of
each component, and records the first as a label.
JointVariation draws several parameters together: the parameters of a part whose
type is drawn first, two resistors from the same reel, or a netlist parameter computed
from two drawn quantities. It can also record labels, such as the type that was drawn.
See the custom example.
When the dependence is a part that comes in several types, CatalogueVariation needs
no function: the type is drawn first, with the given weights, and each parameter is
the median of that type times a factor between 1/spread and spread. The type is
recorded as a label, and the variation is rebuilt from the record of a dataset.
from spicefault.variation import CatalogueVariation
CatalogueVariation(
"Xelectrode",
options={"gel": {"r": 2e3, "c": 50e-9}, "steel": {"r": 2e5, "c": 5e-9}},
weights=(3, 1), spread=2.0, label="electrode_kind",
)
Functions given to CustomVariation and JointVariation must be defined at module
level, so that worker processes can import them. A record cannot store a function: a
dataset keeps the name of the function, and the experiment has to be passed again to
simulate its samples anew.