← domain #2

Source : bhasha_raga_corpus.py

data/library/books/brihaddesi_sharma_1992/formal_grammar/bhasha_raga_corpus.py · 898 lines · 36483 bytes
"""Domaine 2 — bhāṣā / vibhāṣā / grāma-rāga corpus Synthèse 6c.3 du Brihaddesi (Sharma 1992, vols I & II) depuis : - 112 règles génératives 6b (domaine 2) - 261 affirmations sourcées - partition Leiden domain_id=2 (110 concepts) Champ couvert : la taxonomie grāma-rāga (śuddha / bhinna / vesara / gauḍa / sādhāraṇa), les bhāṣā-rāgas, les vibhāṣā-rāgas, l'antara-bhāṣā, et les rāgas individuels (ṭakkarāga, hindolaka, mālavapañcama, bhinnaṣaḍja…) et leurs liens aux parents. Anti-fabrication : chaque type, opération et contrainte cite ≥1 evidence (rule_id 6b ou affirmation_id). Les concepts non sourcés sont listés dans UNRESOLVED. Aucune valeur plausible-mais-non-sourcée n'a été introduite. Python 3.10+. Importable sans dépendance externe. """ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Iterable # ============================================================================= # CONSTANTES — énumérations fermées du Brihaddesi (domaine bhāṣā/rāga) # ============================================================================= # Types partagés avec melodic_derivations (domaine 3) — canonicalisés par 6c.4. try: from .melodic_derivations import Svara, Grāma, ScaleType except ImportError: import os as _os, sys as _sys _sys.path.insert(0, _os.path.dirname(__file__)) from melodic_derivations import Svara, Grāma, ScaleType # type: ignore class GītiClass(str, Enum): """The five gītis of Durgaśakti — define the five categories of grāma-rāga. Each grāma-rāga belongs to exactly one of these; the gīti is the formative principle of its "song-style". evidence: aff#542 ('Gītis should be known to be five viz. śuddhā, then bhinnā, vesarā, gauḍī (and) sādhāritā; this is the opinion of Durgā (Durgaśakti).'), R_4p0_017 (sādhāraṇa = born of all previously-spoken rāgas; sung in sādhāraṇa-gīti), R_869_vesaragiti_category, R_4p1_388 (text order: gauda before vesarā gīti), R_1275_gaudaragas_category """ SUDDHA = "suddha" # = cokṣa (aff#578, aff#587, aff#591) BHINNA = "bhinna" # 5 enumerated bhinna rāgas (aff#578, aff#588) GAUDA = "gauda" # gauḍa-rāgas (R_1275) VESARA = "vesara" # vesara-rāgas (R_389) SADHARANA = "sadharana" # 7 sādhāraṇa rāgas (aff#589, R_4p0_017) class BhasaSubtype(str, Enum): """The four-fold classification of bhāṣās per the Brihaddesi. evidence: R_15_bhasa_classification, aff#874 ('Bhāṣās have been said to be four-fold viz. mūlā (basic) sankīrṇā (mixed), deśajā (regional) (and) chāyāmātrāśrayā (based on the chāyā of a rāga)') """ MULA = "mula" # basic / root SANKIRNA = "sankirna" # mixed DESAJA = "desaja" # regional CHAYAMATRASRAYA = "chayamatrasraya" # based on chāyā of a rāga class BhasaTier(str, Enum): """The 3-level derivational hierarchy: grāma-rāga → bhāṣā → vibhāṣā → antara-bhāṣā. evidence: R_073_bhasas_derivation (bhāṣā derived from grāma-rāga), R_920_vibhasa_from_bhasa (vibhāṣā derived from bhāṣā), R_5p2_921 (antara-bhāṣā derived from vibhāṣā), aff#875, aff#876, aff#877, aff#1964 """ BHASA = "bhasa" VIBHASA = "vibhasa" ANTARA_BHASA = "antara_bhasa" # ----------------------------------------------------------------------------- # Closed enumerations of grāma-rāgas per gīti class # ----------------------------------------------------------------------------- # Each entry below is verbatim from a Brihaddesi rule/affirmation listing. # No name is added by inference. # Five śuddha (= cokṣa) rāgas — aff#587 vol_II_p052 # evidence: aff#587 ('ṣāḍava, pañcama, kaiśikamadhyama, cokṣasādhārita, # cokṣakaiśika'), aff#578 ('Five cokṣa (śuddha, pure rāgas) have # been spoken of, the bhinna ones are of the same number.'), # aff#591 SUDDHA_RAGAS: tuple[str, ...] = ( "sadava", "pancama", "kaisikamadhyama", "coksasadharita", "coksakaisika", ) # Five bhinna rāgas — aff#588 vol_II_p052 # evidence: aff#588 ('bhinna-ṣaḍja, bhinna-tāna, bhinnakaiśikamadhyama, # bhinnapañcama, bhinnakaiśika'), aff#640 (bhinna-ṣaḍja prominence) BHINNA_RAGAS: tuple[str, ...] = ( "bhinnasadja", "bhinnatana", "bhinnakaisikamadhyama", "bhinnapancama", "bhinnakaisika", ) # Seven sādhāraṇa rāgas — aff#589 vol_II_p052 # evidence: aff#589 ('narta, śaka, kakubha, harmāṇapañcama, rūpasādhārita, # gāndhārapañcama, ṣaḍjakaiśika'), aff#841 (sapta sādhāraṇā… # grāmadvayasamāśrayā — "seven sādhāraṇas, resting on both grāmas") # NB: a parallel list (aff#43 in TOC) names 10 sādhāraṇa rāgas including # Bhammāṇapañcama, Pañcamaṣāḍava, Revagupta — this is a TOC enumeration # differing from the body's verse count of seven. Discrepancy logged in # UNRESOLVED. SADHARANA_RAGAS: tuple[str, ...] = ( "narta", "saka", "kakubha", "harmanapancama", # var. bhammāṇapañcama (TOC aff#43) "rupasadharita", "gandharapancama", "sadjakaisika", ) # Vesara rāgas, enumerated per grāma — R_389_vesara_def_enum # evidence: R_389_vesara_def_enum (body lists members per grāma), # aff#777 (Sanskrit verse listing the ṣaḍjagrāma vesaras), # aff#719 ('Vesaras are known to be so because the svaras move # with speed.') VESARA_RAGAS: dict[Grāma, tuple[str, ...]] = { Grāma.SADJA: ( "takkaraga", "sauvira", "takkakaisika", "bottaraga", "vesarasadava", ), Grāma.MADHYAMA: ( "hindolaka", "malavapancama", "malavakaisika", ), } # Gauḍa-rāgas — no closed enumeration rule found in 6b; category is named # (R_1275_gaudaragas_category) and the corpus mentions individual gauḍa # rāgas only intermittently. Listed in UNRESOLVED. # ----------------------------------------------------------------------------- # Grāma-rāga → gīti class (which bucket each named rāga belongs to) # ----------------------------------------------------------------------------- # Each assignment cites the rule / affirmation that pins it. GRAMARAGA_GITI: dict[str, GītiClass] = { # vesara (R_389_vesara_def_enum) "takkaraga": GītiClass.VESARA, "sauvira": GītiClass.VESARA, "takkakaisika": GītiClass.VESARA, "bottaraga": GītiClass.VESARA, "vesarasadava": GītiClass.VESARA, "hindolaka": GītiClass.VESARA, "malavapancama": GītiClass.VESARA, "malavakaisika": GītiClass.VESARA, # bhinna (aff#588) "bhinnasadja": GītiClass.BHINNA, "bhinnatana": GītiClass.BHINNA, "bhinnakaisikamadhyama": GītiClass.BHINNA, "bhinnapancama": GītiClass.BHINNA, "bhinnakaisika": GītiClass.BHINNA, # śuddha (aff#587) "sadava": GītiClass.SUDDHA, "pancama": GītiClass.SUDDHA, "kaisikamadhyama": GītiClass.SUDDHA, "coksasadharita": GītiClass.SUDDHA, "coksakaisika": GītiClass.SUDDHA, # sādhāraṇa (aff#589) "narta": GītiClass.SADHARANA, "saka": GītiClass.SADHARANA, "kakubha": GītiClass.SADHARANA, "harmanapancama": GītiClass.SADHARANA, "rupasadharita": GītiClass.SADHARANA, "gandharapancama": GītiClass.SADHARANA, "sadjakaisika": GītiClass.SADHARANA, } # ----------------------------------------------------------------------------- # Grāma-rāga → parent grāma (which of the two grāmas it belongs to) # ----------------------------------------------------------------------------- # Only assignments with a specific Brihaddesi rule/affirmation are encoded. GRAMARAGA_GRAMA: dict[str, Grāma] = { # vesara, ṣaḍjagrāma — R_389_vesara_def_enum + aff#777 "takkaraga": Grāma.SADJA, # takkaraga_derivation_and_grama, aff#723 "sauvira": Grāma.SADJA, # R_389 + aff#777 "takkakaisika": Grāma.SADJA, # R_389 + aff#777 "bottaraga": Grāma.SADJA, # R_389 + aff#777 "vesarasadava": Grāma.SADJA, # R_389 + aff#777 # vesara, madhyamagrāma "hindolaka": Grāma.MADHYAMA, # hindolaka_madhyamagrama_origin aff#783 "malavapancama": Grāma.MADHYAMA, # R_389 (in madhyamagrāma list) "malavakaisika": Grāma.MADHYAMA, # R_malavakaisika_def + aff#775 # bhinna assignments to a grāma "bhinnakaisika": Grāma.MADHYAMA, # aff#669, aff#676 # other bhinna rāgas: relations to grāma noted at various points but not # all isolated to single-grāma assignment. See UNRESOLVED. # sādhāraṇa "kakubha": Grāma.SADJA, # aff#800, 'related to ṣaḍjagrāma' "rupasadharita": Grāma.SADJA, # aff#843 "saka": Grāma.SADJA, # aff#843 # pañcamaṣāḍava (TOC variant of sādhāraṇa list): aff#826 # revagupta: aff#827. Both listed only in the 10-name TOC, not the # 7-name body verse → kept out of SADHARANA_RAGAS but their grāma is # sourced for cross-reference. } # ----------------------------------------------------------------------------- # Bhāṣā counts per grāma-rāga (closed enumerations from 6b rules) # ----------------------------------------------------------------------------- # Some sources disagree on the count; both values kept when sourced. # evidence per entry. @dataclass(frozen=True) class BhasaCount: """A sourced bhāṣā-count for a grāma-rāga, possibly with a disputing alternative. evidence: see `evidence_rules` per record below. """ primary: int alternative: int | None = None evidence_rules: tuple[str, ...] = () BHASA_COUNTS: dict[str, BhasaCount] = { # R_398_takkaraga_features + aff#878 (12, some 16) + R_954_takkaraga_sixteen "takkaraga": BhasaCount( primary=12, alternative=16, evidence_rules=( "R_398_takkaraga_features", "R_117_takkaraga_bhasa_count", "R_954_takkaraga_sixteen", ), ), # R_c32_bhinnasadja + aff#882 + R_932_bhinnasadja_enum "bhinnasadja": BhasaCount( primary=9, alternative=15, evidence_rules=( "R_c32_bhinnasadja", "R_932_bhinnasadja_enum", ), ), # R_077_bhinnapancama_enumeration + aff#51, aff#904 "bhinnapancama": BhasaCount( primary=4, evidence_rules=("R_077_bhinnapancama_enumeration",), ), # R_114_malavakaisika_enumeration + aff#892 ('thus are the eight bhāṣās') "malavakaisika": BhasaCount( primary=8, evidence_rules=( "R_114_malavakaisika_enumeration", "R_922_malavakaisika_8_bhasas", ), ), # R_137_malavapancama + aff#1225 "malavapancama": BhasaCount( primary=6, evidence_rules=("R_137_malavapancama",), ), # hindolaka_five_bhasas + BD_6_1_R016 (says "at least 6": discrepant) "hindolaka": BhasaCount( primary=5, alternative=6, evidence_rules=("hindolaka_five_bhasas", "BD_6_1_R016"), ), } # Named bhāṣā lists per grāma-rāga — only those given by an explicit rule. # evidence per entry: rule_id named in the comment. BHASA_NAMES_BY_GRAMARAGA: dict[str, tuple[str, ...]] = { # R_114_malavakaisika_enumeration (aff#897, aff#892) "malavakaisika": ( "suddha", "ardhavesarika", "harsapuri", "mangali", "saindhavi", "abhiri", "khanjari", "gurjari", ), # R_137_malavapancama (aff#1225) "malavapancama": ( "vibhavini", "paurali", "vegavanti", "pancami", "andhri", "gandharika", ), # R_077_bhinnapancama_enumeration (aff#51, aff#904) "bhinnapancama": ( "suddhabhinna", "varati", "dhaivatabhusita", "visala", ), # hindolaka_five_bhasas "hindolaka": ( "vesari", "manjari", "chevati", "sadjamadhyama", "madhuri", ), # ṭakkarāga: R_117_takkaraga_bhasa_count names some bhāṣās and some # vibhāṣās ("potā, Devālavardhanī, paurālī, Trāvaṇī, dohyā") — the rule # mixes the two tiers, so we do NOT encode a closed bhāṣā name list for # takkaraga. See UNRESOLVED. # bhinnaṣaḍja: R_c32_bhinnasadja mentions a set including non-bhāṣā items # (Suddha, Madhyama) — kept out for tier-purity reasons. See UNRESOLVED. } # Total bhāṣā + vibhāṣā count across all grāma-rāgas — Brihaddesi-wide constant. # evidence: aff#883 ('bhāṣās and vibhāṣās have been said to be seventy-three # in total'), R_073_bhasas_derivation body ('COUNT(bhasas + vibhasas) = 73') TOTAL_BHASA_PLUS_VIBHASA: int = 73 # ----------------------------------------------------------------------------- # Structural triples (aṁśa, nyāsa, scale_type) per rāga/bhāṣā # ----------------------------------------------------------------------------- # A rāga or bhāṣā is structurally pinned by, at minimum, its aṁśa, its # nyāsa, and the density of its svara set. Other features (weak svaras, # specific saṁcāra patterns, rasa) are deferred — see structural_features. @dataclass(frozen=True) class RagaStructure: """Static structural fingerprint of a rāga or bhāṣā / vibhāṣā. evidence: R_15_bhasa_definition (bhāṣā characterized by aṁśa, nyāsa, structural relationships), graha_amsa_nyasa_svara_functions (these are svara-functions via samavāya). """ name: str tier: BhasaTier | None # None = top-level grāma-rāga parent: str | None # parent rāga or bhāṣā amsa: Svara | None nyasa: Svara | None scale: ScaleType | None weak: frozenset[Svara] = frozenset() omitted: frozenset[Svara] = frozenset() evidence: tuple[str, ...] = () # rule_ids and/or aff#NNN notes: str = "" # Structures pinned by a 6b rule. Each entry cites at least one rule_id. KNOWN_STRUCTURES: dict[str, RagaStructure] = { # === GRĀMA-RĀGAS === "takkaraga": RagaStructure( name="takkaraga", tier=None, parent=None, amsa=Svara.MA, nyasa=Svara.SA, scale=None, weak=frozenset({Svara.NI, Svara.PA}), omitted=frozenset({Svara.PA}), # 'devoid of pañcama' aff#916 evidence=("R_398_takkaraga_features", "takkaraga_graha_amsa_nyasa", "takkaraga_weak_svaras"), notes="parent_jatis=dhaivatī+ṣaḍjamadhyamā; rasa=yuddhavīra/vīra/adbhuta", ), # === BHĀṢĀS === "pinjari": RagaStructure( name="pinjari", tier=BhasaTier.VIBHASA, parent="hindola", amsa=Svara.GA, nyasa=Svara.SA, scale=ScaleType.SADAVA, omitted=frozenset({Svara.NI}), evidence=("R_5p2_607", "pinjari_vibhasha_amsa_nyasa"), ), "drāvidī": RagaStructure( name="drāvidī", tier=BhasaTier.BHASA, parent="takkakaisika", amsa=Svara.PA, nyasa=Svara.SA, scale=None, evidence=("R_252_dravidi", "BD_6_1_R025", "R_1040_dravidi_pancama_amsa"), notes="third bhāṣā born of ṭakkakaiśika; deśa = drāviḍa", ), "bhinnapaurali": RagaStructure( name="bhinnapaurali", tier=BhasaTier.BHASA, parent="hindola", amsa=Svara.MA, nyasa=Svara.SA, scale=ScaleType.SAMPURNA, evidence=("BD_6_1_R022", "R_1039_bhinnapaurali_madhyama_amsa"), ), "paurali": RagaStructure( name="paurali", tier=BhasaTier.VIBHASA, parent="takkaraga", amsa=Svara.MA, nyasa=Svara.DHA, scale=None, weak=frozenset({Svara.RI}), evidence=("R_996_paurali", "paurali_definition_and_origin"), notes="dense mutual movement ma-ri-pa", ), "varati": RagaStructure( name="varati", tier=BhasaTier.BHASA, parent="bhinnapancama", amsa=Svara.MA, nyasa=Svara.DHA, scale=ScaleType.SADAVA, weak=frozenset({Svara.RI}), evidence=("R_1006_varati_struct",), ), "visala": RagaStructure( name="visala", tier=BhasaTier.BHASA, parent="bhinnapancama", amsa=Svara.PA, nyasa=Svara.DHA, scale=ScaleType.SAMPURNA, evidence=("R_4p0_024", "R_077_bhinnapancama_enumeration"), ), "abhiri": RagaStructure( name="abhiri", tier=BhasaTier.BHASA, parent="malavakaisika", amsa=Svara.PA, nyasa=Svara.PA, scale=ScaleType.SAMPURNA, evidence=("abhiri_first_bhasa_from_pancama", "R_114_malavakaisika_enumeration"), notes="replete with niṣāda; two structural variants reported", ), "pota": RagaStructure( name="pota", tier=BhasaTier.VIBHASA, parent="takkaraga", amsa=Svara.PA, nyasa=Svara.SA, scale=None, weak=frozenset({Svara.NI}), evidence=("pota_definition_bhasa",), notes="ornamented by madhyama; starts on ṛṣabha, ends on dha", ), "ardhavesari": RagaStructure( name="ardhavesari", tier=BhasaTier.BHASA, parent="malavakaisika", amsa=Svara.MA, nyasa=Svara.SA, scale=ScaleType.SAMPURNA, weak=frozenset({Svara.NI}), evidence=("r_brd_413_ardhavesari_structure", "R_114_malavakaisika_enumeration"), ), "ravicandrika": RagaStructure( name="ravicandrika", tier=BhasaTier.BHASA, parent=None, # see notes amsa=Svara.GA, nyasa=Svara.SA, scale=None, weak=frozenset({Svara.NI}), evidence=("ravicandrika_structure",), notes="classified saṅkīrṇā; parent rāga not pinned in 6b", ), "vibhavani": RagaStructure( name="vibhavani", tier=BhasaTier.VIBHASA, parent="malavapancama", amsa=Svara.PA, nyasa=Svara.PA, scale=ScaleType.SAMPURNA, evidence=("R_1339_vibhavani_structure", "R_433_vibhavini_struct"), ), "bhavini": RagaStructure( name="bhavini", tier=BhasaTier.VIBHASA, parent="malavapancama", amsa=Svara.GA, nyasa=Svara.PA, scale=None, weak=frozenset({Svara.DHA}), evidence=("R_c193_bhavini", "R_1338_bhavani_vibhasa"), ), "kalingi": RagaStructure( name="kalingi", tier=BhasaTier.ANTARA_BHASA, parent=None, amsa=Svara.GA, nyasa=Svara.DHA, scale=ScaleType.AUDUVA, omitted=frozenset({Svara.PA, Svara.RI}), evidence=("kalingi_structure", "BD_6_1_R020"), notes="sung by people of Kalinga (deśajā)", ), "daksinatya": RagaStructure( name="daksinatya", tier=BhasaTier.VIBHASA, parent="bhinnasadja", amsa=Svara.PA, nyasa=Svara.PA, scale=None, evidence=("R_424_dakshinatya",), ), "dohya": RagaStructure( name="dohya", tier=BhasaTier.BHASA, parent="takkaraga", amsa=Svara.GA, nyasa=Svara.SA, scale=ScaleType.AUDUVA, omitted=frozenset({Svara.PA, Svara.RI}), evidence=("BD_6_1_R004",), ), "travani": RagaStructure( name="travani", tier=BhasaTier.BHASA, parent="takkaraga", amsa=Svara.DHA, nyasa=Svara.SA, scale=ScaleType.AUDUVA, omitted=frozenset({Svara.PA, Svara.RI}), evidence=("travani_takka_pentatonic",), ), "madhuri": RagaStructure( name="madhuri", tier=BhasaTier.BHASA, parent="hindolaka", amsa=Svara.MA, nyasa=Svara.SA, scale=None, weak=frozenset({Svara.PA}), evidence=("madhuri_bhasha_features", "hindolaka_five_bhasas"), ), "chevati": RagaStructure( name="chevati", tier=BhasaTier.BHASA, parent="hindolaka", amsa=Svara.SA, nyasa=Svara.SA, scale=ScaleType.SAMPURNA, evidence=("chevati_mulabhasha_structural", "R_400_chevati", "hindolaka_five_bhasas"), notes="classified mūlabhāṣā (root)", ), "tanavalitika": RagaStructure( name="tanavalitika", tier=BhasaTier.VIBHASA, parent="takkaraga", amsa=Svara.MA, nyasa=Svara.SA, scale=None, evidence=("tanavalitika_amsa_nyasa_and_vibhasa",), ), "devalavardhani": RagaStructure( name="devalavardhani", tier=BhasaTier.BHASA, parent="takkaraga", amsa=Svara.PA, nyasa=Svara.SA, scale=ScaleType.SAMPURNA, evidence=("R_5p2_1032",), ), "madhyamagramika": RagaStructure( name="madhyamagramika", tier=BhasaTier.BHASA, parent="takkaraga", amsa=Svara.MA, nyasa=Svara.SA, scale=ScaleType.SAMPURNA, evidence=("R_4p1_955", "R_956_01"), notes="always saṅkīrṇā (R_4p1_955)", ), } # ----------------------------------------------------------------------------- # Bhāṣā / vibhāṣā / antara-bhāṣā → subtype (mūlā / saṅkīrṇā / deśajā / chāyā) # ----------------------------------------------------------------------------- # Only entries with explicit Brihaddesi assignments. Many bhāṣās have no # subtype assigned in the source. BHASA_SUBTYPES: dict[str, BhasaSubtype] = { # mūlā (root) — chevāṭī, pañcamī, ṣaḍja-bhāṣā, gāndhārī, sauvīrī # evidence: chevati_mulabhasha_structural, R_mulabhasha_class, # sauvira_raga_root_of_sauviri_mulabhasha, # mulabhasha_bhinnashadja_narada "chevati": BhasaSubtype.MULA, "pancami": BhasaSubtype.MULA, # R_mulabhasha_class "sadjabhasha": BhasaSubtype.MULA, # R_mulabhasha_class "gandhari": BhasaSubtype.MULA, # R_mulabhasha_class "sauviri": BhasaSubtype.MULA, # sauvira_raga_root_of_sauviri_mulabhasha # saṅkīrṇā (mixed) # evidence: R_4p1_955 (madhyamagrāmikā always saṅkīrṇā), # ravicandrika_structure, R_5p2_966 (Khañjanī saṅkīrṇā) "madhyamagramika": BhasaSubtype.SANKIRNA, "ravicandrika": BhasaSubtype.SANKIRNA, "khanjani": BhasaSubtype.SANKIRNA, # deśajā (regional) # evidence: paurali_definition_and_origin (deśī-born of ṭakkarāga), # desi_saindhavi_regional_designation, # kalingi_structure (sung by people of Kalinga) "paurali": BhasaSubtype.DESAJA, "saindhavi": BhasaSubtype.DESAJA, "kalingi": BhasaSubtype.DESAJA, # chāyāmātrāśrayā: named in the four-fold scheme (aff#874) but no # individual bhāṣā is assigned to this category by an isolated rule. # See UNRESOLVED. } # ----------------------------------------------------------------------------- # Antara-bhāṣā registry — antara-bhāṣās are explicitly identified # ----------------------------------------------------------------------------- # evidence: BD_6_1_R020 (Kālingī among antara-bhāṣās), # antara_bhasha_distinct_type (Niṣādavatikā), # R_5p2_921 (derivation from vibhāṣās). ANTARA_BHASA_NAMES: tuple[str, ...] = ( "kalingi", "nisadavatika", ) # ============================================================================= # TYPES — dataclasses # ============================================================================= @dataclass(frozen=True) class GrāmaRāga: """A grāma-rāga — top-level rāga associated to a grāma + a gīti class, born of one or more jātis (R_266_gramaraga). evidence: R_gramaragas_def, R_266_gramaraga, grama_raga_origin_and_use, R_073_bhasas_derivation (bhāṣās born of grāma-rāgas) """ name: str grāma: Grāma | None # may be unsourced for some rāgas giti: GītiClass parent_jatis: tuple[str, ...] = () bhasa_count: BhasaCount | None = None structure: RagaStructure | None = None @dataclass(frozen=True) class Bhāṣā: """A bhāṣā / vibhāṣā / antara-bhāṣā — a derived melodic form located inside a grāma-rāga. evidence: R_15_bhasa_definition, R_073_bhasas_derivation, R_920_vibhasa_from_bhasa, R_5p2_921 """ name: str tier: BhasaTier parent_raga_or_bhasa: str # parent grāma-rāga (if bhāṣā) or bhāṣā (if vibhāṣā) subtype: BhasaSubtype | None = None structure: RagaStructure | None = None # ============================================================================= # OPÉRATIONS — interrogations exécutables # ============================================================================= def grama_raga_category(name: str) -> GītiClass: """Return the gīti class (= grāma-rāga category) for a named rāga. evidence: GRAMARAGA_GITI dict (each entry sourced) """ if name not in GRAMARAGA_GITI: raise KeyError( f"{name!r} not in GRAMARAGA_GITI — no Brihaddesi rule pins " f"its gīti class. See SUDDHA_RAGAS / BHINNA_RAGAS / " f"SADHARANA_RAGAS / VESARA_RAGAS." ) return GRAMARAGA_GITI[name] def parent_grama_raga_of(bhasha_name: str) -> str | None: """Return the parent grāma-rāga of a named bhāṣā / vibhāṣā. Walks up the tier: bhāṣā → grāma-rāga (one hop), vibhāṣā → bhāṣā → grāma-rāga (two hops), antara-bhāṣā → vibhāṣā → bhāṣā → grāma-rāga (three). Returns None if the chain is not sourced. evidence: R_073_bhasas_derivation (bhāṣā←grāma-rāga), R_920_vibhasa_from_bhasa (vibhāṣā←bhāṣā), R_5p2_921 (antara-bhāṣā←vibhāṣā) """ s = KNOWN_STRUCTURES.get(bhasha_name) if s is None or s.parent is None: # Try the bhāṣā-name lists by-grāma-rāga for raga, names in BHASA_NAMES_BY_GRAMARAGA.items(): if bhasha_name in names: return raga return None # Walk up visited: set[str] = set() cur = s.parent while cur and cur not in visited: visited.add(cur) if cur in GRAMARAGA_GITI: return cur # we reached a top-level grāma-rāga nxt = KNOWN_STRUCTURES.get(cur) if nxt is None or nxt.parent is None: # Fallback: lookup in BHASA_NAMES_BY_GRAMARAGA for raga, names in BHASA_NAMES_BY_GRAMARAGA.items(): if cur in names: return raga return None cur = nxt.parent return None def bhasas_of(grama_raga: str) -> tuple[str, ...]: """Return the closed bhāṣā list of a grāma-rāga (if sourced). evidence: BHASA_NAMES_BY_GRAMARAGA dict (each entry cites a rule_id) Returns an empty tuple if no sourced list exists. """ return BHASA_NAMES_BY_GRAMARAGA.get(grama_raga, ()) def expected_bhasa_count(grama_raga: str) -> BhasaCount | None: """Expected bhāṣā count (primary + optional alternative) for a rāga. evidence: BHASA_COUNTS dict """ return BHASA_COUNTS.get(grama_raga) def vesara_ragas_of(grama: Grāma) -> tuple[str, ...]: """Return the vesara rāgas enumerated for a grāma. evidence: R_389_vesara_def_enum """ return VESARA_RAGAS[grama] def structural_features(name: str) -> RagaStructure | None: """Return the sourced structural fingerprint of a rāga / bhāṣā. evidence: KNOWN_STRUCTURES (each entry cites its rule_ids) """ return KNOWN_STRUCTURES.get(name) # ============================================================================= # CONTRAINTES — validations # ============================================================================= def is_valid_bhasa_assignment( bhasha_name: str, claimed_parent: str ) -> bool: """True iff `claimed_parent` is a sourced parent of `bhasha_name`. Checks the BHASA_NAMES_BY_GRAMARAGA registry first (closed enumerations), then falls back to KNOWN_STRUCTURES.parent. evidence: R_073_bhasas_derivation, R_920_vibhasa_from_bhasa, R_5p2_921, BHASA_NAMES_BY_GRAMARAGA entries (each rule_id'd) """ if claimed_parent in BHASA_NAMES_BY_GRAMARAGA: if bhasha_name in BHASA_NAMES_BY_GRAMARAGA[claimed_parent]: return True s = KNOWN_STRUCTURES.get(bhasha_name) if s is not None and s.parent == claimed_parent: return True return False def is_valid_bhasa_count(grama_raga: str, count: int) -> bool: """True iff `count` matches the sourced primary or alternative bhāṣā count for `grama_raga`. True (under-constrained) if no count is sourced. evidence: BHASA_COUNTS dict (R_398, R_117, R_954, R_c32, R_932, R_077, R_114, R_922, R_137, hindolaka_five_bhasas, BD_6_1_R016) """ bc = BHASA_COUNTS.get(grama_raga) if bc is None: return True if count == bc.primary: return True return bc.alternative is not None and count == bc.alternative def is_valid_total_bhasa_vibhasa(total: int) -> bool: """True iff `total` == 73 — the Brihaddesi-wide bhāṣā+vibhāṣā count. evidence: aff#883, R_073_bhasas_derivation body """ return total == TOTAL_BHASA_PLUS_VIBHASA def is_valid_tier_chain(child_tier: BhasaTier, parent_tier: BhasaTier | None) -> bool: """Check the tier hierarchy is respected: ANTARA_BHASA ← VIBHASA ← BHASA ← (grāma-rāga, parent_tier=None). evidence: R_073_bhasas_derivation, R_920_vibhasa_from_bhasa, R_5p2_921 """ if child_tier is BhasaTier.BHASA: return parent_tier is None if child_tier is BhasaTier.VIBHASA: return parent_tier is BhasaTier.BHASA or parent_tier is None # R_4p0_001: vibhāṣā derived from a rāga (= grāma-rāga); some # vibhāṣās in the corpus attach directly to a grāma-rāga rather # than via a bhāṣā (e.g. potā ← ṭakkarāga). Both forms accepted. if child_tier is BhasaTier.ANTARA_BHASA: return parent_tier is BhasaTier.VIBHASA return False # ============================================================================= # UNRESOLVED — concepts mentioned but not fully pinned by 6b rules # ============================================================================= UNRESOLVED: tuple[dict[str, str], ...] = ( { "concept": "gauḍa-rāga enumeration", "reason": "R_1275_gaudaragas_category names the category but no 6b " "rule encloses a finite list. Individual gauḍa rāgas are " "not enumerated here.", }, { "concept": "sādhāraṇa count discrepancy", "reason": "Body verse (aff#589) gives 7 rāgas; TOC (aff#43) lists " "10 including Bhammāṇapañcama, Pañcamaṣāḍava, Revagupta. " "Encoded the 7-count list (per verse) — TOC variants kept " "in GRAMARAGA_GRAMA cross-reference only.", }, { "concept": "ṭakkarāga bhāṣā name list", "reason": "R_117_takkaraga_bhasa_count mixes bhāṣā and vibhāṣā names " "without distinguishing tiers; primary count is 12 vs. 16 " "(Yāṣṭika, R_954). No closed bhāṣā list encoded.", }, { "concept": "bhinnaṣaḍja bhāṣā name list", "reason": "R_c32_bhinnasadja gives 9-15 names blended with non-" "bhāṣā items (Suddha, Madhyama). Not tier-pure; no closed " "bhāṣā list encoded. R_932_bhinnasadja_enum confirms 9.", }, { "concept": "chāyāmātrāśrayā instances", "reason": "Named as 4th bhāṣā subtype (aff#874) but no individual " "bhāṣā is assigned to it by an isolated rule.", }, { "concept": "shuddhakaisikamadhyama grāma assignment", "reason": "Disputed in source (cf. domain 3 UNRESOLVED). " "Excluded from GRAMARAGA_GRAMA.", }, { "concept": "Pārvatī parent rāga", "reason": "R_253_parvati_structural gives TWO variants — one in " "hindolaka, one born of bhinna-ṣaḍja. Not pinned.", }, { "concept": "saṅkīrṇā assignment criterion", "reason": "sankirna_criterion_unclear (explicit epistemic open " "issue in source).", }, { "concept": "antara-bhāṣā full registry", "reason": "Only Kālingī (BD_6_1_R020) and Niṣādavatikā " "(antara_bhasha_distinct_type) explicitly tagged. The " "tier exists but its members are not closed-enumerated.", }, { "concept": "ganaila classification (4 vs 5)", "reason": "R_1169_ganaila_fourfold and R_1219_ganaila_fivefold_" "sankara give two counts (4 / 4+saṅkara). Not encoded.", }, ) # ============================================================================= # Self-test (executed at import-time only if __main__) # ============================================================================= if __name__ == "__main__": # Sanity: each rāga in the 5+5+7+8 lists is in GRAMARAGA_GITI all_listed = ( set(SUDDHA_RAGAS) | set(BHINNA_RAGAS) | set(SADHARANA_RAGAS) | set(VESARA_RAGAS[Grāma.SADJA]) | set(VESARA_RAGAS[Grāma.MADHYAMA]) ) for r in all_listed: assert r in GRAMARAGA_GITI, f"{r} missing from GRAMARAGA_GITI" # Sanity: count consistency assert len(SUDDHA_RAGAS) == 5, "śuddha rāgas should be 5 (aff#578, aff#587)" assert len(BHINNA_RAGAS) == 5, "bhinna rāgas should be 5 (aff#578, aff#588)" assert len(SADHARANA_RAGAS) == 7, "sādhāraṇa rāgas should be 7 (aff#589, aff#841)" assert len(VESARA_RAGAS[Grāma.SADJA]) == 5, "5 ṣaḍjagrāma vesaras (aff#777)" assert len(VESARA_RAGAS[Grāma.MADHYAMA]) == 3, "3 madhyamagrāma vesaras (R_389)" # Sanity: GītiClass enumeration matches the 5-fold gīti of Durgaśakti (aff#542) assert {g.value for g in GītiClass} == { "suddha", "bhinna", "gauda", "vesara", "sadharana" } # Sanity: gīti category lookup assert grama_raga_category("takkaraga") is GītiClass.VESARA assert grama_raga_category("bhinnapancama") is GītiClass.BHINNA assert grama_raga_category("rupasadharita") is GītiClass.SADHARANA # Sanity: parent_grama_raga_of walks the hierarchy assert parent_grama_raga_of("abhiri") == "malavakaisika" # named list assert parent_grama_raga_of("vibhavani") == "malavapancama" # via KNOWN_STRUCTURES.parent assert parent_grama_raga_of("vegavanti") == "malavapancama" # closed name list assert parent_grama_raga_of("varati") == "bhinnapancama" # name list assert parent_grama_raga_of("dohya") == "takkaraga" # KNOWN_STRUCTURES assert parent_grama_raga_of("travani") == "takkaraga" # bhāṣā with no Brihaddesi assignment → None assert parent_grama_raga_of("ravicandrika") is None # Sanity: is_valid_bhasa_assignment assert is_valid_bhasa_assignment("madhuri", "hindolaka") assert is_valid_bhasa_assignment("varati", "bhinnapancama") assert is_valid_bhasa_assignment("dohya", "takkaraga") assert not is_valid_bhasa_assignment("madhuri", "takkaraga") # wrong parent assert not is_valid_bhasa_assignment("madhuri", "bhinnapancama") # Sanity: bhāṣā counts and their alternatives assert is_valid_bhasa_count("takkaraga", 12) assert is_valid_bhasa_count("takkaraga", 16) # Yāṣṭika alternative assert not is_valid_bhasa_count("takkaraga", 14) assert is_valid_bhasa_count("bhinnapancama", 4) assert not is_valid_bhasa_count("bhinnapancama", 5) assert is_valid_bhasa_count("malavapancama", 6) assert is_valid_bhasa_count("hindolaka", 5) assert is_valid_bhasa_count("hindolaka", 6) # BD_6_1_R016 alt assert is_valid_bhasa_count("unknown_raga", 99) # under-constrained → True # Sanity: global 73 count assert is_valid_total_bhasa_vibhasa(73) assert not is_valid_total_bhasa_vibhasa(72) # Sanity: tier chain assert is_valid_tier_chain(BhasaTier.BHASA, None) assert is_valid_tier_chain(BhasaTier.VIBHASA, BhasaTier.BHASA) assert is_valid_tier_chain(BhasaTier.VIBHASA, None) # direct vibhāṣā of rāga (R_4p0_001) assert is_valid_tier_chain(BhasaTier.ANTARA_BHASA, BhasaTier.VIBHASA) assert not is_valid_tier_chain(BhasaTier.BHASA, BhasaTier.BHASA) assert not is_valid_tier_chain(BhasaTier.ANTARA_BHASA, BhasaTier.BHASA) # Sanity: structural lookup s = structural_features("takkaraga") assert s is not None assert s.amsa is Svara.MA and s.nyasa is Svara.SA assert Svara.PA in s.omitted # 'devoid of pañcama' (aff#916) s2 = structural_features("kalingi") assert s2 is not None assert s2.tier is BhasaTier.ANTARA_BHASA assert s2.scale is ScaleType.AUDUVA # Sanity: vesara list per grāma assert "takkaraga" in vesara_ragas_of(Grāma.SADJA) assert "hindolaka" in vesara_ragas_of(Grāma.MADHYAMA) assert "hindolaka" not in vesara_ragas_of(Grāma.SADJA) # Sanity: subtype assignments assert BHASA_SUBTYPES["chevati"] is BhasaSubtype.MULA assert BHASA_SUBTYPES["madhyamagramika"] is BhasaSubtype.SANKIRNA assert BHASA_SUBTYPES["paurali"] is BhasaSubtype.DESAJA assert BHASA_SUBTYPES["kalingi"] is BhasaSubtype.DESAJA # Sanity: antara-bhāṣā registry minimally populated assert "kalingi" in ANTARA_BHASA_NAMES print("bhasha_raga_corpus.py — all self-tests pass")