FAILURE MAP
← Case archive

FA-86866 / Physics integrator stability / Open access

Frame loop integrates one frame too many · case 01

Even zero requested frames move the body.

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

ROOT CAUSE

The outer loop iterates range(steps + 1).

VERIFIED REPAIR

Iterate exactly steps frames.

Unsuccessful approach: Starting the range at one runs one frame too few.

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 + 1):
        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.484, 1.712, 27.312, 2.961, 1.788, 0.13, 4], [0.446958, 3.829901]], [[1.133, -1.182, 1.342, 0.781, 1.334, 0.1, 6], [0.491288, -0.912623]], [[-1.578, 0.145, 23.597, 0.588, 1.601, 0.01, 4], [-1.511386, 2.533387]], [[-1.392, -0.154, 8.638, 2.736, 1.406, 0.02, 2], [-1.392808, 0.024118]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[-1.377, 0.559, 2.87, 2.898, 1.015, 0.2, 0], [-1.377, 0.559]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]]], [[[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.526, 0.873, 6.362, 1.828, 1.137, 0.13, 0], [-1.526, 0.873]], [[1.082, 0.058, 37.566, 1.034, 1.55, 0.05, 9], [-0.710961, -1.842409]], [[1.297, -0.319, 39.855, 2.122, 0.103, 0.01, 11], [1.106036, -2.858781]], [[0.764, 1.798, 31.467, 2.606, 1.52, 0.1, 4], [0.561426, -2.076687]], [[-1.669, -1.23, 27.053, 1.533, 0.991, 0.13, 5], [1.161742, 3.128527]], [[1.602, 1.18, 24.517, 1.738, 1.228, 0.05, 12], [-0.636953, -4.398087]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]]], [[[0.718, 1.584, 9.356, 1.657, 0.286, 0.02, 6], [0.869722, 1.022798]], [[0.784, -0.426, 6.875, 0.731, 0.044, 0.1, 14], [-0.132405, 2.308875]], [[-1.544, -1.79, 32.966, 0.813, 1.455, 0.13, 4], [0.965451, -0.257038]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[0.044, 1.377, 37.377, 1.154, 1.523, 0.13, 8], [-0.013801, 0.684737]], [[-1.525, 1.375, 3.248, 1.693, 1.607, 0.1, 4], [-0.87326, 1.732745]], [[-1.184, -1.519, 7.411, 1.786, 1.332, 0.04, 9], [-1.302744, 0.571925]]], [[[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.818, 0.29, 35.382, 2.651, 1.322, 0.04, 12], [-0.242082, -5.8862]], [[-0.271, 1.727, 34.523, 2.128, 1.589, 0.13, 15], [0.205585, 0.49101]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.56, 1.419, 29.61, 1.024, 1.85, 0.1, 6], [0.298786, -1.014037]], [[1.308, -0.567, 4.917, 1.142, 0.149, 0.02, 14], [0.933908, -1.927012]], [[-0.362, 1.363, 36.638, 0.869, 0.886, 0.2, 7], [0.193298, -0.396669]], [[0.484, 0.239, 12.355, 0.868, 0.3, 0.07, 0], [0.484, 0.239]]], [[[-1.576, -0.203, 24.72, 2.347, 1.363, 0.07, 10], [0.760633, 3.318988]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[-0.232, 1.993, 10.835, 2.4, 1.522, 0.01, 9], [-0.054651, 1.942283]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.68, 0.833, 26.799, 1.025, 1.524, 0.13, 6], [0.548391, -4.033319]], [[-1.38, 1.106, 38.807, 2.361, 0.186, 0.05, 14], [1.397704, 0.597706]], [[0.695, 1.714, 8.192, 1.726, 1.775, 0.02, 2], [0.757467, 1.510568]], [[1.759, -0.492, 32.001, 1.805, 1.669, 0.13, 5], [-1.219998, -1.776701]]]]
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.863255, 2.837862][0.446958, 3.829901]Failed
case 1[0.405421, -0.840444][0.491288, -0.912623]Failed
case 2[-1.480676, 3.070942][-1.511386, 2.533387]Failed
case 3[-1.390572, 0.111817][-1.392808, 0.024118]Failed
case 4[-0.063446, -0.248749][-0.010702, -0.661591]Failed
case 5[0.358469, 0.775741][0.278786, 0.856518]Failed
case 6[-1.237301, 0.776909][-1.377, 0.559]Failed
case 7[0.975, -0.5][1.0, 0.0]Failed

SHA-256 / d80dba52bccbea2ab1b71d9bc12eeb1f5a16ad3bb1a24ab028e78d8b7d1945c0

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(1, 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.484, 1.712, 27.312, 2.961, 1.788, 0.13, 4], [0.446958, 3.829901]], [[1.133, -1.182, 1.342, 0.781, 1.334, 0.1, 6], [0.491288, -0.912623]], [[-1.578, 0.145, 23.597, 0.588, 1.601, 0.01, 4], [-1.511386, 2.533387]], [[-1.392, -0.154, 8.638, 2.736, 1.406, 0.02, 2], [-1.392808, 0.024118]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[-1.377, 0.559, 2.87, 2.898, 1.015, 0.2, 0], [-1.377, 0.559]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]]], [[[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.526, 0.873, 6.362, 1.828, 1.137, 0.13, 0], [-1.526, 0.873]], [[1.082, 0.058, 37.566, 1.034, 1.55, 0.05, 9], [-0.710961, -1.842409]], [[1.297, -0.319, 39.855, 2.122, 0.103, 0.01, 11], [1.106036, -2.858781]], [[0.764, 1.798, 31.467, 2.606, 1.52, 0.1, 4], [0.561426, -2.076687]], [[-1.669, -1.23, 27.053, 1.533, 0.991, 0.13, 5], [1.161742, 3.128527]], [[1.602, 1.18, 24.517, 1.738, 1.228, 0.05, 12], [-0.636953, -4.398087]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]]], [[[0.718, 1.584, 9.356, 1.657, 0.286, 0.02, 6], [0.869722, 1.022798]], [[0.784, -0.426, 6.875, 0.731, 0.044, 0.1, 14], [-0.132405, 2.308875]], [[-1.544, -1.79, 32.966, 0.813, 1.455, 0.13, 4], [0.965451, -0.257038]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[0.044, 1.377, 37.377, 1.154, 1.523, 0.13, 8], [-0.013801, 0.684737]], [[-1.525, 1.375, 3.248, 1.693, 1.607, 0.1, 4], [-0.87326, 1.732745]], [[-1.184, -1.519, 7.411, 1.786, 1.332, 0.04, 9], [-1.302744, 0.571925]]], [[[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.818, 0.29, 35.382, 2.651, 1.322, 0.04, 12], [-0.242082, -5.8862]], [[-0.271, 1.727, 34.523, 2.128, 1.589, 0.13, 15], [0.205585, 0.49101]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.56, 1.419, 29.61, 1.024, 1.85, 0.1, 6], [0.298786, -1.014037]], [[1.308, -0.567, 4.917, 1.142, 0.149, 0.02, 14], [0.933908, -1.927012]], [[-0.362, 1.363, 36.638, 0.869, 0.886, 0.2, 7], [0.193298, -0.396669]], [[0.484, 0.239, 12.355, 0.868, 0.3, 0.07, 0], [0.484, 0.239]]], [[[-1.576, -0.203, 24.72, 2.347, 1.363, 0.07, 10], [0.760633, 3.318988]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[-0.232, 1.993, 10.835, 2.4, 1.522, 0.01, 9], [-0.054651, 1.942283]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.68, 0.833, 26.799, 1.025, 1.524, 0.13, 6], [0.548391, -4.033319]], [[-1.38, 1.106, 38.807, 2.361, 0.186, 0.05, 14], [1.397704, 0.597706]], [[0.695, 1.714, 8.192, 1.726, 1.775, 0.02, 2], [0.757467, 1.510568]], [[1.759, -0.492, 32.001, 1.805, 1.669, 0.13, 5], [-1.219998, -1.776701]]]]
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.076434, 4.281738][0.446958, 3.829901]Failed
case 1[0.58429, -0.980985][0.491288, -0.912623]Failed
case 2[-1.53672, 1.970335][-1.511386, 2.533387]Failed
case 3[-1.39329, -0.064522][-1.392808, 0.024118]Failed
case 4[0.07899, -0.603109][-0.010702, -0.661591]Failed
case 5[0.191397, 0.921919][0.278786, 0.856518]Failed
case 6[-1.377, 0.559][-1.377, 0.559]Passed
case 7[1.0, 0.0][1.0, 0.0]Passed

SHA-256 / 2311afed003cf886fd81a6d60e509236af2cd7c9bd16671bbda98d40ad959829

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.484, 1.712, 27.312, 2.961, 1.788, 0.13, 4], [0.446958, 3.829901]], [[1.133, -1.182, 1.342, 0.781, 1.334, 0.1, 6], [0.491288, -0.912623]], [[-1.578, 0.145, 23.597, 0.588, 1.601, 0.01, 4], [-1.511386, 2.533387]], [[-1.392, -0.154, 8.638, 2.736, 1.406, 0.02, 2], [-1.392808, 0.024118]], [[0.514, 1.143, 25.432, 0.518, 1.496, 0.13, 9], [-0.010702, -0.661591]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[-1.377, 0.559, 2.87, 2.898, 1.015, 0.2, 0], [-1.377, 0.559]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]]], [[[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.526, 0.873, 6.362, 1.828, 1.137, 0.13, 0], [-1.526, 0.873]], [[1.082, 0.058, 37.566, 1.034, 1.55, 0.05, 9], [-0.710961, -1.842409]], [[1.297, -0.319, 39.855, 2.122, 0.103, 0.01, 11], [1.106036, -2.858781]], [[0.764, 1.798, 31.467, 2.606, 1.52, 0.1, 4], [0.561426, -2.076687]], [[-1.669, -1.23, 27.053, 1.533, 0.991, 0.13, 5], [1.161742, 3.128527]], [[1.602, 1.18, 24.517, 1.738, 1.228, 0.05, 12], [-0.636953, -4.398087]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]]], [[[0.718, 1.584, 9.356, 1.657, 0.286, 0.02, 6], [0.869722, 1.022798]], [[0.784, -0.426, 6.875, 0.731, 0.044, 0.1, 14], [-0.132405, 2.308875]], [[-1.544, -1.79, 32.966, 0.813, 1.455, 0.13, 4], [0.965451, -0.257038]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[0.044, 1.377, 37.377, 1.154, 1.523, 0.13, 8], [-0.013801, 0.684737]], [[-1.525, 1.375, 3.248, 1.693, 1.607, 0.1, 4], [-0.87326, 1.732745]], [[-1.184, -1.519, 7.411, 1.786, 1.332, 0.04, 9], [-1.302744, 0.571925]]], [[[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[1.818, 0.29, 35.382, 2.651, 1.322, 0.04, 12], [-0.242082, -5.8862]], [[-0.271, 1.727, 34.523, 2.128, 1.589, 0.13, 15], [0.205585, 0.49101]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-0.56, 1.419, 29.61, 1.024, 1.85, 0.1, 6], [0.298786, -1.014037]], [[1.308, -0.567, 4.917, 1.142, 0.149, 0.02, 14], [0.933908, -1.927012]], [[-0.362, 1.363, 36.638, 0.869, 0.886, 0.2, 7], [0.193298, -0.396669]], [[0.484, 0.239, 12.355, 0.868, 0.3, 0.07, 0], [0.484, 0.239]]], [[[-1.576, -0.203, 24.72, 2.347, 1.363, 0.07, 10], [0.760633, 3.318988]], [[0.0, 1.0, 4.0, 2.0, 0.5, 0.1, 3], [0.278786, 0.856518]], [[-0.232, 1.993, 10.835, 2.4, 1.522, 0.01, 9], [-0.054651, 1.942283]], [[1.0, 0.0, 10.0, 1.0, 0.0, 0.05, 0], [1.0, 0.0]], [[-1.68, 0.833, 26.799, 1.025, 1.524, 0.13, 6], [0.548391, -4.033319]], [[-1.38, 1.106, 38.807, 2.361, 0.186, 0.05, 14], [1.397704, 0.597706]], [[0.695, 1.714, 8.192, 1.726, 1.775, 0.02, 2], [0.757467, 1.510568]], [[1.759, -0.492, 32.001, 1.805, 1.669, 0.13, 5], [-1.219998, -1.776701]]]]
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.446958, 3.829901][0.446958, 3.829901]Passed
case 1[0.491288, -0.912623][0.491288, -0.912623]Passed
case 2[-1.511386, 2.533387][-1.511386, 2.533387]Passed
case 3[-1.392808, 0.024118][-1.392808, 0.024118]Passed
case 4[-0.010702, -0.661591][-0.010702, -0.661591]Passed
case 5[0.278786, 0.856518][0.278786, 0.856518]Passed
case 6[-1.377, 0.559][-1.377, 0.559]Passed
case 7[1.0, 0.0][1.0, 0.0]Passed

SHA-256 / c24f638da77269b6879e30af6cbe467eee10eb6d351b62e4b45608743fc668bd

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

Case digest / d720cd11c91c60405be7db202e1cf75801fdf1055d28efca68c674071fcf6520