FA-87226 / Physics integrator stability / Open access
Warm-start impulses are stored but never applied to velocities · case 01
The solver double-applies impulses and contacts bounce on the frame after warm starting.
ROOT CAUSE
The loop that applies cached impulses to vn and vt is missing.
VERIFIED REPAIR
Apply warm-started impulses to the velocities before iterating.
Unsuccessful approach: Applying only the normal warm start leaves tangential velocity inconsistent with jt.
Case contract
solve(vn0, vt0, w, mu, iterations, cached, warm): several contact points share one relative normal and tangent velocity with inverse mass w. Warm start each point with warm*cached [jn, jt] and apply it to the velocities. Each iteration visits points in order: friction first (accumulated jt clamped to |jt|<=mu*jn), then normal (accumulated jn clamped >=0). Return [jn list, jt list, vn, vt] 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(vn0, vt0, w, mu, iterations, cached, warm):
vn, vt = vn0, vt0
jn = [p[0] * warm for p in cached]
jt = [p[1] * warm for p in cached]
for _ in range(iterations):
for i in range(len(cached)):
d = -vt / w
old = jt[i]
lim = mu * jn[i]
jt[i] = max(-lim, min(lim, old + d))
vt += (jt[i] - old) * w
d = -vn / w
old = jn[i]
jn[i] = max(0.0, old + d)
vn += (jn[i] - old) * w
return [[round(a, 6) for a in jn], [round(b, 6) for b in jt], round(vn, 6), round(vt, 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[-2.247, 0.38, 1.395, 0.904, 3, [[1.169, 0.029], [0.767, 0.429], [1.803, -0.227]], 1.0], [[0.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0]], [[-0.196, -1.872, 0.742, 0.597, 1, [[0.945, -0.432]], 1.0], [[0.264151], [0.564165], 0.0, -1.45339]], [[-2.047, 0.542, 1.648, 0.962, 3, [[0.683, -0.852]], 0.0], [[1.242112], [-0.328883], 0.0, 0.0]], [[-3.886, 1.26, 0.358, 0.718, 2, [[0.002, 0.092]], 1.0], [[10.854749], [-3.519553], 0.0, 0.0]], [[-0.408, -2.628, 1.437, 0.602, 2, [[0.638, -0.927], [0.391, 0.996], [0.272, 0.06]], 0.8], [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384]], [[-2.312, 0.747, 1.274, 0.067, 4, [[0.842, -0.863], [1.322, 0.37]], 0.0], [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096]], [[-0.424, 1.049, 0.342, 0.152, 4, [[0.548, 0.136], [0.738, 0.085], [0.894, -0.384]], 0.0], [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552]], [[-1.029, -2.814, 0.539, 0.151, 2, [[1.249, 0.914], [1.809, -0.191], [0.04, -0.518]], 0.0], [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621]]], [[[-1.961, 0.409, 1.562, 0.164, 4, [[1.936, 0.34]], 0.8], [[1.255442], [-0.205892], 0.0, 0.087396]], [[-1.641, 1.073, 0.215, 0.423, 3, [[0.042, 0.969]], 0.5], [[7.632558], [-3.228572], 0.0, 0.378857]], [[-2.77, -2.157, 1.401, 0.492, 2, [[1.174, 0.24], [0.014, 0.755]], 0.0], [[1.977159, 0.0], [0.972762, -0.0], 0.0, -0.79416]], [[-3.3, 1.782, 0.791, 0.074, 3, [[1.882, -0.806], [1.437, 0.743], [1.142, 0.126]], 0.0], [[4.171934, 0.0, 0.0], [-0.308723, -0.0, -0.0], 0.0, 1.5378]], [[-0.995, 2.177, 1.091, 0.541, 3, [[0.575, 0.478], [1.81, 0.14]], 1.0], [[0.0, 0.912007], [-0.0, -0.493396], 0.0, 1.638705]], [[-0.408, 1.907, 0.876, 0.933, 3, [[1.921, 0.676]], 1.0], [[0.465753], [-0.434548], 0.0, 1.526336]], [[-3.08, 1.655, 1.199, 0.534, 4, [[1.868, 0.117], [0.808, 0.716], [1.084, 0.528]], 0.5], [[1.622807, 0.404, 0.542], [-0.866579, -0.215736, -0.289428], 0.0, 0.01028]], [[-0.817, -1.918, 1.765, 0.758, 1, [[1.446, 0.713]], 0.0], [[0.46289], [-0.0], 0.0, -1.918]]], [[[-0.96, 2.332, 1.598, 0.337, 4, [[0.319, 0.721]], 0.5], [[0.600751], [-0.202453], 0.0, 2.00848]], [[-3.772, -2.956, 1.951, 0.596, 2, [[0.99, 0.015], [1.312, 0.389], [0.881, -0.19]], 0.0], [[1.933368, 0.0, 0.0], [1.152287, -0.0, -0.0], 0.0, -0.707888]], [[0.425, -2.924, 1.923, 0.354, 3, [[1.603, 0.918]], 0.0], [[0.0], [-0.0], 0.425, -2.924]], [[-2.965, 0.007, 0.968, 0.693, 1, [[0.12, -0.839], [1.259, 0.044], [0.007, -0.589]], 0.0], [[3.063017, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.0, 0.007]], [[0.155, -2.126, 1.449, 0.01, 2, [[1.311, 0.067]], 0.0], [[0.0], [-0.0], 0.155, -2.126]], [[-3.82, -2.511, 1.521, 0.68, 3, [[1.747, -0.957], [0.325, 0.241], [0.265, -0.616]], 0.5], [[2.216506, 0.1625, 0.1325], [1.450288, 0.1105, 0.0901], 0.0, 0.0]], [[0.156, 2.713, 0.501, 0.188, 2, [[0.708, 0.64], [0.759, 0.228]], 0.5], [[0.0, 0.0], [-0.0, -0.0], 0.156, 2.713]], [[-1.764, 0.95, 0.746, 0.826, 2, [[1.55, -0.124], [0.697, 0.933]], 0.5], [[2.016111, 0.3485], [-0.985597, -0.287861], -0.0, 0.0]]], [[[-1.165, 1.112, 1.201, 0.96, 2, [[1.899, -0.847], [1.9, 0.505]], 1.0], [[0.0, 0.970025], [-0.0, -0.925895], 0.0, 0.0]], [[-2.639, 2.8, 0.999, 0.853, 2, [[0.088, 0.752], [1.652, -0.833], [0.986, -0.298]], 0.8], [[0.531242, 1.3216, 0.7888], [-0.453149, -1.127325, -0.672846], 0.0, 0.548933]], [[-2.578, -2.966, 0.849, 0.64, 2, [[1.16, 0.082]], 0.0], [[3.036514], [1.943369], 0.0, -1.31608]], [[-2.744, -1.245, 1.139, 0.752, 4, [[0.428, -0.831], [0.066, 0.506], [1.83, -0.485]], 0.0], [[2.409131, 0.0, 0.0], [1.093064, -0.0, -0.0], 0.0, 0.0]], [[0.555, 0.388, 0.487, 0.266, 2, [[1.252, 0.927], [0.701, -0.848], [0.227, 0.684]], 0.5], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.555, 0.388]], [[-1.311, -0.201, 1.222, 0.493, 1, [[0.285, -0.793], [0.403, 0.853]], 1.0], [[0.669831, 0.403], [-0.140505, 0.198679], 0.0, -0.129911]], [[-3.078, 1.674, 1.261, 0.594, 2, [[0.82, 0.87], [0.253, -0.273], [0.726, -0.62]], 0.5], [[1.95142, 0.1265, 0.363], [-1.036755, -0.075141, -0.215622], 0.0, 0.0]], [[0.283, -1.445, 1.89, 0.821, 2, [[0.105, 0.547], [1.407, -0.066], [1.234, -0.397]], 0.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.283, -1.445]]], [[[-1.921, -2.28, 1.573, 0.248, 1, [[0.936, -0.469], [1.364, -0.177], [0.681, -0.823]], 0.8], [[0.0, 0.676433, 0.5448], [0.185702, 0.270618, 0.13511], 0.0, -1.34968]], [[-3.102, 2.687, 0.678, 0.828, 3, [[1.24, -0.865], [0.364, 0.726]], 0.8], [[4.284021, 0.2912], [-3.54717, -0.241114], 0.0, 0.118544]], [[-0.343, 2.067, 1.725, 0.908, 2, [[1.305, 0.893], [0.002, 0.214]], 0.5], [[0.197841, 0.001], [-0.179639, -0.000908], 0.0, 1.755556]], [[-3.372, -0.912, 0.24, 0.634, 4, [[1.541, -0.398]], 0.0], [[14.05], [3.8], 0.0, 0.0]], [[-3.985, -2.001, 0.801, 0.762, 2, [[0.335, 0.398], [1.913, 0.838], [0.385, -0.769]], 0.0], [[4.975031, 0.0, 0.0], [2.498127, -0.0, -0.0], 0.0, 0.0]], [[-2.401, -2.168, 0.734, 0.846, 3, [[0.732, -0.632]], 0.5], [[3.271117], [2.767365], 0.0, -0.136754]], [[-2.797, -1.862, 1.127, 0.79, 2, [[1.996, -0.737], [1.165, -0.06]], 0.0], [[2.48181, 0.0], [1.652174, -0.0], 0.0, 0.0]], [[-0.464, 1.964, 1.129, 0.016, 2, [[1.578, 0.441]], 0.5], [[0.410983], [-0.006576], 0.0, 1.956576]]]]
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 | [[2.779753, 0.767, 1.803], [-0.243401, 0.429, -0.227], -0.0, 0.0] | [[0.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0] | Failed |
| case 1 | [[1.209151], [0.564165], 0.0, -1.132846] | [[0.264151], [0.564165], 0.0, -1.45339] | Failed |
| case 2 | [[1.242112], [-0.328883], 0.0, 0.0] | [[1.242112], [-0.328883], 0.0, 0.0] | Passed |
| case 3 | [[10.856749], [-3.427553], 0.0, 0.0] | [[10.854749], [-3.519553], 0.0, 0.0] | Failed |
| case 4 | [[0.794325, 0.3128, 0.2176], [0.478184, 0.188306, 0.130995], -0.0, -1.630313] | [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384] | Failed |
| case 5 | [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096] | [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096] | Passed |
| case 6 | [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552] | [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552] | Passed |
| case 7 | [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621] | [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621] | Passed |
SHA-256 / a78293b180c79d9235189d56bb7100f45ae2e442d19683fced85c4a62ddda381
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(vn0, vt0, w, mu, iterations, cached, warm):
vn, vt = vn0, vt0
jn = [p[0] * warm for p in cached]
jt = [p[1] * warm for p in cached]
for i in range(len(cached)):
vn += jn[i] * w
for _ in range(iterations):
for i in range(len(cached)):
d = -vt / w
old = jt[i]
lim = mu * jn[i]
jt[i] = max(-lim, min(lim, old + d))
vt += (jt[i] - old) * w
d = -vn / w
old = jn[i]
jn[i] = max(0.0, old + d)
vn += (jn[i] - old) * w
return [[round(a, 6) for a in jn], [round(b, 6) for b in jt], round(vn, 6), round(vt, 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[-2.247, 0.38, 1.395, 0.904, 3, [[1.169, 0.029], [0.767, 0.429], [1.803, -0.227]], 1.0], [[0.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0]], [[-0.196, -1.872, 0.742, 0.597, 1, [[0.945, -0.432]], 1.0], [[0.264151], [0.564165], 0.0, -1.45339]], [[-2.047, 0.542, 1.648, 0.962, 3, [[0.683, -0.852]], 0.0], [[1.242112], [-0.328883], 0.0, 0.0]], [[-3.886, 1.26, 0.358, 0.718, 2, [[0.002, 0.092]], 1.0], [[10.854749], [-3.519553], 0.0, 0.0]], [[-0.408, -2.628, 1.437, 0.602, 2, [[0.638, -0.927], [0.391, 0.996], [0.272, 0.06]], 0.8], [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384]], [[-2.312, 0.747, 1.274, 0.067, 4, [[0.842, -0.863], [1.322, 0.37]], 0.0], [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096]], [[-0.424, 1.049, 0.342, 0.152, 4, [[0.548, 0.136], [0.738, 0.085], [0.894, -0.384]], 0.0], [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552]], [[-1.029, -2.814, 0.539, 0.151, 2, [[1.249, 0.914], [1.809, -0.191], [0.04, -0.518]], 0.0], [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621]]], [[[-1.961, 0.409, 1.562, 0.164, 4, [[1.936, 0.34]], 0.8], [[1.255442], [-0.205892], 0.0, 0.087396]], [[-1.641, 1.073, 0.215, 0.423, 3, [[0.042, 0.969]], 0.5], [[7.632558], [-3.228572], 0.0, 0.378857]], [[-2.77, -2.157, 1.401, 0.492, 2, [[1.174, 0.24], [0.014, 0.755]], 0.0], [[1.977159, 0.0], [0.972762, -0.0], 0.0, -0.79416]], [[-3.3, 1.782, 0.791, 0.074, 3, [[1.882, -0.806], [1.437, 0.743], [1.142, 0.126]], 0.0], [[4.171934, 0.0, 0.0], [-0.308723, -0.0, -0.0], 0.0, 1.5378]], [[-0.995, 2.177, 1.091, 0.541, 3, [[0.575, 0.478], [1.81, 0.14]], 1.0], [[0.0, 0.912007], [-0.0, -0.493396], 0.0, 1.638705]], [[-0.408, 1.907, 0.876, 0.933, 3, [[1.921, 0.676]], 1.0], [[0.465753], [-0.434548], 0.0, 1.526336]], [[-3.08, 1.655, 1.199, 0.534, 4, [[1.868, 0.117], [0.808, 0.716], [1.084, 0.528]], 0.5], [[1.622807, 0.404, 0.542], [-0.866579, -0.215736, -0.289428], 0.0, 0.01028]], [[-0.817, -1.918, 1.765, 0.758, 1, [[1.446, 0.713]], 0.0], [[0.46289], [-0.0], 0.0, -1.918]]], [[[-0.96, 2.332, 1.598, 0.337, 4, [[0.319, 0.721]], 0.5], [[0.600751], [-0.202453], 0.0, 2.00848]], [[-3.772, -2.956, 1.951, 0.596, 2, [[0.99, 0.015], [1.312, 0.389], [0.881, -0.19]], 0.0], [[1.933368, 0.0, 0.0], [1.152287, -0.0, -0.0], 0.0, -0.707888]], [[0.425, -2.924, 1.923, 0.354, 3, [[1.603, 0.918]], 0.0], [[0.0], [-0.0], 0.425, -2.924]], [[-2.965, 0.007, 0.968, 0.693, 1, [[0.12, -0.839], [1.259, 0.044], [0.007, -0.589]], 0.0], [[3.063017, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.0, 0.007]], [[0.155, -2.126, 1.449, 0.01, 2, [[1.311, 0.067]], 0.0], [[0.0], [-0.0], 0.155, -2.126]], [[-3.82, -2.511, 1.521, 0.68, 3, [[1.747, -0.957], [0.325, 0.241], [0.265, -0.616]], 0.5], [[2.216506, 0.1625, 0.1325], [1.450288, 0.1105, 0.0901], 0.0, 0.0]], [[0.156, 2.713, 0.501, 0.188, 2, [[0.708, 0.64], [0.759, 0.228]], 0.5], [[0.0, 0.0], [-0.0, -0.0], 0.156, 2.713]], [[-1.764, 0.95, 0.746, 0.826, 2, [[1.55, -0.124], [0.697, 0.933]], 0.5], [[2.016111, 0.3485], [-0.985597, -0.287861], -0.0, 0.0]]], [[[-1.165, 1.112, 1.201, 0.96, 2, [[1.899, -0.847], [1.9, 0.505]], 1.0], [[0.0, 0.970025], [-0.0, -0.925895], 0.0, 0.0]], [[-2.639, 2.8, 0.999, 0.853, 2, [[0.088, 0.752], [1.652, -0.833], [0.986, -0.298]], 0.8], [[0.531242, 1.3216, 0.7888], [-0.453149, -1.127325, -0.672846], 0.0, 0.548933]], [[-2.578, -2.966, 0.849, 0.64, 2, [[1.16, 0.082]], 0.0], [[3.036514], [1.943369], 0.0, -1.31608]], [[-2.744, -1.245, 1.139, 0.752, 4, [[0.428, -0.831], [0.066, 0.506], [1.83, -0.485]], 0.0], [[2.409131, 0.0, 0.0], [1.093064, -0.0, -0.0], 0.0, 0.0]], [[0.555, 0.388, 0.487, 0.266, 2, [[1.252, 0.927], [0.701, -0.848], [0.227, 0.684]], 0.5], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.555, 0.388]], [[-1.311, -0.201, 1.222, 0.493, 1, [[0.285, -0.793], [0.403, 0.853]], 1.0], [[0.669831, 0.403], [-0.140505, 0.198679], 0.0, -0.129911]], [[-3.078, 1.674, 1.261, 0.594, 2, [[0.82, 0.87], [0.253, -0.273], [0.726, -0.62]], 0.5], [[1.95142, 0.1265, 0.363], [-1.036755, -0.075141, -0.215622], 0.0, 0.0]], [[0.283, -1.445, 1.89, 0.821, 2, [[0.105, 0.547], [1.407, -0.066], [1.234, -0.397]], 0.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.283, -1.445]]], [[[-1.921, -2.28, 1.573, 0.248, 1, [[0.936, -0.469], [1.364, -0.177], [0.681, -0.823]], 0.8], [[0.0, 0.676433, 0.5448], [0.185702, 0.270618, 0.13511], 0.0, -1.34968]], [[-3.102, 2.687, 0.678, 0.828, 3, [[1.24, -0.865], [0.364, 0.726]], 0.8], [[4.284021, 0.2912], [-3.54717, -0.241114], 0.0, 0.118544]], [[-0.343, 2.067, 1.725, 0.908, 2, [[1.305, 0.893], [0.002, 0.214]], 0.5], [[0.197841, 0.001], [-0.179639, -0.000908], 0.0, 1.755556]], [[-3.372, -0.912, 0.24, 0.634, 4, [[1.541, -0.398]], 0.0], [[14.05], [3.8], 0.0, 0.0]], [[-3.985, -2.001, 0.801, 0.762, 2, [[0.335, 0.398], [1.913, 0.838], [0.385, -0.769]], 0.0], [[4.975031, 0.0, 0.0], [2.498127, -0.0, -0.0], 0.0, 0.0]], [[-2.401, -2.168, 0.734, 0.846, 3, [[0.732, -0.632]], 0.5], [[3.271117], [2.767365], 0.0, -0.136754]], [[-2.797, -1.862, 1.127, 0.79, 2, [[1.996, -0.737], [1.165, -0.06]], 0.0], [[2.48181, 0.0], [1.652174, -0.0], 0.0, 0.0]], [[-0.464, 1.964, 1.129, 0.016, 2, [[1.578, 0.441]], 0.5], [[0.410983], [-0.006576], 0.0, 1.956576]]]]
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.0, 0.0, 1.610753], [-0.0, -0.0, -0.041401], 0.0, 0.0] | [[0.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0] | Failed |
| case 1 | [[0.264151], [0.564165], 0.0, -1.132846] | [[0.264151], [0.564165], 0.0, -1.45339] | Failed |
| case 2 | [[1.242112], [-0.328883], 0.0, 0.0] | [[1.242112], [-0.328883], 0.0, 0.0] | Passed |
| case 3 | [[10.854749], [-3.427553], 0.0, 0.0] | [[10.854749], [-3.519553], 0.0, 0.0] | Failed |
| case 4 | [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.530682] | [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384] | Failed |
| case 5 | [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096] | [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096] | Passed |
| case 6 | [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552] | [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552] | Passed |
| case 7 | [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621] | [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621] | Passed |
SHA-256 / 60bc9fa24093a76848bdef126689d03572addbcea80d7cbc7306db15a503e7f2
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(vn0, vt0, w, mu, iterations, cached, warm):
vn, vt = vn0, vt0
jn = [p[0] * warm for p in cached]
jt = [p[1] * warm for p in cached]
for i in range(len(cached)):
vn += jn[i] * w
vt += jt[i] * w
for _ in range(iterations):
for i in range(len(cached)):
d = -vt / w
old = jt[i]
lim = mu * jn[i]
jt[i] = max(-lim, min(lim, old + d))
vt += (jt[i] - old) * w
d = -vn / w
old = jn[i]
jn[i] = max(0.0, old + d)
vn += (jn[i] - old) * w
return [[round(a, 6) for a in jn], [round(b, 6) for b in jt], round(vn, 6), round(vt, 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[-2.247, 0.38, 1.395, 0.904, 3, [[1.169, 0.029], [0.767, 0.429], [1.803, -0.227]], 1.0], [[0.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0]], [[-0.196, -1.872, 0.742, 0.597, 1, [[0.945, -0.432]], 1.0], [[0.264151], [0.564165], 0.0, -1.45339]], [[-2.047, 0.542, 1.648, 0.962, 3, [[0.683, -0.852]], 0.0], [[1.242112], [-0.328883], 0.0, 0.0]], [[-3.886, 1.26, 0.358, 0.718, 2, [[0.002, 0.092]], 1.0], [[10.854749], [-3.519553], 0.0, 0.0]], [[-0.408, -2.628, 1.437, 0.602, 2, [[0.638, -0.927], [0.391, 0.996], [0.272, 0.06]], 0.8], [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384]], [[-2.312, 0.747, 1.274, 0.067, 4, [[0.842, -0.863], [1.322, 0.37]], 0.0], [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096]], [[-0.424, 1.049, 0.342, 0.152, 4, [[0.548, 0.136], [0.738, 0.085], [0.894, -0.384]], 0.0], [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552]], [[-1.029, -2.814, 0.539, 0.151, 2, [[1.249, 0.914], [1.809, -0.191], [0.04, -0.518]], 0.0], [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621]]], [[[-1.961, 0.409, 1.562, 0.164, 4, [[1.936, 0.34]], 0.8], [[1.255442], [-0.205892], 0.0, 0.087396]], [[-1.641, 1.073, 0.215, 0.423, 3, [[0.042, 0.969]], 0.5], [[7.632558], [-3.228572], 0.0, 0.378857]], [[-2.77, -2.157, 1.401, 0.492, 2, [[1.174, 0.24], [0.014, 0.755]], 0.0], [[1.977159, 0.0], [0.972762, -0.0], 0.0, -0.79416]], [[-3.3, 1.782, 0.791, 0.074, 3, [[1.882, -0.806], [1.437, 0.743], [1.142, 0.126]], 0.0], [[4.171934, 0.0, 0.0], [-0.308723, -0.0, -0.0], 0.0, 1.5378]], [[-0.995, 2.177, 1.091, 0.541, 3, [[0.575, 0.478], [1.81, 0.14]], 1.0], [[0.0, 0.912007], [-0.0, -0.493396], 0.0, 1.638705]], [[-0.408, 1.907, 0.876, 0.933, 3, [[1.921, 0.676]], 1.0], [[0.465753], [-0.434548], 0.0, 1.526336]], [[-3.08, 1.655, 1.199, 0.534, 4, [[1.868, 0.117], [0.808, 0.716], [1.084, 0.528]], 0.5], [[1.622807, 0.404, 0.542], [-0.866579, -0.215736, -0.289428], 0.0, 0.01028]], [[-0.817, -1.918, 1.765, 0.758, 1, [[1.446, 0.713]], 0.0], [[0.46289], [-0.0], 0.0, -1.918]]], [[[-0.96, 2.332, 1.598, 0.337, 4, [[0.319, 0.721]], 0.5], [[0.600751], [-0.202453], 0.0, 2.00848]], [[-3.772, -2.956, 1.951, 0.596, 2, [[0.99, 0.015], [1.312, 0.389], [0.881, -0.19]], 0.0], [[1.933368, 0.0, 0.0], [1.152287, -0.0, -0.0], 0.0, -0.707888]], [[0.425, -2.924, 1.923, 0.354, 3, [[1.603, 0.918]], 0.0], [[0.0], [-0.0], 0.425, -2.924]], [[-2.965, 0.007, 0.968, 0.693, 1, [[0.12, -0.839], [1.259, 0.044], [0.007, -0.589]], 0.0], [[3.063017, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.0, 0.007]], [[0.155, -2.126, 1.449, 0.01, 2, [[1.311, 0.067]], 0.0], [[0.0], [-0.0], 0.155, -2.126]], [[-3.82, -2.511, 1.521, 0.68, 3, [[1.747, -0.957], [0.325, 0.241], [0.265, -0.616]], 0.5], [[2.216506, 0.1625, 0.1325], [1.450288, 0.1105, 0.0901], 0.0, 0.0]], [[0.156, 2.713, 0.501, 0.188, 2, [[0.708, 0.64], [0.759, 0.228]], 0.5], [[0.0, 0.0], [-0.0, -0.0], 0.156, 2.713]], [[-1.764, 0.95, 0.746, 0.826, 2, [[1.55, -0.124], [0.697, 0.933]], 0.5], [[2.016111, 0.3485], [-0.985597, -0.287861], -0.0, 0.0]]], [[[-1.165, 1.112, 1.201, 0.96, 2, [[1.899, -0.847], [1.9, 0.505]], 1.0], [[0.0, 0.970025], [-0.0, -0.925895], 0.0, 0.0]], [[-2.639, 2.8, 0.999, 0.853, 2, [[0.088, 0.752], [1.652, -0.833], [0.986, -0.298]], 0.8], [[0.531242, 1.3216, 0.7888], [-0.453149, -1.127325, -0.672846], 0.0, 0.548933]], [[-2.578, -2.966, 0.849, 0.64, 2, [[1.16, 0.082]], 0.0], [[3.036514], [1.943369], 0.0, -1.31608]], [[-2.744, -1.245, 1.139, 0.752, 4, [[0.428, -0.831], [0.066, 0.506], [1.83, -0.485]], 0.0], [[2.409131, 0.0, 0.0], [1.093064, -0.0, -0.0], 0.0, 0.0]], [[0.555, 0.388, 0.487, 0.266, 2, [[1.252, 0.927], [0.701, -0.848], [0.227, 0.684]], 0.5], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.555, 0.388]], [[-1.311, -0.201, 1.222, 0.493, 1, [[0.285, -0.793], [0.403, 0.853]], 1.0], [[0.669831, 0.403], [-0.140505, 0.198679], 0.0, -0.129911]], [[-3.078, 1.674, 1.261, 0.594, 2, [[0.82, 0.87], [0.253, -0.273], [0.726, -0.62]], 0.5], [[1.95142, 0.1265, 0.363], [-1.036755, -0.075141, -0.215622], 0.0, 0.0]], [[0.283, -1.445, 1.89, 0.821, 2, [[0.105, 0.547], [1.407, -0.066], [1.234, -0.397]], 0.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.283, -1.445]]], [[[-1.921, -2.28, 1.573, 0.248, 1, [[0.936, -0.469], [1.364, -0.177], [0.681, -0.823]], 0.8], [[0.0, 0.676433, 0.5448], [0.185702, 0.270618, 0.13511], 0.0, -1.34968]], [[-3.102, 2.687, 0.678, 0.828, 3, [[1.24, -0.865], [0.364, 0.726]], 0.8], [[4.284021, 0.2912], [-3.54717, -0.241114], 0.0, 0.118544]], [[-0.343, 2.067, 1.725, 0.908, 2, [[1.305, 0.893], [0.002, 0.214]], 0.5], [[0.197841, 0.001], [-0.179639, -0.000908], 0.0, 1.755556]], [[-3.372, -0.912, 0.24, 0.634, 4, [[1.541, -0.398]], 0.0], [[14.05], [3.8], 0.0, 0.0]], [[-3.985, -2.001, 0.801, 0.762, 2, [[0.335, 0.398], [1.913, 0.838], [0.385, -0.769]], 0.0], [[4.975031, 0.0, 0.0], [2.498127, -0.0, -0.0], 0.0, 0.0]], [[-2.401, -2.168, 0.734, 0.846, 3, [[0.732, -0.632]], 0.5], [[3.271117], [2.767365], 0.0, -0.136754]], [[-2.797, -1.862, 1.127, 0.79, 2, [[1.996, -0.737], [1.165, -0.06]], 0.0], [[2.48181, 0.0], [1.652174, -0.0], 0.0, 0.0]], [[-0.464, 1.964, 1.129, 0.016, 2, [[1.578, 0.441]], 0.5], [[0.410983], [-0.006576], 0.0, 1.956576]]]]
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.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0] | [[0.0, 0.0, 1.610753], [-0.0, -0.0, -0.272401], 0.0, 0.0] | Passed |
| case 1 | [[0.264151], [0.564165], 0.0, -1.45339] | [[0.264151], [0.564165], 0.0, -1.45339] | Passed |
| case 2 | [[1.242112], [-0.328883], 0.0, 0.0] | [[1.242112], [-0.328883], 0.0, 0.0] | Passed |
| case 3 | [[10.854749], [-3.519553], 0.0, 0.0] | [[10.854749], [-3.519553], 0.0, 0.0] | Passed |
| case 4 | [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384] | [[0.0, 0.066325, 0.2176], [-0.0, 0.039928, 0.130995], 0.0, -2.382384] | Passed |
| case 5 | [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096] | [[1.814757, 0.0], [-0.121589, -0.0], 0.0, 0.592096] | Passed |
| case 6 | [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552] | [[1.239766, 0.0, 0.0], [-0.188444, -0.0, -0.0], 0.0, 0.984552] | Passed |
| case 7 | [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621] | [[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621] | Passed |
SHA-256 / 35e4f260230b804268d895ef1b5b2e95293c19277729c9b06502c7795e1a220f
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.686935+00:00.
Case digest / 794d4c9c34a1bfd9a50065b03e32369cd44991997cfc012d66e55a524ed20460