Python API¶
noodlelab.verify¶
Verifiable calculations in plain Python: units, uncertainty, requirements, checks and an audit trail, for people and for AI agents writing code.
This is noodlelab’s science without the editor. Everything here works in any
script, notebook or test after pip install noodlelab:
import noodlelab.verify as nv
with nv.record("pendulum") as rec: # writes runs/<...>/provenance.json
L = rec.input("L", "1.000 ± 0.002 m", source="tape measure, lab book p. 12")
g = rec.input("g", "9.81 ± 0.02 m/s^2", source="local gravity survey")
T = rec.result("period", nv.const.tau * (L / g) ** 0.5) # (2.006 ± 0.003) s
rec.require("PER-001 period <= 2.1 s [Analysis] # The swing shall take at most 2.1 s")
rec.verify("PER-001", T) # ✓ margin +0.094 s
assert rec.passed
The rules it helps you keep (and audit() checks afterwards):
Every number has a unit. Values are Pint quantities; mixing dimensions raises instead of silently giving nonsense. Dimensionless results say so (
unit="1"). Constants are named, never retyped:nv.const.c,rec.constant("g0")(seenoodlelab.constants), with their unit, their source and, for measured ones such asG, their CODATA uncertainty.Every measured input has an uncertainty and a source. Uncertainty is propagated by the GUM’s law (first order, with correlations kept), and
budget()says which input contributes most.monte_carlo()checks the linear result where the model is not linear (JCGM 101).Requirements are written down before they are checked, as text a person can read (
COM-001 link_margin >= 3 dB [Analysis]), and each check reports its margin, not just pass or fail. An uncertain result whose margin is no larger than its expanded uncertainty U = k·u (k = 2, or from the degrees of freedom withcoverage=0.95) is inconclusive, and does not pass: the design may meet the requirement, but the calculation cannot show it. A factor of safety (fs=1.5in the line) adds the margin of safety, which must not be negative.Every run leaves a record: inputs, results, requirements, checks, the code (its hash and git commit), the files read and written (SHA-256) and the environment (Python, platform, package versions), so someone else can see exactly what was computed and repeat it.
noodlelab verify script.py runs a script, audits every record it wrote and
exits non-zero when a check fails or is inconclusive, for CI and for agents checking their own
work. Graphs from the editor are verified the same way (noodlelab verify
analysis.graph.json).
- class noodlelab.verify.Check(passed: 'bool', message: 'str' = '', requirement: 'str' = '', statement: 'str' = '', method: 'str' = '', required: 'str' = '', achieved: 'str' = '', margin: 'float | None' = None, unit: 'str' = '', margin_pct: 'float | None' = None, uncertainty: 'float | None' = None, inconclusive: 'bool' = False, margin_of_safety: 'float | None' = None, p_conform: 'float | None' = None, rule: 'str' = '')[source]¶
- Parameters:
passed (bool)
message (str)
requirement (str)
statement (str)
method (str)
required (str)
achieved (str)
margin (float | None)
unit (str)
margin_pct (float | None)
uncertainty (float | None)
inconclusive (bool)
margin_of_safety (float | None)
p_conform (float | None)
rule (str)
- passed: bool¶
- message: str = ''¶
- requirement: str = ''¶
- statement: str = ''¶
- method: str = ''¶
- required: str = ''¶
- achieved: str = ''¶
- margin: float | None = None¶
- unit: str = ''¶
- margin_pct: float | None = None¶
- uncertainty: float | None = None¶
- inconclusive: bool = False¶
- margin_of_safety: float | None = None¶
- p_conform: float | None = None¶
- rule: str = ''¶
- property status: str¶
"passed","failed"or"inconclusive".
- class noodlelab.verify.DecisionRule(kind, factor=1.0, max_risk=0.025)[source]¶
How to decide a requirement when the uncertainty of the result matters (JCGM 106, 8):
simple: simple acceptance, the best estimate decides (8.2). The chance of a wrong verdict can be up to 50 %, which the check’s probability of conformity shows;guard band: guarded acceptance, the value must be inside the limit byfactor·U (8.3.2), so a value near the limit fails instead of being inconclusive;risk: the probability of conformity decides: pass when it is at least 1 −max_risk(the consumer’s risk of accepting a design that does not conform), fail when it is at mostmax_risk, inconclusive between (8.4, a specific risk).
- Parameters:
kind (Literal['simple', 'guard band', 'risk'])
factor (float)
max_risk (float)
- kind: Literal['simple', 'guard band', 'risk']¶
- factor: float = 1.0¶
- max_risk: float = 0.025¶
- property token: str¶
risk=2.5%.- Type:
The rule as a requirement line writes it
- classmethod parse(text)[source]¶
A rule from
simple,guard=1.5orrisk=2.5%(guardandriskalone take the defaults: 1·U and 2.5 %); None for""ornone.- Parameters:
text (str)
- Return type:
DecisionRule | None
- class noodlelab.verify.Budget(rows, unit='')[source]¶
An uncertainty budget:
[(input, contribution, share of variance), ...], largest first, with the contributions inunit. A list, which a notebook shows as a table.- Parameters:
rows (Iterable[tuple[str, float, float]])
unit (str)
- class noodlelab.verify.Finding(code, level, message, where='')[source]¶
Something
audit()found in a record.- Parameters:
code (str)
level (Literal['error', 'warning', 'info'])
message (str)
where (str)
- code: str¶
- level: Literal['error', 'warning', 'info']¶
- message: str¶
- where: str = ''¶
- class noodlelab.verify.MonteCarlo(mean, std, low, high, p, trials, unit, gum, agrees, message, samples=None, batches=1, stable=None, method='random')[source]¶
A Monte Carlo evaluation (JCGM 101) of a model, next to its GUM result.
- Parameters:
mean (float)
std (float)
low (float)
high (float)
p (float)
trials (int)
unit (str)
gum (Any)
agrees (bool)
message (str)
samples (Any)
batches (int)
stable (bool | None)
method (str)
- mean: float¶
- std: float¶
- low: float¶
- high: float¶
- p: float¶
- trials: int¶
- unit: str¶
- gum: Any¶
- agrees: bool¶
- message: str¶
- samples: Any = None¶
- batches: int = 1¶
- stable: bool | None = None¶
- method: str = 'random'¶
- property value: Any¶
mean ± std.
- Type:
The Monte Carlo result as an uncertain value
- property inconclusive: bool¶
Too few trials to tell (the message says how many would).
- exception noodlelab.verify.NoodlelabWarning[source]¶
What noodlelab says about a calculation that ran but may not be what was meant: requirements left unverified, correlations that were lost.
- class noodlelab.verify.Quantity(value, unit=None)[source]¶
A Pint quantity as a type hint:
Quantity["m/s"]for a speed,Quantityfor any quantity.Quantity(9.81, "m/s^2")makes one, andisinstance(x, Quantity)is true for every Pint quantity.- Parameters:
value (Any)
unit (str | None)
- Return type:
Any
- class noodlelab.verify.Record(name, *, out=None, write=True, auto_verify=True, _caller=None)[source]¶
The audit trail of one calculation. See
record().- Parameters:
name (str)
out (str | Path | None)
write (bool)
auto_verify (bool)
_caller (str | None)
- status: str¶
- error: str | None¶
- inputs: dict[str, dict[str, Any]]¶
- results: dict[str, dict[str, Any]]¶
- calls: list[dict[str, Any]]¶
- files: dict[str, list[dict[str, Any]]]¶
- notes: list[str]¶
- path: Path | None¶
- close()[source]¶
Finish the record and write it (done on leaving the
withblock).- Return type:
Path | None
- input(name, value, *, unit='', source='', note='')[source]¶
A value the calculation starts from:
"9.81 ± 0.02 m/s^2", ameasure`d or :func:`q()value, or a plain number withunit. Name itssource(instrument, dataset, paper, datasheet): the audit asks for one. Measured inputs are labellednamein uncertainty budgets. Returns the value to compute with.- Parameters:
name (str)
value (Any)
unit (str)
source (str)
note (str)
- Return type:
Any
- constant(name, *, label='')[source]¶
A named constant as an input (
rec.constant("g0")), recorded underlabel(default: its name) with its source, so the audit needs no more. Returns the value to compute with.- Parameters:
name (str)
label (str)
- Return type:
Any
- add_inputs(**values)[source]¶
Several inputs at once, named by their keywords, in order:
h, g = rec.add_inputs( h=("2.00 ± 0.01 m", "tape measure"), # (value, source) g={"value": "9.81 ± 0.02 m/s^2", "source": "survey", "note": "local"}, )
Each is a value as
input()takes it,(value, source)or a dict ofinput()’s arguments. Returns them as a namespace (inputs.h) that also unpacks in order.- Parameters:
values (Any)
- Return type:
- add_results(**values)[source]¶
Several results at once, named by their keywords:
rec.add_results(t=(2 * h / g) ** 0.5, v=g * t). Each is a value, or a dict ofresult()’s arguments ({"value": n, "exact": True}). Returns them as a namespace that also unpacks in order.- Parameters:
values (Any)
- Return type:
- result(name, value, *, unit='', exact=False, note='')[source]¶
A result of the calculation, recorded with its unit, uncertainty and budget. Converted to
unitwhen given.exact=Truesays it has no uncertainty on purpose (a count, a definition), which the audit then accepts. Returns the value.- Parameters:
name (str)
value (Any)
unit (str)
exact (bool)
note (str)
- Return type:
Any
- require(spec, value=<not given>, *, k=2.0, coverage=None, fs=None, rule=None)[source]¶
Write down requirements (text, one per line, or parsed ones) before checking them; each is verified with
verify(), or when the record closes against the result of the same name.Given a
valuetoo, the one requirement is verified at once and theCheckreturned:rec.require("DRP-001 fall_time <= 1 s", t).k,coverage,fsandruleas forverify().- Parameters:
spec (str | Requirement | RequirementSet)
value (Any)
k (float)
coverage (float | None)
fs (float | None)
rule (DecisionRule | str | None)
- Return type:
Any
- verify(req, value, *, k=2.0, coverage=None, fs=None, rule=None)[source]¶
Check a result against a requirement: its id (given with
require()), aRequirement, or a line of text.k,coverage,fsandruleas forverify(): the coverage factor, or the coverage probability to take it from the degrees of freedom, a factor of safety and a decision rule.valuecan be aMonteCarloresult.- Parameters:
req (str | Requirement)
value (Any)
k (float)
coverage (float | None)
fs (float | None)
rule (DecisionRule | str | None)
- Return type:
- expect(condition, message)[source]¶
A check that is not a requirement: a sanity bound, a conservation law, agreement with a reference.
conditionis truthy when it holds.- Parameters:
condition (Any)
message (str)
- Return type:
- close_to(name, value, reference, *, rtol=0.0, atol=0.0, k=2.0)[source]¶
Check
valueagrees withreference(a textbook value, another method, an earlier result): withinrtol/atolwhen given, else within k times their combined standard uncertainty (En ≤ 1 at k = 2).Quantities are compared in the reference’s unit, so 20 degC and 293.15 K agree. With a temperature in degC or degF,
atolis a difference (0.5 Kor0.5 delta_degC;0.5 degCis read as one too) andrtolis relative to the absolute temperature.- Parameters:
name (str)
value (Any)
reference (Any)
rtol (float)
atol (Any)
- Return type:
- read(path)[source]¶
Note a file the calculation reads (its SHA-256 goes in the record).
- Parameters:
path (str | Path)
- Return type:
Path
- output(name)[source]¶
A path in this record’s folder to write an output to (a plot, a table); its SHA-256 is recorded when the record closes.
- Parameters:
name (str)
- Return type:
Path
- wrote(path)[source]¶
Note a file the calculation wrote outside the record’s folder.
- Parameters:
path (str | Path)
- Return type:
Path
- note(text)[source]¶
A remark for the reader: an assumption, a simplification, a caveat.
- Parameters:
text (str)
- Return type:
None
- property passed: bool¶
Every check passed, every requirement written down was verified, and the block did not raise.
- property unverified: list[str]¶
Requirements written down but never checked.
- to_dict(*, light=False)[source]¶
The record as JSON-ready data (what
write()saves).light=Trueleaves out the environment and the installed packages, which take a while to list, for showing the record rather than keeping it.- Parameters:
light (bool)
- Return type:
dict[str, Any]
- summary()[source]¶
A few lines for a person (or an agent’s context): the verdict, each check, requirements left unverified and the audit’s findings.
- Return type:
str
- class noodlelab.verify.Requirement(id: 'str', quantity: 'str', op: 'Op', limit: 'float', unit: 'str' = '', upper: 'float | None' = None, tolerance: 'float' = 0.0, text: 'str' = '', method: 'str' = 'Analysis', priority: 'str' = '', parent: 'str' = '', fs: 'float | None' = None, rule: 'str' = '')[source]¶
- Parameters:
id (str)
quantity (str)
op (Literal['>=', '<=', '>', '<', '==', 'between'])
limit (float)
unit (str)
upper (float | None)
tolerance (float)
text (str)
method (str)
priority (str)
parent (str)
fs (float | None)
rule (str)
- id: str¶
- quantity: str¶
- op: Literal['>=', '<=', '>', '<', '==', 'between']¶
- limit: float¶
- unit: str = ''¶
- upper: float | None = None¶
- tolerance: float = 0.0¶
- text: str = ''¶
- method: str = 'Analysis'¶
- priority: str = ''¶
- parent: str = ''¶
- fs: float | None = None¶
- rule: str = ''¶
- property decision_rule: DecisionRule | None¶
- property required: str¶
“≥ 3 dB”, “-20 to 60 °C”.
- Type:
The requirement as a short phrase
- property value: Any¶
a quantity when it has a unit (the lower limit of a range).
- Type:
The limit as a value for the graph
- magnitude(value)[source]¶
valueas plain numbers in this requirement’s unit.- Parameters:
value (Any)
- Return type:
Any
- meets(margin)[source]¶
Whether a margin from
margins()meets the requirement: a value exactly at the limit meets>=(margin 0) but not>. Element-wise on arrays.- Parameters:
margin (Any)
- Return type:
Any
- margins(value)[source]¶
How far inside the limits each value is, in the requirement’s unit (negative: outside; zero: at the limit, see
meets()). Works element-wise on arrays and columns.- Parameters:
value (Any)
- Return type:
Any
- verify(value, *, k=2.0, fs=None, coverage=None, rule=None, samples=None)[source]¶
Compare a result with the requirement. An array passes when every element does; its margin is the smallest (the worst case).
An uncertain value is judged with its expanded uncertainty U = k·u (k = 2 by default, about 95 % for a normal distribution): it fails when its nominal value misses, passes when the nominal value is inside the limit by more than U, and is otherwise inconclusive (not passed): the design may meet the requirement, but the calculation cannot show it. An exact value is judged on its value alone.
coverage: take k from Student’s t for this coverage probability and the value’s effective degrees of freedom instead (seeuncertainty.coverage_factor()), as a mean of few readings needs.fs: a factor of safety (the requirement’s ownfswhen not given). The check then also reports the margin of safety MS (see the module’s docstring), and fails when MS < 0.The check of an uncertain value also reports its probability of conformity;
samples(Monte Carlo trials of the value, one row per trial) give it as the share of trials that conform.rule: decide with aDecisionRule(the requirement’s own when not given).- Parameters:
value (Any)
k (float)
fs (float | None)
coverage (float | None)
rule (DecisionRule | str | None)
samples (Any)
- Return type:
- class noodlelab.verify.RecordData(data, *, path=None)[source]¶
A record read back from its file (see
load()). It is the JSON as a dict, sodata["inputs"]andjson.dumps(data)work as they always did, but it cannot be changed: a record is evidence of a run.- Parameters:
data (dict[str, Any])
path (Path | None)
- clear() None. Remove all items from D.¶
- Parameters:
self (Any)
args (Any)
kwargs (Any)
- Return type:
Any
- pop(k[, d]) v, remove specified key and return the corresponding value.¶
If the key is not found, return the default if given; otherwise, raise a KeyError.
- Parameters:
self (Any)
args (Any)
kwargs (Any)
- Return type:
Any
- popitem(*args, **kwargs)¶
Remove and return a (key, value) pair as a 2-tuple.
Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.
- Parameters:
self (Any)
args (Any)
kwargs (Any)
- Return type:
Any
- setdefault(*args, **kwargs)¶
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- Parameters:
self (Any)
args (Any)
kwargs (Any)
- Return type:
Any
- update([E, ]**F) None. Update D from dict/iterable E and F.¶
If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
- Parameters:
self (Any)
args (Any)
kwargs (Any)
- Return type:
Any
- property format: Literal['record', 'graph', 'unknown']¶
"record"(a script’s),"graph"(a graph run’s) or"unknown".
- property name: str¶
- property status: str¶
- property passed: bool¶
every check passed, every requirement was verified and the calculation did not raise.
- Type:
No error-level finding
- result(name)[source]¶
A result (or input) as a value to compute with again: a quantity, an uncertain one when it had an uncertainty. Its correlations with other values are not in the record, so they are lost: the uncertainty is its own, independent of everything else’s.
- Parameters:
name (str)
- Return type:
Any
- audit()[source]¶
The audit’s findings (see
audit(), andaudit_graph_record()for a graph run).- Return type:
list[Finding]
- summary()[source]¶
A few lines for a person, as
Record.summary()gives.- Return type:
str
- diff(other, *, rtol=1e-09, atol=0.0)[source]¶
What changed from
other(an earlier run) to this one: seenoodlelab.core.rundiff.diff_runs().- Parameters:
other (RecordData | str | Path)
rtol (float)
atol (float)
- class noodlelab.verify.RequirementSet(items=())[source]¶
Requirements by id, in the order they were given.
- Parameters:
items (tuple[Requirement, ...])
- items: tuple[Requirement, ...] = ()¶
- property ids: list[str]¶
- merged(other)[source]¶
- Parameters:
other (RequirementSet)
- Return type:
- class noodlelab.verify.Sweep(rows, params)[source]¶
The rows of a
sweep(), one dict per point: its parameters and what the function returned there (result, or the keys of a dict it returned).column(name)gives one column;to_pandas()a table.- Parameters:
rows (Iterable[dict[str, Any]])
params (list[str])
- property columns: list[str]¶
- class noodlelab.verify.Uncertain(value, uncertainty, unit='', name='')[source]¶
A value with a standard uncertainty, as a type hint:
Uncertainfor any,Uncertain["m"]for a length.Uncertain(9.81, 0.02, "m/s^2")makes one.- Parameters:
value (Any)
uncertainty (float)
unit (str)
name (str)
- Return type:
Any
- class noodlelab.verify.Values[source]¶
Values by name, from
Record.add_inputs()orRecord.add_results():vals.horvals["h"], andh, g = valsunpacks them in the order they were given.
- class noodlelab.verify.Verifications(checks=())[source]¶
Checks of requirements, collected into a compliance matrix (one entry per requirement: verifying it again replaces the earlier entry).
- Parameters:
checks (tuple[Check, ...])
- property passed: bool¶
- property with_conformity: bool¶
Whether any check has a probability of conformity (an uncertain value).
- property columns: list[str]¶
MATRIX_COLUMNS, with “P(conform)” after MS when any check has one.- Type:
The compliance matrix’s columns
- noodlelab.verify.audit(rec)[source]¶
Check a record against the rules of a verifiable calculation (see
RULES): failed or missing checks, results without units or uncertainty, inputs without a source, code that was not committed or has changed since. Takes aRecord, its data, or its file.
- noodlelab.verify.budget(value)[source]¶
[(input, contribution, share of variance), ...], largest first: which named inputs the uncertainty ofvaluecomes from, contributions in its unit.- Parameters:
value (Any)
- Return type:
- noodlelab.verify.checked(req, *, k=2.0, coverage=None, fs=None, rule=None, strict=False)[source]¶
Check the result of every call of a function against a requirement:
@nv.checked("R1 distance <= 50 m") def distance(g, T): ...
The check is made like
verify()(samek,coverage,fsandrule) and lands in the active record, if there is one. A check that does not pass is reported by aNoodlelabWarning;strict=TrueraisesAssertionErrorinstead. The requirement is read when the function is decorated, so a misspelled line fails at import rather than at the first call. The function’s result is returned unchanged.- Parameters:
req (Requirement | str)
k (float)
coverage (float | None)
fs (float | None)
rule (DecisionRule | str | None)
strict (bool)
- Return type:
Callable[[F], F]
- noodlelab.verify.constant(name)[source]¶
A named constant with its unit:
constant("c"),constant("g0"). Measured ones (G,m_e…) carry their CODATA uncertainty. The same asnv.const.c; add your own withdefine_constant().- Parameters:
name (str)
- Return type:
Any
Values whose uncertainties are correlated (GUM 5.2), as a list:
correlated([a, b], [[1, 0.8], [0.8, 1]]): measured valuesaandbkeep their value, standard uncertainty and unit, with the correlation matrixcorrbetween them (two readings of one instrument);correlated([1.2, 0.3], cov=pcov, names=["slope", "offset"], unit="V"): plain numbers with the covariance matrixcov, such as a fit’s parameters (dof: the degrees of freedom it was estimated with).
Each value is named in budgets after
names, or the input it came from. A value that already depends on several sources keeps only its total uncertainty (with a warning).monte_carlo()draws them jointly.- Parameters:
values (Iterable[Any])
corr (Any)
cov (Any)
names (Iterable[str] | None)
unit (str)
dof (float | None)
- Return type:
list[Any]
- noodlelab.verify.current()[source]¶
The record being written (inside
with record(...)), or None.- Return type:
Record | None
- noodlelab.verify.define_constant(name, value, unit='', *, uncertainty=0.0, title='', source='', symbol='', override=False)¶
Add a constant for every graph and script in this process:
define("g_local", 9.8123, "m/s^2", uncertainty=0.0005, source="survey 2024") define("rho_water", "998.2 kg/m^3", title="density of water at 20 °C")
Replacing a built-in constant warns, unless
override=Truesays it is meant.pi,tauandecannot be replaced.- Parameters:
name (str)
value (Any)
unit (str)
uncertainty (float)
title (str)
source (str)
symbol (str)
override (bool)
- Return type:
- noodlelab.verify.finished()[source]¶
Every record finished in this process, oldest first.
- Return type:
list[Record]
- noodlelab.verify.fmt(value, k=1.0)[source]¶
A value for people:
(2.006 ± 0.003) s(GUM rounding),3 dB,0.5.k: coverage factor for an expanded uncertainty.- Parameters:
value (Any)
k (float)
- Return type:
str
- noodlelab.verify.from_repeats(samples, unit='', *, name='')[source]¶
The mean of repeated readings with its standard uncertainty s/√n (GUM Type A, 4.2):
from_repeats([9.79, 9.82, 9.80, 9.81], "m/s^2", name="g"). The mean carries n - 1 degrees of freedom, socoverage=0.95inverify()andmonte_carlo()(a Student’s t) account for how few readings there were.samplesmay be a quantity array, or readings that are uncertain themselves (a calibrated instrument’s), whose uncertainty the mean keeps. Inside amonte_carlo()model this returns one draw.- Parameters:
samples (Any)
unit (str)
name (str)
- Return type:
Any
- noodlelab.verify.load(path)[source]¶
A record from its
provenance.json(or the folder holding it): a script’s (record()) or a graph run’s. It is the record’s data, a read-only dict, with what a reader asks of it:passed,checks,result(name),summary(),audit()anddiff(other).- Parameters:
path (str | Path)
- Return type:
- noodlelab.verify.load_constants(path)¶
define()every constant in aconstants.tomlfile. Raises ConstantsError listing every problem, since a script should stop on them.- Parameters:
path (str | Path)
- Return type:
dict[str, Constant]
- noodlelab.verify.measure(value, u=None, unit='', *, name='', distribution='normal', dof=None)[source]¶
A measured value with its standard uncertainty:
measure("9.81 ± 0.02 m/s^2"),measure("9.81(2) m/s^2")measure(9.81, 0.02, "m/s^2")measure(q("9.81 m/s^2"), 0.02)
namelabels it inbudget().distribution(anddoffor Student’s t) is whatmonte_carlo()samples;uis always the GUM standard uncertainty (for a rectangular distribution of half-width a, give a/√3; for the mean of n readings with spread s, s/√n withdof= n - 1, which is the scale of the t distribution sampled, JCGM 101 6.4.9). An unknown distribution, or t withoutdof, is refused. Inside amonte_carlo()model this returns one draw instead.- Parameters:
value (Any)
u (float | None)
unit (str)
name (str)
distribution (Literal['normal', 'rectangular', 'triangular', 't'])
dof (float | None)
- Return type:
Any
- noodlelab.verify.monte_carlo(model, trials=None, *, seed=1, p=0.95, max_trials=1000000, workers=None, qmc=False)[source]¶
Evaluate
model(a function of no arguments that makes its uncertain inputs withmeasure()and returns a number or quantity) by Monte Carlo, and compare with linear GUM propagation (JCGM 101 8). Use it when the model is not linear in its inputs:agreessays whether the GUM uncertainty can be trusted. The inputs must be made inside the model.Without
trials, it runs as many as the results need (JCGM 101 7.9): batches of 10 000 until the mean, the standard uncertainty and the coverage interval stop moving at the precision they are reported to (two significant digits of u), ormax_trials(stablesays which).trials=20_000runs exactly that many instead, as it did before.A model that takes arrays (NumPy functions and arithmetic do) runs many trials at once; one that does not (an
if,math.sin) runs trial by trial, andworkers=4spreads its batches over 4 processes, with the same result whatever the number.qmc=Truedraws each batch from a scrambled Sobol sequence (needs SciPy and a model that takes arrays): a smooth model needs fewer trials. Both need the adaptive run.- Parameters:
model (Callable[[], Any])
trials (int | None)
seed (int)
p (float)
max_trials (int)
workers (int | None)
qmc (bool)
- Return type:
- noodlelab.verify.nominal(value)[source]¶
The value without its uncertainty.
- Parameters:
value (Any)
- Return type:
Any
- noodlelab.verify.notebook()[source]¶
Show uncertain values in this Jupyter (IPython) session as people write them,
(2.006 ± 0.003) swith GUM rounding, and typeset (_repr_latex_). Records, checks, budgets and Monte Carlo results show as tables without it; this is for the values themselves, which belong to theuncertaintiesand Pint packages. Quantities without an uncertainty keep Pint’s display.- Return type:
None
- noodlelab.verify.q(value, unit='')[source]¶
A quantity:
q("9.81 m/s^2"),q(9.81, "m/s^2"),q("3 dB"). Text with an uncertainty ("9.81 ± 0.02 m/s^2") gives an uncertain one.- Parameters:
value (Any)
unit (str)
- Return type:
Any
- noodlelab.verify.record(name, *, out=None, write=True, auto_verify=True)[source]¶
Start a record of a calculation:
with nv.record("link budget") as rec: ....On leaving the block the record is written to
<out>/<time>-<name>-<id>/provenance.json(outdefaults torunsnext to the script, or$NOODLELAB_RECORDS).write=Falsekeeps it in memory only (tests).A requirement still unverified when the block ends is checked against the result of the same name (
fall_timeagainstrec.result("fall time", ...)), unlessauto_verify=False; one with no such result is left unverified, and aNoodlelabWarningsays so.- Parameters:
name (str)
out (str | Path | None)
write (bool)
auto_verify (bool)
- Return type:
- noodlelab.verify.recorded(name=None, *, out=None, write=True, auto_verify=True)[source]¶
Run every call of a function inside a
record(), so an entry point leaves its provenance without awithblock:@nv.recorded("fall time") def main(): ...
namedefaults to the function’s name. The other arguments are those ofrecord(). A call made while another record is active starts its own, which ends with the call.- Parameters:
name (str | Callable[[...], Any] | None)
out (str | Path | None)
write (bool)
auto_verify (bool)
- Return type:
Any
- noodlelab.verify.requirement(line)[source]¶
One requirement from a line of text (see
requirements()).- Parameters:
line (str)
- Return type:
- noodlelab.verify.requirements(text)[source]¶
Requirements from text, one per line:
COM-001 link_margin >= 3 dB [Analysis] # The link shall close with 3 dB to spare PWR-001 dc_power <= 40 W [Inspection] TMP-001 temperature between -20 and 60 degC
or from records (rows of a CSV/YAML spec: id, quantity, op, limit, unit…).
- Parameters:
text (str | Iterable[dict[str, Any]])
- Return type:
- noodlelab.verify.std_dev(value)[source]¶
The standard uncertainty (0 for an exact value), in the value’s unit.
- Parameters:
value (Any)
- Return type:
Any
- noodlelab.verify.sweep(fn, /, *, grid=True, points=11, **params)[source]¶
Run
fnover a set of parameter values and collect the results:sweep(lambda L: period(L, g), L=q(np.linspace(0.5, 2, 7), "m")).Each parameter is a list, an array (a quantity array gives quantities) or a range as text:
L="0.5 .. 2 m"(pointsvalues from one end to the other) ormethod="[a, b]".grid=Trueruns every combination;grid=Falsetakes the parameters side by side, which must then be as long. Inside a record the sweep’s parameters and size are recorded.- Parameters:
fn (Callable[[...], Any])
grid (bool)
points (int)
params (Any)
- Return type:
- noodlelab.verify.traced(fn=None, *, name=None)[source]¶
Record every call of a function in the active record: its arguments, result (with units and uncertainty), duration and a hash of its source. Outside a record the function runs as usual, at no cost:
@nv.traced def drag(rho, v, cd, area): return 0.5 * rho * v**2 * cd * area
- Parameters:
fn (F | None)
name (str | None)
- Return type:
Any
- noodlelab.verify.verify(req, value, *, k=2.0, coverage=None, fs=None, rule=None)[source]¶
Check
valueagainst a requirement (aRequirementor its line of text). TheChecksays whether it passed, what was achieved and the margin in the requirement’s unit (negative: outside the limit). An array passes when every element does; its margin is the worst case.An uncertain value must meet the limit by more than U = k·u (
k= 2), or it is inconclusive;coverage=0.95takes k from Student’s t and the value’s effective degrees of freedom instead (a mean of few readings).fs: a factor of safety (overridesfs=in the requirement’s line); the check reports the margin of safety and fails when it is negative.An uncertain value’s check also has
p_conform, the probability that the true value meets the requirement (JCGM 106). Pass aMonteCarloresult asvalueto take it from the trials, which follows a skewed result.rule: decide with a decision rule ("risk=2.5%","guard=1","simple"; overrides the one in the requirement’s line) instead of calling a margin within U inconclusive.- Parameters:
req (Requirement | str)
value (Any)
k (float)
coverage (float | None)
fs (float | None)
rule (DecisionRule | str | None)
- Return type:
- noodlelab.verify.wrap(fn=None, *, units_in=None, units_out=None, elementwise=False, validate=False, name=None)[source]¶
Make a function from another library take and return quantities with uncertainty, and record its calls like
traced().The wrapped function receives plain numbers (or arrays) in
units_in({"parameter": "unit"}; a quantity is converted to it, a plain number is taken to be in it, a parameter left out is passed as it is). Its result gets the unitunits_out: a unit string, or a function of the units the arguments came in ({"parameter": unit}) that returns one. For a function that returns a tuple (scipy.integrate.quad: value and error estimate) or a dict, give a tuple or dict of units, withNonefor an element to leave bare. The uncertainty of the inputs is propagated through the call to first order, by numerical derivatives (GUM 5.1), so the library function itself needs no changes.elementwise=Truesays it works element by element on arrays, which makes the propagation cheaper:brentq = nv.wrap(scipy.optimize.brentq, units_in={"a": "m", "b": "m"}, units_out="m")
First order is wrong for a strongly nonlinear function.
validate=Truechecks each call that has uncertain inputs againstmonte_carlo()(many more calls of the function), and says in the record, and in aNoodlelabWarningwhen it fails, whether the result can be trusted. It needs a single number as the result, and inputs that are independent measurements (not computed from others, not arrays); otherwise it says it could not validate.Parameters of the function must be nameable:
*argsand**kwargsare refused, so wrap it in a function with named parameters instead. A call whose result cannot carry an uncertainty runs on the nominal values, with aNoodlelabWarningsaying so.- Parameters:
fn (F | None)
units_in (dict[str, str] | None)
units_out (Any)
elementwise (bool)
validate (bool)
name (str | None)
- Return type:
Any
noodlelab.display¶
Records, checks and values for people: HTML for notebooks, Markdown for reports and chat, LaTeX for documents.
A calculation is only verifiable if someone reads it, and a notebook is where
many people do. Every renderer here works from the record’s data (what
noodlelab.verify.Record.to_dict() gives and noodlelab.verify.load()
reads back), so a record shows the same while it is written and years later.
The HTML is plain tables with inline styles that read in light and dark
notebooks, and the LaTeX needs only amssymb.
Jupyter draws text/latex with MathJax, which typesets mathematics but not
tables, so records and checks show as HTML there; record_latex() is for
a document (rec.to_latex()), and values get _repr_latex_ through
noodlelab.verify.notebook().
- noodlelab.display.value_text(value)[source]¶
(2.006 ± 0.003) s: GUM rounding, the unit as people write it.- Parameters:
value (Any)
- Return type:
str
- noodlelab.display.value_latex(value, *, math_mode=True)[source]¶
(2.006 \pm 0.003)\,\mathrm{s}, in$...$unlessmath_modeis False.- Parameters:
value (Any)
math_mode (bool)
- Return type:
str
- noodlelab.display.check_html(check)[source]¶
One check: its verdict, and its message (requirement, margin, P(conform)).
- Parameters:
check (Any)
- Return type:
str
- noodlelab.display.budget_html(rows, unit='')[source]¶
An uncertainty budget as a table: each input, its contribution and its share of the variance, with a bar so the largest stands out.
- Parameters:
rows (list[tuple[str, float, float]])
unit (str)
- Return type:
str
- noodlelab.display.budget_markdown(rows, unit='')[source]¶
- Parameters:
rows (list[tuple[str, float, float]])
unit (str)
- Return type:
str
- noodlelab.display.record_html(data, findings=(), passed=None)[source]¶
A record as HTML: the verdict, then inputs, results, checks and what the audit found, each as a small table.
- Parameters:
data (dict[str, Any])
findings (list[Any])
passed (bool | None)
- Return type:
str
- noodlelab.display.record_markdown(data, findings=(), passed=None)[source]¶
A record as Markdown: the verdict, then tables of inputs, results and checks.
- Parameters:
data (dict[str, Any])
findings (list[Any])
passed (bool | None)
- Return type:
str
- noodlelab.display.record_latex(data, findings=(), *, standalone=False)[source]¶
A record as a LaTeX calculation sheet: inputs with their sources, results, and each check with its margin.
standalone=Truemakes a whole document (it needsamssymb, andbooktabsfor the rules).- Parameters:
data (dict[str, Any])
findings (list[Any])
standalone (bool)
- Return type:
str
The pytest plugin¶
noodlelab in a test suite: a test that verifies a calculation fails when the calculation’s record does.
pytest loads this by itself once noodlelab is installed (it is a pytest11
entry point). It gives tests the nv_record fixture, a
noodlelab.verify.Record named after the test:
def test_the_bracket_holds(nv_record):
load = nv_record.input("load", "1200 ± 50 N", source="load case 3")
stress = nv_record.result("stress", load / area)
nv_record.require("STR-001 stress <= 180 MPa", stress)
@pytest.mark.noodlelab on a test gives it the record without asking for
the fixture, so a test that only calls @nv.checked functions is failed by
them.
When the test function returns, the record is closed (requirements named
after a result are verified then, as in a with block) and audited: a
failed or inconclusive check, a requirement never verified, or an error fails
the test with the record’s summary. --nv-strict (or
@pytest.mark.noodlelab(strict=True)) fails on the audit’s warnings too:
results without units or uncertainty, nothing verified. Whether the code was
committed is left out, since a developer’s tests run on changes not yet
committed. Records are kept in memory unless --nv-records DIR is given.
Only pytest is imported here at start-up; noodlelab.verify is imported by the fixture, so test suites that never ask for it pay nothing more.
noodlelab.constants¶
Named constants for calculations in Python, with their units and, where they were measured, their CODATA uncertainty:
from noodlelab.constants import c, g0, k_B, pi
E = m * c**2 # a Pint quantity, in kg·m²/s²
W = m * g0 # standard gravity, 9.80665 m/s², exact
F = G * M * m / r**2 # G is uncertain: 6.67430(15)e-11 m³/(kg·s²)
pi, tau and e are the math module’s plain floats. Exact
constants (c, h, k_B, g0…) are quantities, and measured ones
(G, m_e, alpha…) are uncertain values named after the constant,
so noodlelab.verify.budget() can list them. The full table, with sources,
is in noodlelab.core.constants, and noodlelab constants prints it.
Constants you add with define() (or load() from a
constants.toml) can be imported the same way once they are defined.
- class noodlelab.constants.Constant(name: 'str', value: 'float', unit: 'str' = '', uncertainty: 'float' = 0.0, symbol: 'str' = '', title: 'str' = '', source: 'str' = '', group: 'str' = '', origin: 'Origin' = 'built-in', override: 'bool' = False)[source]¶
- Parameters:
name (str)
value (float)
unit (str)
uncertainty (float)
symbol (str)
title (str)
source (str)
group (str)
origin (Literal['built-in', 'registered', 'workspace', 'graph'])
override (bool)
- name: str¶
- value: float¶
- unit: str = ''¶
- uncertainty: float = 0.0¶
- symbol: str = ''¶
- title: str = ''¶
- source: str = ''¶
- group: str = ''¶
- origin: Literal['built-in', 'registered', 'workspace', 'graph'] = 'built-in'¶
- override: bool = False¶
- property exact: bool¶
- value_of()[source]¶
The constant to compute with. A plain float for exact numbers, and a quantity for exact values with a unit. A measured value is uncertain, named after the constant. It is the same value every time, so results computed from
Gtwice stay correlated. Inside a Monte Carlo trial it is a fresh draw instead.- Return type:
Any
- key()[source]¶
What a cache key needs: the definition, not the description.
- Return type:
dict[str, Any]
- exception noodlelab.constants.ConstantsError[source]¶
A constant that cannot be defined (a bad name, value or unit).
- noodlelab.constants.define(name, value, unit='', *, uncertainty=0.0, title='', source='', symbol='', override=False)[source]¶
Add a constant for every graph and script in this process:
define("g_local", 9.8123, "m/s^2", uncertainty=0.0005, source="survey 2024") define("rho_water", "998.2 kg/m^3", title="density of water at 20 °C")
Replacing a built-in constant warns, unless
override=Truesays it is meant.pi,tauandecannot be replaced.- Parameters:
name (str)
value (Any)
unit (str)
uncertainty (float)
title (str)
source (str)
symbol (str)
override (bool)
- Return type:
- noodlelab.constants.load(path)[source]¶
define()every constant in aconstants.tomlfile. Raises ConstantsError listing every problem, since a script should stop on them.- Parameters:
path (str | Path)
- Return type:
dict[str, Constant]
Writing plugins¶
The public API for writing noodlelab plugins.
A plugin is a Python package that noodlelab finds through the
noodlelab.nodes entry point, with a noodlelab-plugin.toml manifest
beside its code. Its nodes are type-hinted functions with node(), and
everything a node needs from noodlelab comes from here:
from noodlelab.plugin import Param, Quantity, node
from noodlelab.plugin.units import ureg
This package is the contract between noodlelab and its plugins. What it
exports keeps working within an API_VERSION; a plugin states the
version it was written for in its manifest (api = 1), and noodlelab
refuses to load one written for a version it does not speak, rather than
failing somewhere inside a run. Everything else in noodlelab (noodlelab.core,
noodlelab.reports…) is internal and may change in any release, so a
plugin that reaches into it can break without warning; noodlelab
check-plugin refuses such imports.
The helpers are grouped in submodules, imported on demand so that a plugin pays only for what it uses:
units: the unit registry and quantitiesuncertainty: values with standard uncertainties (GUM)about: units and descriptions carried by table columnstables: table columns as arrays, and table attributes in fileslists: names, numbers and class breaks typed into text widgetsexpr: checking expressions typed into widgets before evaluating themconstants: named physical constantsfigures: saving matplotlib figuresstudies: parameter studies (design tables)math: Typst maths for previews and reports
- noodlelab.plugin.require(install, *modules)[source]¶
Raise a helpful ImportError when modules a plugin needs are missing.
installsays how to get them: the name of a noodlelab tier ("maths"), or what to install ("noodlelab-thermo[steam]"). Call it at the top of a plugin’s__init__for optional dependencies, so the plugin fails to load with that advice instead of an ImportError deep inside.- Parameters:
install (str)
modules (str)
- Return type:
None
noodlelab.plugins¶
Plugins: finding, checking, installing and versioning node packs.
A plugin is a Python package registered under the noodlelab.nodes entry
point, with a manifest (noodlelab-plugin.toml, see manifest).
Plugins are written against noodlelab.plugin (singular), the public API;
this package (plural) is the machinery around them.
From Python:
import noodlelab.plugins
noodlelab.plugins.require("thermo>=1.2") # fail early, with the install command
noodlelab.plugins.install("thermo") # in a notebook: shows the plan, asks
noodlelab.plugins.health() # what the CLI's `plugins check` shows
- noodlelab.plugins.health(*, home=None, offline=False)[source]¶
The health of every installed plugin (a
status.HealthReport).- Parameters:
home (Path | None)
offline (bool)
- Return type:
Any
- noodlelab.plugins.install(*specs, home=None, yes=False)[source]¶
Install plugins (and the plugins they require) into this Python environment, as
noodlelab plugins adddoes: for notebooks. Unlessyes, show the plan and ask first. The names of the plugins installed; restart the kernel to use a plugin that was upgraded.- Parameters:
specs (str)
home (Path | None)
yes (bool)
- Return type:
list[str]
- noodlelab.plugins.require(*specs)[source]¶
Check that plugins are installed, in the versions a script needs:
import noodlelab.plugins noodlelab.plugins.require("thermo>=1.2", "geo")
Raise an ImportError naming what is missing and the command that installs it. It only reads package metadata, so it is cheap to call at the top of a script.
- Parameters:
specs (str)
- Return type:
None
The node API¶
noodlelab: verifiable science for people and AI agents.
For calculations in plain Python (units, uncertainty, requirements, checks and
a provenance record of every run), use noodlelab.verify:
import noodlelab.verify as nv
with nv.record("pendulum") as rec:
L = rec.input("L", "1.000 ± 0.002 m", source="tape measure")
...
Plugin authors write against noodlelab.plugin, which re-exports these
names with the helpers for units, uncertainty and tables:
from noodlelab.plugin import Param, Probe, ProbeContext, RunContext, node, register_type
- class noodlelab.FileRef(value='')[source]¶
A reference to a file in the workspace, on the server, or on a mount.
It is a
str(so it serialises, hashes and prints as the reference), with methods that work the same for every backend.- Parameters:
value (str | PurePath)
- Return type:
- property target: Target¶
- property mount: str | None¶
- property is_local: bool¶
- property name: str¶
- property suffix: str¶
- with_suffix(suffix)[source]¶
The file next to this one with another extension, on the same storage:
dem.asc→dem.prj, for sidecar files.- Parameters:
suffix (str)
- Return type:
- open(mode='rb', **kwargs)[source]¶
Open for reading (
"rb","r") or, on writable storage, writing.- Parameters:
mode (str)
kwargs (Any)
- Return type:
IO[Any]
- class noodlelab.Param(label=None, description='', widget=None, min=None, max=None, step=None, precision=None, choices=None, accept=None, directory=False, exists=True, options_from=None, empty=None, type=None, multiline=False, required='')[source]¶
Extra metadata for a parameter, used via
Annotated[type, Param(...)].- Parameters:
label (str | None)
description (str)
widget (Literal['number', 'slider', 'combo', 'text', 'toggle', 'none'] | None)
min (float | None)
max (float | None)
step (float | None)
precision (int | None)
choices (Sequence[Any] | None)
accept (Sequence[str] | None)
directory (bool)
exists (bool)
options_from (str | None)
empty (str | None)
type (Any)
multiline (bool)
required (str)
- label: str | None = None¶
- description: str = ''¶
- widget: Literal['number', 'slider', 'combo', 'text', 'toggle', 'none'] | None = None¶
- min: float | None = None¶
- max: float | None = None¶
- step: float | None = None¶
- precision: int | None = None¶
- choices: Sequence[Any] | None = None¶
- accept: Sequence[str] | None = None¶
- directory: bool = False¶
- exists: bool = True¶
- options_from: str | None = None¶
- empty: str | None = None¶
- type: Any = None¶
- multiline: bool = False¶
- required: str = ''¶
- class noodlelab.Preview(*, kind, summary, text=None, image=None, table=None, url=None)[source]¶
- Parameters:
kind (Literal['value', 'text', 'image', 'table', 'file', 'math'])
summary (str)
text (str | None)
image (str | None)
table (TablePreview | None)
url (str | None)
- kind: Literal['value', 'text', 'image', 'table', 'file', 'math']¶
- summary: str¶
- text: str | None¶
- image: str | None¶
- table: TablePreview | None¶
- url: str | None¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class noodlelab.Probe(preview=None, meta=None, output_meta=None, summary=None, sample=None, output_samples=None)[source]¶
What a probe returns (or yields).
preview: any value, rendered with the registered previewers (a DataFrame becomes a table, a Figure an image), or a readyPreviewsuch as one fromimage_preview().meta: small JSON describing the node’s outputs, readable downstream withProbeContext.meta(); it applies to every output unlessoutput_metagives one per output name.summaryoverrides the preview’s one-line summary.sample(oroutput_samplesper output): a small value of the output’s type that downstream nodes are evaluated on while editing (seesample). When left out, a preview value of the output’s type is used, so returningdf.head(20)is enough.- Parameters:
preview (Any)
meta (dict[str, Any] | None)
output_meta (dict[str, dict[str, Any]] | None)
summary (str | None)
sample (Any)
output_samples (dict[str, Any] | None)
- preview: Any = None¶
- meta: dict[str, Any] | None = None¶
- output_meta: dict[str, dict[str, Any]] | None = None¶
- summary: str | None = None¶
- sample: Any = None¶
- output_samples: dict[str, Any] | None = None¶
- class noodlelab.ProbeContext(node_id, root, timeout, _upstream=<factory>, _linked=<factory>, _cancel=<factory>, _started=<factory>, watched=<factory>, logs=<factory>)[source]¶
Passed to probes and options functions that ask for it (by annotation or by naming a parameter
ctx).- Parameters:
node_id (str)
root (Path)
timeout (float)
_upstream (dict[str, dict[str, Any] | None])
_linked (set[str])
_cancel (Event)
_started (float)
watched (dict[str, tuple[Callable[[], Any], Any]])
logs (list[str])
- node_id: str¶
- root: Path¶
- timeout: float¶
- watched: dict[str, tuple[Callable[[], Any], Any]]¶
- logs: list[str]¶
- meta(input)[source]¶
Meta of the output linked into
input, or None (not linked, or the upstream node has no probe).- Parameters:
input (str)
- Return type:
dict[str, Any] | None
- watch(path)[source]¶
Mark a file the probe depends on: the cached result is dropped when it changes (modification time, size, or ETag for remote files). Accepts a Path (resolved against the workspace) or a FileRef; returns it.
- Parameters:
path (str | PurePath)
- Return type:
Any
- property cancelled: bool¶
True once the node’s values changed and a newer probe started. Long probes should check this between steps (or call
check_cancelled()).
- property remaining: float¶
Seconds left in the time budget (may be negative).
- class noodlelab.Problem(severity, message)[source]¶
- Parameters:
severity (Literal['error', 'warning'])
message (str)
- severity: Literal['error', 'warning']¶
Alias for field number 0
- message: str¶
Alias for field number 1
- class noodlelab.RunContext(run_id: 'str', node_id: 'str', workdir: 'Path', scratch: 'Path | None' = None, root: 'Path | None' = None, _emit: 'Callable[[str, dict[str, Any]], None]' = <function RunContext.<lambda> at 0x7ffa087ccae0>, _provenance: 'Any' = None, _about_in: 'dict[str, Any]' = <factory>, _declared: 'dict[str, Any]' = <factory>, _samples: 'Any' = None, _warnings: 'list[str]' = <factory>)[source]¶
- Parameters:
run_id (str)
node_id (str)
workdir (Path)
scratch (Path | None)
root (Path | None)
_emit (Callable[[str, dict[str, Any]], None])
_provenance (Any)
_about_in (dict[str, Any])
_declared (dict[str, Any])
_samples (Any)
_warnings (list[str])
- run_id: str¶
- node_id: str¶
- workdir: Path¶
- scratch: Path | None = None¶
- root: Path | None = None¶
- monte_carlo_samples()[source]¶
The trials the executor ran for a Monte Carlo node (a
Samples), or None.- Return type:
Any
- describe(output='result', **fields)[source]¶
Say what an output is:
title,description,sources,licences,citationsorflags(lists of text). It travels downstream with the value (seenoodlelab.core.about).- Parameters:
output (str)
fields (Any)
- Return type:
None
- input_about(name)[source]¶
What is known about input
name: anAbout, or None.- Parameters:
name (str)
- Return type:
Any
- warn(message)[source]¶
Warn about this node’s result (see
warn()).- Parameters:
message (str)
- Return type:
None
- log(message)[source]¶
Send a log line to the editor (shown in the node inspector).
- Parameters:
message (str)
- Return type:
None
- progress(fraction, message='')[source]¶
Report progress between 0 and 1; drawn as a bar on the node.
- Parameters:
fraction (float)
message (str)
- Return type:
None
- path(name)[source]¶
A path inside this run’s output directory (parent dirs are created).
- Parameters:
name (str)
- Return type:
Path
- temp(name)[source]¶
A path in the run’s scratch folder, for large temporary files.
- Parameters:
name (str)
- Return type:
Path
- run_info()[source]¶
What is known about this run so far, as plain JSON: its id, start time and user, the environment (noodlelab, Python, platform, key library versions), and every node that finished before this one, with its settings and fingerprinted input files (see
noodlelab.core.provenance). Outside the editor andnoodlelab run(e.g. in an exported script) only the environment is known.- Return type:
dict[str, Any]
- noodlelab.as_file(value)[source]¶
A FileRef from a string or path (for node code also called from scripts).
- noodlelab.define_constant(name, value, unit='', *, uncertainty=0.0, title='', source='', symbol='', override=False)¶
Add a constant for every graph and script in this process:
define("g_local", 9.8123, "m/s^2", uncertainty=0.0005, source="survey 2024") define("rho_water", "998.2 kg/m^3", title="density of water at 20 °C")
Replacing a built-in constant warns, unless
override=Truesays it is meant.pi,tauandecannot be replaced.- Parameters:
name (str)
value (Any)
unit (str)
uncertainty (float)
title (str)
source (str)
symbol (str)
override (bool)
- Return type:
- noodlelab.downsample(array, max_side=256)[source]¶
Every n-th pixel so that neither of the first two axes exceeds
max_side. Cheap and shape-preserving; for a real overview read less data instead (e.g. rasterio’sout_shape).- Parameters:
array (Any)
max_side (int)
- Return type:
Any
- noodlelab.error(message)[source]¶
Return from a
.checkfunction to report an error (blocks running).- Parameters:
message (str)
- Return type:
- noodlelab.image_preview(array, *, max_side=256, summary=None)[source]¶
A PNG thumbnail from a NumPy array, with no imaging dependencies.
Accepts
(H, W)grayscale,(H, W, 3|4)RGB(A), and band-first(3|4, H, W)as rasterio returns it. Non-uint8 data is stretched between the 2nd and 98th percentiles; NaN and inf become black.- Parameters:
array (Any)
max_side (int)
summary (str | None)
- Return type:
- noodlelab.node(func: F) F[source]¶
- noodlelab.node(*, id: str | None = None, title: str | None = None, category: str = 'Utility', description: str | None = None, color: str | None = None, outputs: Sequence[str] | None = None, version: int = 1, cacheable: bool = True, checkpoint: bool = True, fold: bool = False, sample: bool | None = None, converter: bool | Literal['implicit'] = False, cost: float = 1.0, vectorized: bool = False, views: Sequence[Literal['processing', 'report']] | None = None) Callable[[F], F]
Mark a function as a node. Usable bare (
@node) or with options.version: bump it to invalidate cached results by hand (editing the function’s source already does this automatically).cacheable: set False when the output can change for the same inputs (unseeded randomness, clocks, hardware, files the node finds by itself). Files named byPath/FileRefinputs need no flag: their size and modification time are part of the cache key.checkpoint: set False to keep results in memory only, e.g. for huge intermediates that are quick to recompute.fold: the node is cheap and pure (a constant such as “File Path” or “Number”), so it is also evaluated while the graph is edited and downstream checks and probes receive its value as if it were typed in.sample: set False to never run the node on samples while editing (expensive work, side effects, statistics a sample would misstate). By default cacheable nodes without a RunContext preview on samples, except nodes that read files (give those a probe); True includes them anyway.converter: True makes the node a way to turn its first input’s type into its output’s type; the editor inserts it when such a link is drawn."implicit"(lossless, no settings) applies it on links, without a node.costranks converter chains (lower is preferred).vectorized: the function also works on many Monte Carlo trials at once: given arrays with one element per trial where it takes a number, it returns one element per trial, element by element (noifon a value), without side effects, and raises where it would raise for some trial alone. A Monte Carlo evaluation whose nodes are all vectorized runs its trials in batches; otherwise, or if a batch disagrees with trials run one at a time, trial by trial.views: the canvases whose add-node menus offer the node, “processing” and/or “report”; by default Report nodes are offered on the Reporting tab, Text nodes on both and all others on the Processing tab. Any node can be placed on either tab (the editor can be set to offer them all).The returned function gets decorators for edit-time helpers:
.check(live health checks),.probe(a quick preview and metadata, e.g. the first rows of a file),.options(input)(dynamic dropdown choices),.uses_constants(the named constants it reads, for its cache key) and, for converters,.accepts(which values the conversion fits).- Parameters:
func (Any)
options (Any)
- Return type:
Any
- noodlelab.register_codec(py_type, name, *, save, load, suffix='.bin')[source]¶
Store values of a type (or a qualified class name, for optional dependencies) in checkpoints with
save/loadinstead of pickle. Thenameis written into every checkpoint, so keep it stable.- Parameters:
py_type (type | str)
name (str)
save (Callable[[Any, IO[bytes]], dict[str, Any] | None])
load (Callable[[IO[bytes], dict[str, Any]], Any])
suffix (str)
- Return type:
Codec
- noodlelab.register_fields(py_type, *, names, type, get)[source]¶
Describe the fields of a type (or a qualified class name) for split outputs.
names(ref, meta): the field names, usually from the output’s meta (metamay be None).type(ref, name, meta): a field’s type hint, also asked without meta when a saved link is checked; returnAnywhen it cannot be known then.get(value, name): read a field of a real value.- Parameters:
py_type (type | str)
names (Callable[[TypeRef, dict[str, Any] | None], list[str]])
type (Callable[[TypeRef, str, dict[str, Any] | None], Any])
get (Callable[[Any, str], Any])
- Return type:
None
- noodlelab.register_meta(py_type)[source]¶
Register a function describing values of a type (or a qualified class name, for optional dependencies) as a small JSON dict.
- Parameters:
py_type (type | str)
- Return type:
Callable[[Callable[[Any], dict[str, Any] | None]], Callable[[Any], dict[str, Any] | None]]
- noodlelab.register_sampler(py_type)[source]¶
Register
fn(value, size) -> smaller value of the same typefor a type (or a qualified class name).sizeis roughly the number of rows or elements along each axis to keep.- Parameters:
py_type (type | str)
- Return type:
Callable[[Callable[[Any, int], Any]], Callable[[Any, int], Any]]
- noodlelab.register_type(py_type, name, color=None, description='')[source]¶
Map a Python type (or its qualified name) to a socket type.
- Parameters:
py_type (type | str)
name (str)
color (str | None)
description (str)
- Return type:
str
- noodlelab.register_unit(newtype, unit)[source]¶
Give a
NewTypea unit: its sockets then connect to every type of the same dimension, converted on the link. Returns the NewType.- Parameters:
newtype (Any)
unit (str)
- Return type:
Any
- noodlelab.table_preview(columns, rows, *, total_rows=None, summary=None)[source]¶
A table preview without pandas, e.g. from
csv.readeror a database cursor.- Parameters:
columns (list[Any])
rows (list[list[Any]])
total_rows (int | None)
summary (str | None)
- Return type:
- noodlelab.unit_type(name, unit, base=<class 'float'>)[source]¶
NewType(name, base)with a unit:Metres = unit_type("Metres", "m").- Parameters:
name (str)
unit (str)
base (Any)
- Return type:
Any
- noodlelab.warn(message)[source]¶
Warn about the result the running node is computing: a fit whose parameters the data cannot tell apart, a matrix close to singular… The run goes on; the warning is shown on the node and kept in the run’s record. Outside a run (plain Python) it is a Python
UserWarning.- Parameters:
message (str)
- Return type:
None