Add cardinality set methods, conformance tests

This commit is contained in:
Jonathan Harker 2024-09-23 11:28:51 +12:00
parent 5e924992d4
commit 066477755d
2 changed files with 80 additions and 0 deletions

View file

@ -168,3 +168,35 @@ class SetClass(list):
pitches += f"{pitch}{sub}"
sup = superscript(self.brightness)
return f"[{pitches}]{sup}"
# Class methods -----------------------------------------------------------
def all_of_cardinality(cardinality: int, tonality: int = 12) -> set:
"""
Returns all set classes of a given cardinality. Tonality can be specified, default is 12.
Warning: high values of tonality can take a long time to calculate (T=24 takes about a
minute on an Intel i7-13700H CPU).
"""
def _powerset(seq):
"""Set of all possible unique subsets."""
from itertools import chain, combinations
return chain.from_iterable(combinations(seq, r) for r in range(len(seq) + 1))
if tonality < cardinality:
return ValueError("Set classes of cardinality {cardinality} are not possible in a {tonality}-tone octave.")
a = set(SetClass(*i) for i in _powerset(range(tonality)) if len(i) == cardinality and 0 in i)
return a
def darkest_of_cardinality(cardinality: int, tonality: int = 12, prime: bool = False) -> set:
"""
Returns all set classes of a given cardinality, in their darkest forms (ignore rotations).
Tonality can be specified, default is 12.
Warning: high values of tonality can take a long time to calculate (T=24 takes about a
minute on an Intel i7-13700H CPU).
"""
if tonality < cardinality:
return ValueError("Set classes of cardinality {cardinality} are not possible in a {tonality}-tone octave.")
a = set(i.darkest_form for i in SetClass.all_of_cardinality(cardinality, tonality))
return a

View file

@ -248,3 +248,51 @@ def test_empty_complement_dozenal_notation():
def test_empty_complement_leonard_notation():
assert f121.leonard_notation == '[0₁1₁2₁3₁4₁5₁6₁7₁8₁9₁10₁11₁]⁶⁶'
# ----------------------------------------------------------------------------
def test_all_of_cardinality_set_lengths():
"Confirm expected lengths of the sets of set classes, for cardinality 1 through 12"
sets = [SetClass.all_of_cardinality(i + 1) for i in range(12)]
assert [len(s) for s in sets] == [1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
def test_darkest_of_cardinality_set_lengths():
"Confirm expected lengths of the sets of set classes, for cardinality 1 through 12"
sets = [SetClass.darkest_of_cardinality(i + 1) for i in range(12)]
assert [len(s) for s in sets] == [1, 6, 22, 55, 66, 127, 66, 55, 22, 6, 1, 1]
def test_all_of_cardinality():
s3 = SetClass.all_of_cardinality(3)
s6 = SetClass.all_of_cardinality(6)
assert len(s3) == 55
assert len(s6) == 462
# all versions (rotations) of each set class are present
assert SetClass(0, 1, 2) in s3
assert SetClass(0, 10, 11) in s3
assert SetClass(0, 4, 6) in s3
assert SetClass(0, 6, 10) in s3
assert SetClass(0, 1, 3, 4, 8, 10) in s6
assert SetClass(0, 4, 6, 8, 9, 11) in s6
def test_darkest_of_cardinality():
s3 = SetClass.darkest_of_cardinality(3)
s6 = SetClass.darkest_of_cardinality(6)
assert len(s3) == 22
assert len(s6) == 127
# only one version (rotation) of each set class is present
assert SetClass(0, 1, 2) in s3
assert SetClass(0, 10, 11) not in s3
assert SetClass(0, 4, 6) in s3
assert SetClass(0, 6, 10) not in s3
assert SetClass(0, 1, 3, 4, 8, 10) in s6
assert SetClass(0, 4, 6, 8, 9, 11) not in s6