Model profiles: scientific model, numerical realisation, lowering¶
A bundled schema (src/sc_neurocore/neurons/model_schemas/<stem>.toml, with a
JSON twin) states equations, state, parameters, a step, a threshold and a
reset. Those fields belong to three different things that must be judged
separately: what the science says, how the schema advances it in time, and
what the RTL emitter can lower from that. The model profile contract
sc-neurocore.model-profile.v1 (sc_neurocore.neurons.model_profile) resolves
every schema into those three layers, derives what it can, lets a schema author
what it cannot, and reports every contradiction. It never changes a number.
The three layers¶
| Layer | Carries | Source |
|---|---|---|
| scientific model | name, author, year, DOI; authored equations; biological state with units; source parameters with units; science.equations_as_published |
schema metadata, [state], [parameters], [dynamics], [science], authored roles |
| numerical realisation | method and family; exactness class; dt and time unit; sub-steps and their kind; macro step; evaluation order; auxiliary registers; implementation and timebase parameters; admissible methods; randomness contract; event contract (detection, condition, stochastic expression, reset, edge logic, refractory register) |
[integration], [threshold], [reset], authored roles, derived rules |
| lowering profile | whether the equation compiler accepts the realisation, its limits, the methods Studio co-simulates, the recommended precision hint | mirrors sc_neurocore.compiler acceptance rules, [hints] |
Every layer has a JSON projection (ModelProfile.to_public_dict) and
parse_profile rebuilds an equal profile from it.
Authoring a profile section¶
Everything a schema does not author is derived: all state is biological, all
parameters are source parameters, a parameter named dt (or dt_ms) that
equals integration.dt is a timebase parameter, sub-steps of an ODE subdivide
time, a map with dt = 1 and no timebase is indexed by iteration. What cannot
be derived is authored in an optional [profile] table:
[profile]
contract = "sc-neurocore.model-profile.v1"
time_unit = "ms"
substep_kind = "stage-iteration" # or "time-subdivision"
exactness = "exact-flow" # optional claim, see below
refractory_register = "refractory_time" # a state variable holding the hold
notes = "..."
[profile.state] # role per state variable
v = "biological"
spike_flag = "auxiliary: event flag raised at stage 4"
[profile.parameters] # role per parameter
dt = "timebase"
delta_ge = "implementation: constant per-macro-step boundary conductance event"
c_m = "source"
[profile.units] # unit per state variable or parameter
v = "mV"
dt = "ms"
Roles are biological / auxiliary for state and source /
implementation / timebase for parameters; text after a colon is the
meaning. Twelve representative schemas author a section: lapicque,
sc_lapicque_lif, lif, adex, hodgkin_huxley, wang_buzsaki, exp_if,
rulkov_map, chialvo_map, escape_rate, poisson, coba_lif. The TOML
and JSON twins must carry the same section; the static validator checks it.
Mapping table: method, exactness, family, lowering¶
| Method | Family | Derived exactness | Claimable exactness | Lowering |
|---|---|---|---|---|
euler |
ode | first-order | none | registered next-state datapath; sub-steps only for crossing, non-resetting models |
gauss_seidel |
ode | first-order-sequential | none | sequential derivative wires in declaration order; same sub-step limit |
rk4 |
ode | fourth-order | none | four staged derivative evaluations per clock; same sub-step limit |
exp_euler |
ode | linearised-exponential | exact-linear-relaxation, verified: each equation is affine in its own variable and reads no other state |
exprel-scaled increment from the symbolic Jacobian the golden compiles |
map |
map | recurrence | exact-flow, authored: the recurrence is a sampled closed-form solution |
next state is the map value; stage iteration folds several clocks per macro step |
An event-only schema (no state, no dynamics) has family event-only and
exactness event-only. A schema whose method or detection is outside the
executable vocabulary (euler, map, rk4, exp_euler, gauss_seidel;
level, crossing, escape_rate, poisson) is a descriptive record: it
documents a hand model, cannot run through UniversalNeuron, has no macro step
and no lowering, and may not author a profile section. Thirteen bundled schemas
are descriptive records; the ledger names them.
Sub-steps have two meanings and the profile says which: time-subdivision
(each sub-step advances dt; the public step is substeps × dt, as in
Hodgkin-Huxley's 100 × 0.01 ms) or stage-iteration (the sub-steps are the
stages of one scheme folded into a map; the public step is one dt, as in the
COBA schema's four RK4 stage registers). A map with substeps > 1 must declare
which; an ODE cannot declare stage iteration.
The evaluation order is derived from the family and the detection: integrate (or iterate, or stage) × sub-steps, then the refractory hold encoded in the dynamics if a register is named, then the threshold (level, or rising edge on the macro boundary for a crossing model without a reset rule), or the hazard and LFSR trial for an escape rate, or the probability and LFSR trial for a Poisson bin, then the reset rule if any.
The randomness contract is none, lfsr16-threshold (a model-scoped 16-bit
LFSR seeded from threshold.rng_seed decides stochastic trials; replayable),
diffusion-noise-global-rng (an expression names xi, drawn from NumPy's
process-global stream; not replayable by the model alone), or both. No bundled
schema uses diffusion noise.
Contradictions and admission¶
resolve_profile lists every contradiction as a problem; a problem makes the
schema unusable by the executable consumers. Contradictions include a role for
an undeclared quantity, a timebase parameter that differs from
integration.dt or whose unit differs from the time unit, a dt parameter the
expressions read that is not bound to the timebase, an exactness claim the
method cannot carry or the symbolic check refuses, substep_kind with one
sub-step, stage iteration on an ODE, a map with sub-steps and no declared kind,
admissible methods or extensions.integrator_options that leave the family,
and a profile section on a descriptive record.
Overrides are admitted through admit_overrides:
- a method must be one of the profile's admissible methods: any ODE method for
an ODE (the realisation is then derived and reported as such), only
mapfor a recurrence; - a step override moves every timebase parameter with it, is refused when a parameter override contradicts the effective step, and is refused for a recurrence without a continuous timebase unless it equals the declared step;
- a seed is refused for a profile that draws no randomness.
Where the contract is enforced¶
UniversalNeuronresolves the profile of every schema and admits every override through it;neuron.profileis the authored profile andneuron.realised_profile()the realisation actually run.- The static validator (
schema_validator,python -m sc_neurocore.neurons validate) reports profile problems as errors, descriptive records as classified warnings, and checks the profile section for TOML/JSON parity. - Studio's model detail carries
profile_contract; the compile configuration offers only integrators inside the family, refuses one that leaves it, and the compile evidence records the realised profile next to the schema digest. - The reference-trace runner instantiates every protocol through the profile, so a protocol step is admitted or refused by the same rules.
- The descriptor skeleton generator resolves the curated schema per class,
not per module, so an
SCcompatibility identity never inherits its source identity's profile.
Per-profile inventory and validator registry¶
tools/model_profile_ledger.py --write renders
docs/_generated/model_profile_ledger.json from
sc_neurocore.neurons.profile_registry: one row per bound (class, schema
stem) with the three layers, the descriptor's own integration label next to
the profile's method (a hand class may keep a different default than its
schema profile; the row says same or differs), the readiness record
verified for exactly that profile, and the validator registry: for every facet,
each validator the descriptor or the schema declares, its resolution, its scope
and the receipt bound to this profile.
Admission is the gate the source-oracle, native-admission and dependency-closure work consume:
| Admission | Meaning |
|---|---|
admitted |
executable, contradiction-free, and every facet the descriptor declares through an evidence field has at least one executable validator scoped to this profile |
blocked |
executable, but a declared facet is backed by prose or a report alone, or only by class-scoped tests of another profile |
not-executable |
a descriptive record |
Descriptor evidence fields are class-scoped: they validate the class's
canonical profile (the one model_identity.schema_for_class returns). A
non-canonical profile is validated only by its own schema
[validation].evidence or by a receipt recorded for it, never by the canonical
profile's tests, so no test admits a profile it did not exercise. Backend
facets have no descriptor evidence field yet; declared-but-unvalidated backends
are listed on the row instead of deciding admission. --check fails when the
tracked ledger is stale; --summary prints the partition.
Migration decisions and limits¶
- No equation, parameter, step, threshold or reset value changed. The twelve authored sections classify; the other schemas derive.
- The COBA schema declares its four map iterations as stage iteration of one 0.1 ms step and its twelve stage registers as auxiliary, which is what the folded RK4 always was; the descriptor and the hand model are untouched.
- Four descriptors (
AiharaMapNeuron,NagumoSatoMapNeuron,SCAdaptiveThresholdMapNeuron,SCChaoticMapNeuron) previously statedmethod = "euler",dt = 0.1because the generator resolved their schema by module name and found none; regenerated per class they state the bound map profile (map,dt = 1.0). No numerics changed; the hand classes are maps. - The
ermentrout_kopell_theta_euler_doireference protocol declareddt = 1.0for a map whose 0.1 Euler step is fixed inside its expression; the override was inert. It now declares the schema's 0.1 and its expected features are unchanged. - Descriptor
integration.methodremains the hand class's label (exact_constant_voltage_flow,baseline_euler, …); the ledger joins it to the profile method and does not rewrite it. - The
sc_wb_nmda_magnesium_blockrecord usesdt = 0.5as the macro step subdivided fifty times, the opposite of the executable convention; being a descriptive record it carries no macro step, and its hand model owns the correction. - Units are declarations checked for consistency with the time unit; they are
not dimensional analysis (that remains
EquationNeuron(units="strict")). - Admission judges validator existence and scope, not scientific sufficiency: an admitted profile still needs the independent source oracle, and a bound receipt still means one executed run, not replication.