{"abstract":"Converged configurations differ from the tip-first CCD contract and use more sweeps.","category":"Inverse kinematics solvers","checks":7,"contract":"Input [lengths, angles, target, iters, tol, lim]: planar serial chain with relative joint angles in degrees and a symmetric joint limit +/-lim degrees. Each iteration first stops if the tip is within tol of target, else counts one sweep that visits joints from the tip joint down to the base, re-evaluating forward kinematics before each joint, rotating by the signed angle between tip and target vectors and clamping the joint. Return [angles (3 decimals), sweeps performed, final error (4 decimals)].","evaluation_group":"w2-inverse_kinematics_solvers-cyclic-coordinate-descent","failed_approach":"Stopping the reversed range at 1 skips the base joint entirely.","family":"w2-inverse_kinematics_solvers-cyclic-coordinate-descent-sweep-order","id":"FA-87941","implementations":{"attempt":{"sha256":"db34153f73e25a660411ed106143d37472f4018b378a72e58f66294b57fcc9aa","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    ls,qs,t,iters,tol,lim=x\n    qs=[math.radians(q) for q in qs]\n    lim=math.radians(lim)\n    def fk(qs):\n        pts=[(0.0,0.0)]\n        a=0.0\n        for l,q in zip(ls,qs):\n            a+=q\n            px,py=pts[-1]\n            pts.append((px+l*math.cos(a),py+l*math.sin(a)))\n        return pts\n    used=0\n    for it in range(iters):\n        pts=fk(qs)\n        ex,ey=pts[-1]\n        if math.hypot(t[0]-ex,t[1]-ey)<=tol: break\n        used+=1\n        for j in range(len(ls)-1,0,-1):\n            pts=fk(qs)\n            jx,jy=pts[j]\n            ex,ey=pts[-1]\n            ve=(ex-jx,ey-jy)\n            vt=(t[0]-jx,t[1]-jy)\n            ang=math.atan2(ve[0]*vt[1]-ve[1]*vt[0],ve[0]*vt[0]+ve[1]*vt[1])\n            qs[j]=max(-lim,min(lim,qs[j]+ang))\n    pts=fk(qs)\n    err=math.hypot(t[0]-pts[-1][0],t[1]-pts[-1][1])\n    return [[round(math.degrees(q),3) for q in qs],used,round(err,4)]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]]], [['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]], ['four links curl', [[[1, 0.8, 0.6, 0.4], [20, 20, 20, 20], [0.3, 1.2], 3, 0.001, 120]], [[36.469, 53.792, 70.335, 118.779], 3, 0.001]], ['limit blocks tip joint', [[[1, 1], [0, 0], [0, -1.8], 2, 0.001, 30]], [[-30.0, -30.0], 2, 1.4333]], ['zero iterations', [[[1, 1], [0, 0], [0, 2], 0, 0.001, 170]], [[0.0, 0.0], 0, 2.8284]]], [['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['four links curl', [[[1, 0.8, 0.6, 0.4], [20, 20, 20, 20], [0.3, 1.2], 3, 0.001, 120]], [[36.469, 53.792, 70.335, 118.779], 3, 0.001]], ['limit blocks tip joint', [[[1, 1], [0, 0], [0, -1.8], 2, 0.001, 30]], [[-30.0, -30.0], 2, 1.4333]], ['zero iterations', [[[1, 1], [0, 0], [0, 2], 0, 0.001, 170]], [[0.0, 0.0], 0, 2.8284]], ['target crossing pi', [[[1, 1, 1], [170, 0, 0], [-2, -0.5], 2, 0.001, 180]], [[168.982, -6.514, 102.831], 2, 0.0176]], ['loose tolerance', [[[1, 1, 1], [5, 5, 5], [2.9, 0.4], 3, 0.2, 170]], [[5.0, 5.0, 5.0], 0, 0.1285]], ['negative limits start', [[[1.5, 1], [-60, -60], [1, 1.5], 2, 0.001, 90]], [[90.0, -90.0], 1, 0.0]]], [['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['target crossing pi', [[[1, 1, 1], [170, 0, 0], [-2, -0.5], 2, 0.001, 180]], [[168.982, -6.514, 102.831], 2, 0.0176]], ['loose tolerance', [[[1, 1, 1], [5, 5, 5], [2.9, 0.4], 3, 0.2, 170]], [[5.0, 5.0, 5.0], 0, 0.1285]], ['negative limits start', [[[1.5, 1], [-60, -60], [1, 1.5], 2, 0.001, 90]], [[90.0, -90.0], 1, 0.0]], ['tight elbow limit', [[[1, 1, 1], [0, 0, 0], [-1, 1], 3, 0.001, 60]], [[60.0, 60.0, 60.0], 3, 0.7321]]], [['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(*args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"239a3a766a42b2057b591fc68939c485c67ef9410ac9d58bd706624546ab9976","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    ls,qs,t,iters,tol,lim=x\n    qs=[math.radians(q) for q in qs]\n    lim=math.radians(lim)\n    def fk(qs):\n        pts=[(0.0,0.0)]\n        a=0.0\n        for l,q in zip(ls,qs):\n            a+=q\n            px,py=pts[-1]\n            pts.append((px+l*math.cos(a),py+l*math.sin(a)))\n        return pts\n    used=0\n    for it in range(iters):\n        pts=fk(qs)\n        ex,ey=pts[-1]\n        if math.hypot(t[0]-ex,t[1]-ey)<=tol: break\n        used+=1\n        for j in range(len(ls)):\n            pts=fk(qs)\n            jx,jy=pts[j]\n            ex,ey=pts[-1]\n            ve=(ex-jx,ey-jy)\n            vt=(t[0]-jx,t[1]-jy)\n            ang=math.atan2(ve[0]*vt[1]-ve[1]*vt[0],ve[0]*vt[0]+ve[1]*vt[1])\n            qs[j]=max(-lim,min(lim,qs[j]+ang))\n    pts=fk(qs)\n    err=math.hypot(t[0]-pts[-1][0],t[1]-pts[-1][1])\n    return [[round(math.degrees(q),3) for q in qs],used,round(err,4)]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]]], [['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]], ['four links curl', [[[1, 0.8, 0.6, 0.4], [20, 20, 20, 20], [0.3, 1.2], 3, 0.001, 120]], [[36.469, 53.792, 70.335, 118.779], 3, 0.001]], ['limit blocks tip joint', [[[1, 1], [0, 0], [0, -1.8], 2, 0.001, 30]], [[-30.0, -30.0], 2, 1.4333]], ['zero iterations', [[[1, 1], [0, 0], [0, 2], 0, 0.001, 170]], [[0.0, 0.0], 0, 2.8284]]], [['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['four links curl', [[[1, 0.8, 0.6, 0.4], [20, 20, 20, 20], [0.3, 1.2], 3, 0.001, 120]], [[36.469, 53.792, 70.335, 118.779], 3, 0.001]], ['limit blocks tip joint', [[[1, 1], [0, 0], [0, -1.8], 2, 0.001, 30]], [[-30.0, -30.0], 2, 1.4333]], ['zero iterations', [[[1, 1], [0, 0], [0, 2], 0, 0.001, 170]], [[0.0, 0.0], 0, 2.8284]], ['target crossing pi', [[[1, 1, 1], [170, 0, 0], [-2, -0.5], 2, 0.001, 180]], [[168.982, -6.514, 102.831], 2, 0.0176]], ['loose tolerance', [[[1, 1, 1], [5, 5, 5], [2.9, 0.4], 3, 0.2, 170]], [[5.0, 5.0, 5.0], 0, 0.1285]], ['negative limits start', [[[1.5, 1], [-60, -60], [1, 1.5], 2, 0.001, 90]], [[90.0, -90.0], 1, 0.0]]], [['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['target crossing pi', [[[1, 1, 1], [170, 0, 0], [-2, -0.5], 2, 0.001, 180]], [[168.982, -6.514, 102.831], 2, 0.0176]], ['loose tolerance', [[[1, 1, 1], [5, 5, 5], [2.9, 0.4], 3, 0.2, 170]], [[5.0, 5.0, 5.0], 0, 0.1285]], ['negative limits start', [[[1.5, 1], [-60, -60], [1, 1.5], 2, 0.001, 90]], [[90.0, -90.0], 1, 0.0]], ['tight elbow limit', [[[1, 1, 1], [0, 0, 0], [-1, 1], 3, 0.001, 60]], [[60.0, 60.0, 60.0], 3, 0.7321]]], [['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(*args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"10f1dc6cc59eaa6006994c18f5281cba9dfb5bd74f80d059b6f8cb33f904592d","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    ls,qs,t,iters,tol,lim=x\n    qs=[math.radians(q) for q in qs]\n    lim=math.radians(lim)\n    def fk(qs):\n        pts=[(0.0,0.0)]\n        a=0.0\n        for l,q in zip(ls,qs):\n            a+=q\n            px,py=pts[-1]\n            pts.append((px+l*math.cos(a),py+l*math.sin(a)))\n        return pts\n    used=0\n    for it in range(iters):\n        pts=fk(qs)\n        ex,ey=pts[-1]\n        if math.hypot(t[0]-ex,t[1]-ey)<=tol: break\n        used+=1\n        for j in range(len(ls)-1,-1,-1):\n            pts=fk(qs)\n            jx,jy=pts[j]\n            ex,ey=pts[-1]\n            ve=(ex-jx,ey-jy)\n            vt=(t[0]-jx,t[1]-jy)\n            ang=math.atan2(ve[0]*vt[1]-ve[1]*vt[0],ve[0]*vt[0]+ve[1]*vt[1])\n            qs[j]=max(-lim,min(lim,qs[j]+ang))\n    pts=fk(qs)\n    err=math.hypot(t[0]-pts[-1][0],t[1]-pts[-1][1])\n    return [[round(math.degrees(q),3) for q in qs],used,round(err,4)]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]]], [['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]], ['four links curl', [[[1, 0.8, 0.6, 0.4], [20, 20, 20, 20], [0.3, 1.2], 3, 0.001, 120]], [[36.469, 53.792, 70.335, 118.779], 3, 0.001]], ['limit blocks tip joint', [[[1, 1], [0, 0], [0, -1.8], 2, 0.001, 30]], [[-30.0, -30.0], 2, 1.4333]], ['zero iterations', [[[1, 1], [0, 0], [0, 2], 0, 0.001, 170]], [[0.0, 0.0], 0, 2.8284]]], [['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['four links curl', [[[1, 0.8, 0.6, 0.4], [20, 20, 20, 20], [0.3, 1.2], 3, 0.001, 120]], [[36.469, 53.792, 70.335, 118.779], 3, 0.001]], ['limit blocks tip joint', [[[1, 1], [0, 0], [0, -1.8], 2, 0.001, 30]], [[-30.0, -30.0], 2, 1.4333]], ['zero iterations', [[[1, 1], [0, 0], [0, 2], 0, 0.001, 170]], [[0.0, 0.0], 0, 2.8284]], ['target crossing pi', [[[1, 1, 1], [170, 0, 0], [-2, -0.5], 2, 0.001, 180]], [[168.982, -6.514, 102.831], 2, 0.0176]], ['loose tolerance', [[[1, 1, 1], [5, 5, 5], [2.9, 0.4], 3, 0.2, 170]], [[5.0, 5.0, 5.0], 0, 0.1285]], ['negative limits start', [[[1.5, 1], [-60, -60], [1, 1.5], 2, 0.001, 90]], [[90.0, -90.0], 1, 0.0]]], [['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['target crossing pi', [[[1, 1, 1], [170, 0, 0], [-2, -0.5], 2, 0.001, 180]], [[168.982, -6.514, 102.831], 2, 0.0176]], ['loose tolerance', [[[1, 1, 1], [5, 5, 5], [2.9, 0.4], 3, 0.2, 170]], [[5.0, 5.0, 5.0], 0, 0.1285]], ['negative limits start', [[[1.5, 1], [-60, -60], [1, 1.5], 2, 0.001, 90]], [[90.0, -90.0], 1, 0.0]], ['tight elbow limit', [[[1, 1, 1], [0, 0, 0], [-1, 1], 3, 0.001, 60]], [[60.0, 60.0, 60.0], 3, 0.7321]]], [['three links one sweep', [[[1, 1, 1], [0, 0, 0], [1.5, 1.5], 1, 0.001, 170]], [[5.996, 17.348, 108.435], 1, 0.3586]], ['three links several sweeps', [[[1, 1, 1], [10, 10, 10], [-1, 2], 4, 0.001, 170]], [[63.011, 58.047, 42.064], 4, 0.0426]], ['target behind base', [[[1, 1], [0, 0], [-1.5, -0.2], 3, 0.001, 175]], [[-121.779, -101.253], 3, 0.2445]], ['tight limits', [[[1, 1, 1], [0, 0, 0], [0.5, 2.5], 3, 0.001, 45]], [[36.006, 45.0, 37.294], 3, 0.0448]], ['already converged', [[[1, 1], [0, 90], [1, 1], 5, 0.001, 170]], [[0.0, 90.0], 0, 0.0]], ['converges early', [[[2, 1], [0, 0], [2, 1], 10, 0.01, 170]], [[0.0, 90.0], 1, 0.0]], ['unreachable stretch', [[[1, 1], [30, 30], [4, 0], 3, 0.001, 170]], [[8.551, -17.103], 3, 2.0222]]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(*args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":"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.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"w2-inverse_kinematics_solvers-cyclic-coordinate-descent-sweep-order","generated_at":"2026-09-29T14:51:03.624503+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"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.","repair":"Iterate j from len-1 down to 0.","root_cause":"The joint loop runs from the base to the tip instead of from the tip to the base.","sha256":"1b34444d27ef8acd488dace25934189758a65a25c1e672d1cfcde3addcde3305","title":"CCD sweep visits joints base first · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":43.366,"exit_code":1,"observations":[{"actual":[[0.0,17.348,108.435],1,0.4117],"check":"three links one sweep","expected":[[5.996,17.348,108.435],1,0.3586],"passed":false},{"actual":[[10.0,103.543,47.675],4,0.8678],"check":"three links several sweeps","expected":[[63.011,58.047,42.064],4,0.0426],"passed":false},{"actual":[[0.0,-175.0],3,1.508],"check":"target behind base","expected":[[-121.779,-101.253],3,0.2445],"passed":false},{"actual":[[0.0,45.0,45.0],3,1.4442],"check":"tight limits","expected":[[36.006,45.0,37.294],3,0.0448],"passed":false},{"actual":[[0.0,90.0],0,0.0],"check":"already converged","expected":[[0.0,90.0],0,0.0],"passed":true},{"actual":[[0.0,90.0],1,0.0],"check":"converges early","expected":[[0.0,90.0],1,0.0],"passed":true},{"actual":[[30.0,-39.065],3,2.1736],"check":"unreachable stretch","expected":[[8.551,-17.103],3,2.0222],"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"three links one sweep\", \"actual\": [[0.0, 17.348, 108.435], 1, 0.4117], \"expected\": [[5.996, 17.348, 108.435], 1, 0.3586], \"passed\": false}, {\"check\": \"three links several sweeps\", \"actual\": [[10.0, 103.543, 47.675], 4, 0.8678], \"expected\": [[63.011, 58.047, 42.064], 4, 0.0426], \"passed\": false}, {\"check\": \"target behind base\", \"actual\": [[0.0, -175.0], 3, 1.508], \"expected\": [[-121.779, -101.253], 3, 0.2445], \"passed\": false}, {\"check\": \"tight limits\", \"actual\": [[0.0, 45.0, 45.0], 3, 1.4442], \"expected\": [[36.006, 45.0, 37.294], 3, 0.0448], \"passed\": false}, {\"check\": \"already converged\", \"actual\": [[0.0, 90.0], 0, 0.0], \"expected\": [[0.0, 90.0], 0, 0.0], \"passed\": true}, {\"check\": \"converges early\", \"actual\": [[0.0, 90.0], 1, 0.0], \"expected\": [[0.0, 90.0], 1, 0.0], \"passed\": true}, {\"check\": \"unreachable stretch\", \"actual\": [[30.0, -39.065], 3, 2.1736], \"expected\": [[8.551, -17.103], 3, 2.0222], \"passed\": false}], \"passed\": false}\n"},"broken":{"elapsed_ms":43.727,"exit_code":1,"observations":[{"actual":[[45.0,0.0,-0.0],1,0.8787],"check":"three links one sweep","expected":[[5.996,17.348,108.435],1,0.3586],"passed":false},{"actual":[[83.521,24.569,67.931],4,0.1957],"check":"three links several sweeps","expected":[[63.011,58.047,42.064],4,0.0426],"passed":false},{"actual":[[-172.405,0.0],3,0.4867],"check":"target behind base","expected":[[-121.779,-101.253],3,0.2445],"passed":false},{"actual":[[41.824,45.0,22.982],3,0.1128],"check":"tight limits","expected":[[36.006,45.0,37.294],3,0.0448],"passed":false},{"actual":[[0.0,90.0],0,0.0],"check":"already converged","expected":[[0.0,90.0],0,0.0],"passed":true},{"actual":[[26.565,0.0],10,0.7639],"check":"converges early","expected":[[0.0,90.0],1,0.0],"passed":false},{"actual":[[-6.607,8.798],3,2.0088],"check":"unreachable stretch","expected":[[8.551,-17.103],3,2.0222],"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"three links one sweep\", \"actual\": [[45.0, 0.0, -0.0], 1, 0.8787], \"expected\": [[5.996, 17.348, 108.435], 1, 0.3586], \"passed\": false}, {\"check\": \"three links several sweeps\", \"actual\": [[83.521, 24.569, 67.931], 4, 0.1957], \"expected\": [[63.011, 58.047, 42.064], 4, 0.0426], \"passed\": false}, {\"check\": \"target behind base\", \"actual\": [[-172.405, 0.0], 3, 0.4867], \"expected\": [[-121.779, -101.253], 3, 0.2445], \"passed\": false}, {\"check\": \"tight limits\", \"actual\": [[41.824, 45.0, 22.982], 3, 0.1128], \"expected\": [[36.006, 45.0, 37.294], 3, 0.0448], \"passed\": false}, {\"check\": \"already converged\", \"actual\": [[0.0, 90.0], 0, 0.0], \"expected\": [[0.0, 90.0], 0, 0.0], \"passed\": true}, {\"check\": \"converges early\", \"actual\": [[26.565, 0.0], 10, 0.7639], \"expected\": [[0.0, 90.0], 1, 0.0], \"passed\": false}, {\"check\": \"unreachable stretch\", \"actual\": [[-6.607, 8.798], 3, 2.0088], \"expected\": [[8.551, -17.103], 3, 2.0222], \"passed\": false}], \"passed\": false}\n"},"fixed":{"elapsed_ms":39.794,"exit_code":0,"observations":[{"actual":[[5.996,17.348,108.435],1,0.3586],"check":"three links one sweep","expected":[[5.996,17.348,108.435],1,0.3586],"passed":true},{"actual":[[63.011,58.047,42.064],4,0.0426],"check":"three links several sweeps","expected":[[63.011,58.047,42.064],4,0.0426],"passed":true},{"actual":[[-121.779,-101.253],3,0.2445],"check":"target behind base","expected":[[-121.779,-101.253],3,0.2445],"passed":true},{"actual":[[36.006,45.0,37.294],3,0.0448],"check":"tight limits","expected":[[36.006,45.0,37.294],3,0.0448],"passed":true},{"actual":[[0.0,90.0],0,0.0],"check":"already converged","expected":[[0.0,90.0],0,0.0],"passed":true},{"actual":[[0.0,90.0],1,0.0],"check":"converges early","expected":[[0.0,90.0],1,0.0],"passed":true},{"actual":[[8.551,-17.103],3,2.0222],"check":"unreachable stretch","expected":[[8.551,-17.103],3,2.0222],"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"three links one sweep\", \"actual\": [[5.996, 17.348, 108.435], 1, 0.3586], \"expected\": [[5.996, 17.348, 108.435], 1, 0.3586], \"passed\": true}, {\"check\": \"three links several sweeps\", \"actual\": [[63.011, 58.047, 42.064], 4, 0.0426], \"expected\": [[63.011, 58.047, 42.064], 4, 0.0426], \"passed\": true}, {\"check\": \"target behind base\", \"actual\": [[-121.779, -101.253], 3, 0.2445], \"expected\": [[-121.779, -101.253], 3, 0.2445], \"passed\": true}, {\"check\": \"tight limits\", \"actual\": [[36.006, 45.0, 37.294], 3, 0.0448], \"expected\": [[36.006, 45.0, 37.294], 3, 0.0448], \"passed\": true}, {\"check\": \"already converged\", \"actual\": [[0.0, 90.0], 0, 0.0], \"expected\": [[0.0, 90.0], 0, 0.0], \"passed\": true}, {\"check\": \"converges early\", \"actual\": [[0.0, 90.0], 1, 0.0], \"expected\": [[0.0, 90.0], 1, 0.0], \"passed\": true}, {\"check\": \"unreachable stretch\", \"actual\": [[8.551, -17.103], 3, 2.0222], \"expected\": [[8.551, -17.103], 3, 2.0222], \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}