FAILURE MAP
← Case archive

FA-86846 / Physics integrator stability / Open access

Spring restoring force pushes the mass away from rest · case 01

Displaced masses accelerate outward and the state grows exponentially.

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

ROOT CAUSE

The Hooke term is written +k*x, so the spring repels instead of restoring.

VERIFIED REPAIR

Use -k*x so the spring always accelerates the mass toward the rest position.

Unsuccessful approach: Using -k*abs(x) restores only on the positive side and pushes negative displacements further away.

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
            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 = [[[[-0.104, -1.626, 5.159, 2.304, 0.637, 0.2, 0], [-0.104, -1.626]], [[-0.562, -1.226, 9.534, 1.694, 1.284, 0.13, 0], [-0.562, -1.226]], [[-1.786, -1.656, 13.016, 2.04, 1.641, 0.02, 1], [-1.814029, -1.40145]], [[0.685, -1.111, 8.677, 2.127, 1.752, 0.1, 8], [-0.325367, -0.873653]], [[-0.128, 1.776, 35.57, 0.657, 1.874, 0.04, 9], [0.123101, -0.690879]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.072, -0.219, 22.982, 2.32, 1.666, 0.05, 1], [-1.056009, 0.319827]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]]], [[[1.742, -0.907, 38.421, 2.244, 0.819, 0.1, 5], [-1.031195, -5.442883]], [[-0.185, -0.874, 37.426, 1.485, 0.472, 0.04, 0], [-0.185, -0.874]], [[-0.365, 1.406, 18.892, 1.116, 1.872, 0.05, 6], [0.118578, 1.39259]], [[-1.356, 1.252, 26.385, 1.751, 0.173, 0.2, 5], [0.659131, -4.378629]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.195, 1.451, 20.307, 2.688, 1.682, 0.1, 2], [0.095172, 1.395255]], [[1.907, 1.457, 31.983, 1.504, 0.494, 0.2, 0], [1.907, 1.457]], [[0.377, 1.608, 11.427, 2.764, 0.526, 0.13, 2], [0.705869, 0.97255]]], [[[-0.081, 1.898, 20.437, 2.775, 1.674, 0.04, 11], [0.532877, 0.702137]], [[0.87, 1.182, 19.781, 1.146, 0.3, 0.1, 0], [0.87, 1.182]], [[1.412, -1.43, 37.043, 2.676, 1.923, 0.01, 4], [1.33665, -2.148434]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.648, -0.117, 24.609, 0.651, 1.732, 0.05, 0], [1.648, -0.117]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.222, -1.354, 12.316, 2.895, 1.536, 0.2, 4], [-0.52136, -1.890917]], [[0.718, 1.584, 9.356, 1.657, 0.286, 0.02, 6], [0.869722, 1.022798]]], [[[-0.722, 1.788, 25.808, 1.535, 1.788, 0.01, 7], [-0.571189, 2.397082]], [[-0.981, -0.335, 12.78, 0.725, 0.124, 0.01, 16], [-0.805868, 2.27257]], [[0.76, 1.611, 15.529, 0.686, 0.511, 0.07, 14], [-0.242783, 2.493606]], [[1.977, 0.462, 36.991, 1.489, 1.033, 0.13, 0], [1.977, 0.462]], [[0.286, 1.636, 25.073, 1.437, 1.309, 0.1, 0], [0.286, 1.636]], [[-1.413, 0.664, 29.552, 2.975, 1.21, 0.13, 8], [1.104015, -1.060714]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]]], [[[-1.215, 0.2, 16.12, 1.251, 1.235, 0.02, 10], [-0.872021, 2.751611]], [[1.873, -0.51, 29.384, 1.99, 0.411, 0.2, 16], [1.352031, 0.801221]], [[-0.078, -0.863, 3.994, 2.622, 1.462, 0.1, 0], [-0.078, -0.863]], [[-0.914, 0.601, 8.25, 1.548, 0.798, 0.02, 0], [-0.914, 0.601]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.925, -1.211, 19.503, 2.115, 0.02, 0.13, 1], [1.574252, -3.400654]], [[-0.584, 1.144, 23.196, 1.052, 0.941, 0.2, 20], [-0.08891, 0.230159]], [[0.36, -1.513, 9.018, 1.848, 0.394, 0.05, 8], [-0.288846, -1.518381]]]]
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.104, -1.626][-0.104, -1.626]Passed
case 1[-0.562, -1.226][-0.562, -1.226]Passed
case 2[-1.823145, -1.857265][-1.814029, -1.40145]Failed
case 3[0.704864, 0.82706][-0.325367, -0.873653]Failed
case 4[0.189531, 1.128832][0.123101, -0.690879]Failed
case 5[1.0, 0.0][1.0, 0.0]Passed
case 6[-1.109105, -0.7421][-1.056009, 0.319827]Failed
case 7[386.76113, 1993.561829][-0.010702, -0.661591]Failed

SHA-256 / 49f8e462687102f105493a82c73eb1ae362f30642828e7e84e0bd6c27f3855c9

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 * abs(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 = [[[[-0.104, -1.626, 5.159, 2.304, 0.637, 0.2, 0], [-0.104, -1.626]], [[-0.562, -1.226, 9.534, 1.694, 1.284, 0.13, 0], [-0.562, -1.226]], [[-1.786, -1.656, 13.016, 2.04, 1.641, 0.02, 1], [-1.814029, -1.40145]], [[0.685, -1.111, 8.677, 2.127, 1.752, 0.1, 8], [-0.325367, -0.873653]], [[-0.128, 1.776, 35.57, 0.657, 1.874, 0.04, 9], [0.123101, -0.690879]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.072, -0.219, 22.982, 2.32, 1.666, 0.05, 1], [-1.056009, 0.319827]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]]], [[[1.742, -0.907, 38.421, 2.244, 0.819, 0.1, 5], [-1.031195, -5.442883]], [[-0.185, -0.874, 37.426, 1.485, 0.472, 0.04, 0], [-0.185, -0.874]], [[-0.365, 1.406, 18.892, 1.116, 1.872, 0.05, 6], [0.118578, 1.39259]], [[-1.356, 1.252, 26.385, 1.751, 0.173, 0.2, 5], [0.659131, -4.378629]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.195, 1.451, 20.307, 2.688, 1.682, 0.1, 2], [0.095172, 1.395255]], [[1.907, 1.457, 31.983, 1.504, 0.494, 0.2, 0], [1.907, 1.457]], [[0.377, 1.608, 11.427, 2.764, 0.526, 0.13, 2], [0.705869, 0.97255]]], [[[-0.081, 1.898, 20.437, 2.775, 1.674, 0.04, 11], [0.532877, 0.702137]], [[0.87, 1.182, 19.781, 1.146, 0.3, 0.1, 0], [0.87, 1.182]], [[1.412, -1.43, 37.043, 2.676, 1.923, 0.01, 4], [1.33665, -2.148434]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.648, -0.117, 24.609, 0.651, 1.732, 0.05, 0], [1.648, -0.117]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.222, -1.354, 12.316, 2.895, 1.536, 0.2, 4], [-0.52136, -1.890917]], [[0.718, 1.584, 9.356, 1.657, 0.286, 0.02, 6], [0.869722, 1.022798]]], [[[-0.722, 1.788, 25.808, 1.535, 1.788, 0.01, 7], [-0.571189, 2.397082]], [[-0.981, -0.335, 12.78, 0.725, 0.124, 0.01, 16], [-0.805868, 2.27257]], [[0.76, 1.611, 15.529, 0.686, 0.511, 0.07, 14], [-0.242783, 2.493606]], [[1.977, 0.462, 36.991, 1.489, 1.033, 0.13, 0], [1.977, 0.462]], [[0.286, 1.636, 25.073, 1.437, 1.309, 0.1, 0], [0.286, 1.636]], [[-1.413, 0.664, 29.552, 2.975, 1.21, 0.13, 8], [1.104015, -1.060714]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]]], [[[-1.215, 0.2, 16.12, 1.251, 1.235, 0.02, 10], [-0.872021, 2.751611]], [[1.873, -0.51, 29.384, 1.99, 0.411, 0.2, 16], [1.352031, 0.801221]], [[-0.078, -0.863, 3.994, 2.622, 1.462, 0.1, 0], [-0.078, -0.863]], [[-0.914, 0.601, 8.25, 1.548, 0.798, 0.02, 0], [-0.914, 0.601]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.925, -1.211, 19.503, 2.115, 0.02, 0.13, 1], [1.574252, -3.400654]], [[-0.584, 1.144, 23.196, 1.052, 0.941, 0.2, 20], [-0.08891, 0.230159]], [[0.36, -1.513, 9.018, 1.848, 0.394, 0.05, 8], [-0.288846, -1.518381]]]]
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.104, -1.626][-0.104, -1.626]Passed
case 1[-0.562, -1.226][-0.562, -1.226]Passed
case 2[-1.823145, -1.857265][-1.814029, -1.40145]Failed
case 3[-0.366875, -1.221102][-0.325367, -0.873653]Failed
case 4[0.067202, -0.114341][0.123101, -0.690879]Failed
case 5[1.0, 0.0][1.0, 0.0]Passed
case 6[-1.109105, -0.7421][-1.056009, 0.319827]Failed
case 7[-32.671793, -168.407784][-0.010702, -0.661591]Failed

SHA-256 / 9172e55a98852b2b1a7c0d0279ccefcebe1ad10d31215462a6e2460f19c2f507

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 = [[[[-0.104, -1.626, 5.159, 2.304, 0.637, 0.2, 0], [-0.104, -1.626]], [[-0.562, -1.226, 9.534, 1.694, 1.284, 0.13, 0], [-0.562, -1.226]], [[-1.786, -1.656, 13.016, 2.04, 1.641, 0.02, 1], [-1.814029, -1.40145]], [[0.685, -1.111, 8.677, 2.127, 1.752, 0.1, 8], [-0.325367, -0.873653]], [[-0.128, 1.776, 35.57, 0.657, 1.874, 0.04, 9], [0.123101, -0.690879]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.072, -0.219, 22.982, 2.32, 1.666, 0.05, 1], [-1.056009, 0.319827]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]]], [[[1.742, -0.907, 38.421, 2.244, 0.819, 0.1, 5], [-1.031195, -5.442883]], [[-0.185, -0.874, 37.426, 1.485, 0.472, 0.04, 0], [-0.185, -0.874]], [[-0.365, 1.406, 18.892, 1.116, 1.872, 0.05, 6], [0.118578, 1.39259]], [[-1.356, 1.252, 26.385, 1.751, 0.173, 0.2, 5], [0.659131, -4.378629]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.195, 1.451, 20.307, 2.688, 1.682, 0.1, 2], [0.095172, 1.395255]], [[1.907, 1.457, 31.983, 1.504, 0.494, 0.2, 0], [1.907, 1.457]], [[0.377, 1.608, 11.427, 2.764, 0.526, 0.13, 2], [0.705869, 0.97255]]], [[[-0.081, 1.898, 20.437, 2.775, 1.674, 0.04, 11], [0.532877, 0.702137]], [[0.87, 1.182, 19.781, 1.146, 0.3, 0.1, 0], [0.87, 1.182]], [[1.412, -1.43, 37.043, 2.676, 1.923, 0.01, 4], [1.33665, -2.148434]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.648, -0.117, 24.609, 0.651, 1.732, 0.05, 0], [1.648, -0.117]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.222, -1.354, 12.316, 2.895, 1.536, 0.2, 4], [-0.52136, -1.890917]], [[0.718, 1.584, 9.356, 1.657, 0.286, 0.02, 6], [0.869722, 1.022798]]], [[[-0.722, 1.788, 25.808, 1.535, 1.788, 0.01, 7], [-0.571189, 2.397082]], [[-0.981, -0.335, 12.78, 0.725, 0.124, 0.01, 16], [-0.805868, 2.27257]], [[0.76, 1.611, 15.529, 0.686, 0.511, 0.07, 14], [-0.242783, 2.493606]], [[1.977, 0.462, 36.991, 1.489, 1.033, 0.13, 0], [1.977, 0.462]], [[0.286, 1.636, 25.073, 1.437, 1.309, 0.1, 0], [0.286, 1.636]], [[-1.413, 0.664, 29.552, 2.975, 1.21, 0.13, 8], [1.104015, -1.060714]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]]], [[[-1.215, 0.2, 16.12, 1.251, 1.235, 0.02, 10], [-0.872021, 2.751611]], [[1.873, -0.51, 29.384, 1.99, 0.411, 0.2, 16], [1.352031, 0.801221]], [[-0.078, -0.863, 3.994, 2.622, 1.462, 0.1, 0], [-0.078, -0.863]], [[-0.914, 0.601, 8.25, 1.548, 0.798, 0.02, 0], [-0.914, 0.601]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[1.925, -1.211, 19.503, 2.115, 0.02, 0.13, 1], [1.574252, -3.400654]], [[-0.584, 1.144, 23.196, 1.052, 0.941, 0.2, 20], [-0.08891, 0.230159]], [[0.36, -1.513, 9.018, 1.848, 0.394, 0.05, 8], [-0.288846, -1.518381]]]]
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.104, -1.626][-0.104, -1.626]Passed
case 1[-0.562, -1.226][-0.562, -1.226]Passed
case 2[-1.814029, -1.40145][-1.814029, -1.40145]Passed
case 3[-0.325367, -0.873653][-0.325367, -0.873653]Passed
case 4[0.123101, -0.690879][0.123101, -0.690879]Passed
case 5[1.0, 0.0][1.0, 0.0]Passed
case 6[-1.056009, 0.319827][-1.056009, 0.319827]Passed
case 7[-0.010702, -0.661591][-0.010702, -0.661591]Passed

SHA-256 / e5b6831d6f5e316bfcdc58257b4573d5388fcdb092dd139c747f99027a609f4a

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.362540+00:00.

Case digest / 0be1e19aeafa46be965cdf430f101c5fef05673227976fbf88ce89881de114cd