Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from copy import deepcopy | |
| from dataclasses import dataclass | |
| from statistics import mean | |
| from .config import price_band | |
| from .environment import V2SupplyMindEnv | |
| from .models import CenterAction, V2JointAction, WarehouseAction | |
| from .policies import heuristic_joint_policy, naive_joint_policy | |
| HORIZON = 7 | |
| BEAM_DEPTH = 2 | |
| BEAM_WIDTH = 2 | |
| MAX_CANDIDATES = 10 | |
| FOLLOW_UP_CANDIDATES = 4 | |
| HARD_BEAM_WIDTH = 3 | |
| HARD_MAX_CANDIDATES = 12 | |
| HARD_FOLLOW_UP_CANDIDATES = 5 | |
| MEDIUM_BEAM_WIDTH = 3 | |
| MEDIUM_MAX_CANDIDATES = 12 | |
| MEDIUM_FOLLOW_UP_CANDIDATES = 5 | |
| class RolloutStats: | |
| global_reward: float | |
| center_reward: float | |
| average_warehouse_reward: float | |
| def rollout_reference(task_id: str, seed: int) -> float: | |
| return rollout_reference_stats(task_id, seed).global_reward | |
| def rollout_reference_stats(task_id: str, seed: int) -> RolloutStats: | |
| candidates = ( | |
| _rollout_planner(task_id, seed), | |
| _rollout_policy(task_id, seed, naive_joint_policy), | |
| _rollout_policy(task_id, seed, heuristic_joint_policy), | |
| ) | |
| return RolloutStats( | |
| global_reward=max(item.global_reward for item in candidates), | |
| center_reward=max(item.center_reward for item in candidates), | |
| average_warehouse_reward=max(item.average_warehouse_reward for item in candidates), | |
| ) | |
| def privileged_reference_policy(observation) -> V2JointAction: | |
| env = V2SupplyMindEnv(default_task_id=observation.task_id) | |
| env.reset_internal(observation.task_id, observation.scenario_info["seed"]) | |
| while env.round_index < observation.round_index and not env.done: | |
| result = env.step(_best_action(env, env.state()), grade_terminal=False) | |
| return _best_action(env, observation) | |
| def _rollout_policy(task_id: str, seed: int, policy) -> RolloutStats: | |
| env = V2SupplyMindEnv(default_task_id=task_id) | |
| observation = env.reset_internal(task_id, seed) | |
| while not env.done: | |
| result = env.step(policy(observation), grade_terminal=False) | |
| observation = result.observation | |
| return _stats_from_env(env) | |
| def _rollout_planner(task_id: str, seed: int) -> RolloutStats: | |
| env = V2SupplyMindEnv(default_task_id=task_id) | |
| observation = env.reset_internal(task_id, seed) | |
| while not env.done: | |
| result = env.step(_best_action(env, observation), grade_terminal=False) | |
| observation = result.observation | |
| return _stats_from_env(env) | |
| def _stats_from_env(env: V2SupplyMindEnv) -> RolloutStats: | |
| warehouse_rewards = [value for key, value in env.agent_rewards.items() if key != "center"] | |
| return RolloutStats( | |
| global_reward=env.cumulative_reward, | |
| center_reward=env.agent_rewards.get("center", 0.0), | |
| average_warehouse_reward=mean(warehouse_rewards) if warehouse_rewards else 0.0, | |
| ) | |
| def _best_action(env: V2SupplyMindEnv, observation) -> V2JointAction: | |
| candidates = _centralized_candidates(env, observation, HORIZON) | |
| best = candidates[0] | |
| best_value = float("-inf") | |
| for action in candidates: | |
| clone = deepcopy(env) | |
| before = clone.cumulative_reward | |
| result = clone.step(action, grade_terminal=False) | |
| value = clone.cumulative_reward - before | |
| if not result.done: | |
| value += _beam_value(clone, result.observation, BEAM_DEPTH) | |
| if value > best_value: | |
| best_value = value | |
| best = action | |
| return best | |
| def _beam_value(env: V2SupplyMindEnv, observation, depth: int) -> float: | |
| start = env.cumulative_reward | |
| beam_width = _beam_width(env) | |
| follow_up_candidates = _follow_up_candidates(env) | |
| beam: list[tuple[float, V2SupplyMindEnv, object]] = [(env.cumulative_reward, env, observation)] | |
| for remaining in range(depth, 0, -1): | |
| expanded: list[tuple[float, V2SupplyMindEnv, object]] = [] | |
| for _, state_env, state_observation in beam: | |
| if state_env.done: | |
| expanded.append((state_env.cumulative_reward, state_env, state_observation)) | |
| continue | |
| candidates = _centralized_candidates(state_env, state_observation, max(3, remaining + 2))[:follow_up_candidates] | |
| for action in candidates: | |
| clone = deepcopy(state_env) | |
| result = clone.step(action, grade_terminal=False) | |
| expanded.append((clone.cumulative_reward, clone, result.observation)) | |
| if not expanded: | |
| break | |
| expanded.sort(key=lambda item: item[0], reverse=True) | |
| beam = expanded[:beam_width] | |
| return max((item[0] for item in beam), default=start) - start | |
| class Demand: | |
| units: int = 0 | |
| value: float = 0.0 | |
| urgent_units: int = 0 | |
| class Mode: | |
| name: str | |
| demand_cover: float | |
| safety_buffer: int | |
| accept_slack: int | |
| allow_procurement: bool = True | |
| allow_transfers: bool = True | |
| allow_liquidation: bool = True | |
| MODES = ( | |
| Mode("balanced", 0.75, 1, 1), | |
| Mode("service_aggressive", 1.00, 1, 0), | |
| Mode("burst_buffer", 1.10, 2, 1), | |
| Mode("cost_conservative", 0.55, 0, 1, allow_procurement=False), | |
| Mode("transfer_first", 0.90, 1, 1, allow_procurement=False), | |
| Mode("perishable_guard", 0.65, 0, 1, allow_liquidation=True), | |
| ) | |
| def _centralized_candidates(env: V2SupplyMindEnv, observation, horizon: int) -> list[V2JointAction]: | |
| pressure = _future_demand(env, observation, horizon) | |
| candidates = [ | |
| _centralized_action(observation, pressure, mode) | |
| for mode in MODES | |
| ] | |
| candidates.append(heuristic_joint_policy(observation)) | |
| candidates.append(naive_joint_policy(observation)) | |
| candidates.append(V2JointAction()) | |
| return _dedupe(candidates)[:_candidate_limit(env)] | |
| def _is_hard(env: V2SupplyMindEnv) -> bool: | |
| return "hard" in env.internal_task_id | |
| def _is_medium(env: V2SupplyMindEnv) -> bool: | |
| return "medium" in env.internal_task_id | |
| def _beam_width(env: V2SupplyMindEnv) -> int: | |
| if _is_hard(env): | |
| return HARD_BEAM_WIDTH | |
| if _is_medium(env): | |
| return MEDIUM_BEAM_WIDTH | |
| return BEAM_WIDTH | |
| def _follow_up_candidates(env: V2SupplyMindEnv) -> int: | |
| if _is_hard(env): | |
| return HARD_FOLLOW_UP_CANDIDATES | |
| if _is_medium(env): | |
| return MEDIUM_FOLLOW_UP_CANDIDATES | |
| return FOLLOW_UP_CANDIDATES | |
| def _candidate_limit(env: V2SupplyMindEnv) -> int: | |
| if _is_hard(env): | |
| return HARD_MAX_CANDIDATES | |
| if _is_medium(env): | |
| return MEDIUM_MAX_CANDIDATES | |
| return MAX_CANDIDATES | |
| def _future_demand(env: V2SupplyMindEnv, observation, horizon: int) -> dict[tuple[str, str], Demand]: | |
| recipe = env._require_recipe() | |
| end_round = min(recipe.profile.total_rounds - 1, observation.round_index + horizon) | |
| demand: dict[tuple[str, str], Demand] = {} | |
| for order in recipe.orders: | |
| status = env.order_status.get(order.order_id) | |
| if status in {"fulfilled", "rejected", "expired"}: | |
| continue | |
| if order.created_round > end_round or order.deadline_round < observation.round_index: | |
| continue | |
| key = (order.warehouse_id, order.sku) | |
| current = demand.get(key, Demand()) | |
| urgent = order.units if order.deadline_round <= observation.round_index + 2 else 0 | |
| demand[key] = Demand( | |
| units=current.units + order.units, | |
| value=current.value + order.units * order.customer_value_per_unit, | |
| urgent_units=current.urgent_units + urgent, | |
| ) | |
| return demand | |
| def _centralized_action(observation, demand: dict[tuple[str, str], Demand], mode: Mode) -> V2JointAction: | |
| projected = { | |
| warehouse_id: dict(warehouse.inventory) | |
| for warehouse_id, warehouse in observation.warehouses.items() | |
| } | |
| depot_left = dict(observation.center.depot_inventory) | |
| shipments = _plan_shipments(observation, demand, mode, projected, depot_left) | |
| transfers = _plan_offer_matches(observation, demand, mode, projected) | |
| procurements = _plan_procurements(observation, demand, mode, projected, depot_left) | |
| liquidations = _plan_liquidations(observation, demand, mode, depot_left) | |
| warehouse_actions = _plan_warehouse_actions(observation, mode, projected, transfers) | |
| return V2JointAction( | |
| warehouse_actions=warehouse_actions, | |
| central_action=CenterAction( | |
| central_procurements=procurements, | |
| central_liquidations=liquidations, | |
| central_replenishments=shipments, | |
| offer_matches=transfers, | |
| ), | |
| ) | |
| def _plan_shipments(observation, demand: dict[tuple[str, str], Demand], mode: Mode, projected: dict[str, dict[str, int]], depot_left: dict[str, int]) -> list[dict]: | |
| if observation.center.depot_trucks_available <= 0: | |
| return [] | |
| rows = [] | |
| for (warehouse_id, sku), item in demand.items(): | |
| current = projected.get(warehouse_id, {}).get(sku, 0) | |
| target = int(item.units * mode.demand_cover) + mode.safety_buffer | |
| gap = max(0, target - current) | |
| if gap <= 0 or depot_left.get(sku, 0) <= 0: | |
| continue | |
| rows.append( | |
| { | |
| "score": item.value + 5.0 * item.urgent_units - 1.5 * current, | |
| "warehouse_id": warehouse_id, | |
| "sku": sku, | |
| "units": gap, | |
| } | |
| ) | |
| shipments: list[dict] = [] | |
| used_trucks = 0 | |
| for row in sorted(rows, key=lambda value: value["score"], reverse=True): | |
| if used_trucks >= observation.center.depot_trucks_available: | |
| break | |
| sku = row["sku"] | |
| units = min(row["units"], depot_left.get(sku, 0), 3) | |
| if units <= 0: | |
| continue | |
| shipments.append( | |
| { | |
| "to_warehouse": row["warehouse_id"], | |
| "sku": sku, | |
| "units": units, | |
| "unit_price": price_band(sku)["fair_wholesale_price"], | |
| } | |
| ) | |
| depot_left[sku] -= units | |
| projected[row["warehouse_id"]][sku] = projected[row["warehouse_id"]].get(sku, 0) + units | |
| used_trucks += 1 | |
| return shipments | |
| def _plan_offer_matches(observation, demand: dict[tuple[str, str], Demand], mode: Mode, projected: dict[str, dict[str, int]]) -> list[dict]: | |
| if not mode.allow_transfers: | |
| return [] | |
| offers: list[dict] = [] | |
| requests: list[dict] = [] | |
| warehouse_actions_for_signals: dict[str, WarehouseAction] = {} | |
| for warehouse_id, inventory in projected.items(): | |
| offer_rows = [] | |
| request_rows = [] | |
| for sku, units in inventory.items(): | |
| need = demand.get((warehouse_id, sku), Demand()).units | |
| if units >= need + 4: | |
| offer_rows.append({"sku": sku, "units": min(3, units - need - 2), "ask_price": price_band(sku)["fair_wholesale_price"]}) | |
| elif need > units: | |
| request_rows.append({"sku": sku, "units": min(3, need - units), "max_price": price_band(sku)["max_wholesale_price"]}) | |
| warehouse_actions_for_signals[warehouse_id] = WarehouseAction(inventory_offers=offer_rows, inventory_requests=request_rows) | |
| for offer in offer_rows: | |
| offers.append({"signal_id": f"{warehouse_id}:offer:{offer['sku']}", "warehouse_id": warehouse_id, **offer}) | |
| for request in request_rows: | |
| requests.append({"signal_id": f"{warehouse_id}:request:{request['sku']}", "warehouse_id": warehouse_id, **request}) | |
| matches = [] | |
| for request in sorted(requests, key=lambda row: -price_band(row["sku"])["customer_value"]): | |
| same_sku_offers = [ | |
| offer for offer in offers | |
| if offer["sku"] == request["sku"] and offer["warehouse_id"] != request["warehouse_id"] and offer["ask_price"] <= request["max_price"] | |
| ] | |
| same_sku_offers.sort(key=lambda row: row["ask_price"]) | |
| for offer in same_sku_offers: | |
| units = min(2, offer["units"], request["units"], projected[offer["warehouse_id"]].get(offer["sku"], 0)) | |
| if units <= 0: | |
| continue | |
| matches.append( | |
| { | |
| "offer_signal_id": offer["signal_id"], | |
| "request_signal_id": request["signal_id"], | |
| "units": units, | |
| "compensation": units * price_band(offer["sku"])["fair_wholesale_price"], | |
| } | |
| ) | |
| projected[offer["warehouse_id"]][offer["sku"]] -= units | |
| projected[request["warehouse_id"]][request["sku"]] = projected[request["warehouse_id"]].get(request["sku"], 0) + units | |
| offer["units"] -= units | |
| request["units"] -= units | |
| break | |
| if len(matches) >= 3: | |
| break | |
| return matches | |
| def _plan_procurements(observation, demand: dict[tuple[str, str], Demand], mode: Mode, projected: dict[str, dict[str, int]], depot_left: dict[str, int]) -> list[dict]: | |
| if not mode.allow_procurement or observation.center.remaining_rounds <= 4: | |
| return [] | |
| network_stock = dict(depot_left) | |
| for inventory in projected.values(): | |
| for sku, units in inventory.items(): | |
| network_stock[sku] = network_stock.get(sku, 0) + units | |
| demand_by_sku: dict[str, int] = {} | |
| value_by_sku: dict[str, float] = {} | |
| for (_, sku), item in demand.items(): | |
| demand_by_sku[sku] = demand_by_sku.get(sku, 0) + item.units | |
| value_by_sku[sku] = value_by_sku.get(sku, 0.0) + item.value | |
| rows = [] | |
| for sku, units in demand_by_sku.items(): | |
| gap = int(units * mode.demand_cover) + mode.safety_buffer - network_stock.get(sku, 0) | |
| if gap > 0: | |
| rows.append({"sku": sku, "gap": gap, "score": value_by_sku.get(sku, 0.0)}) | |
| procurements = [] | |
| for row in sorted(rows, key=lambda value: value["score"], reverse=True)[:1]: | |
| sku = row["sku"] | |
| procurements.append({"sku": sku, "units": min(5, row["gap"]), "max_unit_cost": price_band(sku)["procurement_cost"]}) | |
| return procurements | |
| def _plan_liquidations(observation, demand: dict[tuple[str, str], Demand], mode: Mode, depot_left: dict[str, int]) -> list[dict]: | |
| if not mode.allow_liquidation: | |
| return [] | |
| milk = depot_left.get("fresh_milk", 0) | |
| if milk <= 3: | |
| return [] | |
| age = observation.center.depot_inventory_age.get("fresh_milk", 0.0) | |
| milk_demand = sum(item.units for (_, sku), item in demand.items() if sku == "fresh_milk") | |
| if age < 3.0 and milk <= milk_demand + 2: | |
| return [] | |
| excess = max(0, milk - max(2, milk_demand)) | |
| return [] if excess <= 0 else [{"sku": "fresh_milk", "units": min(4, excess)}] | |
| def _plan_warehouse_actions(observation, mode: Mode, projected: dict[str, dict[str, int]], matches: list[dict]) -> dict[str, WarehouseAction]: | |
| # Recreate offer/request signals required by same-step matches. | |
| offers_by_wh: dict[str, list[dict]] = {warehouse_id: [] for warehouse_id in observation.warehouses} | |
| requests_by_wh: dict[str, list[dict]] = {warehouse_id: [] for warehouse_id in observation.warehouses} | |
| for match in matches: | |
| offer_wh, _, sku = match["offer_signal_id"].split(":") | |
| request_wh, _, _ = match["request_signal_id"].split(":") | |
| offers_by_wh[offer_wh].append({"sku": sku, "units": match["units"], "ask_price": price_band(sku)["fair_wholesale_price"]}) | |
| requests_by_wh[request_wh].append({"sku": sku, "units": match["units"], "max_price": price_band(sku)["max_wholesale_price"]}) | |
| actions: dict[str, WarehouseAction] = {} | |
| for warehouse_id, warehouse in observation.warehouses.items(): | |
| inventory = dict(projected[warehouse_id]) | |
| driver_slots = warehouse.drivers_available | |
| decisions = [] | |
| visible = sorted( | |
| [order for order in warehouse.local_orders if order.status == "pending"], | |
| key=lambda order: (order.deadline_round, -order.units * order.customer_value_per_unit), | |
| ) | |
| for order in visible: | |
| can_wait = order.deadline_round > observation.round_index + mode.accept_slack | |
| can_fill = inventory.get(order.sku, 0) >= order.units and driver_slots > 0 | |
| if can_fill: | |
| decisions.append({"order_id": order.order_id, "decision": "accept"}) | |
| inventory[order.sku] -= order.units | |
| driver_slots -= 1 | |
| elif not can_wait: | |
| decisions.append({"order_id": order.order_id, "decision": "reject"}) | |
| responses = [ | |
| {"proposal_id": proposal.proposal_id, "decision": "accept"} | |
| for proposal in warehouse.pending_transfer_proposals | |
| if inventory.get(proposal.sku, 0) - proposal.units >= warehouse.safety_stock.get(proposal.sku, 1) | |
| ] | |
| actions[warehouse_id] = WarehouseAction( | |
| order_decisions=decisions, | |
| inventory_offers=offers_by_wh[warehouse_id], | |
| inventory_requests=requests_by_wh[warehouse_id], | |
| transfer_responses=responses, | |
| local_priority=[{"sku": "insulin_pack", "priority": 3}, {"sku": "fresh_milk", "priority": 2}], | |
| ) | |
| return actions | |
| def _dedupe(actions: list[V2JointAction]) -> list[V2JointAction]: | |
| unique = [] | |
| seen = set() | |
| for action in actions: | |
| key = action.model_dump_json() | |
| if key in seen: | |
| continue | |
| unique.append(action) | |
| seen.add(key) | |
| return unique | |