Spaces:
Sleeping
Sleeping
| """Baseline policies for BrainRL evaluation. | |
| These are intentionally non-RL policies that share the same OpenEnv interface | |
| as the LLM/GRPO policy. They give the hackathon demo something concrete to | |
| beat without dragging in any Gym/PPO machinery. | |
| """ | |
| from __future__ import annotations | |
| import random | |
| from dataclasses import dataclass | |
| from statistics import mean | |
| from typing import Protocol | |
| from metrics import EpisodeMetrics, score_selection_order | |
| from prompts import llm_prompt_policy_action, static_prompt_policy_action | |
| from server.brain_environment import BrainRegionSelectionEnvironment | |
| class RegionPolicy(Protocol): | |
| name: str | |
| def reset(self, env: BrainRegionSelectionEnvironment) -> None: ... | |
| def select_region(self, env: BrainRegionSelectionEnvironment) -> str: ... | |
| class EpisodeResult: | |
| policy: str | |
| rewards: list[float] | |
| r2_curve: list[float] | |
| selected_regions: list[str] | |
| errors: list[str] | |
| subject_id: str | None = None | |
| run_id: str | None = None | |
| condition: str | None = None | |
| stimulus_window: int | None = None | |
| stimulus_summary: dict | None = None | |
| metrics: EpisodeMetrics | None = None | |
| def final_r2(self) -> float: | |
| return self.r2_curve[-1] if self.r2_curve else 0.0 | |
| def total_reward(self) -> float: | |
| return float(sum(self.rewards)) | |
| def priority_correlation(self) -> float: | |
| return float(self.metrics.priority_correlation) if self.metrics else 0.0 | |
| def two_v_two_accuracy(self) -> float: | |
| return float(self.metrics.two_v_two_accuracy) if self.metrics else 0.0 | |
| class RandomPolicy: | |
| name = "random" | |
| def __init__(self, seed: int = 42): | |
| self._rng = random.Random(seed) | |
| def reset(self, env: BrainRegionSelectionEnvironment) -> None: | |
| return None | |
| def select_region(self, env: BrainRegionSelectionEnvironment) -> str: | |
| selected = set(env._selected_region_ids) | |
| remaining = [ | |
| candidate.region_id | |
| for candidate in env._subset.candidates | |
| if candidate.region_id not in selected | |
| ] | |
| return self._rng.choice(remaining) | |
| class SemanticPriorPolicy: | |
| name = "semantic_prior" | |
| def reset(self, env: BrainRegionSelectionEnvironment) -> None: | |
| self._ordered = [ | |
| candidate.region_id | |
| for candidate in sorted( | |
| env._subset.candidates, | |
| key=lambda item: (item.semantic_prior, item.prune_score), | |
| reverse=True, | |
| ) | |
| ] | |
| def select_region(self, env: BrainRegionSelectionEnvironment) -> str: | |
| selected = set(env._selected_region_ids) | |
| for region_id in self._ordered: | |
| if region_id not in selected: | |
| return region_id | |
| return self._ordered[0] | |
| class GreedyImprovementPolicy: | |
| name = "greedy" | |
| def reset(self, env: BrainRegionSelectionEnvironment) -> None: | |
| return None | |
| def select_region(self, env: BrainRegionSelectionEnvironment) -> str: | |
| selected = list(env._selected_region_ids) | |
| selected_set = set(selected) | |
| best_region: str | None = None | |
| best_gain = float("-inf") | |
| for candidate in env._subset.candidates: | |
| if candidate.region_id in selected_set: | |
| continue | |
| next_regions = selected + [candidate.region_id] | |
| gain = env._score_regions(next_regions) - env._current_r2 | |
| gain -= env._subset.cost_penalty * candidate.cost | |
| if gain > best_gain: | |
| best_gain = gain | |
| best_region = candidate.region_id | |
| if best_region is None: | |
| return env._subset.candidates[0].region_id | |
| return best_region | |
| class PromptPolicy: | |
| """OpenEnv prompt-action policy. | |
| In static mode it uses the exact prompt interface but selects deterministically | |
| from prune_score/semantic_prior. In LLM mode it asks a chat model for one | |
| JSON action. | |
| """ | |
| def __init__(self, use_llm: bool = False): | |
| self.use_llm = use_llm | |
| self.name = "llm_prompt" if use_llm else "prompt_static" | |
| def reset(self, env: BrainRegionSelectionEnvironment) -> None: | |
| return None | |
| def select_region(self, env: BrainRegionSelectionEnvironment) -> str: | |
| state = env._build_selection_state() | |
| if self.use_llm: | |
| region_id, _ = llm_prompt_policy_action(state) | |
| return region_id | |
| return static_prompt_policy_action(state) | |
| def run_policy_episode( | |
| policy: RegionPolicy, | |
| seed: int = 42, | |
| *, | |
| subject_id: str | None = None, | |
| run_id: str | None = None, | |
| condition: str | None = None, | |
| stimulus_window: int | None = None, | |
| env: BrainRegionSelectionEnvironment | None = None, | |
| ) -> EpisodeResult: | |
| env = env or BrainRegionSelectionEnvironment() | |
| env.reset( | |
| seed=seed, | |
| subject_id=subject_id, | |
| run_id=run_id, | |
| condition=condition, | |
| stimulus_window=stimulus_window, | |
| ) | |
| policy.reset(env) | |
| rewards: list[float] = [] | |
| r2_curve: list[float] = [] | |
| errors: list[str] = [] | |
| done = False | |
| while not done: | |
| region_id = policy.select_region(env) | |
| result = env._process_action(region_id) | |
| rewards.append(float(result["reward"])) | |
| r2_curve.append(float(result["current_r2"])) | |
| if result.get("error"): | |
| errors.append(str(result["error"])) | |
| done = bool(result["done"]) | |
| stim = env._stimulus_features | |
| candidate_ids = [candidate.region_id for candidate in env._subset.candidates] | |
| metrics = score_selection_order( | |
| candidate_ids=candidate_ids, | |
| selected_region_ids=env._selected_region_ids, | |
| target_scores=env._effective_base_r2, | |
| ) | |
| return EpisodeResult( | |
| policy=policy.name, | |
| rewards=rewards, | |
| r2_curve=r2_curve, | |
| selected_regions=list(env._selected_region_ids), | |
| errors=errors, | |
| subject_id=subject_id, | |
| run_id=run_id, | |
| condition=condition, | |
| stimulus_window=int(stim["window_index"]) if stim else None, | |
| stimulus_summary={ | |
| "window_index": int(stim["window_index"]), | |
| "n_windows": int(stim["n_windows"]), | |
| "dominant_pos": stim.get("dominant_pos"), | |
| "n_words": int(stim.get("n_words", 0)), | |
| "speech_density": float(stim.get("speech_density", 0.0)), | |
| } | |
| if stim | |
| else None, | |
| metrics=metrics, | |
| ) | |
| def summarize_results( | |
| results: list[EpisodeResult], | |
| example_limit: int = 5, | |
| split_label: str | None = None, | |
| ) -> list[dict[str, object]]: | |
| rows: list[dict[str, object]] = [] | |
| for policy_name in sorted({result.policy for result in results}): | |
| policy_results = [result for result in results if result.policy == policy_name] | |
| example_order = " -> ".join(policy_results[0].selected_regions[:example_limit]) | |
| if len(policy_results[0].selected_regions) > example_limit: | |
| example_order += f" -> (+{len(policy_results[0].selected_regions) - example_limit} more)" | |
| row: dict[str, object] = { | |
| "policy": policy_name, | |
| "episodes": len(policy_results), | |
| "mean_final_r2": mean(result.final_r2 for result in policy_results), | |
| "mean_priority_correlation": mean( | |
| result.priority_correlation for result in policy_results | |
| ), | |
| "mean_2v2_accuracy": mean( | |
| result.two_v_two_accuracy for result in policy_results | |
| ), | |
| "mean_total_reward": mean(result.total_reward for result in policy_results), | |
| "mean_regions_selected": mean( | |
| len(result.selected_regions) for result in policy_results | |
| ), | |
| "example_order": example_order, | |
| } | |
| if split_label: | |
| row["split"] = split_label | |
| rows.append(row) | |
| return rows | |
| def r2_curves_by_policy( | |
| results: list[EpisodeResult], | |
| ) -> dict[str, list[list[float]]]: | |
| """Group r2_curve lists by policy name for plotting.""" | |
| grouped: dict[str, list[list[float]]] = {} | |
| for result in results: | |
| grouped.setdefault(result.policy, []).append(list(result.r2_curve)) | |
| return grouped | |
| def default_baselines(seed: int = 42) -> list[RegionPolicy]: | |
| return [ | |
| RandomPolicy(seed=seed), | |
| SemanticPriorPolicy(), | |
| PromptPolicy(use_llm=False), | |
| GreedyImprovementPolicy(), | |
| ] | |