Moving-Frame UPDE¶
MIF-002 implements the chamber-fixed moving-frame UPDE layer. It reuses
the MIF-001 pair-normalised Doppler-Kuramoto phase derivative and advances
the combined [theta, z] state with a fixed-step Dormand-Prince RK45 update
so absolute axial positions can be evaluated against the chamber reference
point z = 0.
Carrier¶
For each oscillator:
The moving-frame surface adds:
omega_rate_rad_s2: optional MIF-001 affine natural-frequency rate vector, propagated through each Dormand-Prince stage time;reference_point_m: chamber-fixed axial reference, usually0;time_to_reference_s(): per-channel non-negative crossing estimate;collision_imminent(eps_m): simultaneous reference-window predicate;reference_error_m: max absolute distance from the reference;separation_m: max-min axial spread across moving channels;local_error_estimate: RK45 fifth/fourth order embedded difference, using circular deltas for phase components and linear deltas for axial position components.
Python API¶
moving_frame_upde
¶
Moving-frame UPDE carrier with chamber-fixed absolute positions.
MIF-002 owns the chamber-frame trajectory layer that MIF-001 deliberately
keeps minimal. The phase derivative is delegated to the MIF-001
Doppler-Kuramoto carrier while this module advances the combined
[theta, z] state with a fixed-step Dormand-Prince RK45 update and
computes reference-window observables.
The embedded RK error is evaluated with circular phase deltas for theta
components and linear deltas for chamber-frame z components.
MovingFrameUPDESpec(omega_rad_s, coupling_rad_s, phase_lag_rad=0.0, doppler_strength_rad_s=0.0, velocity_epsilon_m_s=1e-09, distance_scale_m=1.0, reference_point_m=0.0, omega_rate_rad_s2=None)
dataclass
¶
MovingFrameUPDEState(t_s, phases_rad, positions_m, velocities_m_s, reference_point_m, separation_m, reference_error_m, order_parameter, phase_lock_error_rad, local_error_estimate)
dataclass
¶
Single moving-frame UPDE state snapshot.
MovingFrameUPDEReport(time_s, phases_rad, positions_m, separation_m, reference_error_m, order_parameter, phase_lock_error_rad, local_error_estimate)
dataclass
¶
Batch simulation trace for a moving-frame UPDE run.
MovingFrameUPDE(spec, phases_rad, positions_m, velocities_m_s)
¶
Stateful fixed-step Dormand-Prince RK45 integrator for MIF-002.
state()
¶
Return a read-only snapshot of the current state.
derivatives(phases_rad=None, positions_m=None, t_s=None)
¶
Return the combined [dtheta/dt, dz/dt] derivative vector.
step(dt_s)
¶
Advance the combined phase/position state by dt_s seconds.
time_to_reference_s()
¶
Return non-negative time-to-reference estimates for each oscillator.
collision_imminent(eps_m=0.002)
¶
Return whether all channels are inside eps_m of the reference point.
copy()
¶
Return an independent copy of the current integrator state.
moving_frame_derivatives(spec, phases_rad, positions_m, velocities_m_s, t_s=0.0)
¶
Return the combined [dtheta/dt, dz/dt] derivative vector.
evaluate_moving_frame_upde(spec, phases_rad, positions_m, velocities_m_s, dt_s, steps)
¶
Run steps RK45 updates and return the full moving-frame trace.
Dispatch¶
Use scpn_mif_core.kinematic.dispatched_moving_frame_upde(...) for the
fastest available measured backend:
The pure Python MovingFrameUPDE class remains available for deterministic
debugging and tests.
Acceptance¶
The committed acceptance path uses the two-body chamber-centre scenario:
- initial positions
[-0.03, 0.03] m; - velocities
[+300000, -300000] m s^-1; - reference point
0 m; - step size
1 ns.
After 100 steps both channels are inside the ±2 mm reference window and
the max-min separation is within 4 mm. The first-step RK45 result is also
checked against an independent Dormand-Prince tableau implementation in the
Python tests, including the branch-cut case where a phase crosses +pi and
the local error must remain a circular embedded delta instead of a spurious
2*pi jump.
Benchmarks¶
Measured on the local i5-11600K rig using Python 3.12.3, Rust 1.85.0,
and Julia 1.12.6. This was a non-isolated workstation comparison with the
CPU governor set to powersave and host load present; the numbers are for
dispatch ordering, not production performance claims. The Julia entries are
measured through the package CLI harness, so the timings include Julia
process startup.
| Group | Backend | Mean | Result |
|---|---|---|---|
derivatives_2 |
Rust | 591 ns | fastest |
derivatives_2 |
Python | 19.56 us | 33.1x slower than Rust |
trace_120 |
Rust | 143.41 us | fastest |
trace_120 |
Python | 30.78 ms | 214.6x slower than Rust |
trace_120 |
Julia CLI | 2.74 s | CLI startup comparison |
affine_trace_1000 |
Rust | 865.91 us | fastest |
affine_trace_1000 |
Python | 199.23 ms | 230.1x slower than Rust |
affine_trace_1000 |
Julia CLI | 3.01 s | CLI startup comparison |
Raw summary: bench/results/moving_frame_upde.json.
Ownership¶
SYNC-STATE: upstream-pending applies to all MIF-002 implementation
surfaces. SCPN-MIF-CORE owns the FRC-specific local carrier until
SCPN-PHASE-ORCHESTRATOR receives the reusable scpn.upde.moving_frame
surface targeted for the 0.7.0 lane.