CLRI-2D horizon h=5

PARITYS1 · dim 12

No clear winner. The survival gap is under 10 percentage points and the balanced-score gap is under 0.05, so neither SolvSRK nor the best baseline clears the win threshold. Either works — choose on cost, licensing, or integration effort. All verdicts →

2D closed-loop EKF+PN interceptor engagement (dim=12) with extended integration horizon h=5. Tests solver survival as dynamics evolve further.

Defense autonomy

Problem definition

Zarchan (2012) Tactical and Strategic Missile Guidance; Bar-Shalom et al. (2001) Estimation with Applications

Canonical RHS excerpt from the registered callable used for this benchmark cell. Expand it to verify the state equations; it is not a standalone runnable fixture.

Show canonical RHS excerpt
def _clamp(x: float, lo: float, hi: float) -> float:
    if x < lo:
        return lo
    if x > hi:
        return hi
    return x

def _los_rate_2d(rx, ry, vrx, vry):
    """LOS rate in 2-D (lambda_dot = (R x V_rel) / R^2)."""
    R2 = rx * rx + ry * ry + 0.01
    return (rx * vry - ry * vrx) / R2

def _sigmoid_window(t: float, t_center: float, tau: float) -> float:
    """Unit pulse centred at *t_center*, width ~4*tau, Lipschitz-continuous."""
    arg = (t - t_center) / max(tau, 1e-12)
    s = 1.0 / (1.0 + np.exp(-arg))
    return 4.0 * s * (1.0 - s)

def rhs(t, y):
    xt, yt = y[0], y[1]
    vxt, vyt = y[2], y[3]
    xi, yi = y[4], y[5]
    vxi, vyi = y[6], y[7]
    xh, yh = y[8], y[9]
    vxh, vyh = y[10], y[11]

    d = np.empty(dim)

    # --- target kinematics (sinusoidal weave) ---
    d[0] = vxt
    d[1] = vyt
    d[2] = a_t * np.sin(omega_t * t)
    d[3] = a_t * np.cos(omega_t * t)

    # --- PN guidance from EKF estimates ---
    rx = xh - xi
    ry = yh - yi
    R = np.sqrt(rx * rx + ry * ry + 0.01)

    vrx = vxh - vxi
    vry = vyh - vyi
    V_c = -(rx * vrx + ry * vry) / R  # closing velocity

    lam_dot = _los_rate_2d(rx, ry, vrx, vry)
    lam_dot = _clamp(lam_dot, -0.5, 0.5)

    a_n = N_pn * max(V_c, 10.0) * lam_dot

    # LOS angle for decomposition
    lam = np.arctan2(ry, rx)
    ax_i = -a_n * np.sin(lam)
    ay_i = a_n * np.cos(lam)

    if R < 0.1:
        ax_i = 0.0
        ay_i = 0.0

    d[4] = vxi
    d[5] = vyi
    d[6] = ax_i
    d[7] = ay_i

    # --- EKF propagation (constant-velocity prediction) ---
    d[8] = vxh
    d[9] = vyh
    d[10] = 0.0
    d[11] = 0.0

    # --- smoothed measurement updates ---
    # Find the nearest update epoch
    k = int(t / T_update + 0.5)
    k = min(k, n_updates - 1)
    t_k = update_times[k]
    w = _sigmoid_window(t, t_k, tau_update)

    if w > 1e-6:
        # True range & bearing
        drx_true = xt - xi
        dry_true = yt - yi
        R_true = np.sqrt(drx_true**2 + dry_true**2 + 0.01)
        theta_true = np.arctan2(dry_true, drx_true)

        # Noisy measurement
        R_meas = R_true + noise_r[k]
        theta_meas = theta_true + noise_th[k]

        # Measurement in Cartesian
        x_meas = xi + R_meas * np.cos(theta_meas)
        y_meas = yi + R_meas * np.sin(theta_meas)

        # Innovation
        innov_x = x_meas - xh
        innov_y = y_meas - yh

        # Correction impulse (scaled by window)
        rate = w / max(tau_update, 1e-6)
        d[8] += K_pos * innov_x * rate
        d[9] += K_pos * innov_y * rate
        d[10] += K_vel * innov_x * rate
        d[11] += K_vel * innov_y * rate

    return d
Parameters
  • K_pos = 0.4
  • K_vel = 0.2
  • N_pn = 3
  • T_update = 0.02
  • a_t = 20
  • dim = 12
  • n_updates = 1502
  • noise_r = [1.25730221093, -1.32104863291, 6.40422650443, 1.04900117153, -5.35669373161, 3.61595054909, …] [shape=(1502,), min=-38.9942173005, max=30.6603673905]
  • noise_th = [-0.0105644988785, -0.00252017064968, -0.00713905077733, -0.0109135436315, 0.00381823718907, 0.00696536738291, …] [shape=(1502,), min=-0.0271792200841, max=0.0259406043333]
  • omega_t = 0.5
  • tau_update = 0.001
  • update_times = [0, 0.02, 0.04, 0.06, 0.08, 0.1, …] [shape=(1502,), min=0, max=30.02]
Initial condition
y(0) = [5000, 2000, -100, 30, 0, 0, 200, 0, 4993.57339588, 1990.28677043, -91.5997093571, 39.8526344105]
Horizon
t ∈ [0, 5]

Canonical RHS excerpt captured from the same registered callable used for the published benchmark. Frozen closure values are summarized below; helper imports and solver settings are intentionally omitted.

Fingerprint

Spread: high

Default noise: low

Recommendation snapshot

Clean best: SolvSRK

Noisy best: SciPy BDF

Coverage

14 solver arms · clean + 5 noise levels

Ranked on survival, precision, and speed

Versions & freeze

Methodology →
Freeze
2026-08-13
libsolvsrk
2.3.0
SciPy
1.14
SUNDIALS
CVODE (bundled backend)

20 seeds/cell default · 14 arms · TRL 4–5 · simulation-lab validated · this page: CLRI-2D horizon h=5 (clri-2d-horizon-h-5)

Governed SolvTune benchmark freeze; per-arm medians only. RHS definitions and raw trial rows are not published.

Self-reported by Resonix Labs · not independently verified

Results matrix

Pick an objective and a noise level to rank all arms on survival, median SCD, median nfev, and median wall time. Medians across seeds.

Objective

Best overall trade-off of survival, precision, and speed.

Noise level

#SolverSurvivalSCDnfevWallScore
1SolvSRK
100%
11.7203,6661.56 s0.898
2SciPy RadauSciPy
100%
9.7243,6246.76 s0.849
3SciPy LSODASciPy
100%
9.663,862713 ms0.847
4Vern9external
100%
9.2145,4428.66 s0.838
5Vern7external
100%
9.293,9226.75 s0.837
6SciPy RK23SciPy
100%
9.1203,3693.33 s0.835
7SciPy BDFSciPy
100%
8.789,7863.66 s0.826
8CVODE Adamsexternal
100%
8.641,743613 ms0.823
9Tsit5external
100%
8.569,0967.08 s0.821
10CVODE BDFexternal
100%
8.258,500861 ms0.814
11FBDFexternal
100%
8.199,7378.43 s0.813
12SciPy RK45SciPy
100%
7.554,896771 ms0.798
13SciPy DOP853SciPy
100%
5.889,9901.23 s0.757
14TRBDF2external
100%
5.282,3357.65 s0.744

At Clean, best balanced arm is SolvSRK.

Values are medians across seeds, measured by Resonix Labs on Resonix hardware and not independently verified; nfev and wall are on reference lab hardware (indicative). Under injected noise only SolvSRK and the SciPy arms are run. How we measure accuracy → · Verification status →

SolvScout · free

Profile your problem for free

This page shows one published benchmark cell. SolvScout fingerprints your ODE, compares it to the full corpus, and recommends a solver with the same survival / precision / speed ranking you see here — including when a SciPy arm wins.

SolvSRK · 30-day trial

Run the winner on your machine

SolvSRK is the stiffness-adaptive integrator behind the SolvSRK column in these tables. Create an account, activate a machine, and take a 30-day trial — same binary you'd ship after purchase.

Cite this page

Replace the access date. Pin the freeze ID and library versions when comparing against a later export. Cite it as what it is — a self-reported vendor benchmark, not an independently verified result. The note field says so; please keep it.

@misc{resonix_evidence_clri_2d_horizon_h_5_2026,
  title        = {Resonix Evidence Portal: CLRI-2D horizon h=5},
  author       = {{Resonix Labs (Canada) Inc.}},
  year         = {2026},
  howpublished = {\url{https://resonix.tech/evidence/problems/clri-2d-horizon-h-5}},
  note         = {Self-reported vendor benchmark; internally generated by Resonix Labs and not independently verified. Accessed YYYY-MM-DD. Freeze 2026-08-13; libsolvsrk 2.3.0; SciPy 1.14.}
}

Related

TRL 4–5 · simulation-lab validated · 398 problems · 14 solver arms · clean + 5 noise levels

Freeze: 2026-08-13 · scipy 1.14 · libsolvsrk 2.3.0 · Methodology

Self-reported by Resonix Labs · not independently verified · Verification status