FAILURE MAP
← Case archive

FA-94456 / Exam timetabling constraints / Open access

Extended sittings compared in input order · case 01

A student's exams listed out of order produce spurious or missed conflicts.

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

ROOT CAUSE

Sittings are not sorted by start before pairing.

VERIFIED REPAIR

Sort sittings by start time, ties by id.

Unsuccessful approach: Sorting by exam id is not chronological.

Case contract

A student's exams [id, start, duration] and an extra-time percentage. Extended duration = ceil(duration * (100 + pct) / 100), then rounded up to a multiple of 5 minutes. In start order (ties by id) a pair conflicts if the next start is earlier than the previous extended end plus the rest minutes. Return [[id, extended end] in order, conflicting pairs].

Why this case matters

Access arrangements extend sittings; the timetable must still leave rest between a student's exams.

1 / The failure

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

N = 1
observations = []
def solve(exams, pct, rest):
    rows = []
    for eid, st, dur in exams:
        ext = -(-dur * (100 + pct) // 100)
        ext = -(-ext // 5) * 5
        rows.append([eid, st, st + ext])
    conf = []
    for a, b in zip(rows, rows[1:]):
        if b[1] < a[2] + rest:
            conf.append([a[0], b[0]])
    return [[[r[0], r[2]] for r in rows], conf]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: chronological order 1', [[['B', 800, 60], ['A', 540, 90]], 15, 30],
   [[['A', 645], ['B', 870]], []]),
  ('regression variant: chronological order 2',
   [[['P1', 740, 120], ['P0', 540, 44], ['W2', 940, 72]], 15, 30],
   [[['P0', 595], ['P1', 880], ['W2', 1025]], []]),
  ('partial repair guard 3', [[['T1', 720, 120], ['V0', 600, 180]], 33, 15],
   [[['V0', 840], ['T1', 880]], [['V0', 'T1']]]),
  ('boundary control 4', [[['A', 540, 120], ['B', 720, 90]], 50, 0], [[['A', 720], ['B', 855]], []]),
  ('boundary control 5', [[['A', 540, 73], ['B', 640, 60]], 10, 15], [[['A', 625], ['B', 710]], []]),
  ('normal control 6', [[['R2', 960, 90], ['T3', 1160, 90], ['V1', 780, 150], ['W0', 600, 73]], 10, 30],
   [[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]]),
  ('normal control 7', [[['W2', 940, 88], ['T0', 540, 180], ['W1', 740, 180], ['R3', 1090, 150]], 25, 15],
   [[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]]),
  ('normal control 8', [[['R3', 1080, 60], ['U0', 600, 100], ['V2', 930, 180], ['R1', 750, 88]], 25, 30],
   [[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]])],
 [('regression: chronological order 1',
   [[['P1', 690, 150], ['S2', 890, 72], ['Q0', 600, 150], ['R3', 1010, 90]], 50, 15],
   [[['Q0', 825], ['P1', 915], ['S2', 1000], ['R3', 1145]], [['Q0', 'P1'], ['P1', 'S2'], ['S2', 'R3']]]),
  ('regression variant: chronological order 2',
   [[['P3', 1080, 180], ['U2', 930, 100], ['W1', 690, 72], ['T0', 540, 72]], 10, 15],
   [[['T0', 620], ['W1', 770], ['U2', 1040], ['P3', 1280]], []]),
  ('partial repair guard 3', [[['V0', 600, 182], ['T1', 690, 73]], 10, 30],
   [[['V0', 805], ['T1', 775]], [['V0', 'T1']]]),
  ('boundary control 4', [[['A', 540, 91]], 10, 30], [[['A', 645]], []]),
  ('boundary control 5', [[['A', 540, 100], ['B', 699, 60]], 25, 30], [[['A', 665], ['B', 774]], []]),
  ('normal control 6', [[['T0', 600, 72], ['P2', 1040, 100], ['T3', 1190, 182], ['Q1', 840, 100]], 10, 15],
   [[['T0', 680], ['Q1', 950], ['P2', 1150], ['T3', 1395]], []]),
  ('normal control 7', [[['Q2', 840, 91], ['S0', 600, 88], ['R1', 750, 73]], 50, 30],
   [[['S0', 735], ['R1', 860], ['Q2', 980]], [['S0', 'R1'], ['R1', 'Q2']]]),
  ('normal control 8', [[['S1', 630, 100], ['V3', 930, 88], ['V0', 540, 180], ['R2', 750, 73]], 50, 30],
   [[['V0', 810], ['S1', 780], ['R2', 860], ['V3', 1065]], [['V0', 'S1'], ['S1', 'R2']]])],
 [('regression: chronological order 1', [[['T1', 840, 72], ['R0', 600, 44]], 33, 30],
   [[['R0', 660], ['T1', 940]], []]),
  ('regression variant: chronological order 2',
   [[['U0', 540, 150], ['P2', 870, 60], ['Q1', 780, 88]], 10, 30],
   [[['U0', 705], ['Q1', 880], ['P2', 940]], [['Q1', 'P2']]]),
  ('partial repair guard 3',
   [[['S2', 810, 100], ['Q0', 600, 72], ['V1', 690, 100], ['R3', 990, 120]], 33, 15],
   [[['Q0', 700], ['V1', 825], ['S2', 945], ['R3', 1150]], [['Q0', 'V1'], ['V1', 'S2']]]),
  ('boundary control 4', [[['A', 540, 88]], 25, 30], [[['A', 650]], []]),
  ('boundary control 5', [[['A', 540, 72]], 33, 15], [[['A', 640]], []]),
  ('normal control 6', [[['S0', 540, 90], ['U1', 740, 90]], 15, 30], [[['S0', 645], ['U1', 845]], []]),
  ('normal control 7', [[['Q2', 810, 120], ['S3', 1050, 73], ['V0', 540, 90], ['V1', 630, 73]], 25, 30],
   [[['V0', 655], ['V1', 725], ['Q2', 960], ['S3', 1145]], [['V0', 'V1']]]),
  ('normal control 8', [[['V1', 630, 182], ['Q0', 540, 120]], 50, 30],
   [[['Q0', 720], ['V1', 905]], [['Q0', 'V1']]])],
 [('regression: chronological order 1',
   [[['S2', 900, 73], ['P1', 780, 60], ['U3', 1050, 44], ['W0', 540, 72]], 50, 30],
   [[['W0', 650], ['P1', 870], ['S2', 1010], ['U3', 1120]], []]),
  ('regression variant: chronological order 2', [[['P1', 780, 150], ['U0', 600, 90]], 50, 15],
   [[['U0', 735], ['P1', 1005]], []]),
  ('partial repair guard 3',
   [[['V0', 600, 90], ['R2', 990, 91], ['S1', 840, 120], ['Q3', 1110, 100]], 33, 30],
   [[['V0', 720], ['S1', 1000], ['R2', 1115], ['Q3', 1245]], [['S1', 'R2'], ['R2', 'Q3']]]),
  ('boundary control 4', [[['A', 540, 120], ['B', 720, 90]], 50, 0], [[['A', 720], ['B', 855]], []]),
  ('boundary control 5', [[['A', 540, 73], ['B', 640, 60]], 10, 15], [[['A', 625], ['B', 710]], []]),
  ('normal control 6', [[['T1', 780, 180], ['P0', 540, 88]], 50, 30], [[['P0', 675], ['T1', 1050]], []]),
  ('normal control 7', [[['P3', 1110, 44], ['V1', 720, 72], ['S2', 870, 72], ['P0', 540, 91]], 15, 15],
   [[['P0', 645], ['V1', 805], ['S2', 955], ['P3', 1165]], []]),
  ('normal control 8', [[['P0', 600, 150], ['R2', 840, 90], ['S1', 720, 90]], 50, 30],
   [[['P0', 825], ['S1', 855], ['R2', 975]], [['P0', 'S1'], ['S1', 'R2']]])],
 [('regression: chronological order 1',
   [[['V2', 810, 180], ['U0', 540, 44], ['V3', 1010, 72], ['T1', 720, 100]], 33, 30],
   [[['U0', 600], ['T1', 855], ['V2', 1050], ['V3', 1110]], [['T1', 'V2'], ['V2', 'V3']]]),
  ('regression variant: chronological order 2',
   [[['Q1', 780, 150], ['P2', 1020, 73], ['V3', 1220, 150], ['W0', 540, 180]], 15, 15],
   [[['W0', 750], ['Q1', 955], ['P2', 1105], ['V3', 1395]], []]),
  ('partial repair guard 3',
   [[['U2', 840, 44], ['W3', 1080, 150], ['V1', 660, 180], ['V0', 540, 91]], 33, 30],
   [[['V0', 665], ['V1', 900], ['U2', 900], ['W3', 1280]], [['V0', 'V1'], ['V1', 'U2']]]),
  ('boundary control 4', [[['A', 540, 91]], 10, 30], [[['A', 645]], []]),
  ('boundary control 5', [[['A', 540, 100], ['B', 699, 60]], 25, 30], [[['A', 665], ['B', 774]], []]),
  ('normal control 6', [[['Q1', 660, 44], ['Q0', 540, 60]], 15, 30], [[['Q0', 610], ['Q1', 715]], []]),
  ('normal control 7', [[['R1', 740, 72], ['U2', 920, 100], ['T3', 1120, 90], ['W0', 540, 120]], 10, 30],
   [[['W0', 675], ['R1', 820], ['U2', 1030], ['T3', 1220]], []]),
  ('normal control 8', [[['U1', 750, 88], ['R2', 900, 182], ['U0', 600, 90]], 10, 15],
   [[['U0', 700], ['U1', 850], ['R2', 1105]], []])]]
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
regression: chronological order 1[[['B', 870], ['A', 645]], [['B', 'A']]][[['A', 645], ['B', 870]], []]Failed
regression variant: chronological order 2[[['P1', 880], ['P0', 595], ['W2', 1025]], [['P1', 'P0']]][[['P0', 595], ['P1', 880], ['W2', 1025]], []]Failed
partial repair guard 3[[['T1', 880], ['V0', 840]], [['T1', 'V0']]][[['V0', 840], ['T1', 880]], [['V0', 'T1']]]Failed
boundary control 4[[['A', 720], ['B', 855]], []][[['A', 720], ['B', 855]], []]Passed
boundary control 5[[['A', 625], ['B', 710]], []][[['A', 625], ['B', 710]], []]Passed
normal control 6[[['R2', 1060], ['T3', 1260], ['V1', 945], ['W0', 685]], [['T3', 'V1'], ['V1', 'W0']]][[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]]Failed
normal control 7[[['W2', 1050], ['T0', 765], ['W1', 965], ['R3', 1280]], [['W2', 'T0'], ['T0', 'W1']]][[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]]Failed
normal control 8[[['R3', 1155], ['U0', 725], ['V2', 1155], ['R1', 860]], [['R3', 'U0'], ['V2', 'R1']]][[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]]Failed

SHA-256 / 0d3e9ca12e78ab7c66d3699c47b5224a39b49ba9a9f8bbc9ced0fd91c439567a

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(exams, pct, rest):
    rows = []
    for eid, st, dur in sorted(exams, key=lambda x: x[0]):
        ext = -(-dur * (100 + pct) // 100)
        ext = -(-ext // 5) * 5
        rows.append([eid, st, st + ext])
    conf = []
    for a, b in zip(rows, rows[1:]):
        if b[1] < a[2] + rest:
            conf.append([a[0], b[0]])
    return [[[r[0], r[2]] for r in rows], conf]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: chronological order 1', [[['B', 800, 60], ['A', 540, 90]], 15, 30],
   [[['A', 645], ['B', 870]], []]),
  ('regression variant: chronological order 2',
   [[['P1', 740, 120], ['P0', 540, 44], ['W2', 940, 72]], 15, 30],
   [[['P0', 595], ['P1', 880], ['W2', 1025]], []]),
  ('partial repair guard 3', [[['T1', 720, 120], ['V0', 600, 180]], 33, 15],
   [[['V0', 840], ['T1', 880]], [['V0', 'T1']]]),
  ('boundary control 4', [[['A', 540, 120], ['B', 720, 90]], 50, 0], [[['A', 720], ['B', 855]], []]),
  ('boundary control 5', [[['A', 540, 73], ['B', 640, 60]], 10, 15], [[['A', 625], ['B', 710]], []]),
  ('normal control 6', [[['R2', 960, 90], ['T3', 1160, 90], ['V1', 780, 150], ['W0', 600, 73]], 10, 30],
   [[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]]),
  ('normal control 7', [[['W2', 940, 88], ['T0', 540, 180], ['W1', 740, 180], ['R3', 1090, 150]], 25, 15],
   [[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]]),
  ('normal control 8', [[['R3', 1080, 60], ['U0', 600, 100], ['V2', 930, 180], ['R1', 750, 88]], 25, 30],
   [[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]])],
 [('regression: chronological order 1',
   [[['P1', 690, 150], ['S2', 890, 72], ['Q0', 600, 150], ['R3', 1010, 90]], 50, 15],
   [[['Q0', 825], ['P1', 915], ['S2', 1000], ['R3', 1145]], [['Q0', 'P1'], ['P1', 'S2'], ['S2', 'R3']]]),
  ('regression variant: chronological order 2',
   [[['P3', 1080, 180], ['U2', 930, 100], ['W1', 690, 72], ['T0', 540, 72]], 10, 15],
   [[['T0', 620], ['W1', 770], ['U2', 1040], ['P3', 1280]], []]),
  ('partial repair guard 3', [[['V0', 600, 182], ['T1', 690, 73]], 10, 30],
   [[['V0', 805], ['T1', 775]], [['V0', 'T1']]]),
  ('boundary control 4', [[['A', 540, 91]], 10, 30], [[['A', 645]], []]),
  ('boundary control 5', [[['A', 540, 100], ['B', 699, 60]], 25, 30], [[['A', 665], ['B', 774]], []]),
  ('normal control 6', [[['T0', 600, 72], ['P2', 1040, 100], ['T3', 1190, 182], ['Q1', 840, 100]], 10, 15],
   [[['T0', 680], ['Q1', 950], ['P2', 1150], ['T3', 1395]], []]),
  ('normal control 7', [[['Q2', 840, 91], ['S0', 600, 88], ['R1', 750, 73]], 50, 30],
   [[['S0', 735], ['R1', 860], ['Q2', 980]], [['S0', 'R1'], ['R1', 'Q2']]]),
  ('normal control 8', [[['S1', 630, 100], ['V3', 930, 88], ['V0', 540, 180], ['R2', 750, 73]], 50, 30],
   [[['V0', 810], ['S1', 780], ['R2', 860], ['V3', 1065]], [['V0', 'S1'], ['S1', 'R2']]])],
 [('regression: chronological order 1', [[['T1', 840, 72], ['R0', 600, 44]], 33, 30],
   [[['R0', 660], ['T1', 940]], []]),
  ('regression variant: chronological order 2',
   [[['U0', 540, 150], ['P2', 870, 60], ['Q1', 780, 88]], 10, 30],
   [[['U0', 705], ['Q1', 880], ['P2', 940]], [['Q1', 'P2']]]),
  ('partial repair guard 3',
   [[['S2', 810, 100], ['Q0', 600, 72], ['V1', 690, 100], ['R3', 990, 120]], 33, 15],
   [[['Q0', 700], ['V1', 825], ['S2', 945], ['R3', 1150]], [['Q0', 'V1'], ['V1', 'S2']]]),
  ('boundary control 4', [[['A', 540, 88]], 25, 30], [[['A', 650]], []]),
  ('boundary control 5', [[['A', 540, 72]], 33, 15], [[['A', 640]], []]),
  ('normal control 6', [[['S0', 540, 90], ['U1', 740, 90]], 15, 30], [[['S0', 645], ['U1', 845]], []]),
  ('normal control 7', [[['Q2', 810, 120], ['S3', 1050, 73], ['V0', 540, 90], ['V1', 630, 73]], 25, 30],
   [[['V0', 655], ['V1', 725], ['Q2', 960], ['S3', 1145]], [['V0', 'V1']]]),
  ('normal control 8', [[['V1', 630, 182], ['Q0', 540, 120]], 50, 30],
   [[['Q0', 720], ['V1', 905]], [['Q0', 'V1']]])],
 [('regression: chronological order 1',
   [[['S2', 900, 73], ['P1', 780, 60], ['U3', 1050, 44], ['W0', 540, 72]], 50, 30],
   [[['W0', 650], ['P1', 870], ['S2', 1010], ['U3', 1120]], []]),
  ('regression variant: chronological order 2', [[['P1', 780, 150], ['U0', 600, 90]], 50, 15],
   [[['U0', 735], ['P1', 1005]], []]),
  ('partial repair guard 3',
   [[['V0', 600, 90], ['R2', 990, 91], ['S1', 840, 120], ['Q3', 1110, 100]], 33, 30],
   [[['V0', 720], ['S1', 1000], ['R2', 1115], ['Q3', 1245]], [['S1', 'R2'], ['R2', 'Q3']]]),
  ('boundary control 4', [[['A', 540, 120], ['B', 720, 90]], 50, 0], [[['A', 720], ['B', 855]], []]),
  ('boundary control 5', [[['A', 540, 73], ['B', 640, 60]], 10, 15], [[['A', 625], ['B', 710]], []]),
  ('normal control 6', [[['T1', 780, 180], ['P0', 540, 88]], 50, 30], [[['P0', 675], ['T1', 1050]], []]),
  ('normal control 7', [[['P3', 1110, 44], ['V1', 720, 72], ['S2', 870, 72], ['P0', 540, 91]], 15, 15],
   [[['P0', 645], ['V1', 805], ['S2', 955], ['P3', 1165]], []]),
  ('normal control 8', [[['P0', 600, 150], ['R2', 840, 90], ['S1', 720, 90]], 50, 30],
   [[['P0', 825], ['S1', 855], ['R2', 975]], [['P0', 'S1'], ['S1', 'R2']]])],
 [('regression: chronological order 1',
   [[['V2', 810, 180], ['U0', 540, 44], ['V3', 1010, 72], ['T1', 720, 100]], 33, 30],
   [[['U0', 600], ['T1', 855], ['V2', 1050], ['V3', 1110]], [['T1', 'V2'], ['V2', 'V3']]]),
  ('regression variant: chronological order 2',
   [[['Q1', 780, 150], ['P2', 1020, 73], ['V3', 1220, 150], ['W0', 540, 180]], 15, 15],
   [[['W0', 750], ['Q1', 955], ['P2', 1105], ['V3', 1395]], []]),
  ('partial repair guard 3',
   [[['U2', 840, 44], ['W3', 1080, 150], ['V1', 660, 180], ['V0', 540, 91]], 33, 30],
   [[['V0', 665], ['V1', 900], ['U2', 900], ['W3', 1280]], [['V0', 'V1'], ['V1', 'U2']]]),
  ('boundary control 4', [[['A', 540, 91]], 10, 30], [[['A', 645]], []]),
  ('boundary control 5', [[['A', 540, 100], ['B', 699, 60]], 25, 30], [[['A', 665], ['B', 774]], []]),
  ('normal control 6', [[['Q1', 660, 44], ['Q0', 540, 60]], 15, 30], [[['Q0', 610], ['Q1', 715]], []]),
  ('normal control 7', [[['R1', 740, 72], ['U2', 920, 100], ['T3', 1120, 90], ['W0', 540, 120]], 10, 30],
   [[['W0', 675], ['R1', 820], ['U2', 1030], ['T3', 1220]], []]),
  ('normal control 8', [[['U1', 750, 88], ['R2', 900, 182], ['U0', 600, 90]], 10, 15],
   [[['U0', 700], ['U1', 850], ['R2', 1105]], []])]]
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
regression: chronological order 1[[['A', 645], ['B', 870]], []][[['A', 645], ['B', 870]], []]Passed
regression variant: chronological order 2[[['P0', 595], ['P1', 880], ['W2', 1025]], []][[['P0', 595], ['P1', 880], ['W2', 1025]], []]Passed
partial repair guard 3[[['T1', 880], ['V0', 840]], [['T1', 'V0']]][[['V0', 840], ['T1', 880]], [['V0', 'T1']]]Failed
boundary control 4[[['A', 720], ['B', 855]], []][[['A', 720], ['B', 855]], []]Passed
boundary control 5[[['A', 625], ['B', 710]], []][[['A', 625], ['B', 710]], []]Passed
normal control 6[[['R2', 1060], ['T3', 1260], ['V1', 945], ['W0', 685]], [['T3', 'V1'], ['V1', 'W0']]][[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]]Failed
normal control 7[[['R3', 1280], ['T0', 765], ['W1', 965], ['W2', 1050]], [['R3', 'T0'], ['T0', 'W1'], ['W1', 'W2']]][[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]]Failed
normal control 8[[['R1', 860], ['R3', 1155], ['U0', 725], ['V2', 1155]], [['R3', 'U0']]][[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]]Failed

SHA-256 / be3dda019d35a90ca4b5a3b8c0a4f7b9d44eaa2f5541b51170e53613d2fd9856

3 / The verified repair

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

N = 1
observations = []
def solve(exams, pct, rest):
    rows = []
    for eid, st, dur in sorted(exams, key=lambda x: (x[1], x[0])):
        ext = -(-dur * (100 + pct) // 100)
        ext = -(-ext // 5) * 5
        rows.append([eid, st, st + ext])
    conf = []
    for a, b in zip(rows, rows[1:]):
        if b[1] < a[2] + rest:
            conf.append([a[0], b[0]])
    return [[[r[0], r[2]] for r in rows], conf]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: chronological order 1', [[['B', 800, 60], ['A', 540, 90]], 15, 30],
   [[['A', 645], ['B', 870]], []]),
  ('regression variant: chronological order 2',
   [[['P1', 740, 120], ['P0', 540, 44], ['W2', 940, 72]], 15, 30],
   [[['P0', 595], ['P1', 880], ['W2', 1025]], []]),
  ('partial repair guard 3', [[['T1', 720, 120], ['V0', 600, 180]], 33, 15],
   [[['V0', 840], ['T1', 880]], [['V0', 'T1']]]),
  ('boundary control 4', [[['A', 540, 120], ['B', 720, 90]], 50, 0], [[['A', 720], ['B', 855]], []]),
  ('boundary control 5', [[['A', 540, 73], ['B', 640, 60]], 10, 15], [[['A', 625], ['B', 710]], []]),
  ('normal control 6', [[['R2', 960, 90], ['T3', 1160, 90], ['V1', 780, 150], ['W0', 600, 73]], 10, 30],
   [[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]]),
  ('normal control 7', [[['W2', 940, 88], ['T0', 540, 180], ['W1', 740, 180], ['R3', 1090, 150]], 25, 15],
   [[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]]),
  ('normal control 8', [[['R3', 1080, 60], ['U0', 600, 100], ['V2', 930, 180], ['R1', 750, 88]], 25, 30],
   [[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]])],
 [('regression: chronological order 1',
   [[['P1', 690, 150], ['S2', 890, 72], ['Q0', 600, 150], ['R3', 1010, 90]], 50, 15],
   [[['Q0', 825], ['P1', 915], ['S2', 1000], ['R3', 1145]], [['Q0', 'P1'], ['P1', 'S2'], ['S2', 'R3']]]),
  ('regression variant: chronological order 2',
   [[['P3', 1080, 180], ['U2', 930, 100], ['W1', 690, 72], ['T0', 540, 72]], 10, 15],
   [[['T0', 620], ['W1', 770], ['U2', 1040], ['P3', 1280]], []]),
  ('partial repair guard 3', [[['V0', 600, 182], ['T1', 690, 73]], 10, 30],
   [[['V0', 805], ['T1', 775]], [['V0', 'T1']]]),
  ('boundary control 4', [[['A', 540, 91]], 10, 30], [[['A', 645]], []]),
  ('boundary control 5', [[['A', 540, 100], ['B', 699, 60]], 25, 30], [[['A', 665], ['B', 774]], []]),
  ('normal control 6', [[['T0', 600, 72], ['P2', 1040, 100], ['T3', 1190, 182], ['Q1', 840, 100]], 10, 15],
   [[['T0', 680], ['Q1', 950], ['P2', 1150], ['T3', 1395]], []]),
  ('normal control 7', [[['Q2', 840, 91], ['S0', 600, 88], ['R1', 750, 73]], 50, 30],
   [[['S0', 735], ['R1', 860], ['Q2', 980]], [['S0', 'R1'], ['R1', 'Q2']]]),
  ('normal control 8', [[['S1', 630, 100], ['V3', 930, 88], ['V0', 540, 180], ['R2', 750, 73]], 50, 30],
   [[['V0', 810], ['S1', 780], ['R2', 860], ['V3', 1065]], [['V0', 'S1'], ['S1', 'R2']]])],
 [('regression: chronological order 1', [[['T1', 840, 72], ['R0', 600, 44]], 33, 30],
   [[['R0', 660], ['T1', 940]], []]),
  ('regression variant: chronological order 2',
   [[['U0', 540, 150], ['P2', 870, 60], ['Q1', 780, 88]], 10, 30],
   [[['U0', 705], ['Q1', 880], ['P2', 940]], [['Q1', 'P2']]]),
  ('partial repair guard 3',
   [[['S2', 810, 100], ['Q0', 600, 72], ['V1', 690, 100], ['R3', 990, 120]], 33, 15],
   [[['Q0', 700], ['V1', 825], ['S2', 945], ['R3', 1150]], [['Q0', 'V1'], ['V1', 'S2']]]),
  ('boundary control 4', [[['A', 540, 88]], 25, 30], [[['A', 650]], []]),
  ('boundary control 5', [[['A', 540, 72]], 33, 15], [[['A', 640]], []]),
  ('normal control 6', [[['S0', 540, 90], ['U1', 740, 90]], 15, 30], [[['S0', 645], ['U1', 845]], []]),
  ('normal control 7', [[['Q2', 810, 120], ['S3', 1050, 73], ['V0', 540, 90], ['V1', 630, 73]], 25, 30],
   [[['V0', 655], ['V1', 725], ['Q2', 960], ['S3', 1145]], [['V0', 'V1']]]),
  ('normal control 8', [[['V1', 630, 182], ['Q0', 540, 120]], 50, 30],
   [[['Q0', 720], ['V1', 905]], [['Q0', 'V1']]])],
 [('regression: chronological order 1',
   [[['S2', 900, 73], ['P1', 780, 60], ['U3', 1050, 44], ['W0', 540, 72]], 50, 30],
   [[['W0', 650], ['P1', 870], ['S2', 1010], ['U3', 1120]], []]),
  ('regression variant: chronological order 2', [[['P1', 780, 150], ['U0', 600, 90]], 50, 15],
   [[['U0', 735], ['P1', 1005]], []]),
  ('partial repair guard 3',
   [[['V0', 600, 90], ['R2', 990, 91], ['S1', 840, 120], ['Q3', 1110, 100]], 33, 30],
   [[['V0', 720], ['S1', 1000], ['R2', 1115], ['Q3', 1245]], [['S1', 'R2'], ['R2', 'Q3']]]),
  ('boundary control 4', [[['A', 540, 120], ['B', 720, 90]], 50, 0], [[['A', 720], ['B', 855]], []]),
  ('boundary control 5', [[['A', 540, 73], ['B', 640, 60]], 10, 15], [[['A', 625], ['B', 710]], []]),
  ('normal control 6', [[['T1', 780, 180], ['P0', 540, 88]], 50, 30], [[['P0', 675], ['T1', 1050]], []]),
  ('normal control 7', [[['P3', 1110, 44], ['V1', 720, 72], ['S2', 870, 72], ['P0', 540, 91]], 15, 15],
   [[['P0', 645], ['V1', 805], ['S2', 955], ['P3', 1165]], []]),
  ('normal control 8', [[['P0', 600, 150], ['R2', 840, 90], ['S1', 720, 90]], 50, 30],
   [[['P0', 825], ['S1', 855], ['R2', 975]], [['P0', 'S1'], ['S1', 'R2']]])],
 [('regression: chronological order 1',
   [[['V2', 810, 180], ['U0', 540, 44], ['V3', 1010, 72], ['T1', 720, 100]], 33, 30],
   [[['U0', 600], ['T1', 855], ['V2', 1050], ['V3', 1110]], [['T1', 'V2'], ['V2', 'V3']]]),
  ('regression variant: chronological order 2',
   [[['Q1', 780, 150], ['P2', 1020, 73], ['V3', 1220, 150], ['W0', 540, 180]], 15, 15],
   [[['W0', 750], ['Q1', 955], ['P2', 1105], ['V3', 1395]], []]),
  ('partial repair guard 3',
   [[['U2', 840, 44], ['W3', 1080, 150], ['V1', 660, 180], ['V0', 540, 91]], 33, 30],
   [[['V0', 665], ['V1', 900], ['U2', 900], ['W3', 1280]], [['V0', 'V1'], ['V1', 'U2']]]),
  ('boundary control 4', [[['A', 540, 91]], 10, 30], [[['A', 645]], []]),
  ('boundary control 5', [[['A', 540, 100], ['B', 699, 60]], 25, 30], [[['A', 665], ['B', 774]], []]),
  ('normal control 6', [[['Q1', 660, 44], ['Q0', 540, 60]], 15, 30], [[['Q0', 610], ['Q1', 715]], []]),
  ('normal control 7', [[['R1', 740, 72], ['U2', 920, 100], ['T3', 1120, 90], ['W0', 540, 120]], 10, 30],
   [[['W0', 675], ['R1', 820], ['U2', 1030], ['T3', 1220]], []]),
  ('normal control 8', [[['U1', 750, 88], ['R2', 900, 182], ['U0', 600, 90]], 10, 15],
   [[['U0', 700], ['U1', 850], ['R2', 1105]], []])]]
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
regression: chronological order 1[[['A', 645], ['B', 870]], []][[['A', 645], ['B', 870]], []]Passed
regression variant: chronological order 2[[['P0', 595], ['P1', 880], ['W2', 1025]], []][[['P0', 595], ['P1', 880], ['W2', 1025]], []]Passed
partial repair guard 3[[['V0', 840], ['T1', 880]], [['V0', 'T1']]][[['V0', 840], ['T1', 880]], [['V0', 'T1']]]Passed
boundary control 4[[['A', 720], ['B', 855]], []][[['A', 720], ['B', 855]], []]Passed
boundary control 5[[['A', 625], ['B', 710]], []][[['A', 625], ['B', 710]], []]Passed
normal control 6[[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]][[['W0', 685], ['V1', 945], ['R2', 1060], ['T3', 1260]], [['V1', 'R2']]]Passed
normal control 7[[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]][[['T0', 765], ['W1', 965], ['W2', 1050], ['R3', 1280]], [['T0', 'W1'], ['W1', 'W2']]]Passed
normal control 8[[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]][[['U0', 725], ['R1', 860], ['V2', 1155], ['R3', 1155]], [['U0', 'R1'], ['V2', 'R3']]]Passed

SHA-256 / 16eb22da13a914ae6069d8b159d766500623831fb5f311b793ae25f2a467c12d

Verification & scope

Stipulated toy exam-timetabling rule set for a bounded model; it does not claim conformance with any institution's regulations or a benchmark specification, and it performs no search or optimisation. 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:52:04.586485+00:00.

Case digest / cf130013be213b0df6077963331cb87b2dd01cf24e829ed625885d3e576561bb