Skip to content

UPDE Moving-Frame Engine

MovingFrameUPDEEngine is the PHA-C.3 kinematic phase-dynamics surface for systems where each oscillator has both a phase and an absolute axial coordinate in a chamber-fixed reference frame. It composes two existing production contracts:

  • SpatialCouplingModulator computes distance-dependent K_nm(z) from the current axial positions.
  • DopplerEngine computes velocity-dependent detuning from the active coupling graph.

The engine then advances phase and position as one coupled step. This is the surface to use when phase synchronisation depends on moving geometry rather than only on oscillator labels.

Use cases

Use the moving-frame engine for domains where a fixed graph is physically incorrect:

  • counter-propagating plasmoid or particle packets approaching a chamber reference point;
  • mobile sensor or clock swarms where distance and relative velocity alter synchronisation;
  • acoustic, RF, or oscillator networks with line-of-flight detuning;
  • digital-twin studies where collision or merge timing must be tied to the same integration clock as phase dynamics;
  • MIF-style axial merger scenarios that require both R(t) and z_i(t).

Mathematical contract

For every outer integration step s, with axial positions z_i(s) and scalar velocities v_i(s):

K_eff_ij(s) = K_base_ij * g(|z_i(s) - z_j(s)|)
D_i(s) = doppler_strength * sum_j |K_eff_ij| * (v_i - v_j) / (|v_i| + eps)
         / sum_j |K_eff_ij|
omega_eff_i(s) = omega_i(s) + D_i(s)
theta(s + dt) = UPDE(theta(s), omega_eff(s), K_eff(s), alpha, dt)
z_i(s + dt) = z_i(s) + v_i(s) * dt

g(distance) is the configured SpatialCouplingModulator decay kernel: inverse_plus_one, exponential, power_law, or inverse_distance. The position update is ballistic over each outer step, which is exact for fixed velocity over that step and consistent with the row-major velocity schedule used by the backend contract. Production validation signs this kinematic identity explicitly: the expected final coordinate is z(0) + dt * sum_s velocity_schedule[s], and every backend row must keep the maximum absolute residual below 1e-9 m before the run is accepted. The benchmark also replays the derived summary equations max_abs_velocity = max(|velocity_schedule|) and path_length_max = max_i sum_s |velocity_schedule[s, i] * dt| under KINEMATIC_SUMMARY_REPLAY_TOLERANCE.

Public API

import numpy as np
from scpn_phase_orchestrator.coupling import SpatialCouplingModulator
from scpn_phase_orchestrator.upde import MovingFrameUPDEEngine

knm = np.array([[0.0, 0.4], [0.4, 0.0]])
engine = MovingFrameUPDEEngine(
    2,
    omega=np.array([0.1, -0.1]),
    k_nm=knm,
    alpha=0.0,
    dt=1.0e-9,
    positions_t0=np.array([-1.0e-6, 1.0e-6]),
    velocities=np.array([500.0, -500.0]),
    spatial_modulator=SpatialCouplingModulator(K_base=0.8),
    doppler_strength=0.01,
    solver="rk45",
)

phases = engine.run(n_steps=2)
positions = engine.positions
near_reference = engine.collision_imminent(threshold_m=1.0e-9)
residual_m = engine.state.kinematic_residual_max_m

positions_t0 must be a finite axial vector with shape (n,). velocities may be a fixed vector or a callable velocities(t) -> array, matching the Doppler schedule contract. omega may be fixed or callable, matching the standard UPDEEngine time-varying frequency contract.

Collision predicate

collision_imminent(threshold_m=...) checks current distance to the reference, next-step distance under the currently resolved velocity, and sign-crossing of the chamber reference. With threshold_m=0.0, exact crossing is still detected. This makes it useful for merge-window monitors without forcing every caller to manually inspect signed positions.

Downstream handoff

Moving-frame outputs can be passed to build_pha_c_handoff_record(...) after a merge-window evaluation when a downstream lane needs compact event evidence instead of full phase/position arrays. The handoff keeps scalar lock evidence, order-parameter evidence, vector digests, and a canonical event hash while remaining non-actuating.

from scpn_phase_orchestrator.upde.pha_c_handoff import (
    build_pha_c_handoff_record,
)

record = build_pha_c_handoff_record(
    phases,
    positions,
    t=engine.time,
    phase_tol_rad=0.01,
    spatial_tol_m=0.002,
    required_consecutive_samples=1,
)

Backend contract

The backend-neutral function moving_frame_run(...) accepts row-major schedules and returns a flat vector:

[final_phase_0, ..., final_phase_n-1, final_z_0, ..., final_z_n-1]

Python, Rust/PyO3, Go, Julia, and Mojo source surfaces share the same contract. Optional accelerator runtimes are feature-detected; unavailable runtimes are reported by the benchmark rather than hidden. The benchmark also records expected_final_position_sha256, reference_kinematic_residual_max_m, kinematic_residual_contract_passed, final_position_equation_validated, max_abs_velocity_equation_validated, path_length_equation_validated, kinematic_equations_validated, max_abs_velocity_m_per_s, and path_length_max_m so polyglot rows cannot pass with a numerically correct phase vector but a physically wrong coordinate, velocity, or path-length summary.

PYTHONPATH=src python benchmarks/upde_moving_frame_benchmark.py --parity-gate

Committed benchmark JSON is local regression and parity evidence only. It is not a production throughput claim unless rerun under the repository benchmark isolation protocol.

Failure boundaries

The moving-frame engine fails closed on:

  • non-finite, complex, object-dtype, or boolean phase, position, omega, velocity, K_nm, or alpha inputs;
  • non-zero self-coupling diagonal in the base K_nm;
  • malformed schedule shapes or mismatched oscillator counts;
  • negative collision thresholds;
  • non-positive spatial decay scale, spatial epsilon, or Doppler epsilon;
  • backend output with non-finite positions, phases outside [0, 2*pi), or final positions that violate the ballistic schedule residual tolerance.

MovingFrameUPDEEngine

MovingFrameUPDEEngine(
    n: int,
    omega: object,
    k_nm: object,
    alpha: object = 0.0,
    dt: float = 0.01,
    positions_t0: object | None = None,
    velocities: object
    | Callable[[float], object]
    | None = None,
    spatial_modulator: SpatialCouplingModulator
    | None = None,
    reference_point: float = 0.0,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1e-09,
    solver: str = "rk45",
    phases: object | None = None,
    velocity_axis: object | None = None,
    t0: float = 0.0,
)

Bases: DopplerEngine

UPDE engine with chamber-frame axial positions and collision checks.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def __init__(
    self,
    n: int,
    omega: object,
    k_nm: object,
    alpha: object = 0.0,
    dt: float = 0.01,
    positions_t0: object | None = None,
    velocities: object | Callable[[float], object] | None = None,
    spatial_modulator: SpatialCouplingModulator | None = None,
    reference_point: float = 0.0,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1.0e-9,
    solver: str = "rk45",
    phases: object | None = None,
    velocity_axis: object | None = None,
    t0: float = 0.0,
) -> None:
    if positions_t0 is None:
        raise ValueError("positions_t0 is required for MovingFrameUPDEEngine")
    if spatial_modulator is None:
        raise ValueError("spatial_modulator is required for MovingFrameUPDEEngine")
    self.spatial_modulator = _validate_spatial_modulator(spatial_modulator)
    self.reference_point = _finite_float(reference_point, name="reference_point")
    super().__init__(
        n,
        omega=omega,
        k_nm=k_nm,
        alpha=alpha,
        dt=dt,
        velocities=velocities,
        doppler_strength=doppler_strength,
        doppler_epsilon=doppler_epsilon,
        solver=solver,
        phases=phases,
        velocity_axis=velocity_axis,
        t0=t0,
    )
    self._positions = _validate_positions_vector(positions_t0, n=n)
    self._knm_effective = self._modulated_knm(self.k_nm, self._positions)
    self._doppler_term = doppler_term(
        self.velocity_current,
        self._knm_effective,
        doppler_strength=self.doppler_strength,
        doppler_epsilon=self.doppler_epsilon,
    )
    self._kinematic_residual_max_m = 0.0
    self._max_abs_velocity_m_per_s = float(np.max(np.abs(self.velocity_current)))
    self._path_length_max_m = 0.0

Attributes

positions property

positions: FloatArray

Current absolute axial coordinate for each oscillator.

Returns

FloatArray Current absolute axial coordinate for each oscillator.

distance_to_reference property

distance_to_reference: FloatArray

Absolute distance from each oscillator to the chamber reference.

Returns

FloatArray Absolute distance from each oscillator to the chamber reference.

knm_effective property

knm_effective: FloatArray

Most recently applied distance-modulated coupling matrix.

Returns

FloatArray Most recently applied distance-modulated coupling matrix.

kinematic_residual_max_m property

kinematic_residual_max_m: float

Maximum residual against z_next = z + v*dt in the last run.

Returns

float Maximum residual against z_next = z + v*dt in the last run.

max_abs_velocity_m_per_s property

max_abs_velocity_m_per_s: float

Maximum absolute axial velocity used by the last step or run.

Returns

float Maximum absolute axial velocity used by the last step or run.

path_length_max_m property

path_length_max_m: float

Maximum per-oscillator axial path length in the last step or run.

Returns

float Maximum per-oscillator axial path length in the last step or run.

state property

state: MovingFrameState

Return the current moving-frame diagnostic snapshot.

Returns

MovingFrameState Return the current moving-frame diagnostic snapshot.

Methods:

collision_imminent

collision_imminent(threshold_m: float = 0.001) -> bool

Return whether any oscillator is at or crosses the reference soon.

Parameters

threshold_m : float Distance threshold in metres.

Returns

bool True when an oscillator is at or crossing the reference within one step.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def collision_imminent(self, threshold_m: float = 1.0e-3) -> bool:
    """Return whether any oscillator is at or crosses the reference soon.

    Parameters
    ----------
    threshold_m : float
        Distance threshold in metres.

    Returns
    -------
    bool
        ``True`` when an oscillator is at or crossing the reference within one step.
    """
    threshold = _validate_nonnegative_float(threshold_m, name="threshold_m")
    signed_now = self._positions - self.reference_point
    signed_next = signed_now + self.velocity_current * self._dt
    current_near = np.abs(signed_now) <= threshold
    next_near = np.abs(signed_next) <= threshold
    crosses = signed_now * signed_next <= 0.0
    return bool(np.any(current_near | next_near | crosses))

step

step(
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
) -> FloatArray

Advance one coupled phase/position step.

Parameters

phases : object | None Oscillator phases in radians, shape (N,). omegas : object | None Natural frequencies in rad/s, shape (N,). knm : object | None Coupling matrix K_nm, shape (N, N). zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. alpha : object | None Phase-lag matrix in radians, shape (N, N), or None for no lag.

Returns

FloatArray The phases after one coupled phase/position step.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def step(
    self,
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
) -> FloatArray:
    """Advance one coupled phase/position step.

    Parameters
    ----------
    phases : object | None
        Oscillator phases in radians, shape ``(N,)``.
    omegas : object | None
        Natural frequencies in rad/s, shape ``(N,)``.
    knm : object | None
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    alpha : object | None
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.

    Returns
    -------
    FloatArray
        The phases after one coupled phase/position step.
    """
    k_base = self.k_nm if knm is None else _validate_knm(knm, n=self._n)
    k_effective = self._modulated_knm(k_base, self._positions)
    positions_start = self._positions.copy()
    out = super().step(phases, omegas, k_effective, zeta, psi, alpha)
    self._knm_effective = k_effective
    self._positions = np.ascontiguousarray(
        positions_start + self.velocity_current * self._dt,
        dtype=np.float64,
    )
    expected_positions = positions_start + self.velocity_current * self._dt
    self._kinematic_residual_max_m = _kinematic_residual_max(
        self._positions,
        expected_positions,
    )
    self._max_abs_velocity_m_per_s = float(np.max(np.abs(self.velocity_current)))
    self._path_length_max_m = float(
        np.max(np.abs(self.velocity_current * self._dt))
    )
    return out

run

run(
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
    n_steps: int = 1,
) -> FloatArray

Run n_steps of joint phase and axial-position dynamics.

Parameters

phases : object | None Oscillator phases in radians, shape (N,). omegas : object | None Natural frequencies in rad/s, shape (N,). knm : object | None Coupling matrix K_nm, shape (N, N). zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. alpha : object | 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 joint phase/position steps.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def run(
    self,
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
    n_steps: int = 1,
) -> FloatArray:
    """Run ``n_steps`` of joint phase and axial-position dynamics.

    Parameters
    ----------
    phases : object | None
        Oscillator phases in radians, shape ``(N,)``.
    omegas : object | None
        Natural frequencies in rad/s, shape ``(N,)``.
    knm : object | None
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    alpha : object | 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`` joint phase/position steps.
    """
    steps = _validate_positive_step_count(n_steps, name="n_steps")
    p = self.phases if phases is None else _validate_phases(phases, n=self._n)
    k = self.k_nm if knm is None else _validate_knm(knm, n=self._n)
    a = self.alpha_matrix if alpha is None else _validate_alpha(alpha, n=self._n)
    omega_source = self._omega_source if omegas is None else omegas
    omega_schedule = self._omega_schedule(omega_source, steps)
    velocity_schedule = self._velocity_schedule(steps)
    z0 = self._positions.copy()
    flat = moving_frame_run(
        p,
        z0,
        omega_schedule,
        k,
        a,
        velocity_schedule,
        self.spatial_modulator,
        self.doppler_strength,
        self.doppler_epsilon,
        zeta,
        psi,
        self._dt,
        self._method,
        1,
        self._atol,
        self._rtol,
    )
    n = self._n
    self.phases = np.ascontiguousarray(flat[:n], dtype=np.float64)
    self._positions = np.ascontiguousarray(flat[n:], dtype=np.float64)
    expected_positions = _expected_positions_from_schedule(
        z0,
        velocity_schedule,
        self._dt,
    )
    self._kinematic_residual_max_m = _kinematic_residual_max(
        self._positions,
        expected_positions,
    )
    self._max_abs_velocity_m_per_s = float(np.max(np.abs(velocity_schedule)))
    self._path_length_max_m = float(
        np.max(np.sum(np.abs(velocity_schedule * self._dt), axis=0))
    )
    if steps > 1:
        last_start_positions = z0 + self._dt * np.sum(
            velocity_schedule[:-1], axis=0
        )
    else:
        last_start_positions = z0
    self._knm_effective = self._modulated_knm(k, last_start_positions)
    self._omega_current = omega_schedule[-1].copy()
    self.velocity_current = velocity_schedule[-1].copy()
    self._doppler_term = doppler_term(
        self.velocity_current,
        self._knm_effective,
        doppler_strength=self.doppler_strength,
        doppler_epsilon=self.doppler_epsilon,
    )
    self._time += steps * self._dt
    return self.phases.copy()

moving_frame_run

moving_frame_run(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_modulator: SpatialCouplingModulator,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1e-09,
    zeta: float = 0.0,
    psi: float = 0.0,
    dt: float = 0.01,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1e-06,
    rtol: float = 0.001,
    *,
    backend: str = "auto",
) -> FloatArray

Run a moving-frame UPDE schedule through the selected backend.

Parameters

phases : object Oscillator phases in radians, shape (N,). positions : object Absolute axial coordinates per oscillator, shape (N,). omega_schedule : object Per-step natural-frequency vectors, shape (n_steps, N). knm : object Coupling matrix K_nm, shape (N, N). alpha : object Phase-lag matrix in radians, shape (N, N), or None for no lag. velocity_schedule : object Per-step axial velocity vectors, shape (n_steps, N). spatial_modulator : SpatialCouplingModulator Configured spatial coupling modulator. doppler_strength : float Doppler coupling-correction strength. doppler_epsilon : float Numerical floor guarding the Doppler denominator. zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. dt : float Integration step size. method : str Integration method (euler, rk4, or rk45). n_substeps : int Number of inner substeps per outer step. atol : float Absolute tolerance for the adaptive (rk45) integrator. rtol : float Relative tolerance for the adaptive (rk45) integrator. backend : str Name of the compute backend to run.

Returns

FloatArray The final phases after running the moving-frame schedule on the selected backend.

Raises

ImportError If the selected backend's runtime is unavailable. ValueError If the schedule contract is invalid; the underlying backend error propagates when every backend fails.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def moving_frame_run(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_modulator: SpatialCouplingModulator,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1.0e-9,
    zeta: float = 0.0,
    psi: float = 0.0,
    dt: float = 0.01,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1.0e-6,
    rtol: float = 1.0e-3,
    *,
    backend: str = "auto",
) -> FloatArray:
    """Run a moving-frame UPDE schedule through the selected backend.

    Parameters
    ----------
    phases : object
        Oscillator phases in radians, shape ``(N,)``.
    positions : object
        Absolute axial coordinates per oscillator, shape ``(N,)``.
    omega_schedule : object
        Per-step natural-frequency vectors, shape ``(n_steps, N)``.
    knm : object
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    alpha : object
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.
    velocity_schedule : object
        Per-step axial velocity vectors, shape ``(n_steps, N)``.
    spatial_modulator : SpatialCouplingModulator
        Configured spatial coupling modulator.
    doppler_strength : float
        Doppler coupling-correction strength.
    doppler_epsilon : float
        Numerical floor guarding the Doppler denominator.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    dt : float
        Integration step size.
    method : str
        Integration method (``euler``, ``rk4``, or ``rk45``).
    n_substeps : int
        Number of inner substeps per outer step.
    atol : float
        Absolute tolerance for the adaptive (rk45) integrator.
    rtol : float
        Relative tolerance for the adaptive (rk45) integrator.
    backend : str
        Name of the compute backend to run.

    Returns
    -------
    FloatArray
        The final phases after running the moving-frame schedule on the selected
        backend.

    Raises
    ------
    ImportError
        If the selected backend's runtime is unavailable.
    ValueError
        If the schedule contract is invalid; the underlying backend error propagates
        when every backend fails.
    """
    modulator = _validate_spatial_modulator(spatial_modulator)
    if modulator.distance_fn is not None:
        raise ValueError(
            "moving_frame_run requires the default axial SpatialCouplingModulator "
            "distance kernel"
        )
    validated = validate_moving_frame_backend_inputs(
        phases,
        positions,
        omega_schedule,
        knm,
        alpha,
        velocity_schedule,
        modulator.K_base,
        modulator.decay_form,
        modulator.decay_exponent,
        modulator.decay_length_scale,
        modulator.epsilon,
        doppler_strength,
        doppler_epsilon,
        zeta,
        psi,
        dt,
        method,
        n_substeps,
        atol,
        rtol,
    )
    (
        p,
        z,
        omega,
        k,
        a,
        velocities,
        k_base,
        decay_code,
        decay_exponent,
        decay_length_scale,
        spatial_eps,
        strength,
        doppler_eps,
        zeta_f,
        psi_f,
        dt_f,
        _n_steps,
        method_s,
        n_substeps_i,
        atol_f,
        rtol_f,
    ) = validated
    expected_positions = _expected_positions_from_schedule(z, velocities, dt_f)
    backends = _backend_map()
    if backend != "auto" and backend not in backends:
        raise ImportError(f"moving-frame backend {backend!r} is not available")
    order = _BACKEND_ORDER if backend == "auto" else (backend,)
    last_error: Exception | None = None
    for name in order:
        fn = backends.get(name)
        if fn is None:
            continue
        try:
            out = fn(
                p,
                z,
                omega,
                k,
                a,
                velocities,
                k_base,
                decay_code,
                decay_exponent,
                decay_length_scale,
                spatial_eps,
                strength,
                doppler_eps,
                zeta_f,
                psi_f,
                dt_f,
                method_s,
                n_substeps_i,
                atol_f,
                rtol_f,
            )
            return validate_moving_frame_backend_output(
                out,
                n=int(p.size),
                expected_positions=expected_positions,
            )
        except (AttributeError, ImportError) as exc:
            last_error = exc
            continue
    if backend != "auto" and last_error is not None:
        raise last_error
    return validate_moving_frame_backend_output(
        moving_frame_run_python(
            p,
            z,
            omega,
            k,
            a,
            velocities,
            k_base,
            decay_code,
            decay_exponent,
            decay_length_scale,
            spatial_eps,
            strength,
            doppler_eps,
            zeta_f,
            psi_f,
            dt_f,
            method_s,
            n_substeps_i,
            atol_f,
            rtol_f,
        ),
        n=int(p.size),
        expected_positions=expected_positions,
    )

moving_frame_run_python

moving_frame_run_python(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_k_base: float,
    spatial_decay_form: object,
    spatial_decay_exponent: float,
    spatial_decay_length_scale: float,
    spatial_epsilon: float,
    doppler_strength: float,
    doppler_epsilon: float,
    zeta: float,
    psi: float,
    dt: float,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1e-06,
    rtol: float = 0.001,
) -> FloatArray

Run the moving-frame UPDE schedule in the Python reference path.

Parameters

phases : object Oscillator phases in radians, shape (N,). positions : object Absolute axial coordinates per oscillator, shape (N,). omega_schedule : object Per-step natural-frequency vectors, shape (n_steps, N). knm : object Coupling matrix K_nm, shape (N, N). alpha : object Phase-lag matrix in radians, shape (N, N), or None for no lag. velocity_schedule : object Per-step axial velocity vectors, shape (n_steps, N). spatial_k_base : float Base coupling strength before spatial modulation. spatial_decay_form : object Spatial decay law name (e.g. exponential or power). spatial_decay_exponent : float Exponent of the spatial decay law. spatial_decay_length_scale : float Characteristic length scale of the spatial decay. spatial_epsilon : float Numerical floor guarding the spatial-decay denominator. doppler_strength : float Doppler coupling-correction strength. doppler_epsilon : float Numerical floor guarding the Doppler denominator. zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. dt : float Integration step size. method : str Integration method (euler, rk4, or rk45). n_substeps : int Number of inner substeps per outer step. atol : float Absolute tolerance for the adaptive (rk45) integrator. rtol : float Relative tolerance for the adaptive (rk45) integrator.

Returns

FloatArray The final phases after running the moving-frame schedule on the Python path.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def moving_frame_run_python(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_k_base: float,
    spatial_decay_form: object,
    spatial_decay_exponent: float,
    spatial_decay_length_scale: float,
    spatial_epsilon: float,
    doppler_strength: float,
    doppler_epsilon: float,
    zeta: float,
    psi: float,
    dt: float,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1.0e-6,
    rtol: float = 1.0e-3,
) -> FloatArray:
    """Run the moving-frame UPDE schedule in the Python reference path.

    Parameters
    ----------
    phases : object
        Oscillator phases in radians, shape ``(N,)``.
    positions : object
        Absolute axial coordinates per oscillator, shape ``(N,)``.
    omega_schedule : object
        Per-step natural-frequency vectors, shape ``(n_steps, N)``.
    knm : object
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    alpha : object
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.
    velocity_schedule : object
        Per-step axial velocity vectors, shape ``(n_steps, N)``.
    spatial_k_base : float
        Base coupling strength before spatial modulation.
    spatial_decay_form : object
        Spatial decay law name (e.g. ``exponential`` or ``power``).
    spatial_decay_exponent : float
        Exponent of the spatial decay law.
    spatial_decay_length_scale : float
        Characteristic length scale of the spatial decay.
    spatial_epsilon : float
        Numerical floor guarding the spatial-decay denominator.
    doppler_strength : float
        Doppler coupling-correction strength.
    doppler_epsilon : float
        Numerical floor guarding the Doppler denominator.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    dt : float
        Integration step size.
    method : str
        Integration method (``euler``, ``rk4``, or ``rk45``).
    n_substeps : int
        Number of inner substeps per outer step.
    atol : float
        Absolute tolerance for the adaptive (rk45) integrator.
    rtol : float
        Relative tolerance for the adaptive (rk45) integrator.

    Returns
    -------
    FloatArray
        The final phases after running the moving-frame schedule on the Python path.
    """
    (
        p,
        z,
        omega,
        k,
        a,
        velocities,
        k_base,
        decay_code,
        decay_exponent,
        decay_length_scale,
        spatial_eps,
        strength,
        doppler_eps,
        zeta_f,
        psi_f,
        dt_f,
        n_steps,
        method_s,
        n_substeps_i,
        atol_f,
        rtol_f,
    ) = validate_moving_frame_backend_inputs(
        phases,
        positions,
        omega_schedule,
        knm,
        alpha,
        velocity_schedule,
        spatial_k_base,
        spatial_decay_form,
        spatial_decay_exponent,
        spatial_decay_length_scale,
        spatial_epsilon,
        doppler_strength,
        doppler_epsilon,
        zeta,
        psi,
        dt,
        method,
        n_substeps,
        atol,
        rtol,
    )
    p_work = p.copy()
    z_work = z.copy()
    for step in range(n_steps):
        k_effective = _axial_spatial_modulate(
            k,
            z_work,
            k_base=k_base,
            decay_code=decay_code,
            decay_exponent=decay_exponent,
            decay_length_scale=decay_length_scale,
            epsilon=spatial_eps,
        )
        correction = doppler_term(
            velocities[step],
            k_effective,
            doppler_strength=strength,
            doppler_epsilon=doppler_eps,
        )
        p_work = upde_run_omega_schedule_python(
            p_work,
            (omega[step] + correction).reshape(1, -1),
            k_effective,
            a,
            zeta_f,
            psi_f,
            dt_f,
            method_s,
            n_substeps_i,
            atol_f,
            rtol_f,
        )
        z_work = np.ascontiguousarray(
            z_work + velocities[step] * dt_f, dtype=np.float64
        )
    return np.ascontiguousarray(np.concatenate([p_work, z_work]), dtype=np.float64)

API documentation

moving_frame

Moving-frame Kuramoto UPDE integration.

MovingFrameUPDEEngine carries one absolute axial coordinate per oscillator alongside phase. Each outer step evaluates distance-dependent coupling from the current positions, applies graph-weighted Doppler detuning from the current velocities, advances phases, then advances positions ballistically over the same chamber-clock step.

Classes

MovingFrameState dataclass

MovingFrameState(
    phases: FloatArray,
    positions: FloatArray,
    velocities: FloatArray,
    knm_effective: FloatArray,
    doppler_term: FloatArray,
    time: float,
    kinematic_residual_max_m: float = 0.0,
    max_abs_velocity_m_per_s: float = 0.0,
    path_length_max_m: float = 0.0,
)

Snapshot of a moving-frame UPDE step or run.

MovingFrameUPDEEngine

MovingFrameUPDEEngine(
    n: int,
    omega: object,
    k_nm: object,
    alpha: object = 0.0,
    dt: float = 0.01,
    positions_t0: object | None = None,
    velocities: object
    | Callable[[float], object]
    | None = None,
    spatial_modulator: SpatialCouplingModulator
    | None = None,
    reference_point: float = 0.0,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1e-09,
    solver: str = "rk45",
    phases: object | None = None,
    velocity_axis: object | None = None,
    t0: float = 0.0,
)

Bases: DopplerEngine

UPDE engine with chamber-frame axial positions and collision checks.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def __init__(
    self,
    n: int,
    omega: object,
    k_nm: object,
    alpha: object = 0.0,
    dt: float = 0.01,
    positions_t0: object | None = None,
    velocities: object | Callable[[float], object] | None = None,
    spatial_modulator: SpatialCouplingModulator | None = None,
    reference_point: float = 0.0,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1.0e-9,
    solver: str = "rk45",
    phases: object | None = None,
    velocity_axis: object | None = None,
    t0: float = 0.0,
) -> None:
    if positions_t0 is None:
        raise ValueError("positions_t0 is required for MovingFrameUPDEEngine")
    if spatial_modulator is None:
        raise ValueError("spatial_modulator is required for MovingFrameUPDEEngine")
    self.spatial_modulator = _validate_spatial_modulator(spatial_modulator)
    self.reference_point = _finite_float(reference_point, name="reference_point")
    super().__init__(
        n,
        omega=omega,
        k_nm=k_nm,
        alpha=alpha,
        dt=dt,
        velocities=velocities,
        doppler_strength=doppler_strength,
        doppler_epsilon=doppler_epsilon,
        solver=solver,
        phases=phases,
        velocity_axis=velocity_axis,
        t0=t0,
    )
    self._positions = _validate_positions_vector(positions_t0, n=n)
    self._knm_effective = self._modulated_knm(self.k_nm, self._positions)
    self._doppler_term = doppler_term(
        self.velocity_current,
        self._knm_effective,
        doppler_strength=self.doppler_strength,
        doppler_epsilon=self.doppler_epsilon,
    )
    self._kinematic_residual_max_m = 0.0
    self._max_abs_velocity_m_per_s = float(np.max(np.abs(self.velocity_current)))
    self._path_length_max_m = 0.0
Attributes
positions property
positions: FloatArray

Current absolute axial coordinate for each oscillator.

Returns

FloatArray Current absolute axial coordinate for each oscillator.

distance_to_reference property
distance_to_reference: FloatArray

Absolute distance from each oscillator to the chamber reference.

Returns

FloatArray Absolute distance from each oscillator to the chamber reference.

knm_effective property
knm_effective: FloatArray

Most recently applied distance-modulated coupling matrix.

Returns

FloatArray Most recently applied distance-modulated coupling matrix.

kinematic_residual_max_m property
kinematic_residual_max_m: float

Maximum residual against z_next = z + v*dt in the last run.

Returns

float Maximum residual against z_next = z + v*dt in the last run.

max_abs_velocity_m_per_s property
max_abs_velocity_m_per_s: float

Maximum absolute axial velocity used by the last step or run.

Returns

float Maximum absolute axial velocity used by the last step or run.

path_length_max_m property
path_length_max_m: float

Maximum per-oscillator axial path length in the last step or run.

Returns

float Maximum per-oscillator axial path length in the last step or run.

state property
state: MovingFrameState

Return the current moving-frame diagnostic snapshot.

Returns

MovingFrameState Return the current moving-frame diagnostic snapshot.

Methods:
collision_imminent
collision_imminent(threshold_m: float = 0.001) -> bool

Return whether any oscillator is at or crosses the reference soon.

Parameters

threshold_m : float Distance threshold in metres.

Returns

bool True when an oscillator is at or crossing the reference within one step.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def collision_imminent(self, threshold_m: float = 1.0e-3) -> bool:
    """Return whether any oscillator is at or crosses the reference soon.

    Parameters
    ----------
    threshold_m : float
        Distance threshold in metres.

    Returns
    -------
    bool
        ``True`` when an oscillator is at or crossing the reference within one step.
    """
    threshold = _validate_nonnegative_float(threshold_m, name="threshold_m")
    signed_now = self._positions - self.reference_point
    signed_next = signed_now + self.velocity_current * self._dt
    current_near = np.abs(signed_now) <= threshold
    next_near = np.abs(signed_next) <= threshold
    crosses = signed_now * signed_next <= 0.0
    return bool(np.any(current_near | next_near | crosses))
step
step(
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
) -> FloatArray

Advance one coupled phase/position step.

Parameters

phases : object | None Oscillator phases in radians, shape (N,). omegas : object | None Natural frequencies in rad/s, shape (N,). knm : object | None Coupling matrix K_nm, shape (N, N). zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. alpha : object | None Phase-lag matrix in radians, shape (N, N), or None for no lag.

Returns

FloatArray The phases after one coupled phase/position step.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def step(
    self,
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
) -> FloatArray:
    """Advance one coupled phase/position step.

    Parameters
    ----------
    phases : object | None
        Oscillator phases in radians, shape ``(N,)``.
    omegas : object | None
        Natural frequencies in rad/s, shape ``(N,)``.
    knm : object | None
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    alpha : object | None
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.

    Returns
    -------
    FloatArray
        The phases after one coupled phase/position step.
    """
    k_base = self.k_nm if knm is None else _validate_knm(knm, n=self._n)
    k_effective = self._modulated_knm(k_base, self._positions)
    positions_start = self._positions.copy()
    out = super().step(phases, omegas, k_effective, zeta, psi, alpha)
    self._knm_effective = k_effective
    self._positions = np.ascontiguousarray(
        positions_start + self.velocity_current * self._dt,
        dtype=np.float64,
    )
    expected_positions = positions_start + self.velocity_current * self._dt
    self._kinematic_residual_max_m = _kinematic_residual_max(
        self._positions,
        expected_positions,
    )
    self._max_abs_velocity_m_per_s = float(np.max(np.abs(self.velocity_current)))
    self._path_length_max_m = float(
        np.max(np.abs(self.velocity_current * self._dt))
    )
    return out
run
run(
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
    n_steps: int = 1,
) -> FloatArray

Run n_steps of joint phase and axial-position dynamics.

Parameters

phases : object | None Oscillator phases in radians, shape (N,). omegas : object | None Natural frequencies in rad/s, shape (N,). knm : object | None Coupling matrix K_nm, shape (N, N). zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. alpha : object | 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 joint phase/position steps.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def run(
    self,
    phases: object | None = None,
    omegas: object | None = None,
    knm: object | None = None,
    zeta: float = 0.0,
    psi: float = 0.0,
    alpha: object | None = None,
    n_steps: int = 1,
) -> FloatArray:
    """Run ``n_steps`` of joint phase and axial-position dynamics.

    Parameters
    ----------
    phases : object | None
        Oscillator phases in radians, shape ``(N,)``.
    omegas : object | None
        Natural frequencies in rad/s, shape ``(N,)``.
    knm : object | None
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    alpha : object | 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`` joint phase/position steps.
    """
    steps = _validate_positive_step_count(n_steps, name="n_steps")
    p = self.phases if phases is None else _validate_phases(phases, n=self._n)
    k = self.k_nm if knm is None else _validate_knm(knm, n=self._n)
    a = self.alpha_matrix if alpha is None else _validate_alpha(alpha, n=self._n)
    omega_source = self._omega_source if omegas is None else omegas
    omega_schedule = self._omega_schedule(omega_source, steps)
    velocity_schedule = self._velocity_schedule(steps)
    z0 = self._positions.copy()
    flat = moving_frame_run(
        p,
        z0,
        omega_schedule,
        k,
        a,
        velocity_schedule,
        self.spatial_modulator,
        self.doppler_strength,
        self.doppler_epsilon,
        zeta,
        psi,
        self._dt,
        self._method,
        1,
        self._atol,
        self._rtol,
    )
    n = self._n
    self.phases = np.ascontiguousarray(flat[:n], dtype=np.float64)
    self._positions = np.ascontiguousarray(flat[n:], dtype=np.float64)
    expected_positions = _expected_positions_from_schedule(
        z0,
        velocity_schedule,
        self._dt,
    )
    self._kinematic_residual_max_m = _kinematic_residual_max(
        self._positions,
        expected_positions,
    )
    self._max_abs_velocity_m_per_s = float(np.max(np.abs(velocity_schedule)))
    self._path_length_max_m = float(
        np.max(np.sum(np.abs(velocity_schedule * self._dt), axis=0))
    )
    if steps > 1:
        last_start_positions = z0 + self._dt * np.sum(
            velocity_schedule[:-1], axis=0
        )
    else:
        last_start_positions = z0
    self._knm_effective = self._modulated_knm(k, last_start_positions)
    self._omega_current = omega_schedule[-1].copy()
    self.velocity_current = velocity_schedule[-1].copy()
    self._doppler_term = doppler_term(
        self.velocity_current,
        self._knm_effective,
        doppler_strength=self.doppler_strength,
        doppler_epsilon=self.doppler_epsilon,
    )
    self._time += steps * self._dt
    return self.phases.copy()

Functions:

validate_moving_frame_backend_inputs

validate_moving_frame_backend_inputs(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_k_base: float,
    spatial_decay_form: object,
    spatial_decay_exponent: float,
    spatial_decay_length_scale: float,
    spatial_epsilon: float,
    doppler_strength: float,
    doppler_epsilon: float,
    zeta: float,
    psi: float,
    dt: float,
    method: str,
    n_substeps: int,
    atol: float,
    rtol: float,
) -> tuple[
    FloatArray,
    FloatArray,
    FloatArray,
    FloatArray,
    FloatArray,
    FloatArray,
    float,
    int,
    float,
    float,
    float,
    float,
    float,
    float,
    float,
    float,
    int,
    str,
    int,
    float,
    float,
]

Validate the backend-neutral moving-frame schedule contract.

Parameters

phases : object Oscillator phases in radians, shape (N,). positions : object Absolute axial coordinates per oscillator, shape (N,). omega_schedule : object Per-step natural-frequency vectors, shape (n_steps, N). knm : object Coupling matrix K_nm, shape (N, N). alpha : object Phase-lag matrix in radians, shape (N, N), or None for no lag. velocity_schedule : object Per-step axial velocity vectors, shape (n_steps, N). spatial_k_base : float Base coupling strength before spatial modulation. spatial_decay_form : object Spatial decay law name (e.g. exponential or power). spatial_decay_exponent : float Exponent of the spatial decay law. spatial_decay_length_scale : float Characteristic length scale of the spatial decay. spatial_epsilon : float Numerical floor guarding the spatial-decay denominator. doppler_strength : float Doppler coupling-correction strength. doppler_epsilon : float Numerical floor guarding the Doppler denominator. zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. dt : float Integration step size. method : str Integration method (euler, rk4, or rk45). n_substeps : int Number of inner substeps per outer step. atol : float Absolute tolerance for the adaptive (rk45) integrator. rtol : float Relative tolerance for the adaptive (rk45) integrator.

Returns

tuple[FloatArray, FloatArray, FloatArray, FloatArray, FloatArray, FloatArray, float, int, float, float, float, float, float, float, float, float, int, str, int, float, float] The validated, canonicalised moving-frame schedule contract tuple.

Raises

ValueError If any schedule array is non-finite or has an inconsistent shape.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def validate_moving_frame_backend_inputs(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_k_base: float,
    spatial_decay_form: object,
    spatial_decay_exponent: float,
    spatial_decay_length_scale: float,
    spatial_epsilon: float,
    doppler_strength: float,
    doppler_epsilon: float,
    zeta: float,
    psi: float,
    dt: float,
    method: str,
    n_substeps: int,
    atol: float,
    rtol: float,
) -> tuple[
    FloatArray,
    FloatArray,
    FloatArray,
    FloatArray,
    FloatArray,
    FloatArray,
    float,
    int,
    float,
    float,
    float,
    float,
    float,
    float,
    float,
    float,
    int,
    str,
    int,
    float,
    float,
]:
    """Validate the backend-neutral moving-frame schedule contract.

    Parameters
    ----------
    phases : object
        Oscillator phases in radians, shape ``(N,)``.
    positions : object
        Absolute axial coordinates per oscillator, shape ``(N,)``.
    omega_schedule : object
        Per-step natural-frequency vectors, shape ``(n_steps, N)``.
    knm : object
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    alpha : object
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.
    velocity_schedule : object
        Per-step axial velocity vectors, shape ``(n_steps, N)``.
    spatial_k_base : float
        Base coupling strength before spatial modulation.
    spatial_decay_form : object
        Spatial decay law name (e.g. ``exponential`` or ``power``).
    spatial_decay_exponent : float
        Exponent of the spatial decay law.
    spatial_decay_length_scale : float
        Characteristic length scale of the spatial decay.
    spatial_epsilon : float
        Numerical floor guarding the spatial-decay denominator.
    doppler_strength : float
        Doppler coupling-correction strength.
    doppler_epsilon : float
        Numerical floor guarding the Doppler denominator.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    dt : float
        Integration step size.
    method : str
        Integration method (``euler``, ``rk4``, or ``rk45``).
    n_substeps : int
        Number of inner substeps per outer step.
    atol : float
        Absolute tolerance for the adaptive (rk45) integrator.
    rtol : float
        Relative tolerance for the adaptive (rk45) integrator.

    Returns
    -------
    tuple[FloatArray, FloatArray, FloatArray, FloatArray, FloatArray, FloatArray, float,
    int, float, float, float, float, float, float, float, float, int, str, int, float,
    float]
        The validated, canonicalised moving-frame schedule contract tuple.

    Raises
    ------
    ValueError
        If any schedule array is non-finite or has an inconsistent shape.
    """
    p_raw = _reject_non_real_array(phases, name="phases")
    if p_raw.ndim != 1 or p_raw.size < 1:
        raise ValueError("phases must be a non-empty vector")
    n = int(p_raw.size)
    p = _validate_phases(p_raw, n=n)
    z = _validate_positions_vector(positions, n=n)
    omega = _validate_schedule(omega_schedule, n=n, name="omega_schedule")
    velocities = _validate_schedule(velocity_schedule, n=n, name="velocity_schedule")
    if velocities.shape[0] != omega.shape[0]:
        raise ValueError("velocity_schedule step count must match omega_schedule")
    k = _validate_knm(knm, n=n)
    a = _validate_alpha(alpha, n=n)
    k_base = _validate_nonnegative_float(spatial_k_base, name="spatial_k_base")
    decay_code = _validate_decay_code(spatial_decay_form)
    decay_exponent = _validate_positive_float(
        spatial_decay_exponent, name="spatial_decay_exponent"
    )
    decay_length_scale = _validate_positive_float(
        spatial_decay_length_scale, name="spatial_decay_length_scale"
    )
    spatial_eps = _validate_positive_float(spatial_epsilon, name="spatial_epsilon")
    strength = _finite_float(doppler_strength, name="doppler_strength")
    doppler_eps = _validate_positive_float(doppler_epsilon, name="doppler_epsilon")
    zeta_f = _finite_float(zeta, name="zeta")
    psi_f = _finite_float(psi, name="psi")
    dt_f = _validate_positive_float(dt, name="dt")
    method_s = _validate_method(method)
    n_substeps_i = _validate_positive_step_count(n_substeps, name="n_substeps")
    atol_f = _validate_positive_float(atol, name="atol")
    rtol_f = _validate_positive_float(rtol, name="rtol")
    return (
        p,
        z,
        omega,
        k,
        a,
        velocities,
        k_base,
        decay_code,
        decay_exponent,
        decay_length_scale,
        spatial_eps,
        strength,
        doppler_eps,
        zeta_f,
        psi_f,
        dt_f,
        int(omega.shape[0]),
        method_s,
        n_substeps_i,
        atol_f,
        rtol_f,
    )

validate_moving_frame_backend_output

validate_moving_frame_backend_output(
    value: object,
    *,
    n: int,
    expected_positions: FloatArray | None = None,
) -> FloatArray

Validate moving-frame backend output before returning it to callers.

Parameters

value : object Backend-produced concatenated phase and position vector, shape (2*N,). n : int Expected oscillator count. expected_positions : FloatArray | None Optional ballistic position reference derived from the submitted schedule.

Returns

FloatArray Contiguous float64 vector containing final phases followed by positions.

Raises

ValueError If the backend output is non-finite, has the wrong shape, leaves the principal phase branch, or violates the ballistic position contract.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def validate_moving_frame_backend_output(
    value: object,
    *,
    n: int,
    expected_positions: FloatArray | None = None,
) -> FloatArray:
    """Validate moving-frame backend output before returning it to callers.

    Parameters
    ----------
    value : object
        Backend-produced concatenated phase and position vector, shape ``(2*N,)``.
    n : int
        Expected oscillator count.
    expected_positions : FloatArray | None
        Optional ballistic position reference derived from the submitted schedule.

    Returns
    -------
    FloatArray
        Contiguous ``float64`` vector containing final phases followed by positions.

    Raises
    ------
    ValueError
        If the backend output is non-finite, has the wrong shape, leaves the
        principal phase branch, or violates the ballistic position contract.
    """
    out = _reject_non_real_array(value, name="moving_frame_backend_output")
    if out.shape != (2 * n,):
        raise ValueError("moving-frame backend output shape must be (2*n,)")
    phases = out[:n]
    positions = out[n:]
    if np.any(phases < 0.0) or np.any(phases >= _TWO_PI):
        raise ValueError("moving-frame backend phases must be in [0, 2*pi)")
    if not np.all(np.isfinite(positions)):
        raise ValueError("moving-frame backend positions must be finite")
    if expected_positions is not None:
        expected = np.ascontiguousarray(expected_positions, dtype=np.float64)
        if expected.shape != positions.shape or not np.all(np.isfinite(expected)):
            raise ValueError("expected_positions must match finite backend positions")
        residual = _kinematic_residual_max(positions, expected)
        if residual > KINEMATIC_RESIDUAL_TOLERANCE_M:
            raise ValueError(
                "moving-frame backend positions violate ballistic kinematics: "
                f"max residual {residual} m exceeds "
                f"{KINEMATIC_RESIDUAL_TOLERANCE_M} m"
            )
    return np.ascontiguousarray(out, dtype=np.float64)

moving_frame_run_python

moving_frame_run_python(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_k_base: float,
    spatial_decay_form: object,
    spatial_decay_exponent: float,
    spatial_decay_length_scale: float,
    spatial_epsilon: float,
    doppler_strength: float,
    doppler_epsilon: float,
    zeta: float,
    psi: float,
    dt: float,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1e-06,
    rtol: float = 0.001,
) -> FloatArray

Run the moving-frame UPDE schedule in the Python reference path.

Parameters

phases : object Oscillator phases in radians, shape (N,). positions : object Absolute axial coordinates per oscillator, shape (N,). omega_schedule : object Per-step natural-frequency vectors, shape (n_steps, N). knm : object Coupling matrix K_nm, shape (N, N). alpha : object Phase-lag matrix in radians, shape (N, N), or None for no lag. velocity_schedule : object Per-step axial velocity vectors, shape (n_steps, N). spatial_k_base : float Base coupling strength before spatial modulation. spatial_decay_form : object Spatial decay law name (e.g. exponential or power). spatial_decay_exponent : float Exponent of the spatial decay law. spatial_decay_length_scale : float Characteristic length scale of the spatial decay. spatial_epsilon : float Numerical floor guarding the spatial-decay denominator. doppler_strength : float Doppler coupling-correction strength. doppler_epsilon : float Numerical floor guarding the Doppler denominator. zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. dt : float Integration step size. method : str Integration method (euler, rk4, or rk45). n_substeps : int Number of inner substeps per outer step. atol : float Absolute tolerance for the adaptive (rk45) integrator. rtol : float Relative tolerance for the adaptive (rk45) integrator.

Returns

FloatArray The final phases after running the moving-frame schedule on the Python path.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def moving_frame_run_python(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_k_base: float,
    spatial_decay_form: object,
    spatial_decay_exponent: float,
    spatial_decay_length_scale: float,
    spatial_epsilon: float,
    doppler_strength: float,
    doppler_epsilon: float,
    zeta: float,
    psi: float,
    dt: float,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1.0e-6,
    rtol: float = 1.0e-3,
) -> FloatArray:
    """Run the moving-frame UPDE schedule in the Python reference path.

    Parameters
    ----------
    phases : object
        Oscillator phases in radians, shape ``(N,)``.
    positions : object
        Absolute axial coordinates per oscillator, shape ``(N,)``.
    omega_schedule : object
        Per-step natural-frequency vectors, shape ``(n_steps, N)``.
    knm : object
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    alpha : object
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.
    velocity_schedule : object
        Per-step axial velocity vectors, shape ``(n_steps, N)``.
    spatial_k_base : float
        Base coupling strength before spatial modulation.
    spatial_decay_form : object
        Spatial decay law name (e.g. ``exponential`` or ``power``).
    spatial_decay_exponent : float
        Exponent of the spatial decay law.
    spatial_decay_length_scale : float
        Characteristic length scale of the spatial decay.
    spatial_epsilon : float
        Numerical floor guarding the spatial-decay denominator.
    doppler_strength : float
        Doppler coupling-correction strength.
    doppler_epsilon : float
        Numerical floor guarding the Doppler denominator.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    dt : float
        Integration step size.
    method : str
        Integration method (``euler``, ``rk4``, or ``rk45``).
    n_substeps : int
        Number of inner substeps per outer step.
    atol : float
        Absolute tolerance for the adaptive (rk45) integrator.
    rtol : float
        Relative tolerance for the adaptive (rk45) integrator.

    Returns
    -------
    FloatArray
        The final phases after running the moving-frame schedule on the Python path.
    """
    (
        p,
        z,
        omega,
        k,
        a,
        velocities,
        k_base,
        decay_code,
        decay_exponent,
        decay_length_scale,
        spatial_eps,
        strength,
        doppler_eps,
        zeta_f,
        psi_f,
        dt_f,
        n_steps,
        method_s,
        n_substeps_i,
        atol_f,
        rtol_f,
    ) = validate_moving_frame_backend_inputs(
        phases,
        positions,
        omega_schedule,
        knm,
        alpha,
        velocity_schedule,
        spatial_k_base,
        spatial_decay_form,
        spatial_decay_exponent,
        spatial_decay_length_scale,
        spatial_epsilon,
        doppler_strength,
        doppler_epsilon,
        zeta,
        psi,
        dt,
        method,
        n_substeps,
        atol,
        rtol,
    )
    p_work = p.copy()
    z_work = z.copy()
    for step in range(n_steps):
        k_effective = _axial_spatial_modulate(
            k,
            z_work,
            k_base=k_base,
            decay_code=decay_code,
            decay_exponent=decay_exponent,
            decay_length_scale=decay_length_scale,
            epsilon=spatial_eps,
        )
        correction = doppler_term(
            velocities[step],
            k_effective,
            doppler_strength=strength,
            doppler_epsilon=doppler_eps,
        )
        p_work = upde_run_omega_schedule_python(
            p_work,
            (omega[step] + correction).reshape(1, -1),
            k_effective,
            a,
            zeta_f,
            psi_f,
            dt_f,
            method_s,
            n_substeps_i,
            atol_f,
            rtol_f,
        )
        z_work = np.ascontiguousarray(
            z_work + velocities[step] * dt_f, dtype=np.float64
        )
    return np.ascontiguousarray(np.concatenate([p_work, z_work]), dtype=np.float64)

moving_frame_run

moving_frame_run(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_modulator: SpatialCouplingModulator,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1e-09,
    zeta: float = 0.0,
    psi: float = 0.0,
    dt: float = 0.01,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1e-06,
    rtol: float = 0.001,
    *,
    backend: str = "auto",
) -> FloatArray

Run a moving-frame UPDE schedule through the selected backend.

Parameters

phases : object Oscillator phases in radians, shape (N,). positions : object Absolute axial coordinates per oscillator, shape (N,). omega_schedule : object Per-step natural-frequency vectors, shape (n_steps, N). knm : object Coupling matrix K_nm, shape (N, N). alpha : object Phase-lag matrix in radians, shape (N, N), or None for no lag. velocity_schedule : object Per-step axial velocity vectors, shape (n_steps, N). spatial_modulator : SpatialCouplingModulator Configured spatial coupling modulator. doppler_strength : float Doppler coupling-correction strength. doppler_epsilon : float Numerical floor guarding the Doppler denominator. zeta : float External drive strength ζ. psi : float External drive reference phase Ψ in radians. dt : float Integration step size. method : str Integration method (euler, rk4, or rk45). n_substeps : int Number of inner substeps per outer step. atol : float Absolute tolerance for the adaptive (rk45) integrator. rtol : float Relative tolerance for the adaptive (rk45) integrator. backend : str Name of the compute backend to run.

Returns

FloatArray The final phases after running the moving-frame schedule on the selected backend.

Raises

ImportError If the selected backend's runtime is unavailable. ValueError If the schedule contract is invalid; the underlying backend error propagates when every backend fails.

Source code in src/scpn_phase_orchestrator/upde/moving_frame.py
def moving_frame_run(
    phases: object,
    positions: object,
    omega_schedule: object,
    knm: object,
    alpha: object,
    velocity_schedule: object,
    spatial_modulator: SpatialCouplingModulator,
    doppler_strength: float = 1.0,
    doppler_epsilon: float = 1.0e-9,
    zeta: float = 0.0,
    psi: float = 0.0,
    dt: float = 0.01,
    method: str = "rk45",
    n_substeps: int = 1,
    atol: float = 1.0e-6,
    rtol: float = 1.0e-3,
    *,
    backend: str = "auto",
) -> FloatArray:
    """Run a moving-frame UPDE schedule through the selected backend.

    Parameters
    ----------
    phases : object
        Oscillator phases in radians, shape ``(N,)``.
    positions : object
        Absolute axial coordinates per oscillator, shape ``(N,)``.
    omega_schedule : object
        Per-step natural-frequency vectors, shape ``(n_steps, N)``.
    knm : object
        Coupling matrix ``K_nm``, shape ``(N, N)``.
    alpha : object
        Phase-lag matrix in radians, shape ``(N, N)``, or ``None`` for no lag.
    velocity_schedule : object
        Per-step axial velocity vectors, shape ``(n_steps, N)``.
    spatial_modulator : SpatialCouplingModulator
        Configured spatial coupling modulator.
    doppler_strength : float
        Doppler coupling-correction strength.
    doppler_epsilon : float
        Numerical floor guarding the Doppler denominator.
    zeta : float
        External drive strength ``ζ``.
    psi : float
        External drive reference phase ``Ψ`` in radians.
    dt : float
        Integration step size.
    method : str
        Integration method (``euler``, ``rk4``, or ``rk45``).
    n_substeps : int
        Number of inner substeps per outer step.
    atol : float
        Absolute tolerance for the adaptive (rk45) integrator.
    rtol : float
        Relative tolerance for the adaptive (rk45) integrator.
    backend : str
        Name of the compute backend to run.

    Returns
    -------
    FloatArray
        The final phases after running the moving-frame schedule on the selected
        backend.

    Raises
    ------
    ImportError
        If the selected backend's runtime is unavailable.
    ValueError
        If the schedule contract is invalid; the underlying backend error propagates
        when every backend fails.
    """
    modulator = _validate_spatial_modulator(spatial_modulator)
    if modulator.distance_fn is not None:
        raise ValueError(
            "moving_frame_run requires the default axial SpatialCouplingModulator "
            "distance kernel"
        )
    validated = validate_moving_frame_backend_inputs(
        phases,
        positions,
        omega_schedule,
        knm,
        alpha,
        velocity_schedule,
        modulator.K_base,
        modulator.decay_form,
        modulator.decay_exponent,
        modulator.decay_length_scale,
        modulator.epsilon,
        doppler_strength,
        doppler_epsilon,
        zeta,
        psi,
        dt,
        method,
        n_substeps,
        atol,
        rtol,
    )
    (
        p,
        z,
        omega,
        k,
        a,
        velocities,
        k_base,
        decay_code,
        decay_exponent,
        decay_length_scale,
        spatial_eps,
        strength,
        doppler_eps,
        zeta_f,
        psi_f,
        dt_f,
        _n_steps,
        method_s,
        n_substeps_i,
        atol_f,
        rtol_f,
    ) = validated
    expected_positions = _expected_positions_from_schedule(z, velocities, dt_f)
    backends = _backend_map()
    if backend != "auto" and backend not in backends:
        raise ImportError(f"moving-frame backend {backend!r} is not available")
    order = _BACKEND_ORDER if backend == "auto" else (backend,)
    last_error: Exception | None = None
    for name in order:
        fn = backends.get(name)
        if fn is None:
            continue
        try:
            out = fn(
                p,
                z,
                omega,
                k,
                a,
                velocities,
                k_base,
                decay_code,
                decay_exponent,
                decay_length_scale,
                spatial_eps,
                strength,
                doppler_eps,
                zeta_f,
                psi_f,
                dt_f,
                method_s,
                n_substeps_i,
                atol_f,
                rtol_f,
            )
            return validate_moving_frame_backend_output(
                out,
                n=int(p.size),
                expected_positions=expected_positions,
            )
        except (AttributeError, ImportError) as exc:
            last_error = exc
            continue
    if backend != "auto" and last_error is not None:
        raise last_error
    return validate_moving_frame_backend_output(
        moving_frame_run_python(
            p,
            z,
            omega,
            k,
            a,
            velocities,
            k_base,
            decay_code,
            decay_exponent,
            decay_length_scale,
            spatial_eps,
            strength,
            doppler_eps,
            zeta_f,
            psi_f,
            dt_f,
            method_s,
            n_substeps_i,
            atol_f,
            rtol_f,
        ),
        n=int(p.size),
        expected_positions=expected_positions,
    )