anhtld commited on
Commit
0540c56
·
verified ·
1 Parent(s): 0fe62eb

auto-sync 2026-07-03T18:06:14Z workspace (part 4)

Browse files
workspace/scripts/audit_action_bounds.py CHANGED
@@ -167,6 +167,19 @@ class _Accumulator:
167
  self.max_base_action_excess = 0.0
168
  self.action_excesses: list[float] = []
169
  self.base_action_excesses: list[float] = []
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  def add(
172
  self,
@@ -183,6 +196,28 @@ class _Accumulator:
183
  base_excess = _max_bound_excess(base_action, low=low, high=high)
184
  action_violation = action_excess > tolerance
185
  base_violation = base_excess > tolerance
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  self.rows += 1
187
  self.charts.add(chart_id)
188
  self.action_violations += int(action_violation)
@@ -217,9 +252,85 @@ class _Accumulator:
217
  "max_base_action_bound_excess": self.max_base_action_excess,
218
  "p95_action_bound_excess": _percentile(self.action_excesses, 95),
219
  "p95_base_action_bound_excess": _percentile(self.base_action_excesses, 95),
 
 
 
 
 
 
 
 
 
220
  }
221
  return payload
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  def _max_bound_excess(values: np.ndarray, *, low: float, high: float) -> float:
225
  below = np.maximum(float(low) - values, 0.0)
@@ -277,6 +388,28 @@ def _report(payload: dict[str, Any]) -> str:
277
  f"{_fmt(row['max_action_bound_excess'])} | "
278
  f"{_fmt(row['p95_action_bound_excess'])} |"
279
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  lines.extend(
281
  [
282
  "",
@@ -321,6 +454,20 @@ def _fmt(value: Any) -> str:
321
  return f"{float(value):.4f}"
322
 
323
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  def _tex(value: Any) -> str:
325
  return str(value).replace("_", "\\_").replace("%", "\\%").replace("&", "\\&")
326
 
 
167
  self.max_base_action_excess = 0.0
168
  self.action_excesses: list[float] = []
169
  self.base_action_excesses: list[float] = []
170
+ self.action_dim = 0
171
+ self.action_value_count = 0
172
+ self.base_action_value_count = 0
173
+ self.base_branch_value_count = 0
174
+ self.action_abs_sum: np.ndarray | None = None
175
+ self.base_action_abs_sum: np.ndarray | None = None
176
+ self.base_branch_abs_sum: np.ndarray | None = None
177
+ self.action_abs_max: np.ndarray | None = None
178
+ self.base_action_abs_max: np.ndarray | None = None
179
+ self.base_branch_abs_max: np.ndarray | None = None
180
+ self.action_dim_violations: np.ndarray | None = None
181
+ self.base_action_dim_violations: np.ndarray | None = None
182
+ self.base_branch_dim_violations: np.ndarray | None = None
183
 
184
  def add(
185
  self,
 
196
  base_excess = _max_bound_excess(base_action, low=low, high=high)
197
  action_violation = action_excess > tolerance
198
  base_violation = base_excess > tolerance
199
+ self._add_dim_stats(
200
+ action,
201
+ attr_prefix="action",
202
+ low=low,
203
+ high=high,
204
+ tolerance=tolerance,
205
+ )
206
+ self._add_dim_stats(
207
+ base_action,
208
+ attr_prefix="base_action",
209
+ low=low,
210
+ high=high,
211
+ tolerance=tolerance,
212
+ )
213
+ if is_base:
214
+ self._add_dim_stats(
215
+ action,
216
+ attr_prefix="base_branch",
217
+ low=low,
218
+ high=high,
219
+ tolerance=tolerance,
220
+ )
221
  self.rows += 1
222
  self.charts.add(chart_id)
223
  self.action_violations += int(action_violation)
 
252
  "max_base_action_bound_excess": self.max_base_action_excess,
253
  "p95_action_bound_excess": _percentile(self.action_excesses, 95),
254
  "p95_base_action_bound_excess": _percentile(self.base_action_excesses, 95),
255
+ "per_dim": {
256
+ "action": self._dim_payload("action"),
257
+ "base_action": self._dim_payload("base_action"),
258
+ "base_branch": self._dim_payload("base_branch"),
259
+ },
260
+ "suggested_global_scale_all_action_max": _safe_inverse(self._max_abs("action")),
261
+ "suggested_global_scale_all_base_branch_max": _safe_inverse(
262
+ self._max_abs("base_branch")
263
+ ),
264
  }
265
  return payload
266
 
267
+ def _ensure_dim(self, dim: int) -> None:
268
+ if dim <= 0:
269
+ raise ValueError("action dimension must be positive")
270
+ if self.action_dim == 0:
271
+ self.action_dim = dim
272
+ for prefix in ("action", "base_action", "base_branch"):
273
+ setattr(self, f"{prefix}_abs_sum", np.zeros(dim, dtype=np.float64))
274
+ setattr(self, f"{prefix}_abs_max", np.zeros(dim, dtype=np.float64))
275
+ setattr(self, f"{prefix}_dim_violations", np.zeros(dim, dtype=np.int64))
276
+ return
277
+ if dim != self.action_dim:
278
+ raise ValueError(f"mixed action dimensions in accumulator: {dim} vs {self.action_dim}")
279
+
280
+ def _add_dim_stats(
281
+ self,
282
+ values: np.ndarray,
283
+ *,
284
+ attr_prefix: str,
285
+ low: float,
286
+ high: float,
287
+ tolerance: float,
288
+ ) -> None:
289
+ matrix = np.asarray(values, dtype=np.float32).reshape(-1, values.shape[-1])
290
+ self._ensure_dim(int(matrix.shape[-1]))
291
+ abs_values = np.abs(matrix).astype(np.float64, copy=False)
292
+ below = np.maximum(float(low) - matrix, 0.0)
293
+ above = np.maximum(matrix - float(high), 0.0)
294
+ violations = np.maximum(below, above) > float(tolerance)
295
+ count_attr = f"{attr_prefix}_value_count"
296
+ setattr(self, count_attr, int(getattr(self, count_attr)) + int(matrix.shape[0]))
297
+ abs_sum = getattr(self, f"{attr_prefix}_abs_sum")
298
+ abs_max = getattr(self, f"{attr_prefix}_abs_max")
299
+ dim_violations = getattr(self, f"{attr_prefix}_dim_violations")
300
+ if abs_sum is None or abs_max is None or dim_violations is None:
301
+ raise RuntimeError("dimension accumulators were not initialized")
302
+ abs_sum += np.sum(abs_values, axis=0)
303
+ np.maximum(abs_max, np.max(abs_values, axis=0), out=abs_max)
304
+ dim_violations += np.sum(violations, axis=0).astype(np.int64)
305
+
306
+ def _dim_payload(self, attr_prefix: str) -> dict[str, Any]:
307
+ count = int(getattr(self, f"{attr_prefix}_value_count"))
308
+ abs_sum = getattr(self, f"{attr_prefix}_abs_sum")
309
+ abs_max = getattr(self, f"{attr_prefix}_abs_max")
310
+ dim_violations = getattr(self, f"{attr_prefix}_dim_violations")
311
+ if count == 0 or abs_sum is None or abs_max is None or dim_violations is None:
312
+ return {
313
+ "value_count": count,
314
+ "mean_abs": [],
315
+ "max_abs": [],
316
+ "violation_rate": [],
317
+ }
318
+ return {
319
+ "value_count": count,
320
+ "mean_abs": (abs_sum / float(count)).tolist(),
321
+ "max_abs": abs_max.tolist(),
322
+ "violation_rate": (dim_violations / float(count)).tolist(),
323
+ "suggested_per_dim_scale_to_unit_max": [
324
+ _safe_inverse(float(value)) for value in abs_max.tolist()
325
+ ],
326
+ }
327
+
328
+ def _max_abs(self, attr_prefix: str) -> float:
329
+ abs_max = getattr(self, f"{attr_prefix}_abs_max")
330
+ if abs_max is None or not np.size(abs_max):
331
+ return float("nan")
332
+ return float(np.max(abs_max))
333
+
334
 
335
  def _max_bound_excess(values: np.ndarray, *, low: float, high: float) -> float:
336
  below = np.maximum(float(low) - values, 0.0)
 
388
  f"{_fmt(row['max_action_bound_excess'])} | "
389
  f"{_fmt(row['p95_action_bound_excess'])} |"
390
  )
391
+ lines.extend(
392
+ [
393
+ "",
394
+ "## Scale Diagnostics",
395
+ "",
396
+ "| Split | Action max scale | Base-branch max scale | Action per-dim max | Base-branch per-dim max | Action per-dim violation | Base-branch per-dim violation |",
397
+ "| --- | ---: | ---: | --- | --- | --- | --- |",
398
+ ]
399
+ )
400
+ for row in payload["rows"]:
401
+ per_dim = row.get("per_dim", {})
402
+ action = per_dim.get("action", {})
403
+ base_branch = per_dim.get("base_branch", {})
404
+ lines.append(
405
+ f"| {row['split']} | "
406
+ f"{_fmt(row.get('suggested_global_scale_all_action_max'))} | "
407
+ f"{_fmt(row.get('suggested_global_scale_all_base_branch_max'))} | "
408
+ f"{_fmt_list(action.get('max_abs'))} | "
409
+ f"{_fmt_list(base_branch.get('max_abs'))} | "
410
+ f"{_fmt_list(action.get('violation_rate'))} | "
411
+ f"{_fmt_list(base_branch.get('violation_rate'))} |"
412
+ )
413
  lines.extend(
414
  [
415
  "",
 
454
  return f"{float(value):.4f}"
455
 
456
 
457
+ def _fmt_list(values: Any) -> str:
458
+ if not isinstance(values, list):
459
+ return "n/a"
460
+ return "[" + ", ".join(_fmt(value) for value in values) + "]"
461
+
462
+
463
+ def _safe_inverse(value: Any) -> float | None:
464
+ if not isinstance(value, (int, float)) or not math.isfinite(float(value)):
465
+ return None
466
+ if float(value) <= 0.0:
467
+ return None
468
+ return 1.0 / float(value)
469
+
470
+
471
  def _tex(value: Any) -> str:
472
  return str(value).replace("_", "\\_").replace("%", "\\%").replace("&", "\\&")
473
 
workspace/scripts/eval_ctt_generated_rollout.py CHANGED
@@ -74,6 +74,17 @@ def main(argv: list[str] | None = None) -> int:
74
  parser.add_argument("--render-backend", default=None)
75
  parser.add_argument("--restore-tolerance", type=float, default=1.0e-5)
76
  parser.add_argument("--delta-scale", type=float, default=1.0)
 
 
 
 
 
 
 
 
 
 
 
77
  parser.add_argument(
78
  "--disable-action-clipping",
79
  action="store_true",
@@ -106,6 +117,8 @@ def main(argv: list[str] | None = None) -> int:
106
  parser.error("--max-target-charts must be positive")
107
  if args.restore_tolerance <= 0.0:
108
  parser.error("--restore-tolerance must be positive")
 
 
109
 
110
  out_dir = args.out_dir
111
  out_dir.mkdir(parents=True, exist_ok=True)
@@ -212,6 +225,7 @@ def main(argv: list[str] | None = None) -> int:
212
  render_backend=args.render_backend,
213
  restore_tolerance=args.restore_tolerance,
214
  clip_actions=not args.disable_action_clipping,
 
215
  log_path=log_path,
216
  )
217
  _append_log(log_path, f"rollout complete rows={len(rows)}")
@@ -236,6 +250,7 @@ def main(argv: list[str] | None = None) -> int:
236
  "source_code": "spline_tangent_code stores start/mid/end residual keyframes",
237
  "lossless": False,
238
  "delta_scale": args.delta_scale,
 
239
  "action_clipping_enabled": not args.disable_action_clipping,
240
  },
241
  "rows": rows,
@@ -525,6 +540,7 @@ def rollout_generated_cases(
525
  render_backend: str | None,
526
  restore_tolerance: float,
527
  clip_actions: bool,
 
528
  log_path: Path | None = None,
529
  ) -> list[dict[str, Any]]:
530
  archives: dict[Path, dict[str, Any]] = {}
@@ -565,6 +581,7 @@ def rollout_generated_cases(
565
  archives=archives,
566
  restore_tolerance=restore_tolerance,
567
  clip_actions=clip_actions,
 
568
  log_path=log_path,
569
  )
570
  )
@@ -595,13 +612,14 @@ def rollout_generated_cases(
595
  action_groups.append(np.stack(group_actions, axis=0))
596
  candidate_values = np.stack(action_groups, axis=0).astype(np.float32)
597
  candidate_values = _adapt_action_dim_4d(candidate_values, env_dim)
 
598
  safety = _action_bound_diagnostics_4d(candidate_values, env)
599
  if clip_actions:
600
  candidate_values = _clip_to_action_space_4d(candidate_values, env)
601
  _append_log(
602
  log_path,
603
  f"execute task={task_id} start={start} shape={candidate_values.shape} "
604
- f"clip_actions={clip_actions}",
605
  )
606
  _after_state, rewards, successes, restore_error = execute_grouped_action_lattice_batch(
607
  base_env,
@@ -643,6 +661,7 @@ def rollout_generated_cases(
643
  count=valid,
644
  ),
645
  restore_error=float(restore_error),
 
646
  )
647
  )
648
  finally:
@@ -662,6 +681,7 @@ def _rollout_cpu_sequential_batch(
662
  archives: dict[Path, dict[str, Any]],
663
  restore_tolerance: float,
664
  clip_actions: bool,
 
665
  log_path: Path | None,
666
  ) -> list[dict[str, Any]]:
667
  rows: list[dict[str, Any]] = []
@@ -691,6 +711,7 @@ def _rollout_cpu_sequential_batch(
691
  1, 1, *action.shape
692
  )
693
  candidate_values = _adapt_action_dim_4d(candidate_values, env_dim)
 
694
  safety = _action_bound_diagnostics_4d(candidate_values, env)
695
  if clip_actions:
696
  candidate_values = _clip_to_action_space_4d(candidate_values, env)
@@ -698,7 +719,7 @@ def _rollout_cpu_sequential_batch(
698
  log_path,
699
  f"execute sequential task={task_id} chart={target.chart_id} "
700
  f"candidate={candidate_index} shape={candidate_values.shape} "
701
- f"clip_actions={clip_actions}",
702
  )
703
  _after_state, rewards, successes, restore_error = execute_grouped_action_lattice_batch(
704
  base_env,
@@ -727,6 +748,7 @@ def _rollout_cpu_sequential_batch(
727
  safety_violations=safety_violations,
728
  action_clip_max_abs=action_clip_max_abs,
729
  restore_error=max(restore_errors, default=0.0),
 
730
  )
731
  )
732
  finally:
@@ -745,6 +767,7 @@ def _measured_row_from_rollout(
745
  safety_violations: list[bool | None] | None = None,
746
  action_clip_max_abs: list[float | None] | None = None,
747
  restore_error: float,
 
748
  ) -> dict[str, Any]:
749
  if safety_violations is None:
750
  safety_violations = [None] * len(utilities)
@@ -765,6 +788,7 @@ def _measured_row_from_rollout(
765
  "state_hash": target.state_hash,
766
  "instruction": target.instruction,
767
  "candidates_evaluated": True,
 
768
  "selected_index": 0,
769
  "base_utility": base_utility,
770
  "stored_base_utility": target.stored_base_utility,
@@ -893,6 +917,14 @@ def _adapt_action_dim_4d(actions: np.ndarray, action_dim: int) -> np.ndarray:
893
  return np.concatenate([actions.astype(np.float32, copy=False), pad], axis=-1)
894
 
895
 
 
 
 
 
 
 
 
 
896
  def _clip_to_action_space_4d(actions: np.ndarray, env: Any) -> np.ndarray:
897
  bounds = _action_space_bounds(actions, env)
898
  if bounds is None:
 
74
  parser.add_argument("--render-backend", default=None)
75
  parser.add_argument("--restore-tolerance", type=float, default=1.0e-5)
76
  parser.add_argument("--delta-scale", type=float, default=1.0)
77
+ parser.add_argument(
78
+ "--execution-action-scale",
79
+ type=float,
80
+ default=1.0,
81
+ help=(
82
+ "Multiply the full base/generated action chunk before action-bound "
83
+ "diagnostics, optional clipping, and simulator execution. This is a "
84
+ "diagnostic for action-representation scale mismatch; keep the default "
85
+ "1.0 for the unscaled deployment convention."
86
+ ),
87
+ )
88
  parser.add_argument(
89
  "--disable-action-clipping",
90
  action="store_true",
 
117
  parser.error("--max-target-charts must be positive")
118
  if args.restore_tolerance <= 0.0:
119
  parser.error("--restore-tolerance must be positive")
120
+ if args.execution_action_scale <= 0.0:
121
+ parser.error("--execution-action-scale must be positive")
122
 
123
  out_dir = args.out_dir
124
  out_dir.mkdir(parents=True, exist_ok=True)
 
225
  render_backend=args.render_backend,
226
  restore_tolerance=args.restore_tolerance,
227
  clip_actions=not args.disable_action_clipping,
228
+ execution_action_scale=args.execution_action_scale,
229
  log_path=log_path,
230
  )
231
  _append_log(log_path, f"rollout complete rows={len(rows)}")
 
250
  "source_code": "spline_tangent_code stores start/mid/end residual keyframes",
251
  "lossless": False,
252
  "delta_scale": args.delta_scale,
253
+ "execution_action_scale": args.execution_action_scale,
254
  "action_clipping_enabled": not args.disable_action_clipping,
255
  },
256
  "rows": rows,
 
540
  render_backend: str | None,
541
  restore_tolerance: float,
542
  clip_actions: bool,
543
+ execution_action_scale: float = 1.0,
544
  log_path: Path | None = None,
545
  ) -> list[dict[str, Any]]:
546
  archives: dict[Path, dict[str, Any]] = {}
 
581
  archives=archives,
582
  restore_tolerance=restore_tolerance,
583
  clip_actions=clip_actions,
584
+ execution_action_scale=execution_action_scale,
585
  log_path=log_path,
586
  )
587
  )
 
612
  action_groups.append(np.stack(group_actions, axis=0))
613
  candidate_values = np.stack(action_groups, axis=0).astype(np.float32)
614
  candidate_values = _adapt_action_dim_4d(candidate_values, env_dim)
615
+ candidate_values = _scale_actions_4d(candidate_values, execution_action_scale)
616
  safety = _action_bound_diagnostics_4d(candidate_values, env)
617
  if clip_actions:
618
  candidate_values = _clip_to_action_space_4d(candidate_values, env)
619
  _append_log(
620
  log_path,
621
  f"execute task={task_id} start={start} shape={candidate_values.shape} "
622
+ f"clip_actions={clip_actions} execution_action_scale={execution_action_scale}",
623
  )
624
  _after_state, rewards, successes, restore_error = execute_grouped_action_lattice_batch(
625
  base_env,
 
661
  count=valid,
662
  ),
663
  restore_error=float(restore_error),
664
+ execution_action_scale=execution_action_scale,
665
  )
666
  )
667
  finally:
 
681
  archives: dict[Path, dict[str, Any]],
682
  restore_tolerance: float,
683
  clip_actions: bool,
684
+ execution_action_scale: float,
685
  log_path: Path | None,
686
  ) -> list[dict[str, Any]]:
687
  rows: list[dict[str, Any]] = []
 
711
  1, 1, *action.shape
712
  )
713
  candidate_values = _adapt_action_dim_4d(candidate_values, env_dim)
714
+ candidate_values = _scale_actions_4d(candidate_values, execution_action_scale)
715
  safety = _action_bound_diagnostics_4d(candidate_values, env)
716
  if clip_actions:
717
  candidate_values = _clip_to_action_space_4d(candidate_values, env)
 
719
  log_path,
720
  f"execute sequential task={task_id} chart={target.chart_id} "
721
  f"candidate={candidate_index} shape={candidate_values.shape} "
722
+ f"clip_actions={clip_actions} execution_action_scale={execution_action_scale}",
723
  )
724
  _after_state, rewards, successes, restore_error = execute_grouped_action_lattice_batch(
725
  base_env,
 
748
  safety_violations=safety_violations,
749
  action_clip_max_abs=action_clip_max_abs,
750
  restore_error=max(restore_errors, default=0.0),
751
+ execution_action_scale=execution_action_scale,
752
  )
753
  )
754
  finally:
 
767
  safety_violations: list[bool | None] | None = None,
768
  action_clip_max_abs: list[float | None] | None = None,
769
  restore_error: float,
770
+ execution_action_scale: float = 1.0,
771
  ) -> dict[str, Any]:
772
  if safety_violations is None:
773
  safety_violations = [None] * len(utilities)
 
788
  "state_hash": target.state_hash,
789
  "instruction": target.instruction,
790
  "candidates_evaluated": True,
791
+ "execution_action_scale": float(execution_action_scale),
792
  "selected_index": 0,
793
  "base_utility": base_utility,
794
  "stored_base_utility": target.stored_base_utility,
 
917
  return np.concatenate([actions.astype(np.float32, copy=False), pad], axis=-1)
918
 
919
 
920
+ def _scale_actions_4d(actions: np.ndarray, scale: float) -> np.ndarray:
921
+ if not math.isfinite(float(scale)) or float(scale) <= 0.0:
922
+ raise ValueError("execution action scale must be positive and finite")
923
+ if float(scale) == 1.0:
924
+ return actions.astype(np.float32, copy=False)
925
+ return (actions.astype(np.float32, copy=False) * float(scale)).astype(np.float32, copy=False)
926
+
927
+
928
  def _clip_to_action_space_4d(actions: np.ndarray, env: Any) -> np.ndarray:
929
  bounds = _action_space_bounds(actions, env)
930
  if bounds is None:
workspace/scripts/slurm/eval_ctt_generated_rollout.sbatch CHANGED
@@ -36,6 +36,7 @@ SIM_BACKEND="${SIM_BACKEND:-physx_cpu}"
36
  RENDER_BACKEND="${RENDER_BACKEND:-cpu}"
37
  RESTORE_TOLERANCE="${RESTORE_TOLERANCE:-1e-5}"
38
  DELTA_SCALE="${DELTA_SCALE:-1.0}"
 
39
  BOOTSTRAP_SAMPLES="${BOOTSTRAP_SAMPLES:-200}"
40
  INCLUDE_TARGETS_WITHOUT_POSITIVES="${INCLUDE_TARGETS_WITHOUT_POSITIVES:-0}"
41
  EXCLUDE_SELF_SOURCE="${EXCLUDE_SELF_SOURCE:-0}"
@@ -89,5 +90,6 @@ apptainer exec \
89
  --render-backend "$RENDER_BACKEND" \
90
  --restore-tolerance "$RESTORE_TOLERANCE" \
91
  --delta-scale "$DELTA_SCALE" \
 
92
  --bootstrap-samples "$BOOTSTRAP_SAMPLES" \
93
  "${EXTRA_ARGS[@]}"
 
36
  RENDER_BACKEND="${RENDER_BACKEND:-cpu}"
37
  RESTORE_TOLERANCE="${RESTORE_TOLERANCE:-1e-5}"
38
  DELTA_SCALE="${DELTA_SCALE:-1.0}"
39
+ EXECUTION_ACTION_SCALE="${EXECUTION_ACTION_SCALE:-1.0}"
40
  BOOTSTRAP_SAMPLES="${BOOTSTRAP_SAMPLES:-200}"
41
  INCLUDE_TARGETS_WITHOUT_POSITIVES="${INCLUDE_TARGETS_WITHOUT_POSITIVES:-0}"
42
  EXCLUDE_SELF_SOURCE="${EXCLUDE_SELF_SOURCE:-0}"
 
90
  --render-backend "$RENDER_BACKEND" \
91
  --restore-tolerance "$RESTORE_TOLERANCE" \
92
  --delta-scale "$DELTA_SCALE" \
93
+ --execution-action-scale "$EXECUTION_ACTION_SCALE" \
94
  --bootstrap-samples "$BOOTSTRAP_SAMPLES" \
95
  "${EXTRA_ARGS[@]}"