FA-81021 / Music interval and transposition theory / Open access
Pitch-class set normal form (right-packed): duplicate pitch classes kept in the set · case 01
Doubled notes such as [9,0,4,4,16] yield a normal form containing repeated pitch classes.
ROOT CAUSE
The pitch classes are reduced mod 12 but not deduplicated before rotation.
VERIFIED REPAIR
Restore the pitch class deduplication step so that it reads `sorted(set(v % 12 for v in x))`.
Unsuccessful approach: Deduplicating raw integers before reduction keeps octave-duplicates like 4 and 16 as separate members.
Case contract
Input a list of integers (reduced mod 12, duplicates removed). Return the normal form: among all rotations of the ascending set, choose the one with the smallest span first-to-last, then smallest span first-to-penultimate, and so on toward the second element; a full tie picks the rotation starting on the lowest pitch class. Empty input returns []; non-integer input returns None.
Why this case matters
Post-tonal analysis tools use normal form to compare and catalogue pitch-class sets.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or not all(isinstance(v, int) for v in x):
return None
pcs = sorted(v % 12 for v in x)
if not pcs:
return []
n = len(pcs)
best = None
for r in range(n):
rot = pcs[r:] + [p + 12 for p in pcs[:r]]
key = [rot[-1] - rot[0]] + [rot[j] - rot[0] for j in range(n - 2, 0, -1)]
cand = (key, rot[0])
if best is None or cand < best[0]:
best = (cand, [p % 12 for p in rot])
return best[1]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 4, 7], [0, 4, 7]), ([7, 4, 0], [0, 4, 7]), ([11, 0, 4], [11, 0, 4]), ([0, 1, 5, 6, 8], [0, 1, 5, 6, 8]), ([0, 1, 3, 5, 8, 9], [8, 9, 0, 1, 3, 5]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([0, 1, 5, 6, 8], [0, 1, 5, 6, 8]), ([0, 1, 3, 5, 8, 9], [8, 9, 0, 1, 3, 5]), ([0, 2, 3, 6, 7, 9], [0, 2, 3, 6, 7, 9]), ([0, 1, 2, 5, 6, 7, 9], [0, 1, 2, 5, 6, 7, 9]), ([0, 1, 2, 4, 5, 7, 9, 10], [9, 10, 0, 1, 2, 4, 5, 7]), ([0, 4, 8], [0, 4, 8]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3])], [([0, 1, 2, 5, 6, 7, 9], [0, 1, 2, 5, 6, 7, 9]), ([0, 1, 2, 4, 5, 7, 9, 10], [9, 10, 0, 1, 2, 4, 5, 7]), ([0, 4, 8], [0, 4, 8]), ([0, 3, 6, 9], [0, 3, 6, 9]), ([0, 6], [0, 6]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([0, 3, 6, 9], [0, 3, 6, 9]), ([0, 6], [0, 6]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([2, 5, 9], [2, 5, 9]), ([10, 2, 5], [10, 2, 5]), ([1, 5, 8], [1, 5, 8]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([9, 0, 4, 4, 16], [9, 0, 4]), ([10, 2, 5], [10, 2, 5]), ([1, 5, 8], [1, 5, 8]), ([11, 2, 6], [11, 2, 6]), ([0, 1, 4, 6], [0, 1, 4, 6]), ([0, 1, 3, 7], [0, 1, 3, 7]), ([5], [5]), ([3, 15, 27], [3])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | [0, 4, 7] | [0, 4, 7] | Passed |
| oracle 1 | [0, 4, 7] | [0, 4, 7] | Passed |
| oracle 2 | [11, 0, 4] | [11, 0, 4] | Passed |
| oracle 3 | [0, 1, 5, 6, 8] | [0, 1, 5, 6, 8] | Passed |
| oracle 4 | [8, 9, 0, 1, 3, 5] | [8, 9, 0, 1, 3, 5] | Passed |
| oracle 5 | [9, 0, 4, 4, 4] | [9, 0, 4] | Failed |
| oracle 6 | [3, 3, 3] | [3] | Failed |
| oracle 7 | [1, 1, 4, 7, 10] | [1, 4, 7, 10] | Failed |
SHA-256 / a586b236000c45e449f27f46bf641d7be7f26a48d8c3433bb8b9af5f75835c85
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or not all(isinstance(v, int) for v in x):
return None
pcs = sorted(set(x))
if not pcs:
return []
n = len(pcs)
best = None
for r in range(n):
rot = pcs[r:] + [p + 12 for p in pcs[:r]]
key = [rot[-1] - rot[0]] + [rot[j] - rot[0] for j in range(n - 2, 0, -1)]
cand = (key, rot[0])
if best is None or cand < best[0]:
best = (cand, [p % 12 for p in rot])
return best[1]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 4, 7], [0, 4, 7]), ([7, 4, 0], [0, 4, 7]), ([11, 0, 4], [11, 0, 4]), ([0, 1, 5, 6, 8], [0, 1, 5, 6, 8]), ([0, 1, 3, 5, 8, 9], [8, 9, 0, 1, 3, 5]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([0, 1, 5, 6, 8], [0, 1, 5, 6, 8]), ([0, 1, 3, 5, 8, 9], [8, 9, 0, 1, 3, 5]), ([0, 2, 3, 6, 7, 9], [0, 2, 3, 6, 7, 9]), ([0, 1, 2, 5, 6, 7, 9], [0, 1, 2, 5, 6, 7, 9]), ([0, 1, 2, 4, 5, 7, 9, 10], [9, 10, 0, 1, 2, 4, 5, 7]), ([0, 4, 8], [0, 4, 8]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3])], [([0, 1, 2, 5, 6, 7, 9], [0, 1, 2, 5, 6, 7, 9]), ([0, 1, 2, 4, 5, 7, 9, 10], [9, 10, 0, 1, 2, 4, 5, 7]), ([0, 4, 8], [0, 4, 8]), ([0, 3, 6, 9], [0, 3, 6, 9]), ([0, 6], [0, 6]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([0, 3, 6, 9], [0, 3, 6, 9]), ([0, 6], [0, 6]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([2, 5, 9], [2, 5, 9]), ([10, 2, 5], [10, 2, 5]), ([1, 5, 8], [1, 5, 8]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([9, 0, 4, 4, 16], [9, 0, 4]), ([10, 2, 5], [10, 2, 5]), ([1, 5, 8], [1, 5, 8]), ([11, 2, 6], [11, 2, 6]), ([0, 1, 4, 6], [0, 1, 4, 6]), ([0, 1, 3, 7], [0, 1, 3, 7]), ([5], [5]), ([3, 15, 27], [3])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | [0, 4, 7] | [0, 4, 7] | Passed |
| oracle 1 | [0, 4, 7] | [0, 4, 7] | Passed |
| oracle 2 | [11, 0, 4] | [11, 0, 4] | Passed |
| oracle 3 | [0, 1, 5, 6, 8] | [0, 1, 5, 6, 8] | Passed |
| oracle 4 | [8, 9, 0, 1, 3, 5] | [8, 9, 0, 1, 3, 5] | Passed |
| oracle 5 | [4, 0, 4, 9] | [9, 0, 4] | Failed |
| oracle 6 | [3, 3, 3] | [3] | Failed |
| oracle 7 | [1, 4, 7, 10] | [1, 4, 7, 10] | Passed |
SHA-256 / 72d9c4d3ef0d8ac2ba90c894586cebad0da8e4d2d946c9231ea5ab69d6ce9bd2
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or not all(isinstance(v, int) for v in x):
return None
pcs = sorted(set(v % 12 for v in x))
if not pcs:
return []
n = len(pcs)
best = None
for r in range(n):
rot = pcs[r:] + [p + 12 for p in pcs[:r]]
key = [rot[-1] - rot[0]] + [rot[j] - rot[0] for j in range(n - 2, 0, -1)]
cand = (key, rot[0])
if best is None or cand < best[0]:
best = (cand, [p % 12 for p in rot])
return best[1]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 4, 7], [0, 4, 7]), ([7, 4, 0], [0, 4, 7]), ([11, 0, 4], [11, 0, 4]), ([0, 1, 5, 6, 8], [0, 1, 5, 6, 8]), ([0, 1, 3, 5, 8, 9], [8, 9, 0, 1, 3, 5]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([0, 1, 5, 6, 8], [0, 1, 5, 6, 8]), ([0, 1, 3, 5, 8, 9], [8, 9, 0, 1, 3, 5]), ([0, 2, 3, 6, 7, 9], [0, 2, 3, 6, 7, 9]), ([0, 1, 2, 5, 6, 7, 9], [0, 1, 2, 5, 6, 7, 9]), ([0, 1, 2, 4, 5, 7, 9, 10], [9, 10, 0, 1, 2, 4, 5, 7]), ([0, 4, 8], [0, 4, 8]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3])], [([0, 1, 2, 5, 6, 7, 9], [0, 1, 2, 5, 6, 7, 9]), ([0, 1, 2, 4, 5, 7, 9, 10], [9, 10, 0, 1, 2, 4, 5, 7]), ([0, 4, 8], [0, 4, 8]), ([0, 3, 6, 9], [0, 3, 6, 9]), ([0, 6], [0, 6]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([0, 3, 6, 9], [0, 3, 6, 9]), ([0, 6], [0, 6]), ([9, 0, 4, 4, 16], [9, 0, 4]), ([2, 5, 9], [2, 5, 9]), ([10, 2, 5], [10, 2, 5]), ([1, 5, 8], [1, 5, 8]), ([3, 15, 27], [3]), ([1, 4, 7, 10, 1], [1, 4, 7, 10])], [([9, 0, 4, 4, 16], [9, 0, 4]), ([10, 2, 5], [10, 2, 5]), ([1, 5, 8], [1, 5, 8]), ([11, 2, 6], [11, 2, 6]), ([0, 1, 4, 6], [0, 1, 4, 6]), ([0, 1, 3, 7], [0, 1, 3, 7]), ([5], [5]), ([3, 15, 27], [3])]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | [0, 4, 7] | [0, 4, 7] | Passed |
| oracle 1 | [0, 4, 7] | [0, 4, 7] | Passed |
| oracle 2 | [11, 0, 4] | [11, 0, 4] | Passed |
| oracle 3 | [0, 1, 5, 6, 8] | [0, 1, 5, 6, 8] | Passed |
| oracle 4 | [8, 9, 0, 1, 3, 5] | [8, 9, 0, 1, 3, 5] | Passed |
| oracle 5 | [9, 0, 4] | [9, 0, 4] | Passed |
| oracle 6 | [3] | [3] | Passed |
| oracle 7 | [1, 4, 7, 10] | [1, 4, 7, 10] | Passed |
SHA-256 / 72be89219c720d49f8fc04553dcbae31f8d0538d0fe58013341f740d560e360a
Verification & scope
A deterministic bounded teaching model with a stipulated toy contract; it is not a complete music notation or theory engine. 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:49:59.252692+00:00.
Case digest / b0bf59e6daefdb88616936bcd7b4d2db3442d9babd1cf961c3f99f8b660f2723