1818"""
1919
2020
21- # NOTE: The soc_analysis script used temporal avalache data to assess SOC.
21+ # NOTE (1) : The soc_analysis script used temporal avalache data to assess SOC.
2222# This functionality is not yet implemented here. We can still derive that data
2323# from the full time series using np.diff(prey_timeseries)
2424
2525
26+ # NOTE (2): Post-processing utilities and plotting are in scripts/analysis.py. This script should
27+ # solely focus on running the experiments and saving raw results.
28+
29+
2630import argparse
2731import hashlib
2832import json
@@ -166,7 +170,7 @@ def run_single_simulation(
166170 rows = grid_size ,
167171 cols = grid_size ,
168172 densities = cfg .densities ,
169- neighborhood = "moore" ,
173+ neighborhood = "moore" , #NOTE: Default neighborhood
170174 params = {
171175 "prey_birth" : prey_birth ,
172176 "prey_death" : prey_death ,
@@ -185,15 +189,16 @@ def run_single_simulation(
185189 warmup_steps = cfg .get_warmup_steps (grid_size )
186190 measurement_steps = cfg .get_measurement_steps (grid_size )
187191
192+
193+ # Warmup phase
188194 for _ in range (warmup_steps ):
189195 model .update ()
190196
191- # Measurement phase
192- prey_pops , pred_pops = [], []
193- evolved_means , evolved_stds = [], []
194- cluster_sizes_prey , cluster_sizes_pred = [], []
195- largest_fractions_prey , largest_fractions_pred = [], []
196- percolates_prey , percolates_pred = [], []
197+ # Measurement phase: start collecting our mertics
198+ prey_pops , pred_pops = [], [] # Prey populations and predator populations
199+ evolved_means , evolved_stds = [], [] # Evolution stats over time
200+ cluster_sizes_prey , cluster_sizes_pred = [], [] # Cluster sizes
201+ largest_fractions_prey , largest_fractions_pred = [], [] # Largest cluster fractions = size of largest cluster / total population
197202 pcf_samples = {'prey_prey' : [], 'pred_pred' : [], 'prey_pred' : []}
198203
199204
@@ -223,11 +228,7 @@ def run_single_simulation(
223228
224229 largest_fractions_prey .append (prey_stats ['largest_fraction' ])
225230 largest_fractions_pred .append (pred_stats ['largest_fraction' ])
226-
227- prey_perc , _ , _ , _ = get_percolating_cluster_fast (model .grid , 1 )
228- pred_perc , _ , _ , _ = get_percolating_cluster_fast (model .grid , 2 )
229- percolates_prey .append (prey_perc )
230- percolates_pred .append (pred_perc )
231+ # NOTE: Change in largest fraction calculation if needed for critical point location
231232
232233 # PCF
233234 if compute_pcf :
@@ -266,14 +267,12 @@ def run_single_simulation(
266267 # Order parameters
267268 "prey_largest_fraction" : float (np .mean (largest_fractions_prey )) if largest_fractions_prey else np .nan ,
268269 "pred_largest_fraction" : float (np .mean (largest_fractions_pred )) if largest_fractions_pred else np .nan ,
269- "prey_percolates" : bool (any (percolates_prey )) if percolates_prey else False ,
270- "pred_percolates" : bool (any (percolates_pred )) if percolates_pred else False ,
271270 }
272271
273272 # Time series (if requested)
274273 if cfg .save_timeseries :
275274 subsample = cfg .timeseries_subsample
276- result ["prey_timeseries" ] = prey_pops [::subsample ]
275+ result ["prey_timeseries" ] = prey_pops [::subsample ] #NOTE: Sample temporal data every 'subsample' steps
277276 result ["pred_timeseries" ] = pred_pops [::subsample ]
278277
279278
@@ -300,6 +299,28 @@ def run_single_simulation(
300299 result ["pcf_prey_pred" ] = pcf_cr .tolist ()
301300
302301 # Short-range indices
302+ """
303+ NOTE: The Pair Correlation function measures spatial correlation at distance r.
304+ g(r) = 1: random (poisson distribution)
305+ g(r) > 1: clustering (more pairs than random)
306+ g(r) < 1: segregation (fewer pairs than random)
307+
308+ prey_clustering_index: Do prey clump together?
309+ pred_clustering_index: Do predators clump together?
310+ segregation_index: Are prey and predators segregated?
311+
312+ For the Hydra effect model:
313+ segregation_index < 1: Prey and predators are spatially separated
314+ prey_clustering_index > 1: Prey form clusters
315+ pred_clustering_index > 1: Predators form clusters
316+
317+ High segregation (low segregation index): prey can reproduce in predator-free zones
318+ High prey clustering: prey form groups that can survive predation
319+ At criticality: expect sepcific balance where clusters are large enough to sustain but
320+ fragmented enough to avoid total predation.
321+
322+ If segregation_index = 1 approx, no Hydra effect -> follow mean field dynamics.
323+ """
303324 short_mask = dist < 3.0
304325 if np .any (short_mask ):
305326 result ["segregation_index" ] = float (np .mean (pcf_cr [short_mask ]))
@@ -308,8 +329,6 @@ def run_single_simulation(
308329
309330 return result
310331
311-
312-
313332# =============================================================================
314333# Experiment Phases
315334# =============================================================================
@@ -331,12 +350,13 @@ def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di
331350
332351 # Build job list
333352 jobs = []
353+ # Sweep through prey_birth and prey_death
334354 for pb in prey_births :
335355 for pd in prey_deaths :
336356 for rep in range (cfg .n_replicates ):
337357 params = {"pb" : pb , "pd" : pd }
338358
339- # Non-evolution run
359+ # Non-evolution run #FIXME: Check if both evo and non-evo are needed for phase 1
340360 seed = generate_unique_seed (params , rep )
341361 jobs .append ((pb , pd , cfg .predator_birth , cfg .predator_death ,
342362 cfg .grid_size , seed , cfg , False ))
@@ -383,7 +403,7 @@ def run_phase2(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di
383403
384404 SOC Hypothesis: Prey evolve toward critical critical point regardless of initial conditions.
385405
386- Test: Start evo from different intial prey_death values (?)
406+ NOTE: Test is currently start evo from different intial prey_death values (?)
387407 If SOC holds, then all runs converge to the same final prey_death near critical point.
388408 """
389409 from joblib import Parallel , delayed
@@ -445,13 +465,14 @@ def run_phase3(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di
445465 """
446466 from joblib import Parallel , delayed
447467
448- pb = cfg .critical_prey_birth
468+ # NOTE: Tuned to critical points from phase 1
469+ pb = cfg .critical_prey_birth
449470 pd = cfg .critical_prey_death
450471
451472 logger .info (f"Phase 3: FSS at critical point (pb={ pb } , pd={ pd } )" )
452473
453474 jobs = []
454- for L in cfg .grid_sizes :
475+ for L in cfg .grid_sizes : # Sweep through grid sizes
455476 warmup_numba_kernels (L , directed_hunting = cfg .directed_hunting )
456477
457478 for rep in range (cfg .n_replicates ):
@@ -475,6 +496,7 @@ def run_phase3(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di
475496 f .flush ()
476497 all_results .append (result )
477498
499+ # Post-run metadata: postprocessing will fit cluster cutoffs vs L
478500 meta = {
479501 "phase" : 3 ,
480502 "description" : "Finite-size scaling" ,
@@ -496,6 +518,10 @@ def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di
496518
497519 - Vary predator_birth and predator_death
498520 - For each combo, sweep prey_death
521+
522+
523+ NOTE: This phase should be subjected to changes depeding on what we are interested in varying
524+ in terms of parameters.
499525 """
500526 from joblib import Parallel , delayed
501527
0 commit comments