Spaces:
Sleeping
Sleeping
| """Regular constraints shared by generation engines.""" | |
| from __future__ import annotations | |
| from collections.abc import Iterable | |
| from .common import Symbol, duration_units, is_triplet_duration | |
| class ThemeGenerationAcceptor: | |
| """Dense weighted DFA for endpoint weights and rhythmic phase constraints.""" | |
| rejected_state = -1 | |
| def __init__( | |
| self, | |
| *, | |
| length: int, | |
| alphabet: Iterable[Symbol], | |
| start_weights: dict[int, float], | |
| end_weights: dict[int, float], | |
| strength: float, | |
| enforce_triplet_groups: bool, | |
| ) -> None: | |
| self.length = length | |
| self.start_weights = start_weights | |
| self.end_weights = end_weights | |
| self.strength = strength | |
| self.enforce_triplet_groups = enforce_triplet_groups | |
| self.name = "theme_generation_dense" | |
| alphabet_tuple = tuple(alphabet) | |
| self.alphabet = frozenset(alphabet_tuple) | |
| self.start_state = self.encode_state(0, 0, 0) | |
| self.state_count_value = (length + 1) * 12 * 3 | |
| self.states = frozenset(range(self.state_count_value)) | |
| self.accept_states = frozenset(self.encode_state(length, phase, 0) for phase in range(12)) | |
| self.dense_transition_by_symbol = { | |
| symbol: self.transition_column(symbol) | |
| for symbol in alphabet_tuple | |
| } | |
| def encode_state(position: int, phase: int, triplets_to_close: int) -> int: | |
| return ((position * 12) + phase) * 3 + triplets_to_close | |
| def decode_state(state: int) -> tuple[int, int, int]: | |
| position_phase, triplets_to_close = divmod(state, 3) | |
| position, phase = divmod(position_phase, 12) | |
| return position, phase, triplets_to_close | |
| def transition_column(self, symbol: Symbol) -> tuple[int, ...]: | |
| return tuple(self.next_state_for_symbol(state, symbol) for state in self.states) | |
| def next_state_for_symbol(self, state: int, symbol: Symbol) -> int: | |
| position, phase, triplets_to_close = self.decode_state(state) | |
| if position >= self.length: | |
| return self.rejected_state | |
| units = duration_units(symbol.duration) | |
| is_triplet = is_triplet_duration(symbol.duration) | |
| next_triplets_to_close = 0 | |
| if self.enforce_triplet_groups: | |
| if triplets_to_close > 0: | |
| if not is_triplet: | |
| return self.rejected_state | |
| next_triplets_to_close = triplets_to_close - 1 | |
| elif is_triplet: | |
| if phase != 0: | |
| return self.rejected_state | |
| next_triplets_to_close = 2 | |
| return self.encode_state( | |
| position + 1, | |
| (phase + units) % 12, | |
| next_triplets_to_close, | |
| ) | |
| def next_state(self, state: int, symbol: Symbol) -> int | None: | |
| transitions = self.dense_transition_by_symbol.get(symbol) | |
| if transitions is None: | |
| return None | |
| next_state = transitions[state] | |
| if next_state == self.rejected_state: | |
| return None | |
| return next_state | |
| def is_accepting(self, state: int) -> bool: | |
| return state in self.accept_states | |
| def transition_weight(self, state: int, symbol: Symbol) -> float: | |
| position, _, _ = self.decode_state(state) | |
| if position == 0: | |
| return self.start_weights.get(symbol.rpc, 1.0) ** self.strength | |
| if position == self.length - 1: | |
| return self.end_weights.get(symbol.rpc, 1.0) ** self.strength | |
| return 1.0 | |
| def state_count(self) -> int: | |
| return self.state_count_value | |
| def make_theme_acceptor( | |
| *, | |
| length: int, | |
| alphabet: Iterable[Symbol], | |
| start_weights: dict[int, float], | |
| end_weights: dict[int, float], | |
| strength: float, | |
| enforce_triplet_groups: bool, | |
| ) -> ThemeGenerationAcceptor: | |
| return ThemeGenerationAcceptor( | |
| length=length, | |
| alphabet=alphabet, | |
| start_weights=start_weights, | |
| end_weights=end_weights, | |
| strength=strength, | |
| enforce_triplet_groups=enforce_triplet_groups, | |
| ) | |