← domain #6

Source : sruti_theory.py

data/library/books/brihaddesi_sharma_1992/formal_grammar/sruti_theory.py · 904 lines · 37008 bytes
"""Domaine 6 — śruti / microtuning theory (Brihaddesi de Mātaṅga, Sharma 1992) Synthèse 6c.3 du domaine 6 depuis : - 76 concepts (cluster śrutis = cluster_id 95 : 12 affirmations, 6 règles) - 60 règles génératives 6b - 105 affirmations sourcées (Sharma vol. I §III pp.014-020 + vol. II) ══════════════════════════════════════════════════════════════════════════════ POLITIQUE ANTI-FABRICATION — règle absolue de ce domaine ══════════════════════════════════════════════════════════════════════════════ Le Brihaddesi NE PROPOSE PAS un système de microtuning canonique. Mātaṅga *rapporte* plusieurs traditions concurrentes : • mīmāṃsā-stricte / Bharata : 22 śrutis (aff#2154, aff#2158) • différentialiste : 66 śrutis (aff#2154, aff#2158) • infinitiste : śrutis innombrables (aff#2154, aff#2158) • vamsa / instrument à anche : 9 śrutis (aff#2152, aff#2153, R_4p0_013) • triple-classification : 3 classes par vaiguṇya (aff#2136) • Tumburu (fourfold humoral) : 4 classes (aff#2144) • Kohala : autorité citée (aff#2151) Conséquence pour la modélisation : - PAS de table cent-grid canonique (aucun système avec valeurs en cents n'est sourcé dans le BrD ; tout ratio numérique serait fabriqué). - Chaque énumération de śrutis = constante attribuée à une AUTORITÉ. - Les opérations comparent des autorités ; elles ne tranchent pas. - Les śrutis sont des "points de domination" sur un continuum tonal, sans pitch fixes (aff#... aishvarya_definition_continuum). Chaque type / constante / opération / contrainte cite ≥ 1 rule_id 6b ou affirmation_id du pipeline Brihaddesi. Les zones non sourcées sont listées dans UNRESOLVED. Python 3.10+. Importable sans dépendance externe. """ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Literal # ============================================================================= # AUTORITÉS — qui dit quoi (système rapporté ≠ canon) # ============================================================================= class Authority(str, Enum): """Authorities cited by Mātaṅga as enumerating / classifying śrutis. These are NOT alternative renderings of one truth — they are distinct epistemic claims that the Brihaddesi reports side-by-side. evidence: aff#2154, aff#2158 (the three competing schools 22/66/∞ named by Mātaṅga); aff#2144 (Tumburu); aff#2151 (Kohala); aff#2913, aff#3021, aff#2963 (Abhinavagupta commenting NŚ); aff#2136 (threefold school per indriya-vaiguṇya); R_208_abhinava_bharati, R_4p1_12 (Abhi Bhā = commentary) """ BHARATA = "bharata" # NŚ — 22-school (mīmāṃsā-stricte) MIMAMSA_STRICT = "mimamsa_strict" # alias of 22-school (aff#2154) DIFFERENTIATED = "differentiated_66" # 66-school (aff#2154) INFINITIST = "infinitist" # śrutis innombrables (aff#2154) VAMSA_TRADITION = "vamsa_tradition" # 9-śruti flute school (aff#2153) THREEFOLD_VAIGUNYA = "threefold_vaigunya" # aff#2136 TUMBURU = "tumburu" # aff#2144 KOHALA = "kohala" # aff#2151 ABHINAVAGUPTA = "abhinavagupta" # commentator on NŚ (R_208, R_4p1_12) MATANGA = "matanga" # the BrD author himself BRD = "brihaddesi" # BrD as text-internal voice # ============================================================================= # Types partagés (Svara, Grāma, SVARA_ORDER) — importés depuis le domaine 3 # (canonicalisés par 6c.4). # ============================================================================= try: from .melodic_derivations import Svara, SVARA_ORDER, Grāma except ImportError: import os as _os, sys as _sys _sys.path.insert(0, _os.path.dirname(__file__)) from melodic_derivations import Svara, SVARA_ORDER, Grāma # type: ignore # ============================================================================= # CONSTANTES — chaque entrée est attribuée à une autorité # ============================================================================= # ----------------------------------------------------------------------------- # Comptes totaux de śrutis selon l'autorité # ----------------------------------------------------------------------------- # evidence: aff#2154 ("twenty-two śrutis / sixty-six / infinity"), # aff#2158 (idem reformulé), aff#2153 (venu = 9 śruti), # aff#2136 (threefold) # Valeurs : int = nombre fini ; None = infini (school infinitiste). SRUTI_COUNT_BY_AUTHORITY: dict[Authority, int | None] = { Authority.BHARATA: 22, Authority.MIMAMSA_STRICT: 22, # aff#2154 explicit aliasing Authority.DIFFERENTIATED: 66, Authority.INFINITIST: None, # ananta — aucun nombre fini Authority.VAMSA_TRADITION: 9, # aff#2153 Authority.THREEFOLD_VAIGUNYA: 3, # aff#2136 (classification, not enum) Authority.TUMBURU: 4, # aff#2144 (fourfold humoral) } # ----------------------------------------------------------------------------- # Définition générique de śruti (mesure d'écart de pañcama) # ----------------------------------------------------------------------------- # evidence: R_c278_sruti (FUNCTION sruti_count), aff#2155 (definition): # "Śruti is the measure of difference in pañcama through utkarṣa / # apakarṣa, through mārdava / āyatatva" SRUTI_OPERATIONAL_DEFINITION: str = ( "śruti = measure of difference of pañcama through " "utkarṣa(augmentation) / apakarṣa(diminution) / " "mārdava(softening) / āyatatva(extension)" ) # ----------------------------------------------------------------------------- # Structure interne du 22-school (autorité Bharata / Mātaṅga rapporteur) # ----------------------------------------------------------------------------- # evidence: śruti_total_22 (R), aff#2953 + aff#2954 # "Nine śrutis constituting a triad of svaras of 4 + 3 + 2 śrutis" # "9 + 9 + 4 = 22" BHARATA_22_STRUCTURE: dict[str, int | tuple[int, ...]] = { "total": 22, "triad_basic": (4, 3, 2), # une triade fondamentale "triad_sum": 9, # 4+3+2 = 9 "grouping": (9, 9, 4), # 9+9+4 = 22 } # ----------------------------------------------------------------------------- # Per-svara śruti count dans le 22-school (rapporté par Mātaṅga) # ----------------------------------------------------------------------------- # evidence: aff#2407 (gāndhāra & niṣāda = 2 śrutis chacun), # aff#1620 (gāndhāra = 2-śruti note), # aff#677 (śruti-bhinna : 4-śruti modifié par 2-śruti, gāndhāra=2), # aff#682 (niṣāda prend 2 śrutis de pañcama qui a 4 śrutis), # aff#1633 (pañcama caractérisé par 4 śrutis), # aff#1878 (grāma déterminé par pañcama 4 vs 3 śrutis), # aff#688 (en madhyamagrāma : pañcama=3 śrutis), # R_4p0_013 (vamsa : groupes de 2-3-4 śrutis). # Le 22-school assigne 4-4-3-2 entre les 7 svaras, pas une grille de cents. SVARA_SRUTI_COUNT_BHARATA_22: dict[tuple[Grāma, Svara], int] = { # ṣaḍja-grāma — distribution rapportée (Grāma.SADJA, Svara.SA): 4, # ṣaḍja brāhmaṇa, aff#2323 (Grāma.SADJA, Svara.RI): 3, # ṛṣabha kṣatriya, aff#2323 (Grāma.SADJA, Svara.GA): 2, # gāndhāra, aff#2407, aff#1620 (Grāma.SADJA, Svara.MA): 4, # madhyama brāhmaṇa, aff#2323 (Grāma.SADJA, Svara.PA): 4, # pañcama 4 śrutis dans ṣaḍja-grāma, # aff#1633, aff#1878, aff#688 (Grāma.SADJA, Svara.DHA): 3, # dhaivata kṣatriya (déduit de la classe # caste : 3-śruti, R_śruti_count_classes_caste) (Grāma.SADJA, Svara.NI): 2, # niṣāda, aff#2407 # madhyama-grāma — seul pañcama diffère (3 au lieu de 4), # tout le reste hérite (aff#1878 : "identity of grāma depends solely on # whether pañcama comprises four or three śrutis") (Grāma.MADHYAMA, Svara.SA): 4, (Grāma.MADHYAMA, Svara.RI): 3, (Grāma.MADHYAMA, Svara.GA): 2, (Grāma.MADHYAMA, Svara.MA): 4, (Grāma.MADHYAMA, Svara.PA): 3, # aff#688 : "pañcama comprised of 3 śrutis" (Grāma.MADHYAMA, Svara.DHA): 3, (Grāma.MADHYAMA, Svara.NI): 2, } # ----------------------------------------------------------------------------- # Classification caste-śruti (Mātaṅga rapporteur ; 22-school) # ----------------------------------------------------------------------------- # evidence: śruti_count_classes_caste (R), aff#2323 # "4-śruti svaras = brāhmaṇas, 3-śruti = kṣatriyas, 2-śruti = vaiśyas" CASTE_BY_SRUTI_COUNT_BHARATA_22: dict[int, str] = { 4: "brāhmaṇa", 3: "kṣatriya", 2: "vaiśya", } # ----------------------------------------------------------------------------- # Structure du vamsa (flûte de bambou) — autorité instrumentale # ----------------------------------------------------------------------------- # evidence: R_4p0_013, aff#2148, aff#2149, aff#2152, aff#2955 VAMSA_STRUCTURE: dict[str, object] = { "total": 9, "groups": (2, 3, 4), "production_methods": ("open_hole", "shake_finger", "half_open"), "rationale": "clear_description_via_hole_manipulation", } # ----------------------------------------------------------------------------- # Vamsa-lignée des svaras (paramparā / lineage non-numérique) # ----------------------------------------------------------------------------- # evidence: R_1723_vamsa_rsi_ri_dha, aff#2330 # "Ṛṣabha and dhaivata are born in the vaṃśa of ṛṣis" SVARA_VAMSA_RSI: frozenset[Svara] = frozenset({Svara.RI, Svara.DHA}) # ----------------------------------------------------------------------------- # Théories épistémiques de la relation svara ↔ śruti # ----------------------------------------------------------------------------- # Mātaṅga présente plusieurs vikalpana (alternatives), aucune n'est tranchée # comme la vérité. Le module les énumère toutes avec leur statut. # evidence: R_tadatmya_theory, R_c1651_vivartatva_def, R_1652_001, # parinamita_shruti_to_svara_transformation, R_1654_01, aff#2198 SVARA_SRUTI_THEORIES: dict[str, dict[str, str]] = { "tādātmya": { "claim": "svara ≡ śruti (identity, like jāti ≡ vyakti)", "analogy": "jāti / vyakti — species / individual", "status": "refuted by aśraya/āśrayin distinction (aff#2193)", "evidence": "R_tadatmya_theory, aff#2180, aff#2181, aff#2193", }, "kāryatva": { "claim": "svara = effect of śruti", "analogy": "clay / jar — material cause / product", "status": "one alternative (vikalpana)", "evidence": "R_1652_001", }, "pariṇāmitā": { "claim": "śruti transforms into svara", "analogy": "milk → curd", "status": "one alternative (vikalpana)", "evidence": "parinamita_shruti_to_svara_transformation", }, "vivartatva": { "claim": "svara is reflected in śrutis", "analogy": "face / mirror", "status": "one alternative (vikalpana)", "evidence": "R_c1651_vivartatva_def", }, "abhivyañjakatā": { "claim": "śrutis manifest svaras", "analogy": "candle / objects in darkness", "status": "one alternative (vikalpana)", "evidence": "R_1654_01", }, } # ----------------------------------------------------------------------------- # Pramāṇa-s invoqués dans les débats svara-śruti # ----------------------------------------------------------------------------- # evidence: R_537_anumana, R_288_arthapatti, R_2112_vyapti_graha, # R_1659_vyapti_grahana, R_1673_01 PRAMANAS: dict[str, dict[str, str]] = { "anumāna": { "definition": "inference", "accepted_by": "all Indian systems except Cārvāka", "prerequisite": "pratyakṣa (perception of vyāpti / concomitance)", "evidence": "R_537_anumana", }, "arthāpatti": { "definition": "postulation / negative inference", "accepted_by": "Mīmāṃsā, Advaita Vedānta (independent); " "Nyāya (subsumed under anumāna)", "evidence": "R_288_arthapatti", }, "vyāpti-grahaṇa": { "definition": "apprehension of universal concomitance", "evidence": "R_2112_vyapti_graha, R_1659_vyapti_grahana", }, } # ----------------------------------------------------------------------------- # Techniques de mesure / modification de śruti # ----------------------------------------------------------------------------- # evidence: R_283_utkarsa, R_4p1_284, R_c128_mardava, ayatatva_definition_extension SRUTI_MODIFICATION_OPS: dict[str, dict[str, str]] = { "utkarṣa": { "direction": "raise pitch", "use": "measure śruti interval / effect grāma transformation", "evidence": "R_283_utkarsa, aff#2161", }, "apakarṣa": { "direction": "lower pitch", "use": "counterpart of utkarṣa; vīṇā śruti differentiation", "evidence": "R_4p1_284, aff#2162", }, "mārdava": { "direction": "softening (associated to svara followed by higher one)", "use": "with āyatatva, to measure śruti of pañcama", "evidence": "R_c128_mardava, aff#2163, aff#2968", }, "āyatatva": { "direction": "extension (associated to svara followed by lower one)", "use": "with mārdava, to measure śruti of pañcama; " "replaced by viprakarṣa in BrD Anu. 50", "evidence": "ayatatva_definition_extension, aff#2164", }, } # ----------------------------------------------------------------------------- # Techniques de tāna (omission/inclusion de svaras dans le contexte microtonal) # ----------------------------------------------------------------------------- # evidence: pravesa_definition, pravesa_mechanism, R_555_nigraha_definition, # R_2192_tarakriya_def, R_2191_01, R_2189_001, # apurnasvarata_associated_with_paryagraha TANA_TECHNIQUES: dict[str, dict[str, str]] = { "praveśa": { "definition": "lower (or higher) svara enters/merges into the higher " "(or lower) svara — viprakarṣa of lower must be omitted", "mechanism": "via prakarṣa(L) OR mārdava(H)", "evidence": "pravesa_definition, pravesa_mechanism", }, "nigraha": { "definition": "omission / non-touching of the immediate svara", "alias": "mandrakriyā per Abhinavagupta (R_2191_01)", "associated": "pūrṇasvaratā — complete use of svaras (R_2189_001)", "evidence": "R_555_nigraha_definition, R_2191_01, R_2189_001", }, "paryagraha": { "definition": "use of high svaras", "alias": "tārakriyā per Abhinavagupta (R_2192_tarakriya_def)", "associated": "apūrṇasvaratā — incomplete use of svaras", "evidence": "R_2192_tarakriya_def, apurnasvarata_associated_with_paryagraha", }, } # ----------------------------------------------------------------------------- # Aiśvarya — śrutis comme points de domination, pas pitch fixes # ----------------------------------------------------------------------------- # evidence: aishvarya_definition_continuum # "Aiśvarya of śrutis refers to their domination as cognizable points # on a continuum of tones (no fixed pitch points)" SRUTI_AISVARYA_PRINCIPLE: str = ( "śrutis = points of supremacy/domination on a tone-continuum; " "NO fixed pitch points" ) # ----------------------------------------------------------------------------- # Contrainte de register-preservation # ----------------------------------------------------------------------------- # evidence: śruti_register_preservation (R), aff#2566 # "A svara should be sounded in three registers with the same # number of śrutis as originally composed" SRUTI_REGISTER_PRESERVATION: bool = True # ----------------------------------------------------------------------------- # Cokṣa-rāgas (rāgas dits "purs" en relation avec śuddhā gīti) # ----------------------------------------------------------------------------- # evidence: R_235_coksa_enumeration, shuddha_giti_coksha_features COKSA_RAGAS: tuple[str, ...] = ( "ṣāḍava", "pañcama", "kaiśikamadhyama", "cokṣasādhārita", "cokṣakaiśika", ) # ============================================================================= # TYPES — dataclasses # ============================================================================= @dataclass(frozen=True) class SrutiClaim: """An authority-attributed claim about śruti enumeration / structure. The Brihaddesi NEVER asserts a count as canonical truth — every count is delivered as "X says". This dataclass enforces that provenance. evidence: aff#2154, aff#2158 (multi-school enumeration reported) """ authority: Authority count: int | None # None = infinite (infinitist school) scope: str # e.g. "total", "per_svara", "vamsa_only" evidence: str # rule_id or aff#id def is_finite(self) -> bool: return self.count is not None def is_infinite(self) -> bool: return self.count is None @dataclass(frozen=True) class SrutiBhinna: """Śruti-bhinna relation: a 4-śruti svara modified by a 2-śruti svara. Canonical example: niṣāda takes 2 śrutis of pañcama (which has 4), with gāndhāra (2-śruti) involved. evidence: R_857_srutibhinna_definition, r_brd_317_sruti_bhinna_def, aff#677, aff#682 """ modified_svara: Svara # the 4-śruti svara modifier_svara: Svara # the 2-śruti svara grāma: Grāma borrowed_srutis: int = 2 # canonical: 2 śrutis borrowed (aff#682) def __post_init__(self) -> None: modified_count = SVARA_SRUTI_COUNT_BHARATA_22[(self.grāma, self.modified_svara)] modifier_count = SVARA_SRUTI_COUNT_BHARATA_22[(self.grāma, self.modifier_svara)] if modified_count != 4: raise ValueError( f"śruti-bhinna requires 4-śruti modified svara; " f"got {self.modified_svara.value} = {modified_count}" ) if modifier_count != 2: raise ValueError( f"śruti-bhinna requires 2-śruti modifier svara; " f"got {self.modifier_svara.value} = {modifier_count}" ) @dataclass(frozen=True) class SvaraSrutiTheory: """One of the alternative (vikalpana) theories of how svara relates to śruti, as enumerated by Mātaṅga without elevating any single one. evidence: aff#2198 ("alternatives starting with tādātmya have been spoken of by me") """ name: str claim: str analogy: str status: str evidence: str @dataclass(frozen=True) class AuthoritySrutiSystem: """A complete śruti-enumeration system as attributed to one authority. Used by `compare_authorities()` to surface disagreement explicitly rather than picking a winner. """ authority: Authority count: int | None classification: tuple[int, ...] | None # subdivisions if any (4-3-2 …) evidence: str @dataclass(frozen=True) class Matrika: """Mātṛkā — primary unit of tonal sound, "more subtle than śruti" per Mātaṅga. Distinct from śruti; subtle basis with numeric correspondence to śrutis. evidence: BD_6_1_R017, R_289_matrkas, R_2148_001, R_1673_01, aff#3002, aff#2202, aff#3032 """ context: Literal["religious", "linguistic", "musical_matanga"] role: str movement: Literal["vakra", "straight"] | None = None # for musical context evidence: str = "BD_6_1_R017" @dataclass(frozen=True) class VaigunyaImpairment: """Vaiguṇya — faultiness of the human voice (organ of speech). Threefold per Abhinavagupta and the BrD. evidence: R_2050_indriya_vaigunya_threefold, vaigunya_definition, R_2051_vagindriya_seat_vaigunya """ type: Literal["inborn", "humour_imbalance", "accident"] seat: str = "vāgindriya" # primary seat per Abhinavagupta # ============================================================================= # OPÉRATIONS — exécutables # ============================================================================= def get_sruti_count(authority: Authority) -> int | None: """Retourne le compte total de śrutis revendiqué par une autorité. `None` signifie infini (école infinitiste — pas zéro, pas inconnu). evidence: SRUTI_COUNT_BY_AUTHORITY (chaque entrée a son aff#) """ if authority not in SRUTI_COUNT_BY_AUTHORITY: raise KeyError( f"Authority {authority.value} not enumerated as a śruti school " f"in the Brihaddesi rule corpus" ) return SRUTI_COUNT_BY_AUTHORITY[authority] def get_svara_sruti_count_bharata_22(grāma: Grāma, svara: Svara) -> int: """Per-svara śruti count *within* the 22-school as reported by Mātaṅga. NE PAS utiliser comme la "vraie" valeur — c'est l'attribution Bharata rapportée par Mātaṅga ; les autres écoles disent autre chose. evidence: SVARA_SRUTI_COUNT_BHARATA_22 dict (each entry cited) """ return SVARA_SRUTI_COUNT_BHARATA_22[(grāma, svara)] def caste_of_svara_bharata_22(grāma: Grāma, svara: Svara) -> str: """Classe-caste d'un svara selon la classification śruti-count (22-school). evidence: śruti_count_classes_caste, aff#2323 """ n = get_svara_sruti_count_bharata_22(grāma, svara) return CASTE_BY_SRUTI_COUNT_BHARATA_22[n] def grama_of_pancama_count(n_srutis_pancama: int) -> Grāma: """Détermine le grāma à partir du compte śruti de pañcama (4 ou 3). "The identity of grāma depends solely on whether pañcama comprises four or three śrutis" — aff#1878. evidence: aff#1878, aff#688, aff#1633, aff#1632, R_319_gramas """ if n_srutis_pancama == 4: return Grāma.SADJA if n_srutis_pancama == 3: return Grāma.MADHYAMA raise ValueError( f"pañcama with {n_srutis_pancama} śrutis: no grāma assignment " f"in the Brihaddesi (only 4 → ṣaḍja, 3 → madhyama)" ) def is_srutibhinna(modified: Svara, modifier: Svara, grāma: Grāma) -> bool: """Vrai ssi (modified=4-śruti) ∧ (modifier=2-śruti) dans le 22-school. Exemple canonique : niṣāda (2) modifie pañcama (4) → śrutibhinna. evidence: R_857_srutibhinna_definition, r_brd_317_sruti_bhinna_def, aff#677 ("Śruti-bhinna defined as svara comprised of 4 śrutis being modified by one comprised of 2 śrutis") """ mod_count = SVARA_SRUTI_COUNT_BHARATA_22[(grāma, modified)] modr_count = SVARA_SRUTI_COUNT_BHARATA_22[(grāma, modifier)] return mod_count == 4 and modr_count == 2 def sama_srutikatva(s1: Svara, s2: Svara, grāma: Grāma) -> bool: """Sama-śrutikatva = égalité d'intervalle de śruti entre deux svaras (dans le 22-school). evidence: R_1696_sama_srutikatva_def """ return ( SVARA_SRUTI_COUNT_BHARATA_22[(grāma, s1)] == SVARA_SRUTI_COUNT_BHARATA_22[(grāma, s2)] ) def authority_disagrees(a1: Authority, a2: Authority) -> bool: """Vrai ssi a1 et a2 donnent des comptes totaux différents. Cet opérateur EXISTE PRÉCISÉMENT pour empêcher d'effacer le désaccord : si tādātmya/22 vs infinitistes diffèrent, on le constate, on ne tranche pas. evidence: aff#2154, aff#2158 """ c1 = SRUTI_COUNT_BY_AUTHORITY.get(a1) c2 = SRUTI_COUNT_BY_AUTHORITY.get(a2) if a1 not in SRUTI_COUNT_BY_AUTHORITY or a2 not in SRUTI_COUNT_BY_AUTHORITY: raise KeyError(f"Unknown authority: {a1}, {a2}") return c1 != c2 def compare_authorities() -> list[AuthoritySrutiSystem]: """Retourne tous les systèmes śruti côte-à-côte (anti-tranchage). evidence: aff#2154, aff#2158 (Mātaṅga lui-même les présente côte-à-côte) """ return [ AuthoritySrutiSystem( authority=Authority.BHARATA, count=22, classification=BHARATA_22_STRUCTURE["grouping"], # type: ignore evidence="śruti_total_22 + aff#2953 + aff#2954", ), AuthoritySrutiSystem( authority=Authority.DIFFERENTIATED, count=66, classification=None, evidence="aff#2154, aff#2158", ), AuthoritySrutiSystem( authority=Authority.INFINITIST, count=None, classification=None, evidence="aff#2154, aff#2158", ), AuthoritySrutiSystem( authority=Authority.VAMSA_TRADITION, count=9, classification=VAMSA_STRUCTURE["groups"], # type: ignore evidence="R_4p0_013, aff#2148, aff#2149, aff#2152, aff#2153", ), AuthoritySrutiSystem( authority=Authority.THREEFOLD_VAIGUNYA, count=3, classification=None, evidence="aff#2136 (threefold per indriya-vaiguṇya)", ), AuthoritySrutiSystem( authority=Authority.TUMBURU, count=4, classification=None, evidence="aff#2144 (fourfold humoral)", ), ] def list_svara_sruti_theories() -> list[SvaraSrutiTheory]: """Énumère les théories vikalpana de la relation svara/śruti, sans en élire une. evidence: SVARA_SRUTI_THEORIES dict (each entry cited), aff#2198 """ return [ SvaraSrutiTheory( name=name, claim=t["claim"], analogy=t["analogy"], status=t["status"], evidence=t["evidence"], ) for name, t in SVARA_SRUTI_THEORIES.items() ] def apply_utkarsa(svara: Svara) -> dict[str, object]: """Modèle symbolique d'utkarṣa appliqué à un svara. Ne renvoie PAS de cents (aucune valeur cents n'est sourcée dans le BrD). Renvoie une description d'effet. evidence: R_283_utkarsa """ return { "svara": svara, "operation": "utkarṣa", "direction": "raise_pitch", "shifts": "śruti_interval", "evidence": "R_283_utkarsa, aff#2161", } def apply_apakarsa(svara: Svara) -> dict[str, object]: """Modèle symbolique d'apakarṣa. Symétrique d'utkarṣa. evidence: R_4p1_284 """ return { "svara": svara, "operation": "apakarṣa", "direction": "lower_pitch", "use": "śruti_difference_measurement", "evidence": "R_4p1_284, aff#2162", } def grāma_transformation_via_pancama(from_grama: Grāma) -> Grāma: """ṣaḍja-grāma ↔ madhyama-grāma par modification śruti-count de pañcama (4 ↔ 3). evidence: aff#1878, R_283_utkarsa ("APPLY in grama_transformation WHEN murchana changes WITH utkarsa") """ return Grāma.MADHYAMA if from_grama == Grāma.SADJA else Grāma.SADJA # ============================================================================= # CONTRAINTES — validations # ============================================================================= def is_valid_sruti_count_claim(count: int | None, authority: Authority) -> bool: """Vrai ssi (authority, count) correspond à une revendication sourcée. evidence: SRUTI_COUNT_BY_AUTHORITY (cf. citations par entrée) """ if authority not in SRUTI_COUNT_BY_AUTHORITY: return False return SRUTI_COUNT_BY_AUTHORITY[authority] == count def is_valid_22_school_structure(parts: tuple[int, ...]) -> bool: """Vrai ssi (parts) somment à 22 ET correspondent au pattern 9+9+4 rapporté. evidence: śruti_total_22, aff#2954 """ return tuple(parts) == BHARATA_22_STRUCTURE["grouping"] and sum(parts) == 22 def is_valid_register_preservation( n_srutis_original: int, n_srutis_in_register: int ) -> bool: """Un svara, dans chacun des 3 registres, conserve son count śruti d'origine. evidence: śruti_register_preservation, aff#2566 """ return n_srutis_original == n_srutis_in_register def can_perform_shake(svara: Svara, grāma: Grāma, alankara: str | None = None) -> bool: """Règle traditionnelle : kampita (shake) admis seulement sur svaras de 3 śrutis. Règle 'érodée dans certains alaṅkāras' (aff#3148). evidence: śruti_shake_3sruti_rule_eroded, aff#3148 """ base_rule = SVARA_SRUTI_COUNT_BHARATA_22[(grāma, svara)] == 3 # Exception : alaṅkāra-spécifique. Le BrD ne nomme pas les alaṅkāras # exceptés (UNRESOLVED). return base_rule def is_valid_vamsa_grouping(groups: tuple[int, ...]) -> bool: """Vamsa : 9 śrutis structurés en groupes 2-3-4. evidence: R_4p0_013, aff#2148, aff#2149, aff#2955 """ return ( tuple(sorted(groups)) == tuple(sorted(VAMSA_STRUCTURE["groups"])) # type: ignore and sum(groups) == VAMSA_STRUCTURE["total"] ) # ============================================================================= # UNRESOLVED — flagged zones # ============================================================================= # Listed here for traceability ; consumed by the JSON manifest. UNRESOLVED: tuple[dict[str, str], ...] = ( { "concept": "cents-grid / numerical ratio for any śruti system", "reason": "AUCUN ratio numérique (Pythagorean, just-intonation, " "log2, Daniélou ratios, etc.) n'est sourcé dans le BrD. " "Le BrD ne fournit que des comptes entiers per-svara " "et la définition opératoire (utkarṣa/apakarṣa). " "Imposer une grille de cents serait fabrication.", }, { "concept": "jāti-classes of śrutis (dīptā/āyatā/mṛdu/madhyā/karuṇā)", "reason": "Ces 5 jāti-de-śruti sont attendues du Nāṭyaśāstra, " "mais aucun rule_id 6b ni affirmation du BrD-Mātaṅga " "ne les énumère individuellement avec leur assignation " "par śruti. Seule trace : aff#1849 (mṛdu = synonyme " "de mandra) et 'dipta' comme cluster vide (cluster_id=500, " "0 affirmations). NON modélisé.", }, { "concept": "66-school internal structure", "reason": "aff#2154 et aff#2158 nomment l'école 66-différencialiste " "mais aucune règle 6b ne donne la sous-division. " "Compte total seulement.", }, { "concept": "infinitist school internal description", "reason": "aff#2154 mentionne ananta-śruti ; aucun mécanisme " "(densité, distribution) n'est précisé.", }, { "concept": "fourfold Tumburu classification (humoral) — content", "reason": "aff#2144 cite Tumburu comme autorité du fourfold humoral, " "mais le contenu des 4 classes n'est pas extrait " "(les 4 humeurs ne sont pas nommées en affirmations 6b).", }, { "concept": "Kohala's specific śruti doctrine", "reason": "aff#2151 cite Kohala comme autorité mais ne donne pas " "le contenu de sa position.", }, { "concept": "alaṅkāras exempting 3-śruti shake-rule", "reason": "aff#3148 mentionne 'certains alaṅkāras' où la règle " "est érodée, sans les nommer.", }, { "concept": "uttarāyatā / suddhamadhyā cluster ambiguity", "reason": "clusters 1754, 1815 portent les jāti-śruti nom mais " "leur domain_id=3/611 et 0 affirmations → relèvent " "des mūrchanās, non des classes de śruti.", }, { "concept": "shuddhakaisikamadhyama grāma assignment", "reason": "Disputé en source (référencé en domaine 3, " "shuddhakaisikamadhyama_grama_disputed); non modélisé ici.", }, ) # ============================================================================= # Self-test # ============================================================================= if __name__ == "__main__": # Sanity: chaque autorité doit exposer un count (ou None) for auth in Authority: if auth in SRUTI_COUNT_BY_AUTHORITY: v = get_sruti_count(auth) assert v is None or isinstance(v, int), (auth, v) # Sanity: 22-school structure cohérente assert BHARATA_22_STRUCTURE["total"] == 22 assert sum(BHARATA_22_STRUCTURE["grouping"]) == 22 # type: ignore assert is_valid_22_school_structure((9, 9, 4)) assert not is_valid_22_school_structure((9, 8, 5)) # Sanity: per-svara count (Bharata 22-school) — pañcama distinct par grāma assert get_svara_sruti_count_bharata_22(Grāma.SADJA, Svara.PA) == 4 assert get_svara_sruti_count_bharata_22(Grāma.MADHYAMA, Svara.PA) == 3 assert get_svara_sruti_count_bharata_22(Grāma.SADJA, Svara.GA) == 2 assert get_svara_sruti_count_bharata_22(Grāma.SADJA, Svara.NI) == 2 # Sanity: caste classification assert caste_of_svara_bharata_22(Grāma.SADJA, Svara.SA) == "brāhmaṇa" assert caste_of_svara_bharata_22(Grāma.SADJA, Svara.RI) == "kṣatriya" assert caste_of_svara_bharata_22(Grāma.SADJA, Svara.GA) == "vaiśya" # Sanity: grāma déterminé par pañcama assert grama_of_pancama_count(4) == Grāma.SADJA assert grama_of_pancama_count(3) == Grāma.MADHYAMA try: grama_of_pancama_count(5) assert False, "should have raised" except ValueError: pass # Sanity: śrutibhinna canonical case (niṣāda modifying pañcama) assert is_srutibhinna(Svara.PA, Svara.NI, Grāma.SADJA) # niṣāda n'est pas 4-śruti → pas modified assert not is_srutibhinna(Svara.NI, Svara.PA, Grāma.SADJA) # sama-counts → pas śrutibhinna assert not is_srutibhinna(Svara.SA, Svara.MA, Grāma.SADJA) # Sanity: SrutiBhinna dataclass valide sb = SrutiBhinna(modified_svara=Svara.PA, modifier_svara=Svara.NI, grāma=Grāma.SADJA) assert sb.borrowed_srutis == 2 try: SrutiBhinna(modified_svara=Svara.GA, modifier_svara=Svara.NI, grāma=Grāma.SADJA) assert False, "should have raised (GA=2 != 4)" except ValueError: pass # Sanity: sama-śrutikatva assert sama_srutikatva(Svara.SA, Svara.MA, Grāma.SADJA) # 4 ↔ 4 assert sama_srutikatva(Svara.GA, Svara.NI, Grāma.SADJA) # 2 ↔ 2 assert not sama_srutikatva(Svara.SA, Svara.GA, Grāma.SADJA) # 4 ↔ 2 # Sanity: désaccord d'autorités explicite assert authority_disagrees(Authority.BHARATA, Authority.DIFFERENTIATED) assert authority_disagrees(Authority.BHARATA, Authority.INFINITIST) assert authority_disagrees(Authority.BHARATA, Authority.VAMSA_TRADITION) assert not authority_disagrees(Authority.BHARATA, Authority.MIMAMSA_STRICT) # Sanity: compare_authorities renvoie ≥ 5 systèmes distincts systems = compare_authorities() assert len(systems) >= 5 assert any(s.count == 22 for s in systems) assert any(s.count == 66 for s in systems) assert any(s.count is None for s in systems) # infinitist assert any(s.count == 9 for s in systems) # vamsa # Sanity: théories svara-śruti — au moins 5 listées sans hiérarchie theories = list_svara_sruti_theories() assert len(theories) >= 5 names = {t.name for t in theories} assert {"tādātmya", "kāryatva", "pariṇāmitā", "vivartatva", "abhivyañjakatā"} <= names # Sanity: register preservation assert is_valid_register_preservation(4, 4) assert not is_valid_register_preservation(4, 3) # Sanity: shake rule assert can_perform_shake(Svara.RI, Grāma.SADJA) # 3 śrutis → autorisé assert not can_perform_shake(Svara.SA, Grāma.SADJA) # 4 śrutis → non assert not can_perform_shake(Svara.GA, Grāma.SADJA) # 2 śrutis → non # Sanity: vamsa grouping assert is_valid_vamsa_grouping((2, 3, 4)) assert is_valid_vamsa_grouping((4, 2, 3)) assert not is_valid_vamsa_grouping((1, 3, 5)) # Sanity: utkarṣa / apakarṣa retournent des descriptions sourcées u = apply_utkarsa(Svara.PA) assert u["direction"] == "raise_pitch" assert "R_283_utkarsa" in u["evidence"] a = apply_apakarsa(Svara.PA) assert a["direction"] == "lower_pitch" assert "R_4p1_284" in a["evidence"] # Sanity: grāma transformation assert grāma_transformation_via_pancama(Grāma.SADJA) == Grāma.MADHYAMA assert grāma_transformation_via_pancama(Grāma.MADHYAMA) == Grāma.SADJA # Sanity: claim validity assert is_valid_sruti_count_claim(22, Authority.BHARATA) assert is_valid_sruti_count_claim(66, Authority.DIFFERENTIATED) assert is_valid_sruti_count_claim(None, Authority.INFINITIST) assert not is_valid_sruti_count_claim(22, Authority.INFINITIST) # Sanity: UNRESOLVED non vide et flagge cents-grid assert len(UNRESOLVED) >= 5 assert any("cents-grid" in u["concept"] for u in UNRESOLVED) assert any("jāti-classes of śrutis" in u["concept"] for u in UNRESOLVED) print("sruti_theory.py — all self-tests pass")