File size: 6,395 Bytes
2b0bffa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | """Latent (hidden) state of the LHC simulator.
The agent never sees these structures. They define the ground-truth particle
properties, detector imperfections, experiment progress flags, and the live
resource budget.
"""
from __future__ import annotations
from typing import Dict, List, Optional
from pydantic import BaseModel, Field
# ── Particle truth ────────────────────────────────────────────────────────
class LatentParticle(BaseModel):
"""The hidden mystery particle that the agent must discover.
Defines the true mass, width, decay branching ratios, spin, parity,
production cross-section, and dominant decay channel. The agent has to
recover these values from noisy observations.
"""
name: str = "X"
mass_gev: float = 125.0
width_gev: float = 0.004
spin: int = 0 # 0, 1, or 2
parity: str = "+" # "+" or "-"
cross_section_fb: float = 50.0 # signal cross-section in femtobarns
decay_branching: Dict[str, float] = Field(
default_factory=lambda: {
"diphoton": 0.0023,
"dilepton_ee": 0.00003,
"dilepton_mumu": 0.00022,
"four_lepton": 0.000125,
"bb": 0.58,
"dijet": 0.30,
},
description="Branching ratio (BR) per decay channel, sums to ~1.",
)
primary_channel: str = "diphoton"
# ── Detector & accelerator state ─────────────────────────────────────────
class DetectorState(BaseModel):
"""Hidden detector and accelerator parameters that shape noise.
These influence resolution, trigger efficiency, pileup, and systematic
uncertainties applied to every observation.
"""
detector_resolution_gev: float = 1.5 # absolute mass resolution σ_m
pileup_mu: float = 30.0 # average pileup interactions per crossing
trigger_efficiency: float = 0.85
luminosity_uncertainty: float = 0.025 # 2.5% relative uncertainty
energy_scale_offset: float = 0.0 # systematic shift in GeV
energy_scale_uncertainty: float = 0.3 # σ on the scale
background_shape_alpha: float = -2.5 # exponent of background ~ 1/m^|α|
qcd_background_strength: float = 1.0 # scale factor for hadronic background
detector_calibrated: bool = False
tracker_aligned: bool = False
# Channel-dependent reconstruction efficiency
channel_efficiency: Dict[str, float] = Field(
default_factory=lambda: {
"diphoton": 0.45,
"dilepton_ee": 0.55,
"dilepton_mumu": 0.70,
"four_lepton": 0.40,
"dijet": 0.80,
"bb": 0.50,
}
)
# ── Experiment progress flags ────────────────────────────────────────────
class ExperimentProgress(BaseModel):
"""Boolean milestones used by rules and reward shaping."""
beam_configured: bool = False
luminosity_allocated: bool = False
trigger_set: bool = False
collisions_collected: bool = False
detector_calibrated: bool = False
tracks_reconstructed: bool = False
channel_selected: bool = False
invariant_mass_built: bool = False
background_subtracted: bool = False
resonance_fitted: bool = False
bump_scanned: bool = False
angular_measured: bool = False
significance_estimated: bool = False
systematics_requested: bool = False
theory_review_requested: bool = False
claim_submitted: bool = False
n_events_collected: int = 0
n_signal_candidates: int = 0
n_background_estimate: int = 0
best_fit_mass_gev: Optional[float] = None
best_fit_width_gev: Optional[float] = None
best_significance_sigma: Optional[float] = None
best_channel: Optional[str] = None
best_beam_energy: Optional[str] = None
# ── Resources ─────────────────────────────────────────────────────────────
class ResourceState(BaseModel):
"""Live resource accounting (superset of the agent-visible ResourceUsage)."""
budget_total_musd: float = 100.0
budget_used_musd: float = 0.0
luminosity_total_fb: float = 300.0
luminosity_used_fb: float = 0.0
time_limit_days: float = 365.0
time_used_days: float = 0.0
compute_hours_used: float = 0.0
@property
def budget_remaining(self) -> float:
return max(0.0, self.budget_total_musd - self.budget_used_musd)
@property
def luminosity_remaining(self) -> float:
return max(0.0, self.luminosity_total_fb - self.luminosity_used_fb)
@property
def time_remaining(self) -> float:
return max(0.0, self.time_limit_days - self.time_used_days)
@property
def budget_exhausted(self) -> bool:
return self.budget_remaining <= 0
@property
def luminosity_exhausted(self) -> bool:
return self.luminosity_remaining <= 0
@property
def time_exhausted(self) -> bool:
return self.time_remaining <= 0
# ── Aggregate hidden state ───────────────────────────────────────────────
class FullLatentState(BaseModel):
"""Complete hidden state of the simulated LHC analysis world."""
particle: LatentParticle = Field(default_factory=LatentParticle)
detector: DetectorState = Field(default_factory=DetectorState)
progress: ExperimentProgress = Field(default_factory=ExperimentProgress)
resources: ResourceState = Field(default_factory=ResourceState)
selected_channel: Optional[str] = None
selected_beam_energy: Optional[str] = None
selected_trigger: Optional[str] = None
candidate_masses_gev: List[float] = Field(default_factory=list)
candidate_significances: List[float] = Field(default_factory=list)
hidden_failure_conditions: List[str] = Field(default_factory=list)
rng_seed: int = 42
step_count: int = 0
|