FAILURE MAP
← Case archive

FA-87176 / Physics integrator stability / Open access

Hemisphere canonicalization negates only the scalar part · case 01

Quaternions with negative w are turned into a different rotation.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Only w is negated when forcing w>=0.

VERIFIED REPAIR

Negate all four components; q and -q are the same rotation.

Unsuccessful approach: Taking absolute values of every component changes the rotation axis.

Case contract

solve(q, omega, dt, steps): orientation quaternion [w,x,y,z], world-frame angular velocity omega. Each step q += 0.5*dt*(0,omega) (x) q (Hamilton product, omega on the left), then renormalize to unit length. Return q with w>=0 (negate all components otherwise), rounded to 6.

Why this case matters

Game and robotics physics loops depend on integrator update order, step control and stabilization terms; a wrong decision point turns a stable simulation into drifting or exploding motion.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(q, omega, dt, steps):
    def mul(a, b):
        aw, ax, ay, az = a
        bw, bx, by, bz = b
        return [aw * bw - ax * bx - ay * by - az * bz,
                aw * bx + ax * bw + ay * bz - az * by,
                aw * by - ax * bz + ay * bw + az * bx,
                aw * bz + ax * by - ay * bx + az * bw]
    w = [0.0, omega[0], omega[1], omega[2]]
    q = list(q)
    for _ in range(steps):
        d = mul(w, q)
        q = [q[i] + 0.5 * dt * d[i] for i in range(4)]
        n = math.sqrt(sum(c * c for c in q))
        q = [c / n for c in q]
    if q[0] < 0:
        q = [-q[0]] + q[1:]
    return [round(c, 6) for c in q]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[[-0.3107, -0.674, -0.3139, -0.5921], [-0.232, -0.094, 3.837], 0.01, 1], [0.300226, 0.66724, 0.327323, 0.597925]], [[[0.1235, -0.981, -0.116, 0.0948], [1.519, -1.533, 2.819], 0.01, 6], [0.154086, -0.964359, -0.208115, 0.054383]], [[[-0.1167, -0.6369, 0.0725, 0.7586], [-2.046, 3.684, -1.377], 0.01, 3], [0.12432, 0.588539, -0.102301, -0.792276]], [[[-0.0519, 0.3724, -0.6259, 0.6833], [-3.675, 2.343, 0.559], 0.1, 4], [0.391258, 0.612492, 0.044021, 0.685444]], [[[0.4923, -0.7394, -0.4537, 0.0707], [0.654, 1.164, -0.044], 0.05, 4], [0.589082, -0.694546, -0.393931, 0.124123]], [[[0.1693, 0.5991, -0.3007, 0.7225], [-0.846, 0.203, 2.272], 0.1, 5], [0.11186, -0.653252, -0.222024, -0.71516]], [[[-0.6715, 0.679, -0.2186, -0.2006], [1.587, 1.637, 3.714], 0.05, 3], [0.634375, -0.600611, 0.079348, 0.480144]], [[[-0.1804, -0.126, 0.6456, -0.7313], [0.559, -1.91, -2.794], 0.05, 1], [0.198164, 0.048329, -0.670767, 0.713069]]], [[[[-0.0856, 0.1732, -0.7636, 0.6161], [-0.55, 3.798, 0.546], 0.01, 4], [0.032223, -0.228697, 0.759147, -0.608568]], [[[0.1838, -0.0253, -0.8789, -0.4395], [1.554, -2.734, -2.377], 0.1, 3], [0.323219, 0.105274, 0.699658, 0.628431]], [[[-0.2187, 0.559, -0.5927, 0.537], [-2.955, 1.391, 3.037], 0.05, 8], [0.00916, 0.908208, 0.144733, 0.392589]], [[[-0.4028, 0.5372, 0.3775, -0.6377], [-2.487, -2.209, -0.784], 0.05, 1], [0.359741, -0.602665, -0.348298, 0.621346]], [[[-0.5213, -0.5642, -0.0067, 0.6402], [-1.291, 3.69, -0.242], 0.01, 1], [0.523949, 0.548933, 0.011501, -0.651166]], [[[0.0704, -0.8303, -0.3766, 0.4047], [-3.613, -3.903, -0.629], 0.01, 8], [0.098492, 0.893525, 0.300152, -0.319093]], [[[0.4389, 0.6456, 0.03, 0.6243], [1.56, -1.019, 2.901], 0.01, 5], [0.367657, 0.642216, 0.041165, 0.671337]], [[[0.1909, -0.5782, 0.5418, -0.5794], [2.961, 1.464, 2.91], 0.05, 8], [0.576988, -0.694529, 0.401075, 0.154446]]], [[[[-0.5736, 0.5131, -0.6375, 0.0369], [2.905, 3.624, -3.693], 0.05, 3], [0.449619, -0.183744, 0.870315, 0.081437]], [[[-0.5477, -0.5732, 0.5822, 0.1802], [-0.237, 0.825, 0.304], 0.05, 3], [0.596708, 0.564271, -0.53714, -0.192402]], [[[-0.6225, -0.405, 0.6692, 0.0264], [-1.412, -0.277, -0.555], 0.01, 6], [0.632967, 0.367278, -0.681494, -0.005031]], [[[0.706, 0.1628, -0.2851, -0.6276], [1.26, -1.35, 0.016], 0.01, 7], [0.684206, 0.223353, -0.290076, -0.630739]], [[[-0.2932, 0.2891, -0.5492, 0.7272], [3.91, 0.481, -1.992], 0.1, 3], [0.153089, 0.02993, 0.92583, -0.344247]], [[[-0.4413, 0.7728, 0.4297, 0.1528], [1.432, -3.081, -3.746], 0.01, 3], [0.42821, -0.778191, -0.402193, -0.222026]], [[[0.6609, 0.5042, 0.0424, 0.5543], [0.725, -2.934, -2.967], 0.05, 9], [0.654178, 0.097457, -0.71189, 0.236149]], [[[0.6226, -0.2376, 0.3328, -0.6672], [0.235, 0.639, 2.682], 0.01, 7], [0.676732, -0.277462, 0.328355, -0.597688]]], [[[[-0.0483, -0.4346, -0.8534, 0.2837], [-2.594, -2.259, -1.165], 0.1, 10], [0.720466, 0.308046, -0.553475, -0.282316]], [[[-0.5243, 0.4858, -0.6518, 0.2534], [0.806, 2.589, -2.197], 0.1, 7], [0.311126, -0.149416, -0.935172, -0.079553]], [[[-0.6239, -0.5212, -0.5767, 0.0806], [3.32, 0.152, -0.19], 0.01, 8], [0.545273, 0.603076, 0.582106, -0.011421]], [[[0.2076, -0.1442, 0.9556, -0.1516], [2.825, 3.025, 3.659], 0.05, 2], [0.1052, -0.304075, 0.945742, 0.045198]], [[[0.5409, 0.1216, -0.7667, 0.3238], [1.625, -0.383, -1.461], 0.01, 8], [0.538036, 0.106531, -0.80006, 0.243047]], [[[-0.7545, 0.0504, 0.0585, -0.6517], [-3.471, -3.414, 0.269], 0.05, 10], [0.158576, -0.945136, -0.083376, 0.273168]], [[[-0.2984, 0.5748, -0.6478, -0.4012], [1.131, 2.056, 1.685], 0.05, 2], [0.227761, -0.5653, 0.600835, 0.517261]], [[[-0.0862, 0.5253, 0.7084, -0.4635], [3.858, -1.823, -2.614], 0.01, 5], [0.134057, -0.580125, -0.717119, 0.36225]]], [[[[0.3167, -0.7605, 0.5016, 0.264], [-3.251, 2.834, -1.136], 0.01, 2], [0.280469, -0.756877, 0.527297, 0.26539]], [[[0.7695, 0.3041, -0.5589, 0.0545], [2.708, 2.922, -1.672], 0.1, 1], [0.796192, 0.361206, -0.468484, -0.127008]], [[[-0.2117, -0.7256, 0.617, -0.2191], [-3.406, 0.968, 2.951], 0.1, 9], [0.373544, -0.073188, 0.877043, 0.293093]], [[[-0.2959, 0.2274, 0.9275, -0.0208], [1.319, 2.295, -0.622], 0.05, 8], [0.717296, -0.221087, -0.643402, -0.150468]], [[[0.7, 0.5103, -0.436, 0.244], [3.937, -1.801, 3.592], 0.1, 9], [0.954153, -0.031937, -0.297609, 0.000207]], [[[0.4408, -0.5714, -0.5378, -0.4359], [1.496, 0.521, 3.047], 0.05, 13], [0.838174, 0.288707, -0.461993, 0.025972]], [[[-0.0537, -0.8252, -0.3784, 0.4159], [1.996, 0.032, -1.898], 0.01, 4], [0.004675, 0.840181, 0.363147, -0.402737]], [[[0.1094, 0.2843, 0.727, 0.6154], [3.004, -3.908, 3.399], 0.05, 2], [0.099484, 0.04945, 0.631065, 0.767733]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("case %d" % i, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
case 0[0.300226, -0.66724, -0.327323, -0.597925][0.300226, 0.66724, 0.327323, 0.597925]Failed
case 1[0.154086, -0.964359, -0.208115, 0.054383][0.154086, -0.964359, -0.208115, 0.054383]Passed
case 2[0.12432, -0.588539, 0.102301, 0.792276][0.12432, 0.588539, -0.102301, -0.792276]Failed
case 3[0.391258, 0.612492, 0.044021, 0.685444][0.391258, 0.612492, 0.044021, 0.685444]Passed
case 4[0.589082, -0.694546, -0.393931, 0.124123][0.589082, -0.694546, -0.393931, 0.124123]Passed
case 5[0.11186, 0.653252, 0.222024, 0.71516][0.11186, -0.653252, -0.222024, -0.71516]Failed
case 6[0.634375, 0.600611, -0.079348, -0.480144][0.634375, -0.600611, 0.079348, 0.480144]Failed
case 7[0.198164, -0.048329, 0.670767, -0.713069][0.198164, 0.048329, -0.670767, 0.713069]Failed

SHA-256 / 8fcf4cb484d537b44716fa8cb962e4d92e912ae5d7f5d1d27c2fd279a507332a

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(q, omega, dt, steps):
    def mul(a, b):
        aw, ax, ay, az = a
        bw, bx, by, bz = b
        return [aw * bw - ax * bx - ay * by - az * bz,
                aw * bx + ax * bw + ay * bz - az * by,
                aw * by - ax * bz + ay * bw + az * bx,
                aw * bz + ax * by - ay * bx + az * bw]
    w = [0.0, omega[0], omega[1], omega[2]]
    q = list(q)
    for _ in range(steps):
        d = mul(w, q)
        q = [q[i] + 0.5 * dt * d[i] for i in range(4)]
        n = math.sqrt(sum(c * c for c in q))
        q = [c / n for c in q]
    if q[0] < 0:
        q = [abs(c) for c in q]
    return [round(c, 6) for c in q]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[[-0.3107, -0.674, -0.3139, -0.5921], [-0.232, -0.094, 3.837], 0.01, 1], [0.300226, 0.66724, 0.327323, 0.597925]], [[[0.1235, -0.981, -0.116, 0.0948], [1.519, -1.533, 2.819], 0.01, 6], [0.154086, -0.964359, -0.208115, 0.054383]], [[[-0.1167, -0.6369, 0.0725, 0.7586], [-2.046, 3.684, -1.377], 0.01, 3], [0.12432, 0.588539, -0.102301, -0.792276]], [[[-0.0519, 0.3724, -0.6259, 0.6833], [-3.675, 2.343, 0.559], 0.1, 4], [0.391258, 0.612492, 0.044021, 0.685444]], [[[0.4923, -0.7394, -0.4537, 0.0707], [0.654, 1.164, -0.044], 0.05, 4], [0.589082, -0.694546, -0.393931, 0.124123]], [[[0.1693, 0.5991, -0.3007, 0.7225], [-0.846, 0.203, 2.272], 0.1, 5], [0.11186, -0.653252, -0.222024, -0.71516]], [[[-0.6715, 0.679, -0.2186, -0.2006], [1.587, 1.637, 3.714], 0.05, 3], [0.634375, -0.600611, 0.079348, 0.480144]], [[[-0.1804, -0.126, 0.6456, -0.7313], [0.559, -1.91, -2.794], 0.05, 1], [0.198164, 0.048329, -0.670767, 0.713069]]], [[[[-0.0856, 0.1732, -0.7636, 0.6161], [-0.55, 3.798, 0.546], 0.01, 4], [0.032223, -0.228697, 0.759147, -0.608568]], [[[0.1838, -0.0253, -0.8789, -0.4395], [1.554, -2.734, -2.377], 0.1, 3], [0.323219, 0.105274, 0.699658, 0.628431]], [[[-0.2187, 0.559, -0.5927, 0.537], [-2.955, 1.391, 3.037], 0.05, 8], [0.00916, 0.908208, 0.144733, 0.392589]], [[[-0.4028, 0.5372, 0.3775, -0.6377], [-2.487, -2.209, -0.784], 0.05, 1], [0.359741, -0.602665, -0.348298, 0.621346]], [[[-0.5213, -0.5642, -0.0067, 0.6402], [-1.291, 3.69, -0.242], 0.01, 1], [0.523949, 0.548933, 0.011501, -0.651166]], [[[0.0704, -0.8303, -0.3766, 0.4047], [-3.613, -3.903, -0.629], 0.01, 8], [0.098492, 0.893525, 0.300152, -0.319093]], [[[0.4389, 0.6456, 0.03, 0.6243], [1.56, -1.019, 2.901], 0.01, 5], [0.367657, 0.642216, 0.041165, 0.671337]], [[[0.1909, -0.5782, 0.5418, -0.5794], [2.961, 1.464, 2.91], 0.05, 8], [0.576988, -0.694529, 0.401075, 0.154446]]], [[[[-0.5736, 0.5131, -0.6375, 0.0369], [2.905, 3.624, -3.693], 0.05, 3], [0.449619, -0.183744, 0.870315, 0.081437]], [[[-0.5477, -0.5732, 0.5822, 0.1802], [-0.237, 0.825, 0.304], 0.05, 3], [0.596708, 0.564271, -0.53714, -0.192402]], [[[-0.6225, -0.405, 0.6692, 0.0264], [-1.412, -0.277, -0.555], 0.01, 6], [0.632967, 0.367278, -0.681494, -0.005031]], [[[0.706, 0.1628, -0.2851, -0.6276], [1.26, -1.35, 0.016], 0.01, 7], [0.684206, 0.223353, -0.290076, -0.630739]], [[[-0.2932, 0.2891, -0.5492, 0.7272], [3.91, 0.481, -1.992], 0.1, 3], [0.153089, 0.02993, 0.92583, -0.344247]], [[[-0.4413, 0.7728, 0.4297, 0.1528], [1.432, -3.081, -3.746], 0.01, 3], [0.42821, -0.778191, -0.402193, -0.222026]], [[[0.6609, 0.5042, 0.0424, 0.5543], [0.725, -2.934, -2.967], 0.05, 9], [0.654178, 0.097457, -0.71189, 0.236149]], [[[0.6226, -0.2376, 0.3328, -0.6672], [0.235, 0.639, 2.682], 0.01, 7], [0.676732, -0.277462, 0.328355, -0.597688]]], [[[[-0.0483, -0.4346, -0.8534, 0.2837], [-2.594, -2.259, -1.165], 0.1, 10], [0.720466, 0.308046, -0.553475, -0.282316]], [[[-0.5243, 0.4858, -0.6518, 0.2534], [0.806, 2.589, -2.197], 0.1, 7], [0.311126, -0.149416, -0.935172, -0.079553]], [[[-0.6239, -0.5212, -0.5767, 0.0806], [3.32, 0.152, -0.19], 0.01, 8], [0.545273, 0.603076, 0.582106, -0.011421]], [[[0.2076, -0.1442, 0.9556, -0.1516], [2.825, 3.025, 3.659], 0.05, 2], [0.1052, -0.304075, 0.945742, 0.045198]], [[[0.5409, 0.1216, -0.7667, 0.3238], [1.625, -0.383, -1.461], 0.01, 8], [0.538036, 0.106531, -0.80006, 0.243047]], [[[-0.7545, 0.0504, 0.0585, -0.6517], [-3.471, -3.414, 0.269], 0.05, 10], [0.158576, -0.945136, -0.083376, 0.273168]], [[[-0.2984, 0.5748, -0.6478, -0.4012], [1.131, 2.056, 1.685], 0.05, 2], [0.227761, -0.5653, 0.600835, 0.517261]], [[[-0.0862, 0.5253, 0.7084, -0.4635], [3.858, -1.823, -2.614], 0.01, 5], [0.134057, -0.580125, -0.717119, 0.36225]]], [[[[0.3167, -0.7605, 0.5016, 0.264], [-3.251, 2.834, -1.136], 0.01, 2], [0.280469, -0.756877, 0.527297, 0.26539]], [[[0.7695, 0.3041, -0.5589, 0.0545], [2.708, 2.922, -1.672], 0.1, 1], [0.796192, 0.361206, -0.468484, -0.127008]], [[[-0.2117, -0.7256, 0.617, -0.2191], [-3.406, 0.968, 2.951], 0.1, 9], [0.373544, -0.073188, 0.877043, 0.293093]], [[[-0.2959, 0.2274, 0.9275, -0.0208], [1.319, 2.295, -0.622], 0.05, 8], [0.717296, -0.221087, -0.643402, -0.150468]], [[[0.7, 0.5103, -0.436, 0.244], [3.937, -1.801, 3.592], 0.1, 9], [0.954153, -0.031937, -0.297609, 0.000207]], [[[0.4408, -0.5714, -0.5378, -0.4359], [1.496, 0.521, 3.047], 0.05, 13], [0.838174, 0.288707, -0.461993, 0.025972]], [[[-0.0537, -0.8252, -0.3784, 0.4159], [1.996, 0.032, -1.898], 0.01, 4], [0.004675, 0.840181, 0.363147, -0.402737]], [[[0.1094, 0.2843, 0.727, 0.6154], [3.004, -3.908, 3.399], 0.05, 2], [0.099484, 0.04945, 0.631065, 0.767733]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("case %d" % i, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
case 0[0.300226, 0.66724, 0.327323, 0.597925][0.300226, 0.66724, 0.327323, 0.597925]Passed
case 1[0.154086, -0.964359, -0.208115, 0.054383][0.154086, -0.964359, -0.208115, 0.054383]Passed
case 2[0.12432, 0.588539, 0.102301, 0.792276][0.12432, 0.588539, -0.102301, -0.792276]Failed
case 3[0.391258, 0.612492, 0.044021, 0.685444][0.391258, 0.612492, 0.044021, 0.685444]Passed
case 4[0.589082, -0.694546, -0.393931, 0.124123][0.589082, -0.694546, -0.393931, 0.124123]Passed
case 5[0.11186, 0.653252, 0.222024, 0.71516][0.11186, -0.653252, -0.222024, -0.71516]Failed
case 6[0.634375, 0.600611, 0.079348, 0.480144][0.634375, -0.600611, 0.079348, 0.480144]Failed
case 7[0.198164, 0.048329, 0.670767, 0.713069][0.198164, 0.048329, -0.670767, 0.713069]Failed

SHA-256 / fd4715de78de32d18e1e1ecc2c993a68505c583d4b5356a1df183d98da3424e2

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(q, omega, dt, steps):
    def mul(a, b):
        aw, ax, ay, az = a
        bw, bx, by, bz = b
        return [aw * bw - ax * bx - ay * by - az * bz,
                aw * bx + ax * bw + ay * bz - az * by,
                aw * by - ax * bz + ay * bw + az * bx,
                aw * bz + ax * by - ay * bx + az * bw]
    w = [0.0, omega[0], omega[1], omega[2]]
    q = list(q)
    for _ in range(steps):
        d = mul(w, q)
        q = [q[i] + 0.5 * dt * d[i] for i in range(4)]
        n = math.sqrt(sum(c * c for c in q))
        q = [c / n for c in q]
    if q[0] < 0:
        q = [-c for c in q]
    return [round(c, 6) for c in q]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[[-0.3107, -0.674, -0.3139, -0.5921], [-0.232, -0.094, 3.837], 0.01, 1], [0.300226, 0.66724, 0.327323, 0.597925]], [[[0.1235, -0.981, -0.116, 0.0948], [1.519, -1.533, 2.819], 0.01, 6], [0.154086, -0.964359, -0.208115, 0.054383]], [[[-0.1167, -0.6369, 0.0725, 0.7586], [-2.046, 3.684, -1.377], 0.01, 3], [0.12432, 0.588539, -0.102301, -0.792276]], [[[-0.0519, 0.3724, -0.6259, 0.6833], [-3.675, 2.343, 0.559], 0.1, 4], [0.391258, 0.612492, 0.044021, 0.685444]], [[[0.4923, -0.7394, -0.4537, 0.0707], [0.654, 1.164, -0.044], 0.05, 4], [0.589082, -0.694546, -0.393931, 0.124123]], [[[0.1693, 0.5991, -0.3007, 0.7225], [-0.846, 0.203, 2.272], 0.1, 5], [0.11186, -0.653252, -0.222024, -0.71516]], [[[-0.6715, 0.679, -0.2186, -0.2006], [1.587, 1.637, 3.714], 0.05, 3], [0.634375, -0.600611, 0.079348, 0.480144]], [[[-0.1804, -0.126, 0.6456, -0.7313], [0.559, -1.91, -2.794], 0.05, 1], [0.198164, 0.048329, -0.670767, 0.713069]]], [[[[-0.0856, 0.1732, -0.7636, 0.6161], [-0.55, 3.798, 0.546], 0.01, 4], [0.032223, -0.228697, 0.759147, -0.608568]], [[[0.1838, -0.0253, -0.8789, -0.4395], [1.554, -2.734, -2.377], 0.1, 3], [0.323219, 0.105274, 0.699658, 0.628431]], [[[-0.2187, 0.559, -0.5927, 0.537], [-2.955, 1.391, 3.037], 0.05, 8], [0.00916, 0.908208, 0.144733, 0.392589]], [[[-0.4028, 0.5372, 0.3775, -0.6377], [-2.487, -2.209, -0.784], 0.05, 1], [0.359741, -0.602665, -0.348298, 0.621346]], [[[-0.5213, -0.5642, -0.0067, 0.6402], [-1.291, 3.69, -0.242], 0.01, 1], [0.523949, 0.548933, 0.011501, -0.651166]], [[[0.0704, -0.8303, -0.3766, 0.4047], [-3.613, -3.903, -0.629], 0.01, 8], [0.098492, 0.893525, 0.300152, -0.319093]], [[[0.4389, 0.6456, 0.03, 0.6243], [1.56, -1.019, 2.901], 0.01, 5], [0.367657, 0.642216, 0.041165, 0.671337]], [[[0.1909, -0.5782, 0.5418, -0.5794], [2.961, 1.464, 2.91], 0.05, 8], [0.576988, -0.694529, 0.401075, 0.154446]]], [[[[-0.5736, 0.5131, -0.6375, 0.0369], [2.905, 3.624, -3.693], 0.05, 3], [0.449619, -0.183744, 0.870315, 0.081437]], [[[-0.5477, -0.5732, 0.5822, 0.1802], [-0.237, 0.825, 0.304], 0.05, 3], [0.596708, 0.564271, -0.53714, -0.192402]], [[[-0.6225, -0.405, 0.6692, 0.0264], [-1.412, -0.277, -0.555], 0.01, 6], [0.632967, 0.367278, -0.681494, -0.005031]], [[[0.706, 0.1628, -0.2851, -0.6276], [1.26, -1.35, 0.016], 0.01, 7], [0.684206, 0.223353, -0.290076, -0.630739]], [[[-0.2932, 0.2891, -0.5492, 0.7272], [3.91, 0.481, -1.992], 0.1, 3], [0.153089, 0.02993, 0.92583, -0.344247]], [[[-0.4413, 0.7728, 0.4297, 0.1528], [1.432, -3.081, -3.746], 0.01, 3], [0.42821, -0.778191, -0.402193, -0.222026]], [[[0.6609, 0.5042, 0.0424, 0.5543], [0.725, -2.934, -2.967], 0.05, 9], [0.654178, 0.097457, -0.71189, 0.236149]], [[[0.6226, -0.2376, 0.3328, -0.6672], [0.235, 0.639, 2.682], 0.01, 7], [0.676732, -0.277462, 0.328355, -0.597688]]], [[[[-0.0483, -0.4346, -0.8534, 0.2837], [-2.594, -2.259, -1.165], 0.1, 10], [0.720466, 0.308046, -0.553475, -0.282316]], [[[-0.5243, 0.4858, -0.6518, 0.2534], [0.806, 2.589, -2.197], 0.1, 7], [0.311126, -0.149416, -0.935172, -0.079553]], [[[-0.6239, -0.5212, -0.5767, 0.0806], [3.32, 0.152, -0.19], 0.01, 8], [0.545273, 0.603076, 0.582106, -0.011421]], [[[0.2076, -0.1442, 0.9556, -0.1516], [2.825, 3.025, 3.659], 0.05, 2], [0.1052, -0.304075, 0.945742, 0.045198]], [[[0.5409, 0.1216, -0.7667, 0.3238], [1.625, -0.383, -1.461], 0.01, 8], [0.538036, 0.106531, -0.80006, 0.243047]], [[[-0.7545, 0.0504, 0.0585, -0.6517], [-3.471, -3.414, 0.269], 0.05, 10], [0.158576, -0.945136, -0.083376, 0.273168]], [[[-0.2984, 0.5748, -0.6478, -0.4012], [1.131, 2.056, 1.685], 0.05, 2], [0.227761, -0.5653, 0.600835, 0.517261]], [[[-0.0862, 0.5253, 0.7084, -0.4635], [3.858, -1.823, -2.614], 0.01, 5], [0.134057, -0.580125, -0.717119, 0.36225]]], [[[[0.3167, -0.7605, 0.5016, 0.264], [-3.251, 2.834, -1.136], 0.01, 2], [0.280469, -0.756877, 0.527297, 0.26539]], [[[0.7695, 0.3041, -0.5589, 0.0545], [2.708, 2.922, -1.672], 0.1, 1], [0.796192, 0.361206, -0.468484, -0.127008]], [[[-0.2117, -0.7256, 0.617, -0.2191], [-3.406, 0.968, 2.951], 0.1, 9], [0.373544, -0.073188, 0.877043, 0.293093]], [[[-0.2959, 0.2274, 0.9275, -0.0208], [1.319, 2.295, -0.622], 0.05, 8], [0.717296, -0.221087, -0.643402, -0.150468]], [[[0.7, 0.5103, -0.436, 0.244], [3.937, -1.801, 3.592], 0.1, 9], [0.954153, -0.031937, -0.297609, 0.000207]], [[[0.4408, -0.5714, -0.5378, -0.4359], [1.496, 0.521, 3.047], 0.05, 13], [0.838174, 0.288707, -0.461993, 0.025972]], [[[-0.0537, -0.8252, -0.3784, 0.4159], [1.996, 0.032, -1.898], 0.01, 4], [0.004675, 0.840181, 0.363147, -0.402737]], [[[0.1094, 0.2843, 0.727, 0.6154], [3.004, -3.908, 3.399], 0.05, 2], [0.099484, 0.04945, 0.631065, 0.767733]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("case %d" % i, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
case 0[0.300226, 0.66724, 0.327323, 0.597925][0.300226, 0.66724, 0.327323, 0.597925]Passed
case 1[0.154086, -0.964359, -0.208115, 0.054383][0.154086, -0.964359, -0.208115, 0.054383]Passed
case 2[0.12432, 0.588539, -0.102301, -0.792276][0.12432, 0.588539, -0.102301, -0.792276]Passed
case 3[0.391258, 0.612492, 0.044021, 0.685444][0.391258, 0.612492, 0.044021, 0.685444]Passed
case 4[0.589082, -0.694546, -0.393931, 0.124123][0.589082, -0.694546, -0.393931, 0.124123]Passed
case 5[0.11186, -0.653252, -0.222024, -0.71516][0.11186, -0.653252, -0.222024, -0.71516]Passed
case 6[0.634375, -0.600611, 0.079348, 0.480144][0.634375, -0.600611, 0.079348, 0.480144]Passed
case 7[0.198164, 0.048329, -0.670767, 0.713069][0.198164, 0.048329, -0.670767, 0.713069]Passed

SHA-256 / c82d8c0095c0890a07a0b9b83a3440ce8f8ff6e47f00a23056b0d69ecb16127b

Verification & scope

A deterministic bounded teaching model with stipulated toy conventions and rounded float output; not a production physics engine or a proof of numerical stability. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:50:56.389087+00:00.

Case digest / 2e3eee5d4b3379970a8f5deb01d8251ecb0959239d8a931e1ad998e93b22ea77