FA-87161 / Physics integrator stability / Open access
World angular velocity is applied on the body side of the product · case 01
Spinning bodies precess about the wrong axis once they are tilted.
ROOT CAUSE
The derivative uses q(x)omega, the body-frame convention.
VERIFIED REPAIR
For world-frame omega multiply omega(x)q.
Unsuccessful approach: Multiplying by the conjugate of q still does not express world-frame spin.
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(q, w)
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.1116, -0.6859, -0.2807, 0.6621], [-1.367, -3.32, -3.869], 0.05, 1], [0.093483, 0.757592, 0.180891, -0.620156]], [[[0.5719, -0.201, -0.6787, -0.4146], [1.924, -1.133, -2.402], 0.05, 5], [0.357598, -0.191716, -0.546007, -0.732971]], [[[-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.5784, -0.2766, 0.5787, -0.504], [-2.529, -0.35, -1.088], 0.1, 1], [0.52115, -0.306506, 0.514969, -0.607671]], [[[0.5438, 0.3302, -0.6582, 0.4024], [-2.872, -3.023, 2.126], 0.05, 4], [0.303775, 0.162656, -0.567546, 0.747767]], [[[-0.4336, 0.4529, 0.2639, 0.733], [-3.578, -1.691, -2.103], 0.01, 1], [0.415442, -0.457106, -0.27584, -0.736461]], [[[-0.6434, -0.5565, -0.5031, -0.1523], [-2.902, -3.621, 0.012], 0.05, 6], [0.967882, 0.094358, 0.128105, 0.194656]], [[[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.3472, -0.3736, 0.4295, 0.7453], [-2.315, 1.527, -3.853], 0.01, 8], [0.287249, 0.223901, -0.5265, -0.768215]], [[[0.8268, -0.0559, 0.3935, 0.398], [-0.387, 0.922, 1.986], 0.1, 7], [0.220922, -0.271091, 0.534556, 0.769385]], [[[0.7724, -0.3363, 0.1754, -0.5095], [-1.034, -3.868, 2.146], 0.05, 4], [0.831714, -0.225919, -0.249805, -0.441373]], [[[0.6623, -0.6015, -0.0252, 0.4461], [1.478, -3.402, -2.007], 0.05, 6], [0.772394, -0.568265, -0.258655, -0.116536]], [[[-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.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.4376, -0.0327, 0.5947, -0.6736], [2.933, -2.297, 1.467], 0.01, 6], [0.507773, 0.026136, 0.618032, -0.5996]], [[[0.0263, -0.6798, 0.6943, 0.2347], [-2.851, -1.309, 1.331], 0.01, 4], [0.000571, 0.704341, -0.687306, -0.177522]]], [[[[-0.3438, -0.8064, 0.2793, -0.3918], [0.487, -0.414, 0.563], 0.05, 6], [0.231973, 0.824132, -0.258947, 0.447149]], [[[-0.3511, 0.4777, 0.5685, -0.5704], [2.489, 3.438, -0.071], 0.1, 2], [0.625498, -0.169104, -0.536584, 0.540586]], [[[0.5702, -0.6235, -0.2795, 0.4562], [-0.18, -0.187, 0.167], 0.1, 4], [0.520981, -0.650508, -0.304658, 0.461087]], [[[-0.5755, -0.1729, 0.1896, -0.7765], [-1.564, 1.954, -3.317], 0.01, 5], [0.652658, 0.171677, -0.144528, 0.723655]], [[[-0.3086, -0.4487, -0.811, 0.2137], [-0.353, -3.189, 2.11], 0.01, 10], [0.461721, 0.383888, 0.790513, -0.12055]], [[[-0.3137, -0.499, 0.6659, 0.4573], [0.29, 3.395, 1.611], 0.1, 5], [0.794564, 0.215018, 0.028732, -0.567106]], [[[-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.571, 0.1155, 0.5121, 0.6312], [2.085, -1.83, -1.285], 0.01, 1], [0.563386, -0.107047, -0.509932, -0.641176]]], [[[[0.5728, 0.3899, 0.4262, -0.5816], [-2.116, -3.996, -1.282], 0.05, 1], [0.613137, 0.428408, 0.323485, -0.579559]], [[[0.2873, -0.6346, 0.6402, -0.324], [3.029, -0.324, 1.16], 0.1, 8], [0.817944, -0.105726, 0.220751, 0.520633]], [[[0.2652, 0.6436, 0.4121, 0.588], [-2.665, 2.022, 0.007], 0.1, 11], [0.187909, -0.02187, 0.507266, -0.840769]], [[[0.2307, 0.3931, 0.7631, -0.4581], [2.174, 2.113, 3.444], 0.05, 10], [0.080385, 0.450009, -0.879659, -0.131262]], [[[0.3499, -0.381, 0.1815, -0.8363], [-2.42, 1.295, -0.715], 0.05, 10], [0.136518, 0.702138, 0.159756, 0.680326]], [[[0.346, -0.6937, -0.611, 0.1602], [-2.952, -1.698, -3.004], 0.1, 5], [0.363368, 0.923626, -0.121437, 0.011516]], [[[0.3265, 0.31, 0.1591, -0.8786], [-1.35, 2.136, -0.561], 0.01, 12], [0.297825, 0.173125, 0.117593, -0.931397]], [[[0.1401, -0.0998, 0.7197, 0.6726], [-3.242, -3.373, -3.717], 0.01, 11], [0.381235, -0.097044, 0.793491, 0.464343]]], [[[[-0.6115, -0.3532, -0.6797, -0.1982], [3.428, -1.888, 3.841], 0.05, 13], [0.244774, 0.22911, 0.220548, -0.915944]], [[[0.2915, -0.4846, 0.5466, 0.6176], [-2.376, 3.234, 3.373], 0.1, 13], [0.105141, -0.495302, 0.564739, 0.651683]], [[[-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.4693, 0.5928, -0.5163, -0.4022], [3.917, 1.937, -1.401], 0.05, 6], [0.62339, 0.001817, 0.422365, 0.658019]], [[[0.5273, 0.4224, -0.4746, 0.5641], [-2.08, 3.628, 3.896], 0.01, 13], [0.516939, 0.571342, -0.141768, 0.621485]], [[[-0.447, -0.6364, 0.627, 0.0463], [3.512, -0.033, 2.944], 0.05, 9], [0.164268, -0.967385, -0.052794, 0.185459]], [[[0.0332, -0.1691, -0.7704, -0.6138], [2.17, 2.479, 1.5], 0.1, 9], [0.881211, -0.074251, 0.351735, -0.30698]], [[[0.1785, -0.1228, -0.7653, 0.6061], [2.105, 0.281, 2.09], 0.01, 8], [0.145584, -0.036317, -0.819027, 0.553788]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| case 0 | [0.093483, 0.5948, 0.357296, -0.714012] | [0.093483, 0.757592, 0.180891, -0.620156] | Failed |
| case 1 | [0.357598, 0.089757, -0.856584, -0.361015] | [0.357598, -0.191716, -0.546007, -0.732971] | Failed |
| case 2 | [0.198164, 0.207771, -0.632864, 0.719059] | [0.198164, 0.048329, -0.670767, 0.713069] | Failed |
| case 3 | [0.52115, -0.386345, 0.611415, -0.453115] | [0.52115, -0.306506, 0.514969, -0.607671] | Failed |
| case 4 | [0.303775, 0.127547, -0.924183, 0.19323] | [0.303775, 0.162656, -0.567546, 0.747767] | Failed |
| case 5 | [0.415442, -0.463949, -0.259142, -0.738244] | [0.415442, -0.457106, -0.27584, -0.736461] | Failed |
| case 6 | [0.967882, 0.247862, 0.004575, 0.04182] | [0.967882, 0.094358, 0.128105, 0.194656] | Failed |
| case 7 | [0.11186, -0.265105, 0.700516, -0.653058] | [0.11186, -0.653252, -0.222024, -0.71516] | Failed |
SHA-256 / 4205aa4d315cfb4874321baf67cce70757f7c6ae08f16ee61af3639daf8b13d1
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[0], -q[1], -q[2], -q[3]])
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.1116, -0.6859, -0.2807, 0.6621], [-1.367, -3.32, -3.869], 0.05, 1], [0.093483, 0.757592, 0.180891, -0.620156]], [[[0.5719, -0.201, -0.6787, -0.4146], [1.924, -1.133, -2.402], 0.05, 5], [0.357598, -0.191716, -0.546007, -0.732971]], [[[-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.5784, -0.2766, 0.5787, -0.504], [-2.529, -0.35, -1.088], 0.1, 1], [0.52115, -0.306506, 0.514969, -0.607671]], [[[0.5438, 0.3302, -0.6582, 0.4024], [-2.872, -3.023, 2.126], 0.05, 4], [0.303775, 0.162656, -0.567546, 0.747767]], [[[-0.4336, 0.4529, 0.2639, 0.733], [-3.578, -1.691, -2.103], 0.01, 1], [0.415442, -0.457106, -0.27584, -0.736461]], [[[-0.6434, -0.5565, -0.5031, -0.1523], [-2.902, -3.621, 0.012], 0.05, 6], [0.967882, 0.094358, 0.128105, 0.194656]], [[[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.3472, -0.3736, 0.4295, 0.7453], [-2.315, 1.527, -3.853], 0.01, 8], [0.287249, 0.223901, -0.5265, -0.768215]], [[[0.8268, -0.0559, 0.3935, 0.398], [-0.387, 0.922, 1.986], 0.1, 7], [0.220922, -0.271091, 0.534556, 0.769385]], [[[0.7724, -0.3363, 0.1754, -0.5095], [-1.034, -3.868, 2.146], 0.05, 4], [0.831714, -0.225919, -0.249805, -0.441373]], [[[0.6623, -0.6015, -0.0252, 0.4461], [1.478, -3.402, -2.007], 0.05, 6], [0.772394, -0.568265, -0.258655, -0.116536]], [[[-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.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.4376, -0.0327, 0.5947, -0.6736], [2.933, -2.297, 1.467], 0.01, 6], [0.507773, 0.026136, 0.618032, -0.5996]], [[[0.0263, -0.6798, 0.6943, 0.2347], [-2.851, -1.309, 1.331], 0.01, 4], [0.000571, 0.704341, -0.687306, -0.177522]]], [[[[-0.3438, -0.8064, 0.2793, -0.3918], [0.487, -0.414, 0.563], 0.05, 6], [0.231973, 0.824132, -0.258947, 0.447149]], [[[-0.3511, 0.4777, 0.5685, -0.5704], [2.489, 3.438, -0.071], 0.1, 2], [0.625498, -0.169104, -0.536584, 0.540586]], [[[0.5702, -0.6235, -0.2795, 0.4562], [-0.18, -0.187, 0.167], 0.1, 4], [0.520981, -0.650508, -0.304658, 0.461087]], [[[-0.5755, -0.1729, 0.1896, -0.7765], [-1.564, 1.954, -3.317], 0.01, 5], [0.652658, 0.171677, -0.144528, 0.723655]], [[[-0.3086, -0.4487, -0.811, 0.2137], [-0.353, -3.189, 2.11], 0.01, 10], [0.461721, 0.383888, 0.790513, -0.12055]], [[[-0.3137, -0.499, 0.6659, 0.4573], [0.29, 3.395, 1.611], 0.1, 5], [0.794564, 0.215018, 0.028732, -0.567106]], [[[-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.571, 0.1155, 0.5121, 0.6312], [2.085, -1.83, -1.285], 0.01, 1], [0.563386, -0.107047, -0.509932, -0.641176]]], [[[[0.5728, 0.3899, 0.4262, -0.5816], [-2.116, -3.996, -1.282], 0.05, 1], [0.613137, 0.428408, 0.323485, -0.579559]], [[[0.2873, -0.6346, 0.6402, -0.324], [3.029, -0.324, 1.16], 0.1, 8], [0.817944, -0.105726, 0.220751, 0.520633]], [[[0.2652, 0.6436, 0.4121, 0.588], [-2.665, 2.022, 0.007], 0.1, 11], [0.187909, -0.02187, 0.507266, -0.840769]], [[[0.2307, 0.3931, 0.7631, -0.4581], [2.174, 2.113, 3.444], 0.05, 10], [0.080385, 0.450009, -0.879659, -0.131262]], [[[0.3499, -0.381, 0.1815, -0.8363], [-2.42, 1.295, -0.715], 0.05, 10], [0.136518, 0.702138, 0.159756, 0.680326]], [[[0.346, -0.6937, -0.611, 0.1602], [-2.952, -1.698, -3.004], 0.1, 5], [0.363368, 0.923626, -0.121437, 0.011516]], [[[0.3265, 0.31, 0.1591, -0.8786], [-1.35, 2.136, -0.561], 0.01, 12], [0.297825, 0.173125, 0.117593, -0.931397]], [[[0.1401, -0.0998, 0.7197, 0.6726], [-3.242, -3.373, -3.717], 0.01, 11], [0.381235, -0.097044, 0.793491, 0.464343]]], [[[[-0.6115, -0.3532, -0.6797, -0.1982], [3.428, -1.888, 3.841], 0.05, 13], [0.244774, 0.22911, 0.220548, -0.915944]], [[[0.2915, -0.4846, 0.5466, 0.6176], [-2.376, 3.234, 3.373], 0.1, 13], [0.105141, -0.495302, 0.564739, 0.651683]], [[[-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.4693, 0.5928, -0.5163, -0.4022], [3.917, 1.937, -1.401], 0.05, 6], [0.62339, 0.001817, 0.422365, 0.658019]], [[[0.5273, 0.4224, -0.4746, 0.5641], [-2.08, 3.628, 3.896], 0.01, 13], [0.516939, 0.571342, -0.141768, 0.621485]], [[[-0.447, -0.6364, 0.627, 0.0463], [3.512, -0.033, 2.944], 0.05, 9], [0.164268, -0.967385, -0.052794, 0.185459]], [[[0.0332, -0.1691, -0.7704, -0.6138], [2.17, 2.479, 1.5], 0.1, 9], [0.881211, -0.074251, 0.351735, -0.30698]], [[[0.1785, -0.1228, -0.7653, 0.6061], [2.105, 0.281, 2.09], 0.01, 8], [0.145584, -0.036317, -0.819027, 0.553788]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| case 0 | [0.127308, 0.592555, 0.355948, -0.711317] | [0.093483, 0.757592, 0.180891, -0.620156] | Failed |
| case 1 | [0.620567, 0.101272, -0.705695, -0.326552] | [0.357598, -0.191716, -0.546007, -0.732971] | Failed |
| case 2 | [0.162392, 0.209161, -0.637098, 0.723869] | [0.198164, 0.048329, -0.670767, 0.713069] | Failed |
| case 3 | [0.590636, -0.365283, 0.578083, -0.428412] | [0.52115, -0.306506, 0.514969, -0.607671] | Failed |
| case 4 | [0.605121, 0.066584, -0.772712, 0.179754] | [0.303775, 0.162656, -0.567546, 0.747767] | Failed |
| case 5 | [0.444616, -0.456861, -0.255183, -0.726964] | [0.415442, -0.457106, -0.27584, -0.736461] | Failed |
| case 6 | [0.449778, 0.797989, 0.392778, 0.081481] | [0.967882, 0.094358, 0.128105, 0.194656] | Failed |
| case 7 | [0.393316, 0.190007, -0.59746, 0.672489] | [0.11186, -0.653252, -0.222024, -0.71516] | Failed |
SHA-256 / caf1c84deebb4427a076323d76527f537250d7512e860c7ea5a8c38a3e4add35
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.1116, -0.6859, -0.2807, 0.6621], [-1.367, -3.32, -3.869], 0.05, 1], [0.093483, 0.757592, 0.180891, -0.620156]], [[[0.5719, -0.201, -0.6787, -0.4146], [1.924, -1.133, -2.402], 0.05, 5], [0.357598, -0.191716, -0.546007, -0.732971]], [[[-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.5784, -0.2766, 0.5787, -0.504], [-2.529, -0.35, -1.088], 0.1, 1], [0.52115, -0.306506, 0.514969, -0.607671]], [[[0.5438, 0.3302, -0.6582, 0.4024], [-2.872, -3.023, 2.126], 0.05, 4], [0.303775, 0.162656, -0.567546, 0.747767]], [[[-0.4336, 0.4529, 0.2639, 0.733], [-3.578, -1.691, -2.103], 0.01, 1], [0.415442, -0.457106, -0.27584, -0.736461]], [[[-0.6434, -0.5565, -0.5031, -0.1523], [-2.902, -3.621, 0.012], 0.05, 6], [0.967882, 0.094358, 0.128105, 0.194656]], [[[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.3472, -0.3736, 0.4295, 0.7453], [-2.315, 1.527, -3.853], 0.01, 8], [0.287249, 0.223901, -0.5265, -0.768215]], [[[0.8268, -0.0559, 0.3935, 0.398], [-0.387, 0.922, 1.986], 0.1, 7], [0.220922, -0.271091, 0.534556, 0.769385]], [[[0.7724, -0.3363, 0.1754, -0.5095], [-1.034, -3.868, 2.146], 0.05, 4], [0.831714, -0.225919, -0.249805, -0.441373]], [[[0.6623, -0.6015, -0.0252, 0.4461], [1.478, -3.402, -2.007], 0.05, 6], [0.772394, -0.568265, -0.258655, -0.116536]], [[[-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.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.4376, -0.0327, 0.5947, -0.6736], [2.933, -2.297, 1.467], 0.01, 6], [0.507773, 0.026136, 0.618032, -0.5996]], [[[0.0263, -0.6798, 0.6943, 0.2347], [-2.851, -1.309, 1.331], 0.01, 4], [0.000571, 0.704341, -0.687306, -0.177522]]], [[[[-0.3438, -0.8064, 0.2793, -0.3918], [0.487, -0.414, 0.563], 0.05, 6], [0.231973, 0.824132, -0.258947, 0.447149]], [[[-0.3511, 0.4777, 0.5685, -0.5704], [2.489, 3.438, -0.071], 0.1, 2], [0.625498, -0.169104, -0.536584, 0.540586]], [[[0.5702, -0.6235, -0.2795, 0.4562], [-0.18, -0.187, 0.167], 0.1, 4], [0.520981, -0.650508, -0.304658, 0.461087]], [[[-0.5755, -0.1729, 0.1896, -0.7765], [-1.564, 1.954, -3.317], 0.01, 5], [0.652658, 0.171677, -0.144528, 0.723655]], [[[-0.3086, -0.4487, -0.811, 0.2137], [-0.353, -3.189, 2.11], 0.01, 10], [0.461721, 0.383888, 0.790513, -0.12055]], [[[-0.3137, -0.499, 0.6659, 0.4573], [0.29, 3.395, 1.611], 0.1, 5], [0.794564, 0.215018, 0.028732, -0.567106]], [[[-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.571, 0.1155, 0.5121, 0.6312], [2.085, -1.83, -1.285], 0.01, 1], [0.563386, -0.107047, -0.509932, -0.641176]]], [[[[0.5728, 0.3899, 0.4262, -0.5816], [-2.116, -3.996, -1.282], 0.05, 1], [0.613137, 0.428408, 0.323485, -0.579559]], [[[0.2873, -0.6346, 0.6402, -0.324], [3.029, -0.324, 1.16], 0.1, 8], [0.817944, -0.105726, 0.220751, 0.520633]], [[[0.2652, 0.6436, 0.4121, 0.588], [-2.665, 2.022, 0.007], 0.1, 11], [0.187909, -0.02187, 0.507266, -0.840769]], [[[0.2307, 0.3931, 0.7631, -0.4581], [2.174, 2.113, 3.444], 0.05, 10], [0.080385, 0.450009, -0.879659, -0.131262]], [[[0.3499, -0.381, 0.1815, -0.8363], [-2.42, 1.295, -0.715], 0.05, 10], [0.136518, 0.702138, 0.159756, 0.680326]], [[[0.346, -0.6937, -0.611, 0.1602], [-2.952, -1.698, -3.004], 0.1, 5], [0.363368, 0.923626, -0.121437, 0.011516]], [[[0.3265, 0.31, 0.1591, -0.8786], [-1.35, 2.136, -0.561], 0.01, 12], [0.297825, 0.173125, 0.117593, -0.931397]], [[[0.1401, -0.0998, 0.7197, 0.6726], [-3.242, -3.373, -3.717], 0.01, 11], [0.381235, -0.097044, 0.793491, 0.464343]]], [[[[-0.6115, -0.3532, -0.6797, -0.1982], [3.428, -1.888, 3.841], 0.05, 13], [0.244774, 0.22911, 0.220548, -0.915944]], [[[0.2915, -0.4846, 0.5466, 0.6176], [-2.376, 3.234, 3.373], 0.1, 13], [0.105141, -0.495302, 0.564739, 0.651683]], [[[-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.4693, 0.5928, -0.5163, -0.4022], [3.917, 1.937, -1.401], 0.05, 6], [0.62339, 0.001817, 0.422365, 0.658019]], [[[0.5273, 0.4224, -0.4746, 0.5641], [-2.08, 3.628, 3.896], 0.01, 13], [0.516939, 0.571342, -0.141768, 0.621485]], [[[-0.447, -0.6364, 0.627, 0.0463], [3.512, -0.033, 2.944], 0.05, 9], [0.164268, -0.967385, -0.052794, 0.185459]], [[[0.0332, -0.1691, -0.7704, -0.6138], [2.17, 2.479, 1.5], 0.1, 9], [0.881211, -0.074251, 0.351735, -0.30698]], [[[0.1785, -0.1228, -0.7653, 0.6061], [2.105, 0.281, 2.09], 0.01, 8], [0.145584, -0.036317, -0.819027, 0.553788]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| case 0 | [0.093483, 0.757592, 0.180891, -0.620156] | [0.093483, 0.757592, 0.180891, -0.620156] | Passed |
| case 1 | [0.357598, -0.191716, -0.546007, -0.732971] | [0.357598, -0.191716, -0.546007, -0.732971] | Passed |
| case 2 | [0.198164, 0.048329, -0.670767, 0.713069] | [0.198164, 0.048329, -0.670767, 0.713069] | Passed |
| case 3 | [0.52115, -0.306506, 0.514969, -0.607671] | [0.52115, -0.306506, 0.514969, -0.607671] | Passed |
| case 4 | [0.303775, 0.162656, -0.567546, 0.747767] | [0.303775, 0.162656, -0.567546, 0.747767] | Passed |
| case 5 | [0.415442, -0.457106, -0.27584, -0.736461] | [0.415442, -0.457106, -0.27584, -0.736461] | Passed |
| case 6 | [0.967882, 0.094358, 0.128105, 0.194656] | [0.967882, 0.094358, 0.128105, 0.194656] | Passed |
| case 7 | [0.11186, -0.653252, -0.222024, -0.71516] | [0.11186, -0.653252, -0.222024, -0.71516] | Passed |
SHA-256 / 7983ebc79d1530d2714abdfc0e0bb8fc23995059d0754bb6be544cd557eb1954
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.264686+00:00.
Case digest / 87921a02547c754211fd09b6df33687dc2c66ab09eb6b5026d98f6bab1ef31bb