core/tests/test_capability_index.py
Shay 2a3f422783 feat(measure): put the relational reader (#596) on the capability index
A1 of the refined sequencing — the binary-relation reader was inert w.r.t. the
yardstick (contributing 0). This adds a comprehension_relational_predicate domain:
binary-relation prose scored against hand-authored independent gold (predicate,
subject, object) triples — INV-25 independent / INV-27 reader-disjoint (the reader
never produced the gold). Index breadth 8->9, capability_score 0.937258->0.944030,
wrong_total still 0; baseline.json re-frozen to digest 1ea91c1e.

Rigor split: the index lane is POSITIVE-ONLY (clean coverage, consistent with the
other 8 lanes — mixing adversarial refuse-cases into the coverage denominator would
make 'added capability' read as a score drop). The #596 fabrication-catch lives in a
dedicated falsification test (evals/relational/v1/refusals.jsonl): the trailing-
qualifier / dangling-copula / negation / verb-form cases MUST refuse — bites if the
reader ever fabricates. Honest coverage gap recorded: overlaps_event has no copular
surface form (verb-form 'A overlaps B' refuses), so 17 positives cover 15/16 predicates.
2026-06-06 10:09:15 -07:00

122 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Cross-domain capability index — AGI-roadmap Phase 1 (MEASURE).
The yardstick that gates every later "more capable" claim. Two honest axes —
**accuracy** (of committed answers; wrong stays 0 in assert mode) and **coverage**
(attempted-not-refused) — aggregated across domains so it CANNOT be gamed by a
narrow per-domain win: the headline coverage is the GEOMETRIC MEAN across domains,
which only rises if *every* domain rises. A hack that maxes one lane and leaves
the rest at zero leaves the geomean ~0.
"""
from __future__ import annotations
from evals.capability_index.index import (
DomainResult,
aggregate,
deterministic_digest,
)
def _d(domain: str, correct: int, wrong: int, refused: int) -> DomainResult:
return DomainResult(domain=domain, correct=correct, wrong=wrong, refused=refused)
def test_domain_result_axes() -> None:
r = _d("logic", correct=8, wrong=0, refused=2)
assert r.total == 10
assert r.attempted == 8
assert r.coverage == 0.8
assert r.accuracy == 1.0 # of committed answers
def test_aggregate_axes_micro() -> None:
idx = aggregate([_d("a", 6, 0, 4), _d("b", 2, 0, 8)])
assert idx.wrong_total == 0
assert idx.coverage == 0.4 # (6+2)/(10+10) micro
assert idx.accuracy == 1.0 # no wrong
assert idx.breadth == 2 # both domains have some coverage
def test_geomean_coverage_resists_narrow_gaming() -> None:
# A NARROW hack: one domain maxed, the rest at zero coverage.
narrow = aggregate(
[_d("gamed", 10, 0, 0), _d("x", 0, 0, 10), _d("y", 0, 0, 10)]
)
# A BALANCED engine: every domain partially covered.
balanced = aggregate(
[_d("gamed", 4, 0, 6), _d("x", 4, 0, 6), _d("y", 4, 0, 6)]
)
# Micro-coverage is similar (~0.33 vs 0.40), but the geomean exposes the hack:
assert narrow.coverage_geomean == 0.0 # any zero-coverage domain -> geomean 0
assert balanced.coverage_geomean > 0.39
# The capability score (geomean × accuracy) refuses to reward the narrow hack.
assert narrow.capability_score == 0.0
assert balanced.capability_score > 0.39
def test_balanced_progress_moves_the_score_monotonically() -> None:
low = aggregate([_d("a", 2, 0, 8), _d("b", 2, 0, 8)])
high = aggregate([_d("a", 6, 0, 4), _d("b", 6, 0, 4)])
assert high.coverage_geomean > low.coverage_geomean
assert high.capability_score > low.capability_score
def test_wrong_is_a_hard_gate() -> None:
# In assert mode wrong MUST be 0; any wrong invalidates the index (score 0)
# and is surfaced — never averaged away.
idx = aggregate([_d("a", 8, 1, 1), _d("b", 5, 0, 5)])
assert idx.wrong_total == 1
assert idx.assert_mode_valid is False
assert idx.capability_score == 0.0 # wrong=0 is non-negotiable in assert mode
def test_digest_is_deterministic_and_bites() -> None:
a = aggregate([_d("a", 6, 0, 4), _d("b", 2, 0, 8)])
b = aggregate([_d("a", 6, 0, 4), _d("b", 2, 0, 8)])
assert deterministic_digest(a) == deterministic_digest(b)
moved = aggregate([_d("a", 7, 0, 3), _d("b", 2, 0, 8)])
assert deterministic_digest(moved) != deterministic_digest(a)
def test_empty_index_is_well_defined() -> None:
idx = aggregate([])
assert idx.coverage == 0.0
assert idx.coverage_geomean == 0.0
assert idx.breadth == 0
assert idx.capability_score == 0.0
def test_real_lanes_compose_into_the_index_with_wrong_zero() -> None:
# The baseline: three structured-input reasoning lanes PLUS the six
# comprehension lanes (prose -> MeaningGraph -> projection -> independent
# oracle / pack-named predicate) compose into the cross-domain index with zero
# wrong commits. The relational_predicate lane (#596) is the 9th domain.
from evals.capability_index.adapters import collect_domain_results
collection = collect_domain_results()
assert collection.not_covered == () # every adapter ran (no silent drop)
idx = aggregate(list(collection.results))
assert idx.wrong_total == 0
assert idx.assert_mode_valid
assert idx.breadth == 9
assert {d.domain for d in idx.domains} == {
"deductive_logic",
"dimensional",
"relational_metric",
"comprehension_set_membership",
"comprehension_syllogism",
"comprehension_total_ordering",
"comprehension_propositional",
"comprehension_relational_metric",
"comprehension_relational_predicate",
}
assert idx.capability_score > 0.5 # real, non-trivial cross-domain capability
def test_index_report_is_deterministic_across_runs() -> None:
# The capability number is reproducible — improvement is a replayable curve.
from evals.capability_index.adapters import collect_domain_results
a = deterministic_digest(aggregate(list(collect_domain_results().results)))
b = deterministic_digest(aggregate(list(collect_domain_results().results)))
assert a == b