初始化项目,由ModelHub XC社区提供模型
Model: rpant/iolai26-solve Source: Original Platform
This commit is contained in:
113
solver/scaffold.py
Normal file
113
solver/scaffold.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""Deterministic analysis blocks (segmentation, alignment, numerals) for the
|
||||
prompt: the full scaffold and the optional light hint."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, List, Optional, Sequence, Tuple
|
||||
|
||||
from .align import align as build_align
|
||||
from .numerals import extract_attested, induce
|
||||
from .preprocess import Puzzle, strip_punct, tokenize
|
||||
from .segment import Segmenter
|
||||
|
||||
MAX_SEG_LINES = 40
|
||||
MAX_ALIGN_LINES = 30
|
||||
MAX_VOCAB = 60
|
||||
|
||||
|
||||
def analysis_blocks(puzzle: Puzzle) -> Tuple[str, str]:
|
||||
"""(segmentation block, alignment block) for the prompt. Also used by the
|
||||
CEGIS proposer (synth.py)."""
|
||||
amap = build_align(puzzle.pairs)
|
||||
align_lines = []
|
||||
for tok, cands in sorted(amap.items()):
|
||||
top = [f"{t} ({s:.1f})" for t, s in cands[:2] if s > 0.2]
|
||||
if top:
|
||||
align_lines.append(f" {tok} ~ {', '.join(top)}")
|
||||
|
||||
vocab, groups = [], {}
|
||||
for p in puzzle.pairs:
|
||||
for t in tokenize(p.src):
|
||||
t = strip_punct(t).casefold()
|
||||
if t and t not in vocab:
|
||||
vocab.append(t)
|
||||
vocab = vocab[:MAX_VOCAB]
|
||||
for tok, cands in amap.items():
|
||||
if cands:
|
||||
groups.setdefault(cands[0][0], set()).add(tok)
|
||||
seg = Segmenter().fit(vocab, share_groups=[g for g in groups.values() if len(g) > 1])
|
||||
seg_lines = []
|
||||
for w in vocab:
|
||||
parts = seg.segment(w)
|
||||
if len(parts) > 1:
|
||||
seg_lines.append(f" {w} = {'-'.join(parts)}")
|
||||
return ("\n".join(seg_lines[:MAX_SEG_LINES]) or " (none found)",
|
||||
"\n".join(align_lines[:MAX_ALIGN_LINES]) or " (none found)")
|
||||
|
||||
|
||||
def light_hint(puzzle: Puzzle, max_lines: int = 10) -> str:
|
||||
"""A minimal, optional hint for the lean prompt: a few morpheme segmentation
|
||||
guesses, framed as fallible. Off by default; enabled via a toggle."""
|
||||
try:
|
||||
seg_block, _align = analysis_blocks(puzzle)
|
||||
except Exception:
|
||||
return ""
|
||||
lines = [l for l in seg_block.splitlines() if l.strip() and "none found" not in l]
|
||||
if not lines:
|
||||
return ""
|
||||
body = "\n".join(lines[:max_lines])
|
||||
return ("Optional hint (an automatic guess at word parts; it may be wrong, "
|
||||
"so rely on the data itself):\n" + body)
|
||||
|
||||
|
||||
def numeral_block(puzzle: Puzzle) -> str:
|
||||
"""Induced numeral-system values, when the CSP solved and round-trip
|
||||
verified them — the strongest kind of hint we can give."""
|
||||
if puzzle.task_type not in ("text_to_num", "num_to_text"):
|
||||
return ""
|
||||
attested = extract_attested(puzzle.pairs)
|
||||
system = induce(attested) if attested else None
|
||||
if system is None:
|
||||
return ""
|
||||
vals = ", ".join(f"{t}={v}" for t, v in sorted(system.values.items(), key=lambda kv: kv[1]))
|
||||
return (f"Numeral analysis (verified against every attested example):\n {vals}\n"
|
||||
f" combination rule: a smaller value directly before a larger one multiplies it; "
|
||||
f"otherwise values add.")
|
||||
|
||||
|
||||
def candidate_block(items_answers: Sequence[Tuple[str, Optional[str], float, str]]) -> str:
|
||||
"""Symbolic candidate answers per item: (item label, answer, confidence,
|
||||
method). Only candidates with real evidence are shown — a low-confidence
|
||||
echo would anchor the model on garbage."""
|
||||
lines = []
|
||||
for label, ans, conf, method in items_answers:
|
||||
if ans and conf >= 0.4:
|
||||
lines.append(f" item {label}: '{ans}' (source: {method}, fit {conf:.2f})")
|
||||
if not lines:
|
||||
return ""
|
||||
return ("Candidate answers from mechanical analysis (adopt if consistent with "
|
||||
"the data, correct if not):\n" + "\n".join(lines))
|
||||
|
||||
|
||||
def build_scaffold(puzzle: Puzzle,
|
||||
answers: Optional[Sequence[Optional[str]]] = None,
|
||||
confs: Optional[Sequence[float]] = None,
|
||||
methods: Optional[Sequence[str]] = None) -> str:
|
||||
"""Full scaffold block for one puzzle's prompt."""
|
||||
seg_block, align_block = analysis_blocks(puzzle)
|
||||
parts = [
|
||||
"## Mechanical analysis (computed from the data above; may contain errors — "
|
||||
"the attested data always wins)",
|
||||
f"Morpheme segmentation hypotheses:\n{seg_block}",
|
||||
f"Word alignment hypotheses (task-language token ~ likely meaning):\n{align_block}",
|
||||
]
|
||||
nb = numeral_block(puzzle)
|
||||
if nb:
|
||||
parts.append(nb)
|
||||
if answers is not None and confs is not None:
|
||||
labels = [it.number or str(i + 1) for i, it in enumerate(puzzle.items)]
|
||||
meths = list(methods) if methods else ["symbolic"] * len(labels)
|
||||
cb = candidate_block(list(zip(labels, answers, confs, meths)))
|
||||
if cb:
|
||||
parts.append(cb)
|
||||
return "\n\n".join(parts)
|
||||
Reference in New Issue
Block a user