S0-R1-C5.5-R6-R11 Causal Noun Scope Frame

#99
Files changed (1) hide show
  1. core/quantitative_engine.py +53 -21
core/quantitative_engine.py CHANGED
@@ -1274,6 +1274,56 @@ Keywords: {keywords}"""
1274
  continue
1275
  return "AFFIRMATIVE_CAUSAL"
1276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1277
  affirmative_noun_patterns = (
1278
  rf"\b(?:is|are|was|were|represent(?:s|ed)?|constitut(?:es|ed))\s+(?:an?\s+|the\s+)?{causal_term}\b",
1279
  rf"\b(?:identify|identifies|identified|establish|establishes|established|demonstrate|demonstrates|demonstrated|show|shows|showed|prove|proves|proved|provide|provides|provided)\b[^.;]{{0,80}}\b{causal_term}\b",
@@ -1282,6 +1332,8 @@ Keywords: {keywords}"""
1282
  )
1283
  for pattern in affirmative_noun_patterns:
1284
  for match in re.finditer(pattern, value, flags=re.I):
 
 
1285
  prefix = value[max(0, match.start() - 80):match.start()]
1286
  if re.search(
1287
  r"\b(?:no|not|without|cannot|can't|insufficient|unable|does not|doesn't|do not|don't|did not|didn't|not able)\b",
@@ -1291,27 +1343,7 @@ Keywords: {keywords}"""
1291
  continue
1292
  return "AFFIRMATIVE_CAUSAL"
1293
 
1294
- limitation_patterns = (
1295
- rf"\b(?:cannot|can't|could not|couldn't|does not|doesn't|do not|don't|did not|didn't|fails? to|failed to|"
1296
- rf"is not able to|are not able to|was not able to|were not able to|unable to|insufficient to|not able to)\s+"
1297
- rf"(?:identify|establish|demonstrate|show|infer|support|imply|mean|claim|justify|warrant|permit|allow|prove)\b[^.;]*\b{causal_term}\b",
1298
- rf"\b(?:is|are|was|were)\s+not\s+(?:designed|intended)\s+to\s+"
1299
- rf"(?:identify|establish|demonstrate|show|infer|support|prove)\b[^.;]*\b{causal_term}\b",
1300
- rf"\bwithout\s+(?:identifying|establishing|demonstrating|showing|inferring|supporting|proving)\b[^.;]*\b{causal_term}\b",
1301
- rf"\b(?:no|without)\s+(?:evidence|basis|support|identification|proof)\b[^.;]*\b{causal_term}\b",
1302
- rf"\b(?:is|are|was|were)\s+not\s+(?:evidence|proof)\s+(?:of|for)\s+(?:an?\s+|the\s+)?{causal_term}\b",
1303
- rf"\b(?:cannot|can't|could not|couldn't)\s+be\s+used\s+to\s+(?:infer|establish|demonstrate|show|prove)\b[^.;]*\b{causal_term}\b",
1304
- rf"\b(?:should not|shouldn't|must not|cannot|can't|could not|couldn't)\s+be\s+"
1305
- rf"(?:interpreted|read|treated|understood)\s+(?:as|to mean)\b[^.;]*\b{causal_term}\b",
1306
- rf"\b(?:is|are|was|were)?\s*(?:restricted|limited|confined)\s+to\b[^.;]*"
1307
- rf"\b(?:not|rather than|instead of)\s+(?:to\s+)?(?:an?\s+|the\s+)?{causal_term}\b",
1308
- rf"\b(?:association|associations|covariation|covariance|correlation|correlations|indirect association|descriptive pattern|descriptive patterns)\b[^.;]*"
1309
- rf"\b(?:not(?:\s+as)?|rather than|instead of)\s+(?:an?\s+|the\s+)?{causal_term}\b",
1310
- rf"\b{causal_term}\b\s+(?:is|are|was|were)\s+(?:not|never)\s+"
1311
- rf"(?:identified|established|demonstrated|shown|inferred|supported|claimed|justified|warranted|proven)\b",
1312
- rf"\b(?:not|no)\s+(?:an?\s+|the\s+)?{causal_term}\b",
1313
- )
1314
- if saw_disclaimed_direct or any(re.search(pattern, value, flags=re.I) for pattern in limitation_patterns):
1315
  return "EXPLICIT_CAUSAL_LIMITATION"
1316
  return "NON_CAUSAL"
1317
 
 
1274
  continue
1275
  return "AFFIRMATIVE_CAUSAL"
1276
 
1277
+ # R6-R11 causal-noun scope frame. A past participle can be either a
1278
+ # predicate ("the analysis identified a causal effect") or a modifier
1279
+ # inside a causal noun phrase ("rather than an identified causal
1280
+ # effect"). The latter inherits the surrounding negation/contrast
1281
+ # operator and must not be promoted to an affirmative causal claim.
1282
+ causal_noun_participle = (
1283
+ r"(?:identified|established|demonstrated|shown|inferred|supported|"
1284
+ r"claimed|justified|warranted|proven)"
1285
+ )
1286
+ causal_noun_adverb = r"(?:empirically|statistically|directly|clearly|credibly|formally|causally)"
1287
+ causal_noun_modifier = (
1288
+ rf"(?:(?:{causal_noun_adverb}\s+){{0,2}}{causal_noun_participle}\s+)?"
1289
+ )
1290
+ causal_noun_surface = rf"(?:an?\s+|the\s+)?{causal_noun_modifier}{causal_term}"
1291
+
1292
+ limitation_patterns = (
1293
+ rf"\b(?:cannot|can't|could not|couldn't|does not|doesn't|do not|don't|did not|didn't|fails? to|failed to|"
1294
+ rf"is not able to|are not able to|was not able to|were not able to|unable to|insufficient to|not able to)\s+"
1295
+ rf"(?:identify|establish|demonstrate|show|infer|support|imply|mean|claim|justify|warrant|permit|allow|prove)\b[^.;]*\b{causal_term}\b",
1296
+ rf"\b(?:is|are|was|were)\s+not\s+(?:designed|intended)\s+to\s+"
1297
+ rf"(?:identify|establish|demonstrate|show|infer|support|prove)\b[^.;]*\b{causal_term}\b",
1298
+ rf"\bwithout\s+(?:identifying|establishing|demonstrating|showing|inferring|supporting|proving)\b[^.;]*\b{causal_term}\b",
1299
+ rf"\b(?:no|without)\s+(?:evidence|basis|support|identification|proof)\b[^.;]*\b{causal_term}\b",
1300
+ rf"\b(?:is|are|was|were)\s+not\s+(?:evidence|proof)\s+(?:of|for)\s+{causal_noun_surface}\b",
1301
+ rf"\b(?:cannot|can't|could not|couldn't)\s+be\s+used\s+to\s+(?:infer|establish|demonstrate|show|prove)\b[^.;]*\b{causal_term}\b",
1302
+ rf"\b(?:should not|shouldn't|must not|cannot|can't|could not|couldn't)\s+be\s+"
1303
+ rf"(?:interpreted|read|treated|understood)\s+(?:as|to mean)\s+{causal_noun_surface}\b",
1304
+ rf"\b(?:is|are|was|were)?\s*(?:restricted|limited|confined)\s+to\b[^.;]*"
1305
+ rf"\b(?:not|rather than|instead of)\s+(?:to\s+)?{causal_noun_surface}\b",
1306
+ rf"\b(?:association|associations|covariation|covariance|correlation|correlations|indirect association|descriptive pattern|descriptive patterns)\b[^.;]*"
1307
+ rf"\b(?:not(?:\s+as)?|rather than|instead of)\s+{causal_noun_surface}\b",
1308
+ rf"\b{causal_term}\b\s+(?:is|are|was|were)\s+(?:not|never)\s+"
1309
+ rf"{causal_noun_participle}\b",
1310
+ rf"\b(?:not|no)\s+{causal_noun_surface}\b",
1311
+ )
1312
+ limitation_matches = [
1313
+ match
1314
+ for pattern in limitation_patterns
1315
+ for match in re.finditer(pattern, value, flags=re.I)
1316
+ ]
1317
+
1318
+ def noun_match_is_inside_limitation(match):
1319
+ # Only suppress the affirmative interpretation when the matched
1320
+ # predicate/noun phrase is actually inside the frozen limitation
1321
+ # span. A later affirmative clause remains fail-closed.
1322
+ return any(
1323
+ limited.start() <= match.start() and match.end() <= limited.end()
1324
+ for limited in limitation_matches
1325
+ )
1326
+
1327
  affirmative_noun_patterns = (
1328
  rf"\b(?:is|are|was|were|represent(?:s|ed)?|constitut(?:es|ed))\s+(?:an?\s+|the\s+)?{causal_term}\b",
1329
  rf"\b(?:identify|identifies|identified|establish|establishes|established|demonstrate|demonstrates|demonstrated|show|shows|showed|prove|proves|proved|provide|provides|provided)\b[^.;]{{0,80}}\b{causal_term}\b",
 
1332
  )
1333
  for pattern in affirmative_noun_patterns:
1334
  for match in re.finditer(pattern, value, flags=re.I):
1335
+ if noun_match_is_inside_limitation(match):
1336
+ continue
1337
  prefix = value[max(0, match.start() - 80):match.start()]
1338
  if re.search(
1339
  r"\b(?:no|not|without|cannot|can't|insufficient|unable|does not|doesn't|do not|don't|did not|didn't|not able)\b",
 
1343
  continue
1344
  return "AFFIRMATIVE_CAUSAL"
1345
 
1346
+ if saw_disclaimed_direct or limitation_matches:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1347
  return "EXPLICIT_CAUSAL_LIMITATION"
1348
  return "NON_CAUSAL"
1349