"""AksharaLP v3 agreement / grammar-consistency features (identical rules in release/space/js/aksharalp.mjs). Scored incrementally when word i is appended to a hypothesis; returns (pos, neg) counts: 1. participle -> auxiliary agreement (gender/number): रहा था / रही थी / रहे थे ok; रहा थे / रहे हूँ / रही था violations. 2. subject pronoun -> auxiliary person/number in the same clause: मैं..हूँ, हम..हैं, तुम..हो, आप..हैं ok; मैं..है, तुम..हूँ violations. Only uses the words already chosen from per-token candidate lists; it never adds words.""" AUX = {"था": "ms", "थी": "fs", "थे": "mp", "थीं": "fp", "हूँ": "1s", "हूं": "1s", "है": "3s", "हैं": "pl", "हो": "2p"} PART = {} for w, g in (("रहा", "ms"), ("रही", "fs"), ("रहे", "mp"), ("चुका", "ms"), ("चुकी", "fs"), ("चुके", "mp"), ("सका", "ms"), ("सकी", "fs"), ("सके", "mp"), ("गया", "ms"), ("गई", "fs"), ("गयी", "fs"), ("गए", "mp"), ("गये", "mp"), ("हुआ", "ms"), ("हुई", "fs"), ("हुए", "mp"), ("लिया", "ms"), ("ली", "fs"), ("लिए", "mp"), ("लिये", "mp"), ("दिया", "ms"), ("दी", "fs"), ("दिए", "mp"), ("दिये", "mp"), ("किया", "ms"), ("की", "fs"), ("किए", "mp"), ("किये", "mp"), ("आया", "ms"), ("आई", "fs"), ("आए", "mp"), ("आये", "mp")): PART[w] = g SUBJ = {"मैं": "1s", "मै": "1s", "हम": "pl", "तुम": "2p", "आप": "hon", "तू": "2s"} BOUND = {"कि", "और", "पर", "लेकिन", "तो", "जब", "क्योंकि", "अगर", "या", "मगर", "जो", ",", ".", "?", "!", "।"} # participle class x auxiliary class -> violation? BAD_PA = {("ms", "fs"), ("ms", "mp"), ("ms", "fp"), ("ms", "pl"), ("fs", "ms"), ("fs", "mp"), ("mp", "ms"), ("mp", "fs"), ("mp", "1s"), ("mp", "3s")} # subject x auxiliary -> +1 ok / -1 violation (absent = neutral) SA = {("1s", "1s"): 1, ("1s", "3s"): -1, ("1s", "pl"): -1, ("1s", "2p"): -1, ("pl", "pl"): 1, ("pl", "1s"): -1, ("pl", "3s"): -1, ("2p", "2p"): 1, ("2p", "1s"): -1, ("2p", "3s"): -1, ("hon", "pl"): 1, ("hon", "1s"): -1, ("hon", "3s"): -1, ("2s", "3s"): 1, ("2s", "1s"): -1} def _part_class(w): g = PART.get(w) if g: return g if len(w) > 2 and w.endswith("ता"): return "ms" if len(w) > 2 and w.endswith("ती"): return "fs" if len(w) > 2 and w.endswith("ते"): return "mp" return None def agree_feats(words, i): """contribution of words[i] given words[:i]; i=None -> 0 (no end-of-sentence term).""" if i is None or i <= 0: return 0, 0 w = words[i]; a = AUX.get(w) if a is None: return 0, 0 pos = neg = 0 pc = _part_class(words[i - 1]) if pc is not None: if (pc, a) in BAD_PA: neg += 1 else: pos += 1 for j in range(i - 1, max(-1, i - 9), -1): # nearest subject pronoun in the clause u = words[j] if u in BOUND: break s = SUBJ.get(u) if s: v = SA.get((s, a), 0); pos += v > 0; neg += v < 0 break return pos, neg def agree_score(words, i, wp=1.0, wn=1.0): p, n = agree_feats(words, i); return wp * p - wn * n