FAILURE MAP
← Case archive

FA-68076 / Traffic signal timing plans / Open access

Shortway offset transition: the adjustment is spread evenly instead of front-loaded · case 01

Shortway offset transition returns a wrong result when the adjustment is spread evenly instead of front-loaded.

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

ROOT CAUSE

Every transition cycle applies the full step, overshooting the target offset on the last cycle.

VERIFIED REPAIR

Restore the last transition cycle rule so that the step reads `d = min(step, amt)`.

Unsuccessful approach: Spreading the correction evenly reaches the offset but not with the stipulated maximum-first profile.

Case contract

Input {cycle, current_offset, target_offset, lengthen_pct, shorten_pct}. The needed correction e = (target - current) mod cycle can be made by lengthening cycles by a total of e or shortening them by cycle - e. Per-cycle limits are floor(cycle*pct/100). Choose the direction needing fewer cycles (ties lengthen) and front-load the maximum adjustment each cycle. Return the list of transition cycle lengths ([] when already in step).

Why this case matters

Signal timing arithmetic is exact and integer or rational; a wrong rule silently produces unsafe or inefficient timing plans.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    C = x['cycle']
    e = (x['target_offset'] - x['current_offset']) % C
    if e == 0:
        return []
    L = C * x['lengthen_pct'] // 100
    S = C * x['shorten_pct'] // 100
    nl = -(-e // L)
    ns = -(-(C - e) // S)
    if nl <= ns:
        amt, step, sign = e, L, 1
    else:
        amt, step, sign = C - e, S, -1
    out = []
    while amt > 0:
        d = step
        out.append(C + sign * d)
        amt -= d
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'cycle': 90, 'current_offset': 10, 'target_offset': 82, 'lengthen_pct': 17, 'shorten_pct': 20}, [72]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 90, 'current_offset': 7, 'target_offset': 6, 'lengthen_pct': 25, 'shorten_pct': 20}, [89]), ({'cycle': 80, 'current_offset': 27, 'target_offset': 75, 'lengthen_pct': 20, 'shorten_pct': 17}, [96, 96, 96]), ({'cycle': 130, 'current_offset': 59, 'target_offset': 25, 'lengthen_pct': 17, 'shorten_pct': 10}, [117, 117, 122]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 130, 'current_offset': 77, 'target_offset': 32, 'lengthen_pct': 20, 'shorten_pct': 20}, [104, 111])], [({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 90, 'current_offset': 56, 'target_offset': 74, 'lengthen_pct': 10, 'shorten_pct': 20}, [99, 99]), ({'cycle': 150, 'current_offset': 42, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 10}, [135, 143]), ({'cycle': 70, 'current_offset': 20, 'target_offset': 48, 'lengthen_pct': 17, 'shorten_pct': 13}, [81, 81, 76]), ({'cycle': 140, 'current_offset': 32, 'target_offset': 8, 'lengthen_pct': 25, 'shorten_pct': 13}, [122, 134]), ({'cycle': 60, 'current_offset': 31, 'target_offset': 31, 'lengthen_pct': 25, 'shorten_pct': 20}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130])], [({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 60, 'current_offset': 4, 'target_offset': 57, 'lengthen_pct': 10, 'shorten_pct': 13}, [53]), ({'cycle': 60, 'current_offset': 12, 'target_offset': 14, 'lengthen_pct': 25, 'shorten_pct': 10}, [62]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 100, 'current_offset': 12, 'target_offset': 12, 'lengthen_pct': 17, 'shorten_pct': 20}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 70, 'current_offset': 55, 'target_offset': 56, 'lengthen_pct': 20, 'shorten_pct': 17}, [71]), ({'cycle': 70, 'current_offset': 17, 'target_offset': 4, 'lengthen_pct': 25, 'shorten_pct': 13}, [61, 66])], [({'cycle': 70, 'current_offset': 65, 'target_offset': 12, 'lengthen_pct': 25, 'shorten_pct': 10}, [87]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 100, 'current_offset': 90, 'target_offset': 89, 'lengthen_pct': 17, 'shorten_pct': 20}, [99]), ({'cycle': 110, 'current_offset': 51, 'target_offset': 51, 'lengthen_pct': 20, 'shorten_pct': 13}, []), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 60, 'current_offset': 47, 'target_offset': 39, 'lengthen_pct': 10, 'shorten_pct': 13}, [53, 59]), ({'cycle': 90, 'current_offset': 25, 'target_offset': 13, 'lengthen_pct': 17, 'shorten_pct': 10}, [81, 87])], [({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 90, 'current_offset': 54, 'target_offset': 43, 'lengthen_pct': 10, 'shorten_pct': 13}, [79]), ({'cycle': 130, 'current_offset': 41, 'target_offset': 22, 'lengthen_pct': 20, 'shorten_pct': 17}, [111]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 140, 'current_offset': 46, 'target_offset': 86, 'lengthen_pct': 25, 'shorten_pct': 13}, [175, 145]), ({'cycle': 120, 'current_offset': 55, 'target_offset': 69, 'lengthen_pct': 20, 'shorten_pct': 17}, [134]), ({'cycle': 100, 'current_offset': 74, 'target_offset': 84, 'lengthen_pct': 10, 'shorten_pct': 17}, [110]), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('timing oracle' + ' %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
timing oracle 0[72][72]Passed
timing oracle 1[120, 120][120, 120]Passed
timing oracle 2[140, 140, 140][140, 140, 130]Failed
timing oracle 3[72][89]Failed
timing oracle 4[96, 96, 96][96, 96, 96]Passed
timing oracle 5[117, 117, 117][117, 117, 122]Failed
timing oracle 6[][]Passed
timing oracle 7[104, 104][104, 111]Failed

SHA-256 / a3c1c581b3b36f0b709d2c93a9302dc349b0d7161756227abf4846173d9ef1be

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    C = x['cycle']
    e = (x['target_offset'] - x['current_offset']) % C
    if e == 0:
        return []
    L = C * x['lengthen_pct'] // 100
    S = C * x['shorten_pct'] // 100
    nl = -(-e // L)
    ns = -(-(C - e) // S)
    if nl <= ns:
        amt, step, sign = e, L, 1
    else:
        amt, step, sign = C - e, S, -1
    out = []
    while amt > 0:
        d = -(-amt // -(-amt // step))
        out.append(C + sign * d)
        amt -= d
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'cycle': 90, 'current_offset': 10, 'target_offset': 82, 'lengthen_pct': 17, 'shorten_pct': 20}, [72]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 90, 'current_offset': 7, 'target_offset': 6, 'lengthen_pct': 25, 'shorten_pct': 20}, [89]), ({'cycle': 80, 'current_offset': 27, 'target_offset': 75, 'lengthen_pct': 20, 'shorten_pct': 17}, [96, 96, 96]), ({'cycle': 130, 'current_offset': 59, 'target_offset': 25, 'lengthen_pct': 17, 'shorten_pct': 10}, [117, 117, 122]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 130, 'current_offset': 77, 'target_offset': 32, 'lengthen_pct': 20, 'shorten_pct': 20}, [104, 111])], [({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 90, 'current_offset': 56, 'target_offset': 74, 'lengthen_pct': 10, 'shorten_pct': 20}, [99, 99]), ({'cycle': 150, 'current_offset': 42, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 10}, [135, 143]), ({'cycle': 70, 'current_offset': 20, 'target_offset': 48, 'lengthen_pct': 17, 'shorten_pct': 13}, [81, 81, 76]), ({'cycle': 140, 'current_offset': 32, 'target_offset': 8, 'lengthen_pct': 25, 'shorten_pct': 13}, [122, 134]), ({'cycle': 60, 'current_offset': 31, 'target_offset': 31, 'lengthen_pct': 25, 'shorten_pct': 20}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130])], [({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 60, 'current_offset': 4, 'target_offset': 57, 'lengthen_pct': 10, 'shorten_pct': 13}, [53]), ({'cycle': 60, 'current_offset': 12, 'target_offset': 14, 'lengthen_pct': 25, 'shorten_pct': 10}, [62]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 100, 'current_offset': 12, 'target_offset': 12, 'lengthen_pct': 17, 'shorten_pct': 20}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 70, 'current_offset': 55, 'target_offset': 56, 'lengthen_pct': 20, 'shorten_pct': 17}, [71]), ({'cycle': 70, 'current_offset': 17, 'target_offset': 4, 'lengthen_pct': 25, 'shorten_pct': 13}, [61, 66])], [({'cycle': 70, 'current_offset': 65, 'target_offset': 12, 'lengthen_pct': 25, 'shorten_pct': 10}, [87]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 100, 'current_offset': 90, 'target_offset': 89, 'lengthen_pct': 17, 'shorten_pct': 20}, [99]), ({'cycle': 110, 'current_offset': 51, 'target_offset': 51, 'lengthen_pct': 20, 'shorten_pct': 13}, []), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 60, 'current_offset': 47, 'target_offset': 39, 'lengthen_pct': 10, 'shorten_pct': 13}, [53, 59]), ({'cycle': 90, 'current_offset': 25, 'target_offset': 13, 'lengthen_pct': 17, 'shorten_pct': 10}, [81, 87])], [({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 90, 'current_offset': 54, 'target_offset': 43, 'lengthen_pct': 10, 'shorten_pct': 13}, [79]), ({'cycle': 130, 'current_offset': 41, 'target_offset': 22, 'lengthen_pct': 20, 'shorten_pct': 17}, [111]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 140, 'current_offset': 46, 'target_offset': 86, 'lengthen_pct': 25, 'shorten_pct': 13}, [175, 145]), ({'cycle': 120, 'current_offset': 55, 'target_offset': 69, 'lengthen_pct': 20, 'shorten_pct': 17}, [134]), ({'cycle': 100, 'current_offset': 74, 'target_offset': 84, 'lengthen_pct': 10, 'shorten_pct': 17}, [110]), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('timing oracle' + ' %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
timing oracle 0[72][72]Passed
timing oracle 1[120, 120][120, 120]Passed
timing oracle 2[137, 137, 136][140, 140, 130]Failed
timing oracle 3[89][89]Passed
timing oracle 4[96, 96, 96][96, 96, 96]Passed
timing oracle 5[118, 119, 119][117, 117, 122]Failed
timing oracle 6[][]Passed
timing oracle 7[107, 108][104, 111]Failed

SHA-256 / 795280116818fcc602a377f0ebfa2b12db50c1e3c3b10e20fcbae67e233dbdd6

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    C = x['cycle']
    e = (x['target_offset'] - x['current_offset']) % C
    if e == 0:
        return []
    L = C * x['lengthen_pct'] // 100
    S = C * x['shorten_pct'] // 100
    nl = -(-e // L)
    ns = -(-(C - e) // S)
    if nl <= ns:
        amt, step, sign = e, L, 1
    else:
        amt, step, sign = C - e, S, -1
    out = []
    while amt > 0:
        d = min(step, amt)
        out.append(C + sign * d)
        amt -= d
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'cycle': 90, 'current_offset': 10, 'target_offset': 82, 'lengthen_pct': 17, 'shorten_pct': 20}, [72]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 90, 'current_offset': 7, 'target_offset': 6, 'lengthen_pct': 25, 'shorten_pct': 20}, [89]), ({'cycle': 80, 'current_offset': 27, 'target_offset': 75, 'lengthen_pct': 20, 'shorten_pct': 17}, [96, 96, 96]), ({'cycle': 130, 'current_offset': 59, 'target_offset': 25, 'lengthen_pct': 17, 'shorten_pct': 10}, [117, 117, 122]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 130, 'current_offset': 77, 'target_offset': 32, 'lengthen_pct': 20, 'shorten_pct': 20}, [104, 111])], [({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 90, 'current_offset': 56, 'target_offset': 74, 'lengthen_pct': 10, 'shorten_pct': 20}, [99, 99]), ({'cycle': 150, 'current_offset': 42, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 10}, [135, 143]), ({'cycle': 70, 'current_offset': 20, 'target_offset': 48, 'lengthen_pct': 17, 'shorten_pct': 13}, [81, 81, 76]), ({'cycle': 140, 'current_offset': 32, 'target_offset': 8, 'lengthen_pct': 25, 'shorten_pct': 13}, [122, 134]), ({'cycle': 60, 'current_offset': 31, 'target_offset': 31, 'lengthen_pct': 25, 'shorten_pct': 20}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130])], [({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 60, 'current_offset': 4, 'target_offset': 57, 'lengthen_pct': 10, 'shorten_pct': 13}, [53]), ({'cycle': 60, 'current_offset': 12, 'target_offset': 14, 'lengthen_pct': 25, 'shorten_pct': 10}, [62]), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 100, 'current_offset': 12, 'target_offset': 12, 'lengthen_pct': 17, 'shorten_pct': 20}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 70, 'current_offset': 55, 'target_offset': 56, 'lengthen_pct': 20, 'shorten_pct': 17}, [71]), ({'cycle': 70, 'current_offset': 17, 'target_offset': 4, 'lengthen_pct': 25, 'shorten_pct': 13}, [61, 66])], [({'cycle': 70, 'current_offset': 65, 'target_offset': 12, 'lengthen_pct': 25, 'shorten_pct': 10}, [87]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 100, 'current_offset': 90, 'target_offset': 89, 'lengthen_pct': 17, 'shorten_pct': 20}, [99]), ({'cycle': 110, 'current_offset': 51, 'target_offset': 51, 'lengthen_pct': 20, 'shorten_pct': 13}, []), ({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130]), ({'cycle': 60, 'current_offset': 47, 'target_offset': 39, 'lengthen_pct': 10, 'shorten_pct': 13}, [53, 59]), ({'cycle': 90, 'current_offset': 25, 'target_offset': 13, 'lengthen_pct': 17, 'shorten_pct': 10}, [81, 87])], [({'cycle': 90, 'current_offset': 30, 'target_offset': 30, 'lengthen_pct': 20, 'shorten_pct': 17}, []), ({'cycle': 90, 'current_offset': 54, 'target_offset': 43, 'lengthen_pct': 10, 'shorten_pct': 13}, [79]), ({'cycle': 130, 'current_offset': 41, 'target_offset': 22, 'lengthen_pct': 20, 'shorten_pct': 17}, [111]), ({'cycle': 100, 'current_offset': 10, 'target_offset': 50, 'lengthen_pct': 20, 'shorten_pct': 20}, [120, 120]), ({'cycle': 140, 'current_offset': 46, 'target_offset': 86, 'lengthen_pct': 25, 'shorten_pct': 13}, [175, 145]), ({'cycle': 120, 'current_offset': 55, 'target_offset': 69, 'lengthen_pct': 20, 'shorten_pct': 17}, [134]), ({'cycle': 100, 'current_offset': 74, 'target_offset': 84, 'lengthen_pct': 10, 'shorten_pct': 17}, [110]), ({'cycle': 120, 'current_offset': 90, 'target_offset': 20, 'lengthen_pct': 17, 'shorten_pct': 13}, [140, 140, 130])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('timing oracle' + ' %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
timing oracle 0[72][72]Passed
timing oracle 1[120, 120][120, 120]Passed
timing oracle 2[140, 140, 130][140, 140, 130]Passed
timing oracle 3[89][89]Passed
timing oracle 4[96, 96, 96][96, 96, 96]Passed
timing oracle 5[117, 117, 122][117, 117, 122]Passed
timing oracle 6[][]Passed
timing oracle 7[104, 111][104, 111]Passed

SHA-256 / 67f55b320c9c8a388cd4c1a16c5d43a2ab305f3f855c7fe49b949fd93850aa93

Verification & scope

A deterministic, bounded toy model with a stipulated contract; it makes no claim of conformance to any agency manual or standard. 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:47:58.891355+00:00.

Case digest / fab426a663a7e0260743e29d98d610004cb98d0f6c7792ef34e562b7577da20b