Configuration Grammar¶
Configuration is the primary abstraction layer in jaxfne. Everything else —
emitters, source tensors, field proxies, probes, objectives, optimizers, and
manifests — is compiled from it. A Configuration is not a bag of settings: it
is a declarative specification of a model that construct() and simulate()
turn into a runnable TFNE graph.
Configuration (declarative specification — the dial you turn)
│
construct() ← the compiler
▼
Emitter → Source → Field → Probe → Objective → Optimizer → Manifest
(the TFNE operator chain — emergent from the specification)
This is the central idea of the package: jaxfne is the mathematical backend,
and the configuration is the biophysical specification. How biophysical the
output is — calibrated amplitudes, real morphology, channel detail — is
determined by how much detail you put into the Configuration (and any
Jaxley models you bridge in), not by a fixed ceiling in the
backend. Every method below adds biophysical specificity to the same chain.
Each method returns a Configuration, so the whole model reads as one fluent
sequence:
import jaxfne as jtfne
cfg = (
jtfne.Configuration()
.runtime(seed=0, duration_ms=1000.0, dt_ms=0.5)
.column(name="V1", layers=["L1", "L2/3", "L4", "L5", "L6"], n=1000)
.geometry(layer_thickness={"L1": 0.1, "L2/3": 0.3, "L4": 0.2, "L5": 0.3, "L6": 0.3})
.cell_types({"E": 0.8, "PV": 0.1, "SST": 0.07, "VIP": 0.03})
.connectivity(mode="sparse")
.set_emitter("izhikevich", "cortical_eig")
.probes(["spikes", "V_m", "LFP", "CSD"], n_contacts=16)
.field(domain="laminar_column", conductivity="proxy")
)
model = jtfne.construct(cfg)
The sections below follow the operator chain top to bottom. Each says what it specifies and how it raises biophysical specificity.
Runtime¶
.runtime(...) / .set_runtime(...) — execution substrate: seed, dtype,
backend, jit, vmap, duration_ms, dt_ms.
Specificity dial: a stable, explicit runtime is the precondition for everything
else. Enable x64 (jtfne.enable_x64()) before building arrays, keep dt_ms
small enough for the dynamics you specify, and fix seed for reproducibility.
The runtime does not change the biology — it determines whether the biology you
specified is computed faithfully.
Geometry¶
.geometry(layer_thickness=, layer_cell_types=), .layer_fractions(...),
.column(name, layers, n) / .add_column(...), .areas([...]),
.uniform3d(radius_mm=, height_mm=).
Specificity dial: geometry is where laminar structure enters. .column() names
the layers and total count; .geometry(layer_thickness=...) turns thicknesses
into cumulative depth bands; .layer_fractions(...) makes per-layer neuron counts
proportional to thickness (or an explicit fraction). Real depth bands are what
let LFP/CSD and spectrolaminar readouts express depth structure — .uniform3d()
placement collapses layer identity and should be used only for non-laminar models.
Cell types¶
.cell_types({...}) / .set_cell_types(...), .area_layer_cell_types(area, {...}),
.cell_params(selector, params).
Specificity dial: the E:I composition and its laminar gradient. A single global
.cell_types({"E": 0.8, ...}) is the coarse setting; .area_layer_cell_types(...)
expresses the verified ground-truth gradient (E rises with depth, inhibition
peaks superficially, PV concentrates at L4). .cell_params(...) overrides
per-selector neuron parameters. The more layer- and type-resolved the
composition, the more the model can reproduce real laminar physiology.
Canonical prior
jtfne.build_laminar_column(n=1000, ei_profile="canonical") applies the
verified per-layer E:I gradient and laminar placement for you — a good
starting point you then refine with the methods above.
Connectivity¶
.connectivity(**) / .set_connectivity(**), .connections(name=, source=, target=,
probability=, weight=, sign=, mechanism=, plasticity=),
.inter_column_connectivity(source_area=, target_area=, layer_to_layer_map=, ...),
.mechanisms(name=, kind=, params=).
Specificity dial: the circuit. .connections(...) declares explicit
source→target rules with probability, weight, sign, and synaptic mechanism;
.inter_column_connectivity(...) adds laminar-aware inter-area edges
(feedforward L2/3→L4, feedback L6→L1/L5). Prefer sparse construction at scale.
Richer, sign- and mechanism-resolved connectivity is what produces emergent
oscillations and the band-localized structure spectrolaminar readouts depend on.
Emitters¶
.set_emitter(family="izhikevich", preset="cortical_eig") / .emitter(**),
.drive(baseline_drive_by_cell_type=, drive_by_layer=, drive_by_area=,
time_schedule=, evoked_windows=, noise_policy=, ...),
.cell_type_drives({...}).
Specificity dial: the neuron model and its input. The built-in Izhikevich emitter
is tunable and float32-stable; .drive(...) sets baseline/laminar/evoked input
and noise. For real channel biophysics and morphology, bridge a Jaxley model
in as the emitter (see Jaxley Interoperability) — a Jaxley
HH network exports real transmembrane ionic current, the physical generator of
the extracellular field.
Sources¶
The source tensor is emergent — construct()/simulate() build it from the
emitter output according to the emitter family and field settings (built-in
spike/current proxy, or reconstructed HH ionic current via the Jaxley bridge).
Specificity dial: a voltage/spike proxy source is coarse; a real transmembrane
current source (Jaxley HH) is the physically meaningful generator. Choosing
the emitter therefore chooses the source's fidelity. See
JaxleyBridge.simulate_laminar_field in the Bridges API.
Fields¶
.field(domain=, conductivity=, boundary=, **).
Specificity dial: how the source becomes an extracellular field readout. The
laminar proxy (Gaussian projection + finite-difference CSD) is a structural
linear_solver readout; its outputs carry the *_proxy suffix and remain
uncalibrated unless you supply calibration. Density-preserving projection
preserves laminar depth structure (row-normalization erases it). Finer geometry +
calibration is the path from proxy to physical amplitude.
Probes¶
.probes([...]) / .set_probes(modes, n_contacts=) / .probe(**).
Specificity dial: what you measure and at what resolution — spikes, V_m,
LFP, CSD, EEG, MEG, spectrolaminar, with contact geometry (n_contacts).
More contacts and more modalities expose more of the model's structure; probes do
not change the model, they read it.
Objectives¶
.objective(firing_rate_target=, spectrolaminar_profile_target=, band_definitions=,
synchrony_metrics=, null_controls=, ablations=, rejection_gates=),
.objective_outputs(name=, dtype=, shape=).
Specificity dial: what "correct" means. Targets (rate, synchrony, spectrolaminar profile), and — importantly — the null controls and ablations that make a result interpretable rather than a fit. A specified null/ablation turns a metric into evidence.
Optimizers¶
.optimizer(optimizer_family="AGSDR", differentiability_status=, surrogate_status=,
search_space=, budget=, seed=, hard_gates=), .trainables(name=, path=, selector=,
bounds=).
Specificity dial: how parameters are searched toward the objective. Declare the
trainable parameters (.trainables(...)), the search space and budget, and the
differentiability/surrogate status. Hard spike reset is non-differentiable, so the
surrogate status governs whether gradient-based tuning is admissible.
Manifests¶
jtfne.manifest(cfg, signals=...), .validate(), .update_metadata(**).
Specificity dial: the receipt. The manifest binds the configuration, runtime
report, artifact hashes, and truth gates into a strict JSON-safe record.
.validate() checks the specification before you run. The manifest is what makes
a result reproducible and auditable — the closing operator of the chain.
Why this matters¶
Reading Configuration as a compiler reframes the whole package: the object
grammar (the fluent cfg chain) and the TFNE operator grammar
(Emitter→Source→Field→Probe→Objective→Optimizer→Manifest) are two views of the
same system. You specify a model declaratively; jaxfne compiles and computes it.
The fidelity of the result is a property of the specification you wrote — which is
why Configuration is the deepest, most important surface in the package.
See also¶
- Tensor-Field Workflows — the operator chain in depth.
- Jaxley Interoperability — real channels/morphology as emitters.
- Bridges API · Fields API · Objectives API