FA-87971 / Inverse kinematics solvers / Open access
FABRIK forward pass starts from a drifted base · case 01
The returned chain has its base displaced from the fixed mount.
ROOT CAUSE
The backward pass result for the base is kept; the base is never restored before the forward pass.
VERIFIED REPAIR
Reset p[0] to the stored base between the backward and forward passes.
Unsuccessful approach: Re-anchoring only after the forward pass restores the coordinate but leaves the first link the wrong length.
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(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]]
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]]]]], [['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 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]]]], ['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]]]], ['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]]]], ['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]]]], ['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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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', 1, [[-0.1421, -0.1086], [1.4472, 1.1056], [1.0, 2.0]]] | ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]] | Failed |
| three unequal links | ['reached', 1, [[-0.0007, -0.0003], [1.8642, 0.7224], [1.7717, 1.2138], [1.0, 2.5]]] | ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]] | Failed |
| 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 / b5903a1eabf739e5e114b953f9f44e187a4eeec1574eefa867351e283bdb8836
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],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]]
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]]
p[0]=base[:]
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]]]]], [['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 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]]]], ['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]]]], ['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]]]], ['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]]]], ['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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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', 1, [[0.0, 0.0], [1.4472, 1.1056], [1.0, 2.0]]] | ['reached', 2, [[0.0, 0.0], [1.6, 1.2], [1.0, 2.0]]] | Failed |
| three unequal links | ['reached', 1, [[0.0, 0.0], [1.8642, 0.7224], [1.7717, 1.2138], [1.0, 2.5]]] | ['reached', 1, [[0.0, 0.0], [1.8649, 0.7227], [1.7717, 1.2139], [0.9999, 2.5001]]] | Failed |
| 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 / a0f64030e2f1015457550191ec2ae6d611deb673380cfa6c4d2f5e9e95c23180
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]]]]], [['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 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]]]], ['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]]]], ['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]]]], ['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]]]], ['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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 / 3e6bedfdde785f7b061dd003fa49e3a920ca50c813906f66015ed0bfa32853ec
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.800261+00:00.
Case digest / 9c1ef69c845798163f951e2b52e383a6f967c941d24f35b91eab90276c263a92