Spaces:
Sleeping
Sleeping
Sahil Tailor commited on
Commit Β·
c477036
1
Parent(s): 6dbfea6
Added eps
Browse files- graders.py +33 -42
- inference.py +8 -1
- 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-
|
| 8 |
|
| 9 |
|
| 10 |
-
|
| 11 |
-
"""
|
|
|
|
| 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 |
-
|
| 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.
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 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 |
-
#
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
| 49 |
else:
|
| 50 |
asm_score = None
|
| 51 |
|
| 52 |
-
#
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 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 |
-
|
| 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(
|
| 73 |
|
| 74 |
# Over-steps penalty
|
| 75 |
if step_count > t.max_steps:
|
| 76 |
base_score *= 0.8
|
| 77 |
|
| 78 |
-
return
|
|
|
|
| 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
|
| 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] = {
|