FA-86841 / Physics integrator stability / Open access
Symplectic Euler substep drifts position with the pre-kick velocity · case 01
A stiff spring that should orbit with bounded energy gains amplitude every frame.
ROOT CAUSE
The position update runs before the velocity kick, turning the scheme into explicit forward Euler.
VERIFIED REPAIR
Kick the velocity first and drift the position with the freshly updated velocity.
Unsuccessful approach: Averaging old and new velocity for the drift gives a trapezoid-like scheme that is not the contracted symplectic update.
Case contract
solve(x0, v0, k, m, c, dt, steps): damped spring a=(-k*x-c*v)/m integrated with semi-implicit Euler (velocity first, then position with the new velocity). Each frame of length dt is split into ceil(dt/0.05) equal substeps (at least one). Return [x, v] rounded to 6 decimals after steps frames.
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(x0, v0, k, m, c, dt, steps):
n_sub = max(1, math.ceil(dt / 0.05 - 1e-12))
h = dt / n_sub
x, v = x0, v0
for _ in range(steps):
for _ in range(n_sub):
a = (-k * x - c * v) / m
x = x + v * h
v = v + a * h
return [round(x, 6), round(v, 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.958, 1.121, 24.102, 1.592, 0.332, 0.04, 3], [-0.694302, 2.683815]], [[-1.072, -0.219, 22.982, 2.32, 1.666, 0.05, 1], [-1.056009, 0.319827]], [[1.454, -0.539, 11.28, 2.71, 0.785, 0.07, 0], [1.454, -0.539]], [[1.545, 1.962, 22.255, 1.613, 1.385, 0.02, 0], [1.545, 1.962]], [[0.428, -0.176, 18.564, 2.203, 1.454, 0.07, 2], [0.36285, -0.625106]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]], [[-1.539, -1.291, 32.554, 2.072, 1.066, 0.07, 3], [-1.199771, 3.497044]]], [[[1.742, -0.907, 38.421, 2.244, 0.819, 0.1, 5], [-1.031195, -5.442883]], [[-1.037, 0.234, 1.989, 0.946, 0.544, 0.01, 8], [-1.011063, 0.39269]], [[1.602, 1.18, 24.517, 1.738, 1.228, 0.05, 12], [-0.636953, -4.398087]], [[-0.384, 0.483, 39.776, 2.351, 1.463, 0.04, 0], [-0.384, 0.483]], [[-1.526, 0.873, 6.362, 1.828, 1.137, 0.13, 0], [-1.526, 0.873]], [[-1.985, 0.451, 27.993, 1.055, 0.395, 0.02, 2], [-1.904435, 2.527224]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.365, 1.406, 18.892, 1.116, 1.872, 0.05, 6], [0.118578, 1.39259]]], [[[1.454, 1.519, 3.577, 1.401, 0.303, 0.04, 8], [1.697018, 0.15014]], [[-1.712, -1.9, 9.139, 0.693, 0.61, 0.05, 13], [0.624756, 4.351887]], [[0.87, 1.182, 19.781, 1.146, 0.3, 0.1, 0], [0.87, 1.182]], [[0.116, -1.998, 11.275, 2.342, 1.481, 0.1, 12], [-0.366142, 1.157058]], [[-0.541, 0.707, 32.675, 1.469, 1.372, 0.2, 9], [0.199962, 0.643469]], [[-1.675, 0.095, 12.217, 1.214, 0.078, 0.13, 11], [0.14895, -5.038149]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.648, -0.117, 24.609, 0.651, 1.732, 0.05, 0], [1.648, -0.117]]], [[[0.286, 1.636, 25.073, 1.437, 1.309, 0.1, 0], [0.286, 1.636]], [[-0.07, 1.272, 25.671, 1.8, 0.333, 0.02, 8], [0.132138, 1.188849]], [[-1.284, 1.069, 32.194, 0.542, 0.353, 0.05, 11], [0.174523, -8.251404]], [[-1.975, -1.176, 27.308, 0.777, 1.386, 0.04, 3], [-1.482343, 6.386036]], [[1.974, -1.489, 34.073, 1.218, 1.372, 0.05, 10], [-1.429284, -2.634338]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[0.484, 0.239, 12.355, 0.868, 0.3, 0.07, 0], [0.484, 0.239]], [[-0.117, 1.853, 21.165, 2.719, 0.626, 0.07, 12], [0.503218, -0.957084]]], [[[-0.078, -0.863, 3.994, 2.622, 1.462, 0.1, 0], [-0.078, -0.863]], [[-0.056, -1.079, 26.249, 1.108, 0.441, 0.07, 8], [-0.030782, 0.966174]], [[0.855, 1.882, 14.464, 2.712, 0.17, 0.02, 0], [0.855, 1.882]], [[0.633, -1.784, 4.173, 2.063, 1.163, 0.13, 21], [0.109799, 0.812645]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.29, -0.513, 1.621, 0.73, 0.535, 0.2, 8], [-0.052879, 0.418713]], [[1.617, 1.871, 21.063, 0.682, 0.338, 0.02, 7], [1.325932, -4.832529]], [[1.925, -1.211, 19.503, 2.115, 0.02, 0.13, 1], [1.574252, -3.400654]]]]
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 | [1.0, 0.0] | [1.0, 0.0] | Passed |
| case 1 | [-0.756262, 2.724071] | [-0.694302, 2.683815] | Failed |
| case 2 | [-1.08295, 0.319827] | [-1.056009, 0.319827] | Failed |
| case 3 | [1.454, -0.539] | [1.454, -0.539] | Passed |
| case 4 | [1.545, 1.962] | [1.545, 1.962] | Passed |
| case 5 | [0.378395, -0.63229] | [0.36285, -0.625106] | Failed |
| case 6 | [0.021512, -2.569141] | [-0.010702, -0.661591] | Failed |
| case 7 | [-1.356003, 3.729909] | [-1.199771, 3.497044] | Failed |
SHA-256 / e4825c66a36c03ef84fc6bf3cffece47887de3ae7899eb463dd5a92119c959cc
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x0, v0, k, m, c, dt, steps):
n_sub = max(1, math.ceil(dt / 0.05 - 1e-12))
h = dt / n_sub
x, v = x0, v0
for _ in range(steps):
for _ in range(n_sub):
a = (-k * x - c * v) / m
vn = v + a * h
x = x + 0.5 * (v + vn) * h
v = vn
return [round(x, 6), round(v, 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.958, 1.121, 24.102, 1.592, 0.332, 0.04, 3], [-0.694302, 2.683815]], [[-1.072, -0.219, 22.982, 2.32, 1.666, 0.05, 1], [-1.056009, 0.319827]], [[1.454, -0.539, 11.28, 2.71, 0.785, 0.07, 0], [1.454, -0.539]], [[1.545, 1.962, 22.255, 1.613, 1.385, 0.02, 0], [1.545, 1.962]], [[0.428, -0.176, 18.564, 2.203, 1.454, 0.07, 2], [0.36285, -0.625106]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]], [[-1.539, -1.291, 32.554, 2.072, 1.066, 0.07, 3], [-1.199771, 3.497044]]], [[[1.742, -0.907, 38.421, 2.244, 0.819, 0.1, 5], [-1.031195, -5.442883]], [[-1.037, 0.234, 1.989, 0.946, 0.544, 0.01, 8], [-1.011063, 0.39269]], [[1.602, 1.18, 24.517, 1.738, 1.228, 0.05, 12], [-0.636953, -4.398087]], [[-0.384, 0.483, 39.776, 2.351, 1.463, 0.04, 0], [-0.384, 0.483]], [[-1.526, 0.873, 6.362, 1.828, 1.137, 0.13, 0], [-1.526, 0.873]], [[-1.985, 0.451, 27.993, 1.055, 0.395, 0.02, 2], [-1.904435, 2.527224]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.365, 1.406, 18.892, 1.116, 1.872, 0.05, 6], [0.118578, 1.39259]]], [[[1.454, 1.519, 3.577, 1.401, 0.303, 0.04, 8], [1.697018, 0.15014]], [[-1.712, -1.9, 9.139, 0.693, 0.61, 0.05, 13], [0.624756, 4.351887]], [[0.87, 1.182, 19.781, 1.146, 0.3, 0.1, 0], [0.87, 1.182]], [[0.116, -1.998, 11.275, 2.342, 1.481, 0.1, 12], [-0.366142, 1.157058]], [[-0.541, 0.707, 32.675, 1.469, 1.372, 0.2, 9], [0.199962, 0.643469]], [[-1.675, 0.095, 12.217, 1.214, 0.078, 0.13, 11], [0.14895, -5.038149]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.648, -0.117, 24.609, 0.651, 1.732, 0.05, 0], [1.648, -0.117]]], [[[0.286, 1.636, 25.073, 1.437, 1.309, 0.1, 0], [0.286, 1.636]], [[-0.07, 1.272, 25.671, 1.8, 0.333, 0.02, 8], [0.132138, 1.188849]], [[-1.284, 1.069, 32.194, 0.542, 0.353, 0.05, 11], [0.174523, -8.251404]], [[-1.975, -1.176, 27.308, 0.777, 1.386, 0.04, 3], [-1.482343, 6.386036]], [[1.974, -1.489, 34.073, 1.218, 1.372, 0.05, 10], [-1.429284, -2.634338]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[0.484, 0.239, 12.355, 0.868, 0.3, 0.07, 0], [0.484, 0.239]], [[-0.117, 1.853, 21.165, 2.719, 0.626, 0.07, 12], [0.503218, -0.957084]]], [[[-0.078, -0.863, 3.994, 2.622, 1.462, 0.1, 0], [-0.078, -0.863]], [[-0.056, -1.079, 26.249, 1.108, 0.441, 0.07, 8], [-0.030782, 0.966174]], [[0.855, 1.882, 14.464, 2.712, 0.17, 0.02, 0], [0.855, 1.882]], [[0.633, -1.784, 4.173, 2.063, 1.163, 0.13, 21], [0.109799, 0.812645]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.29, -0.513, 1.621, 0.73, 0.535, 0.2, 8], [-0.052879, 0.418713]], [[1.617, 1.871, 21.063, 0.682, 0.338, 0.02, 7], [1.325932, -4.832529]], [[1.925, -1.211, 19.503, 2.115, 0.02, 0.13, 1], [1.574252, -3.400654]]]]
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 | [1.0, 0.0] | [1.0, 0.0] | Passed |
| case 1 | [-0.724881, 2.703859] | [-0.694302, 2.683815] | Failed |
| case 2 | [-1.069479, 0.319827] | [-1.056009, 0.319827] | Failed |
| case 3 | [1.454, -0.539] | [1.454, -0.539] | Passed |
| case 4 | [1.545, 1.962] | [1.545, 1.962] | Passed |
| case 5 | [0.37056, -0.628685] | [0.36285, -0.625106] | Failed |
| case 6 | [-0.010003, -1.316001] | [-0.010702, -0.661591] | Failed |
| case 7 | [-1.275917, 3.611958] | [-1.199771, 3.497044] | Failed |
SHA-256 / 2d4a1157e497fa18200bb64c91ad6f71d123d21e5190d0710a15eea3dba22edd
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x0, v0, k, m, c, dt, steps):
n_sub = max(1, math.ceil(dt / 0.05 - 1e-12))
h = dt / n_sub
x, v = x0, v0
for _ in range(steps):
for _ in range(n_sub):
a = (-k * x - c * v) / m
v = v + a * h
x = x + v * h
return [round(x, 6), round(v, 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.958, 1.121, 24.102, 1.592, 0.332, 0.04, 3], [-0.694302, 2.683815]], [[-1.072, -0.219, 22.982, 2.32, 1.666, 0.05, 1], [-1.056009, 0.319827]], [[1.454, -0.539, 11.28, 2.71, 0.785, 0.07, 0], [1.454, -0.539]], [[1.545, 1.962, 22.255, 1.613, 1.385, 0.02, 0], [1.545, 1.962]], [[0.428, -0.176, 18.564, 2.203, 1.454, 0.07, 2], [0.36285, -0.625106]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]], [[-1.539, -1.291, 32.554, 2.072, 1.066, 0.07, 3], [-1.199771, 3.497044]]], [[[1.742, -0.907, 38.421, 2.244, 0.819, 0.1, 5], [-1.031195, -5.442883]], [[-1.037, 0.234, 1.989, 0.946, 0.544, 0.01, 8], [-1.011063, 0.39269]], [[1.602, 1.18, 24.517, 1.738, 1.228, 0.05, 12], [-0.636953, -4.398087]], [[-0.384, 0.483, 39.776, 2.351, 1.463, 0.04, 0], [-0.384, 0.483]], [[-1.526, 0.873, 6.362, 1.828, 1.137, 0.13, 0], [-1.526, 0.873]], [[-1.985, 0.451, 27.993, 1.055, 0.395, 0.02, 2], [-1.904435, 2.527224]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.365, 1.406, 18.892, 1.116, 1.872, 0.05, 6], [0.118578, 1.39259]]], [[[1.454, 1.519, 3.577, 1.401, 0.303, 0.04, 8], [1.697018, 0.15014]], [[-1.712, -1.9, 9.139, 0.693, 0.61, 0.05, 13], [0.624756, 4.351887]], [[0.87, 1.182, 19.781, 1.146, 0.3, 0.1, 0], [0.87, 1.182]], [[0.116, -1.998, 11.275, 2.342, 1.481, 0.1, 12], [-0.366142, 1.157058]], [[-0.541, 0.707, 32.675, 1.469, 1.372, 0.2, 9], [0.199962, 0.643469]], [[-1.675, 0.095, 12.217, 1.214, 0.078, 0.13, 11], [0.14895, -5.038149]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.648, -0.117, 24.609, 0.651, 1.732, 0.05, 0], [1.648, -0.117]]], [[[0.286, 1.636, 25.073, 1.437, 1.309, 0.1, 0], [0.286, 1.636]], [[-0.07, 1.272, 25.671, 1.8, 0.333, 0.02, 8], [0.132138, 1.188849]], [[-1.284, 1.069, 32.194, 0.542, 0.353, 0.05, 11], [0.174523, -8.251404]], [[-1.975, -1.176, 27.308, 0.777, 1.386, 0.04, 3], [-1.482343, 6.386036]], [[1.974, -1.489, 34.073, 1.218, 1.372, 0.05, 10], [-1.429284, -2.634338]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[0.484, 0.239, 12.355, 0.868, 0.3, 0.07, 0], [0.484, 0.239]], [[-0.117, 1.853, 21.165, 2.719, 0.626, 0.07, 12], [0.503218, -0.957084]]], [[[-0.078, -0.863, 3.994, 2.622, 1.462, 0.1, 0], [-0.078, -0.863]], [[-0.056, -1.079, 26.249, 1.108, 0.441, 0.07, 8], [-0.030782, 0.966174]], [[0.855, 1.882, 14.464, 2.712, 0.17, 0.02, 0], [0.855, 1.882]], [[0.633, -1.784, 4.173, 2.063, 1.163, 0.13, 21], [0.109799, 0.812645]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.29, -0.513, 1.621, 0.73, 0.535, 0.2, 8], [-0.052879, 0.418713]], [[1.617, 1.871, 21.063, 0.682, 0.338, 0.02, 7], [1.325932, -4.832529]], [[1.925, -1.211, 19.503, 2.115, 0.02, 0.13, 1], [1.574252, -3.400654]]]]
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 | [1.0, 0.0] | [1.0, 0.0] | Passed |
| case 1 | [-0.694302, 2.683815] | [-0.694302, 2.683815] | Passed |
| case 2 | [-1.056009, 0.319827] | [-1.056009, 0.319827] | Passed |
| case 3 | [1.454, -0.539] | [1.454, -0.539] | Passed |
| case 4 | [1.545, 1.962] | [1.545, 1.962] | Passed |
| case 5 | [0.36285, -0.625106] | [0.36285, -0.625106] | Passed |
| case 6 | [-0.010702, -0.661591] | [-0.010702, -0.661591] | Passed |
| case 7 | [-1.199771, 3.497044] | [-1.199771, 3.497044] | Passed |
SHA-256 / 286e22f59c246923bd6ea63f46236f3851edb388dbc20c0f7ebbe79cc7f9346c
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:53.362280+00:00.
Case digest / 8c553462074da18ed925a8dc16055713a325a9dfe76281f2a86052ff8ec3a6c2