from __future__ import annotations from dataclasses import dataclass from pathlib import Path NOTE_VOCABULARY_SIZE = 8 SEQUENCE_LENGTH = 5 ART_INPUT_LENGTH = 41 ART_CATEGORY_LIMIT = 25 HOPFIELD_WEIGHT_DIMENSION = 64 SALIERI_NODE_COUNT = 61 NoteSequence = tuple[int, ...] @dataclass(frozen=True) class LegacyPaths: root: Path @property def sequence_data(self) -> Path: return self.root / "SEQUENCE.DAT" @property def salieri_config(self) -> Path: return self.root / "S61.DAT" @property def salieri_weights(self) -> Path: return self.root / "S61.WT" @property def hopfield_weights(self) -> Path: return self.root / "HTN.DAT" @dataclass(frozen=True) class SalieriConfig: learning_rate: float alpha: float n_input: int n_hidden: int n_output: int training_iterations: int error_tolerance: float data_file: str report_file: str weight_file: str @dataclass(frozen=True) class LegacyBPWeights: vector_length: int weights: tuple[tuple[float, ...], ...] thetas: tuple[float, ...] @dataclass(frozen=True) class CompositionContext: notes: NoteSequence = (0, 0, 0, 0, 0) delta_vigilance: bool = False new_category: bool = False is_classical: bool = False candidate_note: int = 0 since_novelty: int = 0 frustration: int = 0 note_count: int = 0 @dataclass(frozen=True) class CompositionRecord: notes: tuple[int, ...] per_note_seconds: tuple[float, ...] = () total_seconds: float = 0.0 @dataclass(frozen=True) class CompositionRunReport: notes: tuple[int, ...] per_note_seconds: tuple[float, ...] total_seconds: float parameters: dict[str, object] note_count: int alphabet_size: int unigram_entropy_bits: float conditional_entropy_bits: float normalized_entropy: float predictability: float redundancy: float