Statistical models, experiments, and calibration
These notations support assignment and declaration headers with identical meaning. Use plain, unqualified names. A parenthesized FIT header defines a reusable callable, also available in…
Statistical models, experiments, and calibration
These notations support assignment and declaration headers with identical meaning.
Use plain, unqualified names. A parenthesized FIT header defines a reusable
callable, also available in DO/function bodies. Other study blocks remain
top-level bindings. Blocks cannot be marked input, default, or output. Formatting preserves the authored header;
source-free rendering uses assignment spelling.
Statistical model formulas
input observations = {sales: [4, 5, 7, 8, 11, 13], price: [1, 2, 3, 4, 5, 6]}
demand = FIT ON observations
RESPONSE sales
PREDICTORS price
METHOD linear
MISSING error
END
forecast = MODEL.PREDICT(demand, {price: [7, 8]})
FIT demand ON observations is the alternative header. The bound value is a
reactive fitted statistical model. Changing its data recalculates the model and
its dependent predictions through the compiled model.
Data may be a column record, header matrix, native row-record vector, declared
Table or materialized model-local Frame. Selected numeric columns have equal
lengths; the shared input contract below applies to every estimator. Column names are exact,
case-sensitive keys, local to the dataset; they are not references to model
cells. PREDICTORS price, advertising, price*advertising adds three explicit
terms. price*price is a quadratic term. * means a product only: it does not
implicitly add main effects. Duplicate terms are rejected, including reversed
products. Fixed transformations and categorical contrasts are described below.
Arbitrary term functions, weights and model selection remain outside this profile.
RESPONSE, PREDICTORS, METHOD, and MISSING are required exactly once.
METHOD linear, logistic, and probit use the existing REGRESS, LOGIT.FIT,
and PROBIT.FIT estimators. INTERCEPT TRUE is the default; FALSE removes it.
An estimator's rank, convergence, response-domain and sample-size errors remain
errors. No fallback estimator is substituted.
MISSING error rejects missing values. MISSING drop removes training rows with
null/blank entries in selected columns, and returns their retained zero-based
rowIndices. Invalid numeric strings, booleans and nonfinite numbers are errors in both
policies; categorical columns accept their declared label forms. Prediction preserves input row positions, returning blank for a dropped
row, including a batch where every row is dropped. A missing required column or mismatched length among required columns is always an error.
The model exposes terms, ordered coefficients, namedCoefficients,
rowIndices, droppedRows, the validated spec, and the native estimator
report. The intercept label is (intercept); product labels use *.
MODEL.PREDICT(model, data) needs only predictor columns. Logistic/probit
predictions are probabilities. MODEL.FIT(data, spec) is the equivalent explicit
function; the spec contains response, nested-array terms, method, missing
and boolean intercept.
Bounds: 128 terms, eight factors per term, 100,000 rows, and 1,000,000 row/factor operations before constructing a design matrix. These are admission limits, not statistical guarantees.
Reusable FIT definitions
fit_demand = FIT(rows, confidence_level) ON rows
RESPONSE sales
PREDICTORS price
METHOD linear
CONFIDENCE confidence_level
MISSING error
END
baseline = fit_demand({sales: [1, 3, 5, 7], price: [0, 1, 2, 3]}, 0.95)
forecast = MODEL.PREDICT(baseline, {price: [4]})
FIT fit_demand(rows, confidence_level) ON rows is the equivalent declaration
header. Parentheses distinguish a callable from an immediately bound result;
FIT() ON fixed_rows is a zero-argument constructor. Defining or importing it
performs no fit or data read. A call fits its actual arguments through ordinary
reactive dependency tracking; subsequent field reads use that result.
Up to 64 unique parameters are inferred through the ordinary callable system.
Calls accept named arguments (0.95 AS confidence_level), aliases, partial
application and callable composition. USE can import and re-export these
constructors. A local definition in a DO block becomes a lexical callable; it
creates no global result cell. Callable values remain subject to Grid's ordinary
rules against escaping into cell data, records, collections or STATE.
All existing linear, transformed/category and nonlinear FIT clauses remain available. Data parameters accept the same columns, Table and model-local Frame inputs as other FIT calls. Numeric settings can use parameters. Column names remain dataset keys; nonlinear coefficient names must not duplicate constructor parameters. Portable nonlinear results keep their existing prediction program and can be transferred without the constructor or its training data.
Pass a declared Table at the call site, for example fit_demand(Observations, 0.95). Ordinary parameter forwarding, named arguments and partial application
retain its revision dependency. Automatic adaptation refuses a shared local
Table initializer or a helper that returns a captured Table: rewriting that
shared occurrence could change unrelated reads. Pass the Table directly or
provide an explicit Table/Frame handle in those cases.
Definitions can capture immutable constants and pure helper identities. Mutable
inputs, cells, defaults, STATE, declared Tables and rule-written values must be passed explicitly,
including dependencies hidden behind aliases or helper functions. Each actual
argument retains its ordinary reactive dependency. This capture restriction
applies to reusable constructors; existing name = FIT ON data blocks keep
their reactive behavior.
See the reusable SOLVE/FIT example and reusable optimization definitions.
Linear covariance and confidence
demand = FIT ON observations
RESPONSE sales
PREDICTORS price
METHOD linear
COVARIANCE cluster BY store
CONFIDENCE 0.90
MISSING drop
END
COVARIANCE classical (the default), hc1, cluster BY column, and
newey_west LAGS n select the existing REGRESS covariance implementations.
CONFIDENCE defaults to 0.95 and must evaluate to a finite number strictly between zero
and one. COVARIANCE applies to linear fits. Logistic/probit reject both clauses;
nonlinear FIT retains CONFIDENCE under its existing UNCERTAINTY contract. Covariance changes
inference, not the fitted coefficients. Native rank, degrees-of-freedom and
cluster-count refusals remain errors.
Cluster labels are nonempty strings of at most 128 bytes or finite numbers.
Numbers use REGRESS's text-label convention; numeric 1 and string "1" name
the same cluster. Null labels participate in the same MISSING row mask as the
response and predictors. Invalid labels fail even when another selected field
in that row is missing. A cluster column must have the same row count. Prediction
requires only predictor columns, never cluster labels or response values.
Newey–West uses retained row order; Grid does not infer or sort a time column.
LAGS evaluates to an integer in 0–100,000. The native estimator caps effective
lags at retained sample size minus one and reports requested/effective lags.
HAC covariance is additionally limited to 10,000,000 scalar score-product updates,
counting retained observations, fitted coefficients (including the intercept),
and effective lag pairs. Oversized requests fail before estimation; reduce rows,
predictors or lags.
For the explicit MODEL.FIT spec, optional fields are covariance, cluster,
lags, and confidence; cluster belongs only to "cluster", and lags only
to "newey_west". Existing five-field specs retain their defaults and shape.
See the complete clustered-fit example.
Nonlinear models
decay = FIT ON observations
RESPONSE response
PREDICTORS time
PARAMETER amplitude IN [0, 20] INITIAL 8
PARAMETER rate IN [0.01, 2] INITIAL 0.3
PREDICT amplitude * EXP(-rate * time)
METHOD least_squares
MISSING error
UNCERTAINTY local_iid
CONFIDENCE 0.95
END
forecast = MODEL.PREDICT(decay, {time: [6, 7]})
FIT decay ON observations is equivalent. Each PARAMETER names a coefficient
local to the formula. INITIAL is required; optional finite IN [lower, upper]
bounds may be equal to fix a coefficient. Multiple plain PREDICTORS columns
are supported. Put transformations and interactions in PREDICT. Formula names
may be declared predictors, parameters, lexical LET locals or immutable closed
scalar captures. Ordinary numeric helpers, including import aliases and imported private
helper dependencies, use their defining lexical scope. Arithmetic, comparison,
IF, boolean operators and common pure elementary numeric functions are supported.
Callable aliases retain ordinary expression/import scope rules; a cell cannot store
a function.
Mutable captures (including aliases to inputs and rule-written values), nested lambdas, unresolved higher-order calls, recursion, arrays/records and effectful calls are refused. Pass a changing quantity through a declared predictor or coefficient. Constructor settings keep their surrounding-scope rules.
saturation(amount, maximum, half) = LET(fraction, amount / (half + amount), maximum * fraction)
fit_kinetics = FIT(rows) ON rows
RESPONSE measured_rate
PREDICTORS concentration
PARAMETER maximum IN [0.1, 10] INITIAL 1
PARAMETER half IN [0.01, 10] INITIAL 0.5
PREDICT saturation(concentration, maximum, half)
METHOD least_squares
MISSING error
END
The complete example uses
this same helper in a reaction. Numeric LET initializers see preceding bindings
and outer locals; nested bindings can shadow names. Computed helper arguments
are evaluated once. Lazy IF and unused native error values keep ordinary compiled
semantics. Helpers admit at most 64 arguments and 32 nested calls, within the
4,096-work/depth-64 formula budget. Imports retain ordinary USE capture
admission: checked immutable scalar initializers remain in the defining module's
scope. A consumer cannot replace them with same-named locals or values. Reusable
FIT definitions can also carry their checked immutable helper closures.
The compiler emits an ordinary compiled lambda and the existing CURVE_FIT.REPORT
operation. Unbounded fits use its existing least-squares algorithm; bounds,
weights and uncertainty select its existing corresponding implementations.
ITERATIONS defaults to 200 (1–10,000); TOLERANCE defaults to 1e-10 and
must be positive and finite. There are at most 64 coefficients, 128 predictors,
100,000 rows and 1,000,000 selected data elements. Kernel work limits may be tighter.
STANDARD_DEVIATION sigma supplies positive known observation standard deviations.
The objective is the sum of ((predicted - observed) / standardDeviation)^2.
Optional UNCERTAINTY local_iid assumes equal independent observation variance;
local_known requires STANDARD_DEVIATION. Local intervals inherit the native
rank, active-bound and degrees-of-freedom checks; an unavailable interval is not
silently replaced by another method. UNCERTAINTY bootstrap_normal SAMPLES 1000 SEED 42 selects the native parametric normal bootstrap; the seed is required,
with 100–5,000 samples (default 1,000). The native estimator requires at least
10 expected draws in each confidence tail: samples * (1 - confidence) / 2 >= 10.
For 95% confidence, use at least 400 samples. CONFIDENCE 0.95 requires an uncertainty method.
The result is a grid.statistical-model.curve.v2 record with terms (coefficient
names), coefficients, namedCoefficients, rowIndices, droppedRows, the
native report, and a self-contained prediction program. MODEL.PREDICT
accepts the fitted value through function parameters, records, conditional
selection, or a JSON round trip into another model. It needs no original FIT
binding, source text, training data, or resident model handle.
predict_with(fitted, rows) = MODEL.PREDICT(fitted, rows)
saved = {model: decay}
forecast = predict_with(saved.model, {time: [6, 7]})
The portable model example shows this composition. Saving/loading uses ordinary value transport or storage; reading or predicting from a model starts no job. Fitting stays reactive; an exported value is a snapshot of that fitted result. Replacing an imported value or its prediction data recalculates dependent predictions normally.
The prediction program retains ordered predictor/parameter names and the missing policy. Existing formulas emit program version 1; lexical bindings use version 2 with private local slots and ordered initializers. Import checks fields, names, coefficients, supported pure operations, backward operand references, and expression budgets before executing. It prepares ordinary native compiled code once per prediction batch, using the existing numeric specialization for admitted polynomials and the same general compiled lambda fallback for other formulas. Solver-only intermediate-number conventions do not change prediction semantics. Limits are 4,096 expanded expression nodes, depth 64, and 10,000,000 expanded node visits per batch, alongside the shared row/data limits. Missing prediction rows remain blank; mathematical domain errors retain native row error values. Diagnostic reports are not traversed to predict.
This is a bounded numerical model value. It does not make general closures,
outer mutable bindings, arbitrary user-defined functions, effects, or host handles portable.
Admitted numerical helper bodies become closed expressions in the fitted value;
prediction does not look up or execute helper source. Program-v2 values require a
consumer supporting numerical program version 2; older consumers reject that nested version explicitly.
String-concatenating formulas and incomplete builtin calls are refused; use a
numeric formula so prediction does not depend on text or blank coercion settings.
Linear/logistic/probit values retain their existing prediction contracts. Old
grid.statistical-model.curve.v1 records remain inspectable but lack a formula;
refit their original definition to obtain a portable value. Previously compiled
models with declaration-expanded predictions retains its existing execution.
MODEL.COLUMNS(data, columns, missing[, keepRows]) is the shared strict column
preparation primitive. It returns selected columns, one-based row indices,
zero-based source rowIndices, row valid flags and droppedRows/totalRows.
keepRows = TRUE keeps missing row positions for prediction. Booleans and strings
are never silently converted to observations.
Experiments and parameter sweeps
input growth = 0.04
revenue = 100 * (1 + growth)
sensitivity = EXPERIMENT
VARY growth OVER [0.02, 0.04, 0.06]
SIMULATE FROM 0 TO 4 STEP 1
OBSERVE revenue
RESULTS FRAME sensitivity_results
END
EXPERIMENT sensitivity is the alternative header. The value is an inert
grid.experiment.definition.v1 record containing the existing ExperimentSpec.
Multiple VARY clauses form a Cartesian product in declaration order by default.
DESIGN zip pairs equally positioned values; DESIGN cartesian makes the default explicit. In the simulation profile, parameters
must name model inputs; observations name model outputs. Targets are binding
references in the owning AST, so a checked rename updates them.
Reading the record does not create jobs or Frames. Explicitly call the existing
Host createExperiment(modelId, "sensitivity") operation, or the embedded host's
create_experiment_from_definition(model, "sensitivity"). The typed client
accepts the same definition name; HTTP callers POST a JSON string to /api/models/:id/experiments. The operation also continues to accept
the existing structured request. It resolves the definition,
checks bindings, then uses the established snapshot, job, cancellation, result
Frame and replay owners. Native-document createExperiment accepts the same
name under its existing document ownership checks.
Sweep values and simulation settings can be expressions evaluating to finite numbers. SIMULATE
requires a positive step, an ordered interval, and an exact integral step count;
both endpoints are included. Models may be algebraic: state cells are not required.
SIM_TIME() and SIM_STEP() use the requested clock even without state.
Up to 64 parameters, 128 distinct observations,
10,000 matrix rows and 10,000 simulation steps are accepted by the notation.
Existing Host capacity and operation budgets may reject a smaller request.
In the simulation profile, RESULTS FRAME and SIMULATE are required exactly once; one or more OBSERVE clauses must select at least one output. A definition
without VARY or SCENARIO runs one baseline setting. Without REPLICATE, the
existing deterministic simulation path applies.
Evaluating reusable calculations and schedules
Use EVALUATE to compare pure calculations without introducing model-global
inputs or an artificial simulation clock:
squared(value) = value * value
study = EXPERIMENT
VARY amount OVER [2, 3]
EVALUATE calculation = squared(amount)
OBSERVE value = calculation + 1
RESULTS FRAME cases
END
Here amount and calculation are local to the study. Each case runs in an
isolated snapshot through the existing LIR runtime. Targets run in declaration
order and can use earlier target results. Reading study creates neither a job
nor a Frame; run it with the same explicit experiment operation described above.
Immutable imported helpers are supported. Mutable outer inputs, volatile calls,
effects and unsealed callable execution are refused.
For a configured PLAN value, use SCHEDULE schedule = fabricate(capacity) in
place of EVALUATE. This performs native Planning search inside the case and
lets observations read schedule.objective.makespan or schedule.status.
It does not publish an intermediate schedule or start a child job. See the
complete capacity sweep.
The evaluation profile accepts VARY ... OVER, Cartesian or zip designs,
1–32 ordered targets, and 1–128 observations. Put axes before targets and targets
before observations. Observations must be finite numbers, booleans, bounded text
or null; structured values and inconsistent column types are rejected. Use
RESULTS FRAME once. Simulation clauses remain excluded from the evaluation profile. Random and Latin hypercube sampled designs are admitted as described below. Each case inherits cancellation
and a ten-second deadline; each target/result is limited to 8 MiB.
Sampled pure evaluation
study = EXPERIMENT
DESIGN random COUNT 8 SEED 42
VARY quantity IN [10, 20]
VARY unit_price IN [2, 5]
EVALUATE quote = quantity * unit_price
OBSERVE total = quote
RESULTS FRAME sampled_quotes
END
Every sampled axis must use finite, strictly ordered IN [lower, upper]
bounds; do not mix sampled bounds and enumerated OVER axes. COUNT selects
1–10,000 cases, not cases per axis. Bounds, count and seed may be expressions,
subject to existing study settings admission. Axes remain local to the study.
The existing retained sampler prepares the parameter matrix once using its
seeded SplitMix64 sequence in authored axis order. Reordering axes can change
which draws they receive. Evaluation uses the existing isolated LIR case target;
reading a definition remains inert.
Use DESIGN latin_hypercube COUNT 8 SEED 42 to divide each axis into eight
equal-width strata and draw once in each stratum, with an independent seeded
permutation per axis. This is eight total cases, not an eight-by-eight Cartesian
product. The same ordered axes and seed reproduce the same sample matrix;
reordering axes can change their draws. This guarantees marginal stratification,
not all multidimensional combinations or independent replications. Replicated designs are described below.
See random evaluation and Latin hypercube evaluation. All four expanded compiler checks pass for both sampled designs, including owning/format admission, descriptor axis order, refusals and Cartesian/zip compatibility. The compiler-only run reused existing cached dependencies; no Host runtime build was performed. The new retained Host acceptance (numeric outputs, Frame and reopen) is authored but not yet run; this compiler increment does not claim fresh end-to-end runtime validation. No sampling algorithm or ordinary evaluation path is changed.
Scenario-local evaluation inputs
study = EXPERIMENT
VARY quantity OVER [10, 20]
SCENARIO baseline WITH unit_price = 2, fee = 1
SCENARIO stress WITH unit_price = 5, fee = 3
EVALUATE quote = quantity * unit_price + fee
OBSERVE total = quote
RESULTS FRAME quotes
END
Each axis combination runs once per scenario. Scenario inputs are local to the study, distinct from sweep axes, and every scenario must explicitly supply the same keys. Input order may differ. Declare scenarios before targets; names may not collide with targets or observations. At most 64 combined axis/scenario inputs and 256 scenarios are admitted, within the existing 10,000-case limit. The scenario label and original definition retain the scenario values; result parameter columns continue to describe sweep axes. No default or missing-value fill is inferred. Random and Latin hypercube designs share their sampled axis rows across scenarios, enabling paired comparisons.
Seven focused compiler/input-contract tests pass for this increment. The existing isolated evaluator receives the complete axis-plus-scenario case. Neither definition reading nor case execution mutates caller inputs. This new Host integration has an authored retained/reopen acceptance test; execution of that test remains outstanding under the no-full-native-build constraint. See scenario evaluation.
Replicated evaluation designs
study = EXPERIMENT
DESIGN latin_hypercube COUNT 4 SEED 42
VARY quantity IN [10, 20]
SCENARIO baseline WITH unit_price = 2
SCENARIO stress WITH unit_price = 5
REPLICATE 3 SEED 7
EVALUATE quote = quantity * unit_price
OBSERVE total = quote
RESULTS FRAME replicated_quotes
END
This produces 24 rows: three replicas × four sampled points × two scenarios. Each replica independently stratifies each axis, and its realized points are shared across scenarios. The replication seed drives the existing SplitMix64 stream; each emitted 64-bit word XOR the design seed selects that replica's sample matrix. Retaining both seeds and axis order reproduces the design. Without REPLICATE, the original design-seed sequence is unchanged.
Rows have stable replica-major, point-major, then scenario-major ordinals in their
run IDs. Replica boundaries follow the admitted design size and scenario count;
scenario labels and parameter columns retain their existing meanings. Every row
retains its actual inputs and scalar outcome. No means, uncertainty bands or
independence claims are inferred from duplicate rows. Cartesian and zip designs
repeat their deterministic cases; REPLICATE does not make a pure expression
random or permit RAND() in the target. Replication seeds do not alter enumerated
inputs. A seed change affects sampled designs only.
The existing 10,000-case limit includes all replicas and scenarios and is checked before expansion. REPLICATE count and seed accept the existing expression-valued settings. Invalid counts, overflow and incompatible simulation options refuse. See replicated evaluation. Retained Host execution and replay validation for this new combination remain outstanding; focused sampler tests do not establish that integration.
Comparison recipes using existing calls
The canonical policy comparison
imports shipping_policy.gs as a source-library
asset and calls shipping_table, a native HIT UNIQUE decision, inside
EVALUATE. It compares weights 2 and 8, yielding price_final values 5 and 10.
A second target calls shipping_table.REPORT; observations retain the selected
matched_rule (light or heavy), matching_complete, price and service.
The ordinary shipping helper remains available to existing callers.
DECISION's FIRST, UNIQUE, ANY and COLLECT policies, including row patterns, keep their ordinary callable meaning inside studies. Declaring or reading a study remains inert. The existing explicit experiment operation selects isolated cases and retains scalar result columns. Pass changing policy inputs as explicit parameters; mutable outer captures and effects remain refused. Definition edits require recompiling/readmitting the source; saved studies retain their admitted source/LIR identity rather than silently adopting a new library. Scenario values can feed the same explicit decision parameters.
A failed value call fails that case: a conflict is not a zero or a successful
quote. To compare refusals as data, use .REPORT and explicitly project its
outcome, for example MATCH(report.outcome, ERR(detail) -> detail.code, _ -> "ok"). This retains the distinction between DECISION_CONFLICT,
DECISION_NO_MATCH, and evaluation errors. REPORT values remain ordinary
bounded native records; scalar study columns require explicit projections of
nested collections or records. No arbitrary nested report is silently turned
into a scalar column. FIRST's matching_complete can be false after its first
match; it does not claim exhaustive matching.
The native workflow is covered through direct and owning-source admission, case isolation, cancellation, policy errors and stripped-LIR reopen. Existing Host job/storage/Frame ownership is reused unchanged; the F8 increment does not claim a fresh full-Host restart or UI run. This is native DECISION composition, not DMN/FEEL or BPMN compatibility.
The prediction comparison
fits y = 1 + 2*x once and projects predictions at study-local inputs 4 and 5.
MODEL.PREDICT returns an array; INDEX(prediction, 1) selects the scalar observed
column, giving 9 and 11. The schedule comparison
uses SCHEDULE to compare resource capacities, giving makespans 6 and 4.
All three recipes use the existing explicit experiment operation to retain a job and result Frame. Resolving the definition does not submit it. Source-library files must be supplied to the model's import resolver; these new modules are repository assets, not compiler-embedded modules or Registry publications. These examples use enumerated axes and scalar observations; structured observation columns remain outside this profile.
Calibrating an explicit trajectory
A direct calibration has local parameters and a returned trajectory with its own clock:
estimate = CALIBRATION ON observations
ADJUST rate IN [0.01, 2] INITIAL 0.2
EVALUATE run = REACTION_SOLUTION(conversion, 0, 2,
{parameters: {k: rate}, samples: 5})
FIT run.series.product AT run.time TO measured AT time
ALIGN exact
LOSS squared_error
END
run.time supplies predicted sample times; time selects observation times
from observations. Both axes are explicit. Returned times must be finite and
strictly increasing, with the same length as their predicted values. ALIGN linear interpolates between returned samples; neither alignment extrapolates.
The existing per-target standard deviations, missing-data policy, robust losses
and multiple starts also apply. See the
complete reaction calibration.
Use the existing explicit calibration operation to run the definition. Candidate parameters remain local, source state is unchanged, and retained results include aligned residuals. Direct calibration has a thirty-second operation deadline, at most 100,000 candidate evaluations and 1,000,000 total returned vector elements. A returned vector may contain at most 10,001 samples. Invalid clocks or predictions cannot produce a successful fit.
Scenarios, ranges and seeded replications
outlook = EXPERIMENT
VARY growth FROM 1 TO 3 COUNT 3
SCENARIO baseline WITH starting = 100, noise_scale = 1
SCENARIO stress WITH starting = 80, noise_scale = 2
SIMULATE FROM 0 TO 4 STEP 1
REPLICATE 32 SEED 42
REDRAW per_step
QUANTILES [0.05, 0.5, 0.95]
OBSERVE population
RESULTS FRAME outlook_results
END
The complete scenario example
defines population with STOCK and a native NORMAL uncertainty source.
VARY input FROM first TO last COUNT nuses the existing evenly spaced range expansion. Both endpoints are included whenn > 1; descending ranges are supported.COUNT 1usesfirst. Counts evaluate to whole numbers from 1 to 10,000.SCENARIO name WITH input = value, input = valuegroups finite numeric overrides. A comma permits continuation on the next line.SCENARIO namealone names a baseline with no overrides. Scenarios combine with every sweep tuple, in declaration order; none is implicitly added when scenarios are authored. At most 256 distinct scenario names and 256 overrides per scenario are accepted. All overridden names must be declared model inputs. A scenario cannot override a swept input or repeat an input. The combined matrix remains limited to 10,000 rows. Scenario-only comparisons are supported.REPLICATE n SEED seedselects 1–100,000 stochastic simulation samples for each matrix row. The seed is required. Numeric seeds must be exact integers from zero to 9,007,199,254,740,991; canonical decimal strings support the entire u64 range, for exampleSEED "18446744073709551615". Seeds travel through the compiled model as strings without rounding. This controls the existing simulation uncertainty sources; it does not turn arbitrary external or volatile calls into seeded random generators.REDRAW per_runis the default: each replication keeps its uncertainty draw for the whole trajectory.REDRAW per_stepdraws again at each time point. Matrix rows use the same base seed and corresponding draws, allowing paired comparisons of settings. Changing the seed changes those draws. Both modes use the existing native seed derivation and replay owner.QUANTILES [q, ...]selects final-time distribution summaries, with values in[0, 1]. It requiresREPLICATE, as doesREDRAW. Defaults are 0.05, 0.25, 0.5, 0.75 and 0.95. At most 101 values are accepted; values must remain distinct after rounding to integer percentages because Frame columns use names such aspopulation_p05,population_p50, andpopulation_p95.
Result Frames retain run_id, scenario, sweep parameter columns, and each
output's final, min, max, and mean, plus the requested quantiles. For
replications, the main trajectory is the pointwise sample mean; min, max,
and mean summarize that trajectory over time. Quantiles describe the sample
population at the final time. They are not confidence intervals for the mean.
Retained native output and verified replay expose the full time-varying bands.
Scenario overrides and sweep values apply only to isolated runs; live model
inputs remain unchanged. Existing Host work, storage, timeout and cancellation
budgets still apply. Other existing experiment engines retain their structured
APIs. Random and Latin-hypercube designs are described below; adaptive searches
remain future work.
Reactive settings, paired axes, and named outputs
paired = EXPERIMENT
DESIGN zip
VARY growth OVER growth_choices
VARY starting OVER starting_choices
SIMULATE FROM 0 TO horizon STEP increment
INTEGRATOR rk4
TIME_UNIT "days"
SENSITIVITY growth
OBSERVE population
OBSERVE gain = population - starting
RESULTS FRAME paired_results
END
The complete paired example defines these inputs. Zip axes must have equal nonzero lengths. There is no truncation or broadcasting; scenarios still cross every paired tuple. No axes means one tuple. The existing 10,000-row limit applies after scenarios.
OBSERVE name = expression declares an ordinary model calculation. It is visible
outside the study and cannot collide with an existing input, calculation, function,
or another named observation. Use a distinctive name when multiple studies share
one model. The expression evaluates in each isolated simulation; its name is the
result/Frame column prefix and is retained for replay. Multiple OBSERVE clauses
append outputs, with duplicates refused. Calibration also accepts observations:
they are retained as output trajectories after fitting, without adding to the
objective. Calibration retains at most 16 distinct outputs including trajectory
FIT targets; experiments allow 128. All names participate in checked renaming.
Numeric settings use surrounding Grid expressions: confidence, covariance lags,
parameter bounds and initial values, iteration budgets, tolerances, clocks, range
endpoints/counts, scenario overrides, replication counts, and quantiles. IN
keeps its explicit [lower expression, upper expression] structure. VARY OVER
and QUANTILES also accept a computed numeric vector or one-row/one-column array;
matrices are refused. Numeric settings do not coerce strings or booleans. Known
invalid literals fail at compilation; dynamic errors fail when the FIT resolves
or before a retained job is created. Existing finite, integral and work limits
still apply.
FIT remains reactive. Nonlinear setting expressions resolve in the surrounding
model, including INITIAL amplitude when amplitude is also a local coefficient;
only PREDICT resolves coefficient and predictor locals. See
configured FIT.
Experiment/calibration settings are captured when the definition is explicitly
submitted. Later model edits affect a new submission; retained runs keep their
snapshot and settings. Reading a definition still starts no work.
Native simulation controls
Both experiment and calibration accept INTEGRATOR euler (default) or rk4, and
TIME_UNIT expression for bounded nonempty text. Calibration requires SIMULATE
when these options are supplied. These select the existing simulation settings;
they add no integrator or time-unit conversion. See
configured calibration.
Experiments additionally expose:
SENSITIVITY input, ...selects up to 64 distinct declared numeric inputs for the existing deterministic trajectory sensitivity calculation. It cannot be combined withREPLICATE.ENGINE autoormonte_carlo, andRETAIN_SAMPLES booleanExpression, requireREPLICATE. Retained sample trajectories are available through native replay.CORRELATE leftSource WITH rightSource = rhoExpressionselects existing uncertainty-source correlations, with finite rho in [-1, 1]. Self/duplicate pairs are refused; at most 256 pairs are accepted. Native source and correlation matrix validation still applies. This also requiresREPLICATE.
See correlated samples. Simulation engine/integrator names are literal choices. Unsupported analytic ensembles and time correlation remain unsupported; their behavior is not approximated. Native sensitivity and uncertainty result structures remain the structured API/replay outputs.
Model calibration
input growth = 0.04
predicted_revenue = 100 * (1 + growth)
observed_revenue = 110
estimate_growth = CALIBRATION
ADJUST growth IN [0, 0.2] INITIAL 0.04
FIT predicted_revenue TO observed_revenue
LOSS squared_error
ITERATIONS 100
TOLERANCE 0.0000001
END
CALIBRATION estimate_growth is the alternative header. The inert
grid.calibration.definition.v1 record contains a CalibrationRunRequest.
ADJUST requires finite, strictly ordered bounds. INITIAL is optional: omit
it to use the input’s current value when the definition is read for execution.
An explicit initial value must lie within the bounds; the Host checks the
captured value against the same bounds before optimizing. The compiler requires each parameter to be a declared model input; explicit
operation ingress resolves it against the resident compiled model. Multiple FIT clauses sum the selected residual loss; each pair can be a numeric
scalar or equal-shaped numeric arrays. Missing or invalid values fail.
The compiler emits an ordinary compiled loss cell named
__calibration_estimate_growth_loss. That generated name is reserved by this
block; a conflicting declaration is rejected. MODEL.LOSS(predicted, observed)
is the strict numeric squared-error primitive. No calibration evaluator is added.
Explicitly call createCalibrationRun(modelId, "estimate_growth"), or the embedded host's
create_calibration_from_definition(model, "estimate_growth"). The typed client
accepts the same name; HTTP callers POST a JSON string to
/api/models/:id/calibration/runs. Existing retained
runs supply progress, cancellation, immutable execution context, result and
trace retention, and explicit evidence publication. Reading the definition does
not optimize; completing a run does not write fitted values back to model inputs.
Optional SIMULATE FROM 0 TO 52 STEP 1 uses the existing simulation runner.
The objective is the loss cell at the final simulated step. Without this
clause, calibration evaluates the initial point (zero steps). This is not an
implicit sum over a time series: represent an observed series as equal-shaped
arrays, or author a cumulative loss explicitly in the model.
Bounds: 32 adjusted inputs, 16 FIT clauses, 10,000 iterations, positive finite
tolerance, and the same simulation interval limits. Defaults are 100 iterations
and tolerance 1e-7. The existing Host evaluation, timeout and lifecycle budgets
still apply. FIT … TO … supports squared error, Huber and soft-L1 losses.
The existing Nelder–Mead solver confirms its objective-spread tolerance across
two successive iterations, so a single pair of equal-cost trial points does not
prematurely end the search. This remains a local numerical search.
Custom calibration objectives
estimate = CALIBRATE
ADJUST growth IN [0, 5] INITIAL 1
MINIMIZE validation_error + complexity_penalty
ITERATIONS 150
TOLERANCE 0.00000001
END
MINIMIZE expression replaces all FIT and LOSS clauses. It cannot be combined
with them or with ON observations. It is an ordinary Grid expression referencing
model bindings, including reusable formulas and functions. Checked binding renames
update its references. The existing compiled loss cell and retained calibration runner
handle evaluation, bounds, progress, cancellation, failures and results.
The evaluated objective must be one finite numeric scalar; arrays, blanks,
nonfinite values and formula errors make that candidate evaluation invalid. The
existing optimizer retains the best finite candidate; a run with no finite
evaluations fails with CALIBRATE_NO_FINITE_EVALUATIONS. Negative objective values are
valid. SIMULATE retains the same final-step meaning, and omission evaluates the
initial point. Penalties and validation scores can be authored in the
model; this syntax adds no implicit time aggregation or global-optimum guarantee.
Completing a run does not overwrite model inputs. See the complete
custom-objective example.
Trajectory calibration
input growth = 1
population = growth * SIM_TIME()
observations = {time: [0, 0.5, 1.5, 3], measured: [0, 1, 3, 6], sigma: [1, 1, 1, 1]}
estimate = CALIBRATION ON observations
ADJUST growth IN [0, 4] INITIAL 1
FIT population AT time TO measured
SIMULATE FROM 0 TO 3 STEP 1
ALIGN linear
STANDARD_DEVIATION sigma
MISSING error
LOSS squared_error
END
CALIBRATION estimate ON observations and the shorter CALIBRATE spelling
are equivalent. Each FIT output AT timeColumn TO observedColumn joins a
simulated scalar output to observed rows. Multiple targets can use different
time and observed columns. Alignment defaults to exact: times must match
simulation grid points, allowing only floating-point rounding tolerance.
ALIGN linear interpolates between grid points. Neither mode extrapolates.
Simulation endpoints are included. Observation order and repeated times are
preserved; each row contributes separately.
A block-level STANDARD_DEVIATION sigma supplies a default positive error column.
Each target can override it on the same line:
FIT population AT time TO people STANDARD_DEVIATION population_sigma
FIT revenue AT time TO money STANDARD_DEVIATION revenue_sigma
Without either setting, that target uses unit standard deviations. Each target's
missing-row mask includes its own error column; a missing value in another target
has no effect. Retained deviations must be positive; nonfinite entries are
errors even under drop. The override is explicit and independent of clause
order. See the complete weighted trajectory example.
MISSING error is the default; drop removes incomplete rows separately per
target and retains original row indices. The native objective sums
((predicted - observed) / standardDeviation)^2 over every retained observation
and target. Bounds are 16 targets and 100,000 total observations, with the existing
simulation and calibration operation budgets. Retained runs additionally cap
estimated per-observation residual storage at 4 MiB before creating a job. Observations and alignment are
validated before creating the retained job.
The definition remains inert and uses the same explicit Host submission API.
Each candidate is evaluated by the existing compiled simulation runner. Completion
adds trajectoryResiduals to the calibration result: symbol, original row index,
time, observed value, aligned prediction, raw residual and standard deviation.
The retained simulation outputs still show the winning candidate's full grid.
Inputs are not overwritten. This form needs SIMULATE (structured requests must supply t0, dt and
steps explicitly); the original form without
ON observations continues to minimize its loss at the final simulated step.
Transformed predictors and categories
Linear, logistic and probit FIT accept fixed feature encoders directly in
PREDICTORS. They are dataset-local terms, not calls that capture model globals:
response_model = FIT ON observations
RESPONSE outcome
PREDICTORS LN(income), age^2, CATEGORY(region, "west"), age*CATEGORY(region, "west")
METHOD linear
MISSING drop
END
LN(column), LOG10(column), SQRT(column), ABS(column), EXP(column), and
POWER(column, exponent) produce one numeric term. column^exponent is the same
power term. Exponents are finite numeric literals. CENTER(column) subtracts the
complete-training-row mean; STANDARDIZE(column) also divides by the population
standard deviation. Prediction retains these learned values. A zero standard
deviation, transform domain error or overflow on a retained row is an error even
with MISSING drop.
Only blank/null values are missing. Nested transform calls, arbitrary formulas,
model references and user-defined functions are outside this fixed encoder grammar;
prepare a derived column or use nonlinear FIT's PREDICT for a general formula.
The response remains a named numeric column in its original units.
CATEGORY(column[, reference]) accepts uniformly text or finite numeric labels.
With an intercept it emits treatment contrasts: one indicator per retained level
other than the reference. The optional reference is a nonempty text or finite
numeric literal and must occur among complete training rows. Otherwise the first
sorted retained level is the reference (numeric ascending or text lexicographic).
With INTERCEPT false, all levels are encoded and an explicit reference is refused.
Levels are learned after the shared missing-row mask, never from dropped rows.
Each category allows at most 128 levels; the expanded design allows at most 128
terms and the existing one-million-operation design budget. Products expand the
cross-product of factor columns and add no implicit main effects.
The fitted value uses grid.statistical-model.v2 and includes a portable design
with learned levels, reference, centers and scales. terms, coefficients and
namedCoefficients follow the expanded order. MODEL.PREDICT reuses that design
without relearning from prediction rows; unseen levels are errors. Existing numeric
fits retain the v1 value and prediction contract. Covariance options apply to the
expanded design through the same native REGRESS engine. Nonlinear FIT continues to
list plain predictor columns and put formulas in PREDICT.
The direct MODEL.FIT specification retains terms as column-name products and
may include a matching features array. Each factor is a record with column and
transform; power takes its numeric power field, and category optionally takes
reference. For example terms: [["income"]], features: [[{column: "income", transform: "ln"}]]. The fitted design is output metadata, not an input to fitting.
Shared study data input
FIT ON data, MODEL.FIT, MODEL.PREDICT and MODEL.COLUMNS share named-column
admission. This also supplies nonlinear FIT and trajectory CALIBRATION. Accepted
carriers are column records, matrices with a distinct text header row, native
vectors of row records, declared model Tables, and materialized model-local Frames.
The shared path retains the source row order and existing zero-based rowIndices;
missing-row masks and strict numeric validation are identical across carriers.
Selected column names must be ASCII identifiers of at most 128 bytes; columns
must exist and have equal lengths. Reads are complete or fail;
there is no silent truncation. The limits are 100,000 rows and one million selected
cells before estimation, with additional design expansion limits described above.
table Observations = {schema: "Observations"}
response_model = FIT ON Observations
RESPONSE outcome
PREDICTORS exposure^2, CATEGORY(region, "west")
METHOD linear
MISSING error
END
Declared Tables use the existing Table revision dependency, so committed mutations invalidate the study. For a Table handle supplied as an input value, refresh that input with the current handle/revision after a mutation; direct declared Table references track revisions automatically. Table order is not a statistical ordering guarantee: use an explicitly ordered relation for order-sensitive analysis such as Newey–West. A Frame input is an already materialized native handle supplied by the model's existing ETL or result owner. Reading it does not start an ETL job. Lazy, missing, foreign-model and unreadable Frames fail; no weaker read path is substituted. Changing the input handle causes the normal reactive recomputation. A bare compiled-model session without a host Table/Frame provider still accepts in-memory carriers and rejects resident handles explicitly.
Robust losses and multiple calibration starts
estimate = CALIBRATE ON observations
ADJUST level IN [0, 40] INITIAL 0
FIT prediction AT time TO measured STANDARD_DEVIATION sigma
SIMULATE FROM 0 TO 3 STEP 1
LOSS huber SCALE loss_scale
STARTS start_count SEED 42
ITERATIONS 100
END
LOSS squared_error preserves the existing sum of squared residuals. LOSS huber
and LOSS soft_l1 reduce the effect of large residuals; both accept an optional
positive finite SCALE expression (default 1). The scale is the transition size,
not an estimated noise parameter. For scalar/array FIT predicted TO observed,
r = predicted - observed. For trajectory targets, r is that residual divided
by the target's standard deviation (default 1). With s = SCALE:
| Loss | Contribution of one residual |
|---|---|
squared_error |
r² |
huber |
r² when abs(r) <= s; otherwise 2*s*abs(r) - s² |
soft_l1 |
2*s²*(sqrt(1 + (r/s)²) - 1) |
These conventions match squared error near zero. Contributions are summed, not
averaged. For scalar/array FIT, SCALE is part of the generated compiled loss formula
and evaluates against each candidate, like the predicted and observed expressions.
For trajectory FIT it is captured alongside the observation settings. Use a scale
independent of ADJUST inputs when it should remain fixed throughout calibration.
SCALE on squared error is refused. Missing/nonfinite values retain existing errors. MODEL.LOSS(predicted, observed, "huber", scale) exposes the same
strict scalar/array primitive to custom objectives; its last two arguments are
optional. A MINIMIZE objective cannot also carry LOSS or FIT clauses.
The frozen V1 extension function catalog keeps its existing two-argument
MODEL.LOSS contract; the robust arguments are available in Grid source and compiled models.
STARTS count SEED seed accepts 1..64 starts. The first uses authored INITIAL
values; subsequent starts sample independently and uniformly inside each parameter's
bounds, using the fixed retained seed. Settings can be model expressions and are
captured at submission. Seeds use the same exact integer/decimal-string rules as
REPLICATE. Each start receives ITERATIONS; the host's composed work budget
includes starts × iterations × simulation points. All starts run sequentially
through the existing compiled simulation and bounded Nelder–Mead solver, sharing
cancellation, progress, one bounded trace and one overall best finite result.
Equal best losses retain the earliest candidate. A start with no finite evaluations
does not displace a successful start; all-invalid execution fails.
For multiple starts, results include zero-based bestStart and starts, with
initial points, evaluation ranges, iteration counts, best parameters and losses.
An invalid start has null best parameters/loss. Counts in the overall result are
totals across starts. A single start preserves the existing result shape. This
improves exploration but does not guarantee a global optimum or estimate uncertainty.
See robust calibration.
Sampled experiment designs
coverage = EXPERIMENT
DESIGN latin_hypercube COUNT design_size SEED 42
VARY growth IN [0, 4]
VARY starting IN [10, 20]
SIMULATE FROM 0 TO 3 STEP 1
OBSERVE population
RESULTS FRAME coverage_results
END
DESIGN random COUNT count SEED seed samples independent uniform points.
DESIGN latin_hypercube COUNT count SEED seed divides each input interval into
count equally sized strata, independently shuffles each axis and samples once
within each stratum. Both require one or more VARY input IN [lower, upper]
clauses with finite strictly increasing bounds. Discrete OVER axes and counted
FROM ... TO ... COUNT grids remain Cartesian/zip forms and cannot be mixed with
a sampled design. Bound endpoints, count and seed may be model expressions.
COUNT is the number of parameter points, not points per axis. Each point is
crossed with every scenario. The existing 10,000-total-run, memory and simulation
work limits still apply before job creation. Sampling happens when the bounded
matrix is expanded, before model evaluation.
The retained specification and fixed algorithm reconstruct the same points for
resumption and replay; result rows also retain their exact chosen inputs. A fixed
seed, parameter order and design reproduce the same matrix;
changing any of them may change it. The sampling algorithm is fixed SplitMix64
with 53-bit uniforms and an unbiased Fisher–Yates shuffle.
The design seed is separate from REPLICATE ... SEED ...: the former chooses
parameter points, while the latter governs simulation randomness at each point.
Existing named observations, scenarios, quantiles, Frames and replay remain
available. Sampled designs currently cover bounded continuous uniform inputs;
nonuniform distributions, categorical mixtures and Sobol sequences are later work.
See sampled experiment.