Skip to content

Subsystem: upde — phase-ODE integrator family

The numerical core: integrates coupled phase dynamics. 74 files, ~18.3k LOC.

Inputs

step(phases, omegas, knm, zeta, psi, alpha):

  • phasesNDArray[float64], shape (N,), radians.
  • omegas(N,), natural frequencies (rad/s).
  • knm(N, N), coupling matrix, zero diagonal (validated).
  • alpha(N, N), Sakaguchi phase-lag matrix.
  • zeta — scalar external-drive strength; psi — drive reference phase.
  • IntegrationConfig(dt, method ∈ {euler, rk4, rk45}, substeps, atol, rtol).

Outputs

  • New phases, wrapped to [0, 2π).
  • UPDEState — frozen diagnostic record: per-layer LayerState, cross_layer_alignment, stability_proxy.
  • Order parameter (R ∈ [0,1], ψ) via order_params.compute_order_parameter.

Processing model

Integration methods: forward Euler, classical RK4, and adaptive RK45 (Dormand–Prince 5/4 with PI step control). Geometric variants use an exponential map on the unit circle; the stochastic layer uses Euler–Maruyama; the Ott–Antonsen reduction is an O(1) mean-field predictor (Bessel I0, I1).

Engine variants (14–15 implemented)

UPDEEngine (standard Kuramoto), StuartLandauEngine (phase + amplitude, Hopf), InertialKuramotoEngine (2nd-order swing), SwarmalatorEngine, SimplicialEngine (3-body), TorusEngine (symplectic), DelayedEngine, SplittingEngine (operator splitting), SheafUPDEEngine, SparseUPDEEngine (CSR), HypergraphEngine, DopplerEngine, MovingFrameUPDEEngine, and JAX JaxUPDEEngine / JaxStuartLandauEngine. Papers cited in code (unvouched): Acebrón 2005, Filatrella–Nielsen–Mallick 2008, and others. The two JAX engines validate array source types on the host before JAX device conversion: boolean, complex, and numeric-string aliases fail closed while finite real numeric-object arrays remain compatible.

Backends

Dispatched through upde/_run.py with the fastest-first chain Rust → WebGPU → Mojo → Julia → Go → Python; the per-language forwarder modules (_engine_go.py, _engine_mojo.py, …) point to experimental/accelerators/upde/. Rust paths exist for the standard, sparse, sheaf, geometric, inertial, and Ott–Antonsen engines. The sparse Rust boundary accepts only a finite real phase vector with exact oscillator cardinality and every phase inside [0, 2*pi). Its positive finite adaptive timestep is replayed into the public last_dt diagnostic only after the phase result passes validation. The sheaf boundary validates phase, frequency, restriction-map, and drive-target source types before conversion. Its Rust result is accepted only as a finite real flattened N * D torus state, and the positive finite Rust adaptive timestep is replayed into the public last_dt diagnostic. Finite-difference and optional JAX adjoint paths share one pre-execution state contract for phase, frequency, coupling, and phase-lag arrays. Counts and perturbation/timestep scalars are validated before arithmetic or optional JAX imports, so backend availability cannot mask malformed public input. Bayesian UPDE uses one source-type-aware array boundary for direct inputs, Gaussian distribution parameters, fitted posterior data, and custom distribution samples. Boolean, complex, and numeric-string aliases are rejected before float64 conversion, and finite real drive controls are validated before Monte Carlo rollout. The forward and variational prediction models apply the same source-type-aware contract to phase, frequency, predicted-state, observed-state, and precision vectors. Coercive aliases fail before prediction, free-energy arithmetic, or online state updates, while finite real numeric object arrays remain compatible. Public Strang-splitting phase, frequency, coupling, phase-lag, and optional- backend phase arrays reject boolean, complex/object-complex, and numeric-string aliases before conversion while preserving finite real numeric-object arrays. Existing shape, zero-self-coupling, and torus-output checks remain authoritative. Geometric direct Go, Julia, and Mojo bridges validate torus phase, frequency, coupling, lag, scalar, count, and backend-output payloads before optional native runtime loading; numeric-string aliases are rejected at the Python boundary.

Wiring

Constructed by api.Orchestrator, runtime/simulation.simulate, and the server simulation state. Output (UPDEState, order parameter) feeds monitor/ and, through it, supervisor/. coupling/ supplies the knm each step.

Scope boundaries

  • JAX engines are marked # pragma: no cover (untested in standard CI).
  • bayesian.py raises NotImplementedError for non-NumPy uncertainty backends.
  • The splitting engine documents symplectic reversibility but has no test asserting it.