CoolFace
Datasetpublic

Anamavajra-Labs/sanskrit-karaka-hypergraph

Sanskrit Kāraka Hypergraph A predication hypergraph over Sanskrit: vertices are lemma types, hyperedges are predications, and each tine carries a Pāṇinian kāraka role. Why a hypergraph rather than a graph of binary relations: a sentence is an n-ary predicate, and an n-ary relation does not survive projection onto its binary sub-relations. Given only the pairs agent–object, object–recipient and agent–recipient you can no longer tell whether there was one three-place act or three… See the full description on the dataset page: https://huggingface.co/datasets/Anamavajra-Labs/sanskrit-karaka-hypergraph.

sourceHugging Facecc-by-4.0updated 2mo agoView on Hugging Face
0likes65downloads
Dataset Card

Sanskrit Kāraka Hypergraph

A predication hypergraph over Sanskrit: vertices are lemma types, hyperedges are predications, and each tine carries a Pāṇinian kāraka role.

Why a hypergraph rather than a graph of binary relations: a sentence is an n-ary predicate, and an n-ary relation does not survive projection onto its binary sub-relations. Given only the pairs agent–object, object–recipient and agent–recipient you can no longer tell whether there was one three-place act or three unrelated pairs. A predication is therefore one hyperedge holding all of its arguments at once — which is also how Pāṇini's kāraka theory describes it: a verbal action with typed role-slots (kartṛ, karman, karaṇa, sampradāna, apādāna, adhikaraṇa).

Stored as a reified Levi graph so it can be queried in plain SQL and joined against an ordinary corpus table:

hg_node ──< hg_incidence >── hg_edge
 (lemma)      (role, case)     (predication)

Contents

filerowswhat it is
hg_node.parquet11,617vertices — lemma types, so one lemma from two sentences is one vertex
hg_edge.parquet51,752hyperedges — one per predication, with voice/pada and the sentence text
hg_incidence.parquet98,837tines — the role each vertex plays in each predication
hg_lemma_valency.parquet2,243transitivity and pada profile derived per root
hg_lemma_agentivity.parquet11,617how often a lemma occupies an unambiguous agent slot

Edge kinds: PREDICATION 39,919 (verbal), NOMINAL 10,004 (verbless/copular — pervasive in Sanskrit), KRDANTA 1,829 (a deverbal noun that keeps the verb's frame, e.g. praiṣa-kṛt "maker of the summons").

A kṛdanta earns its edge by keeping that frame, so a head that governs a verbal-looking dependent but ends up with neither karman nor kartṛ is not one: 120 such heads — priyaḥ "dear", bhojanam "food", sakhā "friend", namaḥ "homage" — are filed as nominal predications instead of asserting a frame they do not have.

Source

The DCS gold dependency treebank (Digital Corpus of Sanskrit, CC BY 4.0): 27,255 sentences / 206,889 tokens with gold HEAD/DEPREL and morphology. Nothing here is parsed — the arcs are the treebank's.

The material is Vedic and Upaniṣadic (ṚV, Aitareya-B., AVŚ, Śatapatha-B., BĀU, ChU … 57 works). It is not classical commentarial Sanskrit, and the frequencies do not transfer to it: classical commentary is passive- and compound-heavy, so the -ya- passive calibration here under-detects there, and compounds are folded into a single tine rather than analysed.

What makes this more than a case-to-role table

Vibhakti does not determine kāraka. The mapping is conditioned on the voice of the predicate, and the data show why: the karman appears in the accusative under kartari prayoga and in the nominative under karmaṇi.

voiceAccNom
active19,6992,245 (clausal)
passive58663
past participle112731
gerundive (kṛtya)14241

Reading the bare case would have mislabelled ~1,600 karman tines as agents.

Three prayogas, not two. Besides kartari and karmaṇi there is bhāve prayoga, the impersonal passive of an intransitive root: no karman at all, third singular, agent in the instrumental (tena gamyate).

pada ≠ prayoga. pada is the morphological voice of the ending (parasmaipada / ātmanepada), voice the prayoga that governs the roles. A middle form is still kartari — it just directs the fruit of the act to the agent (P. 1.3.72 kartrabhiprāye kriyāphale ātmanepadam). Since the passive borrows the ātmanepada endings wholesale, -te is systematically ambiguous (anumantrayate middle vs deśyate passive), and pada is read off the ending across all three numbers.

kāraka-vibhakti vs upapada-vibhakti. A case is evidence about a kāraka only when the verb governs it. Where a particle does, the role is not a kāraka: upapada (karmapravacanīya — anu, prati, adhi …, 633 tines), sahārtha (sahayukte 'pradhāne tṛtīyā, 510), upamāna (iva/yathā — the compared item takes the case of what it is compared to, so it mirrors a role without being one, 377). And where the treebank's own relation subtype states the role it outranks the bare case: obl:agent is the kartṛ even in the genitive, obl:benef the sampradāna and not śeṣa, obl:temp the adhikaraṇa and not a karman of duration, obl:manner a kriyāviśeṣaṇa and not — as the bare adverbial nominative would have it — an agent.

Role inventory as loaded: karman 30,620 · kartṛ 15,874 · kriyāviśeṣaṇa 14,689 · vidheya 10,004 · uddeśya 9,424 · karaṇa 4,959 · adhikaraṇa 4,769 · sampradāna 2,566 · sambodhana 2,068 · apādāna 1,425 · upapada 630 · sahārtha 509 · śeṣa 467 · upamāna 376 · unassigned 457. (These moved slightly from an earlier revision after a defect in coordination was fixed: a conjunct was given the head's role without checking that it stood in the same case, so ūrvaiḥ kāvyaiḥ — instrumental — were called agents under an active finite verb, which P. 3.4.69 forbids. Coordination in Sanskrit agrees in case.) The share flagged ambiguous runs from 2% (upamāna) to 29% (karman, where two accusatives leave mukhya and gauṇa undecided).

Arguments the grammar supplies. An absolutive or infinitive is samāna-kartṛka — its agent is by rule the agent of the governing predication; a participle predicates of the noun it modifies. Those tines are filled in and flagged inherited, so they can be excluded when counting surface dependents.

The instrumental defaults to the instrument, not to the agent. kartṛkaraṇayos tṛtīyā (P. 2.3.18) gives the third case to both, so the case decides nothing, and the reading that assumes nothing must win: calling an instrumental the elided agent asserts animacy. Promotion to kartṛ requires positive evidence from hg_lemma_agentivity (123 tines promoted, 106 confirmed as never-agents). Defaulting the other way had read vidyayā prasūtam "produced by knowledge", ślokena abhyuktam "expressed by a verse" and adbhiḥ praṇīyamānaḥ as agentive.

Every tine is auditable

rule records which mapping produced the label, ambiguous marks where the morphology genuinely underdetermines it (dvikarmaka accusatives, the instrumental under a passive, coordinated arguments inheriting a role), and voice_src / pada_src say how the voice and pada were decided. Animacy — annotated nowhere — is approximated by hg_lemma_agentivity, which counts how often a lemma stands as the nominative subject of an active finite verb that also has an object: an actor, not a stative subject. Both counts are kept (n_agent strict, n_subject loose), because the loose one lets stative subjects in — "knowledge is", "the verse says" — and that is how vidyā (2 agentive of 83 occurrences), daṇḍa (1/84) and udaka (2/78) came to be read as agents, while deva (254/997) and pitṛ (63/311) properly are.

Do not read `ambiguous` as a confidence score. It marks where the grammar is underdetermined, which measurably is not where the labeller is most often wrong.

Quality, stated honestly

  • —Coverage — of 172,829 content tokens, 73.9% are in the graph as a tine or head, 5.9% are folded into a tine (compound members), 15.9% are outside by design (argument-internal modifiers — a hyperedge is a valency frame, not the internal structure of its arguments), and 4.3% are unaccounted for.
  • —Per-element QA — qa_hypergraph.py runs 45 checks in SQL over every edge and every tine, not over a sample: referential integrity (including a recursive walk for cycles in the parent_id chain), the domain of every enumerated field, the invariants each edge kind must satisfy, cross-layer consistency, and whether every label is accompanied by the rule that produced it. Currently 0 failures and 2 quantified warnings. It found two defects that no aggregate statistic could show: arity was one short on all 9,884 nominal predications (the head's own tine was appended outside the list the count came from), and 125 predications carried two agents, arriving through the bare-case fallback on relations that are not arguments of the predicate at all — parataxis, dislocated, orphan, acl:*. One kriyā has one kartṛ, so the core relation now wins and the loser is left unassigned with the reason recorded. Several tines under the same relation are exempt: Vedic coordinates asyndetically, so manīṣibhiḥ somaḥ viprebhiḥ ṛkvabhiḥ is three obl:agent tines with no conj between them, and they are coordinate agents rather than rivals.
  • —Consistency — audit_grammar.py enumerates the grammatical space cell by cell (three numbers × three persons × two padas, three prayogas, six kārakas, eight vibhaktis, derivational stems, grammatically-guaranteed arguments) and cross-validates the derived pada against the P./Ā. markers in Monier-Williams: 21 checks, all passing.
  • —Accuracy — 62% agreement with an independent LLM labelling on 166 tines drawn in equal share from the 17 riskiest rule families, up from 51% over two rounds of fixes. The control stratum of plain active core arguments sits at 80%, so 62% is a worst-case figure and not a corpus-wide accuracy. No human gold set exists yet; where the proxy insists on an ordinary kāraka for a particle-governed case (upapada, sahārtha, upamāna) the disagreement is one of inventory, not demonstrably an error.
  • —The `ambiguous` flag is not a confidence score. In the first evaluation round it did not predict disagreement at all (52% agreement on flagged tines against 51% on unflagged); it now separates slightly but upwards, because it increasingly marks tines that a specific rule resolved. Use it to filter, never to triage.

Not modelled, deliberately

hetu (the instrumental and ablative of cause are folded into karaṇa and apādāna — this treebank has no cause relation to key on), karman subtypes (nirvartya/vikārya/prāpya), and kārakas inside a compound: samāsa members are folded into one tine, harmless for Vedic and the largest gap for classical prose.

How the checks are checked

A suite that has never failed on known-bad data is untested, so mutation_test.py plants sixteen known defects one at a time, runs the whole of QA, and records which check fires. Every mutation runs inside a transaction rolled back in a finally; nothing is ever committed. Current result: 13 caught by QA, 3 refused outright by the schema (foreign keys and NOT NULL — a stronger guarantee than any after-the-fact check), 0 undetected.

The first run had 2 undetected, and both were rules the builder enforced while QA merely assumed. Writing the missing invariant immediately exposed two real defects in the live graph — the coordination case-agreement bug noted above. That is the argument for the exercise: the tool that finds nothing may be finding nothing because it cannot look.

What is not in this export

Two things sit in the same tables and are excluded by kind:

The exegetical layer — 202 GLOSS and 119 TOPIC hyperedges over recorded lectures, carrying verbatim quotation text and lecture identities under different rights.

Lemma vertices minted from the DCS inventory (962 of them, lemma_source = 'dcs'). They exist so a term glossed in a lecture can reach a canonical lemma even where the Vedic gold subset never used the word — svaccha is absent from the treebank entirely, which is why every fuzzy matcher "found" it as something else. They carry no kāraka edges, so in a kāraka dataset they would be 962 vertices a consumer cannot use. All 11,617 vertices here are lemma_source = 'treebank'.

Two lessons from that inventory work are worth stating, since both are matching rules a reader might otherwise repeat. Matching must preserve every diacritic: an ASCII fold buys about 600 extra joins and pays for them in wrong ones — saṁsāra onto saṃśara (ś collapsed into s), astra onto aṣṭrā, Gītā onto gīta (feminine into masculine). The marks are phonemic. Only case, spacing and ṁ/ṃ — two notations for one anusvāra — may be normalised. And phonetic matching cannot reach a lemma at all: the key that serves quotation alignment collapses śiva onto fifteen candidates including śava and sabhā.

Higher-order structure

to_xgi.py reads the graph into XGI — the Levi construction in the tables is already what a hypergraph library wants, so it is a read rather than a transformation. Measured over both layers:

layeredgesmean sizecomponentsgiantcost of pairwise decomposition
A (kārakas)45,7882.1271791%×1.9
B (exegesis)32165.901100%×5,191
both46,1092.5769195%×38

The n-ary representation earns its keep in layer B, not here: decomposing the exegetical layer into pairs costs 1,666,307 edges in place of 321, and with them the fact each edge asserts. Layer A's edges are mostly two or three tines, so pairs would cost only ×1.9 — what matters in this dataset is not the arity but the roles, which XGI does not carry and which are kept as edge attributes.

Note also that an XGI hyperedge is a set: it cannot distinguish kartṛ from karman inside one edge. Every measure above is therefore a measure of the unlabelled hypergraph — a statement about shape, not about grammar.

Columns that are always null here

hg_edge carries five columns belonging to a second layer of the same graph — an exegetical layer over recorded Sanskrit lectures, which is not distributed with this dataset because it contains verbatim quotation text and lecture identities under different rights. The columns are part of the shared schema, so they appear in the parquet, empty:

columnbelongs to
start_secthe audio offset of a recited quotation
coverage, jaccardthe quotation-to-edition alignment scores
topic_agreeswhether the passage a lecture is about names the same āhnika as the passage it quoted

Every row in this export has kind in PREDICATION / NOMINAL / KRDANTA. The exporter reads the parquet back after writing and aborts if a row of any other kind reaches it, so this is enforced rather than intended.

Reproducing

Scripts live in `Anamavajra-Labs/tantraloka-pipeline` under scripts/karaka/. build_karaka.py --load rebuilds the whole graph from the treebank in about five seconds with no model calls; audit_grammar.py must stay at 21/21 after any rule change, and qa_hypergraph.py at 59/59 — it checks both layers, including that this export never carries a row from the other one.

The exp_*.py scripts are kept for their negative results, so the measurements are not repeated: trigram pruning speeds matching 28× and destroys it (frequent trigrams are pieces of the words, not separable stopwords), and reference-window size has to be crossed with the acceptance thresholds rather than tuned after them.

Citation

The graph is derived work; cite the treebank as well:

bibtex
@misc{sanskrit_karaka_hypergraph,
  title  = {Sanskrit Kāraka Hypergraph},
  author = {Anamavajra Labs},
  year   = {2026},
  url    = {https://huggingface.co/datasets/Anamavajra-Labs/sanskrit-karaka-hypergraph}
}
@inproceedings{hellwig_dcs,
  title  = {The Digital Corpus of Sanskrit},
  author = {Hellwig, Oliver},
  note   = {http://www.sanskrit-linguistics.org/dcs/}
}