Skip to content

Commit 377bb4b

Browse files
committed
new expriment.py file following Strom's specification with some unimplemented functionality and conceptual gaps in Phase 4 sensitivity analysis. New config.py file for configuartions migrated from experiment script. Prompts and updates up to date
1 parent 1e1561f commit 377bb4b

7 files changed

Lines changed: 779 additions & 1110 deletions

File tree

File renamed without changes.

docs/kimon_prompts.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,9 @@ For parallel simulations, use different seeds per worker (e.g., base_seed + work
362362
7. Use the attached legacy simulation function to compute benchmarking resukts for our optimization. Include functionality to save in a csv and plots showing the most significant results. Include flags to run with or without plots and csv output.
363363

364364

365-
8. Write a few run mock tests for the analysis file to see that the plots render properly.
365+
8. Write a few run mock tests for the analysis file to see that the plots render properly.
366+
367+
368+
## Refactoring Main Experiment Script
369+
370+
Help me create a skeletal version of the updated experiments script for HPC that meets tha phase requirements outlined. The config class has been migrated to config.py.

docs/kimon_updates.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,20 @@ NOTE:
297297
Warmup and Measurement time must be scaled according to grid size. We need to write a method to estimate the approximate simulaiton time required. (Sary is working on that.)
298298

299299

300+
## Updates (25/1)
301+
302+
303+
Refactored ```experiments.py``` according to ```experiments.md```. Phase 4 and Phase 6 of the implementation are still unclear to me:
304+
305+
- Phase 4: We are currently varying ```predator_birth``` and ```predator_birth``` only.
306+
307+
- Phase 6: Will implement if all the other phases pass successfully.
308+
309+
TODO: We need to start drafting a proper version of the analyis (```analysis.py```) for postprocessing of all the data we are currenlty collecting and plotting utilities to visualize them.
310+
311+
312+
313+
300314

301315

302316

models/config.py

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Configuration for Predator-Prey Hydra Effect Experiments
4+
5+
Single Config dataclass with pre-defined instances for each experimental phase.
6+
7+
Usage:
8+
from config import PHASE1_CONFIG, PHASE2_CONFIG, Config
9+
10+
# Use pre-defined config
11+
cfg = PHASE1_CONFIG
12+
13+
# Or create custom config
14+
cfg = Config(grid_size=150, n_replicates=20)
15+
16+
# Or modify existing
17+
cfg = Config(**{**asdict(PHASE1_CONFIG), 'n_replicates': 30})
18+
"""
19+
20+
from dataclasses import dataclass, field, asdict
21+
from typing import Tuple, Optional
22+
import numpy as np
23+
24+
25+
@dataclass
26+
class Config:
27+
"""Central configuration for all experiments."""
28+
29+
# Grid settings
30+
grid_size: int = 100 #FIXME: Decide default configuration
31+
densities: Tuple[float, float] = (0.30, 0.15) # (prey, predator) #FIXME: Default densities
32+
33+
# For FSS experiments: multiple grid sizes
34+
grid_sizes: Tuple[int, ...] = (50, 75, 100, 150, 200)
35+
36+
# Default/fixed parameters
37+
prey_birth: float = 0.20
38+
prey_death: float = 0.05
39+
predator_birth: float = 0.2 # FIXME: Default predator death rate
40+
predator_death: float = 0.1 # FIXME: Default predator death rate
41+
42+
# Critical point (UPDATE AFTER PHASE 1)
43+
critical_prey_birth: float = 0.22 # FIXME: Change after obtaining results
44+
critical_prey_death: float = 0.04 # FIXME; Change after obtaining results
45+
46+
# Prey parameter sweep (Phase 1)
47+
prey_birth_range: Tuple[float, float] = (0.10, 0.35)
48+
prey_death_range: Tuple[float, float] = (0.001, 0.10)
49+
n_prey_birth: int = 15 # FIXME: Decide number of grid points along prey axes
50+
n_prey_death: int = 15
51+
52+
# Predator parameter sweep (Phase 4 sensitivity)
53+
predator_birth_values: Tuple[float, ...] = (0.15, 0.20, 0.25, 0.30) #FIXME: Bogus values for now
54+
predator_death_values: Tuple[float, ...] = (0.05, 0.10, 0.15, 0.20) #FIXME: Bogus values for now
55+
56+
# Perturbation offsets from critical point (Phase 5)
57+
prey_death_offsets: Tuple[float, ...] = (-0.02, -0.01, 0.0, 0.01, 0.02) #FIXME: Bogus values for now
58+
59+
# Number of replicates per parameter configuration
60+
n_replicates: int = 15 # FIXME: Decide number of indep. runs per parameter config
61+
62+
# Simulation steps
63+
warmup_steps: int = 200 # FIXME: Steps to run before measuring
64+
measurement_steps: int = 300 # FIXME: Decide measurement steps
65+
66+
# Evo
67+
with_evolution: bool = False
68+
evolve_sd: float = 0.10
69+
evolve_min: float = 0.001
70+
evolve_max: float = 0.10
71+
72+
# Sensitivity: mutation strength values to test
73+
sensitivity_sd_values: Tuple[float, ...] = (0.02, 0.05, 0.10, 0.15, 0.20) #FIXME: Don't know if we use yet
74+
75+
# Update mode
76+
synchronous: bool = False # Always False for this model
77+
directed_hunting: bool = True
78+
79+
# For Phase 6: compare model variants
80+
directed_hunting_values: Tuple[bool, ...] = (False, True)
81+
82+
# Temporal data collection (time series)
83+
save_timeseries: bool = False
84+
timeseries_subsample: int = 10 # FIXME: Save every how many steps
85+
86+
# PCF settings
87+
collect_pcf: bool = True
88+
pcf_sample_rate: float = 0.2 # Fraction of runs to compute PCF
89+
pcf_max_distance: float = 20.0
90+
pcf_n_bins: int = 20
91+
92+
# Cluster analysis
93+
min_density_for_analysis: float = 0.002 # FIXME: Minimum prey density (fraction of grid) to analyze clusters/PCF
94+
95+
# Perturbation settings (Phase 5)
96+
perturbation_magnitude: float = 0.1 # FIXME: Fractional change to apply at perturbation time
97+
98+
# Parallelization
99+
n_jobs: int = -1 # Use all available cores by default
100+
101+
# Helpers
102+
def get_prey_births(self) -> np.ndarray:
103+
"""Generate prey birth rate sweep values."""
104+
return np.linspace(self.prey_birth_range[0], self.prey_birth_range[1], self.n_prey_birth)
105+
106+
def get_prey_deaths(self) -> np.ndarray:
107+
"""Generate prey death rate sweep values."""
108+
return np.linspace(self.prey_death_range[0], self.prey_death_range[1], self.n_prey_death)
109+
110+
def get_warmup_steps(self, L: int) -> int: #FIXME: This method will be updated depending on Sary's results.
111+
"""Scale warmup with grid size."""
112+
return int(self.warmup_steps * (L / 100))
113+
114+
def get_measurement_steps(self, L: int) -> int:
115+
"""Scale measurement with grid size."""
116+
return int(self.measurement_steps * (L / 100))
117+
118+
def estimate_runtime(self, n_cores: int = 32) -> str:
119+
"""Estimate total runtime based on benchmark data."""
120+
# Benchmark: ~1182 steps/sec for 100x100 grid
121+
ref_size = 100
122+
ref_steps_per_sec = 1182
123+
124+
size_scaling = (self.grid_size / ref_size) ** 2
125+
actual_steps_per_sec = ref_steps_per_sec / size_scaling
126+
127+
total_steps = self.warmup_steps + self.measurement_steps
128+
base_time_s = total_steps / actual_steps_per_sec
129+
130+
# PCF overhead (~8ms for 100x100)
131+
pcf_time_s = (0.008 * size_scaling) if self.collect_pcf else 0
132+
133+
# Count simulations
134+
n_sims = self.n_prey_birth * self.n_prey_death * self.n_replicates
135+
if self.with_evolution:
136+
n_sims *= 2 # Both evo and non-evo runs
137+
138+
total_seconds = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate)
139+
total_seconds /= n_cores
140+
141+
hours = total_seconds / 3600
142+
core_hours = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate) / 3600
143+
144+
return f"{n_sims:,} sims, ~{hours:.1f}h on {n_cores} cores (~{core_hours:.0f} core-hours)"
145+
146+
147+
############################################################################################
148+
# Experimental Phase Configurations
149+
############################################################################################
150+
151+
#FIXME: These configs are arbitraty and should be finalized before running experiments.
152+
153+
# Phase 1: Parameter sweep to find critical point
154+
PHASE1_CONFIG = Config(
155+
grid_size=100,
156+
n_prey_birth=15,
157+
n_prey_death=15,
158+
prey_birth_range=(0.10, 0.35),
159+
prey_death_range=(0.001, 0.10),
160+
n_replicates=15,
161+
warmup_steps=200,
162+
measurement_steps=300,
163+
collect_pcf=True,
164+
pcf_sample_rate=0.2,
165+
save_timeseries=False,
166+
)
167+
168+
# Phase 2: Self-organization (evolution toward criticality)
169+
PHASE2_CONFIG = Config(
170+
grid_size=100,
171+
n_prey_birth=10,
172+
n_replicates=10,
173+
warmup_steps=100,
174+
measurement_steps=1000,
175+
with_evolution=True,
176+
evolve_sd=0.10,
177+
collect_pcf=False, # Not needed for SOC analysis
178+
save_timeseries=True,
179+
timeseries_subsample=10,
180+
)
181+
182+
# Phase 3: Finite-size scaling at critical point
183+
PHASE3_CONFIG = Config(
184+
grid_sizes=(50, 75, 100, 150, 200),
185+
n_replicates=50,
186+
warmup_steps=200,
187+
measurement_steps=300,
188+
collect_pcf=False,
189+
save_timeseries=False,
190+
)
191+
192+
# Phase 4: Sensitivity analysis
193+
PHASE4_CONFIG = Config(
194+
grid_size=100,
195+
predator_birth_values=(0.15, 0.20, 0.25, 0.30),
196+
predator_death_values=(0.05, 0.10, 0.15, 0.20),
197+
n_prey_death=10,
198+
prey_death_range=(0.01, 0.10),
199+
n_replicates=20,
200+
warmup_steps=200,
201+
measurement_steps=300,
202+
with_evolution=True,
203+
collect_pcf=False,
204+
save_timeseries=True,
205+
timeseries_subsample=10,
206+
)
207+
208+
# Phase 5: Perturbation analysis (critical slowing down)
209+
PHASE5_CONFIG = Config(
210+
grid_size=100,
211+
prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02),
212+
n_replicates=20,
213+
warmup_steps=500,
214+
measurement_steps=2000,
215+
perturbation_magnitude=0.1,
216+
collect_pcf=False,
217+
save_timeseries=True,
218+
timeseries_subsample=1, # Full resolution for autocorrelation
219+
)
220+
221+
# Phase 6: Model extensions
222+
PHASE6_CONFIG = Config(
223+
... #FIXME: Will be defined later
224+
)
225+
226+
PHASE_CONFIGS = {
227+
1: PHASE1_CONFIG,
228+
2: PHASE2_CONFIG,
229+
3: PHASE3_CONFIG,
230+
4: PHASE4_CONFIG,
231+
5: PHASE5_CONFIG,
232+
6: PHASE6_CONFIG,
233+
}
234+
235+
def get_phase_config(phase: int) -> Config:
236+
"""Get config for a specific phase."""
237+
if phase not in PHASE_CONFIGS:
238+
raise ValueError(f"Unknown phase {phase}. Valid phases: {list(PHASE_CONFIGS.keys())}")
239+
return PHASE_CONFIGS[phase]

0 commit comments

Comments
 (0)