FAILURE MAP
← Case archive

FA-87986 / Inverse kinematics solvers / Open access

Unreachable FABRIK stretch uses the base distance for every joint · case 01

Stretched chains for unreachable targets have compressed outer links.

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

ROOT CAUSE

Each joint is interpolated with the fixed base-to-target distance instead of its own distance to the target.

VERIFIED REPAIR

Recompute r as the distance from the current joint p[i] to the target.

Unsuccessful approach: Measuring from the not-yet-updated child joint uses a stale position.

Case contract

Input [points, target, tol, iters]: planar joint positions from base to tip. Link lengths come from the initial points. If the base-to-target distance exceeds the total length, stretch every link toward the target and return ["unreachable", points]. Otherwise run FABRIK passes (tip to target and backward, base re-anchored, forward) while the tip is farther than tol and fewer than iters passes ran; return [status, passes, points] with status "reached" when the final tip error <= tol else "max-iterations"; coordinates rounded to 4 places.

Why this case matters

Inverse kinematics code turns task-space goals into joint commands; a wrong branch, sign, limit or update order sends a real arm to the wrong pose.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    pts,t,tol,iters=x
    p=[[float(v) for v in q] for q in pts]
    d=[math.dist(p[i],p[i+1]) for i in range(len(p)-1)]
    base=p[0][:]
    if math.dist(base,t)>sum(d):
        for i in range(len(d)):
            r=math.dist(base,t)
            lam=d[i]/r
            p[i+1]=[(1-lam)*p[i][0]+lam*t[0],(1-lam)*p[i][1]+lam*t[1]]
        return ['unreachable',[[round(v,4) for v in q] for q in p]]
    count=0
    while math.dist(p[-1],t)>tol and count<iters:
        count+=1
        p[-1]=[float(t[0]),float(t[1])]
        for i in range(len(p)-2,-1,-1):
            r=math.dist(p[i+1],p[i])
            lam=d[i]/r
            p[i]=[(1-lam)*p[i+1][0]+lam*p[i][0],(1-lam)*p[i+1][1]+lam*p[i][1]]
        p[0]=base[:]
        for i in range(len(p)-1):
            r=math.dist(p[i+1],p[i])
            lam=d[i]/r
            p[i+1]=[(1-lam)*p[i][0]+lam*p[i+1][0],(1-lam)*p[i][1]+lam*p[i+1][1]]
    status='reached' if math.dist(p[-1],t)<=tol else 'max-iterations'
    return [status,count,[[round(v,4) for v in q] for q in p]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['three unequal links', [[[[0, 0], [2, 0], [2.5, 0], [4, 0]], [1, 2.5], 0.001, 20]], ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]], [['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['pass budget exhausted', [[[[0, 0], [1, 0], [1.5, 0], [3, 0]], [-1, 1.5], 1e-09, 2]], ['max-iterations', 2, [[0.0, 0.0], [0.809, 0.5878], [0.3308, 0.7337], [-0.9691, 1.4822]]]], ['offset base', [[[[1, 1], [2, 1], [3, 1]], [2, 2.5], 0.001, 10]], ['reached', 6, [[1.0, 1.0], [1.8605, 1.5094], [1.9999, 2.4996]]]], ['near full stretch reachable', [[[[0, 0], [0, 1], [0, 3]], [2.9, 0.2], 0.001, 15]], ['reached', 15, [[0.0, 0.0], [0.9105, 0.4134], [2.8991, 0.2001]]]]], [['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['pass budget exhausted', [[[[0, 0], [1, 0], [1.5, 0], [3, 0]], [-1, 1.5], 1e-09, 2]], ['max-iterations', 2, [[0.0, 0.0], [0.809, 0.5878], [0.3308, 0.7337], [-0.9691, 1.4822]]]], ['offset base', [[[[1, 1], [2, 1], [3, 1]], [2, 2.5], 0.001, 10]], ['reached', 6, [[1.0, 1.0], [1.8605, 1.5094], [1.9999, 2.4996]]]], ['near full stretch reachable', [[[[0, 0], [0, 1], [0, 3]], [2.9, 0.2], 0.001, 15]], ['reached', 15, [[0.0, 0.0], [0.9105, 0.4134], [2.8991, 0.2001]]]], ['tip near base unequal', [[[[0, 0], [3, 0], [4, 0]], [2, 0.5], 0.001, 20]], ['max-iterations', 20, [[0.0, 0.0], [2.9861, 0.2886], [2.0083, 0.4982]]]], ['unreachable from offset base', [[[[2, 0], [2, 1], [2, 3]], [2, -4], 0.001, 10]], ['unreachable', [[2.0, 0.0], [2.0, -1.0], [2.0, -3.0]]]], ['start already inside tolerance', [[[[0, 0], [1, 1], [2, 1]], [2, 1.05], 0.1, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 1.0], [2.0, 1.0]]]]], [['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['tip near base unequal', [[[[0, 0], [3, 0], [4, 0]], [2, 0.5], 0.001, 20]], ['max-iterations', 20, [[0.0, 0.0], [2.9861, 0.2886], [2.0083, 0.4982]]]], ['unreachable from offset base', [[[[2, 0], [2, 1], [2, 3]], [2, -4], 0.001, 10]], ['unreachable', [[2.0, 0.0], [2.0, -1.0], [2.0, -3.0]]]], ['start already inside tolerance', [[[[0, 0], [1, 1], [2, 1]], [2, 1.05], 0.1, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 1.0], [2.0, 1.0]]]], ['zero passes allowed', [[[[0, 0], [1, 0], [2, 0]], [0, 2], 0.001, 0]], ['max-iterations', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]], [['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['three unequal links', [[[[0, 0], [2, 0], [2.5, 0], [4, 0]], [1, 2.5], 0.001, 20]], ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['zero passes allowed', [[[[0, 0], [1, 0], [2, 0]], [0, 2], 0.001, 0]], ['max-iterations', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]]]
for label, args, expected in fixtures[N-1]:
    check(label, 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
two links bend up['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]Passed
unequal links['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]Passed
three unequal links['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]Passed
unreachable along x['unreachable', [[0.0, 0.0], [1.0, 0.0], [1.8, 0.0]]]['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]Failed
unreachable diagonal unequal['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.0087, 2.0087]]]['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]Failed
single link one pass['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]Passed
already on target zero tol['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]Passed

SHA-256 / 49449c6f25d2b143c51bb9560b1a0a281e3f141f874e15a348184b381a4894d0

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    pts,t,tol,iters=x
    p=[[float(v) for v in q] for q in pts]
    d=[math.dist(p[i],p[i+1]) for i in range(len(p)-1)]
    base=p[0][:]
    if math.dist(base,t)>sum(d):
        for i in range(len(d)):
            r=math.dist(p[i+1],t)
            lam=d[i]/r
            p[i+1]=[(1-lam)*p[i][0]+lam*t[0],(1-lam)*p[i][1]+lam*t[1]]
        return ['unreachable',[[round(v,4) for v in q] for q in p]]
    count=0
    while math.dist(p[-1],t)>tol and count<iters:
        count+=1
        p[-1]=[float(t[0]),float(t[1])]
        for i in range(len(p)-2,-1,-1):
            r=math.dist(p[i+1],p[i])
            lam=d[i]/r
            p[i]=[(1-lam)*p[i+1][0]+lam*p[i][0],(1-lam)*p[i+1][1]+lam*p[i][1]]
        p[0]=base[:]
        for i in range(len(p)-1):
            r=math.dist(p[i+1],p[i])
            lam=d[i]/r
            p[i+1]=[(1-lam)*p[i][0]+lam*p[i+1][0],(1-lam)*p[i][1]+lam*p[i+1][1]]
    status='reached' if math.dist(p[-1],t)<=tol else 'max-iterations'
    return [status,count,[[round(v,4) for v in q] for q in p]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['three unequal links', [[[[0, 0], [2, 0], [2.5, 0], [4, 0]], [1, 2.5], 0.001, 20]], ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]], [['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['pass budget exhausted', [[[[0, 0], [1, 0], [1.5, 0], [3, 0]], [-1, 1.5], 1e-09, 2]], ['max-iterations', 2, [[0.0, 0.0], [0.809, 0.5878], [0.3308, 0.7337], [-0.9691, 1.4822]]]], ['offset base', [[[[1, 1], [2, 1], [3, 1]], [2, 2.5], 0.001, 10]], ['reached', 6, [[1.0, 1.0], [1.8605, 1.5094], [1.9999, 2.4996]]]], ['near full stretch reachable', [[[[0, 0], [0, 1], [0, 3]], [2.9, 0.2], 0.001, 15]], ['reached', 15, [[0.0, 0.0], [0.9105, 0.4134], [2.8991, 0.2001]]]]], [['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['pass budget exhausted', [[[[0, 0], [1, 0], [1.5, 0], [3, 0]], [-1, 1.5], 1e-09, 2]], ['max-iterations', 2, [[0.0, 0.0], [0.809, 0.5878], [0.3308, 0.7337], [-0.9691, 1.4822]]]], ['offset base', [[[[1, 1], [2, 1], [3, 1]], [2, 2.5], 0.001, 10]], ['reached', 6, [[1.0, 1.0], [1.8605, 1.5094], [1.9999, 2.4996]]]], ['near full stretch reachable', [[[[0, 0], [0, 1], [0, 3]], [2.9, 0.2], 0.001, 15]], ['reached', 15, [[0.0, 0.0], [0.9105, 0.4134], [2.8991, 0.2001]]]], ['tip near base unequal', [[[[0, 0], [3, 0], [4, 0]], [2, 0.5], 0.001, 20]], ['max-iterations', 20, [[0.0, 0.0], [2.9861, 0.2886], [2.0083, 0.4982]]]], ['unreachable from offset base', [[[[2, 0], [2, 1], [2, 3]], [2, -4], 0.001, 10]], ['unreachable', [[2.0, 0.0], [2.0, -1.0], [2.0, -3.0]]]], ['start already inside tolerance', [[[[0, 0], [1, 1], [2, 1]], [2, 1.05], 0.1, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 1.0], [2.0, 1.0]]]]], [['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['tip near base unequal', [[[[0, 0], [3, 0], [4, 0]], [2, 0.5], 0.001, 20]], ['max-iterations', 20, [[0.0, 0.0], [2.9861, 0.2886], [2.0083, 0.4982]]]], ['unreachable from offset base', [[[[2, 0], [2, 1], [2, 3]], [2, -4], 0.001, 10]], ['unreachable', [[2.0, 0.0], [2.0, -1.0], [2.0, -3.0]]]], ['start already inside tolerance', [[[[0, 0], [1, 1], [2, 1]], [2, 1.05], 0.1, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 1.0], [2.0, 1.0]]]], ['zero passes allowed', [[[[0, 0], [1, 0], [2, 0]], [0, 2], 0.001, 0]], ['max-iterations', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]], [['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['three unequal links', [[[[0, 0], [2, 0], [2.5, 0], [4, 0]], [1, 2.5], 0.001, 20]], ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['zero passes allowed', [[[[0, 0], [1, 0], [2, 0]], [0, 2], 0.001, 0]], ['max-iterations', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]]]
for label, args, expected in fixtures[N-1]:
    check(label, 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
two links bend up['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]Passed
unequal links['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]Passed
three unequal links['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]Passed
unreachable along x['unreachable', [[0.0, 0.0], [1.25, 0.0], [2.5, 0.0]]]['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]Failed
unreachable diagonal unequal['unreachable', [[0.0, 0.0], [2.4807, 2.4807], [2.9611, 2.9611]]]['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]Failed
single link one pass['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]Passed
already on target zero tol['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]Passed

SHA-256 / 7a15ee4deab11ec9fc365e82ab7444709a3b78c39d3f0b3b81c0145d4e5f4005

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    pts,t,tol,iters=x
    p=[[float(v) for v in q] for q in pts]
    d=[math.dist(p[i],p[i+1]) for i in range(len(p)-1)]
    base=p[0][:]
    if math.dist(base,t)>sum(d):
        for i in range(len(d)):
            r=math.dist(p[i],t)
            lam=d[i]/r
            p[i+1]=[(1-lam)*p[i][0]+lam*t[0],(1-lam)*p[i][1]+lam*t[1]]
        return ['unreachable',[[round(v,4) for v in q] for q in p]]
    count=0
    while math.dist(p[-1],t)>tol and count<iters:
        count+=1
        p[-1]=[float(t[0]),float(t[1])]
        for i in range(len(p)-2,-1,-1):
            r=math.dist(p[i+1],p[i])
            lam=d[i]/r
            p[i]=[(1-lam)*p[i+1][0]+lam*p[i][0],(1-lam)*p[i+1][1]+lam*p[i][1]]
        p[0]=base[:]
        for i in range(len(p)-1):
            r=math.dist(p[i+1],p[i])
            lam=d[i]/r
            p[i+1]=[(1-lam)*p[i][0]+lam*p[i+1][0],(1-lam)*p[i][1]+lam*p[i+1][1]]
    status='reached' if math.dist(p[-1],t)<=tol else 'max-iterations'
    return [status,count,[[round(v,4) for v in q] for q in p]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['three unequal links', [[[[0, 0], [2, 0], [2.5, 0], [4, 0]], [1, 2.5], 0.001, 20]], ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]], [['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['pass budget exhausted', [[[[0, 0], [1, 0], [1.5, 0], [3, 0]], [-1, 1.5], 1e-09, 2]], ['max-iterations', 2, [[0.0, 0.0], [0.809, 0.5878], [0.3308, 0.7337], [-0.9691, 1.4822]]]], ['offset base', [[[[1, 1], [2, 1], [3, 1]], [2, 2.5], 0.001, 10]], ['reached', 6, [[1.0, 1.0], [1.8605, 1.5094], [1.9999, 2.4996]]]], ['near full stretch reachable', [[[[0, 0], [0, 1], [0, 3]], [2.9, 0.2], 0.001, 15]], ['reached', 15, [[0.0, 0.0], [0.9105, 0.4134], [2.8991, 0.2001]]]]], [['already on target zero tol', [[[[0, 0], [1, 0], [2, 0]], [2, 0], 0, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['pass budget exhausted', [[[[0, 0], [1, 0], [1.5, 0], [3, 0]], [-1, 1.5], 1e-09, 2]], ['max-iterations', 2, [[0.0, 0.0], [0.809, 0.5878], [0.3308, 0.7337], [-0.9691, 1.4822]]]], ['offset base', [[[[1, 1], [2, 1], [3, 1]], [2, 2.5], 0.001, 10]], ['reached', 6, [[1.0, 1.0], [1.8605, 1.5094], [1.9999, 2.4996]]]], ['near full stretch reachable', [[[[0, 0], [0, 1], [0, 3]], [2.9, 0.2], 0.001, 15]], ['reached', 15, [[0.0, 0.0], [0.9105, 0.4134], [2.8991, 0.2001]]]], ['tip near base unequal', [[[[0, 0], [3, 0], [4, 0]], [2, 0.5], 0.001, 20]], ['max-iterations', 20, [[0.0, 0.0], [2.9861, 0.2886], [2.0083, 0.4982]]]], ['unreachable from offset base', [[[[2, 0], [2, 1], [2, 3]], [2, -4], 0.001, 10]], ['unreachable', [[2.0, 0.0], [2.0, -1.0], [2.0, -3.0]]]], ['start already inside tolerance', [[[[0, 0], [1, 1], [2, 1]], [2, 1.05], 0.1, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 1.0], [2.0, 1.0]]]]], [['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['tip near base unequal', [[[[0, 0], [3, 0], [4, 0]], [2, 0.5], 0.001, 20]], ['max-iterations', 20, [[0.0, 0.0], [2.9861, 0.2886], [2.0083, 0.4982]]]], ['unreachable from offset base', [[[[2, 0], [2, 1], [2, 3]], [2, -4], 0.001, 10]], ['unreachable', [[2.0, 0.0], [2.0, -1.0], [2.0, -3.0]]]], ['start already inside tolerance', [[[[0, 0], [1, 1], [2, 1]], [2, 1.05], 0.1, 5]], ['reached', 0, [[0.0, 0.0], [1.0, 1.0], [2.0, 1.0]]]], ['zero passes allowed', [[[[0, 0], [1, 0], [2, 0]], [0, 2], 0.001, 0]], ['max-iterations', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]], [['two links bend up', [[[[0, 0], [1, 0], [2, 0]], [1, 1], 0.001, 10]], ['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]], ['unequal links', [[[[0, 0], [2, 0], [3, 0]], [1, 2], 0.001, 10]], ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]], ['three unequal links', [[[[0, 0], [2, 0], [2.5, 0], [4, 0]], [1, 2.5], 0.001, 20]], ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]], ['unreachable along x', [[[[0, 0], [1, 0], [2, 0]], [5, 0], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]], ['unreachable diagonal unequal', [[[[0, 0], [2, 1], [3, 1]], [4, 4], 0.001, 10]], ['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]], ['single link one pass', [[[[0, 0], [2, 0]], [0, 2], 1e-06, 1]], ['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]], ['zero passes allowed', [[[[0, 0], [1, 0], [2, 0]], [0, 2], 0.001, 0]], ['max-iterations', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]]]]
for label, args, expected in fixtures[N-1]:
    check(label, 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
two links bend up['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]['reached', 1, [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]]Passed
unequal links['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]]Passed
three unequal links['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]]Passed
unreachable along x['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]['unreachable', [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]Passed
unreachable diagonal unequal['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]['unreachable', [[0.0, 0.0], [1.5811, 1.5811], [2.2882, 2.2882]]]Passed
single link one pass['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]['reached', 1, [[0.0, 0.0], [0.0, 2.0]]]Passed
already on target zero tol['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]['reached', 0, [[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]]Passed

SHA-256 / 7be06a4ee2398adde467d64e8bac20fa482e41228d1dbfbd198125e2db7c41bb

Verification & scope

Deterministic planar or low-dimensional teaching model with a stipulated convention; not a general robotics library. 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:51:03.922536+00:00.

Case digest / 71e06583f0503ef854ddaeb46396a6cedbcdce24b271b40a57598b5853d63b20