UPDE Engine¶
The Unified Phase Dynamics Engine (UPDE) is SPO's core integrator subsystem. It provides 19 ODE engine variants covering standard Kuramoto, Bayesian uncertainty propagation, amplitude dynamics (Stuart-Landau), higher-order interactions (simplicial), inertial systems (power grids), stochastic resonance, geometric integration, time delays, financial markets, spatial-phase coupling (swarmalators), hypergraph k-body coupling, mean-field reduction, variational prediction, adjoint gradients, and bifurcation continuation.
Pipeline position¶
CouplingBuilder.build() ──→ K_nm, α
│
Oscillators.extract() ──→ θ, ω │
│
Drivers.compute() ──→ Ψ │
↓
┌─────── UPDEEngine.step(θ, ω, K, ζ, Ψ, α) ───────┐
│ │
│ Euler / RK4 / RK45 (adaptive) │
│ Optional: Rust FFI via spo_kernel │
│ │
└────────────── θ_new ∈ [0, 2π)^N ─────────────────┘
│
↓
compute_order_parameter(θ) → R, ψ
│
BayesianUPDE → posterior predictive R ± sigma
│
↓
RegimeManager.evaluate() → Regime
The engine is the computational core of SPO. Every subsystem feeds into it (coupling, oscillators, drivers) or consumes its output (order parameters, monitors, supervisor).
Engine variants¶
| Engine | State | ODE | Use case |
|---|---|---|---|
| UPDEEngine | θ ∈ [0,2π)^N | Kuramoto | General synchronisation |
| BayesianUPDE | θ plus sampled K,ω | Monte Carlo UPDE | Safety-tier uncertainty quantification |
| SparseUPDEEngine | θ ∈ [0,2π)^N | Sparse Kuramoto | High-N scalability (\(O(N \log N)\)) |
| SheafUPDEEngine | \(\vec{\theta} \in \mathbb{R}^{N \times D}\) | Cellular Sheaf | Multi-dimensional block coupling |
| StuartLandauEngine | [θ,r] ∈ R^{2N} | Stuart-Landau | Amplitude dynamics |
| SimplicialEngine | θ ∈ [0,2π)^N | 3-body Kuramoto | Triadic/group synchronization |
| InertialEngine | [θ,ω̇] ∈ R^{2N} | Swing equation | Power grids |
| SwarmalatorEngine | [x,θ] ∈ R^{(D+1)N} | Position + phase | Swarm robotics |
| StochasticInjector | θ ∈ [0,2π)^N | Euler-Maruyama | Noise resonance |
| GeometricEngine | z ∈ C^N | SO(2) exponential | Long simulations |
| DelayedEngine | θ + buffer | Delayed Kuramoto | Transport delays |
| MarketEngine | θ from Hilbert | Price → phase | Financial markets |
| SplittingEngine | θ ∈ [0,2π)^N | Symplectic split | Energy-preserving |
| HypergraphEngine | θ ∈ [0,2π)^N | k-body coupling | Mixed-order |
| OttAntosenReduction | z ∈ C | Mean-field ODE | Fast prediction |
| PredictionModel | θ ∈ [0,2π)^N | Error injection | FEP-Kuramoto |
| AdjointGradient | ∂R/∂K | Finite diff / JAX | Optimisation |
Performance budgets¶
| Operation | N | Budget | Rust path |
|---|---|---|---|
UPDEEngine.step() |
8 | < 50 μs | ~ 30 μs |
UPDEEngine.step() |
64 | < 1 ms | ~ 0.3 ms |
UPDEEngine.step() |
128 | < 5 ms | ~ 1 ms |
compute_order_parameter() |
256 | < 100 μs | ~ 2 μs |
StuartLandauEngine.step() |
32 | < 2 ms | — |
SplittingEngine.step() |
64 | < 1 ms | — |
DelayedEngine.step() |
32 | < 1 ms | — |
Core Kuramoto Engine¶
First-order Kuramoto ODE: dθ_i/dt = ω_i + Σ_j K_ij sin(θ_j - θ_i - α_ij) + ζ sin(Ψ - θ_i).
Supports Euler, RK4, and RK45 (adaptive) integration. Optional Rust FFI
acceleration via spo_kernel.PyUPDEStepper.
Direct Go, Julia, and Mojo accelerator entrypoints share the same boundary
contract before optional runtime loading: phase and frequency vectors must be
finite real one-dimensional float64 arrays with matching length; coupling
and phase-lag matrices must be finite real square matrices (or flattened
square matrices) matching oscillator count; the coupling diagonal must be
exactly zero to exclude self-coupling; dt, atol, and rtol must be
positive finite scalars; n_steps must be a non-negative integer; and
n_substeps must be a positive integer. A zero-step direct call returns a
copy of the initial phase vector without requiring the optional backend
binary or runtime. Mojo subprocess output must contain exactly one raw stdout
line per oscillator phase; blank, truncated, or overlong output is rejected
before final phase validation.
The public stateless upde_run() and upde_run_omega_schedule() entrypoints
use that same core-owned contract before backend selection. Boolean, complex,
and numeric-string aliases are rejected before conversion for phase,
frequency, coupling, phase-lag, schedule, scalar-control, and count inputs.
Python and optional-backend results then pass through one finite real-vector
cardinality check before publication; Julia returns retain their source dtype
until this check, while Mojo stdout remains an explicitly parsed text protocol.
engine ¶
Stateful :class:UPDEEngine.
The batched integrator is stateless — see
:mod:scpn_phase_orchestrator.upde._run and the re-exported
:func:upde_run. This module keeps the state-heavy observer: the
class pre-allocates scratch buffers for the chosen method, holds a
reentrant lock for thread-safety, and retains _last_dt across
RK45 step calls.
Bayesian UPDE Uncertainty Propagation¶
Samples natural frequencies and coupling matrices from explicit distributions,
runs the existing UPDE kernel for each draw, and reports posterior-predictive
R ± sigma with credible intervals and audit diagnostics.
Public phase, frequency, coupling, phase-lag, posterior-fit, and Gaussian distribution arrays reject boolean, complex, and numeric-string aliases before conversion while preserving real numeric-object arrays. Custom distribution samples replay the same source-type, shape, and finiteness checks before Monte Carlo execution, and drive controls must be finite real scalars. Reserved NumPyro and BlackJAX names remain explicitly fail-closed.
bayesian ¶
Uncertainty propagation for Kuramoto UPDE rollouts.
The shipped backend is deterministic NumPy Monte Carlo over explicit
distributions for omega and K_nm. Probabilistic-programming backends
are reserved as fail-closed names until their samplers are implemented and
benchmarked against this reproducible baseline.
Classes¶
GaussianArrayDistribution
dataclass
¶
GaussianArrayDistribution(
mean: object,
std: object,
non_negative: bool = False,
zero_diagonal: bool = False,
)
Independent Gaussian array distribution with optional matrix guards.
Attributes¶
shape
property
¶
Return the event shape sampled by this Gaussian distribution.
Returns¶
tuple[int, ...] Return the event shape sampled by this Gaussian distribution.
Methods:¶
sample ¶
Draw finite Gaussian samples with optional support guards applied.
Parameters¶
rng : np.random.Generator NumPy random generator used for sampling. n_samples : int Number of samples to draw.
Returns¶
FloatArray
Finite Gaussian samples, shape (n_samples, *event_shape).
Raises¶
TypeError
If rng is not a NumPy random generator.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
BayesianUPDEConfig
dataclass
¶
BayesianUPDEConfig(
n_samples: int = 128,
seed: int | None = None,
dt: float = 0.01,
n_steps: int = 1,
method: MethodName = "rk4",
credible_interval: float = 0.95,
backend: BackendName = "numpy",
n_substeps: int = 1,
atol: float = 1e-06,
rtol: float = 0.001,
)
Configuration for Bayesian UPDE uncertainty propagation.
BayesianUPDEResult
dataclass
¶
BayesianUPDEResult(
r_samples: FloatArray,
final_phase_samples: FloatArray,
omega_mean: FloatArray,
knm_mean: FloatArray,
r_mean: float,
r_sigma: float,
r_lower: float,
r_upper: float,
psi_mean: float,
sample_count: int,
credible_interval: float,
backend: str,
method: str,
)
Posterior predictive order-parameter summary.
Attributes¶
r_plus_minus
property
¶
Methods:¶
to_audit_record ¶
Return JSON-safe uncertainty diagnostics.
Returns¶
dict[str, object] Return JSON-safe uncertainty diagnostics.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
BayesianBackendStatus
dataclass
¶
BayesianBackendStatus(
backend: str,
available: bool,
fail_closed: bool,
reason: str,
sample_count: int,
)
Execution status for one Bayesian UPDE backend name.
Methods:¶
to_audit_record ¶
Return JSON-safe backend availability diagnostics.
Returns¶
dict[str, object] Return JSON-safe backend availability diagnostics.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
GaussianUPDEPosteriorFit
dataclass
¶
GaussianUPDEPosteriorFit(
omega: GaussianArrayDistribution,
knm: GaussianArrayDistribution,
residual_rmse: float,
sample_count: int,
dt: float,
ridge: float,
backend: str = "numpy_lstsq",
)
Gaussian posterior approximation fitted from observed phase trajectories.
Methods:¶
to_audit_record ¶
Return JSON-safe posterior-fit diagnostics.
Returns¶
dict[str, object] Return JSON-safe posterior-fit diagnostics.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
Functions:¶
fit_gaussian_upde_posterior ¶
fit_gaussian_upde_posterior(
phase_trajectory: object,
*,
dt: float,
alpha: object | None = None,
ridge: float = 1e-06,
coupling_std_floor: float = 1e-06,
omega_std_floor: float = 1e-06,
) -> GaussianUPDEPosteriorFit
Fit Gaussian omega and K_nm priors from observed phases.
The estimator is a deterministic NumPy ridge least-squares baseline. It fits the Kuramoto right-hand side independently per target oscillator:
d theta_i / dt = omega_i + sum_j K_ij sin(theta_j - theta_i - alpha_ij).
The result is intentionally review-only: it produces distributions that can
feed :func:bayesian_upde_run, but it does not apply control actions.
Parameters¶
phase_trajectory : object
Observed phase trajectory, shape (T, N).
dt : float
Integration step size.
alpha : object | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
ridge : float
Ridge-regularisation strength for the prior fit.
coupling_std_floor : float
Lower bound on the fitted coupling standard deviation.
omega_std_floor : float
Lower bound on the fitted natural-frequency standard deviation.
Returns¶
GaussianUPDEPosteriorFit
The fitted Gaussian omega and K_nm prior.
Raises¶
ValueError If the phase trajectory is empty or non-finite.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
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 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | |
audit_bayesian_backend_status ¶
audit_bayesian_backend_status(
phases: object,
*,
omega: object,
knm: object,
alpha: object,
zeta: float,
psi: float,
config: BayesianUPDEConfig | None = None,
backends: tuple[BackendName, ...] = (
"numpy",
"numpyro",
"blackjax",
),
) -> tuple[BayesianBackendStatus, ...]
Probe Bayesian backend names without silently accepting unsupported ones.
Parameters¶
phases : object
Oscillator phases in radians, shape (N,).
omega : object
Natural-frequency distribution or array.
knm : object
Coupling matrix K_nm, shape (N, N).
alpha : object
Phase-lag matrix in radians, shape (N, N). Use a zero matrix for no
lag.
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
config : BayesianUPDEConfig | None
Optional configuration object, or None for defaults.
backends : tuple[BackendName, ...]
Backend names to probe, in priority order.
Returns¶
tuple[BayesianBackendStatus, ...] Per-backend availability diagnostics.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
bayesian_upde_run ¶
bayesian_upde_run(
phases: object,
*,
omega: object,
knm: object,
alpha: object,
zeta: float,
psi: float,
config: BayesianUPDEConfig | None = None,
) -> BayesianUPDEResult
Run UPDE over sampled omega and K_nm distributions.
Parameters¶
phases : object
Oscillator phases in radians, shape (N,).
omega : object
Natural-frequency distribution or array.
knm : object
Coupling matrix K_nm, shape (N, N).
alpha : object
Phase-lag matrix in radians, shape (N, N). Use a zero matrix for no
lag.
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
config : BayesianUPDEConfig | None
Optional configuration object, or None for defaults.
Returns¶
BayesianUPDEResult The Bayesian UPDE result with uncertainty diagnostics.
Raises¶
NotImplementedError If the requested backend is not implemented. ValueError If the sampled inputs are invalid.
Source code in src/scpn_phase_orchestrator/upde/bayesian.py
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | |
JAX-Accelerated Kuramoto Engine¶
Optional JAX implementation for GPU-oriented Kuramoto rollouts. It preserves the same validated inputs and phase wrapping semantics as the NumPy engine. Kuramoto and Stuart-Landau state, frequency, growth, coupling, amplitude- coupling, and phase-lag arrays reject boolean, complex, and numeric-string aliases before host conversion or device dispatch. Finite real numeric-object arrays remain supported.
jax_engine ¶
GPU-accelerated Kuramoto solver via JAX JIT compilation.
Raises ImportError if JAX is not installed. Check HAS_JAX before use. Usage: from scpn_phase_orchestrator.upde.jax_engine import HAS_JAX if HAS_JAX: from scpn_phase_orchestrator.upde.jax_engine import JaxUPDEEngine engine = JaxUPDEEngine(n, dt=0.01)
Classes¶
JaxUPDEEngine ¶
JAX-accelerated Kuramoto/UPDE integrator.
GPU-compiled via jax.jit. First call triggers XLA compilation (~1-3s), subsequent calls run at native speed.
Source code in src/scpn_phase_orchestrator/upde/jax_engine.py
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
) -> FloatArray
Advance phases by one Kuramoto step on GPU via JIT-compiled JAX.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
Returns¶
FloatArray The phases after one JIT-compiled Kuramoto step.
Source code in src/scpn_phase_orchestrator/upde/jax_engine.py
JaxStuartLandauEngine ¶
JAX-accelerated Stuart-Landau integrator (RK4 only).
Source code in src/scpn_phase_orchestrator/upde/jax_engine.py
Methods:¶
step ¶
step(
state: FloatArray,
omegas: FloatArray,
mu: FloatArray,
knm: FloatArray,
knm_r: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
epsilon: float = 1.0,
) -> FloatArray
Advance Stuart-Landau state by one RK4 step via JIT-compiled JAX.
Source code in src/scpn_phase_orchestrator/upde/jax_engine.py
Stuart-Landau Amplitude Engine¶
Phase + amplitude dynamics with supercritical/subcritical Hopf bifurcation. State vector: [θ₁...θₙ | r₁...rₙ]. Amplitude coupling via K_r matrix. Amplitudes clamped non-negative after each integration step.
stuart_landau ¶
Thread-safe Stuart-Landau phase-amplitude integrator with backend parity.
StuartLandauEngine advances paired phase and amplitude state vectors using
Euler, RK4, or adaptive RK45 methods, with optional Rust acceleration when the
kernel is installed. Constructor and step validation reject invalid dimensions,
methods, non-finite arrays, and non-finite forcing before solver state changes.
The instance lock protects reusable scratch buffers and adaptive timestep state
for concurrent callers.
Classes¶
StuartLandauEngine ¶
StuartLandauEngine(
n_oscillators: int,
dt: float,
method: str = "euler",
atol: float = 1e-06,
rtol: float = 0.001,
)
Coupled Stuart-Landau (phase-amplitude) integrator.
State vector layout: state[:n] = phases θ, state[n:] = amplitudes r.
Phase ODE (Acebrón et al. 2005, Rev. Mod. Phys. 77(1)): dθ_i/dt = ω_i + Σ_j K_ij sin(θ_j - θ_i - α_ij) + ζ sin(Ψ - θ_i)
Amplitude ODE
dr_i/dt = (μ_i - r_i²)·r_i + ε Σ_j K^r_ij · r_j · cos(θ_j - θ_i - α_ij)
Source code in src/scpn_phase_orchestrator/upde/stuart_landau.py
Attributes¶
last_dt
property
¶
Last accepted timestep (adapts with RK45).
Returns¶
float Last accepted timestep (adapts with RK45).
Methods:¶
step ¶
step(
state: FloatArray,
omegas: FloatArray,
mu: FloatArray,
knm: FloatArray,
knm_r: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
epsilon: float = 1.0,
) -> FloatArray
Advance (θ, r) by one timestep. Returns new state (2N,).
Parameters¶
state : FloatArray
Finite real numeric Stuart-Landau state [θ; r], shape
(2N,). Boolean, complex, and numeric-string aliases are
rejected.
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
mu : FloatArray
Per-oscillator linear growth parameters μ, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
knm_r : FloatArray
Amplitude coupling matrix, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
epsilon : float
Finite-difference perturbation size.
Returns¶
FloatArray
The finite real numeric [θ; r] state, shape (2N,).
Source code in src/scpn_phase_orchestrator/upde/stuart_landau.py
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 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | |
compute_order_parameter ¶
Amplitude-weighted Kuramoto: Z = mean(r_i · exp(i·θ_i)).
Parameters¶
state : FloatArray
Finite real numeric Stuart-Landau state [θ; r], shape
(2N,). Coercive aliases are rejected.
Returns¶
tuple[float, float]
The amplitude-weighted (R, ψ) order parameter and phase.
Source code in src/scpn_phase_orchestrator/upde/stuart_landau.py
compute_mean_amplitude ¶
Mean amplitude across all oscillators.
Parameters¶
state : FloatArray
Finite real numeric Stuart-Landau state [θ; r], shape
(2N,). Coercive aliases are rejected.
Returns¶
float The mean amplitude across all oscillators.
Source code in src/scpn_phase_orchestrator/upde/stuart_landau.py
Simplicial (3-Body) Engine¶
Higher-order interactions beyond pairwise coupling. The 3-body term σ₂/N² Σ_{j,k} sin(θ_j + θ_k - 2θ_i) produces explosive (first-order) synchronization transitions not achievable with pairwise coupling alone. Vectorized via trig identity: 2·S_i·C_i where S = Σsin(Δθ), C = Σcos(Δθ).
Use this engine when the physical or learned topology contains group effects that cannot be decomposed into independent pairwise edges. Practical examples include neural assemblies with co-active triplets, multi-agent coordination under triangular constraints, reaction loops, power-network group modes, and simplicial-complex or hypergraph-derived topology where synchronization thresholds depend on 3-body motifs.
Direct Go, Julia, and Mojo simplicial accelerator entrypoints share the same
validated torus boundary before optional runtime loading: phase and frequency
vectors must be finite real one-dimensional float64 arrays matching the
oscillator count; flattened pairwise coupling and phase-lag buffers must have
exactly N*N values; pairwise self-coupling K_ii must be zero because the
pairwise graph represents interactions between distinct oscillators; zeta,
psi, sigma2, dt, and n_steps must be finite non-boolean controls with
non-negative triadic strength, positive timestep, and non-negative step count.
Zero-step direct calls return a copy of the input phases without loading the
optional runtime. Direct input arrays, shared/public backend outputs, and Julia
raw returns reject numeric-string aliases before float coercion. Backend outputs
must be finite torus phases in [0, 2*pi). The public dispatcher and Rust
wrapper apply that same output contract to optional backend returns before
exposing SimplicialEngine.run() results, so backend physics-contract faults
raise instead of falling through as trusted higher-order synchronization
evidence.
Gambuzza et al. 2023, Nature Physics; Tang et al. 2025. Detailed documentation: Simplicial (3-body) — detailed reference
simplicial ¶
Pairwise + all-to-all 3-body (simplicial) Kuramoto with a 5-backend chain.
Model¶
dθ_i/dt = ω_i
+ (σ₁/N) · Σ_j A_ij · sin(θ_j − θ_i)
+ (σ₂/N²) · Σ_{j,k} sin(θ_j + θ_k − 2θ_i)
+ ζ · sin(ψ − θ_i)
σ₂ > 0 drives explosive (first-order) transitions and shrinks basins of attraction while improving the locking stability of already-synchronous states (Gambuzza et al. 2023; Tang et al. 2025).
Closed form for the 3-body sum¶
Expanding sin(θ_j + θ_k − 2θ_i) = sin((θ_j − θ_i) + (θ_k − θ_i))
and separating the cross terms gives
Σ_{j,k} sin(θ_j + θ_k − 2θ_i) = 2 · S_i · C_i
with
S_i = Σ_j sin(θ_j − θ_i) = (Σ sin θ)·cos θ_i − (Σ cos θ)·sin θ_i
C_i = Σ_j cos(θ_j − θ_i) = (Σ cos θ)·cos θ_i + (Σ sin θ)·sin θ_i
So the 3-body contribution is evaluated in O(N²) (not O(N³))
using two global sums plus the per-node sincos expansion. All five
backends use this identity; the pairwise path matches the Rust
kernel's sincos expansion on the alpha-zero branch and the direct
sin(diff) form otherwise, giving bit-exact parity.
Classes¶
SimplicialEngine ¶
Pairwise + simplicial (3-body, all-to-all) Kuramoto stepper.
The engine's geometry is (n, dt, σ₂); the step itself is
stateless: (phases, omegas, K, α, ζ, ψ) → new_phases.
Initialise the simplicial Kuramoto stepper.
Parameters¶
n_oscillators : int Number of oscillators in the fixed engine geometry. dt : float Positive Euler timestep in seconds. sigma2 : float, default=0.0 Non-negative all-to-all triadic coupling strength.
Source code in src/scpn_phase_orchestrator/upde/simplicial.py
Attributes¶
sigma2
property
writable
¶
Return the configured all-to-all triadic coupling strength.
Returns¶
float Return the configured all-to-all triadic coupling strength.
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
) -> FloatArray
Advance one pairwise-plus-simplicial Kuramoto timestep.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
Returns¶
FloatArray The phases after one pairwise-plus-simplicial step.
Source code in src/scpn_phase_orchestrator/upde/simplicial.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
n_steps: int,
) -> FloatArray
Integrate pairwise-plus-simplicial Kuramoto dynamics.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
n_steps : int
Number of integration steps to run.
Returns¶
FloatArray
The final phases after n_steps simplicial steps.
Raises¶
ValueError
If n_steps is negative or the state arrays are invalid.
Source code in src/scpn_phase_orchestrator/upde/simplicial.py
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 | |
order_parameter ¶
Compute the standard Kuramoto R = |
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/simplicial.py
Functions:¶
Second-Order Inertial Engine (Power Grids)¶
Swing equation: m_i θ̈_i + d_i θ̇_i = P_i + Σ_j K_ij sin(θ_j - θ_i). Models power grid transient stability where m_i is generator inertia, d_i is damping, P_i is power injection (positive = generation, negative = load), and K_ij is transmission line susceptance. RK4 integration.
Includes frequency_deviation() (Hz from nominal — >0.5 Hz triggers load
shedding in real grids) and coherence() (phase-lock measure).
Filatrella et al. 2008; Dörfler & Bullo 2014.
Optional inertial backend outputs are validated before public return: theta
and omega_dot must keep oscillator cardinality and finite values, with
returned phases inside [0, 2*pi). Backend loader/runtime unavailability may
fall through to Python; malformed backend physics payloads raise instead of
becoming swing-equation state.
Public and direct inertial state vectors, coupling buffers, scalar controls,
step counts, frequency_deviation() inputs, coherence() inputs, optional
backend outputs, and direct Julia raw returns reject numeric-string aliases
before Python, NumPy, or accelerator coercion.
inertial ¶
Second-order (swing-equation) Kuramoto with a 5-backend fallback chain.
Model¶
Each oscillator has a phase θ_i and a "frequency-deviation"
ω_i ≡ dθ_i/dt. The swing equation is
M_i · d²θ_i/dt² + D_i · dθ_i/dt = P_i + Σ_j K_ij · sin(θ_j − θ_i)
and is advanced with classical explicit RK4 on the (θ, ω) pair.
This is the power-grid form used in Filatrella-Nielsen-Mallick 2008.
Numerics¶
The derivative uses the sin(θ_j − θ_i) = sin(θ_j)·cos(θ_i) −
cos(θ_j)·sin(θ_i) expansion so that floating-point rounding
matches the Rust kernel (spo-engine/src/inertial.rs) bit-for-bit.
All five backends (Rust, Mojo, Julia, Go, Python) agree within
~1e-14 on the canonical all-to-all test problem; the
dispatcher selects the fastest available path.
Classes¶
InertialKuramotoEngine ¶
Second-order swing-equation Kuramoto stepper with 5-backend dispatch.
The engine's geometry is (n, dt); the step itself is
stateless: (θ, ω, P, K, M, D) → (θ', ω').
Initialise the stateless inertial Kuramoto stepper geometry.
Source code in src/scpn_phase_orchestrator/upde/inertial.py
Methods:¶
step ¶
step(
theta: FloatArray,
omega_dot: FloatArray,
power: FloatArray,
knm: FloatArray,
inertia: FloatArray,
damping: FloatArray,
) -> tuple[FloatArray, FloatArray]
Advance one second-order inertial Kuramoto timestep.
Parameters¶
theta : FloatArray
Oscillator phases in radians, shape (N,).
omega_dot : FloatArray
Instantaneous frequency deviations in rad/s, shape (N,).
power : FloatArray
Per-oscillator power injection in the swing equation, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
inertia : FloatArray
Per-oscillator inertia coefficients, shape (N,).
damping : FloatArray
Per-oscillator damping coefficients, shape (N,).
Returns¶
tuple[FloatArray, FloatArray]
The (θ, ω̇) state after one second-order step.
Source code in src/scpn_phase_orchestrator/upde/inertial.py
run ¶
run(
theta: FloatArray,
omega_dot: FloatArray,
power: FloatArray,
knm: FloatArray,
inertia: FloatArray,
damping: FloatArray,
n_steps: int,
) -> tuple[FloatArray, FloatArray, FloatArray, FloatArray]
Integrate inertial Kuramoto dynamics and return final state plus traces.
Parameters¶
theta : FloatArray
Oscillator phases in radians, shape (N,).
omega_dot : FloatArray
Instantaneous frequency deviations in rad/s, shape (N,).
power : FloatArray
Per-oscillator power injection in the swing equation, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
inertia : FloatArray
Per-oscillator inertia coefficients, shape (N,).
damping : FloatArray
Per-oscillator damping coefficients, shape (N,).
n_steps : int
Number of integration steps to run.
Returns¶
tuple[FloatArray, FloatArray, FloatArray, FloatArray]
The final (θ, ω̇) plus the θ and ω̇ traces.
Source code in src/scpn_phase_orchestrator/upde/inertial.py
frequency_deviation ¶
Return maximum absolute frequency deviation in cycles per unit time.
Parameters¶
omega_dot : FloatArray
Instantaneous frequency deviations in rad/s, shape (N,).
Returns¶
float The maximum absolute frequency deviation in cycles per unit time.
Source code in src/scpn_phase_orchestrator/upde/inertial.py
coherence ¶
Return the Kuramoto order parameter for the supplied phases.
Parameters¶
theta : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/inertial.py
Functions:¶
Financial Market Regime Detection¶
Extracts instantaneous phase from price/return time series via Hilbert transform, computes Kuramoto order parameter R(t) across assets, classifies synchronization regimes (desync/transition/synchronised), and detects crash early warning signals (R crossing threshold from below).
Direct Go, Julia, and Mojo market accelerator entrypoints share a validated
float64 boundary before optional runtime loading: flattened phase payloads
must be finite real vectors with exactly T*N values; T, N, and PLV window
controls must be positive non-boolean integers; the PLV window must not exceed
T; backend R(t) outputs must have length T and lie in [0, 1]; rolling
PLV outputs must have the expected (T-window+1)*N*N cardinality, lie in
[0, 1], preserve unit diagonals, and remain symmetric.
The public market dispatcher applies the same output contract to optional
backend returns before exposing market_order_parameter() or market_plv()
results, so backend physics-contract faults propagate instead of falling
through as trusted market evidence.
R(t) → 1 preceding market crashes documented for Black Monday 1987 and the 2008 financial crisis (arXiv:1109.1167).
market ¶
Kuramoto-based financial market synchronisation analysis.
Exposes a 5-backend fallback chain.
Extracts instantaneous phase from price / return time series via the
Hilbert transform (scipy.signal.hilbert — FFT-based, stays
Python-side because the Rust/Go/Mojo backends do not ship an FFT),
then dispatches the two post-processing compute kernels:
market_order_parameter(phases)—R(t) = |⟨exp(iθ)⟩_N|at every timestep.O(T · N).market_plv(phases, window)— rolling phase-locking-value matrix between assets,O((T − W + 1) · N² · W)with a sincos precompute that eliminates trig from the inner loop.
The detect_regimes classifier and sync_warning crossing
detector are O(T) masking / comparison operations; they stay pure
NumPy. R(t) → 1 preceded Black Monday 1987 and the 2008
crash (arXiv:1109.1167; CEUR-WS Vol-915).
Functions:¶
extract_phase ¶
Extract instantaneous phase from a time series via the Hilbert transform.
Stays Python-side because the transform is FFT-based
(scipy.signal.hilbert) and the compiled backends do not
ship an FFT library.
Parameters¶
series : FloatArray
Real-valued time series, shape (T,).
Returns¶
FloatArray
The instantaneous phase of the series in [0, 2π).
Source code in src/scpn_phase_orchestrator/upde/market.py
market_order_parameter ¶
Return the Kuramoto order parameter R(t) across N assets.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
FloatArray
The Kuramoto order parameter time series R(t).
Source code in src/scpn_phase_orchestrator/upde/market.py
market_plv ¶
Compute the rolling phase-locking-value matrix between assets.
Returns shape (T − window + 1, N, N).
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
window : int
Sliding-window length in samples.
Returns¶
FloatArray
The rolling phase-locking-value matrices, shape (T − window + 1, N, N).
Source code in src/scpn_phase_orchestrator/upde/market.py
detect_regimes ¶
detect_regimes(
R: FloatArray,
sync_threshold: float = 0.7,
desync_threshold: float = 0.3,
) -> IntArray
Classify market synchronisation regimes from R(t).
Returns int32 labels: 0 = desynchronised, 1 = transition,
2 = synchronised. O(T) masking; no multi-language port needed.
Parameters¶
R : FloatArray
Order-parameter time series R(t), shape (T,).
sync_threshold : float
Order parameter above which the market is classed as synchronised.
desync_threshold : float
Order parameter below which the market is classed as desynchronised.
Returns¶
IntArray The per-timestep market regime labels.
Raises¶
ValueError
If the thresholds are inconsistent or R is not 1-D.
Source code in src/scpn_phase_orchestrator/upde/market.py
sync_warning ¶
Detect synchronisation warnings where smoothed R crosses up.
Parameters¶
R : FloatArray
Order-parameter time series R(t), shape (T,).
threshold : float
Decision threshold.
lookback : int
Number of past samples smoothed over before the crossing test.
Returns¶
BoolArray A per-timestep boolean mask of synchronisation warnings.
Source code in src/scpn_phase_orchestrator/upde/market.py
Swarmalator Dynamics¶
Agents with both spatial position x_i ∈ R^D and oscillator phase θ_i ∈ S¹. Phase modulates spatial attraction (J parameter); spatial proximity modulates phase coupling (K/|x_ij|). D-dimensional (2D, 3D supported).
Five collective states: static sync, static async, static phase wave, splintered phase wave, active phase wave — depending on J and K signs.
O'Keeffe, Hong, Strogatz, Nature Communications 2017.
Direct Go, Julia, and Mojo swarmalator accelerator entrypoints share a
validated position-phase boundary before optional runtime loading: positions
must be finite real float64 values with shape (N, D) or exactly N*D
flattened values without numeric-string aliases; phase and frequency vectors
must be finite real one-dimensional float64 arrays of length N without
numeric-string aliases; N, D, and dt must be positive and non-string
typed; and attraction, repulsion, phase-attraction modulation, and
phase-coupling coefficients must be finite real controls. Backend outputs must
return finite positions and torus phases in [0, 2*pi) without numeric-string
aliases, and Mojo stdout must contain exactly N*D + N scalar lines.
The public SwarmalatorEngine.step() dispatcher and Rust wrapper apply the
same output contract before publication, including object-dtype boolean-alias
rejection, while public constructor controls, state arrays, scalar controls,
step counts, order-parameter inputs, optional backend outputs, and direct Julia
raw returns reject numeric-string aliases before Python, NumPy, or accelerator
coercion. Loader and runtime unavailability still fall back to Python.
swarmalator ¶
Swarmalator step (position + phase) with a 5-backend fallback chain.
Swarmalators combine spatial attraction / repulsion with phase
oscillator dynamics (O'Keeffe, Hong & Strogatz, Nat. Commun. 8:1504,
2017). Each agent has a position x_i ∈ ℝ^d and a phase θ_i;
they co-evolve through attract/repulse + phase-coupling terms:
ẋ_i = (1/N) Σ_j (x_j − x_i) [(a + j·cos(θ_j − θ_i)) / |x_j − x_i|
− b / |x_j − x_i|²]
θ̇_i = ω_i + (k / N) Σ_j sin(θ_j − θ_i) / |x_j − x_i|
The repulsion b·(x_j − x_i) / |x_j − x_i|² is the canonical
inverse-distance hard core of O'Keeffe-Hong-Strogatz (magnitude
b / |x_j − x_i|), with a = A = 1, b = B = 1, j = J,
k = K recovering the original model. A single regularisation
constant ε = 1e-6 is added to |x_j − x_i|² (and inside the
sqrt for the attraction/phase |x_j − x_i|) so the kernel is
finite at coincident agents; it vanishes in the ε → 0 limit.
Classes¶
SwarmalatorEngine ¶
Swarmalator stepper with 5-backend dispatch.
The engine is stateful in its (n_agents, dim, dt) geometry
but the step contract is stateless: (pos, phases, omegas) →
(new_pos, new_phases).
Initialise the stateless swarmalator stepper geometry.
Source code in src/scpn_phase_orchestrator/upde/swarmalator.py
Methods:¶
step ¶
step(
pos: FloatArray,
phases: FloatArray,
omegas: FloatArray,
a: float = 1.0,
b: float = 1.0,
j: float = 1.0,
k: float = 1.0,
) -> tuple[FloatArray, FloatArray]
Advance coupled swarmalator positions and phases by one step.
Parameters¶
pos
Agent positions with shape (n_agents, dim).
phases
Agent phases in radians, shape (n_agents,).
omegas
Natural angular frequencies, shape (n_agents,).
a
Baseline spatial attraction coefficient.
b
Spatial repulsion coefficient.
j
Phase-dependent attraction modulation.
k
Phase-coupling coefficient.
Returns¶
tuple[FloatArray, FloatArray]
Updated positions with shape (n_agents, dim) and updated
phases wrapped into [0, 2*pi).
Notes¶
The dispatcher selects the first available accelerated backend and falls back to the NumPy reference path with the same state contract.
Source code in src/scpn_phase_orchestrator/upde/swarmalator.py
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 | |
run ¶
run(
pos: FloatArray,
phases: FloatArray,
omegas: FloatArray,
a: float = 1.0,
b: float = 1.0,
j: float = 1.0,
k: float = 1.0,
n_steps: int = 100,
) -> tuple[FloatArray, FloatArray, FloatArray, FloatArray]
Integrate swarmalator positions and phases with trajectory capture.
Parameters¶
pos : FloatArray
Swarmalator positions, shape (N, 2).
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
a : float
Spatial attraction strength.
b : float
Spatial repulsion strength.
j : float
Phase-to-space coupling strength.
k : float
Space-to-phase coupling strength.
n_steps : int
Number of integration steps to run.
Returns¶
tuple[FloatArray, FloatArray, FloatArray, FloatArray] The final positions and phases plus their trajectory traces.
Source code in src/scpn_phase_orchestrator/upde/swarmalator.py
order_parameter ¶
Return the Kuramoto order parameter for swarmalator phases.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/swarmalator.py
Functions:¶
Stochastic Engine¶
Euler-Maruyama integration with Gaussian noise injection. Includes automatic optimal noise tuning: D ≈ K·R_det/2 (Tselios et al. 2025). Counter-intuitive: noise at D INCREASES synchronization (stochastic resonance). Self-consistency solved via modified Bessel equation (Acebrón et al. 2005).
Public injection validates phase arrays even when D=0: inputs must be finite,
one-dimensional, real numeric payloads, with boolean, complex, and
numeric-string aliases rejected before the no-op return or noise arithmetic.
Noise sweeps apply the same source-type contract to D_range, validate
non-negative integer seeds before range arithmetic, and publish only physical
NoiseProfile records (D >= 0, both order parameters in [0, 1]).
stochastic ¶
Stochastic noise injection and noise-level sweeps for UPDE phase dynamics.
StochasticInjector owns a local random generator and applies
Euler-Maruyama phase noise under validated non-negative diffusion and positive
time-step parameters. find_optimal_noise sweeps finite non-negative
candidate noise levels against a supplied UPDE engine and reports the best
coherence profile without changing the engine configuration or caller-provided
input arrays outside normal engine stepping.
Classes¶
NoiseProfile
dataclass
¶
Validated noise-sweep result linking diffusion to bounded order.
StochasticInjector ¶
Add calibrated noise to phase dynamics.
Euler-Maruyama: θ_i(t+dt) = θ_i(t) + f(θ)dt + √(2Ddt) * ξ_i where ξ_i ~ N(0,1) i.i.d.
Tselios et al. 2025 — stochastic resonance in Kuramoto networks.
Create an injector with finite D and an optional valid seed.
Source code in src/scpn_phase_orchestrator/upde/stochastic.py
Attributes¶
D
property
writable
¶
Return the configured non-negative diffusion coefficient.
Returns¶
float Return the configured non-negative diffusion coefficient.
Methods:¶
inject ¶
Add Wiener noise to phases: θ += √(2D*dt) * N(0,1).
Parameters¶
phases : FloatArray
Finite real numeric oscillator phases in radians, shape (N,).
Boolean, complex, and numeric-string aliases are rejected.
dt : float
Integration step size.
Returns¶
FloatArray The phases with added Wiener noise.
Source code in src/scpn_phase_orchestrator/upde/stochastic.py
Functions:¶
optimal_D ¶
Estimate optimal noise for stochastic resonance.
D* ≈ K·R_det/2 (common noise case). Tselios et al. 2025.
find_optimal_noise ¶
find_optimal_noise(
engine: UPDEEngine,
phases_init: FloatArray,
omegas: FloatArray,
knm: FloatArray,
alpha: FloatArray,
D_range: FloatArray | None = None,
n_steps: int = 500,
seed: int = 42,
) -> NoiseProfile
Sweep noise levels, return D that maximizes R.
Uses the engine to simulate n_steps at each D value.
Parameters¶
engine : UPDEEngine
The UPDE engine used to integrate each trial.
phases_init : FloatArray
Initial oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
D_range : FloatArray | None
Finite non-negative real numeric diffusion coefficients to sweep, or
None for the default range. Coercive aliases are rejected.
n_steps : int
Number of integration steps to run.
seed : int
Non-negative non-boolean seed for the deterministic RNG.
Returns¶
NoiseProfile
The noise profile whose diffusion D maximises R.
Source code in src/scpn_phase_orchestrator/upde/stochastic.py
Geometric (Torus-Preserving) Engine¶
Symplectic Euler on T^N using SO(2) exponential map: z_i = exp(iθ_i).
Avoids mod 2π discontinuity errors that accumulate in standard integrators
over long simulations. Essential for multi-hour or multi-day simulations
where phase wrapping drift becomes significant.
The public dispatcher validates optional backend outputs before publication:
selected Rust, Go, Julia, or Mojo returns must be finite phase vectors with the
same oscillator cardinality, values in [0, 2*pi), and no numeric-string
aliases. Public constructor, state, scalar-control, and order-parameter phase
inputs plus direct Go/Julia/Mojo phase, frequency, coupling, phase-lag, scalar,
count, and backend-output boundaries reject numeric-string aliases before float
coercion or optional native runtime loading.
Detailed documentation: Geometric (SO(2)) — detailed reference
geometric ¶
Torus-preserving symplectic Euler integrator on T^N = (S¹)^N.
Exposes a 5-backend fallback chain.
Scheme¶
Each phase is lifted to the unit circle z_i = exp(iθ_i); the
Kuramoto derivative ω_eff_i is computed in the tangent space,
and z_i is advanced by the exponential map
z_i(t + dt) = z_i(t) · exp(i · ω_eff_i · dt)
followed by renormalisation to the unit circle. This avoids the
mod-2π discontinuity that introduces subtle truncation errors
in standard integrators when trajectories cross θ = 0.
Across the five backends the (z_re, z_im) state is carried in
between steps (no atan2 round-trip per step), matching the Rust
kernel spo-engine/src/geometric.rs bit-for-bit. The pairwise
derivative uses the sincos expansion on the alpha == 0 branch
and the direct atan2 + sin(diff) form otherwise.
Classes¶
TorusEngine ¶
Symplectic Euler on T^N with 5-backend dispatch.
Store (n, dt); step / run are stateless in (θ, ω, K, α,
ζ, ψ).
Source code in src/scpn_phase_orchestrator/upde/geometric.py
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
) -> FloatArray
One torus step; returns phases in [0, 2π).
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
Returns¶
FloatArray
The phases after one torus step, in [0, 2π).
Source code in src/scpn_phase_orchestrator/upde/geometric.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
n_steps: int,
) -> FloatArray
Integrate torus phase dynamics for the requested number of steps.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
n_steps : int
Number of integration steps to run.
Returns¶
FloatArray
The final finite torus phases after n_steps torus steps, in
[0, 2π).
Raises¶
ValueError If the submitted state is malformed or an optional backend returns a phase vector outside the public torus contract.
Source code in src/scpn_phase_orchestrator/upde/geometric.py
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 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | |
order_parameter ¶
Compute the standard Kuramoto R = |
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/geometric.py
Functions:¶
Time-Delayed Coupling Engine¶
The circular buffer supports one fixed discrete delay for every coupling pair,
with current-phase history used while the buffer fills. Time delays generate
"effective higher-order interactions for free" (Ciszak et al. 2025) because
the delayed coupling mixes information across multiple timescales.
Public and direct phase, frequency, coupling, and phase-lag arrays reject
numeric-string aliases before float coercion. The same pre-coercion contract
guards optional-backend outputs and direct Julia raw returns before phase
cardinality, finiteness, and [0, 2*pi) validation.
delay ¶
Time-delayed Kuramoto buffer and engine with validated phase history.
DelayBuffer stores copied finite phase snapshots in a bounded deque, and
DelayedEngine advances phases with delayed coupling, optional external
forcing, and Rust acceleration when available. Constructors and step inputs
reject non-positive dimensions, non-finite scalars, shape-mismatched arrays,
and boolean or numeric-string aliases before integration so delayed history
never aliases invalid caller state.
Classes¶
DelayBuffer ¶
Circular buffer storing phase history for delayed coupling.
Stores last max_delay_steps snapshots. Retrieves phases from
delay_steps steps ago.
Source code in src/scpn_phase_orchestrator/upde/delay.py
Attributes¶
length
property
¶
Methods:¶
push ¶
Append a phase snapshot to the buffer.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Source code in src/scpn_phase_orchestrator/upde/delay.py
get_delayed ¶
Return phases from delay_steps ago, or None if not enough history.
Parameters¶
delay_steps : int Number of steps in the past to retrieve from the delay buffer.
Returns¶
FloatArray | None
The phase snapshot from delay_steps ago, or None if history is
short.
Source code in src/scpn_phase_orchestrator/upde/delay.py
DelayedEngine ¶
Kuramoto with time-delayed coupling.
dθ_i/dt = ω_i + Σ_j K_ij sin(θ_j(t-τ) - θ_i(t) - α_ij)
Source code in src/scpn_phase_orchestrator/upde/delay.py
Attributes¶
delay_steps
property
¶
Return the configured discrete coupling delay.
Returns¶
int Return the configured discrete coupling delay.
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float = 0.0,
psi: float = 0.0,
alpha: FloatArray | None = None,
step_idx: int = 0,
) -> FloatArray
Advance one delayed Kuramoto timestep from validated state arrays.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
step_idx : int
Zero-based index of the current step, used to address delayed coupling
history.
Returns¶
FloatArray
The phases after one delayed Kuramoto step, in [0, 2π).
Source code in src/scpn_phase_orchestrator/upde/delay.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float = 0.0,
psi: float = 0.0,
alpha: FloatArray | None = None,
n_steps: int = 100,
) -> FloatArray
Run delayed Kuramoto integration for n_steps validated steps.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
n_steps : int
Number of integration steps to run.
Returns¶
FloatArray
The final phases after n_steps delayed Kuramoto steps.
Source code in src/scpn_phase_orchestrator/upde/delay.py
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | |
Functions:¶
Ott-Antonsen Mean-Field Reduction¶
Exact analytical reduction for globally-coupled Kuramoto with Lorentzian frequency distribution. Reduces N-oscillator system to a single complex ODE: dz/dt = -(Δ + iω₀)z + (K/2)(z - |z|²z).
Critical coupling K_c = 2Δ. Steady-state: R_ss = √(1 - 2Δ/K). Used by the PredictiveSupervisor as a fast forward model for MPC (O(1) computation vs O(N) for full simulation).
Direct Go, Julia, and Mojo Ott-Antonsen accelerator entrypoints share the
same scalar boundary before optional runtime loading: the complex order
parameter must lie inside the OA unit disk, Lorentzian width must be
non-negative, timestep and step count must be positive, and all scalar
controls must be finite non-boolean real values. Backend outputs are accepted
only when the returned complex state remains in the OA unit disk, R matches
|z|, and psi matches atan2(Im(z), Re(z)), preserving the physical
mean-field state contract across the polyglot chain.
The public dispatcher applies the same output contract to optional backend
returns before publishing OAState, so inconsistent R, inconsistent psi,
or boolean-alias scalar outputs fail closed instead of becoming mean-field
evidence. Empirical frequency samples reject boolean, complex, and
numeric-string aliases before Lorentzian fitting. The direct Rust runner uses
the same pre-coercion scalar-input contract as Go, Julia, and Mojo, while its
optional steady-state helper must return a finite real order parameter in
[0, 1] before publication.
Detailed documentation: Ott-Antonsen Reduction — detailed reference
reduction ¶
Exact mean-field (Ott-Antonsen) reduction for globally-coupled Kuramoto.
Uses a Lorentzian g(ω) and a 5-backend fallback chain.
Dynamics¶
On the Ott-Antonsen manifold the full N-oscillator Kuramoto system reduces to a single complex-scalar ODE:
dz/dt = −(Δ + iω₀)·z + (K/2)·(z − |z|²·z)
with z = R·e^{iψ} the mean-field order parameter, Δ the
half-width of the Lorentzian g(ω), ω₀ its centre, and
K the coupling strength.
Steady-state R_ss = √(1 − 2Δ/K) for K > K_c = 2Δ and
R_ss = 0 below. Reference: Ott & Antonsen 2008, Chaos
18(3):037113.
Numerics¶
run(z0, n_steps) is the compute-kernel path: a tight RK4 loop
on the real/imaginary components of z. This is dispatched
across Rust / Mojo / Julia / Go / Python with bit-exact parity
(scalar ODE, no reduction identities, no global sums — the only
differences between backends are the rounding order of the
k1..k4 accumulation, which matches exactly).
The scalar-output helpers K_c, steady_state_R and
predict_from_oscillators stay native Python + optional Rust —
they are O(1) arithmetic or O(N) percentile work and do not
benefit from multi-language chains.
Classes¶
OAState
dataclass
¶
Ott-Antonsen mean-field state: order parameter and critical coupling.
OttAntonsenReduction ¶
Ott-Antonsen mean-field reduction for globally-coupled Kuramoto.
The class stores (ω₀, Δ, K, dt) and exposes K_c,
steady_state_R(), step(z), run(z0, n_steps) and
predict_from_oscillators(omegas, K). run is dispatched
across the 5-backend chain; the scalar helpers stay Python +
optional Rust.
Source code in src/scpn_phase_orchestrator/upde/reduction.py
Attributes¶
Methods:¶
steady_state_R ¶
Return the analytical steady-state R_ss = √(1 − 2Δ/K) for K > K_c.
Returns¶
float
Return the analytical steady-state R_ss = √(1 − 2Δ/K) for K > K_c.
Source code in src/scpn_phase_orchestrator/upde/reduction.py
step ¶
Single RK4 step on the OA ODE.
Parameters¶
z : complex Complex Ott-Antonsen order parameter.
Returns¶
complex The complex order parameter after one RK4 step.
Source code in src/scpn_phase_orchestrator/upde/reduction.py
run ¶
Integrate n_steps RK4 steps; return the final OAState.
Parameters¶
z0 : complex Initial complex Ott-Antonsen order parameter. n_steps : int Number of integration steps to run.
Returns¶
OAState
The final OAState after n_steps RK4 steps.
Source code in src/scpn_phase_orchestrator/upde/reduction.py
predict_from_oscillators ¶
Fit a Lorentzian to omegas and return the relaxed OAState.
Parameters¶
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
K : float
Global coupling strength.
Returns¶
OAState
The relaxed OAState for the fitted Lorentzian.
Source code in src/scpn_phase_orchestrator/upde/reduction.py
Functions:¶
Variational Free Energy Predictor¶
Implementation of Friston's Free Energy Principle mapped to Kuramoto dynamics. Precision-weighted prediction error drives coupling updates; KL divergence provides a complexity penalty. Online precision estimation from error variance.
Includes PredictionModel (forward prediction with error injection)
and VariationalPredictor (FEP-Kuramoto correspondence).
Their public phase, frequency, predicted-state, observed-state, and precision
vectors reject boolean, complex, and numeric-string aliases before float64
conversion. Real numeric object arrays remain supported, and malformed array
protocol or conversion payloads are normalized to field-specific ValueError
failures before predictor state can mutate.
prediction ¶
Forward and variational prediction models for validated UPDE phase states.
The module supplies a linear prediction-error model and a variational free-energy predictor over one-dimensional oscillator phase vectors. Public constructors and update methods validate oscillator counts, positive time steps, finite phase/frequency arrays, and precision vectors before mutating internal weights, sufficient statistics, or error histories. The implementation is a concrete numerical mechanism and does not claim to formalize phenomenological time-consciousness.
Classes¶
PredictionState
dataclass
¶
PredictionState(
predicted_phases: FloatArray,
prediction_error: FloatArray,
mean_error: float,
weights: FloatArray,
)
Snapshot of the forward prediction model after one update step.
PredictionModel ¶
Linear forward model for phase prediction.
Predicts θ̂(t+dt) from θ(t) using learned weights W: θ̂(t+dt) = θ(t) + dt · (ω + W · sin(Δθ))
Prediction error ε = θ_actual - θ̂ (wrapped to [-π, π]). Weights updated via gradient descent on ε²: W += η · ε ⊗ sin(Δθ)
The prediction error signal can be injected into the UPDE as an additional coupling term, implementing a form of predictive coding where the system minimizes its own prediction error.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
Attributes¶
weights
property
¶
Copy of the current learned weight matrix W.
Returns¶
FloatArray Copy of the current learned weight matrix W.
error_gain
property
¶
Scaling factor applied to prediction error before injection.
Returns¶
float Scaling factor applied to prediction error before injection.
Methods:¶
predict ¶
Predict phases at next timestep.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
dt : float
Integration step size.
Returns¶
FloatArray The predicted phases at the next timestep.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
update ¶
Compute prediction error and update weights.
Call once per timestep AFTER the solver step.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
dt : float
Integration step size.
Returns¶
PredictionState The updated prediction state after one learning step.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
error_coupling ¶
Prediction-error signal for injection into UPDE.
Returns ε_gain · ε_i, where ε_i = θ_actual - θ̂_predicted. Add this to the UPDE derivative to implement predictive coding: dθ/dt = ω + K·sin(Δθ) + gain·ε
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
dt : float
Integration step size.
Returns¶
FloatArray The prediction-error coupling signal for UPDE injection.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
reset ¶
VariationalState
dataclass
¶
VariationalState(
predicted_phases: FloatArray,
error: FloatArray,
free_energy: float,
precision: FloatArray,
complexity: float,
)
Snapshot of the variational predictor after one update step.
VariationalPredictor ¶
VariationalPredictor(
n_oscillators: int,
prior_precision: float = 1.0,
learning_rate: float = 0.01,
)
Variational free energy minimization for phase prediction.
Implements the formal mapping between SCPN phase dynamics and Friston's Free Energy Principle:
F = E_q[log q(theta) - log p(theta, y)] ~ prediction_error^2 / (2 * precision) + complexity
where
theta = phase states (sufficient statistics mu in FEP) y = observed phases q(theta) = recognition density (Gaussian, parameterized by mu, Sigma) prediction_error = y - f(mu) (sensory prediction error) precision = 1/sigma^2 (inverse variance, maps to coupling K) complexity = KL[q||p] (prior deviation cost)
The UPDE coupling term K_ij * sin(theta_j - theta_i) maps to precision-weighted prediction error under Laplace approximation (Friston 2010, Eq. 4).
This is NOT a claim to formalize Husserl's protention. It is a concrete numerical implementation of the mathematical correspondence between Kuramoto coupling and variational inference.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
Attributes¶
precision
property
¶
Copy of the current per-oscillator precision vector.
Returns¶
FloatArray Copy of the current per-oscillator precision vector.
Methods:¶
free_energy ¶
Variational free energy F.
F = sum_i [ (y_i - f(mu_i))^2 * pi_i / 2 ] + sum_i [ log(pi_i) ]
First term: precision-weighted prediction error (accuracy). Second term: log-precision (complexity under Gaussian q). The sign convention follows Friston (2010): F is minimized.
Parameters¶
predicted : FloatArray
Predicted phases in radians, shape (N,).
observed : FloatArray
Observed phases in radians, shape (N,).
precision : FloatArray
Per-oscillator precision vector, shape (N,).
Returns¶
float
The variational free energy F.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
update ¶
One variational update step.
- Predict phases from current sufficient statistics mu.
- Compute precision-weighted prediction error.
- Update mu (gradient descent on F).
- Update precision from error statistics.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
dt : float
Integration step size.
Returns¶
VariationalState The updated variational state after one step.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
precision_weighted_coupling ¶
Precision matrix interpretable as K_ij.
Under the FEP-Kuramoto correspondence (Friston 2010, Laplace approximation), the coupling matrix K_ij maps to the off-diagonal elements of the precision matrix of the generative model.
This returns diag(precision) as the simplest such mapping. For a full N x N coupling matrix, use np.diag(result).
Returns¶
FloatArray Precision matrix interpretable as K_ij.
Source code in src/scpn_phase_orchestrator/upde/prediction.py
reset ¶
Reset precision to prior, zero sufficient statistics, clear history.
Adjoint Gradient Computation¶
Finite-difference and JAX-autodiff gradients of the synchronization cost (1 - R) with respect to the coupling matrix K_nm. Used for gradient-based coupling optimization without the overhead of forward-mode differentiation.
Both paths validate a non-empty finite real phase vector, matching frequency vector, square coupling and phase-lag matrices, and a zero self-coupling diagonal before simulation or optional-backend import. Step counts must be positive non-boolean integers. The finite-difference perturbation and JAX timestep must be positive finite reals; finite-difference drive scalars must be finite real values. Boolean, complex, and numeric-string array aliases fail closed instead of being coerced by NumPy or JAX.
adjoint ¶
Adjoint and finite-difference sensitivities for UPDE coupling gradients.
The module defines the synchronization cost 1 - R and two gradient paths:
a deterministic NumPy finite-difference estimator over coupling entries and a
diffrax continuous-adjoint implementation when the optional JAX/diffrax stack is
installed. The finite-difference path mutates only local coupling copies for
each perturbation; the continuous-adjoint path integrates the same
Kuramoto-Sakaguchi field and differentiates through the solver, agreeing with
the finite-difference reference in direction and — in the fine-dt limit — in
magnitude. It fails explicitly with ImportError when the dependency is
absent instead of silently claiming accelerated gradients, and never mutates the
process-global jax_enable_x64 flag.
Classes¶
Functions:¶
cost_R ¶
Cost 1 − R (minimise to maximise synchronisation).
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The cost 1 − R for the supplied phases.
Source code in src/scpn_phase_orchestrator/upde/adjoint.py
gradient_knm_fd ¶
gradient_knm_fd(
engine: UPDEEngine,
phases_init: FloatArray,
omegas: FloatArray,
knm: FloatArray,
alpha: FloatArray,
n_steps: int = 100,
epsilon: float = 0.0001,
zeta: float = 0.0,
psi: float = 0.0,
) -> FloatArray
Finite-difference gradient of cost_R w.r.t. knm entries.
For each K_ij (i≠j), perturbs by ±ε and measures the effect on R after n_steps. Returns gradient matrix ∂(1-R)/∂K_ij.
Complexity: O(N² · N² · n_steps) = O(N⁴ · n_steps) because each of the ~N² off-diagonal entries requires a full N-step simulation that is itself O(N²) per step. Use gradient_knm_jax() for anything beyond N≈16. The adjoint method via diffrax reduces this to O(n_steps) but requires JAX.
Parameters¶
engine : UPDEEngine
The UPDE engine used to integrate each trial.
phases_init : FloatArray
Initial oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N). Use a zero matrix for no
lag.
n_steps : int
Positive number of integration steps to run.
epsilon : float
Strictly positive finite-difference perturbation size.
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
Returns¶
FloatArray
The finite-difference gradient of the cost with respect to knm.
Source code in src/scpn_phase_orchestrator/upde/adjoint.py
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 | |
gradient_knm_jax ¶
gradient_knm_jax(
phases_init: FloatArray,
omegas: FloatArray,
knm: FloatArray,
alpha: FloatArray,
n_steps: int = 100,
dt: float = 0.01,
) -> FloatArray
Gradient of cost_R w.r.t. knm via a diffrax continuous adjoint.
Integrates the Kuramoto-Sakaguchi field
dθ_i/dt = ω_i + Σ_j K_ij · sin(θ_j − θ_i − α_ij)
over [0, n_steps·dt] with an adaptive solver (diffrax.Tsit5) and a
RecursiveCheckpointAdjoint, then differentiates cost_R of the final
phases with respect to knm using reverse-mode autodiff. This is the
O(1)-memory continuous-adjoint path the finite-difference estimator in
:func:gradient_knm_fd approximates; the two agree in direction and, in the
fine-dt limit, in magnitude (the finite-difference reference
differentiates the discrete explicit-Euler map, so a fixed dt leaves an
O(dt) discretisation gap).
The solver runs in JAX's active default precision — it does not mutate
the process-global jax_enable_x64 flag, so it does not perturb the
float32 default the rest of the differentiable stack relies on. Callers that
need float64 gradients must enable x64 at process start-up themselves.
Parameters¶
phases_init : FloatArray
Initial oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N). Use a zero matrix for no
lag; this is the regime in which the gradient matches the drive-free
(ζ = ψ = 0) finite-difference reference.
n_steps : int
Positive number of nominal steps; the integration horizon is
n_steps · dt.
dt : float
Positive finite nominal step size setting the integration horizon and
the adaptive solver's initial step.
Returns¶
FloatArray
The continuous-adjoint gradient of the cost with respect to knm.
Raises¶
ImportError If the optional JAX/diffrax stack is not installed.
Source code in src/scpn_phase_orchestrator/upde/adjoint.py
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 287 288 289 290 291 292 | |
Order Parameters & Metrics¶
Kuramoto order parameter R (global coherence), PLV (pairwise phase-locking value), and layer coherence (R for oscillator subsets). Optional Rust acceleration.
Direct Go, Julia, and Mojo order-parameter entrypoints share the same typed
boundary before optional runtime loading: phase payloads must be one-dimensional
finite real vectors, PLV inputs must be equal-length phase vectors, and
layer-coherence indices must be unique in-range oscillator indices. Empty
zero-measure calls return the neutral value without loading an optional runtime:
(0.0, 0.0) for global order, 0.0 for PLV, and 0.0 for layer coherence.
Backend outputs are accepted only when physical: R, PLV, and layer coherence
must be finite values in [0, 1], and mean phase must be finite before being
canonicalised to the public [0, 2*pi) convention. Public phase-vector inputs
and shared backend scalar outputs reject numeric-string aliases before float
coercion, so text cannot acquire numeric provenance at either publication
boundary. Mojo stdout remains an explicit text protocol and is parsed before
the shared typed scalar validator runs.
The release benchmark gate for this surface is:
It records Rust/Mojo/Julia/Go/Python status, timing, unavailable-toolchain
reasons, deterministic hashes, and tolerance-bounded parity against the forced
Python reference for global R, mean phase, PLV, and layer coherence. The
reference-suite snapshot exposes this gate as order_parameter_polyglot.
The public dispatcher applies the same scalar output contract to optional
backend returns before publication: order-parameter magnitudes, PLV, and layer
coherence must be finite real values in [0, 1], with boolean aliases rejected
instead of widened to synthetic 0.0 or 1.0 evidence and numeric strings
rejected instead of converted into typed backend evidence.
order_params ¶
Kuramoto order parameter family with 5-backend fallback chain.
Follows the AttnRes-level module standard
(feedback_module_standard_attnres.md):
compute_order_parameter— R and mean phase ψ.compute_plv— phase-locking value between two equal-length phase series.compute_layer_coherence— R restricted to a layer.
Each kernel is available in five languages — Rust, Mojo, Julia, Go,
Python. AVAILABLE_BACKENDS reports detected backends in canonical
fallback order, while ACTIVE_BACKEND is selected by a small import-time
hot-path probe so slow external wrappers do not displace the faster local
path.
Functions:¶
compute_order_parameter ¶
Kuramoto global order parameter (R, ψ).
R = |mean(exp(i · θ))|;
ψ = arg(mean(exp(i · θ))) mod 2π.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
tuple[float, float]
The (R, ψ) Kuramoto order parameter and mean phase.
Notes¶
R carries a positive small-sample bias: N uniformly random phases give
E[R²] = 1/N, so R reads about 1/√N (≈ 0.35 at N = 8) even with
no coherence. A monitor comparing coherence across small populations should use
:func:debiased_squared_order_parameter, whose expectation is 0 under
uniformity, rather than reading the raw R as if it were unbiased.
Source code in src/scpn_phase_orchestrator/upde/order_params.py
debiased_squared_order_parameter ¶
Return the small-N-bias-corrected squared Kuramoto order parameter.
The raw magnitude R = |mean(exp(iθ))| has a positive small-sample bias:
N uniformly random phases give E[R²] = 1/N, so R reads about
1/√N (≈ 0.35 at N = 8) even with no coherence. This estimator removes
that floor. It is the pairwise phase consistency of Vinck et al. (2010),
(N · R² − 1) / (N − 1),
whose expectation is 0 under uniform phases and 1 at perfect synchrony. It can
be slightly negative on a finite anti-aligned sample — that is honest, not an
error. Reuses the accelerated :func:compute_order_parameter for R; the
debiasing itself is a scalar correction.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,) with N ≥ 2.
Returns¶
float
The debiased squared order parameter, in [-1 / (N − 1), 1].
Raises¶
ValueError
If fewer than two phases are supplied — the correction is undefined for a
single oscillator (N − 1 = 0).
References¶
Vinck, M., van Wingerden, M., Womelsdorf, T., Fries, P., & Pennartz, C. M. A. (2010). The pairwise phase consistency: a bias-free measure of rhythmic neuronal synchronization. NeuroImage, 51(1), 112–122.
Source code in src/scpn_phase_orchestrator/upde/order_params.py
compute_plv ¶
Phase-locking value between two equal-length phase series.
PLV = |mean(exp(i · (φ_a − φ_b)))| over samples.
Parameters¶
phases_a : FloatArray
First phase series in radians, shape (T,).
phases_b : FloatArray
Second phase series in radians, shape (T,).
Returns¶
float The phase-locking value between the two series.
Raises¶
ValueError If the two phase series have different lengths.
Source code in src/scpn_phase_orchestrator/upde/order_params.py
compute_layer_coherence ¶
Return the order parameter R for the oscillators in layer_mask.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
layer_mask : BoolArray | IntArray
Boolean mask or integer index array selecting the layer's oscillators.
Returns¶
float
The Kuramoto order parameter R for the selected oscillators.
Source code in src/scpn_phase_orchestrator/upde/order_params.py
metrics ¶
Immutable diagnostic dataclasses for UPDE lock and layer state snapshots.
The module contains data-only records for pairwise lock signatures, per-layer coherence and amplitude summaries, and aggregate UPDE diagnostic state. These types intentionally perform no simulation, I/O, or mutation beyond dataclass construction; producers are responsible for numeric validation before emitting snapshots.
Classes¶
LockSignature
dataclass
¶
Phase-locking value and mean lag between two layers.
LayerState
dataclass
¶
LayerState(
R: float,
psi: float,
lock_signatures: dict[str, LockSignature] = dict(),
mean_amplitude: float = 0.0,
amplitude_spread: float = 0.0,
)
Per-layer diagnostics: order parameter R, mean phase, amplitude stats.
UPDEState
dataclass
¶
UPDEState(
layers: list[LayerState],
cross_layer_alignment: FloatArray,
stability_proxy: float,
regime_id: str,
mean_amplitude: float = 0.0,
pac_max: float = 0.0,
subcritical_fraction: float = 0.0,
boundary_violation_count: int = 0,
imprint_mean: float = 0.0,
)
Full UPDE diagnostic snapshot: per-layer states and aggregates.
Phase-Amplitude Coupling (PAC)¶
Modulation index (MI) via Tort et al. 2010. Bins low-frequency phase,
computes mean high-frequency amplitude per bin, KL divergence from uniform.
Produces N×N PAC matrix: entry [i,j] = MI(phase_i, amplitude_j).
Direct Go, Julia, and Mojo PAC entrypoints now share the same typed boundary
before optional runtime loading: phase and amplitude payloads must be finite
real float64 vectors, amplitudes must be non-negative, n_bins must be a
non-boolean integer of at least two, and matrix calls require exactly T*N
flattened phase and amplitude samples. Empty common MI windows return zero
without loading optional runtimes. Backend MI and pairwise PAC outputs are
accepted only when finite, correctly sized, and inside the physical [0, 1]
interval. Direct Mojo PAC output must contain exactly one scalar line for
modulation-index calls or exactly N×N scalar lines for matrix calls; blank,
non-finite, truncated, or overlong text output is rejected before public
assembly. The public PAC dispatcher applies the same output contract to
optional backend returns before exposing modulation_index() or pac_matrix()
results, so backend physics-contract faults fail closed instead of being
clipped into synthetic PAC evidence.
Central to neuroscience cross-frequency coupling analysis.
pac ¶
Tort 2010 phase-amplitude coupling with 5-backend fallback chain.
Follows feedback_module_standard_attnres.md:
modulation_index— scalar Tort 2010 MI on a single (θ_low, a_high) pair of time series.pac_matrix—(N, N)pairwise MI matrix overNoscillator phase / amplitude channels.pac_gate— pure-Python boolean gate on an MI value (no backend dispatch needed — trivial comparison).
All compute kernels are available in Rust, Mojo, Julia, Go, Python.
AVAILABLE_BACKENDS reports detected backends in canonical fallback order,
while ACTIVE_BACKEND is selected by a small import-time hot-path probe so
slow external wrappers do not displace the faster local path.
Functions:¶
modulation_index ¶
Phase-amplitude coupling via Tort et al. 2010, J. Neurophysiol.
Bins amplitude by phase, computes KL divergence from uniform,
returns the modulation index normalised to [0, 1] by
log(n_bins).
Parameters¶
theta_low : FloatArray
Low-frequency driver phase in radians, shape (T,).
amp_high : FloatArray
High-frequency amplitude envelope, shape (T,).
n_bins : int
Number of phase bins used for the modulation-index histogram.
Returns¶
float The Tort modulation index of phase-amplitude coupling.
Raises¶
ValueError
If n_bins is not a positive integer or inputs mismatch.
Source code in src/scpn_phase_orchestrator/upde/pac.py
pac_matrix ¶
pac_matrix(
phases_history: FloatArray,
amplitudes_history: FloatArray,
n_bins: int = 18,
) -> FloatArray
Return the (N, N) PAC matrix [i, j] = MI(phase_i, amplitude_j).
Parameters¶
phases_history : FloatArray
(T, N) phase time series.
amplitudes_history : FloatArray
(T, N) amplitude time series.
n_bins : int
number of phase bins.
Returns¶
FloatArray
FloatArray The (N, N) phase-amplitude coupling matrix.
Raises¶
ValueError
If n_bins is not positive or the histories have mismatched shapes.
Source code in src/scpn_phase_orchestrator/upde/pac.py
pac_gate ¶
Binary gate: True when PAC exceeds threshold.
Pure-Python helper; no dispatcher — the comparison is trivial.
Parameters¶
pac_value : float A phase-amplitude coupling value. threshold : float Decision threshold.
Returns¶
bool
True when the PAC value exceeds the threshold.
Source code in src/scpn_phase_orchestrator/upde/pac.py
Envelope & Numerics¶
Amplitude envelope extraction and numerical integration utilities
(DP54 coefficients, error estimation, step size control).
The public envelope dispatcher validates optional backend RMS-envelope outputs
before publication: extracted envelopes must keep input cardinality, remain
finite, stay non-negative, and reject numeric-string aliases before coercion;
modulation-depth outputs must be finite scalars inside [0, 1] and reject
numeric-string aliases as well. Public amplitude/envelope inputs and window
share the same alias boundary. Loader/runtime failures still fall through to
the Python floor.
Detailed documentation: Envelope (RMS) — detailed reference
envelope ¶
Sliding-window RMS envelope and modulation-depth statistic.
Exposes a 5-backend fallback chain. AVAILABLE_BACKENDS keeps the canonical
fallback order; ACTIVE_BACKEND is chosen by a small hot-path probe so slow
external wrappers do not displace the faster local path.
The sliding-window RMS uses the O(T) cumulative-sum form: compute
cs[i] = Σ_{k < i} x_k², then
rms[i] = sqrt((cs[i+w] − cs[i]) / w) for valid indices, with a
front-pad of the first valid value. The 1-D path is on the
5-backend chain; the 2-D (T, N) batched path stays pure NumPy
because the Rust FFI is 1-D-only and the vectorised NumPy form is
already near-optimal at realistic N.
Classes¶
EnvelopeState
dataclass
¶
EnvelopeState(
mean_amplitude: float,
amplitude_spread: float,
modulation_depth: float,
subcritical_count: int,
)
Snapshot of amplitude envelope statistics.
Functions:¶
extract_envelope ¶
Sliding-window RMS envelope.
Parameters¶
amplitudes_history : FloatArray
(T,) or (T, N) amplitude time series.
window : int
RMS window length in samples.
Returns¶
FloatArray
Same shape as input; the first window − 1 entries are front-padded with the
first valid RMS value.
Raises¶
ValueError
If window is not a positive integer no larger than the history.
Source code in src/scpn_phase_orchestrator/upde/envelope.py
envelope_modulation_depth ¶
Modulation depth (max − min) / (max + min) ∈ [0, 1].
Returns 0.0 for empty or non-positive envelopes.
Parameters¶
envelope : FloatArray
An amplitude-envelope time series, shape (T,).
Returns¶
float
The modulation depth (max − min) / (max + min) in [0, 1].
Raises¶
ValueError
If envelope contains numeric-string aliases.
Source code in src/scpn_phase_orchestrator/upde/envelope.py
numerics ¶
Numerical integration configuration and explicit-step stability checks.
IntegrationConfig records solver tolerances and method selection, while
check_stability provides a CFL-like phase-step bound for explicit Kuramoto
integration. The helper is deliberately conservative and side-effect free: it
does not adapt solvers or clamp parameters, it only reports whether the supplied
derivative bound keeps a single step below a half-cycle.
Classes¶
IntegrationConfig
dataclass
¶
IntegrationConfig(
dt: float,
substeps: int = 1,
method: str = "euler",
max_dt: float = 0.01,
atol: float = 1e-06,
rtol: float = 0.001,
)
Numerical integration parameters for the phase ODE solver.
Functions:¶
check_stability ¶
CFL-like stability bound for explicit Kuramoto integration.
Analogous to Courant–Friedrichs–Lewy (1928); see docs/specs/upde_numerics.md. dt * max_deriv < pi ensures phase change stays below half-cycle per step.
Parameters¶
dt : float Integration step size. max_omega : float Largest absolute natural frequency in the system. max_coupling : float Largest absolute coupling magnitude in the system.
Returns¶
bool
True when the timestep satisfies the CFL-like stability bound.
Raises¶
ValueError
If dt, max_omega, or max_coupling is not finite and positive.
Source code in src/scpn_phase_orchestrator/upde/numerics.py
Splitting Engine¶
Operator-splitting UPDE integrator for stiff regimes and deterministic phase update decomposition.
splitting ¶
Strang second-order operator splitting for the Kuramoto ODE.
Exposes a 5-backend fallback chain.
Scheme¶
Split dθ/dt = ω + Σ_j K_ij · sin(θ_j − θ_i − α_ij) +
ζ · sin(ψ − θ_i) into
A: dθ/dt = ω (exact rotation)
B: dθ/dt = coupling (RK4 on the nonlinear part)
and compose symmetrically as A(dt/2) → B(dt) → A(dt/2)
(Strang scheme, second-order in dt).
Why split?¶
The ω flow is linear, so it has no truncation error; folding
it into a monolithic RK45 burns integrator budget on a solvable
direction while damping unrelated accuracy in the nonlinear
direction. Reference: Hairer, Lubich & Wanner 2006, Geometric
Numerical Integration §II.5.
Numerics¶
The B-stage RK4 uses the Rust kernel's
sin(θ_j − θ_i) = sin(θ_j)·cos(θ_i) − cos(θ_j)·sin(θ_i)
expansion on the alpha-zero branch so that floating-point
rounding matches Rust (spo-engine/src/splitting.rs)
bit-for-bit. Nonzero alpha falls back to the direct
sin(diff) form in all five backends.
Classes¶
SplittingEngine ¶
Strang-split Kuramoto stepper with 5-backend dispatch.
The engine's geometry is (n, dt); the step is stateless.
Create a Strang-splitting engine for n_oscillators and dt.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
) -> FloatArray
One Strang-split step: A(dt/2) → B(dt) → A(dt/2).
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
Returns¶
FloatArray The phases after one Strang-split step.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
n_steps: int,
) -> FloatArray
Apply repeated Strang-split phase integration steps.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
n_steps : int
Number of integration steps to run.
Returns¶
FloatArray
The final phases after n_steps Strang-split steps.
Raises¶
ValueError
If n_steps is negative or the state arrays are invalid.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | |
order_parameter ¶
Compute the standard Kuramoto R = |
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
Functions:¶
Bifurcation Continuation¶
Traces the synchronization transition R(K) as a function of coupling strength. The incoherent state (R≈0) bifurcates to partial synchronization (R>0) at the critical coupling K_c.
Two interfaces:
trace_sync_transition(): sweep R(K) over a range of coupling strengthsfind_critical_coupling(): binary search for K_c with configurable precision
Analytical reference: K_c = 2/(π g(0)) for Lorentzian g(ω) with half-width Δ gives K_c = 2Δ (Kuramoto 1975, Strogatz 2000).
Usage:
from scpn_phase_orchestrator.upde.bifurcation import (
trace_sync_transition, find_critical_coupling,
)
# Sweep R(K) curve
diagram = trace_sync_transition(omegas, K_range=(0, 5), n_points=50)
print(f"K_c ≈ {diagram.K_critical}")
# Precise K_c via binary search
Kc = find_critical_coupling(omegas, tol=0.05)
bifurcation ¶
Bifurcation continuation for Kuramoto synchronisation transitions.
Traces steady-state order parameter R as a function of coupling
strength K using pseudo-arclength continuation (Keller 1977).
Detects critical coupling K_c where the incoherent state
R ≈ 0 bifurcates to partial synchronisation R > 0.
Analytical reference: K_c = 2 / (π g(0)) for Lorentzian g(ω)
with half-width Δ → K_c = 2Δ (Kuramoto 1975, Strogatz 2000).
5-backend chain via delegation¶
The single-trial kernel steady_state_r(phases, omegas, knm,
alpha, k_scale, dt, n_transient, n_measure) → R is already
dispatched across Rust / Mojo / Julia / Go / Python in
:mod:scpn_phase_orchestrator.upde.basin_stability. This module
delegates to it rather than re-implementing the Euler trial
integrator, which means every trace_sync_transition /
find_critical_coupling call in the Python-composite branch
inherits the full fallback chain for free.
The two composite Rust kernels — trace_sync_transition_rust
(batched K-sweep) and find_critical_coupling_bif_rust (binary
search inside Rust) — are preserved as one-shot fast paths: a
single FFI call amortises the per-K boundary overhead better than
the N_points × dispatch-call path.
Classes¶
BifurcationPoint
dataclass
¶
One sampled point on a Kuramoto synchronisation branch.
BifurcationDiagram
dataclass
¶
Functions:¶
trace_sync_transition ¶
trace_sync_transition(
omegas: FloatArray,
knm_template: FloatArray | None = None,
alpha: FloatArray | None = None,
K_range: tuple[float, float] = (0.0, 5.0),
n_points: int = 50,
dt: float = 0.01,
n_transient: int = 2000,
n_measure: int = 500,
seed: int = 42,
) -> BifurcationDiagram
Trace R(K) for the Kuramoto synchronisation transition.
Sweeps coupling strength K from K_range[0] to
K_range[1], running the ODE to steady state at each point,
and returns a :class:BifurcationDiagram with the (K, R)
pairs plus the estimated critical coupling K_c.
When the Rust composite kernel is available, the whole sweep
is batched into a single FFI call. Otherwise the function
loops in Python and each trial is dispatched through the
5-backend chain inherited from
:func:basin_stability.steady_state_r.
Parameters¶
omegas : FloatArray
Finite real numeric natural frequencies in rad/s, shape (N,).
Boolean, complex, and numeric-string aliases are rejected.
knm_template : FloatArray | None
Unit coupling template scaled along the continuation, or None for
all-to-all. Must be finite, real numeric, and zero-diagonal.
alpha : FloatArray | None
Finite real numeric phase-lag matrix in radians, shape (N, N), or
None for no lag.
K_range : tuple[float, float]
Inclusive (min, max) coupling-strength range to scan.
n_points : int
Number of coupling points sampled across the range.
dt : float
Integration step size.
n_transient : int
Number of transient steps discarded before measurement.
n_measure : int
Number of steps averaged to measure the order parameter.
seed : int
Seed for the deterministic RNG.
Returns¶
BifurcationDiagram
The traced R(K) bifurcation diagram.
Source code in src/scpn_phase_orchestrator/upde/bifurcation.py
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 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | |
find_critical_coupling ¶
find_critical_coupling(
omegas: FloatArray,
knm_template: FloatArray | None = None,
dt: float = 0.01,
n_transient: int = 3000,
n_measure: int = 1000,
tol: float = 0.05,
seed: int = 42,
) -> float
Binary-search the critical coupling K_c where R crosses 0.1.
More precise than :func:trace_sync_transition when only
K_c is needed. Returns nan if no transition is found
in [0, 20].
Parameters¶
omegas : FloatArray
Finite real numeric natural frequencies in rad/s, shape (N,).
Boolean, complex, and numeric-string aliases are rejected.
knm_template : FloatArray | None
Unit coupling template scaled along the continuation, or None for
all-to-all. Must be finite, real numeric, and zero-diagonal.
dt : float
Integration step size.
n_transient : int
Number of transient steps discarded before measurement.
n_measure : int
Number of steps averaged to measure the order parameter.
tol : float
Convergence tolerance for the binary search.
seed : int
Seed for the deterministic RNG.
Returns¶
float
The critical coupling K_c where R first crosses 0.1.
Source code in src/scpn_phase_orchestrator/upde/bifurcation.py
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | |
Basin Stability¶
Monte Carlo estimation of the volume of the basin of attraction for the synchronised state. Basin stability S_B is the probability that a random initial condition converges to the synchronised attractor.
Procedure: Draw n_samples random phase configurations from [0, 2π)^N, integrate each to steady state, check if R_final > R_threshold. S_B = fraction that converge.
multi_basin_stability() classifies outcomes at multiple R thresholds
to detect multi-stability (chimera states, partial synchronization).
Optional Rust, Go, Julia, and Mojo backend outputs are validated before public
publication: each steady-state order-parameter scalar must be finite,
non-boolean, non-numeric-string, and inside [0, 1]. Public and direct
phase, frequency, flattened coupling, phase-lag, scalar-control, threshold,
and count inputs reject numeric-string aliases before float coercion.
Loader/runtime unavailability can still fall through to Python; malformed
backend physics evidence fails closed.
Usage:
from scpn_phase_orchestrator.upde.basin_stability import (
basin_stability, multi_basin_stability,
)
result = basin_stability(omegas, knm, n_samples=1000)
print(f"S_B = {result.S_B:.3f} ({result.n_converged}/{result.n_samples})")
# Multi-threshold detection
results = multi_basin_stability(omegas, knm, R_thresholds=(0.3, 0.6, 0.8))
References: Menck et al. 2013, Nature Physics 9:89-92.
basin_stability ¶
Basin stability for Kuramoto synchronisation with a 5-backend fallback chain.
Monte Carlo estimation of the volume of the basin of attraction for
the synchronised state. Basin stability S_B is the probability
that a random initial condition converges to the synchronised
attractor (Menck et al. 2013, Ji et al. 2014).
Kernel of the computation¶
The single-trial primitive is steady_state_r(phases_init, omegas,
knm, alpha, dt, n_transient, n_measure) → R — explicit Euler
integration of the Kuramoto ODE, transient discarded, time-averaged
order parameter returned. The trial kernel has no RNG and is
dispatched across Rust / Mojo / Julia / Go / Python (bit-exact parity
on deterministic inputs).
RNG ownership¶
The Monte Carlo loop lives in Python: np.random.default_rng(seed)
draws n_samples random phase vectors from [0, 2π)^N and calls
the dispatched trial kernel once per IC. This is the dimension
pattern — Python owns the randomness so the compute primitive stays
deterministic and parity-testable. The original
basin_stability_rust (seed-in → S_B-out) kernel is preserved as a
one-shot fast path when all four arguments match, but regular use
goes through the dispatched per-trial kernel.
Functions:¶
steady_state_r ¶
steady_state_r(
phases_init: FloatArray,
omegas: FloatArray,
knm: FloatArray,
alpha: FloatArray | None = None,
k_scale: float = 1.0,
dt: float = 0.01,
n_transient: int = 500,
n_measure: int = 200,
) -> float
One-trial Kuramoto steady-state R (dispatched).
Integrates the Kuramoto ODE for n_transient + n_measure steps
and returns the time-averaged order parameter over the latter
window. Delegates to the fastest available backend.
Parameters¶
phases_init : FloatArray
Initial oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
alpha : FloatArray | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
k_scale : float
Multiplicative scale applied to the coupling matrix.
dt : float
Integration step size.
n_transient : int
Number of transient steps discarded before measurement.
n_measure : int
Number of steps averaged to measure the order parameter.
Returns¶
float
The steady-state Kuramoto order parameter R of the trial.
Source code in src/scpn_phase_orchestrator/upde/basin_stability.py
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 428 429 430 431 432 433 434 435 436 | |
basin_stability ¶
basin_stability(
omegas: FloatArray,
knm: FloatArray,
alpha: FloatArray | None = None,
dt: float = 0.01,
n_transient: int = 500,
n_measure: int = 200,
n_samples: int = 100,
R_threshold: float = 0.8,
seed: int = 42,
) -> BasinStabilityResult
Estimate basin stability of the synchronised state.
Draws n_samples random initial phase configurations from
[0, 2π)^N, integrates each to steady state via the dispatched
trial kernel, and classifies trials by R_final ≥ R_threshold.
Parameters¶
omegas : FloatArray (N,) natural frequencies. knm : FloatArray (N, N) coupling matrix. alpha : FloatArray | None (N, N) phase lags (default: zeros). dt : float Integration timestep. n_transient : int Transient steps to discard. n_measure : int Steps to average R over. n_samples : int Number of random initial conditions. R_threshold : float Threshold for classifying as "synchronised". seed : int RNG seed (owned by Python).
Returns¶
BasinStabilityResult BasinStabilityResult with S_B, R_final array, and counts.
Source code in src/scpn_phase_orchestrator/upde/basin_stability.py
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | |
multi_basin_stability ¶
multi_basin_stability(
omegas: FloatArray,
knm: FloatArray,
alpha: FloatArray | None = None,
dt: float = 0.01,
n_transient: int = 500,
n_measure: int = 200,
n_samples: int = 100,
R_thresholds: tuple[float, ...] = (0.3, 0.6, 0.8),
seed: int = 42,
) -> dict[str, BasinStabilityResult]
Basin stability at multiple synchronisation thresholds.
One Monte Carlo sweep; threshold classification repeated locally
for each entry of R_thresholds.
Returns¶
Dict mapping ``"R>={thresh:.2f}"`` to BasinStabilityResult.
Parameters¶
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
alpha : FloatArray | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
dt : float
Integration step size.
n_transient : int
Number of transient steps discarded before measurement.
n_measure : int
Number of steps averaged to measure the order parameter.
n_samples : int
Number of random initial-condition samples.
R_thresholds : tuple[float, ...]
Order-parameter thresholds to evaluate basin stability at.
seed : int
Seed for the deterministic RNG.
Source code in src/scpn_phase_orchestrator/upde/basin_stability.py
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 | |
Hypergraph (k-Body) Coupling Engine¶
Generalized k-body Kuramoto interactions via explicit hyperedge lists. Extends beyond the simplicial engine's fixed 3-body coupling to arbitrary k-body interactions for any k ≥ 2.
For a k-hyperedge {i₁, ..., iₖ}, the coupling on oscillator iₘ is: σₖ · sin(Σ_{j≠m} θ_{iⱼ} - (k-1)·θ_{iₘ})
This generalizes: - k=2: sin(θ_j - θ_i) — standard Kuramoto - k=3: sin(θ_j + θ_k - 2θ_i) — simplicial - k=4: sin(θ_j + θ_k + θ_l - 3θ_i) — quartic interaction
Supports mixed-order interactions: some edges pairwise, some 3-body, some 4-body, in the same network.
Optional hypergraph backends are validated before their results publish:
returned phase vectors must keep oscillator cardinality, contain finite real
values, and remain in [0, 2*pi). Loader/runtime unavailability can still fall
back to Python; malformed backend outputs raise instead of becoming simulation
evidence. Public phase, frequency, optional matrix, scalar, count, and
order-parameter inputs, direct backend vectors, index buffers, scalar controls,
and backend outputs reject numeric-string aliases before Python, NumPy, or
accelerator coercion.
Usage:
from scpn_phase_orchestrator.upde.hypergraph import HypergraphEngine
eng = HypergraphEngine(n_oscillators=8, dt=0.01)
eng.add_all_to_all(order=3, strength=0.5) # all 3-body edges
eng.add_edge((0, 1, 2, 3), strength=0.2) # one 4-body edge
phases = eng.run(phases_init, omegas, n_steps=1000,
pairwise_knm=knm) # combine with standard coupling
References: Tanaka & Aoyagi 2011, Phys. Rev. Lett. 106:224101; Bick et al. 2023, Nat. Rev. Physics 5:307-317. Detailed documentation: Hypergraph (k-body) — detailed reference
hypergraph ¶
Hypergraph Kuramoto with arbitrary k-body interactions beyond pairwise.
Exposes a 5-backend fallback chain.
Extends the standard Kuramoto model with k-body coupling terms for any k ≥ 2. The standard model (k=2) and simplicial model (k=3) are special cases.
For a k-hyperedge {i₁, …, iₖ}, the coupling on oscillator iₘ is
σₖ · sin( Σ_{j≠m} θ_{iⱼ} − (k−1)·θ_{iₘ} )
which generalises
k = 2: sin(θ_j − θ_i) — standard Kuramoto
k = 3: sin(θ_j + θ_k − 2·θ_i) — simplicial / triadic
The engine also accepts a dense pairwise coupling matrix
pairwise_knm (optional) and an external-drive field (ζ, ψ).
Numerics¶
The pairwise-derivative loop uses the Rust kernel's
sin(θ_j − θ_i) = sin(θ_j)·cos(θ_i) − cos(θ_j)·sin(θ_i)
expansion in the alpha == 0 fast path so that floating-point
rounding matches Rust (spo-engine/src/hypergraph.rs) bit-for-bit.
Alpha ≠ 0 falls back to the direct sin(θ_j − θ_i − α) form in
all five backends.
References¶
Tanaka & Aoyagi 2011, Phys. Rev. Lett. 106:224101. Skardal & Arenas 2019, Comm. Phys. 2:22. Bick, Gross, Harrington & Schaub 2023, Nat. Rev. Physics 5:307-317.
Classes¶
Hyperedge
dataclass
¶
A k-body interaction among oscillators.
Attributes¶
nodes: Tuple of oscillator indices in this hyperedge.
strength: Coupling strength σₖ for this hyperedge.
HypergraphEngine ¶
Kuramoto engine with arbitrary k-body hypergraph coupling.
Supports mixed-order interactions: some edges can be pairwise,
some 3-body, some 4-body, etc. Each Hyperedge specifies
which oscillators participate and the coupling strength.
Initialise a validated hypergraph Kuramoto engine.
Parameters¶
n_oscillators : int Positive number of oscillators in the simulated system. dt : float Positive finite explicit-Euler step size. hyperedges : list[Hyperedge] | None Optional initial hyperedge definitions to validate and store.
Source code in src/scpn_phase_orchestrator/upde/hypergraph.py
Attributes¶
n_edges
property
¶
Return the number of configured hyperedges.
Returns¶
int Return the number of configured hyperedges.
Methods:¶
add_edge ¶
Validate and append one explicit k-body hyperedge.
Parameters¶
nodes : tuple[int, ...] Indices of the oscillators participating in the hyperedge. strength : float Coupling strength assigned to the hyperedge(s).
Source code in src/scpn_phase_orchestrator/upde/hypergraph.py
add_all_to_all ¶
Add all C(N, order) hyperedges of given order.
Parameters¶
order : int Interaction order (number of oscillators per hyperedge). strength : float Coupling strength assigned to the hyperedge(s).
Raises¶
ValueError
If order is outside 2..N.
Source code in src/scpn_phase_orchestrator/upde/hypergraph.py
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
pairwise_knm: FloatArray | None = None,
alpha: FloatArray | None = None,
zeta: float = 0.0,
psi: float = 0.0,
) -> FloatArray
One explicit-Euler step.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
pairwise_knm : FloatArray | None
Optional pairwise coupling matrix (N, N), or None.
alpha : FloatArray | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
Returns¶
FloatArray The phases after one explicit-Euler hypergraph step.
Source code in src/scpn_phase_orchestrator/upde/hypergraph.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
n_steps: int,
pairwise_knm: FloatArray | None = None,
alpha: FloatArray | None = None,
zeta: float = 0.0,
psi: float = 0.0,
) -> FloatArray
Integrate n_steps Euler steps via the fastest backend; return phases.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
n_steps : int
Number of integration steps to run.
pairwise_knm : FloatArray | None
Optional pairwise coupling matrix (N, N), or None.
alpha : FloatArray | None
Phase-lag matrix in radians, shape (N, N), or None for no lag.
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
Returns¶
FloatArray
The final phases after n_steps hypergraph steps.
Source code in src/scpn_phase_orchestrator/upde/hypergraph.py
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | |
order_parameter ¶
Compute the standard Kuramoto R = |
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/hypergraph.py
Functions:¶
Strang Splitting Engine¶
Symmetric operator splitting: A(dt/2) → B(dt) → A(dt/2) where A is exact rotation (ω·dt) and B is RK4 on coupling. Second-order accurate, time-reversible, preserves symplectic structure approximately.
Direct Go, Julia, and Mojo Strang-splitting accelerator entrypoints share a
validated torus boundary before optional runtime loading: phase and frequency
vectors must be finite real one-dimensional float64 arrays matching the
oscillator count; flattened pairwise coupling and phase-lag buffers must have
exactly N*N values; pairwise self-coupling K_ii must be zero; zeta and
psi must be finite controls; and direct accelerator dt plus n_steps must
be positive. Backend outputs must be finite torus phases in [0, 2*pi), and
public state arrays, direct vectors/matrices, and backend outputs reject
numeric-string aliases before float coercion. Mojo stdout must contain exactly
one phase line per oscillator. The public SplittingEngine still supports
negative dt for reversibility checks by using the Python reference path
instead of direct optional accelerators.
Detailed documentation: Strang Splitting — detailed reference
splitting ¶
Strang second-order operator splitting for the Kuramoto ODE.
Exposes a 5-backend fallback chain.
Scheme¶
Split dθ/dt = ω + Σ_j K_ij · sin(θ_j − θ_i − α_ij) +
ζ · sin(ψ − θ_i) into
A: dθ/dt = ω (exact rotation)
B: dθ/dt = coupling (RK4 on the nonlinear part)
and compose symmetrically as A(dt/2) → B(dt) → A(dt/2)
(Strang scheme, second-order in dt).
Why split?¶
The ω flow is linear, so it has no truncation error; folding
it into a monolithic RK45 burns integrator budget on a solvable
direction while damping unrelated accuracy in the nonlinear
direction. Reference: Hairer, Lubich & Wanner 2006, Geometric
Numerical Integration §II.5.
Numerics¶
The B-stage RK4 uses the Rust kernel's
sin(θ_j − θ_i) = sin(θ_j)·cos(θ_i) − cos(θ_j)·sin(θ_i)
expansion on the alpha-zero branch so that floating-point
rounding matches Rust (spo-engine/src/splitting.rs)
bit-for-bit. Nonzero alpha falls back to the direct
sin(diff) form in all five backends.
Classes¶
SplittingEngine ¶
Strang-split Kuramoto stepper with 5-backend dispatch.
The engine's geometry is (n, dt); the step is stateless.
Create a Strang-splitting engine for n_oscillators and dt.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
) -> FloatArray
One Strang-split step: A(dt/2) → B(dt) → A(dt/2).
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
Returns¶
FloatArray The phases after one Strang-split step.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
knm: FloatArray,
zeta: float,
psi: float,
alpha: FloatArray,
n_steps: int,
) -> FloatArray
Apply repeated Strang-split phase integration steps.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
zeta : float
External drive strength ζ.
psi : float
External drive reference phase Ψ in radians.
alpha : FloatArray
Phase-lag matrix in radians, shape (N, N), or None for no lag.
n_steps : int
Number of integration steps to run.
Returns¶
FloatArray
The final phases after n_steps Strang-split steps.
Raises¶
ValueError
If n_steps is negative or the state arrays are invalid.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | |
order_parameter ¶
Compute the standard Kuramoto R = |
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
float
The Kuramoto order parameter R.
Source code in src/scpn_phase_orchestrator/upde/splitting.py
Functions:¶
Sparse Engine¶
SparseUPDEEngine implements the Kuramoto model using a CSR
(compressed sparse row) coupling matrix. This reduces memory from
O(N^2) dense coupling storage to O(N + E), where E is the number
of active directed edge connections.
It is designed for large-scale simulations (national power grids, social networks) where most oscillators are only coupled to local neighbours.
Features¶
- Scalability: Uses CSR row pointers, column indices, coupling values,
and phase-lag values so sparse topologies avoid dense
N x Nallocation. - FFI parity: Offloads sparse integration to the Rust backend when
spo_kernelis available, with Python fallback preserving the same shape, finite-value, phase-bounds, and adaptive-timestep contracts. Optional-Rust step/run output must remain inside[0, 2*pi), and the backendlast_dtmust be positive and finite before the public diagnostic is updated. - Input validation: Rejects malformed CSR row pointers, invalid oscillator indices, non-finite phase/frequency/coupling arrays, unsupported methods, and malformed optional-backend outputs before downstream workflows consume the result.
sparse_engine ¶
Sparse CSR-style UPDE engine for validated oscillator coupling graphs.
The sparse engine advances phase vectors from row pointers, column indices,
coupling values, and phase-lag values instead of dense N x N matrices.
Inputs are checked for finite values, CSR monotonicity, edge-count consistency,
valid oscillator indices, and method selection before stepping. Optional Rust
execution and Python fallback preserve the same shape and bounds contracts.
Classes¶
SparseUPDEEngine ¶
SparseUPDEEngine(
n_oscillators: int,
dt: float,
method: str = "euler",
atol: float = 1e-06,
rtol: float = 0.001,
)
Kuramoto UPDE integrator with sparse coupling matrix support.
The SparseUPDEEngine solves the Universal Phase Dynamics Equation (UPDE) using a CSR (Compressed Sparse Row) representation for the coupling matrix K_nm and phase lags alpha_nm. This is critical for scaling to large-scale oscillator networks (e.g., N > 10,000) where the dense K_nm matrix would consume terabytes of RAM.
Mathematics: dtheta_i/dt = omega_i + sum_{j in neighbors(i)} K_ij sin(theta_j - theta_i - alpha_ij) + zeta sin(Psi - theta_i)
The integrator supports sub-microsecond in-place plasticity updates when running on the Rust FFI path, allowing the coupling topology to evolve concurrently with the phase dynamics.
Initialize the sparse integrator.
Parameters¶
n_oscillators : int Total number of oscillators N in the network. dt : float Integration timestep in seconds. method : str Numerical method ('euler', 'rk4', or 'rk45'). atol : float Absolute tolerance for adaptive RK45. rtol : float Relative tolerance for adaptive RK45.
Source code in src/scpn_phase_orchestrator/upde/sparse_engine.py
Attributes¶
last_dt
property
¶
Return the most recent accepted Python or Rust timestep.
Returns¶
float Positive finite timestep accepted by the sparse engine.
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
row_ptr: IntArray,
col_indices: IntArray,
knm_values: FloatArray,
zeta: float,
psi: float,
alpha_values: FloatArray,
) -> FloatArray
Advance phases by one sparse timestep, return new phases in [0, 2*pi).
Parameters¶
phases : FloatArray Current phase vector [theta_1, ..., theta_N], shape (N,). omegas : FloatArray Natural frequency vector [omega_1, ..., omega_N], shape (N,). row_ptr : IntArray CSR row pointers, shape (N+1,). col_indices : IntArray CSR column indices, shape (E,). knm_values : FloatArray CSR coupling strengths, shape (E,). zeta : float External forcing strength (global scalar). psi : float Reference phase target (global scalar). alpha_values : FloatArray CSR phase lags, shape (E,).
Returns¶
FloatArray New phase vector [theta_1(t+dt), ..., theta_N(t+dt)], shape (N,).
Source code in src/scpn_phase_orchestrator/upde/sparse_engine.py
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 287 288 289 290 291 292 293 294 295 296 297 | |
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
row_ptr: IntArray,
col_indices: IntArray,
knm_values: FloatArray,
zeta: float,
psi: float,
alpha_values: FloatArray,
n_steps: int,
) -> FloatArray
Run multiple steps in a batch, return final phases.
Parameters¶
phases : FloatArray Initial phase vector. omegas : FloatArray Natural frequencies. row_ptr : IntArray CSR row pointers. col_indices : IntArray CSR column indices. knm_values : FloatArray CSR coupling strengths. zeta : float External forcing strength. psi : float Reference phase target. alpha_values : FloatArray CSR phase lags. n_steps : int Number of integration steps to perform.
Returns¶
FloatArray Final phase vector after n_steps.
Source code in src/scpn_phase_orchestrator/upde/sparse_engine.py
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 | |
Cellular Sheaf Engine¶
SheafUPDEEngine extends the Kuramoto model from scalar phases to
multi-dimensional phase vectors. This implements a cellular-sheaf model of
synchronization.
Instead of a single phase \(\theta_i\), each oscillator maintains a vector \(\vec{\theta}_i \in \mathbb{R}^D\). The scalar coupling \(K_{ij}\) is replaced by a restriction map—a block matrix \(B_{ij} \in \mathbb{R}^{D \times D}\) that maps the phase space of node \(j\) into the reference frame of node \(i\).
Features¶
- Cross-frequency coupling: Dimension \(k\) on node \(j\) can directly drive dimension \(d\) on node \(i\) through off-diagonal elements of \(B_{ij}\).
- Structured topology: Models opinion dynamics, multimodal synchronization, and anisotropic structural constraints natively.
- Rust parity: Uses
PySheafUPDEStepperwhen available while preserving the same public state-shape, numeric-type, finiteness, and torus-domain contracts. - Fail-closed arrays: Phase, frequency, restriction-map, and drive-target arrays reject boolean, complex, and numeric-string aliases before conversion.
- Fail-closed publication: Rust output must be a finite real flattened
N * Dtorus state, and its adaptive timestep must be positive and finite. A zero-step run returns a validated independent copy without backend dispatch.
sheaf_engine ¶
Cellular-sheaf UPDE integrator for multidimensional oscillator phases.
SheafUPDEEngine advances N x D phase matrices using restriction-map
coupling blocks and optional Rust acceleration. It validates oscillator counts,
dimensions, timestep/tolerances, solver method, forcing scalars, phase targets,
and tensor shapes before integration. Instance-level locks protect reusable
scratch buffers so concurrent callers cannot corrupt adaptive or fixed-step
solver state.
Classes¶
SheafUPDEEngine ¶
SheafUPDEEngine(
n_oscillators: int,
d_dimensions: int,
dt: float,
method: str = "euler",
atol: float = 1e-06,
rtol: float = 0.001,
)
Cellular Sheaf UPDE integrator for multi-dimensional phase vectors.
Phase per oscillator is a vector of dimension D. Restriction maps (coupling blocks) B_ij are D x D matrices mapping the phase space of oscillator j into the space of oscillator i.
Mathematics: d(theta_{i,d})/dt = omega_{i,d} + sum_j sum_k B_ij^{dk} sin(theta_{j,k} - theta_{i,d}) + zeta * sin(Psi_d - theta_{i,d})
This enables complex cross-frequency coupling and opinion dynamics over multidimensional belief spaces.
Source code in src/scpn_phase_orchestrator/upde/sheaf_engine.py
Attributes¶
last_dt
property
¶
Return the most recent accepted Python or Rust timestep.
Returns¶
float Positive finite timestep accepted by the sheaf engine.
Methods:¶
step ¶
step(
phases: FloatArray,
omegas: FloatArray,
restriction_maps: FloatArray,
zeta: float,
psi: FloatArray,
) -> FloatArray
Advance phases by one timestep.
Parameters¶
phases : FloatArray Current phase matrix [theta_i,d], shape (N, D). omegas : FloatArray Natural frequency matrix [omega_i,d], shape (N, D). restriction_maps : FloatArray Block matrix coupling [B_ij^{dk}], shape (N, N, D, D). zeta : float External forcing strength (global scalar). psi : FloatArray Reference phase target vector, shape (D,).
Returns¶
FloatArray New phase matrix, shape (N, D).
Source code in src/scpn_phase_orchestrator/upde/sheaf_engine.py
run ¶
run(
phases: FloatArray,
omegas: FloatArray,
restriction_maps: FloatArray,
zeta: float,
psi: FloatArray,
n_steps: int,
) -> FloatArray
Run multiple steps in a batch, return final phases.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N, D).
omegas : FloatArray
Natural frequencies in rad/s, shape (N, D).
restriction_maps : FloatArray
Sheaf restriction maps, shape (N, N, D, D).
zeta : float
External drive strength ζ.
psi : FloatArray
External drive reference phase Ψ in radians, shape (D,).
n_steps : int
Number of integration steps to run. Zero returns an independent,
validated copy without invoking the optional backend.
Returns¶
FloatArray
The final phases after n_steps sheaf steps.
Source code in src/scpn_phase_orchestrator/upde/sheaf_engine.py
Time-varying natural frequencies¶
UPDEEngine now accepts configured fixed or callable natural frequencies via
omega=. If a call omits the omegas argument, the engine resolves the
configured source at the current outer-step time and stores the resolved vector
in omega_current. Callable schedules are materialised as finite (steps, n)
matrices and dispatched through the Rust, Go, Julia, Mojo, or Python schedule
runner when available.
Use this for drifting oscillators, moving-agent frequency shifts, chirps, thermal detuning, and Doppler preparation. The detailed contract is documented in UPDE — Time-varying omega.
PHA-C.2 Doppler-corrected UPDE¶
DopplerEngine adds graph-weighted relative-velocity detuning before each
UPDE outer step. It consumes the PHA-C.5 omega(t) schedule contract and is
used when phase locking depends on moving oscillators rather than fixed natural
frequencies, such as counter-propagating plasmoids, mobile acoustic clocks,
robot/sensor swarms, and moving-grid assets.
See UPDE — Doppler Engine for the mathematical contract, scalar/vector velocity handling, backend parity surface, and Mach-1 counter-propagating acceptance scenario.
PHA-C formal proof-obligation bridge¶
PHACKinematicProofObligation projects a verified end-to-end PHA-C acceptance
record into fixed-point Lean obligations for SPOFormal.Kinematic and
SPOFormal.Continuous. The manifest is review-only and non-actuating: it
binds the accepted timeline hash, acceptance hash, spatial merge-window
tolerance, phase tolerance, time-step units, horizon-time units, Gronwall
budget trace, continuous horizon-drive budget, and certificate theorem names.
Predictive downstream consumers can now supply both
relative_velocity_step_bound_m, coupling_residual_step_bound_m, and
phase_drift_bound_rad when building the obligation. The residual bound is
recorded separately as configured_coupling_residual_step_bound_units, then
combined with the observed moving-frame kinematic residual before the sampled
residual rate, discrete drive bound, and continuous drive bound are accepted.
The phase drift bound is recorded separately as
configured_phase_drift_bound_units, then added to observed phase dispersion
before the phase margin is accepted. The manifest now also names the Lean
PhaseBudgetBounds.budgetCertificate predicate and
phase_budget_certificate_discharges_phase_lock theorem for that phase budget.
The phase_budget_discharged field must replay that theorem condition exactly
before the combined proof obligation can discharge. This keeps FRC or MIF
specialisations from hiding residual uncertainty inside the relative-velocity
term or phase uncertainty inside replay dispersion.
The same manifest now records the Lean
KinematicBounds.acceptanceCertificate predicate and
acceptance_certificate_discharges_runtime_preconditions theorem. The
acceptance_certificate_discharged field is recomputed from the spatial
Gronwall margin, phase-budget discharge, and moving-frame equation replay
certificate before the manifest hash is accepted.
pha_c_formal_obligation ¶
Deterministic Lean proof-obligation manifests for PHA-C acceptance records.
The PHA-C acceptance chain is runtime evidence. The Lean kinematic proofs are
formal evidence. This module binds the two surfaces by projecting a verified
PHACAcceptanceRecord into fixed-point natural-number obligations that match
SPOFormal.Kinematic.KinematicBounds. The resulting manifest remains
review-only and non-actuating; it is a reproducible bridge for release review,
MIF/FRC specialisation, and benchmark gating.
Classes¶
PHACKinematicProofObligation
dataclass
¶
PHACKinematicProofObligation(
schema_version: str,
evidence_kind: str,
claim_boundary: str,
acceptance_claim_boundary: str,
execution_disabled: bool,
actuating: bool,
lean_module: str,
lean_certificate_predicate: str,
lean_theorem: str,
continuous_lean_module: str,
continuous_certificate_predicate: str,
continuous_theorem: str,
phase_lean_module: str,
phase_certificate_predicate: str,
phase_theorem: str,
acceptance_certificate_predicate: str,
acceptance_certificate_theorem: str,
fixed_point_scale_m: float,
fixed_point_scale_rad: float,
fixed_point_time_scale_s: float,
time_step_s: float,
time_scale_units_per_second: int,
time_step_units: int,
horizon_time_units: int,
initial_tolerance_units: int,
lipschitz_step_gain_units: int,
relative_velocity_rate_bound_units_per_second: int,
relative_velocity_step_bound_units: int,
configured_coupling_residual_step_bound_units: int,
coupling_residual_rate_bound_units_per_second: int,
coupling_residual_step_bound_units: int,
continuous_drive_rate_bound_units_per_second: int,
continuous_horizon_drive_bound_units: int,
continuous_linear_budget_units: int,
continuous_margin_units: int,
drive_bound_units: int,
merge_window_tolerance_units: int,
horizon_steps: int,
linear_budget_units: int,
gronwall_budget_units: int,
gronwall_budget_margin_units: int,
gronwall_budget_trace_sha256: str,
window_budget_margin_units: int,
phase_tolerance_units: int,
max_phase_dispersion_units: int,
configured_phase_drift_bound_units: int,
phase_budget_units: int,
phase_margin_units: int,
phase_budget_discharged: bool,
acceptance_kinematic_equations_validated: bool,
acceptance_kinematic_summary_replay_tolerance: float,
acceptance_kinematic_summary_replay_tolerance_units: int,
acceptance_kinematic_summary_replay_tolerance_limit_units: int,
acceptance_replay_certificate_discharged: bool,
acceptance_certificate_discharged: bool,
observed_velocity_step_units: int,
kinematic_residual_units: int,
path_length_units: int,
max_spatial_dispersion_units: int,
continuous_envelope_discharged: bool,
proof_obligations_discharged: bool,
acceptance_sha256: str,
timeline_sha256: str,
record_sha256: str,
)
Review-only fixed-point obligations linked to the Lean kinematic proof.
Functions:¶
pha_c_kinematic_proof_obligation_to_dict ¶
pha_c_kinematic_proof_obligation_to_dict(
obligation: PHACKinematicProofObligation,
) -> dict[str, bool | float | int | str]
Return a verified canonical JSON-safe proof-obligation manifest.
Parameters¶
obligation : PHACKinematicProofObligation The PHA-C kinematic proof obligation to verify and serialise.
Returns¶
dict[str, bool | float | int | str] The verified canonical JSON-safe proof-obligation manifest.
Source code in src/scpn_phase_orchestrator/upde/pha_c_formal_obligation.py
build_pha_c_kinematic_proof_obligation ¶
build_pha_c_kinematic_proof_obligation(
record: PHACAcceptanceRecord,
*,
fixed_point_scale_m: float = PHA_C_FORMAL_DEFAULT_SCALE_M,
fixed_point_scale_rad: float = PHA_C_FORMAL_DEFAULT_SCALE_RAD,
fixed_point_time_scale_s: float = PHA_C_FORMAL_DEFAULT_TIME_SCALE_S,
relative_velocity_step_bound_m: float = 0.0,
coupling_residual_step_bound_m: float = 0.0,
phase_drift_bound_rad: float = 0.0,
lipschitz_step_gain_units: int = 0,
) -> PHACKinematicProofObligation
Project a verified PHA-C acceptance record into Lean proof obligations.
The default obligation is a replay certificate: the maximum observed
spatial dispersion is already measured over the accepted trajectory, so the
Lean drive term only includes explicitly supplied future relative-velocity
slack and the signed moving-frame residual. MIF/FRC specialisations can
provide non-zero relative_velocity_step_bound_m and
coupling_residual_step_bound_m, phase_drift_bound_rad, and
lipschitz_step_gain_units values when they want a predictive
finite-horizon Gronwall certificate instead of a replay-only envelope.
Parameters¶
record : PHACAcceptanceRecord The PHA-C record to operate on. fixed_point_scale_m : float Spatial fixed-point scale in metres. fixed_point_scale_rad : float Phase fixed-point scale in radians. fixed_point_time_scale_s : float Temporal fixed-point scale in seconds. relative_velocity_step_bound_m : float Per-step relative-velocity bound in metres. coupling_residual_step_bound_m : float Per-step coupling-residual bound in metres. phase_drift_bound_rad : float Per-step phase-drift bound in radians. lipschitz_step_gain_units : int Lipschitz step-gain bound in dimensionless integer units.
Returns¶
PHACKinematicProofObligation The Lean proof-obligation projection of the acceptance record.
Source code in src/scpn_phase_orchestrator/upde/pha_c_formal_obligation.py
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | |
verify_pha_c_kinematic_proof_obligation ¶
verify_pha_c_kinematic_proof_obligation(
obligation: PHACKinematicProofObligation,
) -> PHACKinematicProofObligation
Validate a PHA-C Lean proof-obligation manifest fail-closed.
Parameters¶
obligation : PHACKinematicProofObligation The PHA-C kinematic proof obligation to operate on.
Returns¶
PHACKinematicProofObligation The same obligation after fail-closed validation.
Raises¶
TypeError If the manifest has the wrong type. ValueError If the manifest fails validation.
Source code in src/scpn_phase_orchestrator/upde/pha_c_formal_obligation.py
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | |