Add interval vector calculations, tests
This commit is contained in:
parent
77bbc71214
commit
0b968d8e48
2 changed files with 63 additions and 6 deletions
|
|
@ -48,7 +48,7 @@ class SetClass(list):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
"""Return the Python instance string representation."""
|
||||
return f"SetClass{self.pitch_classes}".replace(' ', '')
|
||||
return f"SetClass{set(self.pitch_classes) or '{}'}".replace(' ', '')
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return sum([hash(i) for i in self.pitch_classes])
|
||||
|
|
@ -89,6 +89,38 @@ class SetClass(list):
|
|||
intervals.append(self.tonality - prev)
|
||||
return intervals
|
||||
|
||||
@cache_property
|
||||
def interval_vector(self):
|
||||
"""
|
||||
An ordered tuple containing the multiplicities of each interval class in the set class.
|
||||
Denoted in angle-brackets, e.g.
|
||||
The interval vector of {0,2,4,5,7,9,11} is ⟨2,5,4,3,6,1⟩
|
||||
— Rahn (1987), p. 100
|
||||
"""
|
||||
from itertools import combinations
|
||||
iv = [0 for i in range(1, int(self.tonality / 2) + 1)]
|
||||
for (a, b) in combinations(self.pitch_classes, 2):
|
||||
ic = SetClass.unordered_interval(a, b)
|
||||
iv[ic - 1] += 1
|
||||
return iv
|
||||
|
||||
def ordered_interval(a: int, b: int) -> int:
|
||||
"""
|
||||
The ordered interval or "directed interval" (Babbitt) of two pitch classes is determined by
|
||||
the difference of the pitch class values, modulo 12:
|
||||
i⟨a,b⟩ = b-a mod 12 — Rahn (1987), p. 25
|
||||
"""
|
||||
return (b % 12 - a % 12) % 12
|
||||
|
||||
def unordered_interval(a: int, b: int) -> int:
|
||||
"""
|
||||
The unordered interval (also "interval distance", "interval class", "ic", or "undirected
|
||||
interval") of two pitch classes is the smaller of the two possible ordered intervals
|
||||
(differences in pitch class value):
|
||||
i(a,b) = min: i⟨a,b⟩, i⟨b,a⟩ — Rahn (1987), p. 28
|
||||
"""
|
||||
return min(SetClass.ordered_interval(a, b), SetClass.ordered_interval(b, a))
|
||||
|
||||
@cache_property
|
||||
def versions(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -38,6 +38,31 @@ def test_repeat_intervals():
|
|||
assert a == SetClass(12, 1, -11, 15, -9)
|
||||
|
||||
|
||||
def test_interval_vector():
|
||||
test = [
|
||||
# pairs of set class, expected interval vector
|
||||
(SetClass(0, 3, 4, 7, 8, 11), [3, 0, 3, 6, 3, 0]),
|
||||
(SetClass(0, 1, 3, 5, 6, 8, 10), [2, 5, 4, 3, 6, 1]),
|
||||
]
|
||||
for (sc, iv) in test:
|
||||
assert sc.interval_vector == iv
|
||||
|
||||
|
||||
def test_interval_vector_invarance():
|
||||
test = [
|
||||
SetClass(0, 1, 3, 5, 6, 8, 10),
|
||||
SetClass(0, 1, 3, 5, 7, 8, 10),
|
||||
SetClass(0, 2, 3, 5, 7, 8, 10),
|
||||
SetClass(0, 2, 3, 5, 7, 9, 10),
|
||||
SetClass(0, 2, 4, 5, 7, 9, 10),
|
||||
SetClass(0, 2, 4, 5, 7, 9, 11),
|
||||
SetClass(0, 2, 4, 6, 7, 9, 11),
|
||||
]
|
||||
iv = [2, 5, 4, 3, 6, 1]
|
||||
for sc in test:
|
||||
assert sc.interval_vector == iv
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Example Forte 5-20 set class
|
||||
f520 = SetClass(0, 1, 5, 6, 8)
|
||||
|
|
@ -180,11 +205,11 @@ def test_empty_bright_dark_forms_equal():
|
|||
|
||||
|
||||
def test_empty_duodecimal_notation():
|
||||
assert empty.duodecimal_notation == 'SetClass[]'
|
||||
assert empty.duodecimal_notation == 'SetClass{}'
|
||||
|
||||
|
||||
def test_empty_dozenal_notation():
|
||||
assert empty.dozenal_notation == 'SetClass[]'
|
||||
assert empty.dozenal_notation == 'SetClass{}'
|
||||
|
||||
|
||||
def test_empty_leonard_notation():
|
||||
|
|
@ -192,7 +217,7 @@ def test_empty_leonard_notation():
|
|||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Forte 12-1, the complement of empty (full set class)
|
||||
# Forte 12-1, the complement of empty is the "aggregate" set class
|
||||
f121 = empty.complement
|
||||
|
||||
|
||||
|
|
@ -239,11 +264,11 @@ def test_empty_complement_bright_dark_forms_equal():
|
|||
|
||||
|
||||
def test_empty_complement_duodecimal_notation():
|
||||
assert f121.duodecimal_notation == 'SetClass[0,1,2,3,4,5,6,7,8,9,T,E]'
|
||||
assert f121.duodecimal_notation == 'SetClass{0,1,2,3,4,5,6,7,8,9,T,E}'
|
||||
|
||||
|
||||
def test_empty_complement_dozenal_notation():
|
||||
assert f121.dozenal_notation == 'SetClass[0,1,2,3,4,5,6,7,8,9,↊,↋]'
|
||||
assert f121.dozenal_notation == 'SetClass{0,1,2,3,4,5,6,7,8,9,↊,↋}'
|
||||
|
||||
|
||||
def test_empty_complement_leonard_notation():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue