#! /usr/bin/python3 # Last edited on 2026-06-04 06:17:31 by stolfi # Alternative matching sections for the report -- using # a single bitemplate with penalties for non-standard keywords. import sys, re, string from sys import stderr as err import html_gen as h import html_report_funcs as hr import bitemplate_match_funcs as bmf import analyze_starps_parag_funcs as anf import bimatching_eval_funcs as bef from math import sqrt, hypot, exp, log, floor, ceil, isfinite, isnan, inf, nan def compare_bencao_entry_to_starps_parags \ (code_ch, loc_ch, tvar_ch, cleantx_ch, bitemp, max_score, locs_to_try): # # Arguments: # # {code_ch} four-letter code of the SBJ entry. # {loc_ch} locus ID of theSBJ entry in the CTP file. # {tvar_ch} tag that identifies the variant of that entry (trimming etc). # {cleantx_ch} text of that entry, suitably cleaned and trimmed. # {bitemp} list of EVA RE pattern(s) hopefully matching the keywords in EVA text. # {max-score} max acceptable badness score. # {locs_to_try} set of loc IDs of SPS parags to consider, or {None} for all. # # The parameter {bitemp} must be a bitemplate suitable for # {bmf.match_bitemplate} # # Let {nh} be the number of pattern codes in {kwords}. Let {ng} be # {nh+1}, and {ns} be {ng + nh}. The procedure calls # {anf.evaluate_starps_parags} with arguments {ivt_file}, {locs_to_try}, # {nh}, and {match_func} as the eval function, to scan the # file of good SPS parags {ivt_file} and evaluate each parag for how # well it matches the SBJ. # # For each parag considered, the {match_func} procedure creates a version # {cleantx_ec} of its text, that has only lowercase EVA letters # [a-z?]. It then uses {bmf.match_bitemplate} with the specified {bitemp} # to fing the best bimatching (pair of corresponding macro-parsing) # for {cleantx_ch} and {cleantx_ec}. # # The result of this procedure is a list {parevs} of parag evaluation # tuples (/parevs/) and a dictionary {data} with various counts of the # operation. There will be a parev for each parag that may possibly # match. Parevs with score exceeding {max_score} are discarded. debug = False # Expected counts: nr = len(bitemp); # Rungs in bimatching. nh = nr # Hits. ng = nh + 1; # Gaps. ns = ng + nh # Segments. def match_func(cleantx_ec): # SBJ-SPS entry matching function. # # The function splits the hanzi text {cleantx_ch} and the EVA text {cleantx_ec} # as specified by the bitemplate {bitemp}, using # {tmf.match_bitemplate}, obtaining thwo macro-parsings # for them, {segs_ch[0..ns-1]} and {segs_ec[0..ns-1]}. # # The function then computes a badness {score} for the parag as a # candidate for the SPS version of the SBJ entry. The sizes of the # gaps (even-indexed elements) in the SPS parag macro-parsing # {segs_ec[0..ns-1]} are compared with the sizes of the # corresponding gaps in the macro-parsing {segs_ch[0..ns-1]} of # the SBJ entry, individually and as total, taking into account the # average hanzi-to-EVA conversion factor and roundoff errors due to # the sizes being integers. # # The returned result is the {score} and the macro-parsings # {segs_ch[0..ns-1]}, {segs_ec[0..ns-1]}. def eval_one_gap(gsize_ch, gsize_ec, ig, ng): debug = False if debug: err.write("!E {gsize_ch = !r} {gsize_ec = !r}\n") gap_score = bef.compute_single_gap_score(gsize_ch, "ch", gsize_ec, "ec", ig, ng) if debug: err.write(f"!E {gap_score = :.3f}\n") return gap_score # .................................................................. # Tries to match {cleantx_ch} with {cleantx_ec} as per bitemplate {bitemp}: debug_level = 0 rungs, score = bmf.match_bitemplate \ ( cleantx_ch, cleantx_ec, bitemp, eval_one_gap, max_score, debug_level ) if rungs == None: assert score == +inf segs_ch = None; segs_ec = None; key_penalty = None else: assert len(rungs) == nr; assert isfinite(score) and score >= 0 # Extract the segments defined by the rungs: segs_ch, segs_ec, key_penalty = bmf.extract_segments(cleantx_ch, cleantx_ec, rungs) assert len(segs_ch) == ns and len(segs_ec) == ns; if debug: err.write(f"!e {ns = } {key_penalty = :.3f}\n") mpars = ((segs_ch, '?', ' ',), (segs_ec, '?', ' ',),) wpf.write_parsings(err, "!e", True, mpars) assert len(segs_ec) == ns; if segs_ec != None: assert len(segs_ec) == ns return score, loc_ch, tvar_ch, segs_ch, segs_ec, key_penalty # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ivt_file = f"res/starps-gd-ec-par.ivt" parevs, data = anf.evaluate_starps_parags(ivt_file, locs_to_try, nh, match_func) return parevs, data # ---------------------------------------------------------------------- def add_bitemplate_description(st, kwords_en, bitemp): nr = len(bitemp) assert len(kwords_en) == nr bars = '||' rows = [] for ir in range(nr): if ir > 0: row = tuple([ " " ]*7) rows.append(row) bipat = bitemp[ir] nv = len(bipat) for iv in range(nv): pvar = bipat[iv] pena, pat_ch, pat_ec = pvar row = ( \ (kwords_en[ir] if iv == 0 else ""), bars, f"{pena:.3f}", bars, pat_ch.ljust(3, ' '), bars, pat_ec ) rows.append(row) col_mods = [ "style='padding-left:4ch; padding-right:2ch; text-align:left;'", "style='padding-left:1ch; padding-right:1ch;'", "style='text-align:right;white-space:nowrap;'", "style='padding-left:1ch; padding-right:1ch;'", "style='text-align:left;white-space:nowrap;'", "style='padding-left:1ch; padding-right:1ch;'", "style='text-align:left;white-space:nowrap;'", ] h.table(st, rows, col_mods = col_mods, centered = False) return # ---------------------------------------------------------------------- def get_keyword_highlight_patterns_alt(bitemp): # Extracts from the bitemplate {bitemp} the two keyword patterns # {hipat_ch,hipat_ec} useful for # highlighting potential keywords in gaps of the SBJ and SPS macro. debug = False pats_ch = [] pats_ec = [] for bipat in bitemp: if debug: err.write(f"!/ {bipat = !r}\n") for pvar in bipat: if debug: err.write(f"!/ {pvar = !r}\n") pena, pat_ch, pat_ec = pvar pats_ch += pat_ch.split('|') pats_ec += pat_ec.split('|') hipat_ch = "|".join(tuple(set(tuple(pats_ch)))) hipat_ec = "|".join(tuple(set(tuple(pats_ec)))) assert re.match(hipat_ch, "") == None, f"hipat_ch matches empty" assert re.match(hipat_ec, "") == None, f"hipat_ec matches empty" return hipat_ch, hipat_ec # ----------------------------------------------------------------------