Skip to content

Commit 9c6e5b8

Browse files
authored
Fix progress score to be normalized (#1137)
## Summary Fix our progress scoring to be normalized. ## Detailed description - **Why:** `overall_score` was an un-normalized weighted sum over objectives, not a [0, 1] progress fraction. - **What:** - The progress tracker now normalized `overall_score` - `report_data` reads it directly; the `max_score` helper and the total-groups normalization it fed are removed. - **Impact:** - Backwards compatibility issue with existing benchmark results. These are now not correctly normalized by consumers in the updated code. - Acceptable. Signed-off-by: alex <amillane@nvidia.com>
1 parent dc090c1 commit 9c6e5b8

4 files changed

Lines changed: 28 additions & 19 deletions

File tree

isaaclab_arena/progress_tracking/progress_tracker.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class ProgressState:
7474
"""Per-objective state, keyed by ProgressObjective name."""
7575

7676
overall_score: float
77-
"""Sum of each objective's score weighted by ProgressObjective.score."""
77+
"""Sum of each objective's score weighted by ProgressObjective.score, normalized to [0, 1]."""
7878

7979
all_complete: bool
8080
"""Whether every objective is complete for this env."""
@@ -326,19 +326,25 @@ def get_state(self) -> list[ProgressState]:
326326
completeness = [runner.is_complete() for runner in self.runners]
327327
scores = [runner.overall_score_per_env() for runner in self.runners]
328328

329+
# Total objective weight for normalization.
330+
total_objective_weight = sum(runner.progress_objective.score for runner in self.runners)
331+
329332
output: list[ProgressState] = []
330333
for env_idx in range(self.num_envs):
331334
# Build a per-env state from each runner's state.
332335
progress_objective_states: dict[str, ProgressObjectiveState] = {}
333-
overall_score = 0.0
336+
weighted_score = 0.0
334337
all_complete = True
335338
for i, runner in enumerate(self.runners):
336339
objective = runner.progress_objective
337340
state = runner.get_state_for_env(env_idx, completeness[i][env_idx], scores[i][env_idx])
338341
progress_objective_states[objective.name] = state
339-
overall_score += objective.score * state.score
342+
weighted_score += objective.score * state.score
340343
all_complete = all_complete and state.is_complete
341344

345+
overall_score = (
346+
max(0.0, min(1.0, weighted_score / total_objective_weight)) if total_objective_weight > 0 else 0.0
347+
)
342348
output.append(
343349
ProgressState(
344350
progress_objectives=progress_objective_states,
@@ -385,7 +391,7 @@ class ProgressTrackingRecorder(RecorderTerm):
385391
),
386392
...
387393
},
388-
overall_score=float, # weighted by ProgressObjective.score
394+
overall_score=float, # weighted mean of objective scores, in [0, 1]
389395
all_complete=bool,
390396
),
391397
...

isaaclab_arena/tests/test_progress_objective_tracking.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,12 +558,17 @@ def _test_gating_sequential_task_end_to_end(simulation_app) -> bool:
558558
sm.step(env, step_index=env.episode_length_buf)
559559
assert sm.get_state()[0].progress_objectives["a"].is_complete
560560
assert not sm.get_state()[0].progress_objectives["b"].is_complete
561+
# overall_score is the weighted mean of the two objectives (a=1.0, b=0.0) -> 0.5, not the
562+
# un-normalized sum (1.0).
563+
assert abs(sm.get_state()[0].overall_score - 0.5) < SCORE_TOL
561564

562565
# Advances to subtask 1 so pred_b is now active.
563566
env._current_subtask_idx = [1]
564567
_advance_step(env)
565568
sm.step(env, step_index=env.episode_length_buf)
566569
assert sm.get_state()[0].progress_objectives["b"].is_complete
570+
# Both objectives complete now -> normalized overall_score reaches 1.0.
571+
assert abs(sm.get_state()[0].overall_score - 1.0) < SCORE_TOL
567572
except Exception as e:
568573
print(f"Error: {e}")
569574
traceback.print_exc()

isaaclab_arena/tests/test_report_data.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,22 @@ def _write_run(experiment_dir, run_name: str, records: list[dict], cameras: tupl
5959
return run_dir
6060

6161

62-
def test_progress_fraction_normalizes_by_the_achievable_score():
63-
episode = _episode({"progress": _progress({"a": 1, "b": 1, "c": 1}, [], score=1.5)})
62+
def test_progress_fraction_uses_recorded_overall_score():
63+
# overall_score is recorded already normalized to [0, 1], so it is used directly.
64+
episode = _episode({"progress": {"overall_score": 0.5}})
6465

65-
assert episode.max_score == 3.0
6666
assert episode.progress_fraction == 0.5
6767

6868

69-
def test_progress_fraction_is_none_without_recorded_objectives():
69+
def test_progress_fraction_clamps_out_of_range_overall_score():
70+
episode = _episode({"progress": {"overall_score": 1.5}})
71+
72+
assert episode.progress_fraction == 1.0
73+
74+
75+
def test_progress_fraction_is_none_without_recorded_progress():
7076
episode = _episode({"success": True})
7177

72-
assert episode.max_score is None
7378
assert episode.progress_fraction is None
7479

7580

isaaclab_arena/visualization/report_data.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,11 @@ def success(self) -> bool | None:
6666
success = self.record.get("success")
6767
return success if isinstance(success, bool) else None
6868

69-
@property
70-
def max_score(self) -> float | None:
71-
objectives = _progress_objectives(self.record)
72-
total = sum(_as_float(objective.get("total_groups")) or 0.0 for objective in objectives.values())
73-
return total if total > 0 else None
74-
7569
@property
7670
def progress_fraction(self) -> float | None:
77-
score, max_score = _as_float(_progress(self.record).get("overall_score")), self.max_score
78-
if score is None or max_score is None:
79-
return None
80-
return max(0.0, min(1.0, score / max_score))
71+
# overall_score is recorded already normalized to [0, 1] by the progress tracker.
72+
score = _as_float(_progress(self.record).get("overall_score"))
73+
return None if score is None else max(0.0, min(1.0, score))
8174

8275
@property
8376
def all_objectives_complete(self) -> bool | None:

0 commit comments

Comments
 (0)