diff --git a/setclass/setclass.py b/setclass/setclass.py index 30ff090..6c458bc 100644 --- a/setclass/setclass.py +++ b/setclass/setclass.py @@ -290,3 +290,18 @@ class SetClass(list): minute on an Intel i7-13700H CPU). """ return set(i.rahn_normal_form for i in SetClass.all_of_cardinality(cardinality, tonality)) + + +def bright_rahn_normal_forms(): + """ + John Rahn's normal form from _Basic Atonal Theory_ (1980) is an algorithm to produce a unique + form for each set class. Most of the time it is also the "darkest" (smallest brightness value), + except for all the times when that is not the case; this function returns that list, as a set of + (cardinality, darkest, rahn) tuples. + """ + cases = set() + for C in range(13): # 0 to 12 + for sc in SetClass.all_of_cardinality(C): + if sc.darkest_form.brightness < sc.rahn_normal_form.brightness: + cases.add((C, sc.darkest_form, sc.rahn_normal_form)) + return cases diff --git a/setclass/tests/test_exploration.py b/setclass/tests/test_exploration.py index 1875349..27fb2b4 100644 --- a/setclass/tests/test_exploration.py +++ b/setclass/tests/test_exploration.py @@ -1,4 +1,4 @@ -from setclass.setclass import SetClass +from setclass.setclass import SetClass, bright_rahn_normal_forms def test_brightness_conjecture(): @@ -19,3 +19,43 @@ def test_brightness_conjecture(): test = sc.complement.inversion.darkest_form.brightness - sc.brightness ΔB = int((T - 1) * (T / 2 - C)) assert test == ΔB, f"T={T}, C={C}, ΔB={ΔB}, diff={test}: {sc.darkest_form}, {sc.complement.inversion.darkest_form}" + + +def test_bright_rahn(): + """ + There are 63 instances where the Rahn normal form is not also the "darkest" (smallest sum of + pitch class values). + """ + R = bright_rahn_normal_forms() + assert len(R) == 63 + counts = { + 0: 0, + 1: 0, + 2: 0, + 3: 1, + 4: 3, + 5: 17, + 6: 7, + 7: 23, + 8: 9, + 9: 3, + 10: 0, + 11: 0, + 12: 0 + } + for C in range(13): # 0 to 12 + bright_rahns = set(i for i in R if i[0] == C) + assert len(bright_rahns) == counts[C] + + +def print_bright_rahn(): + """ + Convenience function, to print the list of set classes where John Rahn's normal form is not the + "darkest" form (that with the smallest brightness value), as pairs of "darkest" and Rahn normal + form pairs, using Leonard notation, sorted by cardinality then brightness (sum of pitch class + values). + """ + print("Smallest brightness, Rahn normal form") + set_classes = sorted(bright_rahn_normal_forms(), key=lambda x: x[0] * 1000 + x[1].brightness) + for (C, d, r) in set_classes: + print(f"{d.leonard_notation}, {r.leonard_notation}")