Dishaaa25 commited on
Commit
fae9841
·
1 Parent(s): de4b527

Make training resilient to bad problem templates

Browse files
Files changed (2) hide show
  1. env/generator.py +85 -33
  2. scripts/test_dataset_mode.py +22 -1
env/generator.py CHANGED
@@ -181,47 +181,59 @@ class GeneratorAgent:
181
  return normalized_problem
182
 
183
  rng = self._rng_for(target_tier, history, problem_id, family_weights or {})
184
- template = self._choose_template(
185
  target_tier,
186
  history,
187
  rng,
188
  forced_problem_type=problem_id,
189
  family_weights=family_weights or {},
190
  )
 
191
 
192
- for _ in range(20):
193
- raw_cases = template.case_builder(rng)
194
- test_cases = [
195
- {
196
- "input": case_input,
197
- "output": template.solver(case_input),
198
- "is_visible": index < VISIBLE_TEST_COUNT,
199
- }
200
- for index, case_input in enumerate(raw_cases)
201
- ]
202
- signature = self._problem_signature(template.problem_type, test_cases)
203
- problem = {
204
- "problem_id": f"{template.problem_type}_{signature[:8]}",
205
- "problem_type": template.problem_type,
206
- "difficulty": round(self._tier_to_scalar(target_tier), 4),
207
- "difficulty_label": DIFFICULTY_LABELS[target_tier],
208
- "problem": template.statement_builder(),
209
- "input_format": template.input_format,
210
- "constraints": template.constraints,
211
- "test_cases": test_cases,
212
- "visible_problem": {
213
- "problem": template.statement_builder(),
214
- "input_format": template.input_format,
215
- "constraints": template.constraints,
216
- },
217
- "generation_mode": "deterministic_fallback" if self.deterministic else "local_rule_based",
218
- "validity_bonus": 0.15,
219
- }
220
- normalized_problem = normalize_problem(problem)
221
- if validate_problem(normalized_problem):
222
- return normalized_problem
 
 
 
 
 
 
 
 
223
 
224
- raise ValueError(f"Unable to generate a valid problem for template {template.problem_type}")
 
 
 
225
 
226
  def generate(
227
  self,
@@ -268,6 +280,46 @@ class GeneratorAgent:
268
 
269
  return rng.choices(eligible, weights=weights, k=1)[0]
270
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  def _rng_for(
272
  self,
273
  tier: int,
 
181
  return normalized_problem
182
 
183
  rng = self._rng_for(target_tier, history, problem_id, family_weights or {})
184
+ templates_to_try = self._candidate_templates_for_generation(
185
  target_tier,
186
  history,
187
  rng,
188
  forced_problem_type=problem_id,
189
  family_weights=family_weights or {},
190
  )
191
+ failures: list[str] = []
192
 
193
+ for template in templates_to_try:
194
+ for _ in range(20):
195
+ try:
196
+ raw_cases = template.case_builder(rng)
197
+ test_cases = [
198
+ {
199
+ "input": case_input,
200
+ "output": template.solver(case_input),
201
+ "is_visible": index < VISIBLE_TEST_COUNT,
202
+ }
203
+ for index, case_input in enumerate(raw_cases)
204
+ ]
205
+ signature = self._problem_signature(template.problem_type, test_cases)
206
+ statement = template.statement_builder()
207
+ problem = {
208
+ "problem_id": f"{template.problem_type}_{signature[:8]}",
209
+ "problem_type": template.problem_type,
210
+ "difficulty": round(self._tier_to_scalar(target_tier), 4),
211
+ "difficulty_label": DIFFICULTY_LABELS[target_tier],
212
+ "problem": statement,
213
+ "input_format": template.input_format,
214
+ "constraints": template.constraints,
215
+ "test_cases": test_cases,
216
+ "visible_problem": {
217
+ "problem": statement,
218
+ "input_format": template.input_format,
219
+ "constraints": template.constraints,
220
+ },
221
+ "generation_mode": "deterministic_fallback" if self.deterministic else "local_rule_based",
222
+ "validity_bonus": 0.15,
223
+ }
224
+ normalized_problem = normalize_problem(problem)
225
+ if validate_problem(normalized_problem):
226
+ return normalized_problem
227
+ except Exception as exc:
228
+ failures.append(f"{template.problem_type}: {exc}")
229
+ break
230
+
231
+ failures.append(f"{template.problem_type}: invalid after retries")
232
 
233
+ raise ValueError(
234
+ "Unable to generate a valid problem across candidate templates. "
235
+ f"Last failures: {failures[-5:]}"
236
+ )
237
 
238
  def generate(
239
  self,
 
280
 
281
  return rng.choices(eligible, weights=weights, k=1)[0]
282
 
283
+ def _candidate_templates_for_generation(
284
+ self,
285
+ tier: int,
286
+ history: dict[str, Any],
287
+ rng: random.Random,
288
+ forced_problem_type: str | None = None,
289
+ family_weights: dict[str, float] | None = None,
290
+ ) -> list[ProblemTemplate]:
291
+ if forced_problem_type:
292
+ forced_matches = [template for template in self.templates if template.problem_type == forced_problem_type]
293
+ if forced_matches:
294
+ return forced_matches
295
+
296
+ eligible = [template for template in self.templates if template.difficulty_tier == tier]
297
+ if not eligible:
298
+ eligible = list(self.templates)
299
+
300
+ primary = self._choose_template(
301
+ tier,
302
+ history,
303
+ rng,
304
+ forced_problem_type=forced_problem_type,
305
+ family_weights=family_weights or {},
306
+ )
307
+ remaining_eligible = [template for template in eligible if template != primary]
308
+ rng.shuffle(remaining_eligible)
309
+
310
+ fallback_templates = [template for template in self.templates if template not in eligible and template != primary]
311
+ rng.shuffle(fallback_templates)
312
+
313
+ ordered = [primary, *remaining_eligible, *fallback_templates]
314
+ deduped: list[ProblemTemplate] = []
315
+ seen_problem_types: set[str] = set()
316
+ for template in ordered:
317
+ if template.problem_type in seen_problem_types:
318
+ continue
319
+ seen_problem_types.add(template.problem_type)
320
+ deduped.append(template)
321
+ return deduped
322
+
323
  def _rng_for(
324
  self,
325
  tier: int,
scripts/test_dataset_mode.py CHANGED
@@ -2,6 +2,7 @@ from __future__ import annotations
2
 
3
  import sys
4
  from pathlib import Path
 
5
 
6
  ROOT = Path(__file__).resolve().parents[1]
7
  if str(ROOT) not in sys.path:
@@ -9,7 +10,7 @@ if str(ROOT) not in sys.path:
9
 
10
  from env import dataset_loader
11
  from env.adapt_env import AdaptEnvironment
12
- from env.generator import GeneratorAgent, validate_problem
13
  from env.test_cases import load_problem_bank
14
  from models import AdaptAction
15
 
@@ -107,6 +108,26 @@ def main() -> None:
107
  assert injected_result.execution_status == "completed"
108
  assert injected_result.reward > 0.0
109
  assert 0.0 <= injected_result.reward_components.get("efficiency_score", -1.0) <= 1.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  print("Dataset mode smoke tests passed")
111
  finally:
112
  dataset_loader._BANK = original_bank
 
2
 
3
  import sys
4
  from pathlib import Path
5
+ from unittest.mock import patch
6
 
7
  ROOT = Path(__file__).resolve().parents[1]
8
  if str(ROOT) not in sys.path:
 
10
 
11
  from env import dataset_loader
12
  from env.adapt_env import AdaptEnvironment
13
+ from env.generator import GeneratorAgent, ProblemTemplate, validate_problem
14
  from env.test_cases import load_problem_bank
15
  from models import AdaptAction
16
 
 
108
  assert injected_result.execution_status == "completed"
109
  assert injected_result.reward > 0.0
110
  assert 0.0 <= injected_result.reward_components.get("efficiency_score", -1.0) <= 1.0
111
+
112
+ resilient_agent = GeneratorAgent()
113
+ good_template = next(template for template in resilient_agent.templates if template.problem_type == "sum_even_numbers")
114
+ bad_template = ProblemTemplate(
115
+ problem_type="always_bad",
116
+ difficulty_tier=good_template.difficulty_tier,
117
+ title="Always Bad",
118
+ input_format=good_template.input_format,
119
+ constraints=good_template.constraints,
120
+ statement_builder=lambda: "Bad template used for fallback testing.",
121
+ solver=good_template.solver,
122
+ case_builder=lambda rng: ["1\n1\n"] * 10,
123
+ )
124
+ resilient_agent.templates = [bad_template, *resilient_agent.templates]
125
+
126
+ with patch.object(resilient_agent, "_choose_template", return_value=bad_template):
127
+ resilient_problem = resilient_agent.generate_problem("easy", {})
128
+ assert resilient_problem["problem_type"] != "always_bad"
129
+ assert validate_problem(resilient_problem)
130
+
131
  print("Dataset mode smoke tests passed")
132
  finally:
133
  dataset_loader._BANK = original_bank