Disciplined Path β (field decode α was empirically falsified). Reads S-P-O
structure SYMBOLICALLY from the token sequence via domain-agnostic templates
keyed on FUNCTION WORDS + ORDER, mints content as MeaningGraph entities/relations,
parse-or-refuse (wrong=0 at the comprehension layer).
Templates (set_membership): 'X is a Y' -> member; 'all Xs are Ys' -> subset;
'is X a Y?' / 'are all Xs Ys?' -> queries; definite-NP ('the X is a Y');
conservative singularization incl. irregulars (people->person), refusing
unknown morphology rather than guessing.
End-to-end: prose -> comprehend -> project -> INDEPENDENT set_membership oracle
-> answer vs gold. Result on the real v1 lane: 8 correct / 0 wrong / 0 refused
(full coverage, wrong=0). Cross-content generality (animals/professions/geography)
asserted. 14 reader unit tests + 3 end-to-end tests, each bites under its
violation. Additive only; invariants + capability index green.
57 lines
2.2 KiB
Python
57 lines
2.2 KiB
Python
"""Projectors — map a comprehended ``MeaningGraph`` into a reasoner's input shape.
|
|
|
|
The comprehension reader produces a neutral ``MeaningGraph``; a projector adapts
|
|
it to a specific independent-gold reasoner so the reader can be scored end-to-end
|
|
(prose -> MeaningGraph -> projection -> oracle -> answer vs gold). Projectors hold
|
|
NO decision logic — they only re-shape; the verdict is the independent oracle's.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from generate.meaning_graph.reader import Comprehension
|
|
|
|
|
|
def to_set_membership(comp: Comprehension) -> tuple[dict[str, Any], dict[str, Any]] | None:
|
|
"""Project into ``(structure, query)`` for ``evals.set_membership.oracle``.
|
|
|
|
Returns ``None`` when the comprehension does not carry exactly one membership
|
|
question (nothing to ask the oracle) — the caller treats that as a refusal.
|
|
"""
|
|
graph = comp.meaning_graph
|
|
individuals = sorted({e.entity_id for e in graph.entities if e.kind == "individual"})
|
|
classes = sorted({e.entity_id for e in graph.entities if e.kind == "class"})
|
|
|
|
member_facts = [
|
|
(r.arguments[0], r.arguments[1])
|
|
for r in graph.relations
|
|
if r.predicate == "member" and not r.negated
|
|
]
|
|
subset_facts = [
|
|
(r.arguments[0], r.arguments[1])
|
|
for r in graph.relations
|
|
if r.predicate == "subset" and not r.negated
|
|
]
|
|
|
|
member_queries = [q for q in comp.queries if q.predicate == "member" and not q.negated]
|
|
subset_queries = [q for q in comp.queries if q.predicate == "subset" and not q.negated]
|
|
if len(member_queries) + len(subset_queries) != 1:
|
|
return None
|
|
|
|
sets = [
|
|
{"id": cid, "members": sorted({ind for ind, cls in member_facts if cls == cid})}
|
|
for cid in classes
|
|
]
|
|
structure = {
|
|
"elements": individuals,
|
|
"sets": sets,
|
|
"subsets": [{"subset": a, "superset": b} for a, b in subset_facts],
|
|
}
|
|
if member_queries:
|
|
q = member_queries[0]
|
|
query = {"kind": "member", "element": q.arguments[0], "set": q.arguments[1]}
|
|
else:
|
|
q = subset_queries[0]
|
|
query = {"kind": "subset", "subset": q.arguments[0], "superset": q.arguments[1]}
|
|
return structure, query
|