diff --git a/setclass/setclass.py b/setclass/setclass.py index d9b52ce..00ea8dc 100644 --- a/setclass/setclass.py +++ b/setclass/setclass.py @@ -7,7 +7,7 @@ import re class SetClass(list): """ Musical set class, containing zero or more pitch classes. This implementation can handle set - classes of any arbitrary :attr:`tonality`, or number of divisions of the octave. + classes of any arbitrary :attr:`tonality`, or number of uniform divisions of the octave. """ def __init__(self, *args: int, tonality: int = 12) -> None: @@ -15,8 +15,8 @@ class SetClass(list): Instantiate a :class:`ClassSet` object with a series of integers. Each supplied pitch class is "normalised" by modulo the :attr:`tonality`, the set is - sorted in ascending order, and values are transposed (rotated) until the lowest value is 0. - For a number of divisions of the octave other than the default (Western harmony) value of + sorted in ascending order, and values are transposed until the lowest pitch class value is + 0. For a number of divisions of the octave other than the default (Western harmony) value of 12, supply an integer value using the 'tonality' keyword. Example: >>> sc1 = SetClass(0, 7, 9) @@ -30,7 +30,7 @@ class SetClass(list): *args (int): The pitch class values. tonality (int): - The :attr:`tonality`, or modulus, is the number of divisions of the octave. + The :attr:`tonality`, or modulus, is the number of uniform divisions of the octave. If unspecified, the default value of 12 is assumed (Western harmony). """ self._tonality = tonality @@ -69,7 +69,7 @@ class SetClass(list): @cached_property def tonality(self) -> int: """ - The number of (equal) divisions of the octave. The default value is 12, which + The number of uniform divisions of the octave. The default value is 12, which represents traditional Western chromatic harmony, the octave divided into twelve semitones. """ return self._tonality @@ -87,7 +87,7 @@ class SetClass(list): The brightness of the set class. Brightness (B) is a property proposed by Brian Leonard, defined as the sum of the values of - the :attr:`pitch_classes`. + the :attr:`pitch_classes` in the set class. """ return sum(self.pitch_classes) @@ -97,8 +97,8 @@ class SetClass(list): The decimal value of the binary representation of the :attr:`pitch_classes`. Returns the decimal value of the pitch classes expressed as a binary bit mask, i.e. the sum - of 2ⁱ where i is each pitch class value. For example, set class {0,1,4,6} has a binary value - of 000001010011, which is the decimal value 83. + of 2ⁱ where i is each pitch class value in ascending order. For example, set class {0,1,4,6} + has a binary value of 000001010011, which is the decimal value 83. Further reading: Goyette (2012) p. 25, citing Brinkman (1986). """ @@ -118,22 +118,6 @@ class SetClass(list): intervals.append(self.tonality - prev) return intervals - @cached_property - def z_relations(self) -> list: - """ - A :class:`list` of the Z-relations of this set class. - - Allen Forte in his book *The Structure of Atonal Music* (1973) described the relationship - between twins of set classes that share the same :attr:`interval_vector`, but are not - related by :attr:`inversion`, :attr:`complement`, or transposition (rotation), as Z-related - ('Z' for *zygote* from Greek: ζυγωτός, 'joined'). This property returns all distinct set - classes with the same interval vector. - - For example, Forte 4-Z15 {0,1,4,6} and Forte 4-Z29 {0,1,3,7} both have iv⟨1,1,1,1,1,1⟩ but - are not inversions, complements, or transpositions (rotations) of each other. - """ - return [i for i in SetClass.darkest_of_cardinality(self.cardinality) if i.interval_vector == self.interval_vector] - @cached_property def interval_vector(self) -> list: """ @@ -150,6 +134,22 @@ class SetClass(list): iv[ic - 1] += 1 return iv + @cached_property + def z_relations(self) -> list: + """ + A :class:`list` of the Z-relations of this set class. + + Allen Forte in his book *The Structure of Atonal Music* (1973) described the relationship + between twins of set classes that share the same :attr:`interval_vector`, but are not + related by :attr:`inversion`, :attr:`complement`, or transposition, as Z-related ('Z' for + *zygote* from Greek: ζυγωτός, 'joined' or 'paired'). This property returns all distinct set + classes with the same interval vector. + + For example, Forte 4-Z15 {0,1,4,6} and Forte 4-Z29 {0,1,3,7} both have iv⟨1,1,1,1,1,1⟩ but + are not inversions, complements, or transpositions of each other. + """ + return [i for i in SetClass.darkest_of_cardinality(self.cardinality) if i.interval_vector == self.interval_vector] + def ordered_interval(self, a: int, b: int) -> int: """ Return the ordered interval of two pitch classes. @@ -195,8 +195,8 @@ class SetClass(list): @cached_property def versions(self) -> list(SetClass): """ - All possible zero-normalised transpositions (rotations) of this set class, sorted by - :attr:`brightness`. See Rahn (1980), Tₙ set types. + All possible zero-normalised transpositions of this set class, sorted by :attr:`brightness`. + See Rahn (1980), Tₙ set types. """ # The empty set class has one version, itself if not self.pitch_classes: @@ -216,10 +216,10 @@ class SetClass(list): The Rahn normal form of the set class. John Rahn's normal form described in his book *Basic Atonal Theory* (1980) is an algorithm - to produce a unique form for each set class (see :attr:`rahn_normal_form`). Often wrongly - described as "most packed to the left", Leonard describes it as "most dispersed from the - right". Find the smallest outside interval, and if necessary proceed inwards from the right - finding the smallest next interval until one result remains. See Rahn (1980), p. 33. + to produce a unique form for each set class. Often wrongly described as "most packed to the + left", Leonard describes it as "most dispersed from the right". Find the smallest outside + interval, and if necessary proceed inwards from the right finding the smallest next interval + until one result remains. See Rahn (1980), p. 33. """ def _most_dispersed(versions, n): return [i for i in versions if i.pitch_classes[-n] == min([i.pitch_classes[-n] for i in versions])] @@ -231,6 +231,15 @@ class SetClass(list): n += 1 return versions[0] + @cached_property + def rahn_prime_form(self) -> SetClass: + """ + The Rahn prime is the most dispersed from the right of the Rahn normal forms of a set class + and its inversion. + """ + prime = min(self.rahn_normal_form.decimal, self.inversion.rahn_normal_form.decimal) + return self.inversion.rahn_normal_form if self.inversion.rahn_normal_form.decimal == prime else self.inversion.rahn_normal_form + @cached_property def packed_left(self) -> SetClass: """ @@ -314,9 +323,15 @@ class SetClass(list): @cached_property def is_symmetrical(self) -> bool: """ - Whether this set class is symmetrical upon inversion, for example Forte 5-Z17: + Whether this set class is symmetrical upon inversion, for example Forte 5-Z37: - {0,1,2,5,9} → {0,4,8,9,11} + >>> sc = SetClass(0, 1, 2, 5, 9) + >>> sc.rahn_normal_form + SetClass{0,3,4,5,8} + >>> sc.inversion.rahn_normal_form + SetClass{0,3,4,5,8} + >>> sc.is_symmetrical + True """ return self.darkest_form == self.inversion.darkest_form @@ -400,7 +415,7 @@ class SetClass(list): string (str): Any string representation of a set class, containing some integer content. tonality (int): - The :attr:`tonality`, or modulus, is the number of divisions of the octave. + The :attr:`tonality`, or modulus, is the number of uniform divisions of the octave. If unspecified, the default value of 12 is assumed (Western harmony). Returns: @@ -421,7 +436,7 @@ class SetClass(list): cardinality (int): The :attr:`cardinality` of the set classes to return. tonality (int): - The :attr:`tonality`, or modulus, is the number of divisions of the octave. + The :attr:`tonality`, or modulus, is the number of uniform divisions of the octave. If unspecified, the default value of 12 is assumed (Western harmony). Returns: @@ -445,8 +460,8 @@ class SetClass(list): their darkest forms. This produces a smaller set than :attr:`all_of_cardinality`, since it eliminates set classes - that are "brighter" transpositions (rotations) of the darkest :class:`SetClass` returned by - the :attr:`darkest_form` property. + that are "brighter" transpositions of the darkest :class:`SetClass` returned by the + :attr:`darkest_form` property. .. warning:: High values of tonality can take a long time to calculate (T=24 takes about a minute on an Intel i7-13700H CPU). @@ -455,7 +470,7 @@ class SetClass(list): cardinality (int): The :attr:`cardinality` of the set classes to return. tonality (int): - The :attr:`tonality`, or modulus, is the number of divisions of the octave. + The :attr:`tonality`, or modulus, is the number of uniform divisions of the octave. If unspecified, the default value of 12 is assumed (Western harmony). Returns: @@ -470,7 +485,7 @@ class SetClass(list): their Rahn normal form. This produces a smaller set than :attr:`all_of_cardinality`, since it eliminates set classes - that are transpositions (rotations) of the normalised :class:`SetClass` returned by the + that are transpositions of the normalised :class:`SetClass` returned by the :attr:`rahn_normal_form` property. .. warning:: High values of tonality can take a long time to calculate (T=24 takes about a @@ -480,7 +495,7 @@ class SetClass(list): cardinality (int): The :attr:`cardinality` of the set classes to return. tonality (int): - The :attr:`tonality`, or modulus, is the number of divisions of the octave. + The :attr:`tonality`, or modulus, is the number of uniform divisions of the octave. If unspecified, the default value of 12 is assumed (Western harmony). Returns: