Skip to content

Commit 329cde9

Browse files
committed
Tighten exact numeric and condition handling
1 parent f89b24c commit 329cde9

3 files changed

Lines changed: 113 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Versioning follows [Semantic Versioning](https://semver.org/).
1919
- Fixed composite scientific units such as `MV/m` being misread as numerator-only units.
2020
- Added numeric claim support for common physical-science units such as `eV`, `Ω·cm`, `S/m`, and `MPa`.
2121
- Treated `estimated to be <value>` as a reported numeric value while keeping predictive `estimated to exceed` frames conservative.
22+
- Prevented comparative evidence such as `>220 °C` from accepting an exact `220 °C` claim.
23+
- Allowed physical measurement conditions such as `1.7 eV in the temperature range` and `5 S/m at 1 kHz` without relaxing count-claim scope guards.
2224

2325
## [1.1.2] — 2026-06-08
2426

src/ref_verify/numeric_claim.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,50 @@
119119
"within",
120120
}
121121

122+
_PHYSICAL_MEASUREMENT_UNITS = {
123+
"ev",
124+
"kev",
125+
"mev",
126+
"ohm-cm",
127+
"gohm-cm",
128+
"mohm-cm",
129+
"kohm-cm",
130+
"s/m",
131+
"ms/m",
132+
"ks/m",
133+
"pa",
134+
"kpa",
135+
"mpa",
136+
"gpa",
137+
"v/m",
138+
"mv/m",
139+
"kv/m",
140+
"v/cm",
141+
"mv/cm",
142+
"kv/cm",
143+
"v/mm",
144+
"mv/mm",
145+
"kv/mm",
146+
}
147+
148+
_MEASUREMENT_CONDITION_TERMS = {
149+
"bias",
150+
"field",
151+
"frequency",
152+
"humidity",
153+
"hz",
154+
"k",
155+
"khz",
156+
"mhz",
157+
"pressure",
158+
"range",
159+
"ranges",
160+
"relative",
161+
"rh",
162+
"temperature",
163+
"voltage",
164+
}
165+
122166
_UNIT_TERMS = {
123167
"a",
124168
"c",
@@ -423,12 +467,28 @@ def _has_unsupported_frame(value: str) -> bool:
423467
token
424468
for token in re.findall(r"[a-zA-Z]+", value[match.end() :].lower())
425469
]
426-
if any(token in _SCOPE_OR_COMPARATIVE_SUFFIX_TERMS for token in suffix_tokens):
470+
if _has_disqualifying_suffix(match.group("unit"), suffix_tokens):
427471
return True
428472
prefix_tokens = re.findall(r"[a-zA-Z]+", value.lower())[:2]
429473
return any(token in {"after", "before", "under", "in"} for token in prefix_tokens)
430474

431475

476+
def _has_disqualifying_suffix(unit: str, suffix_tokens: list[str]) -> bool:
477+
if not any(token in _SCOPE_OR_COMPARATIVE_SUFFIX_TERMS for token in suffix_tokens):
478+
return False
479+
normalized_unit = _normalize_unit(unit)
480+
if (
481+
normalized_unit in _PHYSICAL_MEASUREMENT_UNITS
482+
and _looks_like_measurement_condition(suffix_tokens)
483+
):
484+
return False
485+
return True
486+
487+
488+
def _looks_like_measurement_condition(tokens: list[str]) -> bool:
489+
return any(token in _MEASUREMENT_CONDITION_TERMS for token in tokens)
490+
491+
432492
def _evidence_entails_claim(
433493
evidence_value: float,
434494
evidence_comparator: str,
@@ -445,4 +505,4 @@ def _evidence_entails_claim(
445505
return evidence_value < claim_value
446506
if claim_comparator == "lte":
447507
return evidence_value <= claim_value
448-
return evidence_value == claim_value
508+
return evidence_comparator == "exact" and evidence_value == claim_value

tests/test_numeric_claim.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ def test_accepts_temperature_claim(self):
4242
self.assertEqual(result.status, "SUPPORTED")
4343
self.assertIn("37 °C", result.evidence)
4444

45+
def test_rejects_comparative_evidence_for_exact_claim(self):
46+
cases = (
47+
"The polymer provided high thermal resistance (>220 °C).",
48+
"The polymer provided high thermal resistance at least 220 °C.",
49+
"The polymer provided high thermal resistance up to 220 °C.",
50+
)
51+
52+
for abstract in cases:
53+
with self.subTest(abstract=abstract):
54+
result = check_numeric_claim_support(
55+
abstract,
56+
"polymer thermal resistance 220 °C",
57+
)
58+
59+
self.assertEqual(result.status, "PARTIAL")
60+
4561
def test_accepts_concentration_claim(self):
4662
result = check_numeric_claim_support(
4763
"Cells were treated with 10 mg/mL polymer.",
@@ -111,6 +127,39 @@ def test_accepts_physical_science_units(self):
111127

112128
self.assertEqual(result.status, "SUPPORTED")
113129

130+
def test_accepts_physical_measurement_condition_suffixes(self):
131+
cases = (
132+
(
133+
(
134+
"The effective work function for aluminum-polyimide is estimated "
135+
"to be 1.7 eV in the temperature range between 100 and 270 °C."
136+
),
137+
"effective work function for aluminum-polyimide is 1.7 eV",
138+
),
139+
(
140+
"The conductivity reached 5 S/m at 1 kHz.",
141+
"conductivity reached 5 S/m",
142+
),
143+
(
144+
"The stress reached 120 MPa at 300 K.",
145+
"stress reached 120 MPa",
146+
),
147+
)
148+
149+
for abstract, claim in cases:
150+
with self.subTest(claim=claim):
151+
result = check_numeric_claim_support(abstract, claim)
152+
153+
self.assertEqual(result.status, "SUPPORTED")
154+
155+
def test_rejects_count_claim_condition_suffixes(self):
156+
result = check_numeric_claim_support(
157+
"The device lifetime was 5000 cycles at 5 V.",
158+
"device lifetime was 5000 cycles",
159+
)
160+
161+
self.assertEqual(result.status, "PARTIAL")
162+
114163
def test_rejects_missing_subject_binding(self):
115164
result = check_numeric_claim_support(
116165
"The device was tested extensively. A 95% response rate was observed.",

0 commit comments

Comments
 (0)