Sahil Tailor commited on
Commit
c477036
Β·
1 Parent(s): 6dbfea6

Added eps

Browse files
Files changed (3) hide show
  1. graders.py +33 -42
  2. inference.py +8 -1
  3. tasks.py +0 -8
graders.py CHANGED
@@ -4,75 +4,66 @@ from typing import Any, Dict
4
 
5
  from .tasks import TASK_REGISTRY, Task
6
 
7
- _EPS = 1e-6 # keeps scores strictly inside (0, 1) as required by the platform
8
 
9
 
10
- class ManufacturingTaskGrader:
11
- """Discriminating scorer for a completed episode.
 
12
 
13
- Sub-scores:
14
- 1. on_time_delivery_rate β€” on_time_deliveries / total delivery windows
15
- 2. reward_efficiency β€” total_reward / task.target_reward
16
- 3. assembly_rate β€” assemblies_completed / total assembly orders (if any)
17
- 4. energy_health β€” mean final energy / ENERGY_MAX (100)
18
- 5. invalid_action_penalty β€” 1 - invalid_actions / max_invalid_actions
19
 
20
- Weights: delivery Γ— 3, reward_efficiency Γ— 2, assembly Γ— 1.5, energy Γ— 1, penalty Γ— 1
21
- """
22
 
23
  def __init__(self, task_name: str) -> None:
24
  if task_name not in TASK_REGISTRY:
25
  raise ValueError(f"Unknown task '{task_name}'.")
26
  self.task: Task = TASK_REGISTRY[task_name]
27
 
28
- def grade(
29
- self,
30
- metrics: Dict[str, Any],
31
- step_count: int,
32
- platforms: list,
33
- total_reward: float = 0.0,
34
- ) -> float:
35
  t = self.task
36
 
37
- # 1. On-time delivery rate (naturally ≀ 1 since windows are removed on match)
38
- total_windows = len(t.delivery_windows)
39
- on_time = metrics.get("on_time_deliveries", 0)
40
- delivery_score = min(1.0, on_time / max(total_windows, 1))
41
-
42
- # 2. Reward efficiency (cap at 1.0; target_reward sits above empirical perfect)
43
- reward_score = min(1.0, total_reward / t.target_reward)
44
 
45
- # 3. Assembly rate (only for tasks with assembly orders)
46
- total_assemblies = sum(1 for o in t.pending_orders if o.requires_assembly)
47
- if total_assemblies > 0:
48
- asm_score = min(1.0, metrics.get("assemblies_completed", 0) / total_assemblies)
 
49
  else:
50
  asm_score = None
51
 
52
- # 4. Energy health β€” mean final energy / 100 (capped at 1.0)
53
- energy_score = min(1.0, (
54
- sum(p.energy for p in platforms) / (100.0 * len(platforms))
55
- if platforms else 0.0
56
- ))
 
 
 
 
 
 
57
 
58
- # 5. Invalid-action penalty
59
  inv = metrics.get("invalid_actions", 0)
60
  if t.max_invalid_actions > 0:
61
- invalid_penalty = max(0.0, 1.0 - inv / t.max_invalid_actions)
62
  else:
63
- invalid_penalty = 1.0 if inv == 0 else 0.0
64
 
65
  # Weighted average
66
- weights = [3.0, 2.0, 1.0, 1.0]
67
- scores = [delivery_score, reward_score, energy_score, invalid_penalty]
68
  if asm_score is not None:
69
- weights.append(1.5)
70
  scores.append(asm_score)
71
 
72
- base_score = sum(w * s for w, s in zip(weights, scores)) / sum(weights)
73
 
74
  # Over-steps penalty
75
  if step_count > t.max_steps:
76
  base_score *= 0.8
77
 
78
- return max(_EPS, min(1.0 - _EPS, base_score))
 
4
 
5
  from .tasks import TASK_REGISTRY, Task
6
 
7
+ _EPS = 1e-3 # keeps every score in the open interval (0, 1)
8
 
9
 
10
+ def _clamp(value: float) -> float:
11
+ """Clamp *value* to the open interval (0, 1) exclusive."""
12
+ return max(_EPS, min(1.0 - _EPS, value))
13
 
 
 
 
 
 
 
14
 
15
+ class ManufacturingTaskGrader:
16
+ """Deterministic 0.0–1.0 scorer for a completed episode."""
17
 
18
  def __init__(self, task_name: str) -> None:
19
  if task_name not in TASK_REGISTRY:
20
  raise ValueError(f"Unknown task '{task_name}'.")
21
  self.task: Task = TASK_REGISTRY[task_name]
22
 
23
+ def grade(self, metrics: Dict[str, Any], step_count: int, platforms: list) -> float:
 
 
 
 
 
 
24
  t = self.task
25
 
26
+ # 1. Production runs
27
+ prod_score = _clamp(
28
+ min(1.0, metrics.get("production_runs", 0) / max(t.min_production_runs, 1))
29
+ )
 
 
 
30
 
31
+ # 2. Assemblies (skip if not required)
32
+ if t.min_assemblies > 0:
33
+ asm_score = _clamp(
34
+ min(1.0, metrics.get("assemblies_completed", 0) / t.min_assemblies)
35
+ )
36
  else:
37
  asm_score = None
38
 
39
+ # 3. Deliveries
40
+ del_score = _clamp(
41
+ min(1.0, metrics.get("deliveries_completed", 0) / max(t.min_deliveries, 1))
42
+ )
43
+
44
+ # 4. Energy health (average final energy across platforms)
45
+ if platforms:
46
+ avg_energy = sum(p.energy for p in platforms) / len(platforms)
47
+ else:
48
+ avg_energy = 0.0
49
+ energy_score = _clamp(min(1.0, avg_energy / max(t.min_energy_final, 1.0)))
50
 
51
+ # 5. Invalid-action penalty (1.0 = no invalids, 0.0 = at or above max)
52
  inv = metrics.get("invalid_actions", 0)
53
  if t.max_invalid_actions > 0:
54
+ invalid_penalty = _clamp(max(0.0, 1.0 - inv / t.max_invalid_actions))
55
  else:
56
+ invalid_penalty = _clamp(1.0 if inv == 0 else 0.0)
57
 
58
  # Weighted average
59
+ scores = [prod_score, del_score, energy_score, invalid_penalty]
 
60
  if asm_score is not None:
 
61
  scores.append(asm_score)
62
 
63
+ base_score = sum(scores) / len(scores)
64
 
65
  # Over-steps penalty
66
  if step_count > t.max_steps:
67
  base_score *= 0.8
68
 
69
+ return _clamp(round(base_score, 4))
inference.py CHANGED
@@ -119,6 +119,13 @@ TASK_TYPES = {"easy": EasyTask, "medium": MediumTask, "hard": HardTask}
119
  VALID_ACTIONS = {"produce", "assemble", "deliver", "recharge"}
120
  ACTION_PATTERN = re.compile(r"(produce|assemble|deliver|recharge)", re.IGNORECASE)
121
 
 
 
 
 
 
 
 
122
  # ── system prompt ──────────────────────────────────────────────────────────────
123
  SYSTEM_PROMPT = textwrap.dedent("""
124
  You are controlling orbital manufacturing platforms.
@@ -491,7 +498,7 @@ async def run_task(task_name: str, client: Optional[Any]) -> TaskRunResult:
491
 
492
  final_state = env.state()
493
  metrics = {k: float(v) for k, v in final_state.metrics.items()}
494
- score = grader.grade(metrics, final_state.step_count, final_state.platforms, final_state.total_reward)
495
 
496
  print(
497
  f"[END] task={task_name} score={score:.4f}"
 
119
  VALID_ACTIONS = {"produce", "assemble", "deliver", "recharge"}
120
  ACTION_PATTERN = re.compile(r"(produce|assemble|deliver|recharge)", re.IGNORECASE)
121
 
122
+ _SCORE_EPS = 1e-9 # keeps every score strictly inside (0, 1)
123
+
124
+
125
+ def _clamp_score(value: float) -> float:
126
+ """Clamp *value* to the open interval (0, 1) exclusive."""
127
+ return max(_SCORE_EPS, min(1.0 - _SCORE_EPS, float(value)))
128
+
129
  # ── system prompt ──────────────────────────────────────────────────────────────
130
  SYSTEM_PROMPT = textwrap.dedent("""
131
  You are controlling orbital manufacturing platforms.
 
498
 
499
  final_state = env.state()
500
  metrics = {k: float(v) for k, v in final_state.metrics.items()}
501
+ score = _clamp_score(grader.grade(metrics, final_state.step_count, final_state.platforms))
502
 
503
  print(
504
  f"[END] task={task_name} score={score:.4f}"
tasks.py CHANGED
@@ -23,11 +23,6 @@ class Task(ABC):
23
  min_energy_final: float
24
  max_invalid_actions: int
25
 
26
- # Reward ceiling used by the grader β€” set above the empirical perfect score
27
- # so that reward_score = total_reward / target_reward < 1.0 even for a
28
- # flawless run, giving continuous signal instead of a hard cap.
29
- target_reward: float
30
-
31
 
32
  class EasyTask(Task):
33
  name = "easy"
@@ -55,7 +50,6 @@ class EasyTask(Task):
55
  min_deliveries = 2
56
  min_energy_final = 40.0
57
  max_invalid_actions = 3
58
- target_reward = 260.0 # ~12% above empirical perfect (231.50)
59
 
60
 
61
  class MediumTask(Task):
@@ -101,7 +95,6 @@ class MediumTask(Task):
101
  min_deliveries = 6
102
  min_energy_final = 25.0
103
  max_invalid_actions = 8
104
- target_reward = 800.0 # ~12% above empirical perfect (712.80)
105
 
106
 
107
  class HardTask(Task):
@@ -178,7 +171,6 @@ class HardTask(Task):
178
  min_deliveries = 14
179
  min_energy_final = 15.0
180
  max_invalid_actions = 12
181
- target_reward = 1900.0 # ~12% above empirical perfect (1704.40)
182
 
183
 
184
  TASK_REGISTRY: Dict[str, Task] = {
 
23
  min_energy_final: float
24
  max_invalid_actions: int
25
 
 
 
 
 
 
26
 
27
  class EasyTask(Task):
28
  name = "easy"
 
50
  min_deliveries = 2
51
  min_energy_final = 40.0
52
  max_invalid_actions = 3
 
53
 
54
 
55
  class MediumTask(Task):
 
95
  min_deliveries = 6
96
  min_energy_final = 25.0
97
  max_invalid_actions = 8
 
98
 
99
 
100
  class HardTask(Task):
 
171
  min_deliveries = 14
172
  min_energy_final = 15.0
173
  max_invalid_actions = 12
 
174
 
175
 
176
  TASK_REGISTRY: Dict[str, Task] = {