import collections
import dataclasses
import operator as operator_module
import typing
from . import math as _math
[docs]@dataclasses.dataclass(slots=True)
class Pattern:
"""
Pattern.
.. container:: example
Matches three indices out of every eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
.. container:: example
Matches three indices out of every sixteen:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=16,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8
9
10
11
12
13
14
15
.. container:: example
Works with improper indices:
>>> pattern = abjad.Pattern(
... indices=[16, 17, 23],
... period=16,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8
9
10
11
12
13
14
15
.. container:: example
Sieve from opening of Xenakis's Psappha:
>>> sieve_1a = abjad.index([0, 1, 7], 8)
>>> sieve_1b = abjad.index([1, 3], 5)
>>> sieve_1 = sieve_1a & sieve_1b
>>> sieve_2a = abjad.index([0, 1, 2], 8)
>>> sieve_2b = abjad.index([0], 5)
>>> sieve_2 = sieve_2a & sieve_2b
>>> sieve_3 = abjad.index([3], 8)
>>> sieve_4 = abjad.index([4], 8)
>>> sieve_5a = abjad.index([5, 6], 8)
>>> sieve_5b = abjad.index([2, 3, 4], 5)
>>> sieve_5 = sieve_5a & sieve_5b
>>> sieve_6a = abjad.index([1], 8)
>>> sieve_6b = abjad.index([2], 5)
>>> sieve_6 = sieve_6a & sieve_6b
>>> sieve_7a = abjad.index([6], 8)
>>> sieve_7b = abjad.index([1], 5)
>>> sieve_7 = sieve_7a & sieve_7b
>>> sieve = sieve_1 | sieve_2 | sieve_3 | sieve_4 | sieve_5 | sieve_6 | sieve_7
>>> sieve.get_boolean_vector(total_length=40)
[1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0]
.. container:: example
Inverted works like this:
Matches three indices out of every eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> pattern.inverted is None
True
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
Pattern that rejects three indices from every eight; equivalently, pattern
matches ``8-3=5`` indices out of every eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... inverted=True
... )
>>> pattern.inverted
True
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2 True
3 True
4 True
5 True
6 True
7
8
9
10 True
11 True
12 True
13 True
14 True
15
Matches every index that is (one of the first three indices) OR (one of the last
three indices):
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern = pattern_1 | pattern_2
>>> pattern.inverted is None
True
>>> pattern.get_boolean_vector(total_length=16)
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
Matches every index that is NOT (one of the first three indices) OR (one of the
last three indices):
>>> import dataclasses
>>> pattern = dataclasses.replace(pattern, inverted=True)
>>> pattern.inverted
True
>>> pattern.get_boolean_vector(total_length=16)
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
.. container:: example
Payload works like this: Pattern with string payload assigned to three of every
eight indices:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... payload='Allegro non troppo',
... period=8,
... )
>>> total_length = 10
>>> for index in range(10):
... match = pattern.matches_index(index, total_length)
... if match:
... payload = pattern.payload
... else:
... payload = ''
... print(index, repr(payload))
...
0 'Allegro non troppo'
1 'Allegro non troppo'
2 ''
3 ''
4 ''
5 ''
6 ''
7 'Allegro non troppo'
8 'Allegro non troppo'
9 'Allegro non troppo'
.. container:: example
Period works like this:
Pattern with a period of eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> pattern.period
8
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
Same pattern with a period of sixteen:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=16,
... )
>>> pattern.period
16
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8
9
10
11
12
13
14
15
Gets period of pattern that indexs every fourth and fifth element:
>>> pattern_1 = abjad.Pattern([0], period=4)
>>> pattern_2 = abjad.Pattern([0], period=5)
>>> pattern = pattern_1 | pattern_2
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=4), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=5)), payload=None, period=20)
>>> pattern.period
20
Returns none when pattern contains acyclic parts:
>>> pattern_1 = abjad.Pattern([0], period=4)
>>> pattern_2 = abjad.Pattern([0])
>>> pattern = pattern_1 | pattern_2
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=4), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
>>> pattern.period is None
True
"""
indices: typing.Any = None
inverted: typing.Any = None
operator: typing.Any = None
patterns: typing.Any = None
payload: typing.Any = None
period: typing.Any = None
_name_to_operator = {
"and": operator_module.and_,
"or": operator_module.or_,
"xor": operator_module.xor,
}
[docs] def __post_init__(self):
if self.indices is not None:
assert all(isinstance(_, int) for _ in self.indices), repr(self.indices)
self.indices = tuple(self.indices)
if self.inverted is not None:
self.inverted = bool(self.inverted)
if self.operator is not None:
assert self.operator in self._name_to_operator, repr(self.operator)
if self.patterns is not None:
assert all(isinstance(_, type(self)) for _ in self.patterns)
self.patterns = tuple(self.patterns)
if self.period is None:
if self.patterns:
periods = [_.period for _ in self.patterns]
if None not in periods:
self.period = _math.least_common_multiple(*periods)
if self.period is not None:
assert _math.is_positive_integer(self.period), repr(self.period)
### SPECIAL METHODS ###
[docs] def __and__(self, pattern):
"""
Logical AND of two patterns.
.. container:: example
Flat grouping of two patterns:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_1 & pattern_2
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
.. container:: example
Flat grouping of three patterns:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_3 = abjad.index([0], 2)
>>> pattern = pattern_1 & pattern_2 & pattern_3
>>> pattern
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
.. container:: example
Nested grouping of three patterns:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_3 = abjad.index([0], 2)
>>> pattern = pattern_1 & pattern_2 | pattern_3
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
.. container:: example
In-place AND is allowed:
>>> pattern = abjad.index_first(3)
>>> pattern &= abjad.index_last(3)
>>> pattern
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
Returns new pattern.
"""
if self._can_append_to_self(pattern, "and"):
if self.patterns is None:
self_patterns = [self]
else:
self_patterns = list(self.patterns)
patterns = self_patterns + [pattern]
result = type(self)(operator="and", patterns=patterns)
else:
result = type(self)(operator="and", patterns=[self, pattern])
return result
[docs] def __invert__(self):
"""
Inverts pattern.
.. container:: example
>>> pattern = abjad.index_first(3)
>>> pattern
Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None)
>>> pattern = ~pattern
>>> pattern
Pattern(indices=(0, 1, 2), inverted=True, operator=None, patterns=None, payload=None, period=None)
>>> pattern = ~pattern
>>> pattern
Pattern(indices=(0, 1, 2), inverted=False, operator=None, patterns=None, payload=None, period=None)
Negation defined equal to inversion.
.. container:: example
Matches every index that is (one of the first three indices) or (one of the
last three indices):
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern = pattern_1 | pattern_2
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
.. container:: example
Matches every index that is NOT (one of the first three indices) or
(one of the last three indices):
>>> pattern = ~pattern
>>> pattern
Pattern(indices=None, inverted=True, operator='or', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
Returns new pattern.
"""
inverted = not self.inverted
return dataclasses.replace(self, inverted=inverted)
[docs] def __len__(self):
"""
Gets length of pattern.
.. container:: example
Gets length of cyclic pattern:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> len(pattern)
8
Length of cyclic pattern defined equal to period of the pattern.
.. container:: example
Gets length of acyclic pattern:
>>> pattern = abjad.Pattern(
... indices=[0, 2, 3],
... )
>>> len(pattern)
4
Length of acyclic pattern defined equal to greatest index in
pattern, plus 1.
.. container:: example
Gets length of pattern with negative indices:
>>> pattern = abjad.Pattern(
... indices=[-3],
... )
>>> len(pattern)
3
Length of pattern with negative indices defined equal to absolute
value of least index.
Returns nonnegative integer.
"""
if self.period is not None:
return self.period
if self.indices:
absolute_indices = []
for index in self.indices:
if 0 <= index:
absolute_indices.append(index)
else:
index = abs(index) - 1
absolute_indices.append(index)
maximum_index = max(absolute_indices)
return maximum_index + 1
return 0
[docs] def __or__(self, pattern):
"""
Logical OR of two patterns.
.. container:: example
Flat grouping of two patterns:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern = pattern_1 | pattern_2
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
.. container:: example
Flat grouping:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_3 = abjad.index([0], 2)
>>> pattern = pattern_1 | pattern_2 | pattern_3
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1]
.. container:: example
Nested grouping:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_3 = abjad.index([0], 2)
>>> pattern = pattern_1 | pattern_2 & pattern_3
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2)), payload=None, period=None)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
.. container:: example
In-place OR is allowed:
>>> pattern = abjad.index_first(3)
>>> pattern |= abjad.index_last(3)
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
Returns new pattern.
"""
if self._can_append_to_self(pattern, "or"):
if self.patterns is None:
self_patterns = [self]
else:
self_patterns = list(self.patterns)
patterns = self_patterns + [pattern]
result = type(self)(operator="or", patterns=patterns)
else:
result = type(self)(operator="or", patterns=[self, pattern])
return result
[docs] def __xor__(self, pattern):
"""
Logical XOR of two patterns.
.. container:: example
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern = pattern_1 ^ pattern_2
>>> pattern
Pattern(indices=None, inverted=None, operator='xor', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
.. container:: example
Flat grouping:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_3 = abjad.index([0], 2)
>>> pattern = pattern_1 ^ pattern_2 ^ pattern_3
>>> pattern
Pattern(indices=None, inverted=None, operator='xor', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1]
.. container:: example
Nested grouping:
>>> pattern_1 = abjad.index_first(3)
>>> pattern_2 = abjad.index_last(3)
>>> pattern_3 = abjad.index([0], 2)
>>> pattern = pattern_1 ^ pattern_2 & pattern_3
>>> pattern
Pattern(indices=None, inverted=None, operator='xor', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2)), payload=None, period=None)), payload=None, period=None)
>>> pattern.get_boolean_vector(total_length=16)
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
.. container:: example
In-place XOR is allowed:
>>> pattern = abjad.index_first(3)
>>> pattern ^= abjad.index_last(3)
>>> pattern
Pattern(indices=None, inverted=None, operator='xor', patterns=(Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
Returns new pattern.
"""
if self._can_append_to_self(pattern, "xor"):
if self.patterns is None:
self_patterns = [self]
else:
self_patterns = list(self.patterns)
patterns = self_patterns + [pattern]
result = type(self)(operator="xor", patterns=patterns)
else:
result = type(self)(operator="xor", patterns=[self, pattern])
return result
### PRIVATE METHODS ###
def _can_append_to_self(self, pattern, operator_):
if not isinstance(pattern, type(self)):
return False
if self.operator is None:
return True
if self.operator == operator_ and (
pattern.operator is None or (pattern.operator == self.operator)
):
return True
return False
def _make_subscript_string(self):
return str(self)
@property
def weight(self):
"""
Gets weight of pattern.
.. container:: example
Gets weight of cyclic pattern:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> pattern.weight
3
.. container:: example
Gets weight of acyclic pattern:
>>> pattern = abjad.Pattern(
... indices=[0, 2, 3],
... )
>>> pattern.weight
3
Weight defined equal to number of indices in pattern.
Returns nonnegative integer.
"""
return len(self.indices)
### PUBLIC METHODS ###
[docs] def advance(self, count: int | None = None) -> "Pattern":
"""
Advances pattern.
.. container:: example
>>> pattern = abjad.Pattern([0, 2, 12])
>>> pattern = pattern.advance(8)
>>> pattern
Pattern(indices=(4,), inverted=None, operator=None, patterns=None, payload=None, period=None)
>>> pattern = pattern.advance(8)
>>> pattern
Pattern(indices=(), inverted=None, operator=None, patterns=None, payload=None, period=None)
.. container:: example
Returns copy of pattern when count is none:
>>> pattern = abjad.Pattern([0, 2, 12])
>>> pattern.advance()
Pattern(indices=(0, 2, 12), inverted=None, operator=None, patterns=None, payload=None, period=None)
.. container:: example exception
Raises exception on attempt to advance negative pattern:
>>> pattern = abjad.Pattern([-2, -1])
>>> pattern.advance(8)
Traceback (most recent call last):
...
Exception: can not advance pattern with negative indices ...
"""
if not count:
return dataclasses.replace(self)
assert 0 < count, repr(count)
for index in self.indices:
if index < 0:
message = "can not advance pattern"
message += f" with negative indices ({repr(self)})."
raise Exception(message)
new_indices = []
for index in self.indices:
new_index = index - count
if 0 <= new_index:
new_indices.append(new_index)
return dataclasses.replace(self, indices=new_indices)
[docs] @classmethod
def from_vector(class_, vector):
"""
Makes pattern from boolean ``vector``.
.. container:: example
Matches three indices out of every five:
>>> pattern = [1, 0, 0, 1, 1]
>>> pattern = abjad.Pattern.from_vector(pattern)
>>> pattern
Pattern(indices=(0, 3, 4), inverted=None, operator=None, patterns=None, payload=None, period=5)
>>> total_length = 10
>>> for index in range(10):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2
3 True
4 True
5 True
6
7
8 True
9 True
.. container:: example
Matches three indices out of every six:
>>> pattern = [1, 0, 0, 1, 1, 0]
>>> pattern = abjad.Pattern.from_vector(pattern)
>>> pattern
Pattern(indices=(0, 3, 4), inverted=None, operator=None, patterns=None, payload=None, period=6)
>>> total_length = 12
>>> for index in range(12):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2
3 True
4 True
5
6 True
7
8
9 True
10 True
11
Returns pattern.
"""
vector = [bool(_) for _ in vector]
period = len(vector)
indices = [i for i, x in enumerate(vector) if x]
return class_(period=period, indices=indices)
[docs] def get_boolean_vector(self, total_length=None):
"""
Gets boolean vector of pattern applied to input sequence with
``total_length``.
.. container:: example
Gets boolean vector of acyclic pattern:
>>> pattern = abjad.Pattern(
... indices=[4, 5, 6, 7],
... )
>>> pattern.get_boolean_vector(4)
[0, 0, 0, 0]
>>> pattern.get_boolean_vector(8)
[0, 0, 0, 0, 1, 1, 1, 1]
>>> pattern.get_boolean_vector(16)
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
Sets total length to length of pattern when ``total_length`` is
none:
>>> pattern.get_boolean_vector()
[0, 0, 0, 0, 1, 1, 1, 1]
.. container:: example
Gets vector of cyclic pattern:
>>> pattern = abjad.Pattern(
... indices=[4, 5, 6, 7],
... period=20,
... )
>>> pattern.get_boolean_vector(4)
[0, 0, 0, 0]
>>> pattern.get_boolean_vector(8)
[0, 0, 0, 0, 1, 1, 1, 1]
>>> pattern.get_boolean_vector(16)
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
Sets total length to length of pattern when ``total_length`` is
none:
>>> pattern.get_boolean_vector()
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
.. container:: example
Gets vector of inverted pattern:
>>> pattern = abjad.Pattern(
... indices=[4, 5, 6, 7],
... period=20,
... )
>>> pattern.get_boolean_vector(4)
[0, 0, 0, 0]
>>> pattern.get_boolean_vector(8)
[0, 0, 0, 0, 1, 1, 1, 1]
>>> pattern.get_boolean_vector(16)
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
Sets total length to length of pattern when ``total_length`` is
none:
>>> pattern.get_boolean_vector()
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
.. container:: example
Two-part pattern with logical OR:
>>> pattern = abjad.Pattern(
... operator='or',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... ),
... ],
... )
>>> pattern.get_boolean_vector(4)
[1, 1, 1, 1]
>>> pattern.get_boolean_vector(8)
[1, 1, 1, 0, 0, 1, 1, 1]
>>> pattern.get_boolean_vector(16)
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
Matches every index that is (one of the first three indices) OR
(one of the last three indices).
.. container:: example
Two-part pattern with mixed periodic and inverted parts:
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... inverted=True,
... ),
... ],
... )
>>> pattern.get_boolean_vector(4)
[1, 0, 0, 0]
>>> pattern.get_boolean_vector(8)
[1, 0, 1, 0, 1, 0, 0, 0]
>>> pattern.get_boolean_vector(16)
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0]
Matches every index that is (equal to 0 % 2) AND (not one of the
last three indices).
.. container:: example
Cyclic pattern that indexes every fourth and fifth item:
>>> pattern_1 = abjad.Pattern([0], period=4)
>>> pattern_2 = abjad.Pattern([0], period=5)
>>> pattern = pattern_1 | pattern_2
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=4), Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=5)), payload=None, period=20)
>>> pattern.get_boolean_vector(4)
[1, 0, 0, 0]
>>> pattern.get_boolean_vector(8)
[1, 0, 0, 0, 1, 1, 0, 0]
>>> pattern.get_boolean_vector(16)
[1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1]
Sets total length to period of pattern when ``total_length`` is
none:
>>> pattern.period
20
>>> pattern.get_boolean_vector()
[1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0]
>>> pattern.period == len(pattern.get_boolean_vector())
True
Returns list of ones and zeroes.
"""
total_length = total_length or len(self)
boolean_vector = []
for index in range(total_length):
result = self.matches_index(index, total_length)
boolean_vector.append(int(result))
return boolean_vector
[docs] def get_matching_items(self, sequence):
"""
Gets maching items from sequence.
.. container:: example
>>> pattern = abjad.Pattern(
... indices=[4, 5, 6, 7],
... )
>>> pattern.get_matching_items('abcdefghijklmnopqrstuvwxyz')
['e', 'f', 'g', 'h']
.. container:: example
>>> pattern = abjad.Pattern(
... indices=[8, 9],
... period=10,
... )
>>> pattern.get_matching_items('abcdefghijklmnopqrstuvwxyz')
['i', 'j', 's', 't']
.. container:: example
>>> pattern = abjad.index_first(1) | abjad.index_last(2)
>>> pattern.get_matching_items('abcdefghijklmnopqrstuvwxyz')
['a', 'y', 'z']
Returns list.
"""
assert isinstance(sequence, collections.abc.Iterable), repr(sequence)
length = len(sequence)
items = []
for i in range(length):
if self.matches_index(i, length):
item = sequence[i]
items.append(item)
return items
[docs] @staticmethod
def index(indices, period=None, inverted=None):
"""
Makes pattern that matches ``indices``.
.. container:: example
Indexes item 2:
>>> pattern = abjad.index([2])
>>> pattern
Pattern(indices=(2,), inverted=None, operator=None, patterns=None, payload=None, period=None)
.. container:: example
Indexes items 2, 3 and 5:
>>> pattern = abjad.index([2, 3, 5])
>>> pattern
Pattern(indices=(2, 3, 5), inverted=None, operator=None, patterns=None, payload=None, period=None)
Returns pattern.
"""
assert all(isinstance(_, int) for _ in indices), repr(indices)
indices = indices or []
return Pattern(
indices=indices,
inverted=inverted,
period=period,
)
[docs] @staticmethod
def index_all(inverted=None):
"""
Makes pattern that matches all indices.
.. container:: example
Indexes all divisions for tie creation:
>>> pattern = abjad.index_all()
>>> pattern
Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=1)
Returns pattern.
"""
return Pattern(indices=[0], inverted=inverted, period=1)
[docs] @staticmethod
def index_first(n, inverted=None):
"""
Makes pattern that matches the first ``n`` indices.
.. container:: example
Indexes first item:
>>> pattern = abjad.index_first(1)
>>> pattern
Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=None)
.. container:: example
Indexes first two items:
>>> pattern = abjad.index_first(2)
>>> pattern
Pattern(indices=(0, 1), inverted=None, operator=None, patterns=None, payload=None, period=None)
.. container:: example
Indexes nothing:
>>> pattern = abjad.index_first(0)
>>> pattern
Pattern(indices=None, inverted=None, operator=None, patterns=None, payload=None, period=None)
Returns pattern.
"""
assert isinstance(n, int), repr(n)
if 0 < n:
indices = list(range(n))
else:
indices = None
return Pattern(indices=indices, inverted=inverted)
[docs] @staticmethod
def index_last(n, inverted=None):
"""
Makes pattern that matches the last ``n`` indices.
.. container:: example
Indexes last two items:
>>> pattern = abjad.index_last(2)
>>> pattern
Pattern(indices=(-2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
.. container:: example
Indexes nothing:
>>> pattern = abjad.index_last(0)
>>> pattern
Pattern(indices=None, inverted=None, operator=None, patterns=None, payload=None, period=None)
Returns pattern.
"""
assert isinstance(n, int), repr(n)
if 0 < n:
start = -1
stop = -n - 1
stride = -1
indices = list(reversed(range(start, stop, stride)))
else:
indices = None
return Pattern(indices=indices, inverted=inverted)
[docs] def matches_index(self, index, total_length, rotation=None):
"""
Is true when pattern matches ``index`` taken under ``total_length``.
.. container:: example
Matches three indices out of every eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
Matches three indices out of every eight, offset ``1`` to the left:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(
... index,
... total_length,
... rotation=1,
... )
... match = match or ''
... print(index, match)
0 True
1
2
3
4
5
6 True
7 True
8 True
9
10
11
12
13
14 True
15 True
Matches three indices out of every eight, offset ``2`` to the
left:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(
... index,
... total_length,
... rotation=2,
... )
... match = match or ''
... print(index, match)
0
1
2
3
4
5 True
6 True
7 True
8
9
10
11
12
13 True
14 True
15 True
.. container:: example
Matches three indices out of every sixteen:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=16,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8
9
10
11
12
13
14
15
Matches three indices out of every sixteen, offset ``1`` to the
left:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=16,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(
... index,
... total_length,
... rotation=1,
... )
... match = match or ''
... print(index, match)
0 True
1
2
3
4
5
6 True
7
8
9
10
11
12
13
14
15 True
Matches three indices out of every sixteen, offset ``2`` to the
left:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=16,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(
... index,
... total_length,
... rotation=2,
... )
... match = match or ''
... print(index, match)
0
1
2
3
4
5 True
6
7
8
9
10
11
12
13
14 True
15 True
.. container:: example
Empty pattern:
>>> pattern = abjad.Pattern()
Total length 16:
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Total length 8:
>>> total_length = 8
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2
3
4
5
6
7
Total length 4:
>>> total_length = 4
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2
3
Matches nothing.
.. container:: example
Simple pattern:
Logical OR:
>>> pattern = abjad.Pattern(
... operator='or',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... ],
... )
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5
6
7
8
9
10
11
12
13
14
15
Logical AND:
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... ],
... )
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5
6
7
8
9
10
11
12
13
14
15
Logical XOR:
>>> pattern = abjad.Pattern(
... operator='xor',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... ],
... )
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5
6
7
8
9
10
11
12
13
14
15
Matches every index that is (one of the first three indices).
Ignores ``operator``.
.. container:: example
Two-part pattern with logical OR:
>>> pattern = abjad.Pattern(
... operator='or',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... ),
... ],
... )
Total length 16:
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5
6
7
8
9
10
11
12
13 True
14 True
15 True
Total length 8:
>>> total_length = 8
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5 True
6 True
7 True
Total length 4:
>>> total_length = 4
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3 True
Matches every index that is (one of the first three indices) OR
(one of the last three indices).
.. container:: example
Two-part pattern with logical AND:
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... ),
... ],
... )
Total length 16:
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Total length 8:
>>> total_length = 8
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2
3
4
5
6
7
Total length 4:
>>> total_length = 4
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1 True
2 True
3
Matches every index that is (one of the first three indices) AND
(one of the last three indices).
.. container:: example
Two-part pattern with logical XOR:
>>> pattern = abjad.Pattern(
... operator='xor',
... patterns=[
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... ),
... ],
... )
Total length 16:
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5
6
7
8
9
10
11
12
13 True
14 True
15 True
Total length 8:
>>> total_length = 8
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4
5 True
6 True
7 True
Total length 4:
>>> total_length = 4
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2
3 True
Matches every index that is (one of the first three indices) XOR
(one of the last three indices).
.. container:: example
Two-part pattern with mixed periodic and inverted parts:
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... inverted=True,
... ),
... ],
... )
Total length 16:
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2 True
3
4 True
5
6 True
7
8 True
9
10 True
11
12 True
13
14
15
Total length 8:
>>> total_length = 8
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2 True
3
4 True
5
6
7
Total length 4:
>>> total_length = 4
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2
3
Matches every index that is (equal to 0 % 2) AND (not one of the
last three indices).
.. container:: example
Complex pattern with compound and simple parts:
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... inverted=True,
... ),
... ],
... )
>>> pattern = abjad.Pattern(
... operator='or',
... patterns=[
... pattern,
... abjad.Pattern(
... indices=[0, 1, 2],
... ),
... ],
... )
>>> pattern
Pattern(indices=None, inverted=None, operator='or', patterns=(Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2), Pattern(indices=(-3, -2, -1), inverted=True, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None), Pattern(indices=(0, 1, 2), inverted=None, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
Total length 16:
>>> total_length = 16
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4 True
5
6 True
7
8 True
9
10 True
11
12 True
13
14
15
Total length 8:
>>> total_length = 8
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
4 True
5
6
7
Total length 4:
>>> total_length = 4
>>> for index in range(total_length):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2 True
3
Matches every index that is ((equal to 0 % 2) AND (not one of the
last three indices)) OR is (one of the first three indices).
Returns true or false.
"""
if not self.patterns:
assert 0 <= total_length
if 0 <= index:
nonnegative_index = index
else:
nonnegative_index = total_length - abs(index)
inverted = bool(self.inverted)
if not self.indices:
return False ^ inverted
if self.period is None:
for index in self.indices:
if index < 0:
index = total_length - abs(index)
if index == nonnegative_index and index < total_length:
return True ^ inverted
else:
if rotation is not None:
nonnegative_index += rotation
nonnegative_index = nonnegative_index % self.period
for index in self.indices:
if index < 0:
index = total_length - abs(index)
index = index % self.period
if index == nonnegative_index and index < total_length:
return True ^ inverted
if (index % self.period) == nonnegative_index and (
index % self.period < total_length
):
return True ^ inverted
return False ^ inverted
elif len(self.patterns) == 1:
pattern = self.patterns[0]
result = pattern.matches_index(index, total_length, rotation=rotation)
else:
operator_ = self._name_to_operator[self.operator]
pattern = self.patterns[0]
result = pattern.matches_index(index, total_length, rotation=rotation)
for pattern in self.patterns[1:]:
result_ = pattern.matches_index(index, total_length, rotation=rotation)
result = operator_(result, result_)
if self.inverted:
result = not (result)
return result
[docs] def reverse(self):
"""
Reverses pattern.
.. container:: example
Matches three indices out of every eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
.. container:: example
Reverses pattern:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> pattern = pattern.reverse()
>>> pattern
Pattern(indices=(-1, -2, -8), inverted=None, operator=None, patterns=None, payload=None, period=8)
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1
2
3
4
5
6 True
7 True
8 True
9
10
11
12
13
14 True
15 True
.. container:: example
Matches every index that is (equal to 0 % 2) AND (not one of the
last three indices):
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... inverted=True,
... ),
... ],
... )
>>> pattern
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2), Pattern(indices=(-3, -2, -1), inverted=True, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
Reverses pattern:
>>> pattern = pattern.reverse()
>>> pattern
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(-1,), inverted=None, operator=None, patterns=None, payload=None, period=2), Pattern(indices=(2, 1, 0), inverted=True, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
New pattern matches every index that is (equal to -1 % 2) AND (not one of the
first three indices).
Returns new pattern.
"""
if not self.patterns:
indices = [-index - 1 for index in self.indices]
return dataclasses.replace(self, indices=indices)
patterns = [_.reverse() for _ in self.patterns]
return dataclasses.replace(self, patterns=patterns)
[docs] def rotate(self, n=0):
"""
Rotates pattern by index ``n``.
.. container:: example
Matches three indices out of every eight:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
Rotates pattern two elements to the right:
>>> pattern = abjad.Pattern(
... indices=[0, 1, 7],
... period=8,
... )
>>> pattern = pattern.rotate(n=2)
>>> pattern
Pattern(indices=(2, 3, 9), inverted=None, operator=None, patterns=None, payload=None, period=8)
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1 True
2 True
3 True
4
5
6
7
8
9 True
10 True
11 True
12
13
14
15
.. container:: example
Matches three indices out of every eight with negative indices:
>>> pattern = abjad.Pattern(
... indices=[-3, -2, -1],
... period=8,
... )
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0
1
2
3
4
5 True
6 True
7 True
8
9
10
11
12
13 True
14 True
15 True
Rotates pattern two elements to the right:
>>> pattern = abjad.Pattern(
... indices=[-3, -2, -1],
... period=8,
... )
>>> pattern = pattern.rotate(n=2)
>>> pattern
Pattern(indices=(-1, 0, 1), inverted=None, operator=None, patterns=None, payload=None, period=8)
>>> total_length = 16
>>> for index in range(16):
... match = pattern.matches_index(index, total_length)
... match = match or ''
... print(index, match)
0 True
1 True
2
3
4
5
6
7 True
8 True
9 True
10
11
12
13
14
15 True
.. container:: example
Matches every index that is (equal to 0 % 2) AND (not one of the
last three indices):
>>> pattern = abjad.Pattern(
... operator='and',
... patterns=[
... abjad.Pattern(
... indices=[0],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... inverted=True,
... ),
... ],
... )
>>> pattern
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(0,), inverted=None, operator=None, patterns=None, payload=None, period=2), Pattern(indices=(-3, -2, -1), inverted=True, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
Rotates pattern two elements to the right:
>>> pattern = pattern.rotate(n=2)
>>> pattern
Pattern(indices=None, inverted=None, operator='and', patterns=(Pattern(indices=(2,), inverted=None, operator=None, patterns=None, payload=None, period=2), Pattern(indices=(-1, 0, 1), inverted=True, operator=None, patterns=None, payload=None, period=None)), payload=None, period=None)
New pattern matches every index that is (equal to 2 % 2) AND (not the first,
second or last index in the pattern).
Returns new pattern.
"""
if not self.patterns:
indices = [index + n for index in self.indices]
return dataclasses.replace(self, indices=indices)
patterns = [_.rotate(n=n) for _ in self.patterns]
return dataclasses.replace(self, patterns=patterns)
[docs]@dataclasses.dataclass(slots=True, unsafe_hash=True)
class PatternTuple:
"""
Pattern tuple.
.. container:: example
Three patterns:
>>> patterns = abjad.PatternTuple([
... abjad.Pattern(
... indices=[0, 1, 7],
... period=10,
... ),
... abjad.Pattern(
... indices=[-2, -1],
... ),
... abjad.Pattern(
... indices=[2],
... period=3,
... ),
... ])
>>> patterns
PatternTuple(items=(Pattern(indices=(0, 1, 7), inverted=None, operator=None, patterns=None, payload=None, period=10), Pattern(indices=(-2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None), Pattern(indices=(2,), inverted=None, operator=None, patterns=None, payload=None, period=3)))
.. container:: example
Two patterns:
>>> patterns = abjad.PatternTuple([
... abjad.Pattern(
... indices=[1],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... ),
... ])
>>> patterns
PatternTuple(items=(Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2), Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)))
"""
items: typing.Sequence = ()
[docs] def __post_init__(self):
self.items = tuple(self.items or [])
[docs] def get_matching_pattern(self, index, total_length, rotation=None):
"""
Gets pattern matching ``index``.
.. container:: example
Two patterns:
>>> patterns = abjad.PatternTuple([
... abjad.Pattern(
... indices=[1],
... period=2,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... ),
... ])
Gets patterns that match the first ten indices:
>>> for i in range(10):
... match = patterns.get_matching_pattern(i, 10)
... print(i, match)
...
0 None
1 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
2 None
3 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
4 None
5 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
6 None
7 Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
8 Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
9 Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
Last three indices match the second pattern.
Gets patterns that match next ten indices:
>>> for i in range(10, 20):
... match = patterns.get_matching_pattern(i, 10)
... print(i, match)
...
10 None
11 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
12 None
13 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
14 None
15 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
16 None
17 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
18 None
19 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
Last three indices no longer match the second pattern.
.. container:: example
Gets patterns that match the first ten indices, with rotation set
to ``1``:
>>> for i in range(10):
... match = patterns.get_matching_pattern(i, 10, rotation=1)
... print(i, match)
...
0 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
1 None
2 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
3 None
4 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
5 None
6 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
7 Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
8 Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
9 Pattern(indices=(-3, -2, -1), inverted=None, operator=None, patterns=None, payload=None, period=None)
Matching indices of first pattern offset by ``1``.
Gets patterns that match next ten indices with rotation set to
``1``:
>>> for i in range(10, 20):
... match = patterns.get_matching_pattern(i, 10, rotation=1)
... print(i, match)
...
10 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
11 None
12 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
13 None
14 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
15 None
16 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
17 None
18 Pattern(indices=(1,), inverted=None, operator=None, patterns=None, payload=None, period=2)
19 None
Matching indices of first pattern offset by ``1``.
.. container:: example
With inverted patterns:
>>> patterns = abjad.PatternTuple([
... abjad.Pattern(
... indices=[-3],
... inverted=True,
... ),
... ])
>>> for i in range(10):
... match = patterns.get_matching_pattern(i, 10)
... print(i, match)
...
0 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
1 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
2 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
3 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
4 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
5 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
6 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
7 None
8 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
9 Pattern(indices=(-3,), inverted=True, operator=None, patterns=None, payload=None, period=None)
Returns pattern or none.
"""
for pattern in reversed(self.items):
if hasattr(pattern, "pattern"):
if pattern.pattern.matches_index(
index, total_length, rotation=rotation
):
return pattern
elif pattern.matches_index(index, total_length, rotation=rotation):
return pattern
[docs] def get_matching_payload(self, index, total_length, rotation=None):
"""
Gets payload attached to pattern matching ``index``.
.. container:: example
Two patterns. Underlying notes with even divisions
assigned to the last three indices:
>>> patterns = abjad.PatternTuple([
... abjad.Pattern(
... indices=[0],
... payload='staccato',
... period=1,
... ),
... abjad.Pattern(
... indices=[-3, -2, -1],
... payload='tenuto',
... ),
... ])
Over ten indices:
>>> for i in range(10):
... match = patterns.get_matching_payload(i, 10)
... print(i, match)
...
0 staccato
1 staccato
2 staccato
3 staccato
4 staccato
5 staccato
6 staccato
7 tenuto
8 tenuto
9 tenuto
Over fifteen indices:
>>> for i in range(15):
... match = patterns.get_matching_payload(i, 15)
... print(i, match)
...
0 staccato
1 staccato
2 staccato
3 staccato
4 staccato
5 staccato
6 staccato
7 staccato
8 staccato
9 staccato
10 staccato
11 staccato
12 tenuto
13 tenuto
14 tenuto
"""
pattern = self.get_matching_pattern(index, total_length, rotation=rotation)
payload = None
if pattern:
payload = pattern.payload
return payload