FAILURE MAP
← Case archive

FA-87236 / Physics integrator stability / Open access

Cached normal impulse ignores the warm-start factor · case 01

Changing the warm factor has no effect on normal impulses and damping settings misbehave.

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

ROOT CAUSE

jn is seeded with the raw cached value.

VERIFIED REPAIR

Scale cached impulses by the warm factor.

Unsuccessful approach: Squaring the warm factor under-seeds the normal impulse.

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] 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 = [[[[-3.886, 1.26, 0.358, 0.718, 2, [[0.002, 0.092]], 1.0], [[10.854749], [-3.519553], 0.0, 0.0]], [[-0.147, -1.284, 0.48, 0.792, 3, [[1.388, -0.326], [0.337, 0.037]], 0.0], [[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576]], [[-0.717, 2.25, 1.239, 0.977, 1, [[1.943, 0.223], [1.215, -0.282], [0.426, -0.176]], 0.8], [[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0]], [[-1.151, -2.942, 1.666, 0.825, 4, [[0.959, 0.822], [1.872, 0.85], [0.021, -0.236]], 0.0], [[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425]], [[0.385, 0.568, 0.63, 0.737, 3, [[0.414, -0.654], [0.917, 0.611], [1.569, 0.695]], 1.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568]], [[-1.902, 0.699, 0.438, 0.22, 3, [[1.049, -0.312], [0.241, -0.124]], 0.5], [[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056]], [[-2.972, 0.049, 0.405, 0.199, 3, [[1.938, 0.409], [0.746, 0.957], [1.714, -0.847]], 1.0], [[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0]], [[-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.559, 1.606, 1.849, 0.194, 2, [[0.671, 0.987], [1.821, 0.059], [0.496, -0.817]], 0.5], [[0.0, 0.595158, 0.248], [-0.0, -0.115461, -0.048112], 0.0, 1.303554]], [[-1.753, -2.782, 0.464, 0.155, 4, [[0.504, 0.834], [1.119, 0.469], [1.759, -0.733]], 0.5], [[2.339017, 0.5595, 0.8795], [0.362548, 0.086722, 0.136322], 0.0, -2.510285]], [[-0.349, 0.709, 1.805, 0.199, 2, [[1.996, 0.558], [0.968, -0.267], [0.402, -0.027]], 0.5], [[0.0, 0.0, 0.193352], [-0.0, -0.0, -0.038477], 0.0, 0.639549]], [[-1.981, -2.068, 0.556, 0.937, 2, [[0.091, 0.771], [0.183, 0.742], [0.226, 0.981]], 1.0], [[3.15395, 0.183, 0.226], [2.955251, 0.171471, 0.211762], 0.0, -0.211803]], [[-0.308, 2.306, 0.481, 0.691, 2, [[1.507, 0.678]], 0.5], [[0.640333], [-0.44247], 0.0, 2.093172]], [[-2.225, -2.18, 0.935, 0.027, 4, [[0.823, -0.309], [1.755, 0.946], [0.134, -0.423]], 0.5], [[1.435179, 0.8775, 0.067], [0.03875, 0.023692, 0.001809], 0.0, -2.119925]], [[-1.602, -1.851, 1.049, 0.189, 2, [[0.015, 0.622], [1.841, 0.588], [0.748, 0.191]], 1.0], [[0.0, 0.779169, 0.748], [-0.0, 0.147263, 0.141372], 0.0, -1.548222]], [[-1.5, 0.461, 1.602, 0.795, 3, [[0.381, -0.102], [0.711, 0.701]], 0.0], [[0.93633, 0.0], [-0.287765, -0.0], 0.0, 0.0]]], [[[-0.543, -0.389, 0.316, 0.68, 4, [[1.044, -0.289], [0.189, -0.756], [1.244, -0.187]], 0.8], [[0.571954, 0.1512, 0.9952], [0.388929, 0.102816, 0.676736], 0.0, -0.01976]], [[-2.82, 1.429, 1.478, 0.114, 2, [[0.588, -0.945], [0.049, -0.934], [1.67, 0.896]], 0.0], [[1.907984, 0.0, 0.0], [-0.21751, -0.0, -0.0], 0.0, 1.10752]], [[-3.678, 1.879, 1.356, 0.559, 1, [[1.998, -0.372], [1.803, -0.832]], 0.8], [[1.269989, 1.4424], [-0.720093, -0.6656], 0.0, 0.0]], [[-0.181, 1.833, 1.447, 0.671, 2, [[1.934, 0.697]], 0.5], [[0.125086], [-0.083933], 0.0, 1.711549]], [[-3.591, -2.324, 0.75, 0.86, 3, [[1.451, 0.31], [1.726, -0.878], [1.597, 0.48]], 0.0], [[4.788, 0.0, 0.0], [3.098667, -0.0, -0.0], 0.0, 0.0]], [[-1.345, -2.266, 0.903, 0.154, 1, [[1.454, -0.867], [0.873, 0.725], [1.147, 0.427]], 0.8], [[0.0, 0.57188, 0.9176], [0.179133, 0.107554, 0.14131], -0.0, -1.879519]], [[-3.696, 0.12, 1.786, 0.345, 2, [[0.799, 0.973], [1.882, -0.91], [1.537, -0.334]], 1.0], [[0.0, 0.532429, 1.537], [-0.0, 0.183688, -0.250877], 0.0, 0.0]], [[0.116, -0.499, 1.672, 0.665, 2, [[0.119, -0.566], [0.513, -0.365]], 1.0], [[0.0, 0.0], [-0.0, -0.0], 0.116, -0.499]]], [[[-2.82, -0.094, 0.206, 0.78, 3, [[1.785, 0.528], [1.511, 0.405]], 0.8], [[12.48052, 1.2088], [0.132311, 0.324], 0.0, 0.0]], [[-1.164, 1.755, 1.237, 0.583, 4, [[1.08, -0.704], [0.957, 0.84], [0.832, -0.311]], 0.8], [[0.0, 0.275386, 0.6656], [-0.0, -0.16055, -0.388045], 0.0, 1.076388]], [[-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.109, 0.743, 0.738, 0.709, 2, [[0.608, 0.1], [0.211, -0.405]], 0.8], [[0.0, 0.147696], [-0.0, -0.104717], 0.0, 0.665719]], [[-2.556, -2.827, 0.23, 0.153, 3, [[1.823, -0.175], [0.097, 0.367], [0.539, 0.682]], 0.8], [[10.604243, 0.0776, 0.4312], [1.622449, 0.011873, 0.065974], -0.0, -2.435932]], [[-0.52, -1.692, 1.404, 0.252, 2, [[1.649, -0.735], [1.894, -0.042], [0.627, -0.23]], 0.8], [[0.0, 0.0, 0.37037], [-0.0, -0.0, 0.093333], 0.0, -1.56096]], [[-1.435, -0.23, 1.181, 0.57, 4, [[0.586, 0.07], [1.854, 0.393], [1.683, -0.07]], 0.8], [[0.0, 0.0, 1.215072], [-0.0, -0.0, 0.19475], 0.0, 0.0]], [[-1.42, -2.766, 1.952, 0.672, 1, [[0.763, -0.712], [0.115, 0.007], [1.727, -0.475]], 0.8], [[0.0, 0.0, 0.727459], [0.410189, 0.061824, 0.928435], 0.0, -0.032326]]], [[[-1.423, 0.283, 0.863, 0.565, 3, [[1.695, -0.044], [0.068, 0.684], [1.253, 0.865]], 0.0], [[1.648899, 0.0, 0.0], [-0.327926, -0.0, -0.0], 0.0, 0.0]], [[-3.608, 2.414, 1.999, 0.229, 4, [[0.847, 0.163], [0.68, 0.56], [1.391, 0.661]], 0.8], [[0.148102, 0.544, 1.1128], [-0.033915, -0.124576, -0.254831], 0.0, 1.587768]], [[-3.781, 1.225, 1.132, 0.558, 4, [[0.356, 0.879], [1.1, -0.78]], 0.5], [[2.790106, 0.55], [-0.775255, -0.3069], 0.0, 0.0]], [[-3.375, -0.173, 1.484, 0.913, 3, [[0.032, 0.042]], 1.0], [[2.274259], [0.116577], 0.0, 0.0]], [[0.147, 2.069, 0.764, 0.355, 4, [[1.439, 0.632], [0.237, -0.431], [1.556, -0.02]], 0.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.147, 2.069]], [[-1.103, 2.647, 1.706, 0.083, 4, [[0.166, -0.336], [0.433, -0.922], [0.293, -0.641]], 0.8], [[0.065742, 0.3464, 0.2344], [-0.005457, -0.028751, -0.019455], 0.0, 2.555451]], [[-2.728, -0.41, 1.133, 0.579, 4, [[1.078, -0.064]], 0.5], [[2.407767], [0.361871], 0.0, 0.0]], [[-2.702, 1.37, 0.759, 0.879, 3, [[1.886, -0.458], [0.617, -0.655], [0.032, -0.208]], 0.0], [[3.559947, 0.0, 0.0], [-1.805007, -0.0, -0.0], 0.0, 0.0]]]]
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[[10.854749], [-3.519553], 0.0, 0.0][[10.854749], [-3.519553], 0.0, 0.0]Passed
case 1[[0.0, 0.30625], [0.0, 0.24255], 0.0, -1.167576][[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576]Failed
case 2[[0.0, 0.152692, 0.426], [-1.449581, -0.2256, -0.1408], 0.0, -0.0][[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0]Failed
case 3[[0.0, 0.669876, 0.021], [-0.0, 0.552648, 0.017325], 0.0, -1.992425][[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425]Failed
case 4[[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568][[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568]Passed
case 5[[4.101466, 0.241], [-0.902322, -0.05302], 0.0, 0.28056][[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056]Failed
case 6[[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0][[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0]Passed
case 7[[0.060091, 1.809, 0.04], [0.009074, 0.273159, 0.00604], 0.0, -2.658621][[1.909091, 0.0, 0.0], [0.288273, -0.0, -0.0], 0.0, -2.658621]Failed

SHA-256 / 3fabc89b16058a71a8d87edb34f97ffc26df42ddde3219a823305be707a306a5

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 * 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 = [[[[-3.886, 1.26, 0.358, 0.718, 2, [[0.002, 0.092]], 1.0], [[10.854749], [-3.519553], 0.0, 0.0]], [[-0.147, -1.284, 0.48, 0.792, 3, [[1.388, -0.326], [0.337, 0.037]], 0.0], [[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576]], [[-0.717, 2.25, 1.239, 0.977, 1, [[1.943, 0.223], [1.215, -0.282], [0.426, -0.176]], 0.8], [[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0]], [[-1.151, -2.942, 1.666, 0.825, 4, [[0.959, 0.822], [1.872, 0.85], [0.021, -0.236]], 0.0], [[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425]], [[0.385, 0.568, 0.63, 0.737, 3, [[0.414, -0.654], [0.917, 0.611], [1.569, 0.695]], 1.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568]], [[-1.902, 0.699, 0.438, 0.22, 3, [[1.049, -0.312], [0.241, -0.124]], 0.5], [[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056]], [[-2.972, 0.049, 0.405, 0.199, 3, [[1.938, 0.409], [0.746, 0.957], [1.714, -0.847]], 1.0], [[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0]], [[-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.559, 1.606, 1.849, 0.194, 2, [[0.671, 0.987], [1.821, 0.059], [0.496, -0.817]], 0.5], [[0.0, 0.595158, 0.248], [-0.0, -0.115461, -0.048112], 0.0, 1.303554]], [[-1.753, -2.782, 0.464, 0.155, 4, [[0.504, 0.834], [1.119, 0.469], [1.759, -0.733]], 0.5], [[2.339017, 0.5595, 0.8795], [0.362548, 0.086722, 0.136322], 0.0, -2.510285]], [[-0.349, 0.709, 1.805, 0.199, 2, [[1.996, 0.558], [0.968, -0.267], [0.402, -0.027]], 0.5], [[0.0, 0.0, 0.193352], [-0.0, -0.0, -0.038477], 0.0, 0.639549]], [[-1.981, -2.068, 0.556, 0.937, 2, [[0.091, 0.771], [0.183, 0.742], [0.226, 0.981]], 1.0], [[3.15395, 0.183, 0.226], [2.955251, 0.171471, 0.211762], 0.0, -0.211803]], [[-0.308, 2.306, 0.481, 0.691, 2, [[1.507, 0.678]], 0.5], [[0.640333], [-0.44247], 0.0, 2.093172]], [[-2.225, -2.18, 0.935, 0.027, 4, [[0.823, -0.309], [1.755, 0.946], [0.134, -0.423]], 0.5], [[1.435179, 0.8775, 0.067], [0.03875, 0.023692, 0.001809], 0.0, -2.119925]], [[-1.602, -1.851, 1.049, 0.189, 2, [[0.015, 0.622], [1.841, 0.588], [0.748, 0.191]], 1.0], [[0.0, 0.779169, 0.748], [-0.0, 0.147263, 0.141372], 0.0, -1.548222]], [[-1.5, 0.461, 1.602, 0.795, 3, [[0.381, -0.102], [0.711, 0.701]], 0.0], [[0.93633, 0.0], [-0.287765, -0.0], 0.0, 0.0]]], [[[-0.543, -0.389, 0.316, 0.68, 4, [[1.044, -0.289], [0.189, -0.756], [1.244, -0.187]], 0.8], [[0.571954, 0.1512, 0.9952], [0.388929, 0.102816, 0.676736], 0.0, -0.01976]], [[-2.82, 1.429, 1.478, 0.114, 2, [[0.588, -0.945], [0.049, -0.934], [1.67, 0.896]], 0.0], [[1.907984, 0.0, 0.0], [-0.21751, -0.0, -0.0], 0.0, 1.10752]], [[-3.678, 1.879, 1.356, 0.559, 1, [[1.998, -0.372], [1.803, -0.832]], 0.8], [[1.269989, 1.4424], [-0.720093, -0.6656], 0.0, 0.0]], [[-0.181, 1.833, 1.447, 0.671, 2, [[1.934, 0.697]], 0.5], [[0.125086], [-0.083933], 0.0, 1.711549]], [[-3.591, -2.324, 0.75, 0.86, 3, [[1.451, 0.31], [1.726, -0.878], [1.597, 0.48]], 0.0], [[4.788, 0.0, 0.0], [3.098667, -0.0, -0.0], 0.0, 0.0]], [[-1.345, -2.266, 0.903, 0.154, 1, [[1.454, -0.867], [0.873, 0.725], [1.147, 0.427]], 0.8], [[0.0, 0.57188, 0.9176], [0.179133, 0.107554, 0.14131], -0.0, -1.879519]], [[-3.696, 0.12, 1.786, 0.345, 2, [[0.799, 0.973], [1.882, -0.91], [1.537, -0.334]], 1.0], [[0.0, 0.532429, 1.537], [-0.0, 0.183688, -0.250877], 0.0, 0.0]], [[0.116, -0.499, 1.672, 0.665, 2, [[0.119, -0.566], [0.513, -0.365]], 1.0], [[0.0, 0.0], [-0.0, -0.0], 0.116, -0.499]]], [[[-2.82, -0.094, 0.206, 0.78, 3, [[1.785, 0.528], [1.511, 0.405]], 0.8], [[12.48052, 1.2088], [0.132311, 0.324], 0.0, 0.0]], [[-1.164, 1.755, 1.237, 0.583, 4, [[1.08, -0.704], [0.957, 0.84], [0.832, -0.311]], 0.8], [[0.0, 0.275386, 0.6656], [-0.0, -0.16055, -0.388045], 0.0, 1.076388]], [[-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.109, 0.743, 0.738, 0.709, 2, [[0.608, 0.1], [0.211, -0.405]], 0.8], [[0.0, 0.147696], [-0.0, -0.104717], 0.0, 0.665719]], [[-2.556, -2.827, 0.23, 0.153, 3, [[1.823, -0.175], [0.097, 0.367], [0.539, 0.682]], 0.8], [[10.604243, 0.0776, 0.4312], [1.622449, 0.011873, 0.065974], -0.0, -2.435932]], [[-0.52, -1.692, 1.404, 0.252, 2, [[1.649, -0.735], [1.894, -0.042], [0.627, -0.23]], 0.8], [[0.0, 0.0, 0.37037], [-0.0, -0.0, 0.093333], 0.0, -1.56096]], [[-1.435, -0.23, 1.181, 0.57, 4, [[0.586, 0.07], [1.854, 0.393], [1.683, -0.07]], 0.8], [[0.0, 0.0, 1.215072], [-0.0, -0.0, 0.19475], 0.0, 0.0]], [[-1.42, -2.766, 1.952, 0.672, 1, [[0.763, -0.712], [0.115, 0.007], [1.727, -0.475]], 0.8], [[0.0, 0.0, 0.727459], [0.410189, 0.061824, 0.928435], 0.0, -0.032326]]], [[[-1.423, 0.283, 0.863, 0.565, 3, [[1.695, -0.044], [0.068, 0.684], [1.253, 0.865]], 0.0], [[1.648899, 0.0, 0.0], [-0.327926, -0.0, -0.0], 0.0, 0.0]], [[-3.608, 2.414, 1.999, 0.229, 4, [[0.847, 0.163], [0.68, 0.56], [1.391, 0.661]], 0.8], [[0.148102, 0.544, 1.1128], [-0.033915, -0.124576, -0.254831], 0.0, 1.587768]], [[-3.781, 1.225, 1.132, 0.558, 4, [[0.356, 0.879], [1.1, -0.78]], 0.5], [[2.790106, 0.55], [-0.775255, -0.3069], 0.0, 0.0]], [[-3.375, -0.173, 1.484, 0.913, 3, [[0.032, 0.042]], 1.0], [[2.274259], [0.116577], 0.0, 0.0]], [[0.147, 2.069, 0.764, 0.355, 4, [[1.439, 0.632], [0.237, -0.431], [1.556, -0.02]], 0.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.147, 2.069]], [[-1.103, 2.647, 1.706, 0.083, 4, [[0.166, -0.336], [0.433, -0.922], [0.293, -0.641]], 0.8], [[0.065742, 0.3464, 0.2344], [-0.005457, -0.028751, -0.019455], 0.0, 2.555451]], [[-2.728, -0.41, 1.133, 0.579, 4, [[1.078, -0.064]], 0.5], [[2.407767], [0.361871], 0.0, 0.0]], [[-2.702, 1.37, 0.759, 0.879, 3, [[1.886, -0.458], [0.617, -0.655], [0.032, -0.208]], 0.0], [[3.559947, 0.0, 0.0], [-1.805007, -0.0, -0.0], 0.0, 0.0]]]]
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[[10.854749], [-3.519553], 0.0, 0.0][[10.854749], [-3.519553], 0.0, 0.0]Passed
case 1[[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576][[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576]Passed
case 2[[0.0, 0.306052, 0.27264], [-1.214919, -0.460262, -0.1408], 0.0, -0.0][[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0]Failed
case 3[[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425][[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425]Passed
case 4[[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568][[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568]Passed
case 5[[4.282216, 0.06025], [-0.942087, -0.013255], 0.0, 0.28056][[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056]Failed
case 6[[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0][[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0]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 / 3957871a2a584aa8bf26db13f4ed7947e4496fd8a80220eca1df29b77fa1d705

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 = [[[[-3.886, 1.26, 0.358, 0.718, 2, [[0.002, 0.092]], 1.0], [[10.854749], [-3.519553], 0.0, 0.0]], [[-0.147, -1.284, 0.48, 0.792, 3, [[1.388, -0.326], [0.337, 0.037]], 0.0], [[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576]], [[-0.717, 2.25, 1.239, 0.977, 1, [[1.943, 0.223], [1.215, -0.282], [0.426, -0.176]], 0.8], [[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0]], [[-1.151, -2.942, 1.666, 0.825, 4, [[0.959, 0.822], [1.872, 0.85], [0.021, -0.236]], 0.0], [[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425]], [[0.385, 0.568, 0.63, 0.737, 3, [[0.414, -0.654], [0.917, 0.611], [1.569, 0.695]], 1.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568]], [[-1.902, 0.699, 0.438, 0.22, 3, [[1.049, -0.312], [0.241, -0.124]], 0.5], [[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056]], [[-2.972, 0.049, 0.405, 0.199, 3, [[1.938, 0.409], [0.746, 0.957], [1.714, -0.847]], 1.0], [[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0]], [[-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.559, 1.606, 1.849, 0.194, 2, [[0.671, 0.987], [1.821, 0.059], [0.496, -0.817]], 0.5], [[0.0, 0.595158, 0.248], [-0.0, -0.115461, -0.048112], 0.0, 1.303554]], [[-1.753, -2.782, 0.464, 0.155, 4, [[0.504, 0.834], [1.119, 0.469], [1.759, -0.733]], 0.5], [[2.339017, 0.5595, 0.8795], [0.362548, 0.086722, 0.136322], 0.0, -2.510285]], [[-0.349, 0.709, 1.805, 0.199, 2, [[1.996, 0.558], [0.968, -0.267], [0.402, -0.027]], 0.5], [[0.0, 0.0, 0.193352], [-0.0, -0.0, -0.038477], 0.0, 0.639549]], [[-1.981, -2.068, 0.556, 0.937, 2, [[0.091, 0.771], [0.183, 0.742], [0.226, 0.981]], 1.0], [[3.15395, 0.183, 0.226], [2.955251, 0.171471, 0.211762], 0.0, -0.211803]], [[-0.308, 2.306, 0.481, 0.691, 2, [[1.507, 0.678]], 0.5], [[0.640333], [-0.44247], 0.0, 2.093172]], [[-2.225, -2.18, 0.935, 0.027, 4, [[0.823, -0.309], [1.755, 0.946], [0.134, -0.423]], 0.5], [[1.435179, 0.8775, 0.067], [0.03875, 0.023692, 0.001809], 0.0, -2.119925]], [[-1.602, -1.851, 1.049, 0.189, 2, [[0.015, 0.622], [1.841, 0.588], [0.748, 0.191]], 1.0], [[0.0, 0.779169, 0.748], [-0.0, 0.147263, 0.141372], 0.0, -1.548222]], [[-1.5, 0.461, 1.602, 0.795, 3, [[0.381, -0.102], [0.711, 0.701]], 0.0], [[0.93633, 0.0], [-0.287765, -0.0], 0.0, 0.0]]], [[[-0.543, -0.389, 0.316, 0.68, 4, [[1.044, -0.289], [0.189, -0.756], [1.244, -0.187]], 0.8], [[0.571954, 0.1512, 0.9952], [0.388929, 0.102816, 0.676736], 0.0, -0.01976]], [[-2.82, 1.429, 1.478, 0.114, 2, [[0.588, -0.945], [0.049, -0.934], [1.67, 0.896]], 0.0], [[1.907984, 0.0, 0.0], [-0.21751, -0.0, -0.0], 0.0, 1.10752]], [[-3.678, 1.879, 1.356, 0.559, 1, [[1.998, -0.372], [1.803, -0.832]], 0.8], [[1.269989, 1.4424], [-0.720093, -0.6656], 0.0, 0.0]], [[-0.181, 1.833, 1.447, 0.671, 2, [[1.934, 0.697]], 0.5], [[0.125086], [-0.083933], 0.0, 1.711549]], [[-3.591, -2.324, 0.75, 0.86, 3, [[1.451, 0.31], [1.726, -0.878], [1.597, 0.48]], 0.0], [[4.788, 0.0, 0.0], [3.098667, -0.0, -0.0], 0.0, 0.0]], [[-1.345, -2.266, 0.903, 0.154, 1, [[1.454, -0.867], [0.873, 0.725], [1.147, 0.427]], 0.8], [[0.0, 0.57188, 0.9176], [0.179133, 0.107554, 0.14131], -0.0, -1.879519]], [[-3.696, 0.12, 1.786, 0.345, 2, [[0.799, 0.973], [1.882, -0.91], [1.537, -0.334]], 1.0], [[0.0, 0.532429, 1.537], [-0.0, 0.183688, -0.250877], 0.0, 0.0]], [[0.116, -0.499, 1.672, 0.665, 2, [[0.119, -0.566], [0.513, -0.365]], 1.0], [[0.0, 0.0], [-0.0, -0.0], 0.116, -0.499]]], [[[-2.82, -0.094, 0.206, 0.78, 3, [[1.785, 0.528], [1.511, 0.405]], 0.8], [[12.48052, 1.2088], [0.132311, 0.324], 0.0, 0.0]], [[-1.164, 1.755, 1.237, 0.583, 4, [[1.08, -0.704], [0.957, 0.84], [0.832, -0.311]], 0.8], [[0.0, 0.275386, 0.6656], [-0.0, -0.16055, -0.388045], 0.0, 1.076388]], [[-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.109, 0.743, 0.738, 0.709, 2, [[0.608, 0.1], [0.211, -0.405]], 0.8], [[0.0, 0.147696], [-0.0, -0.104717], 0.0, 0.665719]], [[-2.556, -2.827, 0.23, 0.153, 3, [[1.823, -0.175], [0.097, 0.367], [0.539, 0.682]], 0.8], [[10.604243, 0.0776, 0.4312], [1.622449, 0.011873, 0.065974], -0.0, -2.435932]], [[-0.52, -1.692, 1.404, 0.252, 2, [[1.649, -0.735], [1.894, -0.042], [0.627, -0.23]], 0.8], [[0.0, 0.0, 0.37037], [-0.0, -0.0, 0.093333], 0.0, -1.56096]], [[-1.435, -0.23, 1.181, 0.57, 4, [[0.586, 0.07], [1.854, 0.393], [1.683, -0.07]], 0.8], [[0.0, 0.0, 1.215072], [-0.0, -0.0, 0.19475], 0.0, 0.0]], [[-1.42, -2.766, 1.952, 0.672, 1, [[0.763, -0.712], [0.115, 0.007], [1.727, -0.475]], 0.8], [[0.0, 0.0, 0.727459], [0.410189, 0.061824, 0.928435], 0.0, -0.032326]]], [[[-1.423, 0.283, 0.863, 0.565, 3, [[1.695, -0.044], [0.068, 0.684], [1.253, 0.865]], 0.0], [[1.648899, 0.0, 0.0], [-0.327926, -0.0, -0.0], 0.0, 0.0]], [[-3.608, 2.414, 1.999, 0.229, 4, [[0.847, 0.163], [0.68, 0.56], [1.391, 0.661]], 0.8], [[0.148102, 0.544, 1.1128], [-0.033915, -0.124576, -0.254831], 0.0, 1.587768]], [[-3.781, 1.225, 1.132, 0.558, 4, [[0.356, 0.879], [1.1, -0.78]], 0.5], [[2.790106, 0.55], [-0.775255, -0.3069], 0.0, 0.0]], [[-3.375, -0.173, 1.484, 0.913, 3, [[0.032, 0.042]], 1.0], [[2.274259], [0.116577], 0.0, 0.0]], [[0.147, 2.069, 0.764, 0.355, 4, [[1.439, 0.632], [0.237, -0.431], [1.556, -0.02]], 0.0], [[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.147, 2.069]], [[-1.103, 2.647, 1.706, 0.083, 4, [[0.166, -0.336], [0.433, -0.922], [0.293, -0.641]], 0.8], [[0.065742, 0.3464, 0.2344], [-0.005457, -0.028751, -0.019455], 0.0, 2.555451]], [[-2.728, -0.41, 1.133, 0.579, 4, [[1.078, -0.064]], 0.5], [[2.407767], [0.361871], 0.0, 0.0]], [[-2.702, 1.37, 0.759, 0.879, 3, [[1.886, -0.458], [0.617, -0.655], [0.032, -0.208]], 0.0], [[3.559947, 0.0, 0.0], [-1.805007, -0.0, -0.0], 0.0, 0.0]]]]
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[[10.854749], [-3.519553], 0.0, 0.0][[10.854749], [-3.519553], 0.0, 0.0]Passed
case 1[[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576][[0.30625, 0.0], [0.24255, -0.0], 0.0, -1.167576]Passed
case 2[[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0][[0.0, 0.237892, 0.3408], [-1.449581, -0.2256, -0.1408], 0.0, -0.0]Passed
case 3[[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425][[0.690876, 0.0, 0.0], [0.569973, -0.0, -0.0], 0.0, -1.992425]Passed
case 4[[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568][[0.0, 0.0, 0.0], [-0.0, -0.0, -0.0], 0.385, 0.568]Passed
case 5[[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056][[4.221966, 0.1205], [-0.928832, -0.02651], 0.0, 0.28056]Passed
case 6[[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0][[4.878272, 0.746, 1.714], [-0.230988, 0.148454, -0.038454], 0.0, 0.0]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 / ef895205dfce50a26c79e69d43a0cd909986f916789acaf4b65cc259bd1c4889

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

Case digest / 10e9031615e45da33eb899a270786c193665f4dd98e19d39707b88d0452303b1