Neural Network Module (nn)¶
GPU-first differentiable Kuramoto dynamics for neural network integration via JAX and equinox. Every function and layer is JIT-compilable, vmap-compatible, and fully differentiable — enabling gradient-based coupling inference, synchronisation optimisation, and physics-informed machine learning.
Requires: pip install scpn-phase-orchestrator[nn] (installs jax + equinox + optax)
Runtime API¶
Production ML jobs should make the accelerator contract explicit at start-up:
from scpn_phase_orchestrator.nn import (
KuramotoLayer,
jax_runtime_info,
require_accelerator,
)
print(jax_runtime_info())
device = require_accelerator()
require_accelerator() raises when JAX is installed but only CPU devices are
visible. Use require_accelerator(allow_cpu=True) only for CI, notebooks, and
smoke tests that intentionally run without a GPU/TPU.
runtime ¶
Runtime contract for the GPU-first differentiable nn API.
The numerical layers remain pure JAX/equinox modules. This module makes the runtime status explicit for ML users: whether JAX is installed, which backend is active, which devices are visible, and whether a non-CPU accelerator is available for production training paths.
Classes¶
JaxRuntimeInfo
dataclass
¶
Functions:¶
require_jax ¶
Return the imported JAX module or raise a clear installation error.
Returns¶
ModuleType Return the imported JAX module or raise a clear installation error.
Raises¶
RuntimeError If the JAX runtime requirement is not met.
Source code in src/scpn_phase_orchestrator/nn/runtime.py
jax_runtime_info ¶
Return an import-safe summary of the active JAX runtime.
The function never imports JAX when it is not installed, so base package
users can still import scpn_phase_orchestrator.nn and receive an
actionable runtime report.
Returns¶
JaxRuntimeInfo Return an import-safe summary of the active JAX runtime.
Source code in src/scpn_phase_orchestrator/nn/runtime.py
default_device ¶
Return the default JAX device label used by the nn API.
Returns¶
str
Return the default JAX device label used by the nn API.
Raises¶
RuntimeError If the JAX runtime requirement is not met.
Source code in src/scpn_phase_orchestrator/nn/runtime.py
require_accelerator ¶
Return the production training device or fail fast on CPU-only runtimes.
Parameters¶
allow_cpu : bool
Permit CPU-only JAX execution. This is useful for CI, documentation examples,
and small smoke tests. Production ML training should keep the default False
so misconfigured GPU jobs fail before expensive work starts.
Returns¶
str
A JAX device label such as "gpu:0", "tpu:0", or "cpu:0" when
allow_cpu is enabled.
Raises¶
RuntimeError If the JAX runtime requirement is not met.
Source code in src/scpn_phase_orchestrator/nn/runtime.py
Architecture¶
┌─────────────────────────┐
│ Functional API (JAX) │
│ kuramoto_forward() │
│ winfree_forward() │
│ simplicial_forward() │
│ stuart_landau_forward() │
│ order_parameter() │
└───────────┬─────────────┘
│
┌───────────────┼───────────────┐
↓ ↓ ↓
KuramotoLayer SimplicialLayer StuartLandauLayer
(eqx.Module) (eqx.Module) (eqx.Module)
│ │ │
↓ ↓ ↓
training.py UDEKuramotoLayer BOLDGenerator
(loss + optim) (physics + MLP) (hemodynamics)
│
┌─────────┼──────────┬───────────────┐
↓ ↓ ↓ ↓
InverseKuramoto Reservoir OIM DifferentiableSupervisor
(coupling inference) (readout) (combinatorial) (closed-loop policy)
Functional API¶
Pure JAX functions — no state, no side effects. Each function is
decorated with @jax.jit internally or designed to be JIT'd by the caller.
Kuramoto model¶
| Function | Signature |
|---|---|
kuramoto_step |
(phases, omegas, K, dt) → phases |
kuramoto_rk4_step |
(phases, omegas, K, dt) → phases |
kuramoto_forward |
(phases, omegas, K, dt, n_steps, method="rk4") → (final, traj) |
Masked (sparse) variants append _masked and take an additional
mask: jax.Array parameter for selective coupling.
Winfree model¶
| Function | Signature |
|---|---|
winfree_step |
(phases, omegas, K, dt) → phases |
winfree_rk4_step |
(phases, omegas, K, dt) → phases |
winfree_forward |
(phases, omegas, K, dt, n_steps, method="rk4") → (final, traj) |
Winfree coupling: dθ_i/dt = ω_i + K · Q(θ_i) · Σ P(θ_j) where Q is the sensitivity function and P is the pulse function.
Use Winfree dynamics when the observed coupling is pulse-driven rather than a smooth sinusoidal pull. Typical cases include biological pacemakers, circadian or neural populations with event-like signalling, flashing or firing oscillator ensembles, endocrine or chemical pulse trains, and sensor networks where one unit perturbs another through brief impulses. The model is the right API choice when the phase response curve and pulse waveform are part of the hypothesis, not just incidental numerical details.
Simplicial (3-body) model¶
| Function | Signature |
|---|---|
simplicial_step |
(phases, omegas, K, dt, sigma2=0.0) → phases |
simplicial_rk4_step |
(phases, omegas, K, dt, sigma2=0.0) → phases |
simplicial_forward |
(phases, omegas, K, dt, n_steps, sigma2=0.0, method="rk4") → (final, traj) |
The sigma2 parameter controls the 3-body interaction strength.
When sigma2=0, reduces to standard Kuramoto.
Use simplicial dynamics when pairwise edges are not enough to represent the interaction mechanism. The 3-body term models triadic or group constraints: neural assemblies whose synchrony depends on co-active triplets, social or multi-agent triads, reaction loops, power-network group modes, and topology extracted from simplicial complexes or hypergraphs. This surface is intended for regressions where cluster states, abrupt synchronization transitions, or learned coupling cannot be explained by pairwise Kuramoto alone.
Winfree and simplicial models answer different modelling questions. Winfree changes the timing law from smooth coupling to pulse-response coupling. Simplicial Kuramoto changes the interaction topology from pairwise edges to group terms. Combining them conceptually is useful for event-driven systems with group structure, for example spiking neural assemblies, biological tissue motifs, swarm coordination with triadic constraints, or industrial networks whose failure modes depend on triangular dependencies rather than isolated links.
Stuart-Landau model¶
| Function | Signature |
|---|---|
stuart_landau_step |
(phases, amps, omegas, mu, K, K_r, dt, eps=1.0) → (phases, amps) |
stuart_landau_rk4_step |
(phases, amps, omegas, mu, K, K_r, dt, eps=1.0) → (phases, amps) |
stuart_landau_forward |
(phases, amps, omegas, mu, K, K_r, dt, n_steps, eps=1.0, method="rk4") → (phases, amps, phase_traj, amp_traj) |
Analysis functions¶
| Function | Returns | Description |
|---|---|---|
order_parameter(phases) |
scalar | Kuramoto R = |Σ exp(iθ)|/N |
plv(trajectory) |
(N,N) array | Pairwise phase-locking value |
coupling_laplacian(K) |
(N,N) array | Graph Laplacian L = D - K |
saf_order_parameter(K, omegas, solver="auto") |
scalar | Self-consistent analytical R |
saf_loss(K, omegas, budget, solver="auto") |
scalar | Differentiable SAF loss |
Spectral Alignment Function use cases¶
SAF estimates the synchrony of a Kuramoto network directly from the coupling Laplacian and natural frequencies, without rolling out the ODE. Use it when the question is "which topology should synchronise this frequency field?" rather than "what is the phase trajectory at each time step?"
Concrete uses:
- Coupling-topology optimisation under a wiring or energy budget.
- Fast screening of candidate graphs before expensive time-domain simulation.
- Differentiable regularisation for learned
Kmatrices in neural pipelines. - Review of
auto_initial_kmatrices produced by auto-binding before runtime actuation. - Sensitivity analysis for which frequency modes are poorly aligned with the graph Laplacian.
Solver modes:
solver="eigh"computes the exact dense Laplacian eigendecomposition and is appropriate for small and medium dense systems where eigenvectors are needed for auditability.solver="cg"uses the equivalent Laplacian pseudoinverse formulation and conjugate-gradient matrix-vector products. This avoids full eigendecomposition and is the preferred GPU path for large dense systems.solver="auto"useseighup toexact_size_limitand switches tocgabove that size.
Scaling limits: both paths still consume a dense (N, N) coupling matrix. The
CG path removes the cubic eigensolver bottleneck, but it does not make dense
memory disappear. For very sparse networks, keep a sparse or masked coupling
representation upstream and materialise dense K only when the SAF audit size
fits device memory.
Boundary contract: K must be a square real-valued JAX-compatible coupling
matrix and omegas must be a real-valued one-dimensional vector with matching
length. Boolean and complex payloads are rejected before Laplacian construction.
SAF solver controls are finite positive scalars or integers, while saf_loss
budget controls are finite non-negative scalars.
functional ¶
Pure JAX functions for differentiable Kuramoto dynamics.
All functions are JIT-compilable, vmap-compatible, and differentiable via JAX autodiff. No NumPy conversions — inputs and outputs stay as JAX arrays for gradient flow.
Requires: jax>=0.4
Functions:¶
kuramoto_step ¶
Single Euler step of the Kuramoto model.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling matrix. dt : float integration timestep.
Returns¶
jax.Array (N,) updated phases, wrapped to [0, 2pi).
Source code in src/scpn_phase_orchestrator/nn/functional.py
kuramoto_rk4_step ¶
Single RK4 step of the Kuramoto model.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling matrix. dt : float integration timestep.
Returns¶
jax.Array (N,) updated phases, wrapped to [0, 2pi).
Source code in src/scpn_phase_orchestrator/nn/functional.py
kuramoto_forward ¶
kuramoto_forward(
phases: Array,
omegas: Array,
K: Array,
dt: float,
n_steps: int,
method: str = "rk4",
) -> tuple[jax.Array, jax.Array]
Run N Kuramoto steps, returning final state and trajectory.
Uses jax.lax.scan for efficient compilation and autodiff.
Parameters¶
phases : jax.Array (N,) initial oscillator phases. omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling matrix. dt : float integration timestep. n_steps : int number of integration steps. method : str "rk4" or "euler".
Returns¶
tuple[jax.Array, jax.Array] : final: (N,) phases after n_steps trajectory: (n_steps, N) full phase trajectory.
Source code in src/scpn_phase_orchestrator/nn/functional.py
kuramoto_step_masked ¶
kuramoto_step_masked(
phases: Array,
omegas: Array,
K: Array,
mask: Array,
dt: float,
) -> jax.Array
Single Euler step with masked coupling.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling weights. mask : jax.Array (N, N) binary mask (1 = edge exists, 0 = no edge). dt : float integration timestep.
Returns¶
jax.Array (N,) updated phases.
Source code in src/scpn_phase_orchestrator/nn/functional.py
kuramoto_rk4_step_masked ¶
kuramoto_rk4_step_masked(
phases: Array,
omegas: Array,
K: Array,
mask: Array,
dt: float,
) -> jax.Array
Single RK4 step with masked coupling.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
omegas : jax.Array
Natural frequencies in rad/s, shape (N,).
K : jax.Array
Coupling matrix K, shape (N, N).
mask : jax.Array
Boolean coupling mask, shape (N, N).
dt : float
Integration step size.
Returns¶
jax.Array The phases after one masked RK4 step.
Source code in src/scpn_phase_orchestrator/nn/functional.py
kuramoto_forward_masked ¶
kuramoto_forward_masked(
phases: Array,
omegas: Array,
K: Array,
mask: Array,
dt: float,
n_steps: int,
method: str = "rk4",
) -> tuple[jax.Array, jax.Array]
Run N Kuramoto steps with masked coupling.
Parameters¶
phases : jax.Array (N,) initial phases. omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling weights. mask : jax.Array (N, N) binary mask. dt : float timestep. n_steps : int integration steps. method : str "rk4" or "euler".
Returns¶
tuple[jax.Array, jax.Array] (final, trajectory) — same as kuramoto_forward.
Source code in src/scpn_phase_orchestrator/nn/functional.py
winfree_step ¶
Single Euler step of the Winfree model.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). omegas : jax.Array (N,) natural frequencies. K : float scalar coupling strength. dt : float integration timestep.
Returns¶
jax.Array (N,) updated phases.
Source code in src/scpn_phase_orchestrator/nn/functional.py
winfree_rk4_step ¶
Single RK4 step of the Winfree model.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
omegas : jax.Array
Natural frequencies in rad/s, shape (N,).
K : float
Coupling matrix K, shape (N, N).
dt : float
Integration step size.
Returns¶
jax.Array The phases after one Winfree RK4 step.
Source code in src/scpn_phase_orchestrator/nn/functional.py
winfree_forward ¶
winfree_forward(
phases: Array,
omegas: Array,
K: float,
dt: float,
n_steps: int,
method: str = "rk4",
) -> tuple[jax.Array, jax.Array]
Run N steps of Winfree dynamics.
Parameters¶
phases : jax.Array (N,) initial phases. omegas : jax.Array (N,) natural frequencies. K : float scalar coupling strength. dt : float timestep. n_steps : int integration steps. method : str "rk4" or "euler".
Returns¶
tuple[jax.Array, jax.Array] (final, trajectory).
Source code in src/scpn_phase_orchestrator/nn/functional.py
simplicial_step ¶
simplicial_step(
phases: Array,
omegas: Array,
K: Array,
dt: float,
sigma2: float | Array = 0.0,
) -> jax.Array
Single Euler step of the simplicial (3-body) Kuramoto model.
Extends standard Kuramoto with higher-order 3-body interactions that produce explosive (first-order) synchronization transitions.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) pairwise coupling matrix. dt : float integration timestep. sigma2 : float | jax.Array 3-body coupling strength (0 = standard Kuramoto).
Returns¶
jax.Array (N,) updated phases, wrapped to [0, 2pi).
Source code in src/scpn_phase_orchestrator/nn/functional.py
simplicial_rk4_step ¶
simplicial_rk4_step(
phases: Array,
omegas: Array,
K: Array,
dt: float,
sigma2: float | Array = 0.0,
) -> jax.Array
Single RK4 step of the simplicial (3-body) Kuramoto model.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) pairwise coupling matrix. dt : float integration timestep. sigma2 : float | jax.Array 3-body coupling strength (0 = standard Kuramoto).
Returns¶
jax.Array (N,) updated phases, wrapped to [0, 2pi).
Source code in src/scpn_phase_orchestrator/nn/functional.py
simplicial_forward ¶
simplicial_forward(
phases: Array,
omegas: Array,
K: Array,
dt: float,
n_steps: int,
sigma2: float | Array = 0.0,
method: str = "rk4",
) -> tuple[jax.Array, jax.Array]
Run N steps of simplicial Kuramoto, returning final state and trajectory.
Parameters¶
phases : jax.Array (N,) initial oscillator phases. omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) pairwise coupling matrix. dt : float integration timestep. n_steps : int number of integration steps. sigma2 : float | jax.Array 3-body coupling strength (0 = standard Kuramoto). method : str "rk4" or "euler".
Returns¶
tuple[jax.Array, jax.Array] (final, trajectory) where trajectory is (n_steps, N).
Source code in src/scpn_phase_orchestrator/nn/functional.py
stuart_landau_step ¶
stuart_landau_step(
phases: Array,
amplitudes: Array,
omegas: Array,
mu: Array,
K: Array,
K_r: Array,
dt: float,
epsilon: float = 1.0,
) -> tuple[jax.Array, jax.Array]
Single Euler step of the Stuart-Landau oscillator model.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). amplitudes : jax.Array (N,) oscillator amplitudes (r >= 0). omegas : jax.Array (N,) natural frequencies. mu : jax.Array (N,) bifurcation parameters (supercritical if mu > 0). K : jax.Array (N, N) phase coupling matrix. K_r : jax.Array (N, N) amplitude coupling matrix. dt : float integration timestep. epsilon : float amplitude coupling strength.
Returns¶
tuple[jax.Array, jax.Array] (new_phases, new_amplitudes).
Source code in src/scpn_phase_orchestrator/nn/functional.py
stuart_landau_rk4_step ¶
stuart_landau_rk4_step(
phases: Array,
amplitudes: Array,
omegas: Array,
mu: Array,
K: Array,
K_r: Array,
dt: float,
epsilon: float = 1.0,
) -> tuple[jax.Array, jax.Array]
Single RK4 step of the Stuart-Landau oscillator model.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2pi). amplitudes : jax.Array (N,) oscillator amplitudes (r >= 0). omegas : jax.Array (N,) natural frequencies. mu : jax.Array (N,) bifurcation parameters. K : jax.Array (N, N) phase coupling matrix. K_r : jax.Array (N, N) amplitude coupling matrix. dt : float integration timestep. epsilon : float amplitude coupling strength.
Returns¶
tuple[jax.Array, jax.Array] (new_phases, new_amplitudes).
Source code in src/scpn_phase_orchestrator/nn/functional.py
stuart_landau_forward ¶
stuart_landau_forward(
phases: Array,
amplitudes: Array,
omegas: Array,
mu: Array,
K: Array,
K_r: Array,
dt: float,
n_steps: int,
epsilon: float = 1.0,
method: str = "rk4",
) -> tuple[jax.Array, jax.Array, jax.Array, jax.Array]
Run N Stuart-Landau steps, returning final state and trajectories.
Parameters¶
phases : jax.Array (N,) initial phases. amplitudes : jax.Array (N,) initial amplitudes. omegas : jax.Array (N,) natural frequencies. mu : jax.Array (N,) bifurcation parameters. K : jax.Array (N, N) phase coupling matrix. K_r : jax.Array (N, N) amplitude coupling matrix. dt : float integration timestep. n_steps : int number of steps. epsilon : float amplitude coupling strength. method : str "rk4" or "euler".
Returns¶
tuple[jax.Array, jax.Array, jax.Array, jax.Array] (final_phases, final_amplitudes, phase_traj, amp_traj) where trajectories are (n_steps, N).
Source code in src/scpn_phase_orchestrator/nn/functional.py
order_parameter ¶
Kuramoto order parameter R = |
Differentiable scalar measuring global synchronization. R=1 means perfect sync, R~0 means incoherent.
Parameters¶
phases : jax.Array (N,) or (T, N) oscillator phases.
Returns¶
jax.Array Scalar R value (or (T,) if trajectory input).
Source code in src/scpn_phase_orchestrator/nn/functional.py
plv ¶
Phase-Locking Value matrix from a phase trajectory.
PLV_ij = |
Parameters¶
trajectory : jax.Array (T, N) phase trajectory.
Returns¶
jax.Array (N, N) PLV matrix, values in [0, 1].
Source code in src/scpn_phase_orchestrator/nn/functional.py
coupling_laplacian ¶
Compute the graph Laplacian from a coupling matrix.
L = D - K, where D_ii = sum_j K_ij.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix.
Returns¶
jax.Array (N, N) Laplacian matrix.
Source code in src/scpn_phase_orchestrator/nn/functional.py
saf_order_parameter ¶
saf_order_parameter(
K: Array,
omegas: Array,
eps: float = 1e-08,
solver: str = "auto",
exact_size_limit: int = 256,
cg_tol: float = 1e-05,
cg_maxiter: int | None = None,
) -> jax.Array
Spectral Alignment Function: closed-form order parameter estimate.
r ≈ 1 - (1/2N) Σ_{j=2}^N λ_j⁻² ⟨v^j, ω⟩²
where λ_j are Laplacian eigenvalues and v^j are eigenvectors. Valid in the strongly-coupled regime. The exact path differentiates through Laplacian eigendecomposition. The conjugate-gradient path uses the equivalent identity Σ λ_j⁻² ⟨v^j, ω⟩² = ||L⁺ω||², avoids full eigendecomposition, and maps large dense problems to GPU-friendly matrix-vector operations.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix (non-negative). omegas : jax.Array (N,) natural frequencies. eps : float regularization for small eigenvalues. solver : str "auto", "eigh", or "cg". "auto" uses exact eigendecomposition up to exact_size_limit and conjugate gradient above it. exact_size_limit : int Largest N where "auto" keeps the exact eigensolver. cg_tol : float Relative tolerance for the conjugate-gradient solver. cg_maxiter : int | None Optional maximum conjugate-gradient iterations.
Returns¶
jax.Array Scalar estimated order parameter in [0, 1].
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/functional.py
saf_loss ¶
saf_loss(
K: Array,
omegas: Array,
budget: float = 0.0,
budget_weight: float = 0.1,
solver: str = "auto",
exact_size_limit: int = 256,
cg_tol: float = 1e-05,
cg_maxiter: int | None = None,
) -> jax.Array
Loss function for coupling topology optimization via SAF.
Minimizes -r_SAF (maximize synchronization) with optional L1 budget constraint on total coupling strength.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix. omegas : jax.Array (N,) natural frequencies. budget : float target total coupling strength (0 = no constraint). budget_weight : float penalty weight for budget violation. solver : str SAF solver passed to saf_order_parameter. exact_size_limit : int Largest N where "auto" keeps the exact eigensolver. cg_tol : float Relative tolerance for the conjugate-gradient solver. cg_maxiter : int | None Optional maximum conjugate-gradient iterations.
Returns¶
jax.Array Scalar loss (lower = better synchronization).
Source code in src/scpn_phase_orchestrator/nn/functional.py
KuramotoLayer¶
Equinox module wrapping Kuramoto dynamics as a learnable layer.
KuramotoLayer(
n: int, # number of oscillators
n_steps: int = 50, # integration steps per forward pass
dt: float = 0.01, # timestep
K_scale: float = 0.1, # initialisation scale for K
mask: jax.Array | None = None, # sparse coupling mask
key: jax.Array, # PRNG key
)
Learnable parameters: K (coupling matrix), omegas (frequencies).
| Method | Signature | Description |
|---|---|---|
__call__ |
(phases) → final_phases |
Forward pass |
forward_with_trajectory |
(phases) → (final, trajectory) |
With full trajectory |
sync_score |
(phases) → R |
Order parameter after forward pass |
kuramoto_layer ¶
Equinox module wrapping Kuramoto dynamics as a differentiable layer.
The KuramotoLayer maps input features to oscillator phases, runs N steps of Kuramoto dynamics with a learnable coupling matrix K, and returns the synchronized phase representation. Fully differentiable via JAX autodiff.
Requires: jax>=0.4, equinox>=0.11
Classes¶
KuramotoLayer ¶
KuramotoLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
mask: Array | None = None,
*,
key: Array,
)
Bases: Module
Differentiable Kuramoto oscillator layer.
Learnable parameters
K: (n, n) coupling matrix — controls which oscillators synchronize omegas: (n,) natural frequencies
Static config
n_steps: integration steps per forward pass dt: integration timestep
Source code in src/scpn_phase_orchestrator/nn/kuramoto_layer.py
Attributes¶
coupling
property
¶
Symmetric coupling matrix used by the dynamics.
Kuramoto coupling is undirected, so the dynamics use the symmetric
part (K + Kᵀ)/2. Because the loss depends only on this symmetric
part, the gradient with respect to K is itself symmetric, so
gradient training started from a symmetric K keeps K = Kᵀ
instead of drifting into a physically meaningless directed matrix.
Returns¶
jax.Array Symmetric coupling matrix used by the dynamics.
Methods:¶
__call__ ¶
Run Kuramoto dynamics on input phases.
Parameters¶
phases : jax.Array (n,) initial phase angles in [0, 2pi).
Returns¶
jax.Array (n,) phase angles after n_steps of Kuramoto integration.
Source code in src/scpn_phase_orchestrator/nn/kuramoto_layer.py
forward_with_trajectory ¶
Run dynamics and return both final state and full trajectory.
Parameters¶
phases : jax.Array (n,) initial phase angles.
Returns¶
tuple[jax.Array, jax.Array] (final_phases, trajectory) where trajectory is (n_steps, n).
Source code in src/scpn_phase_orchestrator/nn/kuramoto_layer.py
sync_score ¶
Run dynamics and return final synchronization (order parameter R).
Useful as a differentiable loss target: maximize R for sync, minimize R for desync.
Parameters¶
phases : jax.Array (n,) initial phases.
Returns¶
jax.Array Scalar R in [0, 1].
Source code in src/scpn_phase_orchestrator/nn/kuramoto_layer.py
Functions:¶
SimplicialKuramotoLayer¶
Extends KuramotoLayer with learnable 3-body interaction strength σ₂.
SimplicialKuramotoLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
sigma2_init: float = 0.0, # initial 3-body strength
key: jax.Array,
)
Learnable parameters: K, omegas, sigma2.
When sigma2=0, output matches KuramotoLayer (verified in tests).
simplicial_layer ¶
Equinox module wrapping simplicial (3-body) Kuramoto dynamics.
Extends KuramotoLayer with a learnable 3-body coupling strength sigma2. When sigma2=0, reduces to standard pairwise Kuramoto. Nonzero sigma2 produces explosive (first-order) synchronization transitions (Gambuzza et al. 2023, Nature Physics).
First differentiable 3-body Kuramoto layer in open source.
Requires: jax>=0.4, equinox>=0.11
Classes¶
SimplicialKuramotoLayer ¶
SimplicialKuramotoLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
sigma2_init: float = 0.0,
*,
key: Array,
)
Bases: Module
Differentiable simplicial Kuramoto layer with 3-body interactions.
Learnable parameters
K: (n, n) pairwise coupling matrix omegas: (n,) natural frequencies sigma2: scalar 3-body coupling strength
Static config
n_steps: integration steps per forward pass dt: integration timestep
Source code in src/scpn_phase_orchestrator/nn/simplicial_layer.py
Attributes¶
coupling
property
¶
Symmetric pairwise coupling matrix used by the dynamics (K + Kᵀ)/2.
Pairwise coupling is undirected, so the loss depends only on the
symmetric part; the gradient w.r.t. K is therefore symmetric and
training from a symmetric K keeps it symmetric rather than drifting
into a directed matrix.
Returns¶
jax.Array
Symmetric pairwise coupling matrix used by the dynamics (K + Kᵀ)/2.
Methods:¶
__call__ ¶
Run simplicial Kuramoto dynamics on input phases.
Parameters¶
phases : jax.Array (n,) initial phase angles in [0, 2pi).
Returns¶
jax.Array (n,) phase angles after n_steps of integration.
Source code in src/scpn_phase_orchestrator/nn/simplicial_layer.py
forward_with_trajectory ¶
Run dynamics and return both final state and full trajectory.
Parameters¶
phases : jax.Array (n,) initial phase angles.
Returns¶
tuple[jax.Array, jax.Array] (final_phases, trajectory) where trajectory is (n_steps, n).
Source code in src/scpn_phase_orchestrator/nn/simplicial_layer.py
sync_score ¶
Run dynamics and return final synchronization (order parameter R).
Parameters¶
phases : jax.Array (n,) initial phases.
Returns¶
jax.Array Scalar R in [0, 1].
Source code in src/scpn_phase_orchestrator/nn/simplicial_layer.py
Functions:¶
StuartLandauLayer¶
Phase + amplitude dynamics with learnable bifurcation parameters.
StuartLandauLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
epsilon: float = 1.0,
key: jax.Array,
)
Learnable parameters: K, K_r (amplitude coupling), omegas, mu (bifurcation).
| Method | Returns | Description |
|---|---|---|
__call__ |
(phases, amps) |
Forward phase + amplitude |
sync_score |
scalar | R from final phases |
mean_amplitude |
scalar | Mean amplitude after forward |
stuart_landau_layer ¶
Equinox module wrapping Stuart-Landau dynamics as a differentiable layer.
Unlike the Kuramoto-only KuramotoLayer, this layer has both phase AND amplitude dynamics, enabling representation of feature presence/absence (amplitude) alongside binding relationships (phase).
Solves AKOrN's limitations: amplitude allows memory, no N>32 degradation, supercritical/subcritical bifurcation as a natural activation gate.
Requires: jax>=0.4, equinox>=0.11
Classes¶
StuartLandauLayer ¶
StuartLandauLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
epsilon: float = 1.0,
*,
key: Array,
)
Bases: Module
Differentiable Stuart-Landau oscillator layer.
Learnable parameters
K: (n, n) phase coupling matrix K_r: (n, n) amplitude coupling matrix omegas: (n,) natural frequencies mu: (n,) bifurcation parameters (>0: supercritical, <0: subcritical)
Static config
n_steps: integration steps per forward pass dt: integration timestep epsilon: amplitude coupling strength
Source code in src/scpn_phase_orchestrator/nn/stuart_landau_layer.py
Attributes¶
coupling
property
¶
Symmetric phase-coupling matrix used by the dynamics (K + Kᵀ)/2.
Phase coupling is undirected, so the loss depends only on the symmetric
part; the gradient w.r.t. K is therefore symmetric and training from
a symmetric K keeps it symmetric instead of drifting directed.
Returns¶
jax.Array
Symmetric phase-coupling matrix used by the dynamics (K + Kᵀ)/2.
coupling_r
property
¶
Symmetric amplitude-coupling matrix (K_r + K_rᵀ)/2 (see coupling).
Returns¶
jax.Array
Symmetric amplitude-coupling matrix (K_r + K_rᵀ)/2 (see coupling).
Methods:¶
__call__ ¶
Run Stuart-Landau dynamics on input state.
Parameters¶
phases : jax.Array (n,) initial phase angles in [0, 2pi). amplitudes : jax.Array (n,) initial amplitudes (r >= 0).
Returns¶
tuple[jax.Array, jax.Array] (final_phases, final_amplitudes).
Source code in src/scpn_phase_orchestrator/nn/stuart_landau_layer.py
forward_with_trajectory ¶
forward_with_trajectory(
phases: Array, amplitudes: Array
) -> tuple[jax.Array, jax.Array, jax.Array, jax.Array]
Run dynamics and return full trajectories.
Returns¶
(final_phases, final_amplitudes, phase_trajectory, amplitude_trajectory)
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
amplitudes : jax.Array
Oscillator amplitudes, shape (N,).
Source code in src/scpn_phase_orchestrator/nn/stuart_landau_layer.py
sync_score ¶
Run dynamics and return final synchronization (order parameter R).
Parameters¶
phases : jax.Array (n,) initial phases. amplitudes : jax.Array (n,) initial amplitudes.
Returns¶
jax.Array Scalar R in [0, 1].
Source code in src/scpn_phase_orchestrator/nn/stuart_landau_layer.py
mean_amplitude ¶
Run dynamics and return mean final amplitude.
Useful as a differentiable activity measure: high mean amplitude means oscillators are active (supercritical), low means quiescent.
Parameters¶
phases : jax.Array (n,) initial phases. amplitudes : jax.Array (n,) initial amplitudes.
Returns¶
jax.Array Scalar mean amplitude.
Source code in src/scpn_phase_orchestrator/nn/stuart_landau_layer.py
Functions:¶
BOLD Signal Generator¶
Balloon-Windkessel hemodynamic model converting oscillator amplitudes to simulated fMRI BOLD signal. Differentiable for gradient-based fMRI fitting.
| Function | Description |
|---|---|
balloon_windkessel_step |
One Euler step of BW hemodynamics |
bold_signal |
V,Q → BOLD observation equation |
bold_from_neural |
Full neural → BOLD conversion |
State variables: signal (s), flow (f), volume (v), deoxyhemoglobin (q).
bold ¶
Balloon-Windkessel hemodynamic model in JAX.
Converts neural activity (oscillator amplitude envelope) to simulated fMRI BOLD signal. Fully differentiable for gradient-based optimization of oscillator parameters to match empirical fMRI data.
Friston et al. 2000 (Balloon model), Stephan et al. 2007 (parameters). Requires: jax>=0.4
Functions:¶
balloon_windkessel_step ¶
balloon_windkessel_step(
s: Array,
f: Array,
v: Array,
q: Array,
x: Array,
dt: float,
kappa: float = KAPPA,
gamma: float = GAMMA,
tau: float = TAU,
alpha: float = ALPHA,
e0: float = E0,
) -> tuple[jax.Array, jax.Array, jax.Array, jax.Array]
Single Euler step of the Balloon-Windkessel hemodynamic model.
Parameters¶
s : jax.Array (N,) vasodilatory signal. f : jax.Array (N,) blood inflow (normalised, resting=1). v : jax.Array (N,) blood volume (normalised, resting=1). q : jax.Array (N,) deoxyhaemoglobin content (normalised, resting=1). x : jax.Array (N,) neural input (amplitude envelope). dt : float integration timestep (seconds). kappa : float signal decay rate (default 0.65). gamma : float flow-dependent elimination (default 0.41). tau : float haemodynamic transit time (default 0.98). alpha : float Grubb's vessel stiffness exponent (default 0.32). e0 : float resting oxygen extraction fraction (default 0.34).
Returns¶
tuple[jax.Array, jax.Array, jax.Array, jax.Array] (new_s, new_f, new_v, new_q).
Source code in src/scpn_phase_orchestrator/nn/bold.py
bold_signal ¶
bold_signal(
v: Array,
q: Array,
v0: float = V0,
k1: float = K1,
k2: float = K2,
k3: float = K3,
) -> jax.Array
Compute BOLD signal from blood volume and deoxyhemoglobin.
Parameters¶
v : jax.Array (N,) or (T, N) blood volume. q : jax.Array (N,) or (T, N) deoxyhaemoglobin.
Returns¶
jax.Array BOLD signal, same shape as input.
Source code in src/scpn_phase_orchestrator/nn/bold.py
bold_from_neural ¶
bold_from_neural(
neural: Array,
dt: float,
dt_bold: float = 0.5,
kappa: float = KAPPA,
gamma: float = GAMMA,
tau: float = TAU,
alpha: float = ALPHA,
e0: float = E0,
) -> jax.Array
Generate BOLD signal from neural activity time series.
Runs the Balloon-Windkessel model on the neural input and returns the BOLD signal at a lower sampling rate (TR = dt_bold).
Parameters¶
neural : jax.Array (T, N) neural activity time series (e.g., amplitude envelope). dt : float simulation timestep (seconds). dt_bold : float BOLD sampling period (seconds, default 0.5s = 2Hz). kappa : float signal decay rate (default 0.65). gamma : float flow-dependent elimination (default 0.41). tau : float haemodynamic transit time (default 0.98). alpha : float Grubb's vessel stiffness exponent (default 0.32). e0 : float resting oxygen extraction fraction (default 0.34).
Returns¶
jax.Array (T_bold, N) BOLD signal, where T_bold = T * dt / dt_bold.
Source code in src/scpn_phase_orchestrator/nn/bold.py
Reservoir Computing¶
Kuramoto-based echo state network with linear readout.
| Function | Description |
|---|---|
reservoir_features(phases) |
cos/sin feature extraction |
reservoir_drive(phases, omegas, K, W_in, u, dt, n_steps) |
Driven reservoir dynamics |
ridge_readout(features, targets, alpha=1e-4) |
Ridge regression readout weights |
reservoir_predict(features, W_out) |
Prediction from trained readout |
Universal approximation near edge-of-bifurcation (arXiv:2407.16172).
reservoir ¶
Kuramoto-based reservoir computing in JAX.
Uses a Kuramoto oscillator network as a nonlinear reservoir. Input signals modulate natural frequencies; the reservoir's phase state is read out via a trained linear layer.
Theory: universal approximation near edge-of-bifurcation (arXiv:2407.16172, 2024). The Ott-Antonsen critical coupling K_c = 2*Delta defines the optimal operating point.
Requires: jax>=0.4
Functions:¶
reservoir_features ¶
Extract features from oscillator phases for readout.
Features: [cos(theta_1), sin(theta_1), ..., cos(theta_N), sin(theta_N), R] Total: 2*N + 1 features.
Parameters¶
phases : jax.Array (N,) oscillator phases.
Returns¶
jax.Array (2*N + 1,) feature vector.
Source code in src/scpn_phase_orchestrator/nn/reservoir.py
reservoir_drive ¶
reservoir_drive(
phases: Array,
omegas: Array,
K: Array,
W_in: Array,
u: Array,
dt: float,
n_steps: int,
) -> jax.Array
Drive reservoir with input signal and collect features at each step.
Input is injected into natural frequencies: omega_i(t) = omega_i + W_in @ u(t).
Parameters¶
phases : jax.Array (N,) initial oscillator phases. omegas : jax.Array (N,) base natural frequencies. K : jax.Array (N, N) fixed coupling matrix. W_in : jax.Array (N, D_in) input weight matrix. u : jax.Array (T, D_in) input signal sequence. dt : float integration timestep. n_steps : int Kuramoto steps per input sample.
Returns¶
jax.Array (T, 2*N + 1) feature matrix for readout training.
Source code in src/scpn_phase_orchestrator/nn/reservoir.py
ridge_readout ¶
Train linear readout via ridge regression.
W_out = (F^T F + alpha I)^{-1} F^T Y
Parameters¶
features : jax.Array (T, D_feat) reservoir feature matrix. targets : jax.Array (T, D_out) target outputs. alpha : float L2 regularization strength.
Returns¶
jax.Array (D_feat, D_out) readout weight matrix.
Source code in src/scpn_phase_orchestrator/nn/reservoir.py
reservoir_predict ¶
Apply trained readout to reservoir features.
Parameters¶
features : jax.Array (T, D_feat) feature matrix. W_out : jax.Array (D_feat, D_out) readout weights.
Returns¶
jax.Array (T, D_out) predictions.
Source code in src/scpn_phase_orchestrator/nn/reservoir.py
Differentiable Chimera Metrics¶
JAX-native local order-parameter and chimera-index helpers for gradient-aware topology searches.
chimera ¶
JAX-based chimera state detection for coupled oscillator networks.
Chimera states are spatiotemporal patterns where synchronised and incoherent domains coexist (Kuramoto & Battogtokh 2002). This module provides differentiable detection, enabling gradient-based search for chimera-producing coupling matrices.
Requires: jax>=0.4
Functions:¶
local_order_parameter ¶
Local Kuramoto order parameter R_i for each oscillator.
R_i = |mean(exp(i·Δθ_j)) for neighbours j of i|
Neighbours defined by nonzero entries in K. Vectorised — no Python loops.
Parameters¶
phases : jax.Array (N,) oscillator phases. K : jax.Array (N, N) coupling matrix (nonzero = neighbour).
Returns¶
jax.Array (N,) local order parameters in [0, 1].
Source code in src/scpn_phase_orchestrator/nn/chimera.py
chimera_index ¶
Scalar chimera index: variance of local order parameters.
High variance = coexistence of coherent (R≈1) and incoherent (R≈0) domains. Zero variance = uniform state (either all sync or all desync). Differentiable.
Parameters¶
phases : jax.Array (N,) oscillator phases. K : jax.Array (N, N) coupling matrix.
Returns¶
jax.Array Scalar chimera index (higher = more chimera-like).
Source code in src/scpn_phase_orchestrator/nn/chimera.py
detect_chimera ¶
detect_chimera(
phases: Array,
K: Array,
coherent_threshold: float = 0.8,
incoherent_threshold: float = 0.3,
) -> tuple[jax.Array, jax.Array]
Classify oscillators as coherent or incoherent.
Parameters¶
phases : jax.Array (N,) oscillator phases. K : jax.Array (N, N) coupling matrix. coherent_threshold : float R_i above this → coherent. incoherent_threshold : float R_i below this → incoherent.
Returns¶
tuple[jax.Array, jax.Array] (coherent_mask, incoherent_mask): (N,) boolean arrays.
Source code in src/scpn_phase_orchestrator/nn/chimera.py
Differentiable Spectral Metrics¶
JAX-native graph Laplacian metrics used by topology and synchronisability experiments.
spectral ¶
Differentiable spectral metrics for coupling matrix analysis.
All functions are differentiable via jnp.linalg.eigh, enabling gradient-based topology optimisation: find the sparsest K that maintains synchronisability above a target threshold.
Requires: jax>=0.4
Functions:¶
laplacian_spectrum ¶
Sorted eigenvalues of the graph Laplacian L = D - K.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix (non-negative weights).
Returns¶
jax.Array (N,) eigenvalues in ascending order. First is ~0 (connected graph).
Source code in src/scpn_phase_orchestrator/nn/spectral.py
algebraic_connectivity ¶
Second-smallest Laplacian eigenvalue (Fiedler value).
Measures how well-connected the network is. Zero iff disconnected. Differentiable — gradient flows through eigh.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix.
Returns¶
jax.Array Scalar lambda_2.
Source code in src/scpn_phase_orchestrator/nn/spectral.py
eigenratio ¶
Ratio lambda_N / lambda_2 (synchronisability metric).
Lower eigenratio = more synchronisable (Barahona & Pecora 2002). The MSF (master stability function) approach shows that coupled oscillators synchronise when all transverse eigenvalues fall within the MSF stability interval.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix.
Returns¶
jax.Array Scalar lambda_N / lambda_2.
Source code in src/scpn_phase_orchestrator/nn/spectral.py
sync_threshold ¶
Critical coupling strength estimate (Dorfler & Bullo 2014).
K_c ≈ max|ω_i - ω_j| / lambda_2
Below K_c, the network cannot synchronise. Above, it can.
Parameters¶
K : jax.Array (N, N) symmetric coupling matrix. omegas : jax.Array (N,) natural frequencies.
Returns¶
jax.Array Scalar estimated critical coupling.
Source code in src/scpn_phase_orchestrator/nn/spectral.py
Theta Neuron Dynamics¶
Differentiable Ermentrout-Kopell theta-neuron dynamics for excitable systems.
theta_neuron ¶
Theta neuron (Ermentrout-Kopell canonical model) for coupled excitable systems.
dθ_i/dt = (1 - cos(θ_i)) + (1 + cos(θ_i)) · (η_i + I_syn_i)
where I_syn_i = Σ_j K_ij · (1 - cos(θ_j)) is synaptic input.
The theta neuron is the canonical model for Type I neuronal excitability (Ermentrout & Kopell 1986). Unlike Kuramoto oscillators which are always oscillating, theta neurons can be excitable (η < 0) — they fire only when driven by sufficient synaptic input.
Requires: jax>=0.4
Classes¶
ThetaNeuronLayer ¶
ThetaNeuronLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
eta_mean: float = -0.5,
*,
key: Array,
)
Bases: Module
Differentiable theta neuron layer.
Learnable parameters
K: (n, n) synaptic coupling matrix eta: (n,) excitability parameters
Static config
n_steps, dt
Source code in src/scpn_phase_orchestrator/nn/theta_neuron.py
Methods:¶
__call__ ¶
Run theta neuron dynamics on input phases.
Source code in src/scpn_phase_orchestrator/nn/theta_neuron.py
forward_with_trajectory ¶
Run dynamics and return full trajectory.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
Returns¶
tuple[jax.Array, jax.Array] The final phases and the full trajectory.
Source code in src/scpn_phase_orchestrator/nn/theta_neuron.py
Functions:¶
theta_neuron_step ¶
Single Euler step of the theta neuron model.
Parameters¶
phases : jax.Array (N,) neuron phases in [0, 2pi). eta : jax.Array (N,) excitability parameters (η>0: oscillatory, η<0: excitable). K : jax.Array (N, N) synaptic coupling matrix. dt : float integration timestep.
Returns¶
jax.Array (N,) updated phases.
Source code in src/scpn_phase_orchestrator/nn/theta_neuron.py
theta_neuron_rk4_step ¶
Single RK4 step of the theta neuron model.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
eta : jax.Array
Per-neuron excitability parameters, shape (N,).
K : jax.Array
Coupling matrix K, shape (N, N).
dt : float
Integration step size.
Returns¶
jax.Array The phases after one theta-neuron RK4 step.
Source code in src/scpn_phase_orchestrator/nn/theta_neuron.py
theta_neuron_forward ¶
theta_neuron_forward(
phases: Array,
eta: Array,
K: Array,
dt: float,
n_steps: int,
method: str = "rk4",
) -> tuple[jax.Array, jax.Array]
Run N steps of theta neuron dynamics.
Parameters¶
phases : jax.Array (N,) initial phases. eta : jax.Array (N,) excitability parameters. K : jax.Array (N, N) synaptic coupling. dt : float timestep. n_steps : int integration steps. method : str "rk4" or "euler".
Returns¶
tuple[jax.Array, jax.Array] (final, trajectory) where trajectory is (n_steps, N).
Source code in src/scpn_phase_orchestrator/nn/theta_neuron.py
UDE-Kuramoto (Universal Differential Equation)¶
Physics backbone (sin(Δθ) coupling) plus a learned neural residual. The MLP handles model mismatch that the analytical Kuramoto model cannot capture.
CouplingResidual (eqx.Module)¶
Small MLP: Linear(1, hidden) → tanh → Linear(hidden, 1).
UDEKuramotoLayer (eqx.Module)¶
Learnable: K, omegas, residual (CouplingResidual MLP).
ude ¶
UDE-Kuramoto: physics backbone + learned neural residual.
dθ_i/dt = ω_i + Σ_j K_ij · [sin(θ_j - θ_i) + NN_φ(θ_j - θ_i)]
The known Kuramoto structure provides the mechanistic backbone. A small neural network NN_φ handles model mismatch: higher harmonics, asymmetric coupling, amplitude-dependent effects. Trained end-to-end via JAX autodiff.
Rackauckas et al. 2020 (UDE framework); Frontiers Comp. Neuro. 2025. First Python UDE implementation for oscillator networks.
Requires: jax>=0.4, equinox>=0.11
Classes¶
CouplingResidual ¶
Bases: Module
Small MLP that learns the residual coupling function.
Maps phase difference Δθ → correction to sin(Δθ).
Source code in src/scpn_phase_orchestrator/nn/ude.py
Methods:¶
__call__ ¶
Evaluate residual for a single phase difference scalar.
The output is squashed to [-1, 1] with tanh: a physical coupling
function of a phase difference is bounded (the sin backbone has
magnitude ≤ 1), so the learned correction must be too. Without the bound
the linear output head extrapolates without limit on phase differences
unseen during training, and forward integration outside the training
window diverges to NaN.
Source code in src/scpn_phase_orchestrator/nn/ude.py
UDEKuramotoLayer ¶
UDEKuramotoLayer(
n: int,
n_steps: int = 50,
dt: float = 0.01,
K_scale: float = 0.1,
hidden: int = 16,
*,
key: Array,
)
Bases: Module
UDE-Kuramoto layer: physics backbone + learned residual.
Learnable parameters
K: (n, n) coupling matrix omegas: (n,) natural frequencies residual: CouplingResidual MLP
Source code in src/scpn_phase_orchestrator/nn/ude.py
Methods:¶
__call__ ¶
Integrate the UDE-Kuramoto forward map and return the final phases.
Source code in src/scpn_phase_orchestrator/nn/ude.py
forward_with_trajectory ¶
Run dynamics and return (final_phases, trajectory).
The backend is validated here, outside the compiled region, so an invalid value fails fast with a plain Python error rather than a tracing-time exception; each backend body is a separately compiled helper.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
backend : str
Integration backend. "euler" (default) is the reproducible
explicit jax.lax.scan map whose fixed grid keeps trajectory
hashes stable; "diffrax" routes through
:func:scpn_phase_orchestrator.nn.neural_ode.solve_ude_adjoint,
an adaptive solver under a checkpointed continuous adjoint that
samples the same n_steps grid, giving O(1)-memory training
gradients. Requires the diffrax dependency.
Returns¶
tuple[jax.Array, jax.Array]
The final phases (shape (N,)) and the trajectory (shape
(n_steps, N)).
Raises¶
ValueError
If backend is neither "euler" nor "diffrax".
Source code in src/scpn_phase_orchestrator/nn/ude.py
sync_score ¶
Kuramoto order parameter R after running the layer forward.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
Returns¶
jax.Array
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/nn/ude.py
Functions:¶
ude_kuramoto_step ¶
ude_kuramoto_step(
phases: Array,
omegas: Array,
K: Array,
residual_fn: CouplingResidual,
dt: float,
) -> jax.Array
Single Euler step of UDE-Kuramoto.
Parameters¶
phases : jax.Array (N,) oscillator phases. omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling matrix. residual_fn : CouplingResidual learned coupling correction. dt : float integration timestep.
Returns¶
jax.Array (N,) updated phases.
Source code in src/scpn_phase_orchestrator/nn/ude.py
ude_kuramoto_forward ¶
ude_kuramoto_forward(
phases: Array,
omegas: Array,
K: Array,
residual_fn: CouplingResidual,
dt: float,
n_steps: int,
) -> tuple[jax.Array, jax.Array]
Run N steps of UDE-Kuramoto, returning final state and trajectory.
Parameters¶
phases : jax.Array (N,) initial phases. omegas : jax.Array (N,) natural frequencies. K : jax.Array (N, N) coupling matrix. residual_fn : CouplingResidual learned coupling correction. dt : float integration timestep. n_steps : int number of steps.
Returns¶
tuple[jax.Array, jax.Array] (final_phases, trajectory).
Source code in src/scpn_phase_orchestrator/nn/ude.py
Neural-ODE continuous adjoint (diffrax)¶
The explicit Euler map stores every step, so reverse-mode gradients cost
O(n_steps) memory. solve_ude_adjoint integrates the same UDE-Kuramoto
vector field with an adaptive solver (diffrax.Tsit5 by default) under a
configurable adjoint — RecursiveCheckpointAdjoint (logarithmic checkpointing)
or BacksolveAdjoint (O(1) memory). Integration runs on the unwrapped phase
(the coupling is 2π-periodic, so the field is wrap-invariant while an adaptive
solver must not see the % 2π discontinuities); wrapping is applied once, to the
returned states. The solver never mutates the global jax_enable_x64 flag, so
the dtype of every intermediate follows the input arrays.
solve_ude_adjoint(phases, omegas, K, residual_fn, *, t1, dt0=0.01,
solver=None, adjoint=None, rtol=1e-6, atol=1e-6,
max_steps=4096, saveat_ts=None, wrap=True)
Requires the diffrax dependency (the nn, jax, or full extra).
neural_ode ¶
Continuous-time adjoint integration of the UDE-Kuramoto vector field.
The explicit jax.lax.scan Euler map in :mod:scpn_phase_orchestrator.nn.ude
stores every intermediate state, so reverse-mode gradients cost O(n_steps)
memory. This module integrates the same vector field
dθ_i/dt = ω_i + Σ_j K_ij · [sin(θ_j − θ_i) + NN_φ(θ_j − θ_i)]
with an adaptive higher-order solver (diffrax.Tsit5 by default) under a
configurable adjoint. diffrax.RecursiveCheckpointAdjoint gives logarithmic
checkpointing; diffrax.BacksolveAdjoint reconstructs the forward trajectory
backwards for O(1) memory. Both differentiate through the coupling matrix
K and the learned residual, so this is the production gradient path the
finite-difference estimator in :mod:scpn_phase_orchestrator.upde.adjoint
approximates.
The integration runs on the unwrapped phase: the coupling depends only on
phase differences and sin is 2π-periodic, so the vector field is
invariant to wrapping, while an adaptive solver must not see the % 2π
discontinuities that the Euler map introduces at each step. Wrapping is applied
once, to the returned states.
The dtype of every intermediate follows the input arrays — the solver never
mutates the global jax_enable_x64 flag, so callers keep the float32 default
of the rest of nn unless they opt into x64 themselves.
Requires: jax>=0.4, equinox>=0.11, diffrax>=0.5.
Classes¶
Functions:¶
solve_ude_adjoint ¶
solve_ude_adjoint(
phases: Array,
omegas: Array,
K: Array,
residual_fn: CouplingResidual,
*,
t1: float,
dt0: float = 0.01,
solver: AbstractSolver[Any] | None = None,
adjoint: AbstractAdjoint | None = None,
rtol: float = 1e-06,
atol: float = 1e-06,
max_steps: int = 4096,
saveat_ts: Array | None = None,
wrap: bool = True,
throw: bool = True,
) -> jax.Array
Integrate UDE-Kuramoto with an adaptive solver and continuous adjoint.
Parameters¶
phases : jax.Array
Initial oscillator phases in radians, shape (N,).
omegas : jax.Array
Natural frequencies in rad/s, shape (N,).
K : jax.Array
Coupling matrix, shape (N, N).
residual_fn : CouplingResidual
Learned per-pair coupling correction.
t1 : float
Final integration time; the interval is [0, t1]. Must be positive.
dt0 : float
Initial step size handed to the adaptive controller. Must be positive.
solver : diffrax.AbstractSolver or None
The ODE solver. Defaults to :class:diffrax.Tsit5 (5th-order adaptive).
adjoint : diffrax.AbstractAdjoint or None
The reverse-mode strategy. Defaults to
:class:diffrax.RecursiveCheckpointAdjoint; pass
:class:diffrax.BacksolveAdjoint for O(1) memory.
rtol : float
Relative tolerance for the PID step-size controller. Must be positive.
atol : float
Absolute tolerance for the PID step-size controller. Must be positive.
max_steps : int
Upper bound on solver steps. Must be positive.
saveat_ts : jax.Array or None
Times at which to save the trajectory. None saves only the final
state and returns shape (N,); a length-T array returns shape
(T, N).
wrap : bool
When True (default) the returned phases are wrapped into
[0, 2π); when False the unwrapped phases are returned.
throw : bool
Stiffness guard. When True (default) a solve that exhausts
max_steps — the symptom of a stiff or blowing-up field — raises
instead of silently returning non-finite phases, so a diverging
integration can never masquerade as a valid result. Set False to
recover the non-finite solution for inspection (e.g. to locate the
offending oscillator) rather than raising; raise max_steps or soften
the field when this trips.
Returns¶
jax.Array
The final phases (shape (N,)) when saveat_ts is None, else
the saved trajectory (shape (T, N)).
Raises¶
ValueError
If t1, dt0, rtol, atol or max_steps is not
positive, or if phases is not one-dimensional.
Source code in src/scpn_phase_orchestrator/nn/neural_ode.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
Inverse Kuramoto¶
Gradient-based inference of K and ω from observed phase trajectories.
| Function | Description |
|---|---|
infer_coupling(observed, dt, n_epochs, lr, ...) |
Full gradient descent inference |
analytical_inverse(observed, dt, alpha) |
Closed-form least-squares |
hybrid_inverse(observed, dt, ...) |
Analytical + gradient refinement |
inverse_loss(K, omegas, observed, dt, l1) |
Differentiable loss |
coupling_correlation(K_true, K_inferred) |
Pearson r for validation |
inverse ¶
Infer coupling matrix K and natural frequencies ω from observed phases.
Three methods, in order of preference:
-
analytical_inverse (Pikovsky 2008) — O(N³) linear regression on sin(Δθ) basis functions. Exact for noiseless Kuramoto, >0.95 correlation, completes in seconds. Use this by default.
-
hybrid_inverse — analytical init + gradient refinement. Handles model mismatch (noise, higher harmonics) by starting from the analytical solution and running a few Adam epochs.
-
infer_coupling — pure gradient descent through ODE solver. Kept for backward compatibility. Slow (minutes), lower accuracy.
Requires: jax>=0.4
Functions:¶
inverse_loss ¶
inverse_loss(
K: Array,
omegas: Array,
observed: Array,
dt: float,
l1_weight: float = 0.0,
) -> jax.Array
Loss for inverse Kuramoto: prediction error + optional L1 sparsity.
Runs the forward model from observed[0] and compares the predicted trajectory against the observed trajectory.
Parameters¶
K : jax.Array (N, N) coupling matrix to optimize. omegas : jax.Array (N,) natural frequencies to optimize. observed : jax.Array (T, N) observed phase trajectory. dt : float integration timestep. l1_weight : float L1 penalty on K for sparsity (0 = no penalty).
Returns¶
jax.Array Scalar loss.
Source code in src/scpn_phase_orchestrator/nn/inverse.py
analytical_inverse ¶
Recover K and ω from observed phases via linear regression.
Exploits the Kuramoto structure directly (Pikovsky 2008): dθ_i/dt = ω_i + Σ_j K_ij sin(θ_j - θ_i)
Finite-difference dθ/dt, build sin(Δθ) basis, solve via lstsq. O(N³) per oscillator, no ODE backprop, no gradient vanishing.
Parameters¶
observed : jax.Array (T, N) phase trajectory, T >= 3. dt : float integration timestep. alpha : float Tikhonov (ridge) regularisation strength. 0 = no reg.
Returns¶
tuple[jax.Array, jax.Array] (K, omegas): inferred (N, N) coupling and (N,) frequencies.
Source code in src/scpn_phase_orchestrator/nn/inverse.py
hybrid_inverse ¶
hybrid_inverse(
observed: Array,
dt: float,
alpha: float = 0.0,
n_refine: int = 50,
lr: float = 0.005,
window_size: int = 10,
) -> tuple[jax.Array, jax.Array, list[float]]
Analytical inverse + gradient refinement for noisy data.
Runs analytical_inverse() for the initial estimate, then refines with a few Adam epochs using multiple shooting. Handles model mismatch (noise, higher harmonics, amplitude effects).
Parameters¶
observed : jax.Array (T, N) phase trajectory. dt : float integration timestep. alpha : float Tikhonov regularisation for analytical step. n_refine : int Adam refinement epochs (0 = analytical only). lr : float learning rate for refinement. window_size : int shooting window size for refinement.
Returns¶
tuple[jax.Array, jax.Array, list[float]] (K, omegas, losses): inferred params + refinement loss history.
Source code in src/scpn_phase_orchestrator/nn/inverse.py
infer_coupling ¶
infer_coupling(
observed: Array,
dt: float,
n_epochs: int = 200,
lr: float = 0.01,
l1_weight: float = 0.001,
seed: int = 0,
window_size: int = 0,
grad_clip: float = 1.0,
) -> tuple[jax.Array, jax.Array, list[float]]
Infer coupling matrix K and frequencies ω from observed phases.
Uses Adam optimiser with gradient clipping and optional multiple shooting for gradient-stable training through ODE solvers.
Parameters¶
observed : jax.Array (T, N) observed phase trajectory. dt : float integration timestep used to generate the data. n_epochs : int optimisation epochs. lr : float learning rate (for Adam). l1_weight : float L1 sparsity penalty on K. seed : int random seed for initialisation. window_size : int if >0, use multiple shooting with this window size. Recommended: 10-20 steps. 0 = single-shot (original behaviour). grad_clip : float maximum gradient norm (0 = no clipping).
Returns¶
tuple[jax.Array, jax.Array, list[float]] (K, omegas, losses) where: K: (N, N) inferred coupling matrix omegas: (N,) inferred natural frequencies losses: list of loss values per epoch.
Source code in src/scpn_phase_orchestrator/nn/inverse.py
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | |
coupling_correlation ¶
Pearson correlation between true and inferred coupling matrices.
Parameters¶
K_true : jax.Array (N, N) ground truth coupling. K_inferred : jax.Array (N, N) inferred coupling.
Returns¶
jax.Array Scalar correlation in [-1, 1].
Source code in src/scpn_phase_orchestrator/nn/inverse.py
Oscillator Ising Machine (OIM)¶
Combinatorial optimisation via phase clustering. Maps graph colouring, max-cut, and QUBO to Kuramoto dynamics.
| Function | Description |
|---|---|
oim_solve(adj, n_colors, key, ...) |
Full solver with annealing + restarts |
oim_forward(phases, adj, n_colors, dt, n_steps) |
Forward integration |
extract_coloring(phases, n_colors) |
Hard colour assignment |
coloring_violations(colors, adj) |
Count constraint violations |
coloring_energy(phases, adj, n_colors) |
Continuous energy |
First open-source OIM simulator.
oim ¶
Kuramoto-based combinatorial optimization via phase clustering.
Maps NP-hard problems (graph coloring, max-cut, QUBO) to coupled oscillator dynamics. Oscillators settle into k distinct phase clusters, each cluster corresponding to a color/partition.
The coupling function is modified from standard sin(Δθ) to produce equidistant phase clusters (Nature Scientific Reports 2017, Böhm & Schumacher 2020). GPU-accelerated via JAX.
First open-source oscillator Ising machine simulator.
Requires: jax>=0.4
Functions:¶
oim_step ¶
oim_step(
phases: Array,
adjacency: Array,
n_colors: int,
dt: float,
coupling_strength: float = 1.0,
) -> jax.Array
Single step of OIM coloring dynamics.
Parameters¶
phases : jax.Array (N,) oscillator phases. adjacency : jax.Array (N, N) graph adjacency matrix (1 = edge, 0 = no edge). n_colors : int number of colors (phase clusters). dt : float integration timestep. coupling_strength : float overall coupling scale.
Returns¶
jax.Array (N,) updated phases.
Source code in src/scpn_phase_orchestrator/nn/oim.py
oim_forward ¶
oim_forward(
phases: Array,
adjacency: Array,
n_colors: int,
dt: float,
n_steps: int,
coupling_strength: float = 1.0,
) -> tuple[jax.Array, jax.Array]
Run OIM dynamics for n_steps, returning final phases and trajectory.
Parameters¶
phases : jax.Array (N,) initial random phases. adjacency : jax.Array (N, N) graph adjacency matrix. n_colors : int number of colors. dt : float timestep. n_steps : int number of integration steps. coupling_strength : float overall coupling scale.
Returns¶
tuple[jax.Array, jax.Array] (final_phases, trajectory) where trajectory is (n_steps, N).
Source code in src/scpn_phase_orchestrator/nn/oim.py
extract_coloring ¶
Extract integer color assignment from oscillator phases.
Maps each phase to the nearest cluster center at 2πk/n_colors.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2π). n_colors : int number of colors.
Returns¶
jax.Array (N,) integer colour labels in {0, 1, ..., n_colors-1}.
Source code in src/scpn_phase_orchestrator/nn/oim.py
extract_coloring_soft ¶
Extract color assignment using circular distance to cluster centres.
More accurate than floor bucketing when phases sit near bucket boundaries. Assigns each oscillator to the nearest of the n_colors equidistant cluster centres.
Parameters¶
phases : jax.Array (N,) oscillator phases in [0, 2π). n_colors : int number of colors.
Returns¶
jax.Array (N,) integer colour labels in {0, 1, ..., n_colors-1}.
Source code in src/scpn_phase_orchestrator/nn/oim.py
oim_solve ¶
oim_solve(
adjacency: Array,
n_colors: int,
*,
key: Array,
dt: float = 0.05,
k_min: float = 0.1,
k_max: float = 10.0,
n_anneal: int = 1000,
n_refine: int = 500,
n_restarts: int = 10,
) -> tuple[jax.Array, jax.Array, float]
Solve graph coloring via OIM with annealing and multi-start.
Fully vectorised: restarts run in parallel via vmap, annealing and refinement use jax.lax.scan (no Python loops). 70x faster than the sequential version on GPU.
Parameters¶
adjacency : jax.Array
(N, N) graph adjacency matrix.
n_colors : int
number of colors.
key : jax.Array
PRNG key.
dt : float
Requested integration timestep, used as an upper bound. The effective
step is reduced when k_max * coupling_n * max_degree * dt would
exceed the explicit-Euler stability radius, so the dynamics settle into
the ground state instead of overshooting it.
k_min : float
initial coupling strength (low = exploration).
k_max : float
final coupling strength (high = exploitation).
n_anneal : int
ramp-up steps.
n_refine : int
hold steps after annealing.
n_restarts : int
number of random restarts.
Returns¶
tuple[jax.Array, jax.Array, float] (best_colors, best_phases, best_energy).
Source code in src/scpn_phase_orchestrator/nn/oim.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
coloring_violations ¶
Count edges where both endpoints have the same color.
Parameters¶
colors : jax.Array (N,) integer colour labels. adjacency : jax.Array (N, N) adjacency matrix.
Returns¶
jax.Array Scalar: number of violated edges.
Source code in src/scpn_phase_orchestrator/nn/oim.py
coloring_energy ¶
Continuous energy function for the coloring problem.
E = Σ_{(i,j)∈E} cos(n_colors · (θ_i - θ_j))
Minimized when connected nodes are in different phase clusters (cos(n·Δθ) = -1 when Δθ = π/n, i.e., maximally separated). Differentiable for gradient-based optimization.
Parameters¶
phases : jax.Array (N,) oscillator phases. adjacency : jax.Array (N, N) adjacency matrix. n_colors : int number of colors.
Returns¶
jax.Array Scalar energy (lower = better colouring).
Source code in src/scpn_phase_orchestrator/nn/oim.py
Training Utilities¶
Loss functions¶
| Function | Description |
|---|---|
sync_loss(model, phases, target_R=1.0) |
(1 - R)² loss |
trajectory_loss(model, phases, observed) |
MSE on phase trajectory |
coupling_sparsity_loss(K, target_density) |
L1 sparsity penalty |
Training loop¶
from scpn_phase_orchestrator.nn.training import train
model, losses = train(
model=layer,
loss_fn=lambda m: sync_loss(m, phases),
optimizer=optax.adam(1e-3),
n_epochs=500,
callback=lambda ep, m, l: print(f"Epoch {ep}: loss={float(l):.4f}"),
)
Data generation¶
| Function | Returns |
|---|---|
generate_kuramoto_data(N, T, dt, K_scale, key) |
(K, omegas, phases_init, trajectory) |
generate_chimera_data(N, T, dt, coupling, range, key) |
(K, omegas, trajectory) |
training ¶
Loss functions, training loops, and data generation for nn/ layers.
Integrates with optax for optimisation. All loss functions are differentiable via JAX autodiff and compatible with equinox modules.
Requires: jax>=0.4, equinox>=0.11, optax>=0.2
Functions:¶
sync_loss ¶
Drive oscillator layer toward a target synchronisation level.
Parameters¶
model : eqx.Module equinox layer with call(phases) → final_phases. phases : jax.Array (N,) initial phases. target_R : float target order parameter R in [0, 1].
Returns¶
jax.Array Scalar loss (R - target_R)^2.
Source code in src/scpn_phase_orchestrator/nn/training.py
trajectory_loss ¶
trajectory_loss(
model: Module,
phases: Array,
observed: Array,
*,
backend: str = "euler",
) -> jax.Array
Fit model trajectory to observed phase data.
Uses circular distance (via cos) to handle 2pi wrapping.
Parameters¶
model : eqx.Module
equinox layer with forward_with_trajectory(phases) → (final, traj).
phases : jax.Array
(N,) initial phases.
observed : jax.Array
(T, N) observed phase trajectory.
backend : str
Integration backend forwarded to the layer. "euler" (default)
calls forward_with_trajectory(phases) unchanged, so any
trajectory-capable layer works and existing hashes are preserved.
"diffrax" requests the checkpointed continuous-adjoint path and
therefore requires a backend-aware layer such as
:class:~scpn_phase_orchestrator.nn.ude.UDEKuramotoLayer.
Returns¶
jax.Array Scalar mean circular distance.
Source code in src/scpn_phase_orchestrator/nn/training.py
coupling_sparsity_loss ¶
L1 penalty driving K toward target density.
Parameters¶
K : jax.Array (N, N) coupling matrix. target_density : float fraction of nonzero entries desired.
Returns¶
jax.Array Scalar penalty: |mean(|K|) - target_density * mean(|K|_initial)|.
Source code in src/scpn_phase_orchestrator/nn/training.py
train_step ¶
train_step(
model: Module,
loss_fn: Callable[[Module], Array],
opt_state: Any,
optimizer: GradientTransformation,
) -> tuple[eqx.Module, Any, jax.Array]
Single optimisation step using optax.
Parameters¶
model : eqx.Module equinox module to optimise. loss_fn : Callable[[eqx.Module], jax.Array] callable(model) → scalar loss. opt_state : Any optax optimiser state. optimizer : optax.GradientTransformation optax optimiser (e.g. optax.adam(1e-3)).
Returns¶
tuple[eqx.Module, Any, jax.Array] (updated_model, updated_opt_state, loss_value).
Source code in src/scpn_phase_orchestrator/nn/training.py
train ¶
train(
model: Module,
loss_fn: Callable[[Module], Array],
optimizer: GradientTransformation,
n_epochs: int,
*,
callback: Callable[[int, Module, Array], None]
| None = None,
) -> tuple[eqx.Module, list[float]]
Full training loop.
Parameters¶
model : eqx.Module equinox module to train. loss_fn : Callable[[eqx.Module], jax.Array] callable(model) → scalar loss. optimizer : optax.GradientTransformation optax optimiser. n_epochs : int number of training steps. callback : Callable[[int, eqx.Module, jax.Array], None] | None optional fn(epoch, model, loss) called each step.
Returns¶
tuple[eqx.Module, list[float]] (trained_model, loss_history).
Source code in src/scpn_phase_orchestrator/nn/training.py
generate_kuramoto_data ¶
generate_kuramoto_data(
N: int,
T: int,
dt: float = 0.01,
K_scale: float = 0.3,
*,
key: Array,
) -> tuple[jax.Array, jax.Array, jax.Array, jax.Array]
Generate synthetic Kuramoto trajectory with known ground truth.
Parameters¶
N : int number of oscillators. T : int number of timesteps. dt : float integration timestep. K_scale : float coupling matrix scale. key : jax.Array PRNG key.
Returns¶
tuple[jax.Array, jax.Array, jax.Array, jax.Array] (K_true, omegas_true, phases0, trajectory) where trajectory is (T, N).
Source code in src/scpn_phase_orchestrator/nn/training.py
generate_chimera_data ¶
generate_chimera_data(
N: int,
T: int,
dt: float = 0.01,
coupling_strength: float = 0.5,
coupling_range: int = 4,
*,
key: Array,
) -> tuple[jax.Array, jax.Array, jax.Array]
Generate chimera-producing Kuramoto dynamics on a ring.
Uses non-local coupling on a 1D ring (Kuramoto & Battogtokh 2002) that produces coexistence of synchronised and incoherent domains.
Parameters¶
N : int number of oscillators on the ring. T : int number of timesteps. dt : float timestep. coupling_strength : float overall coupling scale. coupling_range : int number of neighbours on each side. key : jax.Array PRNG key.
Returns¶
tuple[jax.Array, jax.Array, jax.Array] (K, phases0, trajectory) where K is (N, N), trajectory is (T, N).
Source code in src/scpn_phase_orchestrator/nn/training.py
Phase autoencoder¶
nn.phase_autoencoder learns the asymptotic phase, isochrons and
phase-sensitivity function of a limit-cycle oscillator from state time series
alone (Yawata, Fukami, Taira & Nakao 2024, Chaos 34, 063111). The encoder maps
the state to a three-component latent whose first two components lie on the unit
circle so that θ = atan2(Y₂, Y₁) is the asymptotic phase; the latent evolves by
an exactly-linear normal-form flow with learnable frequency ω and decay λ,
trained against a four-term reconstruction/phase/deviation/centring loss. The
trained weights are extracted to the pure-NumPy oscillators.phase_reduction
evaluator so the phase and the phase response curve are available on the control
path without JAX.
phase_autoencoder ¶
A phase autoencoder for model-free phase reduction of limit-cycle oscillators.
Classical phase reduction needs the vector field. The phase autoencoder of
Yawata, Fukami, Taira & Nakao (2024) learns the asymptotic phase, the isochrons
and the phase-sensitivity function from state time series alone. An encoder maps
the oscillator state x to a three-component latent Y = (Y₁, Y₂, Y₃) whose
first two components are constrained to the unit circle Y₁² + Y₂² = 1 so that
θ = atan2(Y₂, Y₁) is the asymptotic phase; the latent then evolves by the
exactly-linear normal-form flow
Y₁,ₜ₊τ = Y₁ cos(ωτ) − Y₂ sin(ωτ),
Y₂,ₜ₊τ = Y₁ sin(ωτ) + Y₂ cos(ωτ),
Y₃,ₜ₊τ = e^{λτ} Y₃, λ < 0,
with learnable frequency ω and decay λ; a decoder reconstructs x. The
four-term training objective ties reconstruction, the uniform phase rotation, the
amplitude decay and a centring term that prevents the trivial ω = 0 solution.
The trained encoder/decoder weights and (ω, λ) are extracted to a
:class:PhaseReductionWeights record consumed by the pure-NumPy, dependency-light
evaluator in oscillators.phase_reduction so the asymptotic phase and the
phase-sensitivity function are available on the control path without JAX.
This module follows the published latent constraint and four-term loss; the encoder and decoder are plain ReLU multilayer perceptrons (no batch normalisation, which is a training-stability detail outside the phase-reduction mathematics and would complicate the frozen-weights evaluator).
References¶
- Yawata, Fukami, Taira & Nakao 2024, Chaos 34, 063111 (arXiv:2403.06992) — phase autoencoder for limit-cycle oscillators.
Classes¶
PhaseReductionWeights
dataclass
¶
PhaseReductionWeights(
encoder_weights: tuple[FloatArray, ...],
encoder_biases: tuple[FloatArray, ...],
decoder_weights: tuple[FloatArray, ...],
decoder_biases: tuple[FloatArray, ...],
omega: float,
decay: float,
state_dim: int,
)
Frozen encoder/decoder weights and (ω, λ) of a phase autoencoder.
Parameters¶
encoder_weights, encoder_biases : tuple[numpy.ndarray, ...]
Per-layer encoder weight matrices (out, in) and bias vectors.
decoder_weights, decoder_biases : tuple[numpy.ndarray, ...]
Per-layer decoder weight matrices and bias vectors.
omega : float
The learned angular frequency ω.
decay : float
The learned amplitude decay λ < 0.
state_dim : int
The oscillator state dimension n.
PhaseAutoencoder ¶
Bases: Module
Encoder/decoder with a phase-circle latent and normal-form dynamics.
Parameters¶
state_dim : int
The oscillator state dimension n.
hidden : int
Hidden width of the encoder and decoder multilayer perceptrons.
key : jax.Array
PRNG key for weight initialisation.
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
Attributes¶
omega
property
¶
The learned angular frequency ω of the latent rotation.
Returns¶
jax.Array
The learned angular frequency ω.
decay
property
¶
The learned amplitude decay λ = −softplus(raw_decay) < 0.
Returns¶
jax.Array
The learned amplitude decay λ, strictly negative.
Methods:¶
encode_raw ¶
Encode a single state to the unnormalised latent (Ỹ₁, Ỹ₂, Ỹ₃).
Parameters¶
state : jax.Array
The oscillator state of shape (state_dim,).
Returns¶
jax.Array
The unnormalised latent of shape (3,).
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
encode ¶
Encode a single state to the phase-circle latent (Y₁, Y₂, Y₃).
Parameters¶
state : jax.Array
The oscillator state of shape (state_dim,).
Returns¶
jax.Array
The latent of shape (3,) with Y₁² + Y₂² = 1.
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
decode ¶
Decode a latent (Y₁, Y₂, Y₃) back to the oscillator state.
Parameters¶
latent : jax.Array
The latent of shape (3,).
Returns¶
jax.Array
The reconstructed oscillator state of shape (state_dim,).
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
advance ¶
Advance a latent by dt under the exactly-linear normal-form flow.
Parameters¶
latent : jax.Array
The latent of shape (3,).
dt : float
The time increment.
Returns¶
jax.Array
The advanced latent of shape (3,).
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
asymptotic_phase ¶
Return the asymptotic phase θ = atan2(Y₂, Y₁) of a state.
Parameters¶
state : jax.Array
The oscillator state of shape (state_dim,).
Returns¶
jax.Array
The asymptotic phase in (−π, π].
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
Functions:¶
extract_phase_reduction_weights ¶
Extract a trained model's weights into a frozen NumPy record.
Parameters¶
model : PhaseAutoencoder The trained phase autoencoder.
Returns¶
PhaseReductionWeights
The frozen encoder/decoder weights and (ω, λ) for the pure-NumPy
oscillators.phase_reduction evaluator.
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
phase_autoencoder_loss ¶
phase_autoencoder_loss(
model: PhaseAutoencoder,
windows: Array,
*,
dt: float,
weight_recon: float = 1.0,
weight_phase: float = 0.5,
weight_deviation: float = 0.5,
weight_aux: float = 2.0,
) -> jax.Array
Return the four-term phase-autoencoder training loss (Yawata et al. 2024).
Parameters¶
model : PhaseAutoencoder
The model under training.
windows : jax.Array
Trajectory windows of shape (batch, K + 1, state_dim) — K + 1
consecutive states sampled at spacing dt.
dt : float
The sampling interval between consecutive states in a window.
weight_recon, weight_phase, weight_deviation, weight_aux : float
The loss-term weights.
Returns¶
jax.Array The scalar total loss.
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
train_phase_autoencoder ¶
train_phase_autoencoder(
windows: Array,
*,
dt: float,
state_dim: int,
hidden: int = 64,
epochs: int = 200,
learning_rate: float = 0.001,
seed: int = 0,
loss_kwargs: dict[str, float] | None = None,
) -> tuple[PhaseAutoencoder, jax.Array]
Train a phase autoencoder on trajectory windows.
Parameters¶
windows : jax.Array
Trajectory windows of shape (batch, K + 1, state_dim).
dt : float
The sampling interval within a window.
state_dim : int
The oscillator state dimension n.
hidden : int
Hidden width of the encoder/decoder.
epochs : int
Number of full-batch Adam steps.
learning_rate : float
The Adam learning rate.
seed : int
PRNG seed for weight initialisation.
loss_kwargs : dict[str, float] | None
Optional overrides for the loss-term weights.
Returns¶
tuple[PhaseAutoencoder, jax.Array] The trained model and the final loss value.
Source code in src/scpn_phase_orchestrator/nn/phase_autoencoder.py
Differentiable Supervisor¶
nn.supervisor provides the differentiable neural policy surface for
closed-loop Kuramoto control. It is intentionally separate from
supervisor.policy.SupervisorPolicy: the neural policy remains a
JAX/equinox module trained over simulator or replay rollouts, while live
actuation still flows through ControlAction, mapper limits, and safety gates.
The built-in objective maximizes good-partition synchrony while penalizing bad-partition synchrony, control energy, and abrupt action changes. The module also includes a squashed-Gaussian action sampler and clipped PPO loss/train step for on-policy RL experiments. This is a production-quality differentiable training surface, not a claim that large-scale RL benchmarks or a preprint have already been completed.
import equinox as eqx
import jax
import jax.numpy as jnp
import optax
from scpn_phase_orchestrator.nn import (
DifferentiableSupervisorConfig,
DifferentiableSupervisorPolicy,
KuramotoSupervisorScenario,
supervisor_train_step,
)
scenario = KuramotoSupervisorScenario(
phases=jnp.array([0.0, 0.1, 2.7, 3.1]),
omegas=jnp.array([0.04, 0.03, -0.03, -0.04]),
base_K=jnp.full((4, 4), 0.03) - jnp.eye(4) * 0.03,
good_mask=jnp.array([1.0, 1.0, 0.0, 0.0]),
bad_mask=jnp.array([0.0, 0.0, 1.0, 1.0]),
dt=0.02,
inner_steps=4,
horizon=3,
)
policy = DifferentiableSupervisorPolicy(
DifferentiableSupervisorConfig(n_oscillators=4),
key=jax.random.PRNGKey(0),
)
optimizer = optax.adam(1e-3)
opt_state = optimizer.init(eqx.filter(policy, eqx.is_array))
policy, opt_state, loss = supervisor_train_step(
policy,
scenario,
opt_state,
optimizer,
)
supervisor ¶
Differentiable supervisor policies for closed-loop Kuramoto control.
This package is the JAX/equinox counterpart to
supervisor.policy.SupervisorPolicy. It keeps the learning surface fully
differentiable and array-native, then exposes a small adapter for the existing
ControlAction actuation path. Safety projection, rate limits, and live
interlocks remain outside the gradient path.
Classes¶
DifferentiableSupervisorConfig
dataclass
¶
DifferentiableSupervisorConfig(
n_oscillators: int,
hidden_width: int = 32,
hidden_depth: int = 2,
n_layer_controls: int = 2,
max_global_delta_K: float = 0.05,
max_global_delta_zeta: float = 0.1,
max_layer_delta_K: float = 0.03,
control_energy_weight: float = 0.01,
bad_sync_weight: float = 0.25,
smoothness_weight: float = 0.001,
)
Static configuration for DifferentiableSupervisorPolicy.
Parameters¶
n_oscillators : object
Number of oscillators in the controlled Kuramoto system.
hidden_width : object
Width of each MLP hidden layer.
hidden_depth : object
Number of hidden layers in the MLP.
n_layer_controls : object
Number of mask-scoped K controls. The default maps to good and bad
partitions in KuramotoSupervisorScenario.
max_global_delta_K : object
Absolute bound for global coupling increments.
max_global_delta_zeta : object
Absolute bound for global damping/drive command.
max_layer_delta_K : object
Absolute bound for partition-local coupling deltas.
control_energy_weight : object
Quadratic penalty on control action magnitude.
bad_sync_weight : object
Penalty for synchronising the bad partition.
smoothness_weight : object
Quadratic penalty on action changes over rollout.
KuramotoSupervisorScenario ¶
Bases: NamedTuple
Closed-loop differentiable Kuramoto control problem.
good_mask and bad_mask are non-negative oscillator membership
weights. Binary masks are typical, but soft memberships are supported for
differentiable curriculum construction.
SupervisorAction ¶
Bases: NamedTuple
Continuous differentiable control emitted by the neural supervisor.
SupervisorActionProjection ¶
Bases: NamedTuple
Non-actuating safety projection record for a neural supervisor proposal.
SupervisorBaselineReport ¶
Bases: NamedTuple
Aggregate audit report for supervisor baseline comparison records.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable baseline aggregation report.
Returns¶
dict[str, Any] Return a JSON-serialisable baseline aggregation report.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorCorpusReplayProposals ¶
Bases: NamedTuple
Replay-only proposal set generated from a supervisor scenario corpus.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable corpus proposal record.
Returns¶
dict[str, Any] Return a JSON-serialisable corpus proposal record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorExperimentManifest ¶
Bases: NamedTuple
Reproducibility manifest for supervisor baseline experiment artefacts.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable reproducibility manifest.
Returns¶
dict[str, Any] Return a JSON-serialisable reproducibility manifest.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorHandTunedBaselineComparison ¶
Bases: NamedTuple
Audit-only comparison against the rule-based SupervisorPolicy.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable hand-tuned-baseline comparison record.
Returns¶
dict[str, Any] Return a JSON-serialisable hand-tuned-baseline comparison record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorLearnerProposalComparison ¶
Bases: NamedTuple
Audit-only comparison against learner-shaped autotune proposal records.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable learner-proposal comparison record.
Returns¶
dict[str, Any] Return a JSON-serialisable learner-proposal comparison record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorLossAux ¶
Bases: NamedTuple
Diagnostics returned by closed_loop_supervisor_loss.
SupervisorPPOAux ¶
Bases: NamedTuple
Diagnostics returned by ppo_supervisor_loss.
SupervisorPPOBatch ¶
Bases: NamedTuple
On-policy PPO batch for the differentiable supervisor.
Arrays carry a leading batch dimension. actions must contain bounded
continuous actions produced by pack_supervisor_action.
SupervisorPPOCheckpoint ¶
Bases: NamedTuple
Loaded PPO checkpoint state for deterministic supervisor training resume.
SupervisorPPOCorpusRollout ¶
Bases: NamedTuple
Corpus-wide replay rollout with per-episode scenario provenance.
SupervisorPPORollout ¶
Bases: NamedTuple
Replay-only rollout outputs for PPO-style supervisor training.
SupervisorPPOTrainResult ¶
Bases: NamedTuple
PPO training result with checkpoint-resume bookkeeping.
SupervisorRandomBaselineComparison ¶
Bases: NamedTuple
Audit-only comparison against a seeded bounded-random action baseline.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable random-baseline comparison record.
Returns¶
dict[str, Any] Return a JSON-serialisable random-baseline comparison record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorReplayComparison ¶
Bases: NamedTuple
Audit-only comparison between neural supervisor and replay policy search.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable non-actuating comparison record.
Returns¶
dict[str, Any] Return a JSON-serialisable non-actuating comparison record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorReplayProposal ¶
Bases: NamedTuple
Replay-only neural supervisor proposal record for audit review.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable replay proposal record.
Returns¶
dict[str, Any] Return a JSON-serialisable replay proposal record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
SupervisorScenarioCorpus ¶
Bases: NamedTuple
Validated replay scenario corpus for supervisor training.
SupervisorStaticBaselineComparison ¶
Bases: NamedTuple
Audit-only comparison against a static zero-action supervisor baseline.
Methods:¶
to_audit_record ¶
Return a JSON-serialisable static-baseline comparison record.
Returns¶
dict[str, Any] Return a JSON-serialisable static-baseline comparison record.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_types.py
DifferentiableSupervisorPolicy ¶
Bases: Module
Equinox neural supervisor for differentiable Kuramoto control.
The policy consumes a compact feature vector derived from phases, masks,
and coupling statistics. Its output is a bounded continuous control action
that can be differentiated through a full jax.lax.scan rollout.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
Methods:¶
__call__ ¶
Return a bounded continuous control action for scenario.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
Functions:¶
masked_order_parameter ¶
Weighted Kuramoto order parameter for a partition of oscillators.
Parameters¶
phases : jax.Array
Oscillator phases in radians, shape (N,).
weights : jax.Array
Per-oscillator partition weights.
Returns¶
jax.Array The weighted Kuramoto order parameter.
Source code in src/scpn_phase_orchestrator/nn/supervisor/_shared.py
supervisor_action_to_candidate ¶
supervisor_action_to_candidate(
action: SupervisorAction,
*,
base: KnobPolicyCandidate | None = None,
) -> KnobPolicyCandidate
Map a supervisor action onto a candidate, relative to a base candidate.
Parameters¶
action : SupervisorAction
The supervisor control action: a global coupling delta, a global damping
delta, and per-layer coupling deltas.
base : KnobPolicyCandidate | None
The candidate the deltas are applied to. The alpha, Psi and
cross_channel_gains knobs are carried through from it unchanged.
Defaults to the zero candidate.
Returns¶
KnobPolicyCandidate
A candidate with K and zeta advanced by the global deltas and
channel_weights advanced by the per-layer coupling deltas.
Source code in src/scpn_phase_orchestrator/nn/supervisor/candidate_bridge.py
supervisor_policy_to_candidate ¶
supervisor_policy_to_candidate(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
base: KnobPolicyCandidate | None = None,
) -> KnobPolicyCandidate
Run a supervisor policy deterministically and map its action to a candidate.
Parameters¶
policy : DifferentiableSupervisorPolicy The learned supervisor policy. It is evaluated for its deterministic mean action; no stochastic sample is drawn. scenario : KuramotoSupervisorScenario The scenario the policy is evaluated on. base : KnobPolicyCandidate | None The base candidate the deltas are applied to. Defaults to the zero candidate.
Returns¶
KnobPolicyCandidate The candidate equivalent of the policy's recommendation.
Source code in src/scpn_phase_orchestrator/nn/supervisor/candidate_bridge.py
load_supervisor_ppo_checkpoint ¶
load_supervisor_ppo_checkpoint(
checkpoint_dir: str | Path,
*,
template_policy: DifferentiableSupervisorPolicy,
template_opt_state: Any,
) -> SupervisorPPOCheckpoint
Load a PPO supervisor checkpoint against explicit policy/state templates.
Parameters¶
checkpoint_dir : str | Path
Directory for training checkpoints, or None.
template_policy : DifferentiableSupervisorPolicy
Template policy used to reconstruct the checkpoint.
template_opt_state : Any
Template optimiser state used to reconstruct the checkpoint.
Returns¶
SupervisorPPOCheckpoint The loaded PPO checkpoint.
Raises¶
FileNotFoundError If the checkpoint cannot be found.
Source code in src/scpn_phase_orchestrator/nn/supervisor/checkpoint.py
save_supervisor_ppo_checkpoint ¶
save_supervisor_ppo_checkpoint(
checkpoint_dir: str | Path,
*,
policy: DifferentiableSupervisorPolicy,
opt_state: Any,
key: Array,
n_updates: int,
loss_history: Array,
metadata: dict[str, Any] | None = None,
overwrite: bool = False,
) -> Path
Persist PPO supervisor trainer state for deterministic resume.
Parameters¶
checkpoint_dir : str | Path
Directory for training checkpoints, or None.
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
opt_state : Any
The optax optimiser state.
key : jax.Array
JAX PRNG key.
n_updates : int
Number of optimiser updates performed.
loss_history : jax.Array
Recorded per-step loss history.
metadata : dict[str, Any] | None
Associated metadata mapping, or None.
overwrite : bool
Whether to overwrite an existing checkpoint.
Returns¶
Path The path of the written checkpoint.
Raises¶
NotADirectoryError
If the checkpoint directory path is not a directory.
FileExistsError
If the checkpoint already exists and overwrite is false.
Source code in src/scpn_phase_orchestrator/nn/supervisor/checkpoint.py
compare_supervisor_hand_tuned_baseline ¶
compare_supervisor_hand_tuned_baseline(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
boundary_state: BoundaryState | None = None,
comparison_label: str = "hand_tuned_supervisor_policy",
) -> SupervisorHandTunedBaselineComparison
Compare a neural supervisor proposal against rule-based SupervisorPolicy.
Parameters¶
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
scenario : KuramotoSupervisorScenario
The Kuramoto supervisor scenario.
boundary_state : BoundaryState | None
The boundary-observer state, or None.
comparison_label : str
Label identifying the comparison.
Returns¶
SupervisorHandTunedBaselineComparison The supervisor-vs-rule-based comparison.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/comparison.py
compare_supervisor_learner_proposals ¶
compare_supervisor_learner_proposals(
supervisor_proposal: SupervisorReplayProposal
| SupervisorCorpusReplayProposals,
learner_proposals: Iterable[Any],
*,
comparison_label: str = "learner_proposal_generators",
) -> SupervisorLearnerProposalComparison
Compare supervisor replay output with replay-only autotune learner proposals.
Parameters¶
supervisor_proposal : SupervisorReplayProposal | SupervisorCorpusReplayProposals The supervisor replay proposal(s). learner_proposals : Iterable[Any] The replay-only learner proposals. comparison_label : str Label identifying the comparison.
Returns¶
SupervisorLearnerProposalComparison The supervisor-vs-learner comparison.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/comparison.py
compare_supervisor_random_baseline ¶
compare_supervisor_random_baseline(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
key: Array,
comparison_label: str = "bounded_random_action",
) -> SupervisorRandomBaselineComparison
Compare one deterministic supervisor proposal against bounded randomness.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario. key : jax.Array JAX PRNG key. comparison_label : str Label identifying the comparison.
Returns¶
SupervisorRandomBaselineComparison The supervisor-vs-random comparison.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/comparison.py
compare_supervisor_replay_proposal ¶
compare_supervisor_replay_proposal(
supervisor_proposal: SupervisorReplayProposal
| SupervisorCorpusReplayProposals,
replay_policy_search: Any,
*,
comparison_label: str = "replay_policy_search",
) -> SupervisorReplayComparison
Compare supervisor replay proposals with a replay policy-search result.
The result is deliberately audit-only: it records both proposal surfaces and scalar comparison metrics, but it never authorises live actuation.
Parameters¶
supervisor_proposal : SupervisorReplayProposal | SupervisorCorpusReplayProposals The supervisor replay proposal(s). replay_policy_search : Any The replay policy-search result. comparison_label : str Label identifying the comparison.
Returns¶
SupervisorReplayComparison The supervisor-vs-policy-search comparison.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/comparison.py
compare_supervisor_static_baseline ¶
compare_supervisor_static_baseline(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
comparison_label: str = "static_zero_action",
) -> SupervisorStaticBaselineComparison
Compare one deterministic supervisor proposal against zero-action control.
This is a benchmark/audit primitive only. It runs both candidates on the same scenario and records scalar metrics without returning actuation objects or enabling any live adapter handoff.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario. comparison_label : str Label identifying the comparison.
Returns¶
SupervisorStaticBaselineComparison The supervisor-vs-zero-action comparison.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/comparison.py
apply_supervisor_action ¶
apply_supervisor_action(
base_K: Array,
action: SupervisorAction,
scenario: KuramotoSupervisorScenario,
) -> jax.Array
Apply continuous supervisor output to a symmetric coupling matrix.
Parameters¶
base_K : jax.Array Base symmetric coupling matrix. action : SupervisorAction The supervisor control action. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario.
Returns¶
jax.Array The modified symmetric coupling matrix.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
closed_loop_supervisor_loss ¶
closed_loop_supervisor_loss(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
) -> tuple[jax.Array, SupervisorLossAux]
Differentiable closed-loop objective for Kuramoto supervisor training.
The reward maximises good-partition synchrony while penalising bad-partition
synchrony, control energy, and abrupt action changes. The returned value is
a minimisation loss suitable for jax.grad or optax.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario.
Returns¶
tuple[jax.Array, SupervisorLossAux] The loss and its auxiliary metrics.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
control_actions_from_supervisor ¶
control_actions_from_supervisor(
action: SupervisorAction,
*,
ttl_s: float = 5.0,
include_layer_actions: bool = True,
) -> list[ControlAction]
Convert a detached neural supervisor output into actuation commands.
Parameters¶
action : SupervisorAction The supervisor control action. ttl_s : float Action time-to-live in seconds. include_layer_actions : bool Whether to include per-layer actions.
Returns¶
list[ControlAction] The actuation control actions.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
pack_supervisor_action ¶
Pack SupervisorAction controls into a flat continuous action vector.
Parameters¶
action : SupervisorAction The supervisor control action.
Returns¶
jax.Array The flat continuous action vector.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
sample_supervisor_action ¶
sample_supervisor_action(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
key: Array,
) -> tuple[SupervisorAction, jax.Array]
Sample a bounded squashed-Gaussian action and its log probability.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario. key : jax.Array JAX PRNG key.
Returns¶
tuple[SupervisorAction, jax.Array] The sampled action and its log probability.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
supervisor_action_bound_penalty ¶
supervisor_action_bound_penalty(
action: SupervisorAction,
config: DifferentiableSupervisorConfig,
) -> jax.Array
Differentiable quadratic penalty for proposals outside action bounds.
Parameters¶
action : SupervisorAction The supervisor control action. config : DifferentiableSupervisorConfig The supervisor configuration.
Returns¶
jax.Array The quadratic out-of-bounds penalty.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
supervisor_action_log_prob ¶
supervisor_action_log_prob(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
action: SupervisorAction,
) -> tuple[jax.Array, jax.Array, jax.Array]
Return squashed-Gaussian log probability, entropy proxy, and value.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario. action : SupervisorAction The supervisor control action.
Returns¶
tuple[jax.Array, jax.Array, jax.Array] The log probability, entropy proxy, and value.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
supervisor_train_step ¶
supervisor_train_step(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
opt_state: Any,
optimizer: GradientTransformation,
) -> tuple[DifferentiableSupervisorPolicy, Any, jax.Array]
Run one optax update for the differentiable supervisor objective.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario. opt_state : Any The optax optimiser state. optimizer : optax.GradientTransformation The optax optimiser.
Returns¶
tuple[DifferentiableSupervisorPolicy, Any, jax.Array] The updated policy, optimiser state, and loss.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
unpack_supervisor_action ¶
unpack_supervisor_action(
values: Array,
*,
value_estimate: Array,
config: DifferentiableSupervisorConfig,
) -> SupervisorAction
Unpack a flat action vector using config.n_layer_controls.
Parameters¶
values : jax.Array Flat packed action values. value_estimate : jax.Array The critic value estimate. config : DifferentiableSupervisorConfig The supervisor configuration.
Returns¶
SupervisorAction
The reconstructed SupervisorAction.
Source code in src/scpn_phase_orchestrator/nn/supervisor/policy.py
ppo_supervisor_loss ¶
ppo_supervisor_loss(
policy: DifferentiableSupervisorPolicy,
batch: SupervisorPPOBatch,
*,
clip_epsilon: float = 0.2,
value_clip: float | None = None,
value_weight: float = 0.5,
entropy_weight: float = 0.01,
) -> tuple[jax.Array, SupervisorPPOAux]
Clipped PPO objective for bounded differentiable supervisor actions.
Parameters¶
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
batch : SupervisorPPOBatch
The PPO training batch.
clip_epsilon : float
PPO clipping epsilon.
value_clip : float | None
Value-function clip range, or None.
value_weight : float
Weight of the value loss.
entropy_weight : float
Weight of the entropy bonus.
Returns¶
tuple[jax.Array, SupervisorPPOAux] The clipped PPO loss and its auxiliary metrics.
Source code in src/scpn_phase_orchestrator/nn/supervisor/ppo.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
ppo_supervisor_train_epochs ¶
ppo_supervisor_train_epochs(
policy: DifferentiableSupervisorPolicy,
batch: SupervisorPPOBatch,
key: Array,
opt_state: Any,
optimizer: GradientTransformation,
*,
n_epochs: int,
minibatch_size: int = 32,
clip_epsilon: float = 0.2,
value_clip: float | None = None,
value_weight: float = 0.5,
entropy_weight: float = 0.01,
entropy_schedule: tuple[float, ...] | None = None,
max_grad_norm: float | None = None,
kl_early_stop: float | None = None,
) -> tuple[
DifferentiableSupervisorPolicy, Any, jax.Array, int
]
Run PPO training for multiple epochs with deterministic minibatching.
Parameters¶
policy : DifferentiableSupervisorPolicy Optimisable differentiable supervisor policy. batch : SupervisorPPOBatch Flattened PPO batch from rollout collection. key : jax.Array JAX PRNG key used only for shuffle ordering. opt_state : Any Optimiser state. optimizer : optax.GradientTransformation Optax optimiser transformation. n_epochs : int Number of passes over the dataset. minibatch_size : int Per-update minibatch size. clip_epsilon : float PPO clipping radius. value_weight : float Value-function loss coefficient. entropy_weight : float Entropy bonus coefficient. entropy_schedule : tuple[float, ...] | None Optional non-negative per-update entropy weights. When shorter than the number of updates, the final value is held. max_grad_norm : float | None Optional global gradient-norm clip radius. kl_early_stop : float | None Optional KL threshold for early stopping.
Returns¶
tuple[DifferentiableSupervisorPolicy, Any, jax.Array, int] (policy, opt_state, loss_history, n_updates).
Source code in src/scpn_phase_orchestrator/nn/supervisor/ppo.py
ppo_supervisor_train_step ¶
ppo_supervisor_train_step(
policy: DifferentiableSupervisorPolicy,
batch: SupervisorPPOBatch,
opt_state: Any,
optimizer: GradientTransformation,
*,
clip_epsilon: float = 0.2,
value_clip: float | None = None,
value_weight: float = 0.5,
entropy_weight: float = 0.01,
max_grad_norm: float | None = None,
) -> tuple[DifferentiableSupervisorPolicy, Any, jax.Array]
Run one optax update using the clipped PPO supervisor objective.
Parameters¶
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
batch : SupervisorPPOBatch
The PPO training batch.
opt_state : Any
The optax optimiser state.
optimizer : optax.GradientTransformation
The optax optimiser.
clip_epsilon : float
PPO clipping epsilon.
value_clip : float | None
Value-function clip range, or None.
value_weight : float
Weight of the value loss.
entropy_weight : float
Weight of the entropy bonus.
max_grad_norm : float | None
Gradient-norm clip threshold, or None.
Returns¶
tuple[DifferentiableSupervisorPolicy, Any, jax.Array] The updated policy, optimiser state, and loss.
Source code in src/scpn_phase_orchestrator/nn/supervisor/ppo.py
ppo_supervisor_train_with_checkpoint ¶
ppo_supervisor_train_with_checkpoint(
policy: DifferentiableSupervisorPolicy,
batch: SupervisorPPOBatch,
key: Array,
opt_state: Any,
optimizer: GradientTransformation,
*,
n_epochs: int,
checkpoint_dir: str | Path | None = None,
resume: bool = False,
minibatch_size: int = 32,
clip_epsilon: float = 0.2,
value_clip: float | None = None,
value_weight: float = 0.5,
entropy_weight: float = 0.01,
entropy_schedule: tuple[float, ...] | None = None,
max_grad_norm: float | None = None,
kl_early_stop: float | None = None,
metadata: dict[str, Any] | None = None,
) -> SupervisorPPOTrainResult
Run PPO epochs and optionally checkpoint a deterministic resume state.
Parameters¶
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
batch : SupervisorPPOBatch
The PPO training batch.
key : jax.Array
JAX PRNG key.
opt_state : Any
The optax optimiser state.
optimizer : optax.GradientTransformation
The optax optimiser.
n_epochs : int
Number of training epochs.
checkpoint_dir : str | Path | None
Directory for training checkpoints, or None.
resume : bool
Whether to resume from a checkpoint.
minibatch_size : int
Minibatch size.
clip_epsilon : float
PPO clipping epsilon.
value_clip : float | None
Value-function clip range, or None.
value_weight : float
Weight of the value loss.
entropy_weight : float
Weight of the entropy bonus.
entropy_schedule : tuple[float, ...] | None
Per-epoch entropy-weight schedule, or None.
max_grad_norm : float | None
Gradient-norm clip threshold, or None.
kl_early_stop : float | None
KL early-stopping threshold, or None.
metadata : dict[str, Any] | None
Associated metadata mapping, or None.
Returns¶
SupervisorPPOTrainResult The PPO training result.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/ppo.py
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | |
project_supervisor_action_for_audit ¶
project_supervisor_action_for_audit(
action: SupervisorAction,
config: DifferentiableSupervisorConfig,
*,
previous_action: SupervisorAction | None = None,
ttl_s: float = 5.0,
max_ttl_s: float = 5.0,
rate_limit_fraction: float = 1.0,
include_layer_actions: bool = True,
regime_churn_score: float | None = None,
max_regime_churn: float | None = None,
) -> SupervisorActionProjection
Project a neural proposal into replay-safe bounds with audit metadata.
This is intentionally non-actuating. It creates the explicit audit envelope
that callers can inspect before converting a proposal into ControlAction
objects for any live adapter path.
Parameters¶
action : SupervisorAction
The supervisor control action.
config : DifferentiableSupervisorConfig
The supervisor configuration.
previous_action : SupervisorAction | None
The previous supervisor action, or None.
ttl_s : float
Action time-to-live in seconds.
max_ttl_s : float
Maximum action time-to-live in seconds.
rate_limit_fraction : float
Maximum fractional change per step.
include_layer_actions : bool
Whether to include per-layer actions.
regime_churn_score : float | None
The regime-churn score, or None.
max_regime_churn : float | None
Maximum allowed regime churn, or None.
Returns¶
SupervisorActionProjection The replay-safe action projection with audit metadata.
Source code in src/scpn_phase_orchestrator/nn/supervisor/projection.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | |
build_supervisor_baseline_report ¶
build_supervisor_baseline_report(
comparisons: Iterable[Any],
*,
report_label: str = "supervisor_baseline_report",
) -> SupervisorBaselineReport
Aggregate already-generated supervisor comparison records for review.
Parameters¶
comparisons : Iterable[Any] The supervisor comparison records. report_label : str Label for the baseline report.
Returns¶
SupervisorBaselineReport The aggregated baseline report.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/replay.py
build_supervisor_corpus_replay_proposals ¶
build_supervisor_corpus_replay_proposals(
policy: DifferentiableSupervisorPolicy,
corpus: SupervisorScenarioCorpus,
*,
previous_action: SupervisorAction | None = None,
ttl_s: float = 5.0,
max_ttl_s: float = 5.0,
rate_limit_fraction: float = 1.0,
include_layer_actions: bool = True,
regime_churn_scores: tuple[float, ...] | None = None,
max_regime_churn: float | None = None,
) -> SupervisorCorpusReplayProposals
Build deterministic replay-only proposals for every corpus scenario.
Parameters¶
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
corpus : SupervisorScenarioCorpus
The validated supervisor scenario corpus.
previous_action : SupervisorAction | None
The previous supervisor action, or None.
ttl_s : float
Action time-to-live in seconds.
max_ttl_s : float
Maximum action time-to-live in seconds.
rate_limit_fraction : float
Maximum fractional change per step.
include_layer_actions : bool
Whether to include per-layer actions.
regime_churn_scores : tuple[float, ...] | None
Per-scenario regime-churn scores, or None.
max_regime_churn : float | None
Maximum allowed regime churn, or None.
Returns¶
SupervisorCorpusReplayProposals The replay-only proposals for every corpus scenario.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/replay.py
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
build_supervisor_experiment_manifest ¶
build_supervisor_experiment_manifest(
baseline_report: SupervisorBaselineReport,
*,
command: str,
git_sha: str,
dependency_lock: Mapping[str, Any],
device_info: Mapping[str, Any],
seed_list: Iterable[int],
config_json_path: str | None = None,
metrics_jsonl_path: str | None = None,
summary_table_path: str | None = None,
checkpoint_manifest_path: str | None = None,
plot_manifest_path: str | None = None,
) -> SupervisorExperimentManifest
Build a reproducibility manifest for a supervisor baseline report.
Parameters¶
baseline_report : SupervisorBaselineReport
The aggregated baseline report.
command : str
The command line recorded with the run.
git_sha : str
Git commit SHA recorded with the run.
dependency_lock : Mapping[str, Any]
Dependency lock mapping recorded with the run.
device_info : Mapping[str, Any]
Device information recorded with the run.
seed_list : Iterable[int]
Seeds for the experiment runs.
config_json_path : str | None
Filesystem path to the config json, or None.
metrics_jsonl_path : str | None
Filesystem path to the metrics jsonl, or None.
summary_table_path : str | None
Filesystem path to the summary table, or None.
checkpoint_manifest_path : str | None
Filesystem path to the checkpoint manifest, or None.
plot_manifest_path : str | None
Filesystem path to the plot manifest, or None.
Returns¶
SupervisorExperimentManifest The reproducibility manifest.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/replay.py
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | |
build_supervisor_replay_proposal ¶
build_supervisor_replay_proposal(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
scenario_metadata: dict[str, Any] | None = None,
previous_action: SupervisorAction | None = None,
ttl_s: float = 5.0,
max_ttl_s: float = 5.0,
rate_limit_fraction: float = 1.0,
include_layer_actions: bool = True,
regime_churn_score: float | None = None,
max_regime_churn: float | None = None,
) -> SupervisorReplayProposal
Build a deterministic replay-only proposal from a neural supervisor.
The proposal is an audit artefact only. It does not return
ControlAction objects and carries actuation_permitted=False so that
downstream replay/autotune surfaces can review it without enabling live
actuation.
Parameters¶
policy : DifferentiableSupervisorPolicy
The differentiable supervisor policy.
scenario : KuramotoSupervisorScenario
The Kuramoto supervisor scenario.
scenario_metadata : dict[str, Any] | None
Scenario metadata, or None.
previous_action : SupervisorAction | None
The previous supervisor action, or None.
ttl_s : float
Action time-to-live in seconds.
max_ttl_s : float
Maximum action time-to-live in seconds.
rate_limit_fraction : float
Maximum fractional change per step.
include_layer_actions : bool
Whether to include per-layer actions.
regime_churn_score : float | None
The regime-churn score, or None.
max_regime_churn : float | None
Maximum allowed regime churn, or None.
Returns¶
SupervisorReplayProposal The replay-only supervisor proposal.
Source code in src/scpn_phase_orchestrator/nn/supervisor/replay.py
build_supervisor_scenario_corpus ¶
build_supervisor_scenario_corpus(
records: Iterable[Mapping[str, Any]],
*,
dtype: Any = jnp.float32,
) -> SupervisorScenarioCorpus
Convert replay/audit records into validated supervisor scenarios.
Parameters¶
records : Iterable[Mapping[str, Any]] Replay/audit records to convert. dtype : Any Target array dtype.
Returns¶
SupervisorScenarioCorpus The validated supervisor scenario corpus.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/rollouts.py
collect_supervisor_corpus_rollouts ¶
collect_supervisor_corpus_rollouts(
policy: DifferentiableSupervisorPolicy,
corpus: SupervisorScenarioCorpus,
*,
key: Array,
n_episodes_per_scenario: int,
gamma: float = 0.99,
gae_lambda: float = 0.95,
trajectory_jitter: float = 0.0,
) -> SupervisorPPOCorpusRollout
Collect replay-only PPO rollouts across a validated scenario corpus.
The returned batch is a flat concatenation suitable for PPO epochs. All
corpus scenarios must share dt, inner_steps, horizon, and
oscillator tensor shapes because SupervisorPPOBatch stores timing
fields once for the full batch.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. corpus : SupervisorScenarioCorpus The validated supervisor scenario corpus. key : jax.Array JAX PRNG key. n_episodes_per_scenario : int Number of episodes per corpus scenario. gamma : float Flow-dependent elimination rate. gae_lambda : float Generalised-advantage-estimation lambda. trajectory_jitter : float Trajectory jitter magnitude.
Returns¶
SupervisorPPOCorpusRollout The collected corpus PPO rollouts.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/rollouts.py
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | |
collect_supervisor_rollouts ¶
collect_supervisor_rollouts(
policy: DifferentiableSupervisorPolicy,
scenario: KuramotoSupervisorScenario,
*,
key: Array,
n_episodes: int,
gamma: float = 0.99,
gae_lambda: float = 0.95,
trajectory_jitter: float = 0.0,
) -> SupervisorPPORollout
Collect deterministic, replay-only PPO rollouts from a starting scenario.
Parameters¶
policy : DifferentiableSupervisorPolicy The differentiable supervisor policy. scenario : KuramotoSupervisorScenario The Kuramoto supervisor scenario. key : jax.Array JAX PRNG key. n_episodes : int Number of rollout episodes. gamma : float Flow-dependent elimination rate. gae_lambda : float Generalised-advantage-estimation lambda. trajectory_jitter : float Trajectory jitter magnitude.
Returns¶
SupervisorPPORollout The collected PPO rollouts.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/nn/supervisor/rollouts.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
Physics validation¶
The nn module includes 13 physics validation test files
(test_nn_physics_validation_p1 through _p13) verifying:
- Energy conservation under Hamiltonian coupling
- Gradient correctness via finite-difference comparison
- Order parameter convergence for strong coupling
- Stuart-Landau bifurcation (subcritical → supercritical)
- Simplicial explosive synchronisation
- BOLD hemodynamic response shape
- Reservoir echo state property
- UDE residual convergence
- OIM graph colouring correctness
- Inverse coupling recovery (r > 0.95)