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:
|
def __repr__(self) -> str:
|
||||||
"""Return the Python instance string representation."""
|
"""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:
|
def __hash__(self) -> int:
|
||||||
return sum([hash(i) for i in self.pitch_classes])
|
return sum([hash(i) for i in self.pitch_classes])
|
||||||
|
|
@ -89,6 +89,38 @@ class SetClass(list):
|
||||||
intervals.append(self.tonality - prev)
|
intervals.append(self.tonality - prev)
|
||||||
return intervals
|
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
|
@cache_property
|
||||||
def versions(self):
|
def versions(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,31 @@ def test_repeat_intervals():
|
||||||
assert a == SetClass(12, 1, -11, 15, -9)
|
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
|
# Example Forte 5-20 set class
|
||||||
f520 = SetClass(0, 1, 5, 6, 8)
|
f520 = SetClass(0, 1, 5, 6, 8)
|
||||||
|
|
@ -180,11 +205,11 @@ def test_empty_bright_dark_forms_equal():
|
||||||
|
|
||||||
|
|
||||||
def test_empty_duodecimal_notation():
|
def test_empty_duodecimal_notation():
|
||||||
assert empty.duodecimal_notation == 'SetClass[]'
|
assert empty.duodecimal_notation == 'SetClass{}'
|
||||||
|
|
||||||
|
|
||||||
def test_empty_dozenal_notation():
|
def test_empty_dozenal_notation():
|
||||||
assert empty.dozenal_notation == 'SetClass[]'
|
assert empty.dozenal_notation == 'SetClass{}'
|
||||||
|
|
||||||
|
|
||||||
def test_empty_leonard_notation():
|
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
|
f121 = empty.complement
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -239,11 +264,11 @@ def test_empty_complement_bright_dark_forms_equal():
|
||||||
|
|
||||||
|
|
||||||
def test_empty_complement_duodecimal_notation():
|
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():
|
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():
|
def test_empty_complement_leonard_notation():
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue