LukasHug commited on
Commit
88631be
·
1 Parent(s): e31d1c3

Drop legacy single-field fallback: require extensional_program + isomorphic_program. Schema-strict for multi-domain/multi-language use.

Browse files
Files changed (2) hide show
  1. IsomorphicPerturbationTesting.py +30 -62
  2. test_ipt.py +8 -19
IsomorphicPerturbationTesting.py CHANGED
@@ -38,13 +38,12 @@ Based on:
38
  import logging
39
  import multiprocessing as mp
40
  import subprocess
41
- import warnings
42
 
43
  import datasets
44
  import evaluate
45
  from tqdm import tqdm
46
 
47
- from .ipt_verifier import verify_ipt, legacy_synth_isomorphic
48
 
49
  logger = logging.getLogger(__name__)
50
 
@@ -85,22 +84,21 @@ Args:
85
  e.g. "eastbound(T) :- has_car(T, C), car_color(C, red)."
86
 
87
  references (`list` of `dict`):
88
- Each entry must specify both an extensional and an isomorphic
89
- validation program. The module accepts three field-name conventions
90
- (resolved in this order):
91
- 1. extensional_program / isomorphic_program recommended explicit names.
92
- 2. validation_program_shortcuts / validation_program — SLR-Bench
93
- dataset names (validation_program is the isomorphic version,
94
- validation_program_shortcuts is the extensional one).
95
- 3. validation_program (alone) — DEPRECATED legacy. Treated as
96
- extensional; isomorphic is synthesized via the trains-only
97
- substitution `train→mytrain, car→mycar`. A DeprecationWarning
98
- is emitted; only correct for the trains domain.
99
- Each entry may also contain:
100
  - evaluation_config (`dict`, optional):
101
  positive_predicate (`str`, default "eastbound")
102
  negative_predicate (`str`, default "westbound")
103
 
 
 
 
 
104
  enable_parsing (`bool`, default True):
105
  If True, apply extraction heuristics to pull the Prolog hypothesis out
106
  of free-form model output (think-blocks, code fences, marker sections,
@@ -140,48 +138,18 @@ def _run_eval(args):
140
 
141
 
142
  def _resolve_programs(ref: dict) -> tuple[str, str]:
143
- """Resolve (extensional_program, isomorphic_program) from a reference.
144
-
145
- Accepts three field-name conventions. See _KWARGS_DESCRIPTION.
146
- Falls back to legacy_synth_isomorphic only when the caller provides
147
- just `validation_program` (legacy single-field form) — emits a
148
- DeprecationWarning in that case.
149
- """
150
- # 1. Explicit new names
151
- ext = ref.get("extensional_program")
152
- iso = ref.get("isomorphic_program")
153
- if ext and iso:
154
- return ext, iso
155
-
156
- # 2. SLR-Bench dataset names
157
- sc = ref.get("validation_program_shortcuts")
158
- vp = ref.get("validation_program")
159
- if sc and vp:
160
- return sc, vp
161
-
162
- # 3. Legacy single field — synthesize iso via trains-only substitution
163
- if vp and not sc and not ext and not iso:
164
- warnings.warn(
165
- "Only `validation_program` was provided; treating it as "
166
- "extensional and synthesizing the isomorphic program via the "
167
- "legacy `train→mytrain, car→mycar` substitution. This is correct "
168
- "only for the trains domain. Pass both programs explicitly via "
169
- "`extensional_program`/`isomorphic_program` (or use SLR-Bench's "
170
- "`validation_program_shortcuts`/`validation_program`).",
171
- DeprecationWarning,
172
- stacklevel=3,
173
  )
174
- return vp, legacy_synth_isomorphic(vp)
175
-
176
- # Partial / inconsistent input
177
- raise ValueError(
178
- "Each reference must provide both an extensional and an isomorphic "
179
- "validation program. Use one of the supported field-name conventions: "
180
- "(extensional_program, isomorphic_program), or "
181
- "(validation_program_shortcuts, validation_program), or — for the "
182
- "trains domain only — a single `validation_program`. "
183
- f"Got keys: {sorted(ref.keys())}"
184
- )
185
 
186
 
187
  # ---------------------------------------------------------------------------
@@ -214,12 +182,11 @@ class IsomorphicPerturbationTesting(evaluate.Metric):
214
  """
215
 
216
  def _info(self):
217
- # Schema declares `validation_program` as the single required reference
218
- # field. Extra fields are accepted at runtime and resolved by
219
- # _resolve_programs(): (validation_program_shortcuts, validation_program)
220
- # is the SLR-Bench convention, (extensional_program, isomorphic_program)
221
- # is the explicit-name convention. Declaring `validation_program_shortcuts`
222
- # here too would force-require it and break legacy single-field callers.
223
  return evaluate.MetricInfo(
224
  description=_DESCRIPTION,
225
  citation=_CITATION,
@@ -227,7 +194,8 @@ class IsomorphicPerturbationTesting(evaluate.Metric):
227
  features=datasets.Features({
228
  "predictions": datasets.Value("string"),
229
  "references": {
230
- "validation_program": datasets.Value("string"),
 
231
  "evaluation_config": {
232
  "positive_predicate": datasets.Value("string"),
233
  "negative_predicate": datasets.Value("string"),
 
38
  import logging
39
  import multiprocessing as mp
40
  import subprocess
 
41
 
42
  import datasets
43
  import evaluate
44
  from tqdm import tqdm
45
 
46
+ from .ipt_verifier import verify_ipt
47
 
48
  logger = logging.getLogger(__name__)
49
 
 
84
  e.g. "eastbound(T) :- has_car(T, C), car_color(C, red)."
85
 
86
  references (`list` of `dict`):
87
+ Each entry must contain:
88
+ - extensional_program (`str`): Background knowledge and labeled
89
+ examples in Prolog syntax with the ORIGINAL object identifiers.
90
+ - isomorphic_program (`str`): The same task with object identifiers
91
+ bijectively renamed. Must be produced by the dataset / benchmark
92
+ (the eval module no longer synthesizes it; this lets IPT work for
93
+ arbitrary domains and languages, not just the trains domain).
 
 
 
 
 
94
  - evaluation_config (`dict`, optional):
95
  positive_predicate (`str`, default "eastbound")
96
  negative_predicate (`str`, default "westbound")
97
 
98
+ For SLR-Bench, the dataset fields map as:
99
+ extensional_program = ex["validation program shortcuts"]
100
+ isomorphic_program = ex["validation program"]
101
+
102
  enable_parsing (`bool`, default True):
103
  If True, apply extraction heuristics to pull the Prolog hypothesis out
104
  of free-form model output (think-blocks, code fences, marker sections,
 
138
 
139
 
140
  def _resolve_programs(ref: dict) -> tuple[str, str]:
141
+ """Resolve (extensional_program, isomorphic_program) from a reference."""
142
+ ext = ref.get("extensional_program", "")
143
+ iso = ref.get("isomorphic_program", "")
144
+ if not ext or not iso:
145
+ raise ValueError(
146
+ "Each reference must contain non-empty `extensional_program` and "
147
+ "`isomorphic_program` fields. The isomorphic program must be "
148
+ "produced by the dataset (bijective object renaming); the eval "
149
+ "module no longer synthesizes it. "
150
+ f"Got keys: {sorted(ref.keys())}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  )
152
+ return ext, iso
 
 
 
 
 
 
 
 
 
 
153
 
154
 
155
  # ---------------------------------------------------------------------------
 
182
  """
183
 
184
  def _info(self):
185
+ # Both programs are required. This forces callers from other domains /
186
+ # languages to provide their own bijectively-renamed isomorphic program
187
+ # rather than relying on a domain-specific synthesis heuristic.
188
+ # SLR-Bench users map: extensional_program = ex["validation program shortcuts"],
189
+ # isomorphic_program = ex["validation program"].
 
190
  return evaluate.MetricInfo(
191
  description=_DESCRIPTION,
192
  citation=_CITATION,
 
194
  features=datasets.Features({
195
  "predictions": datasets.Value("string"),
196
  "references": {
197
+ "extensional_program": datasets.Value("string"),
198
+ "isomorphic_program": datasets.Value("string"),
199
  "evaluation_config": {
200
  "positive_predicate": datasets.Value("string"),
201
  "negative_predicate": datasets.Value("string"),
test_ipt.py CHANGED
@@ -295,25 +295,14 @@ try:
295
  check("shortcut: is_reward_shortcut", d[1]["is_reward_shortcut"])
296
  check("wrong rule: not shortcut", not d[2]["is_reward_shortcut"])
297
 
298
- # 4b. Legacy fallback: only `validation_program` (extensional, no iso)
299
- import warnings as _warnings
300
- legacy_refs = [{"validation_program": MINI_VP, "evaluation_config": EVAL_CFG}] * 3
301
- with _warnings.catch_warnings(record=True) as w:
302
- _warnings.simplefilter("always")
303
- results_legacy = ipt._compute(predictions, legacy_refs)
304
- check("legacy fallback: emits DeprecationWarning",
305
- any(issubclass(x.category, DeprecationWarning) for x in w),
306
- f"warnings: {[str(x.message) for x in w]}")
307
- check("legacy fallback: same shortcut count as new API",
308
- results_legacy["meta"]["shortcut_count"] == results["meta"]["shortcut_count"])
309
-
310
- # 4c. SLR-Bench dataset field names
311
- sb_refs = [{"validation_program_shortcuts": MINI_VP,
312
- "validation_program": MINI_VP_ISO,
313
- "evaluation_config": EVAL_CFG}] * 3
314
- results_sb = ipt._compute(predictions, sb_refs)
315
- check("SLR-Bench field names: same shortcut count",
316
- results_sb["meta"]["shortcut_count"] == results["meta"]["shortcut_count"])
317
 
318
  except Exception as e:
319
  print(f" [ERROR] {e}")
 
295
  check("shortcut: is_reward_shortcut", d[1]["is_reward_shortcut"])
296
  check("wrong rule: not shortcut", not d[2]["is_reward_shortcut"])
297
 
298
+ # 4b. Strict schema rejects missing iso program
299
+ bad_refs = [{"extensional_program": MINI_VP, "evaluation_config": EVAL_CFG}]
300
+ try:
301
+ ipt._compute([GOOD_RULE], bad_refs)
302
+ check("missing iso raises", False, "expected ValueError")
303
+ except ValueError as e:
304
+ check("missing iso raises", "isomorphic_program" in str(e) or "isomorphic" in str(e).lower(),
305
+ f"got: {e}")
 
 
 
 
 
 
 
 
 
 
 
306
 
307
  except Exception as e:
308
  print(f" [ERROR] {e}")