From e10b916b03cd340278f85f76cebc16e38d712b3b Mon Sep 17 00:00:00 2001 From: Kimon Date: Fri, 30 Jan 2026 11:21:52 +0100 Subject: [PATCH 1/7] added docstrings --- .DS_Store | Bin 0 -> 10244 bytes .gitignore | 10 +- bash_scripts/{run_phase6.sh => run_phase5.sh} | 0 docs/GEN_AI.md | 3 + docs/HPC_GUIDE.md | 57 -- docs/experiments.md | 233 ----- docs/kimon_prompts.md | 5 +- docs/{prompts.md => storm_prompts.md} | 0 {scripts => misc}/analysis.py | 0 models/CA.py | 308 +++++-- models/config.py | 207 ++++- models/numba_optimized.py | 813 ++++++++++++++---- notebooks/.DS_Store | Bin 0 -> 6148 bytes notebooks/plots.ipynb | 84 +- scripts/.DS_Store | Bin 0 -> 6148 bytes scripts/experiments.py | 467 ++++++++-- tests/smoke_test.py | 698 --------------- 17 files changed, 1462 insertions(+), 1423 deletions(-) create mode 100644 .DS_Store rename bash_scripts/{run_phase6.sh => run_phase5.sh} (100%) delete mode 100644 docs/HPC_GUIDE.md delete mode 100644 docs/experiments.md rename docs/{prompts.md => storm_prompts.md} (100%) rename {scripts => misc}/analysis.py (100%) create mode 100644 notebooks/.DS_Store create mode 100644 scripts/.DS_Store delete mode 100644 tests/smoke_test.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2039b75e51cf6da83afed3199a959f66d9ec022e GIT binary patch literal 10244 zcmeHMO>7%Q6n@*p-nCMYDO44QN~<6t4pFd0RJGzKZt5VYKteHp5=E{Zn`G&F*Uhez zq7Y<}6F*XmQmBLwf&&ubKz)M)ho0b?6FqRLkmvzbf(sH=f%4wW#(uNjH9a9{W~AM> zp84L)_ujmDv$F;OW3JOE0@wgx;YBjP7jFrL+UXi9d!m}-C=JvHcd1xe^jqyAq^{5& z&>qkp&>qkp&>r}&cmVfoD%OOQ`mQ~oJ)k|%_W%vpa2wHo%O9}4H4V-m zphx^O$bS{Nd$7v+9=zNm&#+d`@9g$xXS(MEKLs)1lZ@cw+nqLzZ;J5)^q3EgxX(n! zjv+XYKfh^Sd+w~@F@zd$Nk?%$QO=*h8LWyK?aw3CJn8z2QRGl^Cf`io6org{6fN#i z$Xxm`9m%bF^u?>+ek8q{(l4L+e?}`-W<}KQg*rxQfKgh-`y#C1%4uWt2CALLL1fJB zOx05?g+~sfUMiN`dSl=D!&~RY435AL=JHnXioNg*Qo-!dKzl=|srJ!48h5gJ?BnWd zK|7l7uj*~r3Bp3*dt=YocqVIFrZr)`Tv_(lDq$_`1aq~Om+05CzVFs|o@@2zo#t9K zf6p=B3u}(oToDRRqlu6cFF0PqUz_tgULz2?71?0rtXwsJV150e;>?tNWM*T^UO!Se zG-V$u7B@C>*8ck*e0ZUH%58c6TVxl~H>AWwq?WhwXWtw?J+nWK&G0rp9Y?QR+wLdD z$Yng(lG?euOV@kEqCf6xWyN!SZwc#&C)N8HZm}?fi`d4)tdHZoA@lg>ybsR%mkU1> z59}oKXbjFfsh#tN%sK2DvbYRd7${4W%wwT~9Fukzbog>)(G_N5jHUw@Q``^l?e|W7DMoG`F;PDFDGTT96}93B^|}Xgxkb8rmBZG-Z! zWLrjLDtft(?Fj~DDbAcB71rjxzp4Fj#yzM1vd{E6+hl~o)Z8Yfx@ZsVG7pSNdu{su zKcD{p|1R^2HdlLKcX_}VFIUQQX!Fi_IBq_DYq#+_i5C^Zb)^(4cq0hai_h`+gFeSU y5Y-h(Qo;68B)d`yb*vZaDCO7uf9cNveg4Nuu#(RA{0%CpU3q`f=l}oX`TuVfqX4r2 literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index b13ecaa..8a871d4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,12 +12,4 @@ results/ data/ .pytest_cache/ -.DS_Store - -hpc_data/phase4_18735304 - -hpc_data/phase6_18780164 - - -hpc_data/**/*.jsonl -hpc_data/**/*.jsonl +.DS_Store/ diff --git a/bash_scripts/run_phase6.sh b/bash_scripts/run_phase5.sh similarity index 100% rename from bash_scripts/run_phase6.sh rename to bash_scripts/run_phase5.sh diff --git a/docs/GEN_AI.md b/docs/GEN_AI.md index e69de29..c35c60d 100644 --- a/docs/GEN_AI.md +++ b/docs/GEN_AI.md @@ -0,0 +1,3 @@ +All our prompts will go here. + +A reflection of our GENAI usage should also be placed here. \ No newline at end of file diff --git a/docs/HPC_GUIDE.md b/docs/HPC_GUIDE.md deleted file mode 100644 index 248e642..0000000 --- a/docs/HPC_GUIDE.md +++ /dev/null @@ -1,57 +0,0 @@ -### Snellius Usage Breakdown - -``` -ssh kanagnostopoul@snellius.surf.nl - -# On a separate terminal run the following - -# Upload the entire project directory (including your models/ folder) - -rsync -avz --progress --exclude-from='.rsync-exclude' \ - ~/CSS_Project/ kanagnostopoul@snellius.surf.nl:~/CSS_Project/ - -# On the Snellius terminal - -module load 2023 Python/3.11.3-GCCcore-12.3.0 -python3 -m venv ~/css_env -source ~/css_env/bin/activate -pip install numpy scipy matplotlib joblib - -# To do a dry run for testing the entire environment - -python3 pp_analysis.py --mode full --dry-run - -# For async run - -python3 pp_analysis.py --mode full --output results_${SLURM_JOB_ID} --cores $SLURM_CPUS_PER_TASK --async - -# To submit a job - -sbatch run_analysis.sh - -# Check Queue Status - -squeue -u $USER - -# Cancel a job - -scancel - -# Monitoring live progress - -tail -f pp_phase4_18735304.err - -# Watch task completetion - -watch -n 5 squeue -u $USER -watch -n 10 "ls -1 results_JOBID | wc -l" - -# Fetching the results once the job is done - -scp -r kanagnostopoul@snellius.surf.nl:~/CSS_Project/results/phase6_18832958/ ./results/ -``` - -The jobscript template can be found in ```run_analysis.sh``` (default rome paritition). - - -Snellius Partitions Page: https://servicedesk.surf.nl/wiki/spaces/WIKI/pages/30660209/Snellius+partitions+and+accounting \ No newline at end of file diff --git a/docs/experiments.md b/docs/experiments.md deleted file mode 100644 index 0202b42..0000000 --- a/docs/experiments.md +++ /dev/null @@ -1,233 +0,0 @@ -# Metrics and measures -This is what should be measured each run. These runs can then be further aggregated for final metrics. -### Fixed parameter runs -- Population count (mean and std after warmup) -- Cluster size distribution (means and stds after warmup) -### Evolution runs -It is important to scrutenize whether these should be time-series or steady state values. -- Population count (time series after warmup) -- Cluster size distribution (time series after warmup) -- Prey death rate (time series mean and std after warmup) - -# Experiments -These phases should be completed sequentially, deepening our understanding at each step. The different experiments in each phase should be completed with data from the same runs. -### Phase 1: finding the critical point -- Create bifurcation diagram of mean population count, varying prey death rate - - Look for critical transition -- Create log-log plot of cluster size distribution, varying prey death rate - - Look for power-law -### Phase 2: self-organization -- Measure final prey death rate after evolution - - Look for self-organized criticality: an SOC-system should move towards the critical point -### Phase 3: finite-size scaling -- Sweep of grid sizes at critical point - - Check for power-law cut-offs -### Phase 4: sensitivity analysis -- Show sensitivity of hydra effect varying other parameters - - Investigate the ubiquity of the critical point across parameter regimes -- Show correlation between critical prey death rate and post-evolution prey death rate, varying other parameters - - Again look for self-organized criticality: an SOC-system should move towards the critical point regardless of other parameters -### Phase 5: perturbation analysis -- Create autocorrelation plot of mean population count, following perturbations around the critical point - - Look for critical slowing down: perturbations to states closer to the critical point should more slowly return to the steady state - - This requires time series data -### Phase 6: model extensions -- Investigate whether hydra effect and SOC still occur with diffusion and directed reproduction - -# Todo -The main functionality is all complete. Thus, the models folder should be relatively untouched. -However, it is important to standardize experiments and analysis. The following files should be used for this. -These files should contain very little (if any) functionality outside of what is listed here. -### experiments.py -This is the file that will be run on the cluster and should generate all experiment data. -- General config class to setup experiments (grid size, parameters, sweep, evolution, repetitions, etc.) -- Config objects for each phase (see phases above) -- Function that runs the experiment based on a config object (calls run_single_simulation in parallel) - - Should save results to results folder (which can then be used by analysis.py) -- Function that runs a single simulation, saving all necessary results - - This needs functionality to run a predetermined amount of time with a warmup - - And needs functionality to dynamically run until it has found a steady state -- Should not contain any analysis (power-law fitting, bifurcation, etc.) - - Exception to this is the PCF data -- Function to estimate runtime (already exists) -- Should have argparse functionality to choose which phase to execute -- Nice-to-have: argparse functionality to create new config object for arbitrary experiments -### analysis.py -This is the file that will generate our plots and statistics for the analysis. -- Function to create bifurcation diagram to find critical point -- Function to create log-log plot to check for power-law - - Should also fit a power-function to the data (see scrips/experiments.fit_truncated_power_law) -- Function to calculate/ show similarity between post-evolution prey death rates and critical points -- Function for sensitivity analysis -- Function for perturbation analysis - ---- - - -## What we are currently collecting: - -### 2D Parameter Sweep - -We map the full phase space to find: -- Hydra regions -- Critical points -- Coexistence boundaries -- Evolutionary advantage zones - -For now at least we sweep: - -``` -prey_birth in [0.10, 0.35] -prey_death in [0.001, 0.10] -``` - -Metrics Collected (so far): - -1. Population Dynamics - -``` - -prey_mean: time-averaged prey pop -prey_std: variability in prey - -# same for predator as above - -prey_survived: did prey persist -pred_survived: did pred perist - -``` - -2. Cluster structure - -``` - -prey_n_clusters: total number of prey clusters -pred_n_clusters: total number of pred clusters -prey_tau: power law exp -prey_s_c: cutoff cluster sizes -pred_tau: pred cluster exp -pred_s_c: pred cutoff - -``` - -3. Order Parameters - -``` -prey_largest_fraction_mean -prey_largest_fraction_std -pred_largest_fraction_mean -prey_percolation_prob: fraction of samples with spanning cluster -pred_percolation_prob: predator percolation prob - -``` - - -4. Spatial Correlations - -``` - -pcf_distances: distance bins in lattice units -pcf_prey_prey_mean: prey-prey correlation function -pcf_pred_pred_mean -pcf_prey_pred_mean -segregation_index: measure spatial mixing -prey_clustering_index: short range prey clustering -pred_clustering_index - -``` - -5. Evolutionary dynamics - -``` -evolved_prey_death_mean: time avg evolved mortality rate -evolved_prey_death_std -evolved_prey_death_final -evolve_sd: mutation strength used - -``` - ---- - -### Finite-size scaling - -We choose a fixed point identified in the main simulation run ```(target_prey_birth, target_prey_death)``` ideally near hydra boundary. - - -For selected grid sizes (TBD) we run independent reps for each size. - - -Metrics: - -``` -grid_size -prey_mean, prey_std -prey_survived: bool -prey_largest_fraction: order parameter -prey_percolation_prob -prey_tau: grid size dependent exponent -prey_tau_se: SE on tau -prey_s_c: cutoff scales -``` - ---- - -### Evo Sensitivity - -How does mutation strength affect evolutionary advantage in Hydra regions, speed of adaptation and final evolved mortality rates. - -Again. choose fixed point identified from main analysis. - -Metrics Dict: - -``` -prey_mean: in cell units as the below metrics as well -prey_std -pred_mean -pred_std -prey_survived: bool - -+ same cluster metrics and spatial correlation metrics - - -evolved_prey_death_mean: avg mortality across all prey -evolved_prey_death_std -evolved_prey_death_final -evolve_sd -``` - - -## Additions Required: - -1. Temporal dynamics for time series analysis. Needed to add critical slowing down effect near phase transitions. - -``` -result["prey_timeseries"] = prey_pops[::10] # Subsample every 10 steps -result["pred_timeseries"] = pred_pops[::10] - -``` - -``` -def run_perturbation_experiment(...): - # Save full time series only for these special runs -``` - -2. Snapshots of spatial configurations. This is a costly operation so we need to figure out how and when to do it in the sim. - -3. Saving final grid states? - -``` -result["final_grid"] = model.grid.copy() -``` - - - - - - - - - - - - - diff --git a/docs/kimon_prompts.md b/docs/kimon_prompts.md index c43602a..7e91867 100644 --- a/docs/kimon_prompts.md +++ b/docs/kimon_prompts.md @@ -369,7 +369,8 @@ For parallel simulations, use different seeds per worker (e.g., base_seed + work 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. - ## Data Post-processing -Help me load and parse the data according to job and experimental phase number. The data will be analyzed in a jupyter notebook rather than a py file for usability. \ No newline at end of file +Help me load and parse the data according to job and experimental phase number. The data will be analyzed in a jupyter notebook rather than a py file for usability. + + diff --git a/docs/prompts.md b/docs/storm_prompts.md similarity index 100% rename from docs/prompts.md rename to docs/storm_prompts.md diff --git a/scripts/analysis.py b/misc/analysis.py similarity index 100% rename from scripts/analysis.py rename to misc/analysis.py diff --git a/models/CA.py b/models/CA.py index aef57d3..fe6b30d 100644 --- a/models/CA.py +++ b/models/CA.py @@ -1,8 +1,5 @@ -"""Cellular automaton base class. - -Defines a CA class with initialization, neighbor counting, update (to override), -and run loop. Uses a numpy Generator for all randomness and supports -Neumann and Moore neighborhoods with periodic boundaries. +""" +Cellular automaton base class. """ from typing import Tuple, Dict, Optional @@ -22,15 +19,27 @@ class CA: - """Base cellular automaton class. + """ + Base cellular automaton class for spatial simulations. + + This class provides a framework for multi-species cellular automata with + support for global parameters, per-cell evolving parameters, and + grid initialization based on density. Attributes - - n_species: number of distinct (non-zero) states - - grid: 2D numpy array containing integers in {0, 1, ..., n_species} - - neighborhood: either "neumann" or "moore" - - generator: numpy.random.Generator used for all randomness - - params: global parameters dict - - cell_params: local (per-cell) parameters dict + ---------- + grid : np.ndarray + 2D numpy array containing integers in range [0, n_species]. + params : Dict[str, Any] + Global parameters shared by all cells. + cell_params : Dict[str, Any] + Local per-cell parameters, typically stored as numpy arrays matching the grid shape. + neighborhood : str + The adjacency rule used ('neumann' or 'moore'). + generator : np.random.Generator + The random number generator instance for reproducibility. + species_names : Tuple[str, ...] + Human-readable names for each species state. """ # Default colormap spec (string or sequence); resolved in `visualize` at runtime @@ -39,19 +48,23 @@ class CA: # Read-only accessors for size/densities (protected attributes set in __init__) @property def rows(self) -> int: + """int: Number of rows in the grid.""" return getattr(self, "_rows") @property def cols(self) -> int: + """int: Number of columns in the grid.""" return getattr(self, "_cols") @property def densities(self) -> Tuple[float, ...]: + """Tuple[float, ...]: Initial density fraction for each species.""" return tuple(getattr(self, "_densities")) # make n_species protected with read-only property @property def n_species(self) -> int: + """int: Number of distinct species states (excluding empty state 0).""" return int(getattr(self, "_n_species")) def __init__( @@ -64,22 +77,26 @@ def __init__( cell_params: Dict[str, object], seed: Optional[int] = None, ) -> None: - """Initialize the cellular automaton. - - Args: - - rows (int): number of rows (>0) - - cols (int): number of columns (>0) - - densities (tuple of floats): initial density for each species. The - length of this tuple defines `n_species`. Values must be >=0 and sum - to at most 1. Each value gives the fraction of the grid to set to - that species (state values are 1..n_species). - - neighborhood (str): either "neumann" (4-neighbors) or "moore" - (8-neighbors). - - params (dict): global parameters. - - cell_params (dict): local per-cell parameters. - - seed (Optional[int]): seed for the numpy random generator. - - Returns: None + """ + Initialize the cellular automaton grid and configurations. + + Parameters + ---------- + rows : int + Number of rows in the grid (must be > 0). + cols : int + Number of columns in the grid (must be > 0). + densities : Tuple[float, ...] + Initial density for each species. Length defines `n_species`. + Values must sum to <= 1.0. + neighborhood : {'neumann', 'moore'} + Type of neighborhood connectivity. + params : Dict[str, Any] + Initial global parameter values. + cell_params : Dict[str, Any] + Initial local per-cell parameters. + seed : int, optional + Seed for the random number generator. """ assert isinstance(rows, int) and rows > 0, "rows must be positive int" assert isinstance(cols, int) and cols > 0, "cols must be positive int" @@ -142,12 +159,16 @@ def __init__( self.grid[r, c] = i + 1 def validate(self) -> None: - """Validate core CA invariants. + """ + Validate core CA invariants and grid dimensions. - Checks that `neighborhood` is valid, that `self.grid` has the - texpected shape `(rows, cols)`, and that any numpy arrays in - `self.cell_params` have matching shapes. Raises `ValueError` on - validation failure. + Checks that the neighborhood is valid, the grid matches initialized dimensions, + and that local parameter arrays match the grid shape. + + Raises + ------ + ValueError + If any structural invariant is violated. """ if self.neighborhood not in ("neumann", "moore"): raise ValueError("neighborhood must be 'neumann' or 'moore'") @@ -164,10 +185,29 @@ def validate(self) -> None: raise ValueError(f"cell_params['{k}'] must have shape equal to grid") def _infer_species_from_param_name(self, param_name: str) -> Optional[int]: - """Infer species index (1-based) from a parameter name using `species_names`. - - Returns the 1-based species index if a matching prefix is found, - otherwise `None`. + """ + Infer the 1-based species index from a parameter name using `species_names`. + + This method checks if the given parameter name starts with any of the + defined species names followed by an underscore (e.g., 'prey_birth'). + It is used to automatically route global parameters to the correct + species' local parameter arrays. + + Parameters + ---------- + param_name : str + The name of the parameter to check. + + Returns + ------- + Optional[int] + The 1-based index of the species if a matching prefix is found; + otherwise, None. + + Notes + ----- + The method expects `self.species_names` to be a collection of strings. + If `param_name` is not a string or no match is found, it returns None. """ if not isinstance(param_name, str): return None @@ -184,13 +224,44 @@ def evolve( min_val: Optional[float] = None, max_val: Optional[float] = None, ) -> None: - """Enable per-cell evolution for `param` on `species`. - - If `species` is None, attempt to infer the species using - `_infer_species_from_param_name(param)` which matches against - `self.species_names`. This keeps `CA` free of domain-specific - (predator/prey) logic while preserving backward compatibility when - subclasses set `species_names` (e.g. `('prey','predator')`). + """ + Enable per-cell evolution for a specific parameter on a given species. + + This method initializes a spatial parameter array (local parameter map) + for a global parameter. It allows individual cells to carry their own + values for that parameter, which can then mutate and evolve during + the simulation. + + Parameters + ---------- + param : str + The name of the global parameter to enable for evolution. + Must exist in `self.params`. + species : int, optional + The 1-based index of the species to which this parameter applies. + If None, the method attempts to infer the species from the + parameter name prefix. + sd : float, default 0.05 + The standard deviation of the Gaussian mutation applied during + inheritance/reproduction. + min_val : float, optional + The minimum allowable value for the parameter (clamping). + Defaults to 0.01 if not provided. + max_val : float, optional + The maximum allowable value for the parameter (clamping). + Defaults to 0.99 if not provided. + + Raises + ------ + ValueError + If the parameter is not in `self.params`, the species cannot be + inferred, or the species index is out of bounds. + + Notes + ----- + The local parameter is stored in `self.cell_params` as a 2D numpy + array initialized with the current global value for all cells of + the target species, and `NaN` elsewhere. """ if min_val is None: min_val = 0.01 @@ -219,13 +290,27 @@ def evolve( } def update(self) -> None: - """Perform one update step. - - This base implementation must be overridden by subclasses. It raises - NotImplementedError to indicate it should be provided by concrete - models that inherit from `CA`. - - Returns: None + """ + Perform one update step of the cellular automaton. + + This is an abstract method that defines the transition rules of the + system. It must be implemented by concrete subclasses to specify + how cell states and parameters change over time based on their + current state and neighborhood. + + Raises + ------ + NotImplementedError + If called directly on the base class instead of an implementation. + + Returns + ------- + None + + Notes + ----- + In a typical implementation, this method handles the logic for + stochastic transitions, movement, or predator-prey interactions. """ raise NotImplementedError( "Override update() in a subclass to define CA dynamics" @@ -237,12 +322,35 @@ def run( stop_evolution_at: Optional[int] = None, snapshot_iters: Optional[list] = None, ) -> None: - """Run the CA for a number of steps. - - Args: - - steps (int): number of iterations to run (must be non-negative). - - Returns: None + """ + Execute the cellular automaton simulation for a specified number of steps. + + This method drives the simulation loop, calling `update()` at each + iteration. It manages visualization updates, directory creation for + data persistence, and handles the freezing of evolving parameters + at a specific time step. + + Parameters + ---------- + steps : int + The total number of iterations to run (must be non-negative). + stop_evolution_at : int, optional + The 1-based iteration index after which parameter mutation is + disabled. Useful for observing system stability after a period + of adaptation. + snapshot_iters : List[int], optional + A list of specific 1-based iteration indices at which to save + the current grid state to the results directory. + + Returns + ------- + None + + Notes + ----- + If snapshots are requested, a results directory is automatically created + using a timestamped subfolder (e.g., 'results/run-1700000000/'). + Visualization errors are logged but do not terminate the simulation. """ assert ( isinstance(steps, int) and steps >= 0 @@ -294,19 +402,46 @@ def run( class PP(CA): - """Predator-prey CA. - - States: 0 = empty, 1 = prey, 2 = predator - - Parameters (in `params` dict). Allowed keys and defaults: - - "prey_death": 0.05 - - "predator_death": 0.1 - - "prey_birth": 0.25 - - "predator_birth": 0.2 + """ + Predator-Prey Cellular Automaton model with Numba-accelerated kernels. + + This model simulates a stochastic predator-prey system where species + interact on a 2D grid. It supports evolving per-cell death rates, + periodic boundary conditions, and both random and directed hunting + behaviors. + + Parameters + ---------- + rows : int, default 10 + Number of rows in the simulation grid. + cols : int, default 10 + Number of columns in the simulation grid. + densities : Tuple[float, ...], default (0.2, 0.1) + Initial population densities for (prey, predator). + neighborhood : {'moore', 'neumann'}, default 'moore' + The neighborhood type for cell interactions. + params : Dict[str, object], optional + Global parameters: "prey_death", "predator_death", "prey_birth", + "predator_birth". + cell_params : Dict[str, object], optional + Initial local parameter maps (2D arrays). + seed : int, optional + Random seed for reproducibility. + synchronous : bool, default True + If True, updates the entire grid at once. If False, updates + cells asynchronously. + directed_hunting : bool, default False + If True, predators selectively hunt prey rather than choosing + neighbors at random. - The constructor validates parameters are in [0,1] and raises if - other user-supplied params are present. The `synchronous` flag - chooses the update mode (default True -> synchronous updates). + Attributes + ---------- + species_names : Tuple[str, ...] + Labels for the species ('prey', 'predator'). + synchronous : bool + Current update mode. + directed_hunting : bool + Current hunting strategy logic. """ # Default colors: 0=empty black, 1=prey green, 2=predator red @@ -324,6 +459,9 @@ def __init__( synchronous: bool = True, directed_hunting: bool = False, # New directed hunting option ) -> None: + """ + Initialize the Predator-Prey CA with validated parameters and kernels. + """ # Allowed params and defaults _defaults = { "prey_death": 0.05, @@ -374,13 +512,19 @@ def __init__( # Remove PP-specific evolve wrapper; use CA.evolve with optional species def validate(self) -> None: - """Validate PP-specific invariants in addition to base CA checks. - - Checks: - - each global parameter is numeric and in [0,1] - - per-cell evolved parameter arrays (in `_evolve_info`) have non-NaN - positions matching the species grid and contain values within the - configured min/max range (or are NaN). + """ + Validate Predator-Prey specific invariants and spatial parameter arrays. + + Extends the base CA validation to ensure that numerical parameters are + within the [0, 1] probability range and that evolved parameter maps + (e.g., prey_death) correctly align with the species locations. + + Raises + ------ + ValueError + If grid shapes, parameter ranges, or species masks are inconsistent. + TypeError + If parameters are non-numeric. """ super().validate() @@ -428,6 +572,14 @@ def validate(self) -> None: ) def update_async(self) -> None: + """ + Execute an asynchronous update using the optimized Numba kernel. + + This method retrieves the evolved parameter maps and delegates the + stochastic transitions to the `PPKernel`. Asynchronous updates + typically handle cell-by-cell logic where changes can be + immediately visible to neighbors. + """ # Get the evolved prey death map # Fallback to a full array of the global param if it doesn't exist yet p_death_arr = self.cell_params.get("prey_death") @@ -455,7 +607,9 @@ def update_async(self) -> None: ) def update(self) -> None: - """Dispatch to synchronous or asynchronous update mode.""" + """ + Dispatch the simulation step based on the configured update mode. + """ if self.synchronous: self.update_sync() else: diff --git a/models/config.py b/models/config.py index 669fe38..7b2e0cd 100644 --- a/models/config.py +++ b/models/config.py @@ -15,45 +15,6 @@ # Or modify existing cfg = Config(**{**asdict(PHASE1_CONFIG), 'n_replicates': 30}) - - - -NOTE: Saving snapshots of the grid can be implemented with the following logic: - - final_grid: cluster analysis verfication for every n_stps. - - For Phase 3, save fro all grif sizes - - Add to config: - save_final_grid: bool = False - save_grid_timeseries: bool = False # Very costly, use sparingly - grid_timeseries_subsample: int = N # Save every N steps - snapshot_sample_rate: float = 0.0X # Only X% of runs save snapshots - - For run_single_simulation(): - # After cluster analysis - if cfg.save_final_grid: - # Only save for a sample of runs - if np.random.random() < cfg.snapshot_sample_rate: - result["final_grid"] = model.grid.tolist() # JSON-serializable - - # For grid timeseries (use very sparingly): - if cfg.save_grid_timeseries: - grid_snapshots = [] - - # Inside measurement loop: - if cfg.save_grid_timeseries and step % cfg.grid_timeseries_subsample == 0: - grid_snapshots.append(model.grid.copy()) - - # After loop: - if cfg.save_grid_timeseries and grid_snapshots: - # Save separately to avoid bloating JSONL - snapshot_path = output_dir / f"snapshots_{seed}.npz" - np.savez_compressed(snapshot_path, grids=np.array(grid_snapshots)) - result["snapshot_file"] = str(snapshot_path) - - - OR create separate snapshot runs using some sort of SNAPSHOT_CONFIG. """ from dataclasses import dataclass, field, asdict @@ -63,7 +24,86 @@ @dataclass class Config: - """Central configuration for all experiments.""" + """ + Central configuration for Predator-Prey Hydra Effect experiments. + + This dataclass aggregates all hyperparameters, grid settings, and + experimental phase definitions. It includes helper methods for + parameter sweep generation and runtime estimation. + + Attributes + ---------- + grid_size : int, default 1000 + The side length of the square simulation grid. + densities : Tuple[float, float], default (0.30, 0.15) + Initial population fractions for (prey, predator). + grid_sizes : Tuple[int, ...], default (50, 100, 250, 500, 1000, 2500) + Grid dimensions used for Finite-Size Scaling (FSS) analysis. + prey_birth : float, default 0.2 + Default global birth rate for the prey species. + prey_death : float, default 0.05 + Default global death rate for the prey species. + predator_birth : float, default 0.8 + Default global birth rate for the predator species. + predator_death : float, default 0.05 + Default global death rate for the predator species. + critical_prey_birth : float, default 0.20 + Identified critical birth rate for prey (Phase 1 result). + critical_prey_death : float, default 0.947 + Identified critical death rate for prey (Phase 1 result). + prey_death_range : Tuple[float, float], default (0.0, 0.2) + Bounds for the prey death rate sweep in Phase 1. + n_prey_birth : int, default 15 + Number of points along the prey birth rate axis for sweeps. + n_prey_death : int, default 5 + Number of points along the prey death rate axis for sweeps. + predator_birth_values : Tuple[float, ...] + Discrete predator birth rates used for sensitivity analysis. + predator_death_values : Tuple[float, ...] + Discrete predator death rates used for sensitivity analysis. + prey_death_offsets : Tuple[float, ...] + Delta values applied to critical death rates for perturbation tests. + n_replicates : int, default 15 + Number of independent stochastic runs per parameter set. + warmup_steps : int, default 300 + Iterations to run before beginning data collection. + measurement_steps : int, default 500 + Iterations spent collecting statistics after warmup. + with_evolution : bool, default False + Toggle for enabling per-cell parameter mutation. + evolve_sd : float, default 0.10 + Standard deviation for parameter mutation (Gaussian). + evolve_min : float, default 0.0 + Lower bound clamp for evolving parameters. + evolve_max : float, default 0.10 + Upper bound clamp for evolving parameters. + sensitivity_sd_values : Tuple[float, ...] + Range of mutation strengths tested in sensitivity phases. + synchronous : bool, default False + If True, use synchronous grid updates (not recommended). + directed_hunting : bool, default False + Toggle for targeted predator movement logic. + directed_hunting_values : Tuple[bool, ...] + Options compared during Phase 6 extensions. + save_timeseries : bool, default False + Toggle for recording step-by-step population data. + timeseries_subsample : int, default 10 + Frequency of temporal data points (e.g., every 10 steps). + collect_pcf : bool, default True + Toggle for Pair Correlation Function analysis. + pcf_sample_rate : float, default 0.2 + Probability that a specific replicate will compute PCFs. + pcf_max_distance : float, default 20.0 + Maximum radial distance for spatial correlation analysis. + pcf_n_bins : int, default 20 + Number of bins in the PCF histogram. + min_density_for_analysis : float, default 0.002 + Population threshold below which spatial analysis is skipped. + perturbation_magnitude : float, default 0.1 + Strength of external shocks applied in Phase 5. + n_jobs : int, default -1 + Number of CPU cores for parallelization (-1 uses all available). + """ # Grid settings grid_size: int = 1000 # FIXME: Decide default configuration @@ -167,13 +207,27 @@ class Config: # Helpers def get_prey_births(self) -> np.ndarray: - """Generate prey birth rate sweep values.""" + """ + Generate a linear range of prey birth rates for experimental sweeps. + + Returns + ------- + np.ndarray + 1D array of birth rates based on `prey_birth_range` and `n_prey_birth`. + """ return np.linspace( self.prey_birth_range[0], self.prey_birth_range[1], self.n_prey_birth ) def get_prey_deaths(self) -> np.ndarray: - """Generate prey death rate sweep values.""" + """ + Generate a linear range of prey death rates for experimental sweeps. + + Returns + ------- + np.ndarray + 1D array of death rates based on `prey_death_range` and `n_prey_death`. + """ return np.linspace( self.prey_death_range[0], self.prey_death_range[1], self.n_prey_death ) @@ -181,15 +235,59 @@ def get_prey_deaths(self) -> np.ndarray: def get_warmup_steps( self, L: int ) -> int: # FIXME: This method will be updated depending on Sary's results. - """Scale warmup with grid size.""" + """ + Calculate the required warmup steps scaled by grid size. + + Parameters + ---------- + L : int + The side length of the current grid. + + Returns + ------- + int + The number of steps to discard before measurement. + """ return self.warmup_steps def get_measurement_steps(self, L: int) -> int: - """Scale measurement with grid size.""" + """ + Determine the number of measurement steps based on the grid side length. + + This method allows for dynamic scaling of data collection duration relative + to the system size. Currently, it returns a fixed value, but it is + designed to be overridden for studies where measurement time must + scale with the grid size (e.g., $L^z$ scaling in critical dynamics). + + Parameters + ---------- + L : int + The side length of the current simulation grid. + + Returns + ------- + int + The number of iterations to perform for statistical measurement. + """ return self.measurement_steps def estimate_runtime(self, n_cores: int = 32) -> str: - """Estimate total runtime based on benchmark data.""" + """ + Estimate the wall-clock time required to complete the experiment. + + Calculations account for grid size scaling, PCF overhead, + replicate counts, and available parallel resources. + + Parameters + ---------- + n_cores : int, default 32 + The number of CPU cores available for execution. + + Returns + ------- + str + A human-readable summary of simulation count and estimated hours. + """ # Benchmark: ~1182 steps/sec for 100x100 grid ref_size = 100 ref_steps_per_sec = 1182 @@ -321,7 +419,24 @@ def estimate_runtime(self, n_cores: int = 32) -> str: def get_phase_config(phase: int) -> Config: - """Get config for a specific phase.""" + """ + Retrieve the configuration object for a specific experimental phase. + + Parameters + ---------- + phase : int + The phase number (1 through 6) to retrieve. + + Returns + ------- + Config + The configuration instance associated with the requested phase. + + Raises + ------ + ValueError + If the phase number is not found in the pre-defined PHASE_CONFIGS. + """ if phase not in PHASE_CONFIGS: raise ValueError( f"Unknown phase {phase}. Valid phases: {list(PHASE_CONFIGS.keys())}" diff --git a/models/numba_optimized.py b/models/numba_optimized.py index 90fba55..83c78bb 100644 --- a/models/numba_optimized.py +++ b/models/numba_optimized.py @@ -2,13 +2,6 @@ """ Numba-optimized kernels for predator-prey cellular automaton. -Added full cluster detection with labels - -Key additions: -- detect_clusters_fast(): Returns (labels, sizes_dict) like Hoshen-Kopelman -- get_cluster_stats_fast(): Full statistics including largest_fraction -- get_percolating_cluster_fast(): Percolation detection for phase transitions - Optimizations: 1. Cell-list PCF: O(N) average instead of O(N²) brute force 2. Pre-allocated work buffers for async kernel @@ -54,7 +47,29 @@ def prange(*args): @njit(cache=True) def set_numba_seed(seed: int) -> None: - """Seed Numba's internal RNG from within a JIT context.""" + """ + Seed Numba's internal random number generator from within a JIT context. + + This function ensures that Numba's independent random number generator + is synchronized with the provided seed, enabling reproducibility for + jit-compiled functions that use NumPy's random operations. + + Parameters + ---------- + seed : int + The integer value used to initialize the random number generator. + + Returns + ------- + None + + Notes + ----- + Because Numba maintains its own internal state for random number + generation, calling `np.random.seed()` in standard Python code will not + affect jit-compiled functions. This helper must be called to bridge + that gap. + """ np.random.seed(seed) @@ -79,7 +94,55 @@ def _pp_async_kernel_random( evolution_stopped: bool, occupied_buffer: np.ndarray, ) -> np.ndarray: - """Asynchronous predator-prey update kernel with random neighbor selection.""" + """ + Asynchronous predator-prey update kernel with random neighbor selection. + + This Numba-accelerated kernel performs an asynchronous update of the + simulation grid. It identifies all occupied cells, shuffles them to + ensure unbiased processing, and applies stochastic rules for prey + mortality, prey reproduction (with optional parameter evolution), + predator mortality, and predation. + + Parameters + ---------- + grid : np.ndarray + 2D integer array representing the simulation grid (0: Empty, 1: Prey, 2: Predator). + prey_death_arr : np.ndarray + 2D float array storing the individual prey death rates for evolution tracking. + p_birth_val : float + Base probability of prey reproduction into an adjacent empty cell. + p_death_val : float + Base probability of prey death (though individual rates in `prey_death_arr` are used). + pred_birth_val : float + Probability of a predator reproducing after consuming prey. + pred_death_val : float + Probability of a predator dying. + dr_arr : np.ndarray + Array of row offsets defining the neighborhood. + dc_arr : np.ndarray + Array of column offsets defining the neighborhood. + evolve_sd : float + Standard deviation of the mutation applied to the prey death rate during reproduction. + evolve_min : float + Lower bound for the evolved prey death rate. + evolve_max : float + Upper bound for the evolved prey death rate. + evolution_stopped : bool + If True, offspring inherit the parent's death rate without mutation. + occupied_buffer : np.ndarray + Pre-allocated 2D array used to store and shuffle coordinates of occupied cells. + + Returns + ------- + grid : np.ndarray + The updated simulation grid. + + Notes + ----- + The kernel uses periodic boundary conditions. The Fisher-Yates shuffle on + `occupied_buffer` ensures that the asynchronous updates do not introduce + directional bias. + """ rows, cols = grid.shape n_shifts = len(dr_arr) @@ -166,12 +229,50 @@ def _pp_async_kernel_directed( """ Asynchronous predator-prey update kernel with directed behavior. - Directed behavior: - - Prey: Searches all neighbors for empty cells, randomly picks one to reproduce into - - Predator: Searches all neighbors for prey, randomly picks one to hunt - - This makes both species more "intelligent" compared to random neighbor selection. - Uses efficient two-pass counting approach (Numba-compatible, no heap allocation). + This kernel implements "intelligent" species behavior: prey actively search + for empty spaces to reproduce, and predators actively search for nearby + prey to hunt. A two-pass approach is used to stochastically select a + valid target from the neighborhood without heap allocation. + + Parameters + ---------- + grid : np.ndarray + 2D integer array representing the simulation grid (0: Empty, 1: Prey, 2: Predator). + prey_death_arr : np.ndarray + 2D float array storing individual prey mortality rates for evolution. + p_birth_val : float + Probability of prey reproduction attempt. + p_death_val : float + Base probability of prey mortality. + pred_birth_val : float + Probability of a predator reproduction attempt (hunting success). + pred_death_val : float + Probability of predator mortality. + dr_arr : np.ndarray + Row offsets defining the spatial neighborhood (e.g., Moore or von Neumann). + dc_arr : np.ndarray + Column offsets defining the spatial neighborhood. + evolve_sd : float + Standard deviation for mutations in prey death rates. + evolve_min : float + Minimum allowable value for evolved prey death rates. + evolve_max : float + Maximum allowable value for evolved prey death rates. + evolution_stopped : bool + If True, prevents mutation during prey reproduction. + occupied_buffer : np.ndarray + Pre-allocated array for storing and shuffling active cell coordinates. + + Returns + ------- + grid : np.ndarray + The updated simulation grid. + + Notes + ----- + The directed behavior significantly changes the system dynamics compared to + random neighbor selection, often leading to different critical thresholds + and spatial patterning. Periodic boundary conditions are applied. """ rows, cols = grid.shape n_shifts = len(dr_arr) @@ -288,7 +389,37 @@ def _pp_async_kernel_directed( class PPKernel: - """Wrapper for predator-prey kernel with pre-allocated buffers.""" + """ + Wrapper for predator-prey kernel with pre-allocated buffers. + + This class manages the spatial configuration and memory buffers required + for the Numba-accelerated update kernels. By pre-allocating the + `occupied_buffer`, it avoids expensive memory allocations during the + simulation loop. + + Parameters + ---------- + rows : int + Number of rows in the simulation grid. + cols : int + Number of columns in the simulation grid. + neighborhood : {'moore', 'von_neumann'}, optional + The neighborhood type determining adjacent cells. 'moore' includes + diagonals (8 neighbors), 'von_neumann' does not (4 neighbors). + Default is 'moore'. + directed_hunting : bool, optional + If True, uses the directed behavior kernel where species search for + targets. If False, uses random neighbor selection. Default is False. + + Attributes + ---------- + rows : int + Grid row count. + cols : int + Grid column count. + directed_hunting : bool + Toggle for intelligent behavior logic. + """ def __init__( self, @@ -322,6 +453,37 @@ def update( evolve_max: float = 0.1, evolution_stopped: bool = True, ) -> np.ndarray: + """ + Execute a single asynchronous update step using the configured kernel. + + Parameters + ---------- + grid : np.ndarray + The current 2D simulation grid. + prey_death_arr : np.ndarray + 2D array of individual prey mortality rates. + prey_birth : float + Prey reproduction probability. + prey_death : float + Base prey mortality probability. + pred_birth : float + Predator reproduction (hunting success) probability. + pred_death : float + Predator mortality probability. + evolve_sd : float, optional + Mutation standard deviation (default 0.1). + evolve_min : float, optional + Minimum evolved death rate (default 0.001). + evolve_max : float, optional + Maximum evolved death rate (default 0.1). + evolution_stopped : bool, optional + Whether to disable mutation during this step (default True). + + Returns + ------- + np.ndarray + The updated grid after one full asynchronous pass. + """ if self.directed_hunting: return _pp_async_kernel_directed( grid, @@ -372,7 +534,44 @@ def _flood_fill( cols: int, moore: bool, ) -> int: - """Stack-based flood fill with configurable neighborhood and periodic BC.""" + """ + Perform a stack-based flood fill to measure the size of a connected cluster. + + This Numba-accelerated function identifies all contiguous cells of a + specific target value starting from a given coordinate. It supports + both Moore and von Neumann neighborhoods and implements periodic + boundary conditions (toroidal topology). + + Parameters + ---------- + grid : np.ndarray + 2D integer array representing the simulation environment. + visited : np.ndarray + 2D boolean array tracked across calls to avoid re-processing cells. + start_r : int + Starting row index for the flood fill. + start_c : int + Starting column index for the flood fill. + target : int + The cell value (e.g., 1 for Prey, 2 for Predator) to include in the cluster. + rows : int + Total number of rows in the grid. + cols : int + Total number of columns in the grid. + moore : bool + If True, use a Moore neighborhood (8 neighbors). If False, use a + von Neumann neighborhood (4 neighbors). + + Returns + ------- + size : int + The total number of connected cells belonging to the cluster. + + Notes + ----- + The function uses a manual stack implementation to avoid recursion limit + issues and is optimized for use within JIT-compiled loops. + """ max_stack = rows * cols stack_r = np.empty(max_stack, dtype=np.int32) stack_c = np.empty(max_stack, dtype=np.int32) @@ -415,7 +614,36 @@ def _flood_fill( @njit(cache=True) def _measure_clusters(grid: np.ndarray, species: int, moore: bool = True) -> np.ndarray: - """Measure all cluster sizes for a species (sizes only).""" + """ + Identify and measure the sizes of all connected clusters for a specific species. + + This function scans the entire grid and initiates a flood-fill algorithm + whenever an unvisited cell of the target species is encountered. It + returns an array containing the size (cell count) of each identified cluster. + + Parameters + ---------- + grid : np.ndarray + 2D integer array representing the simulation environment. + species : int + The target species identifier (e.g., 1 for Prey, 2 for Predator). + moore : bool, optional + Determines the connectivity logic. If True, uses the Moore neighborhood + (8 neighbors); if False, uses the von Neumann neighborhood (4 neighbors). + Default is True. + + Returns + ------- + cluster_sizes : np.ndarray + A 1D array of integers where each element represents the size of + one connected cluster. + + Notes + ----- + This function is Numba-optimized and utilizes an internal `visited` mask + to ensure each cell is processed only once, maintaining $O(N)$ + complexity relative to the number of cells. + """ rows, cols = grid.shape visited = np.zeros((rows, cols), dtype=np.bool_) @@ -501,66 +729,6 @@ def _detect_clusters_numba( return labels, sizes[:n_clusters] - -@njit(cache=True) -def _check_percolation( - labels: np.ndarray, - sizes: np.ndarray, - direction: int, -) -> Tuple[bool, int, int]: - """ - Check for percolating clusters. - - Args: - direction: 0=horizontal, 1=vertical, 2=both - - Returns: - percolates, perc_label, perc_size - """ - rows, cols = labels.shape - max_label = len(sizes) - - touches_left = np.zeros(max_label + 1, dtype=np.bool_) - touches_right = np.zeros(max_label + 1, dtype=np.bool_) - touches_top = np.zeros(max_label + 1, dtype=np.bool_) - touches_bottom = np.zeros(max_label + 1, dtype=np.bool_) - - for i in range(rows): - if labels[i, 0] > 0: - touches_left[labels[i, 0]] = True - if labels[i, cols - 1] > 0: - touches_right[labels[i, cols - 1]] = True - - for j in range(cols): - if labels[0, j] > 0: - touches_top[labels[0, j]] = True - if labels[rows - 1, j] > 0: - touches_bottom[labels[rows - 1, j]] = True - - best_label = 0 - best_size = 0 - - for label in range(1, max_label + 1): - percolates_h = touches_left[label] and touches_right[label] - percolates_v = touches_top[label] and touches_bottom[label] - - is_percolating = False - if direction == 0: - is_percolating = percolates_h - elif direction == 1: - is_percolating = percolates_v - else: - is_percolating = percolates_h or percolates_v - - if is_percolating: - cluster_size = sizes[label - 1] - if cluster_size > best_size: - best_size = cluster_size - best_label = label - - return best_label > 0, best_label, best_size - - # ============================================================================ # PUBLIC API - CLUSTER DETECTION # ============================================================================ @@ -572,18 +740,39 @@ def measure_cluster_sizes_fast( neighborhood: str = "moore", ) -> np.ndarray: """ - Measure cluster sizes only (fastest method). - - Use when you only need size statistics, not the label array. - ~25x faster than pure Python. - - Args: - grid: 2D array of cell states - species: Target species value (1=prey, 2=predator) - neighborhood: 'moore' (8-connected) or 'neumann' (4-connected) - - Returns: - 1D array of cluster sizes + Measure cluster sizes for a specific species using Numba-accelerated flood fill. + + This function provides a high-performance interface for calculating cluster + size statistics without the overhead of generating a full label map. It is + optimized for large-scale simulation analysis where only distribution + metrics (e.g., mean size, max size) are required. + + Parameters + ---------- + grid : np.ndarray + A 2D array representing the simulation environment. + species : int + The target species identifier (e.g., 1 for Prey, 2 for Predator). + neighborhood : {'moore', 'neumann'}, optional + The connectivity rule. 'moore' uses 8-way connectivity (including diagonals); + 'neumann' uses 4-way connectivity. Default is 'moore'. + + Returns + ------- + cluster_sizes : np.ndarray + A 1D array of integers, where each element is the cell count of an + identified cluster. + + Notes + ----- + The input grid is cast to `int32` to ensure compatibility with the + underlying JIT-compiled `_measure_clusters` kernel. + + Examples + -------- + >>> sizes = measure_cluster_sizes_fast(grid, species=1, neighborhood='moore') + >>> if sizes.size > 0: + ... print(f"Largest cluster: {sizes.max()}") """ grid_int = np.asarray(grid, dtype=np.int32) moore = neighborhood == "moore" @@ -596,23 +785,41 @@ def detect_clusters_fast( neighborhood: str = "moore", ) -> Tuple[np.ndarray, Dict[int, int]]: """ - Full cluster detection with labels (Numba-accelerated). - - Returns both the label array and size dictionary for richer analysis. - - Args: - grid: 2D array of cell states - species: Target species value (1=prey, 2=predator) - neighborhood: 'moore' (8-connected) or 'neumann' (4-connected) - - Returns: - labels: 2D array where each cell has its cluster ID (0 = non-target) - sizes: Dict mapping cluster_id -> cluster_size - - Example: - >>> labels, sizes = detect_clusters_fast(grid, species=1) - >>> largest_id = max(sizes, key=sizes.get) - >>> largest_size = sizes[largest_id] + Perform full cluster detection with labels using Numba acceleration. + + This function returns a label array for spatial analysis and a dictionary + of cluster sizes. It is significantly faster than standard Python or + SciPy equivalents for large simulation grids. + + Parameters + ---------- + grid : np.ndarray + A 2D array representing the simulation environment. + species : int + The target species identifier (e.g., 1 for Prey, 2 for Predator). + neighborhood : {'moore', 'neumann'}, optional + The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' + uses 4-way connectivity. Default is 'moore'. + + Returns + ------- + labels : np.ndarray + A 2D int32 array where each cell contains its unique cluster ID. + Cells not belonging to the target species are 0. + sizes : dict + A dictionary mapping cluster IDs to their respective cell counts. + + Notes + ----- + The underlying Numba kernel uses a stack-based flood fill to avoid + recursion limits and handles periodic boundary conditions. + + Examples + -------- + >>> labels, sizes = detect_clusters_fast(grid, species=1) + >>> if sizes: + ... largest_id = max(sizes, key=sizes.get) + ... print(f"Cluster {largest_id} size: {sizes[largest_id]}") """ grid_int = np.asarray(grid, dtype=np.int32) moore = neighborhood == "moore" @@ -627,23 +834,42 @@ def get_cluster_stats_fast( neighborhood: str = "moore", ) -> Dict: """ - Compute comprehensive cluster statistics (Numba-accelerated). - - Args: - grid: 2D array of cell states - species: Target species value - neighborhood: 'moore' or 'neumann' - - Returns: - Dictionary with keys: - - 'n_clusters': Total number of clusters - - 'sizes': Array of sizes (sorted descending) - - 'largest': Size of largest cluster - - 'largest_fraction': S_max / N (order parameter for percolation) - - 'mean_size': Mean cluster size - - 'size_distribution': Dict[size -> count] - - 'labels': Cluster label array - - 'size_dict': Dict[label -> size] + Compute comprehensive cluster statistics for a species using Numba acceleration. + + This function integrates cluster detection and labeling to provide a + full suite of spatial metrics. It calculates the cluster size distribution + and the largest cluster fraction, which often serves as an order + parameter in percolation theory and Phase 1-3 analyses. + + Parameters + ---------- + grid : np.ndarray + A 2D array representing the simulation environment. + species : int + The target species identifier (e.g., 1 for Prey, 2 for Predator). + neighborhood : {'moore', 'neumann'}, optional + The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' + uses 4-way connectivity. Default is 'moore'. + + Returns + ------- + stats : dict + A dictionary containing: + - 'n_clusters': Total count of isolated clusters. + - 'sizes': Sorted array (descending) of all cluster sizes. + - 'largest': Size of the single largest cluster. + - 'largest_fraction': Size of the largest cluster divided by + the total population of the species. + - 'mean_size': Average size of all clusters. + - 'size_distribution': Frequency mapping of {size: count}. + - 'labels': 2D array of unique cluster IDs. + - 'size_dict': Mapping of {label_id: size}. + + Examples + -------- + >>> stats = get_cluster_stats_fast(grid, species=1) + >>> print(f"Found {stats['n_clusters']} prey clusters.") + >>> print(f"Order parameter: {stats['largest_fraction']:.3f}") """ labels, size_dict = detect_clusters_fast(grid, species, neighborhood) @@ -680,47 +906,6 @@ def get_cluster_stats_fast( "size_dict": size_dict, } - -def get_percolating_cluster_fast( - grid: np.ndarray, - species: int, - neighborhood: str = "moore", - direction: str = "both", -) -> Tuple[bool, int, int, np.ndarray]: - """ - Detect percolating (spanning) clusters (Numba-accelerated). - - A percolating cluster connects opposite edges of the grid, - indicating a phase transition in percolation theory. - - Args: - grid: 2D array of cell states - species: Target species value - neighborhood: 'moore' or 'neumann' - direction: 'horizontal', 'vertical', or 'both' - - Returns: - percolates: True if a spanning cluster exists - cluster_label: Label of the percolating cluster (0 if none) - cluster_size: Size of the percolating cluster (0 if none) - labels: Full cluster label array - - Example: - >>> percolates, label, size, labels = get_percolating_cluster_fast(grid, 1) - >>> if percolates: - >>> print(f"Prey percolates with {size} cells!") - """ - grid_int = np.asarray(grid, dtype=np.int32) - moore = neighborhood == "moore" - labels, sizes_arr = _detect_clusters_numba(grid_int, np.int32(species), moore) - - dir_map = {"horizontal": 0, "vertical": 1, "both": 2} - dir_int = dir_map.get(direction, 2) - - percolates, perc_label, perc_size = _check_percolation(labels, sizes_arr, dir_int) - return percolates, int(perc_label), int(perc_size), labels - - # ============================================================================ # PCF COMPUTATION (Cell-list accelerated) # ============================================================================ @@ -733,7 +918,48 @@ def _build_cell_list( L_row: float, L_col: float, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, float, float]: - """Build cell list for spatial hashing.""" + """ + Build a cell list for spatial hashing to accelerate neighbor lookups. + + This Numba-optimized function partitions a set of coordinates into a + grid of cells. It uses a three-pass approach to calculate cell occupancy, + compute starting offsets for each cell in a flat index array, and finally + populate that array with position indices. + + Parameters + ---------- + positions : np.ndarray + An (N, 2) float array of coordinates within the simulation domain. + n_cells : int + The number of cells along one dimension of the square grid. + L_row : float + The total height (row extent) of the simulation domain. + L_col : float + The total width (column extent) of the simulation domain. + + Returns + ------- + indices : np.ndarray + A 1D array of original position indices, reordered so that indices + belonging to the same cell are contiguous. + offsets : np.ndarray + A 2D array where `offsets[r, c]` is the starting index in the + `indices` array for cell (r, c). + cell_counts : np.ndarray + A 2D array where `cell_counts[r, c]` is the number of points + contained in cell (r, c). + cell_size_r : float + The calculated height of an individual cell. + cell_size_c : float + The calculated width of an individual cell. + + Notes + ----- + This implementation assumes periodic boundary conditions via the + modulo operator during coordinate-to-cell mapping. It is designed to + eliminate heap allocations within the main simulation loop by using + Numba's efficient array handling. + """ n_pos = len(positions) cell_size_r = L_row / n_cells cell_size_c = L_col / n_cells @@ -772,7 +998,39 @@ def _periodic_dist_sq( L_row: float, L_col: float, ) -> float: - """Squared periodic distance.""" + """ + Calculate the squared Euclidean distance between two points with periodic boundary conditions. + + This Numba-optimized function accounts for toroidal topology by finding the + shortest path between coordinates across the grid edges. Using the squared + distance avoids the computational expense of a square root operation, + making it ideal for high-frequency spatial queries. + + Parameters + ---------- + r1 : float + Row coordinate of the first point. + c1 : float + Column coordinate of the first point. + r2 : float + Row coordinate of the second point. + c2 : float + Column coordinate of the second point. + L_row : float + Total height (row extent) of the periodic domain. + L_col : float + Total width (column extent) of the periodic domain. + + Returns + ------- + dist_sq : float + The squared shortest distance between the two points. + + Notes + ----- + The function applies the minimum image convention, ensuring that the + distance never exceeds half the domain length in any dimension. + """ dr = abs(r1 - r2) dc = abs(c1 - c2) if dr > L_row * 0.5: @@ -798,7 +1056,57 @@ def _pcf_cell_list( self_correlation: bool, n_cells: int, ) -> np.ndarray: - """Compute PCF histogram using cell lists.""" + """ + Compute a Pair Correlation Function (PCF) histogram using spatial cell lists. + + This Numba-accelerated parallel kernel calculates distances between two sets + of points (pos_i and pos_j). It uses a cell list (spatial hashing) to + restrict distance calculations to neighboring cells within the maximum + specified distance, significantly improving performance from $O(N^2)$ + to $O(N)$. + + Parameters + ---------- + pos_i : np.ndarray + (N, 2) float array of coordinates for the primary species. + pos_j : np.ndarray + (M, 2) float array of coordinates for the secondary species. + indices_j : np.ndarray + Flattened indices of pos_j sorted by cell, produced by `_build_cell_list`. + offsets_j : np.ndarray + 2D array of starting offsets for each cell in `indices_j`. + counts_j : np.ndarray + 2D array of particle counts within each cell for species J. + cell_size_r : float + Height of a single spatial cell. + cell_size_c : float + Width of a single spatial cell. + L_row : float + Total height of the periodic domain. + L_col : float + Total width of the periodic domain. + max_distance : float + Maximum radial distance (r) to consider for the correlation. + n_bins : int + Number of bins in the distance histogram. + self_correlation : bool + If True, assumes species I and J are the same and avoids double-counting + or self-interaction. + n_cells : int + Number of cells per dimension in the spatial hash grid. + + Returns + ------- + hist : np.ndarray + A 1D array of length `n_bins` containing the counts of pairs found + at each radial distance. + + Notes + ----- + The kernel uses `prange` for parallel execution across points in `pos_i`. + Local histograms are used per thread to prevent race conditions during + reduction. Periodic boundary conditions are handled via `_periodic_dist_sq`. + """ n_i = len(pos_i) bin_width = max_distance / n_bins max_dist_sq = max_distance * max_distance @@ -855,7 +1163,46 @@ def compute_pcf_periodic_fast( n_bins: int = 50, self_correlation: bool = False, ) -> Tuple[np.ndarray, np.ndarray, int]: - """Cell-list accelerated PCF computation.""" + """ + Compute the Pair Correlation Function (PCF) using cell-list acceleration. + + This high-level function coordinates the spatial hashing and histogram + calculation to determine the $g(r)$ function. It normalizes the resulting + histogram by the expected number of pairs in an ideal gas of the same + density, accounting for the toroidal area of each radial bin. + + Parameters + ---------- + positions_i : np.ndarray + (N, 2) array of coordinates for species I. + positions_j : np.ndarray + (M, 2) array of coordinates for species J. + grid_shape : tuple of int + The (rows, cols) dimensions of the simulation grid. + max_distance : float + The maximum radius to calculate correlations for. + n_bins : int, optional + Number of bins for the radial distribution (default 50). + self_correlation : bool, optional + Set to True if computing the correlation of a species with itself + to avoid self-counting (default False). + + Returns + ------- + bin_centers : np.ndarray + The central radial distance for each histogram bin. + pcf : np.ndarray + The normalized $g(r)$ values. A value of 1.0 indicates no spatial + correlation; > 1.0 indicates clustering; < 1.0 indicates repulsion. + total_pairs : int + The total count of pairs found within the `max_distance`. + + Notes + ----- + The function dynamically determines the optimal number of cells for the + spatial hash based on the `max_distance` and grid dimensions to maintain + linear time complexity. + """ rows, cols = grid_shape L_row, L_col = float(rows), float(cols) area = L_row * L_col @@ -915,7 +1262,39 @@ def compute_all_pcfs_fast( max_distance: Optional[float] = None, n_bins: int = 50, ) -> Dict[str, Tuple[np.ndarray, np.ndarray, int]]: - """Compute all three PCFs using cell-list acceleration.""" + """ + Compute all three species Pair Correlation Functions (PCFs) using cell-list acceleration. + + This function calculates the spatial auto-correlations (Prey-Prey, + Predator-Predator) and the cross-correlation (Prey-Predator) for a given + simulation grid. It identifies particle positions and leverages + Numba-accelerated cell lists to handle the computations efficiently. + + Parameters + ---------- + grid : np.ndarray + 2D integer array where 1 represents prey and 2 represents predators. + max_distance : float, optional + The maximum radial distance for the correlation. Defaults to 1/4 + of the minimum grid dimension if not provided. + n_bins : int, optional + Number of distance bins for the histogram. Default is 50. + + Returns + ------- + results : dict + A dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'. + Each value is a tuple containing: + - bin_centers (np.ndarray): Radial distances. + - pcf_values (np.ndarray): Normalized g(r) values. + - pair_count (int): Total number of pairs found. + + Notes + ----- + The PCF provides insight into the spatial organization of the system. + g(r) > 1 at short distances indicates aggregation (clustering), + while g(r) < 1 indicates exclusion or repulsion. + """ rows, cols = grid.shape if max_distance is None: max_distance = min(rows, cols) / 4.0 @@ -964,7 +1343,32 @@ def compute_all_pcfs_fast( def warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False): - """Pre-compile all Numba kernels.""" + """ + Pre-compile all Numba-accelerated kernels to avoid first-run latency. + + This function executes a single step of the simulation and each analysis + routine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation, + the first call to a decorated function incurs a compilation overhead. + Running this warmup ensures that subsequent experimental runs are timed + accurately and perform at full speed. + + Parameters + ---------- + grid_size : int, optional + The side length of the dummy grid used for warmup (default 100). + directed_hunting : bool, optional + If True, also warms up the directed behavior update kernel (default False). + + Returns + ------- + None + + Notes + ----- + This function checks for `NUMBA_AVAILABLE` before execution. It warms up + the `PPKernel` (random and optionally directed), as well as the + spatial analysis functions (`compute_all_pcfs_fast`, `detect_clusters_fast`, etc.). + """ if not NUMBA_AVAILABLE: return @@ -991,11 +1395,39 @@ def warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False): _ = measure_cluster_sizes_fast(grid, 1) _ = detect_clusters_fast(grid, 1) _ = get_cluster_stats_fast(grid, 1) - _ = get_percolating_cluster_fast(grid, 1) def benchmark_kernels(grid_size: int = 100, n_runs: int = 20): - """Benchmark random vs directed kernels.""" + """ + Benchmark the execution performance of random vs. directed update kernels. + + This utility measures the average time per simulation step for both the + stochastic (random neighbor) and heuristic (directed hunting/reproduction) + update strategies. It accounts for the computational overhead introduced + by the "intelligent" search logic used in directed mode. + + Parameters + ---------- + grid_size : int, optional + The side length of the square simulation grid (default 100). + n_runs : int, optional + The number of iterations to perform for averaging performance (default 20). + + Returns + ------- + t_random : float + Average time per step for the random kernel in milliseconds. + t_directed : float + Average time per step for the directed kernel in milliseconds. + + Notes + ----- + The function ensures a fair comparison by: + 1. Using a fixed seed for reproducible initial grid states. + 2. Warming up Numba kernels before timing to exclude JIT compilation latency. + 3. Copying the grid and death arrays for each iteration to maintain + consistent population densities throughout the benchmark. + """ import time print("=" * 60) @@ -1049,7 +1481,32 @@ def benchmark_kernels(grid_size: int = 100, n_runs: int = 20): def benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20): - """Benchmark cluster detection methods.""" + """ + Benchmark the performance of different cluster detection and analysis routines. + + This function evaluates three levels of spatial analysis: + 1. Size measurement only (fastest, no label map). + 2. Full detection (returns label map and size dictionary). + 3. Comprehensive statistics (calculates distributions, means, and order parameters). + + Parameters + ---------- + grid_size : int, optional + Side length of the square grid for benchmarking (default 100). + n_runs : int, optional + Number of iterations to average for performance results (default 20). + + Returns + ------- + stats : dict + The result dictionary from the final comprehensive statistics run. + + Notes + ----- + The benchmark uses a fixed prey density of 30% to ensure a representative + distribution of clusters. It pre-warms the Numba kernels to ensure that + the measurements reflect execution speed rather than compilation time. + """ import time print("=" * 60) @@ -1070,7 +1527,6 @@ def benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20): _ = measure_cluster_sizes_fast(grid, 1) _ = detect_clusters_fast(grid, 1) _ = get_cluster_stats_fast(grid, 1) - _ = get_percolating_cluster_fast(grid, 1) # Benchmark sizes only t0 = time.perf_counter() @@ -1093,13 +1549,6 @@ def benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20): t_stats = (time.perf_counter() - t0) / n_runs * 1000 print(f"get_cluster_stats_fast: {t_stats:.2f} ms") - # Benchmark percolation - t0 = time.perf_counter() - for _ in range(n_runs): - perc, label, size, _ = get_percolating_cluster_fast(grid, 1) - t_perc = (time.perf_counter() - t0) / n_runs * 1000 - print(f"get_percolating_cluster_fast: {t_perc:.2f} ms (percolates={perc})") - print( f"\nOverhead for labels: {t_detect - t_sizes:.2f} ms (+{100*(t_detect/t_sizes - 1):.0f}%)" ) diff --git a/notebooks/.DS_Store b/notebooks/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..90df1da2b8df92bc09467b42fd5a5b13f2b16e89 GIT binary patch literal 6148 zcmeHKOHKko5PhvIh-`=(jh|#rK%yrY2`*fF0Av_Mk_iw#5_Yrq4qn0YxzbnF9Wo3< z*r_p9ru+4;UU$7_x@`ciy-aU_CV)DVVC{rig-O4p9Xkpw(&?1r7G3m^@vplx+8Rs& zQ{b;DAm8pKru)W_%VhL*`%LjlY|UBKHN32^`)jk zo^G*D>AS*!Y#C38)gF$w5=U`m3D1-PUdfX&A0TDD&+<}NDa`v;_E}=RDy*!URpOL6 zW*F*Bl@a5PyzhmG&kpbkYld}o?D#0LT^wMWGx{EPWufb=T%qnsafOHUTyc*Vc%ib- zTB>H1T`ICRHY=4K7_qiyq)F-bA-hoYV%6+d72usMR&RK;+!QbcOo5F8ay~>%f^opy zqaGd1^a(($v005}c}s{-6fh2$dt?vIc~_!$b>WHOygS>YFfI<5d-U#b;ql?Z%r3l8 zoX$@ENQcA49xXQoOo43$j(xQ$_y7I=_y4w&)l30X;9n`=YMsYUn<0g}btgEvYa^y7 qCNUY8dsG)@;X2lWT*c>1YV3=oK#T+C9@#>(e*{zp%S?eERp1-qGpXzV literal 0 HcmV?d00001 diff --git a/notebooks/plots.ipynb b/notebooks/plots.ipynb index 66b07b4..ea33e96 100644 --- a/notebooks/plots.ipynb +++ b/notebooks/plots.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 43, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -304,7 +304,7 @@ } ], "source": [ - "DATA_ROOT = Path.home() / \"CSS_Project\" / \"hpc_data\"\n", + "DATA_ROOT = Path.home() / \"CSS_Project\" / \"data\"\n", "\n", "\n", "def load_jsonl(filepath):\n", @@ -352,7 +352,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -474,7 +474,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -515,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -597,7 +597,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -709,7 +709,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -805,7 +805,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -839,7 +839,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -909,7 +909,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -960,7 +960,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -1074,7 +1074,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -1102,7 +1102,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -1179,7 +1179,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -1218,7 +1218,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -1304,7 +1304,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1379,7 +1379,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -1446,7 +1446,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -1600,7 +1600,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -1820,7 +1820,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -1831,7 +1831,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -1860,7 +1860,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -1891,7 +1891,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -1929,7 +1929,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -1978,7 +1978,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -2022,7 +2022,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -2077,7 +2077,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -2092,7 +2092,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/var/folders/fm/9tf79c_d1691c4jwk2qqr42m0000gn/T/ipykernel_6177/2511870083.py:122: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n", + "/var/folders/fm/9tf79c_d1691c4jwk2qqr42m0000gn/T/ipykernel_35436/2511870083.py:122: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n", " plt.tight_layout(rect=[0, 0, 0.88, 1])\n" ] }, @@ -2236,7 +2236,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -2406,7 +2406,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -2519,7 +2519,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -2694,7 +2694,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -2705,7 +2705,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -2734,7 +2734,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -2765,7 +2765,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -2803,7 +2803,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -2852,7 +2852,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -2896,7 +2896,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 44, "metadata": {}, "outputs": [ { @@ -2951,7 +2951,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 45, "metadata": {}, "outputs": [ { @@ -3121,7 +3121,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 46, "metadata": {}, "outputs": [ { @@ -3234,7 +3234,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 47, "metadata": {}, "outputs": [ { diff --git a/scripts/.DS_Store b/scripts/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..effc3961920249afcd61b8cadb099545e20a701d GIT binary patch literal 6148 zcmeHKJ5B>J5S@V(E2T+^sC7r@3ou+;Ibo{mDwymn=rd`6KxIcNmc({8!`_6jx1M6=6 zA&YrN*pOf#7zhS}fneZQ4B*ZdDaMA;2Lr)CF!08JoDT_2uyo9ZdUT-E5&$UAXcg#E zONdW$EFH5URv>JyKyzg;G1%NOpWLo=%!cMp?8OKB&acG_>+Xo3)SS39j6N6$2Ko#f z+HfNG|2ck{!6Lu!5~E-s7}zrgcvLN_89vJI)=!@&cWpwuL=zFaED8jA?-783oFhlN cXzNLI*p-giP@>4Zx&z}OpoByh4EzBDFHS%;MgRZ+ literal 0 HcmV?d00001 diff --git a/scripts/experiments.py b/scripts/experiments.py index db7e63c..a7be8d6 100644 --- a/scripts/experiments.py +++ b/scripts/experiments.py @@ -7,8 +7,7 @@ Phase 2: Self-organization analysis (evolution toward criticality) Phase 3: Finite-size scaling at critical point Phase 4: Sensitivity analysis across parameter regimes - Phase 5: Perturbation analysis (critical slowing down) - Phase 6: Model extensions (directed hunting comparison) + Phase 5: Model extensions (directed hunting comparison) Usage: python experiments.py --phase 1 # Run phase 1 @@ -16,16 +15,6 @@ python experiments.py --phase all # Run all phases python experiments.py --phase 1 --output results/ # Custom output """ - - -# NOTE (1): The soc_analysis script used temporal avalache data to assess SOC. -# This functionality is not yet implemented here. We can still derive that data -# from the full time series using np.diff(prey_timeseries) - -# NOTE (2): Post-processing utilities and plotting are in scripts/analysis.py. This script should -# solely focus on running the experiments and saving raw results. - - import argparse import hashlib import json @@ -76,20 +65,103 @@ def set_numba_seed(seed): # Utility Functions # ============================================================================= - def generate_unique_seed(params: dict, rep: int) -> int: - """Create deterministic seed from parameters.""" + """ + Create a deterministic seed from a dictionary of parameters and a repetition index. + + This function serializes the input dictionary into a sorted JSON string, + appends the repetition count, and hashes the resulting string using SHA-256. + The first 8 characters of the hex digest are then converted to an integer + to provide a stable, unique seed for random number generators. + + Parameters + ---------- + params : dict + A dictionary of configuration parameters. Keys are sorted to ensure + determinism regardless of insertion order. + rep : int + The repetition or iteration index, used to ensure different seeds + are generated for the same parameter set across multiple runs. + + Returns + ------- + int + A unique integer seed derived from the input parameters. + + Examples + -------- + >>> params = {'learning_rate': 0.01, 'batch_size': 32} + >>> generate_unique_seed(params, 1) + 3432571217 + >>> generate_unique_seed(params, 2) + 3960013583 + """ identifier = json.dumps(params, sort_keys=True) + f"_{rep}" return int(hashlib.sha256(identifier.encode()).hexdigest()[:8], 16) def count_populations(grid: np.ndarray) -> Tuple[int, int, int]: - """Count empty, prey, predator cells.""" + """ + Count the number of empty, prey, and predator cells in the simulation grid. + + Parameters + ---------- + grid : np.ndarray + A 2D NumPy array representing the simulation environment, where: + - 0: Empty cell + - 1: Prey + - 2: Predator + + Returns + ------- + empty_count : int + Total number of cells with a value of 0. + prey_count : int + Total number of cells with a value of 1. + predator_count : int + Total number of cells with a value of 2. + + Examples + -------- + >>> grid = np.array([[0, 1], [2, 1]]) + >>> count_populations(grid) + (1, 2, 1) + """ return int(np.sum(grid == 0)), int(np.sum(grid == 1)), int(np.sum(grid == 2)) def get_evolved_stats(model, param: str) -> Dict: - """Get statistics of evolved parameter from model.""" + """ + Get statistics of an evolved parameter from the model. + + This function retrieves parameter values from the model's internal storage, + filters out NaN values, and calculates basic descriptive statistics. + + Parameters + ---------- + model : object + The simulation model instance containing a `cell_params` attribute + with a `.get()` method. + param : str + The name of the parameter to calculate statistics for. + + Returns + ------- + stats : dict + A dictionary containing the following keys: + - 'mean': Arithmetic mean of valid values. + - 'std': Standard deviation of valid values. + - 'min': Minimum valid value. + - 'max': Maximum valid value. + - 'n': Count of non-NaN values. + If no valid data is found, all stats return NaN and n returns 0. + + Examples + -------- + >>> stats = get_evolved_stats(my_model, "speed") + >>> print(stats['mean']) + 1.25 + """ arr = model.cell_params.get(param) if arr is None: return {"mean": np.nan, "std": np.nan, "min": np.nan, "max": np.nan, "n": 0} @@ -108,7 +180,34 @@ def get_evolved_stats(model, param: str) -> Dict: def average_pcfs( pcf_list: List[Tuple[np.ndarray, np.ndarray, int]] ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - """Average multiple PCF measurements with standard error.""" + """ + Average multiple Pair Correlation Function (PCF) measurements and calculate standard error. + + Parameters + ---------- + pcf_list : list of tuple + A list where each element is a tuple containing: + - distances (np.ndarray): The radial distances (r). + - pcf_values (np.ndarray): The correlation values g(r). + - count (int): Metadata or weight (not used in current calculation). + + Returns + ------- + distances : np.ndarray + The radial distances from the first entry in the list. + pcf_mean : np.ndarray + The element-wise mean of the PCF values across all measurements. + pcf_se : np.ndarray + The standard error of the mean for the PCF values. + + Examples + -------- + >>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10), + ... (np.array([0, 1]), np.array([1.2, 1.8]), 12)] + >>> dist, mean, se = average_pcfs(data) + >>> mean + array([1.1, 1.9]) + """ if len(pcf_list) == 0: return np.array([]), np.array([]), np.array([]) @@ -122,14 +221,72 @@ def average_pcfs( def save_results_jsonl(results: List[Dict], output_path: Path): - """Save results incrementally to JSONL format.""" + """ + Save a list of dictionaries to a file in JSON Lines (JSONL) format. + + Each dictionary in the list is serialized into a single JSON string and + written as a new line. Non-serializable objects are converted to strings + using the default string representation. + + Parameters + ---------- + results : list of dict + The collection of result dictionaries to be saved. + output_path : Path + The file system path (pathlib.Path) where the JSONL file will be created. + + Returns + ------- + None + + Notes + ----- + The file is opened in 'w' (write) mode, which will overwrite any existing + content at the specified path. + + Examples + -------- + >>> data = [{"id": 1, "score": 0.95}, {"id": 2, "score": 0.88}] + >>> save_results_jsonl(data, Path("results.jsonl")) + """ with open(output_path, "w", encoding="utf-8") as f: for result in results: f.write(json.dumps(result, default=str) + "\n") def save_results_npz(results: List[Dict], output_path: Path): - """Save results to compressed NPZ format.""" + """ + Save simulation results to a compressed NumPy (.npz) binary file. + + This function flattens a list of result dictionaries into a single + dictionary of NumPy arrays, prefixing keys with the run index to + maintain data separation. The resulting file is compressed to + reduce storage space. + + Parameters + ---------- + results : list of dict + A list where each dictionary contains key-value pairs of + simulation data (e.g., arrays, lists, or scalars). + output_path : Path + The file system path (pathlib.Path) where the compressed + NPZ file will be saved. + + Returns + ------- + None + + Notes + ----- + The keys in the saved file follow the format 'run_{index}_{original_key}'. + Values are automatically converted to NumPy arrays if they are not + already. + + Examples + -------- + >>> results = [{"energy": [1, 2]}, {"energy": [3, 4]}] + >>> save_results_npz(results, Path("output.npz")) + """ data = {} for i, res in enumerate(results): for key, val in res.items(): @@ -138,7 +295,35 @@ def save_results_npz(results: List[Dict], output_path: Path): def load_results_jsonl(input_path: Path) -> List[Dict]: - """Load results from JSONL format.""" + """ + Load simulation results from a JSON Lines (JSONL) formatted file. + + This function reads a file line-by-line, parsing each line as an + independent JSON object and aggregating them into a list of dictionaries. + + Parameters + ---------- + input_path : Path + The file system path (pathlib.Path) to the JSONL file. + + Returns + ------- + results : list of dict + A list of dictionaries reconstructed from the file content. + + Raises + ------ + FileNotFoundError + If the specified input path does not exist. + json.JSONDecodeError + If a line in the file is not valid JSON. + + Examples + -------- + >>> data = load_results_jsonl(Path("results.jsonl")) + >>> len(data) + 2 + """ results = [] with open(input_path, "r", encoding="utf-8") as f: for line in f: @@ -163,9 +348,52 @@ def run_single_simulation( compute_pcf: Optional[bool] = None, ) -> Dict: """ - Run a single PP simulation and collect metrics. - - Returns dict with population, cluster, PCF, and evolution metrics. + Run a single Predator-Prey (PP) simulation and collect comprehensive metrics. + + This function initializes a Cellular Automata model, executes a warmup phase + to reach steady state, and then performs a measurement phase to track + population dynamics, spatial clustering, and evolutionary changes. + + Parameters + ---------- + prey_birth : float + The probability or rate of prey reproduction. + prey_death : float + The base probability or rate of prey mortality. + predator_birth : float + The probability or rate of predator reproduction upon consuming prey. + predator_death : float + The probability or rate of predator mortality. + grid_size : int + The side length of the square simulation grid. + seed : int + Random seed for ensuring reproducibility of the simulation run. + cfg : Config + A configuration object containing simulation hyperparameters (densities, + sampling rates, timing, etc.). + with_evolution : bool, optional + If True, enables the evolution of the 'prey_death' parameter within + the model (default is False). + compute_pcf : bool, optional + Explicit toggle for Pair Correlation Function calculation. If None, + it is determined by `cfg.pcf_sample_rate` (default is None). + + Returns + ------- + result : dict + A dictionary containing simulation results including: + - Input parameters and survival flags. + - Population mean and standard deviation for both species. + - Cluster statistics (number of clusters, sizes, largest fractions). + - Evolutionary statistics (mean, std, min, max, and final values). + - PCF data and spatial indices (segregation and clustering). + - Optional time series for populations and evolved parameters. + + Notes + ----- + The function relies on several external utilities: `count_populations`, + `get_evolved_stats`, `get_cluster_stats_fast`, `compute_all_pcfs_fast`, + and `average_pcfs`. """ from models.CA import PP @@ -334,28 +562,6 @@ def run_single_simulation( result["pcf_prey_pred"] = pcf_cr.tolist() # Short-range indices - """ - NOTE: The Pair Correlation function measures spatial correlation at distance r. - g(r) = 1: random (poisson distribution) - g(r) > 1: clustering (more pairs than random) - g(r) < 1: segregation (fewer pairs than random) - - prey_clustering_index: Do prey clump together? - pred_clustering_index: Do predators clump together? - segregation_index: Are prey and predators segregated? - - For the Hydra effect model: - segregation_index < 1: Prey and predators are spatially separated - prey_clustering_index > 1: Prey form clusters - pred_clustering_index > 1: Predators form clusters - - High segregation (low segregation index): prey can reproduce in predator-free zones - High prey clustering: prey form groups that can survive predation - At criticality: expect sepcific balance where clusters are large enough to sustain but - fragmented enough to avoid total predation. - - If segregation_index = 1 approx, no Hydra effect -> follow mean field dynamics. - """ short_mask = dist < 3.0 if np.any(short_mask): result["segregation_index"] = float(np.mean(pcf_cr[short_mask])) @@ -372,11 +578,38 @@ def run_single_simulation( def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]: """ - Phase 1: Parameter sweep to find critical point. - - - 2D sweep of prey_birth prey_death - - Both with and without evolution - - Outputs: bifurcation data, cluster distributions + Execute Phase 1 of the simulation: a parameter sweep to identify critical points. + + This function performs a 1D sweep across varying prey mortality rates while + keeping other parameters fixed. It utilizes parallel execution via joblib + and saves results incrementally to a JSONL file to ensure data integrity + during long-running batches. + + Parameters + ---------- + cfg : Config + Configuration object containing simulation hyperparameters, sweep + ranges, and execution settings (n_jobs, grid_size, etc.). + output_dir : Path + Directory where result files (JSONL) and metadata (JSON) will be stored. + logger : logging.Logger + Logger instance for tracking simulation progress and recording + operational metadata. + + Returns + ------- + all_results : list of dict + A list of dictionaries containing the metrics collected from every + individual simulation run in the sweep. + + Notes + ----- + The function performs the following steps: + 1. Pre-warms Numba kernels for performance. + 2. Generates a deterministic set of simulation jobs using unique seeds. + 3. Executes simulations in parallel using a generator for memory efficiency. + 4. Records metadata including a timestamp and a serialized snapshot of + the configuration. """ from joblib import Parallel, delayed @@ -439,14 +672,35 @@ def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di def run_phase2(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]: """ - Phase 2: Self-organization analysis. - - SOC Hypothesis: Prey evolve toward critical critical point regardless of initial conditions. - - NOTE: Test is currently start evo from different intial prey_death values (?) - If SOC holds, then all runs converge to the same final prey_death near critical point. - - FIXME: This run script needs to be adjusted + Execute Phase 2 of the simulation: self-organization and criticality analysis. + + This phase tests the Self-Organized Criticality (SOC) hypothesis by + initializing simulations at different points in the parameter space and + observing whether evolutionary pressure drives the system toward a + common critical point, regardless of initial prey mortality rates. + + Parameters + ---------- + cfg : Config + Configuration object containing simulation hyperparameters, evolution + settings, and execution constraints. + output_dir : Path + Directory where result files (JSONL) and metadata (JSON) will be stored. + logger : logging.Logger + Logger instance for tracking progress and evolutionary convergence. + + Returns + ------- + all_results : list of dict + A list of dictionaries containing metrics from the evolutionary + simulation runs. + + Notes + ----- + The function captures: + 1. Convergence of 'prey_death' across multiple replicates. + 2. Final steady-state population distributions. + 3. Incremental saving of results to prevent data loss. """ from joblib import Parallel, delayed @@ -568,12 +822,37 @@ def run_phase3(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]: """ - Phase 4: Global Sensitivity Analysis. - Vary: prey_birth, prey_death, predator_birth, predator_death - - prey_death: 10 values from 0.05 to 0.95 - - prey_birth, predator_birth, predator_death: 11 values each from 0 to 1 - Reps: 10 - Grid size: 250 + Execute Phase 3 of the simulation: Finite-Size Scaling (FSS) analysis. + + This phase investigates how spatial structures, specifically cluster size + cutoffs, scale with the system size (L) at the critical point identified + in Phase 1. This is essential for determining the universality class of + the phase transition. + + Parameters + ---------- + cfg : Config + Configuration object containing critical point parameters, the list of + grid sizes to test, and execution settings. + output_dir : Path + Directory where result files (JSONL) and FSS metadata (JSON) will be + stored. + logger : logging.Logger + Logger instance for tracking progress across different grid sizes. + + Returns + ------- + all_results : list of dict + A list of dictionaries containing metrics and cluster statistics for + each grid size and replicate. + + Notes + ----- + The function performs the following: + 1. Iterates through multiple grid sizes defined in `cfg.grid_sizes`. + 2. Generates parallel jobs for each size using critical birth/death rates. + 3. Saves results incrementally to allow for post-simulation analysis of + power-law exponents. """ from joblib import Parallel, delayed import itertools @@ -732,17 +1011,38 @@ def run_phase5(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]: """ - Phase 6: Model Extensions - Directed Hunting Comparison. - Same 4D sweep as Phase 4, but with directed_hunting=True. - Vary: prey_birth, prey_death, predator_birth, predator_death - - prey_death: 10 values from 0.05 to 0.95 - - prey_birth, predator_birth, predator_death: 11 values each from 0 to 1 - Reps: 10 - Grid size: 250 - Compare results with Phase 4 to assess impact of directed hunting on: - - Critical point location - - Hydra effect persistence - - SOC signatures + Execute Phase 6 of the simulation: Global 4D parameter sweep with directed hunting. + + This phase performs a comprehensive sensitivity analysis by varying four key + parameters (prey birth/death and predator birth/death) while directed + hunting is enabled. The results allow for a direct comparison with Phase 4 + to determine how predator search behavior shifts the system's critical + thresholds and stability. + + Parameters + ---------- + cfg : Config + Configuration object containing simulation hyperparameters, parallel + execution settings, and the fixed grid size for this phase. + output_dir : Path + Directory where the result JSONL file and execution metadata will + be stored. + logger : logging.Logger + Logger instance for tracking the progress of the high-volume + simulation batch. + + Returns + ------- + all_results : list of dict + A list of dictionaries containing metrics for every simulation in + the 4D parameter grid. + + Notes + ----- + The function utilizes a Cartesian product of parameter ranges to build a + job list of over 13,000 unique parameter sets (multiplied by replicates). + Seeds are uniquely generated to distinguish these runs from other phases + even if parameter values overlap. """ from joblib import Parallel, delayed import itertools @@ -854,6 +1154,20 @@ def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di def main(): + """ + Orchestrate the predator-prey experimental suite across multiple phases. + + This entry point handles command-line arguments, sets up logging and output + directories, and executes the requested simulation phases (1-6). It + supports parallel execution, dry runs for runtime estimation, and + automated configuration persistence. + + Notes + ----- + The script dynamically retrieves phase-specific configurations using + `get_phase_config` and dispatches execution to the corresponding runner + in the `PHASE_RUNNERS` mapping. + """ parser = argparse.ArgumentParser( description="Predator-Prey Hydra Effect Experiments", formatter_class=argparse.RawDescriptionHelpFormatter, @@ -863,8 +1177,7 @@ def main(): 2 Self-organization (evolution toward criticality) 3 Finite-size scaling at critical point 4 Sensitivity analysis across parameter regimes - 5 Perturbation analysis (critical slowing down) - 6 Model extensions (directed hunting comparison) + 5 Model extensions (directed hunting comparison) """, ) parser.add_argument( diff --git a/tests/smoke_test.py b/tests/smoke_test.py deleted file mode 100644 index 8ec5f9b..0000000 --- a/tests/smoke_test.py +++ /dev/null @@ -1,698 +0,0 @@ -#!/usr/bin/env python3 -""" -Smoke Test for Predator-Prey Simulation Pipeline - -Run this before HPC submission to verify everything works correctly. - -Usage: - python smoke_test.py # Run all tests - python smoke_test.py --quick # Run minimal tests only - python smoke_test.py --verbose # Extra output - -Tests: - 1. Module imports - 2. Numba kernel (random movement) - 3. Numba kernel (directed hunting) - 4. Full simulation (random, no evolution) - 5. Full simulation (random, with evolution) - 6. Full simulation (directed, no evolution) - 7. Full simulation (directed, with evolution) - 8. PCF computation - 9. Cluster measurement - 10. Reproducibility (seeding) - 11. Binary save/load roundtrip -""" - -import sys -import time -import argparse -import tempfile -from pathlib import Path - -# Setup path -project_root = str(Path(__file__).resolve().parents[1]) -if project_root not in sys.path: - sys.path.insert(0, project_root) - -import numpy as np - -# Track results -RESULTS = [] -VERBOSE = False - - -def log(msg: str, level: str = "INFO"): - """Print formatted log message.""" - symbols = {"INFO": "ℹ", "PASS": "✓", "FAIL": "✗", "WARN": "⚠", "RUN": "→"} - print(f" {symbols.get(level, '•')} {msg}") - - -def run_test(name: str, func, *args, **kwargs): - """Run a test function and track results.""" - print(f"\n{'='*60}") - print(f"TEST: {name}") - print("=" * 60) - - start = time.perf_counter() - try: - result = func(*args, **kwargs) - elapsed = time.perf_counter() - start - - if result: - log(f"PASSED in {elapsed:.2f}s", "PASS") - RESULTS.append((name, True, elapsed, None)) - return True - else: - log(f"FAILED in {elapsed:.2f}s", "FAIL") - RESULTS.append((name, False, elapsed, "Test returned False")) - return False - except Exception as e: - elapsed = time.perf_counter() - start - log(f"FAILED with exception: {e}", "FAIL") - RESULTS.append((name, False, elapsed, str(e))) - if VERBOSE: - import traceback - - traceback.print_exc() - return False - - -def test_imports(): - """Test that all required modules import correctly.""" - log("Importing numba_optimized...", "RUN") - from models.numba_optimized import ( - PPKernel, - compute_all_pcfs_fast, - measure_cluster_sizes_fast, - set_numba_seed, - NUMBA_AVAILABLE, - ) - - log(f"NUMBA_AVAILABLE = {NUMBA_AVAILABLE}") - - if not NUMBA_AVAILABLE: - log("Numba not available - performance will be degraded", "WARN") - - log("Importing CA module...", "RUN") - from models.CA import PP, set_numba_seed as ca_seed - - log("Importing pp_analysis...", "RUN") - from scripts.experiments import ( - Config, - run_single_simulation, - count_populations, - ) - - log("All imports successful") - return True - - -def test_numba_kernel_random(): - """Test Numba kernel with random movement.""" - from models.numba_optimized import PPKernel, set_numba_seed - - log("Creating kernel (directed_hunting=False)...", "RUN") - kernel = PPKernel(50, 50, "moore", directed_hunting=False) - assert kernel.directed_hunting == False - - log("Setting up test grid...", "RUN") - np.random.seed(42) - set_numba_seed(42) - grid = np.random.choice([0, 1, 2], (50, 50), p=[0.55, 0.30, 0.15]).astype(np.int32) - prey_death = np.full((50, 50), 0.05, dtype=np.float64) - prey_death[grid != 1] = np.nan - - initial_prey = np.sum(grid == 1) - initial_pred = np.sum(grid == 2) - log(f"Initial: prey={initial_prey}, pred={initial_pred}") - - log("Running 100 update steps...", "RUN") - for _ in range(100): - kernel.update(grid, prey_death, 0.2, 0.05, 0.2, 0.1, evolution_stopped=False) - - final_prey = np.sum(grid == 1) - final_pred = np.sum(grid == 2) - log(f"Final: prey={final_prey}, pred={final_pred}") - - # Verify grid is valid - assert grid.min() >= 0, "Grid has negative values" - assert grid.max() <= 2, "Grid has values > 2" - assert not np.any(np.isnan(grid)), "Grid has NaN values" - - # Verify prey_death consistency - prey_mask = grid == 1 - if np.any(prey_mask): - assert np.all( - ~np.isnan(prey_death[prey_mask]) - ), "Prey cells missing death rates" - assert np.all(np.isnan(prey_death[~prey_mask])), "Non-prey cells have death rates" - - log("Grid and prey_death arrays are consistent") - return True - - -def test_numba_kernel_directed(): - """Test Numba kernel with directed hunting.""" - from models.numba_optimized import PPKernel, set_numba_seed - - log("Creating kernel (directed_hunting=True)...", "RUN") - kernel = PPKernel(50, 50, "moore", directed_hunting=True) - assert kernel.directed_hunting == True - - log("Setting up test grid...", "RUN") - np.random.seed(42) - set_numba_seed(42) - grid = np.random.choice([0, 1, 2], (50, 50), p=[0.55, 0.30, 0.15]).astype(np.int32) - prey_death = np.full((50, 50), 0.05, dtype=np.float64) - prey_death[grid != 1] = np.nan - - initial_prey = np.sum(grid == 1) - initial_pred = np.sum(grid == 2) - log(f"Initial: prey={initial_prey}, pred={initial_pred}") - - log("Running 100 update steps...", "RUN") - for _ in range(100): - kernel.update(grid, prey_death, 0.2, 0.05, 0.2, 0.1, evolution_stopped=False) - - final_prey = np.sum(grid == 1) - final_pred = np.sum(grid == 2) - log(f"Final: prey={final_prey}, pred={final_pred}") - - # Verify grid is valid - assert grid.min() >= 0, "Grid has negative values" - assert grid.max() <= 2, "Grid has values > 2" - - log("Directed hunting kernel working correctly") - return True - - -def test_ca_model_random(): - """Test CA PP model with random movement.""" - from models.CA import PP - from models.numba_optimized import set_numba_seed - - log("Creating PP model (directed_hunting=False)...", "RUN") - np.random.seed(42) - set_numba_seed(42) - - model = PP( - rows=50, - cols=50, - densities=(0.30, 0.15), - neighborhood="moore", - params={ - "prey_birth": 0.2, - "prey_death": 0.05, - "predator_birth": 0.2, - "predator_death": 0.1, - }, - seed=42, - synchronous=False, - directed_hunting=False, - ) - - assert model.directed_hunting == False - - initial_prey = np.sum(model.grid == 1) - initial_pred = np.sum(model.grid == 2) - log(f"Initial: prey={initial_prey}, pred={initial_pred}") - - log("Running 100 steps...", "RUN") - model.run(100) - - final_prey = np.sum(model.grid == 1) - final_pred = np.sum(model.grid == 2) - log(f"Final: prey={final_prey}, pred={final_pred}") - - assert model.grid.min() >= 0 - assert model.grid.max() <= 2 - - return True - - -def test_ca_model_directed(): - """Test CA PP model with directed hunting.""" - from models.CA import PP - from models.numba_optimized import set_numba_seed - - log("Creating PP model (directed_hunting=True)...", "RUN") - np.random.seed(42) - set_numba_seed(42) - - model = PP( - rows=50, - cols=50, - densities=(0.30, 0.15), - neighborhood="moore", - params={ - "prey_birth": 0.2, - "prey_death": 0.05, - "predator_birth": 0.2, - "predator_death": 0.1, - }, - seed=42, - synchronous=False, - directed_hunting=True, - ) - - assert model.directed_hunting == True - - initial_prey = np.sum(model.grid == 1) - initial_pred = np.sum(model.grid == 2) - log(f"Initial: prey={initial_prey}, pred={initial_pred}") - - log("Running 100 steps...", "RUN") - model.run(100) - - final_prey = np.sum(model.grid == 1) - final_pred = np.sum(model.grid == 2) - log(f"Final: prey={final_prey}, pred={final_pred}") - - assert model.grid.min() >= 0 - assert model.grid.max() <= 2 - - return True - - -def test_ca_model_with_evolution(): - """Test CA PP model with evolution enabled.""" - from models.CA import PP - from models.numba_optimized import set_numba_seed - - log("Creating PP model with evolution...", "RUN") - np.random.seed(42) - set_numba_seed(42) - - model = PP( - rows=50, - cols=50, - densities=(0.30, 0.15), - neighborhood="moore", - params={ - "prey_birth": 0.2, - "prey_death": 0.05, - "predator_birth": 0.2, - "predator_death": 0.1, - }, - seed=42, - synchronous=False, - directed_hunting=True, - ) - - log("Enabling prey_death evolution...", "RUN") - model.evolve("prey_death", sd=0.05, min_val=0.01, max_val=0.15) - - initial_mean = np.nanmean(model.cell_params["prey_death"]) - log(f"Initial prey_death mean: {initial_mean:.4f}") - - log("Running 200 steps...", "RUN") - model.run(200) - - final_values = model.cell_params["prey_death"] - valid_values = final_values[~np.isnan(final_values)] - - if len(valid_values) > 0: - final_mean = np.mean(valid_values) - final_std = np.std(valid_values) - log(f"Final prey_death: mean={final_mean:.4f}, std={final_std:.4f}") - - # Check bounds - assert valid_values.min() >= 0.01 - 1e-9, "Values below minimum" - assert valid_values.max() <= 0.15 + 1e-9, "Values above maximum" - log("Evolution bounds respected") - else: - log("No prey survived - cannot check evolution", "WARN") - - return True - - -def test_full_simulation_pipeline(): - """Test the full simulation pipeline via run_single_simulation.""" - from scripts.experiments import Config, run_single_simulation - from models.numba_optimized import set_numba_seed - - log("Creating fast config...", "RUN") - cfg = Config() - cfg.default_grid = 40 - cfg.warmup_steps = 50 - cfg.measurement_steps = 100 - cfg.cluster_samples = 1 - cfg.collect_pcf = True - cfg.pcf_sample_rate = 1.0 # Always compute PCF for this test - - # Test random movement - log("Running simulation (random movement, no evolution)...", "RUN") - cfg.directed_hunting = False - np.random.seed(42) - set_numba_seed(42) - - result_random = run_single_simulation( - prey_birth=0.2, - prey_death=0.05, - grid_size=40, - seed=42, - with_evolution=False, - cfg=cfg, - compute_pcf=True, - ) - - assert "prey_mean" in result_random - assert "pred_mean" in result_random - log( - f"Random: prey_mean={result_random['prey_mean']:.1f}, pred_mean={result_random['pred_mean']:.1f}" - ) - - # Test directed hunting - log("Running simulation (directed hunting, no evolution)...", "RUN") - cfg.directed_hunting = True - np.random.seed(42) - set_numba_seed(42) - - result_directed = run_single_simulation( - prey_birth=0.2, - prey_death=0.05, - grid_size=40, - seed=42, - with_evolution=False, - cfg=cfg, - compute_pcf=True, - ) - - assert "prey_mean" in result_directed - log( - f"Directed: prey_mean={result_directed['prey_mean']:.1f}, pred_mean={result_directed['pred_mean']:.1f}" - ) - - # Test with evolution - log("Running simulation (directed hunting, with evolution)...", "RUN") - np.random.seed(42) - set_numba_seed(42) - - result_evo = run_single_simulation( - prey_birth=0.2, - prey_death=0.05, - grid_size=40, - seed=42, - with_evolution=True, - cfg=cfg, - compute_pcf=True, - ) - - assert result_evo["with_evolution"] == True - log(f"Evolution: prey_mean={result_evo['prey_mean']:.1f}") - - return True - - -def test_pcf_computation(): - """Test PCF computation.""" - from models.numba_optimized import compute_all_pcfs_fast, set_numba_seed - - log("Creating test grid...", "RUN") - np.random.seed(42) - set_numba_seed(42) - grid = np.random.choice([0, 1, 2], (100, 100), p=[0.55, 0.30, 0.15]).astype( - np.int32 - ) - - n_prey = np.sum(grid == 1) - n_pred = np.sum(grid == 2) - log(f"Grid: prey={n_prey}, pred={n_pred}") - - log("Computing PCFs...", "RUN") - t0 = time.perf_counter() - pcfs = compute_all_pcfs_fast(grid, max_distance=20.0, n_bins=20) - elapsed = time.perf_counter() - t0 - log(f"PCF computation took {elapsed*1000:.1f}ms") - - # Check all three PCFs - for key in ["prey_prey", "pred_pred", "prey_pred"]: - assert key in pcfs, f"Missing PCF: {key}" - dist, pcf, n_pairs = pcfs[key] - - assert len(dist) == 20, f"{key}: wrong number of bins" - assert len(pcf) == 20, f"{key}: wrong PCF length" - assert not np.any(np.isnan(pcf)), f"{key}: PCF contains NaN" - - log(f"{key}: n_pairs={n_pairs}, mean_pcf={np.mean(pcf):.3f}") - - return True - - -def test_cluster_measurement(): - """Test cluster size measurement.""" - from models.numba_optimized import measure_cluster_sizes_fast - - log("Creating grid with known clusters...", "RUN") - grid = np.zeros((30, 30), dtype=np.int32) - - # Cluster 1: 3x3 = 9 cells - grid[2:5, 2:5] = 1 - # Cluster 2: 2x4 = 8 cells - grid[10:12, 10:14] = 1 - # Cluster 3: single cell - grid[20, 20] = 1 - # Cluster 4: L-shape = 5 cells - grid[25, 25:28] = 1 - grid[26:28, 25] = 1 - - expected_sizes = sorted([9, 8, 1, 5], reverse=True) - log(f"Expected cluster sizes: {expected_sizes}") - - log("Measuring clusters...", "RUN") - sizes = measure_cluster_sizes_fast(grid, 1) - actual_sizes = sorted(sizes, reverse=True) - log(f"Actual cluster sizes: {list(actual_sizes)}") - - assert len(sizes) == 4, f"Expected 4 clusters, found {len(sizes)}" - assert list(actual_sizes) == expected_sizes, "Cluster sizes don't match" - - # Verify total cells - assert sum(sizes) == np.sum(grid == 1), "Cluster total doesn't match grid total" - - log("Cluster measurement correct") - return True - - -def test_reproducibility(): - """Test that seeding produces reproducible results.""" - from models.numba_optimized import PPKernel, set_numba_seed - - log("Running simulation twice with same seed...", "RUN") - - def run_sim(seed): - np.random.seed(seed) - set_numba_seed(seed) - grid = np.random.choice([0, 1, 2], (30, 30), p=[0.55, 0.30, 0.15]).astype( - np.int32 - ) - prey_death = np.full((30, 30), 0.05, dtype=np.float64) - prey_death[grid != 1] = np.nan - - kernel = PPKernel(30, 30, "moore", directed_hunting=True) - for _ in range(50): - kernel.update( - grid, prey_death, 0.2, 0.05, 0.2, 0.1, evolution_stopped=False - ) - - return grid.copy(), prey_death.copy() - - grid1, pd1 = run_sim(12345) - grid2, pd2 = run_sim(12345) - - prey1, prey2 = np.sum(grid1 == 1), np.sum(grid2 == 1) - pred1, pred2 = np.sum(grid1 == 2), np.sum(grid2 == 2) - - log(f"Run 1: prey={prey1}, pred={pred1}") - log(f"Run 2: prey={prey2}, pred={pred2}") - - if np.array_equal(grid1, grid2): - log("Grids are IDENTICAL - perfect reproducibility", "PASS") - else: - diff_count = np.sum(grid1 != grid2) - log(f"Grids differ in {diff_count} cells - may indicate seeding issue", "WARN") - # Still pass if populations match (some internal ordering may differ) - if prey1 == prey2 and pred1 == pred2: - log("Populations match - acceptable", "PASS") - else: - return False - - return True - - -def test_binary_save_load(): - """Test binary save/load roundtrip.""" - from scripts.experiments import save_sweep_binary, load_sweep_binary - - log("Creating test results...", "RUN") - results = [ - { - "prey_birth": 0.2, - "prey_death": 0.05, - "prey_mean": 150.5, - "pred_mean": 75.2, - "seed": 42, - "grid_size": 50, - "with_evolution": False, - }, - { - "prey_birth": 0.3, - "prey_death": 0.08, - "prey_mean": 120.3, - "pred_mean": 90.1, - "seed": 43, - "grid_size": 50, - "with_evolution": True, - }, - ] - - with tempfile.TemporaryDirectory() as tmpdir: - filepath = Path(tmpdir) / "test_results.npz" - - log(f"Saving to {filepath}...", "RUN") - save_sweep_binary(results, filepath) - - assert filepath.exists(), "File not created" - log(f"File size: {filepath.stat().st_size} bytes") - - log("Loading back...", "RUN") - loaded = load_sweep_binary(filepath) - - assert len(loaded) == len(results), "Wrong number of results loaded" - - for i, (orig, load) in enumerate(zip(results, loaded)): - for key in orig: - if isinstance(orig[key], float): - assert np.isclose( - orig[key], load[key] - ), f"Result {i}, key {key} mismatch" - else: - assert orig[key] == load[key], f"Result {i}, key {key} mismatch" - - log("Roundtrip successful") - - return True - - -def test_hunting_dynamics_comparison(): - """Compare dynamics between random and directed hunting.""" - from models.numba_optimized import PPKernel, set_numba_seed - - log("Setting up comparison...", "RUN") - - # Use same initial grid - np.random.seed(999) - template = np.random.choice([0, 1, 2], (60, 60), p=[0.50, 0.35, 0.15]).astype( - np.int32 - ) - - def run_mode(directed: bool, seed: int = 999): - grid = template.copy() - prey_death = np.full((60, 60), 0.05, dtype=np.float64) - prey_death[grid != 1] = np.nan - - set_numba_seed(seed) - kernel = PPKernel(60, 60, "moore", directed_hunting=directed) - - history = [] - for step in range(100): - kernel.update(grid, prey_death, 0.2, 0.05, 0.5, 0.1) # High pred birth - if step % 10 == 0: - history.append((np.sum(grid == 1), np.sum(grid == 2))) - - return history - - log("Running random movement...", "RUN") - hist_random = run_mode(directed=False) - - log("Running directed hunting...", "RUN") - hist_directed = run_mode(directed=True) - - log("\nPopulation dynamics comparison:") - log(f"{'Step':<6} {'Random':<20} {'Directed':<20}") - log("-" * 46) - for i, ((pr, pdr), (pd, pdd)) in enumerate(zip(hist_random, hist_directed)): - step = i * 10 - log(f"{step:<6} prey={pr:<4} pred={pdr:<4} prey={pd:<4} pred={pdd:<4}") - - # Final comparison - final_random_prey = hist_random[-1][0] - final_directed_prey = hist_directed[-1][0] - - log(f"\nFinal prey - Random: {final_random_prey}, Directed: {final_directed_prey}") - - # Directed hunting with high predator birth typically depletes prey faster - # But we don't assert this strictly due to stochastic nature - log("Dynamics comparison complete") - - return True - - -def print_summary(): - """Print test summary.""" - print("\n" + "=" * 60) - print("SMOKE TEST SUMMARY") - print("=" * 60) - - passed = sum(1 for _, success, _, _ in RESULTS if success) - failed = sum(1 for _, success, _, _ in RESULTS if not success) - total_time = sum(t for _, _, t, _ in RESULTS) - - for name, success, elapsed, error in RESULTS: - status = "PASS" if success else "FAIL" - print(f" {status} {name} ({elapsed:.2f}s)") - if error and not success: - print(f" Error: {error[:60]}...") - - print("-" * 60) - print(f" Total: {passed} passed, {failed} failed in {total_time:.2f}s") - print("=" * 60) - - if failed == 0: - print("\ALL TESTS PASSED - Ready for HPC submission!\n") - else: - print(f"\n⚠️ {failed} TEST(S) FAILED - Please fix before HPC submission.\n") - - return failed == 0 - - -def main(): - global VERBOSE - - parser = argparse.ArgumentParser(description="Pre-HPC Smoke Test") - parser.add_argument("--quick", action="store_true", help="Run minimal tests only") - parser.add_argument("--verbose", action="store_true", help="Extra output") - args = parser.parse_args() - - VERBOSE = args.verbose - - print("\n" + "=" * 60) - print(" PREDATOR-PREY SIMULATION - PRE-HPC SMOKE TEST") - print("=" * 60) - print(f" Time: {time.strftime('%Y-%m-%d %H:%M:%S')}") - print(f" Python: {sys.version.split()[0]}") - print("=" * 60) - - # Core tests (always run) - run_test("Module Imports", test_imports) - run_test("Numba Kernel (Random)", test_numba_kernel_random) - run_test("Numba Kernel (Directed)", test_numba_kernel_directed) - run_test("CA Model (Random)", test_ca_model_random) - run_test("CA Model (Directed)", test_ca_model_directed) - - if not args.quick: - # Extended tests - run_test("CA Model (Evolution)", test_ca_model_with_evolution) - run_test("Full Simulation Pipeline", test_full_simulation_pipeline) - run_test("PCF Computation", test_pcf_computation) - run_test("Cluster Measurement", test_cluster_measurement) - run_test("Reproducibility (Seeding)", test_reproducibility) - run_test("Binary Save/Load", test_binary_save_load) - run_test("Hunting Dynamics Comparison", test_hunting_dynamics_comparison) - - success = print_summary() - sys.exit(0 if success else 1) - - -if __name__ == "__main__": - main() From e74fbc89e15a11812905e7d1a251518d77788050 Mon Sep 17 00:00:00 2001 From: Kimon Date: Fri, 30 Jan 2026 11:28:10 +0100 Subject: [PATCH 2/7] fixing config --- models/config.py | 46 +++++++++---------------- scripts/experiments.py | 77 +++--------------------------------------- 2 files changed, 21 insertions(+), 102 deletions(-) diff --git a/models/config.py b/models/config.py index 7b2e0cd..ce4c3c6 100644 --- a/models/config.py +++ b/models/config.py @@ -21,6 +21,8 @@ from typing import Tuple, Optional import numpy as np +#FIXME: Tidy up config file for submission + @dataclass class Config: @@ -106,11 +108,11 @@ class Config: """ # Grid settings - grid_size: int = 1000 # FIXME: Decide default configuration + grid_size: int = 1000 densities: Tuple[float, float] = ( 0.30, 0.15, - ) # (prey, predator) #FIXME: Default densities + ) # (prey, predator) # For FSS experiments: multiple grid sizes grid_sizes: Tuple[int, ...] = (50, 100, 250, 500, 1000, 2500) @@ -118,8 +120,8 @@ class Config: # Default/fixed parameters prey_birth: float = 0.2 prey_death: float = 0.05 - predator_birth: float = 0.8 # FIXME: Default predator death rate - predator_death: float = 0.05 # FIXME: Default predator death rate + predator_birth: float = 0.8 + predator_death: float = 0.05 # Critical point (UPDATE AFTER PHASE 1) critical_prey_birth: float = 0.20 @@ -127,7 +129,7 @@ class Config: # Prey parameter sweep (Phase 1) prey_death_range: Tuple[float, float] = (0.0, 0.2) - n_prey_birth: int = 15 # FIXME: Decide number of grid points along prey axes + n_prey_birth: int = 15 n_prey_death: int = 5 # Predator parameter sweep (Phase 4 sensitivity) @@ -136,13 +138,13 @@ class Config: 0.20, 0.25, 0.30, - ) # FIXME: Bogus values for now + ) predator_death_values: Tuple[float, ...] = ( 0.05, 0.10, 0.15, 0.20, - ) # FIXME: Bogus values for now + ) # Perturbation offsets from critical point (Phase 5) prey_death_offsets: Tuple[float, ...] = ( @@ -151,14 +153,14 @@ class Config: 0.0, 0.01, 0.02, - ) # FIXME: Bogus values for now + ) # Number of replicates per parameter configuration - n_replicates: int = 15 # FIXME: Decide number of indep. runs per parameter config + n_replicates: int = 15 # Simulation steps - warmup_steps: int = 300 # FIXME: Steps to run before measuring - measurement_steps: int = 500 # FIXME: Decide measurement steps + warmup_steps: int = 300 + measurement_steps: int = 500 # Evo with_evolution: bool = False @@ -173,7 +175,7 @@ class Config: 0.10, 0.15, 0.20, - ) # FIXME: Don't know if we use yet + ) # Update mode synchronous: bool = False # Always False for this model @@ -184,7 +186,7 @@ class Config: # Temporal data collection (time series) save_timeseries: bool = False - timeseries_subsample: int = 10 # FIXME: Save every how many steps + timeseries_subsample: int = 10 # PCF settings collect_pcf: bool = True @@ -319,7 +321,6 @@ def estimate_runtime(self, n_cores: int = 32) -> str: # Experimental Phase Configurations ############################################################################################ -# FIXME: These configs are arbitraty and should be finalized before running experiments. PHASE1_CONFIG = Config( grid_size=1000, @@ -381,22 +382,8 @@ def estimate_runtime(self, n_cores: int = 32) -> str: directed_hunting=False, ) - -# Phase 5: Perturbation analysis (critical slowing down) -PHASE5_CONFIG = Config( - grid_size=100, - prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), # FIXME: Is this what we vary? - n_replicates=20, - warmup_steps=500, - measurement_steps=2000, - perturbation_magnitude=0.1, - collect_pcf=False, - save_timeseries=True, - timeseries_subsample=1, # Full resolution for autocorrelation -) - # Phase 6: Model extensions (directed reproduction); same config as phase 4 but with directed reproduction -PHASE6_CONFIG = Config( +PHASE5_CONFIG = Config( grid_size=250, n_replicates=10, warmup_steps=500, @@ -414,7 +401,6 @@ def estimate_runtime(self, n_cores: int = 32) -> str: 3: PHASE3_CONFIG, 4: PHASE4_CONFIG, 5: PHASE5_CONFIG, - 6: PHASE6_CONFIG, } diff --git a/scripts/experiments.py b/scripts/experiments.py index a7be8d6..1aa8916 100644 --- a/scripts/experiments.py +++ b/scripts/experiments.py @@ -945,73 +945,7 @@ def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di def run_phase5(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]: """ - Phase 5: Perturbation analysis (critical slowing down). - - - Points around critical point - - Full time series for autocorrelation analysis - - Measure relaxation times - """ - from joblib import Parallel, delayed - - warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting) - - pb = cfg.critical_prey_birth - base_pd = cfg.critical_prey_death - - jobs = [] - for offset in cfg.prey_death_offsets: - pd = base_pd + offset - if pd <= 0: - continue - - for rep in range(cfg.n_replicates): - params = {"offset": offset, "phase": 5} - seed = generate_unique_seed(params, rep) - jobs.append( - ( - pb, - pd, - cfg.predator_birth, - cfg.predator_death, - cfg.grid_size, - seed, - cfg, - False, - ) - ) - - logger.info(f"Phase 5: {len(jobs):,} simulations") - logger.info(f" prey_death offsets: {cfg.prey_death_offsets}") - logger.info(f" Base critical point: pb={pb}, pd={base_pd}") - - output_jsonl = output_dir / "phase5_results.jsonl" - all_results = [] - - with open(output_jsonl, "w", encoding="utf-8") as f: - executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator") - tasks = (delayed(run_single_simulation)(*job) for job in jobs) - - for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 5"): - f.write(json.dumps(result, default=str) + "\n") - f.flush() - all_results.append(result) - - meta = { - "phase": 5, - "description": "Perturbation analysis / critical slowing down", - "n_sims": len(all_results), - "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), - } - with open(output_dir / "phase5_metadata.json", "w") as f: - json.dump(meta, f, indent=2, default=str) - - logger.info(f"Phase 5 complete. Results: {output_jsonl}") - return all_results - - -def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]: - """ - Execute Phase 6 of the simulation: Global 4D parameter sweep with directed hunting. + Execute Phase 5 of the simulation: Global 4D parameter sweep with directed hunting. This phase performs a comprehensive sensitivity analysis by varying four key parameters (prey birth/death and predator birth/death) while directed @@ -1054,7 +988,7 @@ def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di other_param_values = np.linspace(0.0, 1.0, 11) # 11 values for the rest # Logging - logger.info(f"Phase 6: Full 4D Parameter Sweep (Directed Hunting)") + logger.info(f"Phase 5: Full 4D Parameter Sweep (Directed Hunting)") logger.info(f" prey_death: 10 values from 0.05 to 0.95") logger.info(f" prey_birth, pred_birth, pred_death: 11 values each from 0 to 1") logger.info(f" Grid Size: {cfg.grid_size}") @@ -1101,7 +1035,7 @@ def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di f" Total simulations: {len(jobs):,}" ) # 11 * 10 * 11 * 11 * n_reps = 13,310 * n_reps - output_jsonl = output_dir / "phase6_results.jsonl" + output_jsonl = output_dir / "phase5_results.jsonl" all_results = [] with open(output_jsonl, "w", encoding="utf-8") as f: @@ -1117,7 +1051,7 @@ def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di # Save Metadata meta = { - "phase": 6, + "phase": 5, "description": "Global 4D Sensitivity Analysis with Directed Hunting", "prey_death_values": prey_death_values.tolist(), "other_param_values": other_param_values.tolist(), @@ -1135,7 +1069,7 @@ def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di with open(output_dir / "phase6_metadata.json", "w") as f: json.dump(meta, f, indent=2, default=str) - logger.info(f"Phase 6 complete. Results: {output_jsonl}") + logger.info(f"Phase 5 complete. Results: {output_jsonl}") return all_results @@ -1149,7 +1083,6 @@ def run_phase6(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di 3: run_phase3, 4: run_phase4, 5: run_phase5, - 6: run_phase6, } From cee5813e54038906f4decd59870eabc719ffe395 Mon Sep 17 00:00:00 2001 From: Kimon Anagnostopoulos <96205326+codegithubka@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:30:46 +0100 Subject: [PATCH 3/7] Delete scripts/.DS_Store --- scripts/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 scripts/.DS_Store diff --git a/scripts/.DS_Store b/scripts/.DS_Store deleted file mode 100644 index effc3961920249afcd61b8cadb099545e20a701d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJ5B>J5S@V(E2T+^sC7r@3ou+;Ibo{mDwymn=rd`6KxIcNmc({8!`_6jx1M6=6 zA&YrN*pOf#7zhS}fneZQ4B*ZdDaMA;2Lr)CF!08JoDT_2uyo9ZdUT-E5&$UAXcg#E zONdW$EFH5URv>JyKyzg;G1%NOpWLo=%!cMp?8OKB&acG_>+Xo3)SS39j6N6$2Ko#f z+HfNG|2ck{!6Lu!5~E-s7}zrgcvLN_89vJI)=!@&cWpwuL=zFaED8jA?-783oFhlN cXzNLI*p-giP@>4Zx&z}OpoByh4EzBDFHS%;MgRZ+ From d78404a605c803b063adc78ed443e282c5b77c4a Mon Sep 17 00:00:00 2001 From: Kimon Date: Fri, 30 Jan 2026 12:34:12 +0100 Subject: [PATCH 4/7] adding documentation slowly using pdoc --- bash_scripts/run_phase1.sh | 11 +- bash_scripts/run_phase2.sh | 13 +- bash_scripts/run_phase3.sh | 13 +- bash_scripts/run_phase4.sh | 15 +- bash_scripts/run_phase5.sh | 19 +- docs/index.html | 221 ++ docs/models.html | 291 ++ docs/models/CA.html | 2613 ++++++++++++++++ docs/models/config.html | 2057 +++++++++++++ docs/models/numba_optimized.html | 3327 ++++++++++++++++++++ docs/scripts.html | 327 ++ docs/scripts/experiments.html | 3382 +++++++++++++++++++++ docs/search.js | 46 + {docs => genai_usage}/GEN_AI.md | 0 {docs => genai_usage}/kimon_prompts.md | 0 {docs => genai_usage}/sary_prompts.md | 0 {docs => genai_usage}/sofronia_prompts.md | 0 {docs => genai_usage}/storm_prompts.md | 0 misc/benchmark.py | 2 +- misc/profile_sim.py | 26 - misc/soc_analysis.py | 1 - models/CA.py | 84 +- models/__init__.py | 19 +- models/config.py | 46 +- models/numba_optimized.py | 196 +- notebooks/plots.ipynb | 167 +- requirements.txt | 3 +- scripts/__init__.py | 37 +- scripts/experiments.py | 126 +- 29 files changed, 12556 insertions(+), 486 deletions(-) create mode 100644 docs/index.html create mode 100644 docs/models.html create mode 100644 docs/models/CA.html create mode 100644 docs/models/config.html create mode 100644 docs/models/numba_optimized.html create mode 100644 docs/scripts.html create mode 100644 docs/scripts/experiments.html create mode 100644 docs/search.js rename {docs => genai_usage}/GEN_AI.md (100%) rename {docs => genai_usage}/kimon_prompts.md (100%) rename {docs => genai_usage}/sary_prompts.md (100%) rename {docs => genai_usage}/sofronia_prompts.md (100%) rename {docs => genai_usage}/storm_prompts.md (100%) delete mode 100644 misc/profile_sim.py diff --git a/bash_scripts/run_phase1.sh b/bash_scripts/run_phase1.sh index 07b5996..760ba56 100644 --- a/bash_scripts/run_phase1.sh +++ b/bash_scripts/run_phase1.sh @@ -14,9 +14,6 @@ # ============================================================================= # # PHASE 1: Find critical point via 2D sweep of prey_birth × prey_death -# - 15 × 15 × 15 reps × 2 (evo/non-evo) = 6,750 simulations -# - Estimated runtime: ~2 min on 32 cores -# - Memory: ~8 GB peak # # SUBMIT: sbatch run_phase1.sh # MONITOR: squeue -u $USER @@ -88,10 +85,4 @@ echo "Results in: $OUTPUT_DIR/" echo "" echo "Output files:" ls -lh $OUTPUT_DIR/ -echo "" -echo "Next steps:" -echo " 1. Download phase1_results.jsonl" -echo " 2. Run analysis.py to find critical point" -echo " 3. Update config.py with critical_prey_birth/death" -echo " 4. Run phase 2-5" -echo "========================================" \ No newline at end of file +echo "" \ No newline at end of file diff --git a/bash_scripts/run_phase2.sh b/bash_scripts/run_phase2.sh index 05da45e..8ee9708 100644 --- a/bash_scripts/run_phase2.sh +++ b/bash_scripts/run_phase2.sh @@ -13,11 +13,6 @@ # PP Hydra Effect - Phase 2: Self-Organization (SOC Test) # ============================================================================= # -# PHASE 2: Test if prey_death evolves toward critical point -# - 6 initial prey_death values × 30 reps = 180 simulations -# - Longer runs (5000 steps) for evolution to equilibrate -# - Tracks evolved_prey_death_timeseries -# # SUBMIT: sbatch run_phase2.sh # MONITOR: squeue -u $USER # CANCEL: scancel @@ -88,10 +83,4 @@ echo "Results in: $OUTPUT_DIR/" echo "" echo "Output files:" ls -lh $OUTPUT_DIR/ -echo "" -echo "Next steps:" -echo " 1. Download phase2_results.jsonl" -echo " 2. Plot evolved_prey_death_final vs initial prey_death" -echo " 3. Check if all runs converge to ~0.095-0.105 (critical point)" -echo " 4. If SOC confirmed, proceed to Phase 3 (finite-size scaling)" -echo "========================================" \ No newline at end of file +echo "" \ No newline at end of file diff --git a/bash_scripts/run_phase3.sh b/bash_scripts/run_phase3.sh index 59b9e86..54a9076 100644 --- a/bash_scripts/run_phase3.sh +++ b/bash_scripts/run_phase3.sh @@ -13,11 +13,6 @@ # PP Hydra Effect - Phase 3: Finite-Size Scaling # ============================================================================= # -# PHASE 3: Test finite-size scaling at critical point -# - Grid sizes: 50, 100, 250, 500, 1000 -# - 20 replicates per size = 100 simulations -# - Cluster size distributions for power-law analysis -# # SUBMIT: sbatch run_phase3.sh # MONITOR: squeue -u $USER # CANCEL: scancel @@ -86,10 +81,4 @@ echo "Results in: $OUTPUT_DIR/" echo "" echo "Output files:" ls -lh $OUTPUT_DIR/ -echo "" -echo "Next steps:" -echo " 1. Download phase3_results.jsonl" -echo " 2. Analyze cluster size distributions P(s) for each grid size" -echo " 3. Fit power-law exponent tau from P(s) ~ s^(-tau)" -echo " 4. Check finite-size cutoff s_max ~ L^D (fractal dimension)" -echo "========================================" \ No newline at end of file +echo "" \ No newline at end of file diff --git a/bash_scripts/run_phase4.sh b/bash_scripts/run_phase4.sh index cd242aa..cf8258c 100644 --- a/bash_scripts/run_phase4.sh +++ b/bash_scripts/run_phase4.sh @@ -13,13 +13,6 @@ # PP Hydra Effect - Phase 4: Global Sensitivity Analysis # ============================================================================= # -# PHASE 4: Full 4D Parameter Sweep (Global Sensitivity) -# - Parameters: prey_birth, prey_death, pred_birth, pred_death -# - Sweep: 0.0 to 1.0 (11 values each) = 14,641 combinations -# - Replicates: 10 per combination -# - Total Simulations: ~146,410 -# - Grid Size: 250x250 -# # SUBMIT: sbatch run_phase4.sh # MONITOR: squeue -u $USER # CANCEL: scancel @@ -89,10 +82,4 @@ echo "Results in: $OUTPUT_DIR/" echo "" echo "Output files:" ls -lh $OUTPUT_DIR/ -echo "" -echo "Next steps:" -echo " 1. Download phase4_results.jsonl" -echo " 2. Perform Global Sensitivity Analysis (Sobol Indices)" -echo " 3. Identify parameter dominance for extinction events" -echo " 4. Plot parameter heatmaps for predator/prey survival" -echo "========================================" \ No newline at end of file +echo "" \ No newline at end of file diff --git a/bash_scripts/run_phase5.sh b/bash_scripts/run_phase5.sh index f925586..9d84a9f 100644 --- a/bash_scripts/run_phase5.sh +++ b/bash_scripts/run_phase5.sh @@ -10,17 +10,9 @@ #SBATCH --error=pp_phase6_%j.err # ============================================================================= -# PP Hydra Effect - Phase 6: Directed Hunting 4D Sweep +# PP Hydra Effect - Phase 5: Directed Hunting 4D Sweep # ============================================================================= # -# PHASE 6: Full 4D parameter sweep with directed hunting enabled -# - Same structure as Phase 4 but with directed_hunting=True -# - 11^4 × 10 reps = 146,410 simulations -# - Grid size: 250 -# - Collects time series for comparison with Phase 4 -# - Estimated runtime: ~4-6 hours on 128 cores -# - Memory: mem=0 (use all available node memory) -# # PURPOSE: Test if Hydra effect and SOC persist under directed hunting # # SUBMIT: sbatch run_phase6.sh @@ -93,11 +85,4 @@ echo "Results in: $OUTPUT_DIR/" echo "" echo "Output files:" ls -lh $OUTPUT_DIR/ -echo "" -echo "Next steps:" -echo " 1. Download phase6_results.jsonl" -echo " 2. Compare with Phase 4 results (random hunting baseline)" -echo " 3. Analyze if Hydra effect persists under directed hunting" -echo " 4. Compare critical point locations between Phase 4 and Phase 6" -echo " 5. Check for differences in SOC signatures" -echo "========================================" \ No newline at end of file +echo "" \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..17220d7 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,221 @@ + + + + + + + Module List + + + + + + + + + + +
+ + pdoc + + +
+
+ + \ No newline at end of file diff --git a/docs/models.html b/docs/models.html new file mode 100644 index 0000000..da381cc --- /dev/null +++ b/docs/models.html @@ -0,0 +1,291 @@ + + + + + + + models API documentation + + + + + + + + + +
+
+

+models

+ +

Models Package

+ +

This package contains the core simulation logic for the Predator-Prey +Cellular Automata.

+ +

Key Components

+ +
    +
  • CA: The base Cellular Automata class.
  • +
  • experiment: Tools for running batches and collecting data.
  • +
  • numba_optimized: High-performance kernels for HPC execution.
  • +
+ +

Example

+ +

```python +from models.CA import PredatorPreyCA +sim = PredatorPreyCA(rows=100, cols=100)

+
+ + + + + +
 1"""
+ 2# Models Package
+ 3
+ 4This package contains the core simulation logic for the Predator-Prey 
+ 5Cellular Automata.
+ 6
+ 7## Key Components
+ 8- `CA`: The base Cellular Automata class.
+ 9- `experiment`: Tools for running batches and collecting data.
+10- `numba_optimized`: High-performance kernels for HPC execution.
+11
+12## Example
+13```python
+14from models.CA import PredatorPreyCA
+15sim = PredatorPreyCA(rows=100, cols=100)
+16"""
+17
+18from .CA import CA, PP
+
+ + +
+
+ + \ No newline at end of file diff --git a/docs/models/CA.html b/docs/models/CA.html new file mode 100644 index 0000000..4666efe --- /dev/null +++ b/docs/models/CA.html @@ -0,0 +1,2613 @@ + + + + + + + models.CA API documentation + + + + + + + + + +
+
+

+models.CA

+ +

Cellular automaton base class.

+
+ + + + + +
  1"""
+  2Cellular automaton base class.
+  3"""
+  4
+  5from typing import Tuple, Dict, Optional
+  6
+  7import numpy as np
+  8import logging
+  9import sys
+ 10from pathlib import Path
+ 11
+ 12# Add parent directory to path for imports
+ 13sys.path.insert(0, str(Path(__file__).parent.parent))
+ 14
+ 15from models.numba_optimized import PPKernel, set_numba_seed
+ 16
+ 17# Module logger
+ 18logger = logging.getLogger(__name__)
+ 19
+ 20
+ 21class CA:
+ 22    """
+ 23    Base cellular automaton class for spatial simulations.
+ 24
+ 25    This class provides a framework for multi-species cellular automata with
+ 26    support for global parameters, per-cell evolving parameters, and
+ 27    grid initialization based on density.
+ 28
+ 29    Attributes
+ 30    ----------
+ 31    grid : np.ndarray
+ 32        2D numpy array containing integers in range [0, n_species].
+ 33    params : Dict[str, Any]
+ 34        Global parameters shared by all cells.
+ 35    cell_params : Dict[str, Any]
+ 36        Local per-cell parameters, typically stored as numpy arrays matching the grid shape.
+ 37    neighborhood : str
+ 38        The adjacency rule used ('neumann' or 'moore').
+ 39    generator : np.random.Generator
+ 40        The random number generator instance for reproducibility.
+ 41    species_names : Tuple[str, ...]
+ 42        Human-readable names for each species state.
+ 43    """
+ 44
+ 45    # Default colormap spec (string or sequence); resolved in `visualize` at runtime
+ 46    _default_cmap = "viridis"
+ 47
+ 48    # Read-only accessors for size/densities (protected attributes set in __init__)
+ 49    @property
+ 50    def rows(self) -> int:
+ 51        """int: Number of rows in the grid."""
+ 52        return getattr(self, "_rows")
+ 53
+ 54    @property
+ 55    def cols(self) -> int:
+ 56        """int: Number of columns in the grid."""
+ 57        return getattr(self, "_cols")
+ 58
+ 59    @property
+ 60    def densities(self) -> Tuple[float, ...]:
+ 61        """Tuple[float, ...]: Initial density fraction for each species."""
+ 62        return tuple(getattr(self, "_densities"))
+ 63
+ 64    # make n_species protected with read-only property
+ 65    @property
+ 66    def n_species(self) -> int:
+ 67        """int: Number of distinct species states (excluding empty state 0)."""
+ 68        return int(getattr(self, "_n_species"))
+ 69
+ 70    def __init__(
+ 71        self,
+ 72        rows: int,
+ 73        cols: int,
+ 74        densities: Tuple[float, ...],
+ 75        neighborhood: str,
+ 76        params: Dict[str, object],
+ 77        cell_params: Dict[str, object],
+ 78        seed: Optional[int] = None,
+ 79    ) -> None:
+ 80        """
+ 81        Initialize the cellular automaton grid and configurations.
+ 82
+ 83        Parameters
+ 84        ----------
+ 85        rows : int
+ 86            Number of rows in the grid (must be > 0).
+ 87        cols : int
+ 88            Number of columns in the grid (must be > 0).
+ 89        densities : Tuple[float, ...]
+ 90            Initial density for each species. Length defines `n_species`.
+ 91            Values must sum to <= 1.0.
+ 92        neighborhood : {'neumann', 'moore'}
+ 93            Type of neighborhood connectivity.
+ 94        params : Dict[str, Any]
+ 95            Initial global parameter values.
+ 96        cell_params : Dict[str, Any]
+ 97            Initial local per-cell parameters.
+ 98        seed : int, optional
+ 99            Seed for the random number generator.
+100        """
+101        assert isinstance(rows, int) and rows > 0, "rows must be positive int"
+102        assert isinstance(cols, int) and cols > 0, "cols must be positive int"
+103        assert (
+104            isinstance(densities, tuple) and len(densities) > 0
+105        ), "densities must be a non-empty tuple"
+106        for d in densities:
+107            assert (
+108                isinstance(d, (float, int)) and d >= 0
+109            ), "each density must be non-negative"
+110        total_density = float(sum(densities))
+111        assert total_density <= 1.0 + 1e-12, "sum of densities must not exceed 1"
+112        assert neighborhood in (
+113            "neumann",
+114            "moore",
+115        ), "neighborhood must be 'neumann' or 'moore'"
+116
+117        self._n_species: int = len(densities)
+118        # store protected size/density attributes (read-only properties exposed)
+119        self._rows: int = rows
+120        self._cols: int = cols
+121        self._densities: Tuple[float, ...] = tuple(densities)
+122        self.params: Dict[str, object] = dict(params) if params is not None else {}
+123        self.cell_params: Dict[str, object] = (
+124            dict(cell_params) if cell_params is not None else {}
+125        )
+126
+127        # per-parameter evolve metadata and evolution state
+128        # maps parameter name -> dict with keys 'sd','min','max','species'
+129        self._evolve_info: Dict[str, Dict[str, float]] = {}
+130        # when True, inheritance uses deterministic copy from parent (no mutation)
+131        self._evolution_stopped: bool = False
+132
+133        # human-readable species names (useful for visualization). Default
+134        # generates generic names based on n_species; subclasses may override.
+135        self.species_names: Tuple[str, ...] = tuple(
+136            f"species{i+1}" for i in range(self._n_species)
+137        )
+138        self.neighborhood: str = neighborhood
+139        self.generator: np.random.Generator = np.random.default_rng(seed)
+140
+141        self.grid: np.ndarray = np.zeros((rows, cols), dtype=int)
+142
+143        total_cells = rows * cols
+144        # Fill grid with species states 1..n_species according to densities.
+145        for i, dens in enumerate(densities):
+146            if dens <= 0:
+147                continue
+148            n_to_fill = int(round(total_cells * float(dens)))
+149            if n_to_fill <= 0:
+150                continue
+151            empty_flat = np.flatnonzero(self.grid.ravel() == 0)
+152            if len(empty_flat) == 0:
+153                break
+154            n_choice = min(n_to_fill, len(empty_flat))
+155            chosen = self.generator.choice(empty_flat, size=n_choice, replace=False)
+156            # assign chosen flattened indices to state i+1
+157            r = chosen // cols
+158            c = chosen % cols
+159            self.grid[r, c] = i + 1
+160
+161    def validate(self) -> None:
+162        """
+163        Validate core CA invariants and grid dimensions.
+164
+165        Checks that the neighborhood is valid, the grid matches initialized dimensions,
+166        and that local parameter arrays match the grid shape.
+167
+168        Raises
+169        ------
+170        ValueError
+171            If any structural invariant is violated.
+172        """
+173        if self.neighborhood not in ("neumann", "moore"):
+174            raise ValueError("neighborhood must be 'neumann' or 'moore'")
+175
+176        expected_shape = (int(getattr(self, "_rows")), int(getattr(self, "_cols")))
+177        if self.grid.shape != expected_shape:
+178            raise ValueError(
+179                f"grid shape {self.grid.shape} does not match expected {expected_shape}"
+180            )
+181
+182        # Ensure any array in cell_params matches grid shape
+183        for k, v in (self.cell_params or {}).items():
+184            if isinstance(v, np.ndarray) and v.shape != expected_shape:
+185                raise ValueError(f"cell_params['{k}'] must have shape equal to grid")
+186
+187    def _infer_species_from_param_name(self, param_name: str) -> Optional[int]:
+188        """
+189        Infer the 1-based species index from a parameter name using `species_names`.
+190
+191        This method checks if the given parameter name starts with any of the
+192        defined species names followed by an underscore (e.g., 'prey_birth').
+193        It is used to automatically route global parameters to the correct
+194        species' local parameter arrays.
+195
+196        Parameters
+197        ----------
+198        param_name : str
+199            The name of the parameter to check.
+200
+201        Returns
+202        -------
+203        Optional[int]
+204            The 1-based index of the species if a matching prefix is found;
+205            otherwise, None.
+206
+207        Notes
+208        -----
+209        The method expects `self.species_names` to be a collection of strings.
+210        If `param_name` is not a string or no match is found, it returns None.
+211        """
+212        if not isinstance(param_name, str):
+213            return None
+214        for idx, name in enumerate(self.species_names or ()):  # type: ignore
+215            if isinstance(name, str) and param_name.startswith(f"{name}_"):
+216                return idx + 1
+217        return None
+218
+219    def evolve(
+220        self,
+221        param: str,
+222        species: Optional[int] = None,
+223        sd: float = 0.05,
+224        min_val: Optional[float] = None,
+225        max_val: Optional[float] = None,
+226    ) -> None:
+227        """
+228        Enable per-cell evolution for a specific parameter on a given species.
+229
+230        This method initializes a spatial parameter array (local parameter map)
+231        for a global parameter. It allows individual cells to carry their own
+232        values for that parameter, which can then mutate and evolve during
+233        the simulation.
+234
+235        Parameters
+236        ----------
+237        param : str
+238            The name of the global parameter to enable for evolution.
+239            Must exist in `self.params`.
+240        species : int, optional
+241            The 1-based index of the species to which this parameter applies.
+242            If None, the method attempts to infer the species from the
+243            parameter name prefix.
+244        sd : float, default 0.05
+245            The standard deviation of the Gaussian mutation applied during
+246            inheritance/reproduction.
+247        min_val : float, optional
+248            The minimum allowable value for the parameter (clamping).
+249            Defaults to 0.01 if not provided.
+250        max_val : float, optional
+251            The maximum allowable value for the parameter (clamping).
+252            Defaults to 0.99 if not provided.
+253
+254        Raises
+255        ------
+256        ValueError
+257            If the parameter is not in `self.params`, the species cannot be
+258            inferred, or the species index is out of bounds.
+259
+260        Notes
+261        -----
+262        The local parameter is stored in `self.cell_params` as a 2D numpy
+263        array initialized with the current global value for all cells of
+264        the target species, and `NaN` elsewhere.
+265        """
+266        if min_val is None:
+267            min_val = 0.01
+268        if max_val is None:
+269            max_val = 0.99
+270        if param not in self.params:
+271            raise ValueError(f"Unknown parameter '{param}'")
+272        if species is None:
+273            species = self._infer_species_from_param_name(param)
+274            if species is None:
+275                raise ValueError(
+276                    "species must be provided or inferable from param name and species_names"
+277                )
+278        if not isinstance(species, int) or species <= 0 or species > self._n_species:
+279            raise ValueError("species must be an integer between 1 and n_species")
+280
+281        arr = np.full(self.grid.shape, np.nan, dtype=float)
+282        mask = self.grid == int(species)
+283        arr[mask] = float(self.params[param])
+284        self.cell_params[param] = arr
+285        self._evolve_info[param] = {
+286            "sd": float(sd),
+287            "min": float(min_val),
+288            "max": float(max_val),
+289            "species": int(species),
+290        }
+291
+292    def update(self) -> None:
+293        """
+294        Perform one update step of the cellular automaton.
+295
+296        This is an abstract method that defines the transition rules of the
+297        system. It must be implemented by concrete subclasses to specify
+298        how cell states and parameters change over time based on their
+299        current state and neighborhood.
+300
+301        Raises
+302        ------
+303        NotImplementedError
+304            If called directly on the base class instead of an implementation.
+305
+306        Returns
+307        -------
+308        None
+309
+310        Notes
+311        -----
+312        In a typical implementation, this method handles the logic for
+313        stochastic transitions, movement, or predator-prey interactions.
+314        """
+315        raise NotImplementedError(
+316            "Override update() in a subclass to define CA dynamics"
+317        )
+318
+319    def run(
+320        self,
+321        steps: int,
+322        stop_evolution_at: Optional[int] = None,
+323        snapshot_iters: Optional[list] = None,
+324    ) -> None:
+325        """
+326        Execute the cellular automaton simulation for a specified number of steps.
+327
+328        This method drives the simulation loop, calling `update()` at each
+329        iteration. It manages visualization updates, directory creation for
+330        data persistence, and handles the freezing of evolving parameters
+331        at a specific time step.
+332
+333        Parameters
+334        ----------
+335        steps : int
+336            The total number of iterations to run (must be non-negative).
+337        stop_evolution_at : int, optional
+338            The 1-based iteration index after which parameter mutation is
+339            disabled. Useful for observing system stability after a period
+340            of adaptation.
+341        snapshot_iters : List[int], optional
+342            A list of specific 1-based iteration indices at which to save
+343            the current grid state to the results directory.
+344
+345        Returns
+346        -------
+347        None
+348
+349        Notes
+350        -----
+351        If snapshots are requested, a results directory is automatically created
+352        using a timestamped subfolder (e.g., 'results/run-1700000000/').
+353        Visualization errors are logged but do not terminate the simulation.
+354        """
+355        assert (
+356            isinstance(steps, int) and steps >= 0
+357        ), "steps must be a non-negative integer"
+358
+359        # normalize snapshot iteration list
+360        snapshot_set = set(snapshot_iters) if snapshot_iters is not None else set()
+361
+362        for i in range(steps):
+363            self.update()
+364            # Update visualization if enabled every `interval` iterations
+365            if getattr(self, "_viz_on", False):
+366                # iteration number is 1-based for display
+367                try:
+368                    self._viz_update(i + 1)
+369                except Exception:
+370                    # Log visualization errors but don't stop the simulation
+371                    logger.exception(
+372                        "Visualization update failed at iteration %d", i + 1
+373                    )
+374
+375            # create snapshots if requested at this iteration
+376            if (i + 1) in snapshot_set:
+377                try:
+378                    # create snapshot folder if not present
+379                    if (
+380                        not hasattr(self, "_viz_snapshot_dir")
+381                        or self._viz_snapshot_dir is None
+382                    ):
+383                        import os, time
+384
+385                        base = "results"
+386                        ts = int(time.time())
+387                        run_folder = f"run-{ts}"
+388                        full = os.path.join(base, run_folder)
+389                        os.makedirs(full, exist_ok=True)
+390                        self._viz_snapshot_dir = full
+391                    self._viz_save_snapshot(i + 1)
+392                except (OSError, PermissionError):
+393                    logger.exception(
+394                        "Failed to create or write snapshot at iteration %d", i + 1
+395                    )
+396
+397            # stop evolution at specified time-step (disable further evolution)
+398            if stop_evolution_at is not None and (i + 1) == int(stop_evolution_at):
+399                # mark evolution as stopped; do not erase evolve metadata so
+400                # deterministic inheritance can still use parent values
+401                self._evolution_stopped = True
+402
+403
+404class PP(CA):
+405    """
+406    Predator-Prey Cellular Automaton model with Numba-accelerated kernels.
+407
+408    This model simulates a stochastic predator-prey system where species
+409    interact on a 2D grid. It supports evolving per-cell death rates,
+410    periodic boundary conditions, and both random and directed hunting
+411    behaviors.
+412
+413    Parameters
+414    ----------
+415    rows : int, default 10
+416        Number of rows in the simulation grid.
+417    cols : int, default 10
+418        Number of columns in the simulation grid.
+419    densities : Tuple[float, ...], default (0.2, 0.1)
+420        Initial population densities for (prey, predator).
+421    neighborhood : {'moore', 'neumann'}, default 'moore'
+422        The neighborhood type for cell interactions.
+423    params : Dict[str, object], optional
+424        Global parameters: "prey_death", "predator_death", "prey_birth",
+425        "predator_birth".
+426    cell_params : Dict[str, object], optional
+427        Initial local parameter maps (2D arrays).
+428    seed : int, optional
+429        Random seed for reproducibility.
+430    synchronous : bool, default True
+431        If True, updates the entire grid at once. If False, updates
+432        cells asynchronously.
+433    directed_hunting : bool, default False
+434        If True, predators selectively hunt prey rather than choosing
+435        neighbors at random.
+436
+437    Attributes
+438    ----------
+439    species_names : Tuple[str, ...]
+440        Labels for the species ('prey', 'predator').
+441    synchronous : bool
+442        Current update mode.
+443    directed_hunting : bool
+444        Current hunting strategy logic.
+445    """
+446
+447    # Default colors: 0=empty black, 1=prey green, 2=predator red
+448    _default_cmap = ("black", "green", "red")
+449
+450    def __init__(
+451        self,
+452        rows: int = 10,
+453        cols: int = 10,
+454        densities: Tuple[float, ...] = (0.2, 0.1),
+455        neighborhood: str = "moore",
+456        params: Dict[str, object] = None,
+457        cell_params: Dict[str, object] = None,
+458        seed: Optional[int] = None,
+459        synchronous: bool = True,
+460        directed_hunting: bool = False,  # New directed hunting option
+461    ) -> None:
+462        """
+463        Initialize the Predator-Prey CA with validated parameters and kernels.
+464        """
+465        # Allowed params and defaults
+466        _defaults = {
+467            "prey_death": 0.05,
+468            "predator_death": 0.1,
+469            "prey_birth": 0.25,
+470            "predator_birth": 0.2,
+471        }
+472
+473        # Validate user-supplied params: only allowed keys
+474        if params is None:
+475            merged_params = dict(_defaults)
+476        else:
+477            if not isinstance(params, dict):
+478                raise TypeError("params must be a dict or None")
+479            extra = set(params.keys()) - set(_defaults.keys())
+480            if extra:
+481                raise ValueError(f"Unexpected parameter keys: {sorted(list(extra))}")
+482            # Do not override user-set values: start from defaults then update with user values
+483            merged_params = dict(_defaults)
+484            merged_params.update(params)
+485
+486        # Validate numerical ranges
+487        for k, v in merged_params.items():
+488            if not isinstance(v, (int, float)):
+489                raise TypeError(f"Parameter '{k}' must be a number between 0 and 1")
+490            if not (0.0 <= float(v) <= 1.0):
+491                raise ValueError(f"Parameter '{k}' must be between 0 and 1")
+492
+493        # Call base initializer with merged params
+494        super().__init__(
+495            rows, cols, densities, neighborhood, merged_params, cell_params, seed
+496        )
+497
+498        self.synchronous: bool = bool(synchronous)
+499        self.directed_hunting: bool = bool(directed_hunting)
+500
+501        # set human-friendly species names for PP
+502        self.species_names = ("prey", "predator")
+503
+504        if seed is not None:
+505            # This sets the seed for all @njit functions globally
+506            set_numba_seed(seed)
+507
+508        self._kernel = PPKernel(
+509            rows, cols, neighborhood, directed_hunting=directed_hunting
+510        )
+511
+512    # Remove PP-specific evolve wrapper; use CA.evolve with optional species
+513
+514    def validate(self) -> None:
+515        """
+516        Validate Predator-Prey specific invariants and spatial parameter arrays.
+517
+518        Extends the base CA validation to ensure that numerical parameters are
+519        within the [0, 1] probability range and that evolved parameter maps
+520        (e.g., prey_death) correctly align with the species locations.
+521
+522        Raises
+523        ------
+524        ValueError
+525            If grid shapes, parameter ranges, or species masks are inconsistent.
+526        TypeError
+527            If parameters are non-numeric.
+528        """
+529        super().validate()
+530
+531        # Validate global params
+532        for k, v in (self.params or {}).items():
+533            if not isinstance(v, (int, float)):
+534                raise TypeError(f"Parameter '{k}' must be numeric")
+535            if not (0.0 <= float(v) <= 1.0):
+536                raise ValueError(f"Parameter '{k}' must be between 0 and 1")
+537
+538        # Validate per-cell evolve arrays
+539        for pname, meta in (self._evolve_info or {}).items():
+540            arr = self.cell_params.get(pname)
+541            if not isinstance(arr, np.ndarray):
+542                # absent or non-array per-cell params are allowed; skip
+543                continue
+544            # shape already checked in super().validate(), but be explicit
+545            if arr.shape != self.grid.shape:
+546                raise ValueError(f"cell_params['{pname}'] must match grid shape")
+547            # expected non-NaN positions correspond to species stored in metadata
+548            species = None
+549            if isinstance(meta, dict) and "species" in meta:
+550                species = int(meta.get("species"))
+551            else:
+552                # try to infer species from parameter name using species_names
+553                species = self._infer_species_from_param_name(pname)
+554                if species is None:
+555                    raise ValueError(
+556                        f"cell_params['{pname}'] missing species metadata and could not infer from name"
+557                    )
+558            nonnan = ~np.isnan(arr)
+559            expected = self.grid == species
+560            if not np.array_equal(nonnan, expected):
+561                raise ValueError(
+562                    f"cell_params['{pname}'] non-NaN entries must match positions of species {species}"
+563                )
+564            # values must be within configured range where not NaN
+565            mn = float(meta.get("min", 0.0))
+566            mx = float(meta.get("max", 1.0))
+567            vals = arr[~np.isnan(arr)]
+568            if vals.size > 0:
+569                if np.any(vals < mn) or np.any(vals > mx):
+570                    raise ValueError(
+571                        f"cell_params['{pname}'] contains values outside [{mn}, {mx}]"
+572                    )
+573
+574    def update_async(self) -> None:
+575        """
+576        Execute an asynchronous update using the optimized Numba kernel.
+577
+578        This method retrieves the evolved parameter maps and delegates the
+579        stochastic transitions to the `PPKernel`. Asynchronous updates
+580        typically handle cell-by-cell logic where changes can be
+581        immediately visible to neighbors.
+582        """
+583        # Get the evolved prey death map
+584        # Fallback to a full array of the global param if it doesn't exist yet
+585        p_death_arr = self.cell_params.get("prey_death")
+586        if p_death_arr is None:
+587            p_death_arr = np.full(
+588                self.grid.shape, self.params["prey_death"], dtype=np.float64
+589            )
+590
+591        meta = self._evolve_info.get(
+592            "prey_death", {"sd": 0.05, "min": 0.001, "max": 0.1}
+593        )
+594
+595        # Call the optimized kernel (uses pre-allocated buffers)
+596        self._kernel.update(
+597            self.grid,
+598            p_death_arr,
+599            float(self.params["prey_birth"]),
+600            float(self.params["prey_death"]),
+601            float(self.params["predator_birth"]),
+602            float(self.params["predator_death"]),
+603            float(meta["sd"]),
+604            float(meta["min"]),
+605            float(meta["max"]),
+606            self._evolution_stopped,
+607        )
+608
+609    def update(self) -> None:
+610        """
+611        Dispatch the simulation step based on the configured update mode.
+612        """
+613        if self.synchronous:
+614            self.update_sync()
+615        else:
+616            self.update_async()
+
+ + +
+
+
+ logger = +<Logger models.CA (WARNING)> + + +
+ + + + +
+
+ +
+ + class + CA: + + + +
+ +
 22class CA:
+ 23    """
+ 24    Base cellular automaton class for spatial simulations.
+ 25
+ 26    This class provides a framework for multi-species cellular automata with
+ 27    support for global parameters, per-cell evolving parameters, and
+ 28    grid initialization based on density.
+ 29
+ 30    Attributes
+ 31    ----------
+ 32    grid : np.ndarray
+ 33        2D numpy array containing integers in range [0, n_species].
+ 34    params : Dict[str, Any]
+ 35        Global parameters shared by all cells.
+ 36    cell_params : Dict[str, Any]
+ 37        Local per-cell parameters, typically stored as numpy arrays matching the grid shape.
+ 38    neighborhood : str
+ 39        The adjacency rule used ('neumann' or 'moore').
+ 40    generator : np.random.Generator
+ 41        The random number generator instance for reproducibility.
+ 42    species_names : Tuple[str, ...]
+ 43        Human-readable names for each species state.
+ 44    """
+ 45
+ 46    # Default colormap spec (string or sequence); resolved in `visualize` at runtime
+ 47    _default_cmap = "viridis"
+ 48
+ 49    # Read-only accessors for size/densities (protected attributes set in __init__)
+ 50    @property
+ 51    def rows(self) -> int:
+ 52        """int: Number of rows in the grid."""
+ 53        return getattr(self, "_rows")
+ 54
+ 55    @property
+ 56    def cols(self) -> int:
+ 57        """int: Number of columns in the grid."""
+ 58        return getattr(self, "_cols")
+ 59
+ 60    @property
+ 61    def densities(self) -> Tuple[float, ...]:
+ 62        """Tuple[float, ...]: Initial density fraction for each species."""
+ 63        return tuple(getattr(self, "_densities"))
+ 64
+ 65    # make n_species protected with read-only property
+ 66    @property
+ 67    def n_species(self) -> int:
+ 68        """int: Number of distinct species states (excluding empty state 0)."""
+ 69        return int(getattr(self, "_n_species"))
+ 70
+ 71    def __init__(
+ 72        self,
+ 73        rows: int,
+ 74        cols: int,
+ 75        densities: Tuple[float, ...],
+ 76        neighborhood: str,
+ 77        params: Dict[str, object],
+ 78        cell_params: Dict[str, object],
+ 79        seed: Optional[int] = None,
+ 80    ) -> None:
+ 81        """
+ 82        Initialize the cellular automaton grid and configurations.
+ 83
+ 84        Parameters
+ 85        ----------
+ 86        rows : int
+ 87            Number of rows in the grid (must be > 0).
+ 88        cols : int
+ 89            Number of columns in the grid (must be > 0).
+ 90        densities : Tuple[float, ...]
+ 91            Initial density for each species. Length defines `n_species`.
+ 92            Values must sum to <= 1.0.
+ 93        neighborhood : {'neumann', 'moore'}
+ 94            Type of neighborhood connectivity.
+ 95        params : Dict[str, Any]
+ 96            Initial global parameter values.
+ 97        cell_params : Dict[str, Any]
+ 98            Initial local per-cell parameters.
+ 99        seed : int, optional
+100            Seed for the random number generator.
+101        """
+102        assert isinstance(rows, int) and rows > 0, "rows must be positive int"
+103        assert isinstance(cols, int) and cols > 0, "cols must be positive int"
+104        assert (
+105            isinstance(densities, tuple) and len(densities) > 0
+106        ), "densities must be a non-empty tuple"
+107        for d in densities:
+108            assert (
+109                isinstance(d, (float, int)) and d >= 0
+110            ), "each density must be non-negative"
+111        total_density = float(sum(densities))
+112        assert total_density <= 1.0 + 1e-12, "sum of densities must not exceed 1"
+113        assert neighborhood in (
+114            "neumann",
+115            "moore",
+116        ), "neighborhood must be 'neumann' or 'moore'"
+117
+118        self._n_species: int = len(densities)
+119        # store protected size/density attributes (read-only properties exposed)
+120        self._rows: int = rows
+121        self._cols: int = cols
+122        self._densities: Tuple[float, ...] = tuple(densities)
+123        self.params: Dict[str, object] = dict(params) if params is not None else {}
+124        self.cell_params: Dict[str, object] = (
+125            dict(cell_params) if cell_params is not None else {}
+126        )
+127
+128        # per-parameter evolve metadata and evolution state
+129        # maps parameter name -> dict with keys 'sd','min','max','species'
+130        self._evolve_info: Dict[str, Dict[str, float]] = {}
+131        # when True, inheritance uses deterministic copy from parent (no mutation)
+132        self._evolution_stopped: bool = False
+133
+134        # human-readable species names (useful for visualization). Default
+135        # generates generic names based on n_species; subclasses may override.
+136        self.species_names: Tuple[str, ...] = tuple(
+137            f"species{i+1}" for i in range(self._n_species)
+138        )
+139        self.neighborhood: str = neighborhood
+140        self.generator: np.random.Generator = np.random.default_rng(seed)
+141
+142        self.grid: np.ndarray = np.zeros((rows, cols), dtype=int)
+143
+144        total_cells = rows * cols
+145        # Fill grid with species states 1..n_species according to densities.
+146        for i, dens in enumerate(densities):
+147            if dens <= 0:
+148                continue
+149            n_to_fill = int(round(total_cells * float(dens)))
+150            if n_to_fill <= 0:
+151                continue
+152            empty_flat = np.flatnonzero(self.grid.ravel() == 0)
+153            if len(empty_flat) == 0:
+154                break
+155            n_choice = min(n_to_fill, len(empty_flat))
+156            chosen = self.generator.choice(empty_flat, size=n_choice, replace=False)
+157            # assign chosen flattened indices to state i+1
+158            r = chosen // cols
+159            c = chosen % cols
+160            self.grid[r, c] = i + 1
+161
+162    def validate(self) -> None:
+163        """
+164        Validate core CA invariants and grid dimensions.
+165
+166        Checks that the neighborhood is valid, the grid matches initialized dimensions,
+167        and that local parameter arrays match the grid shape.
+168
+169        Raises
+170        ------
+171        ValueError
+172            If any structural invariant is violated.
+173        """
+174        if self.neighborhood not in ("neumann", "moore"):
+175            raise ValueError("neighborhood must be 'neumann' or 'moore'")
+176
+177        expected_shape = (int(getattr(self, "_rows")), int(getattr(self, "_cols")))
+178        if self.grid.shape != expected_shape:
+179            raise ValueError(
+180                f"grid shape {self.grid.shape} does not match expected {expected_shape}"
+181            )
+182
+183        # Ensure any array in cell_params matches grid shape
+184        for k, v in (self.cell_params or {}).items():
+185            if isinstance(v, np.ndarray) and v.shape != expected_shape:
+186                raise ValueError(f"cell_params['{k}'] must have shape equal to grid")
+187
+188    def _infer_species_from_param_name(self, param_name: str) -> Optional[int]:
+189        """
+190        Infer the 1-based species index from a parameter name using `species_names`.
+191
+192        This method checks if the given parameter name starts with any of the
+193        defined species names followed by an underscore (e.g., 'prey_birth').
+194        It is used to automatically route global parameters to the correct
+195        species' local parameter arrays.
+196
+197        Parameters
+198        ----------
+199        param_name : str
+200            The name of the parameter to check.
+201
+202        Returns
+203        -------
+204        Optional[int]
+205            The 1-based index of the species if a matching prefix is found;
+206            otherwise, None.
+207
+208        Notes
+209        -----
+210        The method expects `self.species_names` to be a collection of strings.
+211        If `param_name` is not a string or no match is found, it returns None.
+212        """
+213        if not isinstance(param_name, str):
+214            return None
+215        for idx, name in enumerate(self.species_names or ()):  # type: ignore
+216            if isinstance(name, str) and param_name.startswith(f"{name}_"):
+217                return idx + 1
+218        return None
+219
+220    def evolve(
+221        self,
+222        param: str,
+223        species: Optional[int] = None,
+224        sd: float = 0.05,
+225        min_val: Optional[float] = None,
+226        max_val: Optional[float] = None,
+227    ) -> None:
+228        """
+229        Enable per-cell evolution for a specific parameter on a given species.
+230
+231        This method initializes a spatial parameter array (local parameter map)
+232        for a global parameter. It allows individual cells to carry their own
+233        values for that parameter, which can then mutate and evolve during
+234        the simulation.
+235
+236        Parameters
+237        ----------
+238        param : str
+239            The name of the global parameter to enable for evolution.
+240            Must exist in `self.params`.
+241        species : int, optional
+242            The 1-based index of the species to which this parameter applies.
+243            If None, the method attempts to infer the species from the
+244            parameter name prefix.
+245        sd : float, default 0.05
+246            The standard deviation of the Gaussian mutation applied during
+247            inheritance/reproduction.
+248        min_val : float, optional
+249            The minimum allowable value for the parameter (clamping).
+250            Defaults to 0.01 if not provided.
+251        max_val : float, optional
+252            The maximum allowable value for the parameter (clamping).
+253            Defaults to 0.99 if not provided.
+254
+255        Raises
+256        ------
+257        ValueError
+258            If the parameter is not in `self.params`, the species cannot be
+259            inferred, or the species index is out of bounds.
+260
+261        Notes
+262        -----
+263        The local parameter is stored in `self.cell_params` as a 2D numpy
+264        array initialized with the current global value for all cells of
+265        the target species, and `NaN` elsewhere.
+266        """
+267        if min_val is None:
+268            min_val = 0.01
+269        if max_val is None:
+270            max_val = 0.99
+271        if param not in self.params:
+272            raise ValueError(f"Unknown parameter '{param}'")
+273        if species is None:
+274            species = self._infer_species_from_param_name(param)
+275            if species is None:
+276                raise ValueError(
+277                    "species must be provided or inferable from param name and species_names"
+278                )
+279        if not isinstance(species, int) or species <= 0 or species > self._n_species:
+280            raise ValueError("species must be an integer between 1 and n_species")
+281
+282        arr = np.full(self.grid.shape, np.nan, dtype=float)
+283        mask = self.grid == int(species)
+284        arr[mask] = float(self.params[param])
+285        self.cell_params[param] = arr
+286        self._evolve_info[param] = {
+287            "sd": float(sd),
+288            "min": float(min_val),
+289            "max": float(max_val),
+290            "species": int(species),
+291        }
+292
+293    def update(self) -> None:
+294        """
+295        Perform one update step of the cellular automaton.
+296
+297        This is an abstract method that defines the transition rules of the
+298        system. It must be implemented by concrete subclasses to specify
+299        how cell states and parameters change over time based on their
+300        current state and neighborhood.
+301
+302        Raises
+303        ------
+304        NotImplementedError
+305            If called directly on the base class instead of an implementation.
+306
+307        Returns
+308        -------
+309        None
+310
+311        Notes
+312        -----
+313        In a typical implementation, this method handles the logic for
+314        stochastic transitions, movement, or predator-prey interactions.
+315        """
+316        raise NotImplementedError(
+317            "Override update() in a subclass to define CA dynamics"
+318        )
+319
+320    def run(
+321        self,
+322        steps: int,
+323        stop_evolution_at: Optional[int] = None,
+324        snapshot_iters: Optional[list] = None,
+325    ) -> None:
+326        """
+327        Execute the cellular automaton simulation for a specified number of steps.
+328
+329        This method drives the simulation loop, calling `update()` at each
+330        iteration. It manages visualization updates, directory creation for
+331        data persistence, and handles the freezing of evolving parameters
+332        at a specific time step.
+333
+334        Parameters
+335        ----------
+336        steps : int
+337            The total number of iterations to run (must be non-negative).
+338        stop_evolution_at : int, optional
+339            The 1-based iteration index after which parameter mutation is
+340            disabled. Useful for observing system stability after a period
+341            of adaptation.
+342        snapshot_iters : List[int], optional
+343            A list of specific 1-based iteration indices at which to save
+344            the current grid state to the results directory.
+345
+346        Returns
+347        -------
+348        None
+349
+350        Notes
+351        -----
+352        If snapshots are requested, a results directory is automatically created
+353        using a timestamped subfolder (e.g., 'results/run-1700000000/').
+354        Visualization errors are logged but do not terminate the simulation.
+355        """
+356        assert (
+357            isinstance(steps, int) and steps >= 0
+358        ), "steps must be a non-negative integer"
+359
+360        # normalize snapshot iteration list
+361        snapshot_set = set(snapshot_iters) if snapshot_iters is not None else set()
+362
+363        for i in range(steps):
+364            self.update()
+365            # Update visualization if enabled every `interval` iterations
+366            if getattr(self, "_viz_on", False):
+367                # iteration number is 1-based for display
+368                try:
+369                    self._viz_update(i + 1)
+370                except Exception:
+371                    # Log visualization errors but don't stop the simulation
+372                    logger.exception(
+373                        "Visualization update failed at iteration %d", i + 1
+374                    )
+375
+376            # create snapshots if requested at this iteration
+377            if (i + 1) in snapshot_set:
+378                try:
+379                    # create snapshot folder if not present
+380                    if (
+381                        not hasattr(self, "_viz_snapshot_dir")
+382                        or self._viz_snapshot_dir is None
+383                    ):
+384                        import os, time
+385
+386                        base = "results"
+387                        ts = int(time.time())
+388                        run_folder = f"run-{ts}"
+389                        full = os.path.join(base, run_folder)
+390                        os.makedirs(full, exist_ok=True)
+391                        self._viz_snapshot_dir = full
+392                    self._viz_save_snapshot(i + 1)
+393                except (OSError, PermissionError):
+394                    logger.exception(
+395                        "Failed to create or write snapshot at iteration %d", i + 1
+396                    )
+397
+398            # stop evolution at specified time-step (disable further evolution)
+399            if stop_evolution_at is not None and (i + 1) == int(stop_evolution_at):
+400                # mark evolution as stopped; do not erase evolve metadata so
+401                # deterministic inheritance can still use parent values
+402                self._evolution_stopped = True
+
+ + +

Base cellular automaton class for spatial simulations.

+ +

This class provides a framework for multi-species cellular automata with +support for global parameters, per-cell evolving parameters, and +grid initialization based on density.

+ +
Attributes
+ +
    +
  • grid (np.ndarray): +2D numpy array containing integers in range [0, n_species].
  • +
  • params (Dict[str, Any]): +Global parameters shared by all cells.
  • +
  • cell_params (Dict[str, Any]): +Local per-cell parameters, typically stored as numpy arrays matching the grid shape.
  • +
  • neighborhood (str): +The adjacency rule used ('neumann' or 'moore').
  • +
  • generator (np.random.Generator): +The random number generator instance for reproducibility.
  • +
  • species_names (Tuple[str, ...]): +Human-readable names for each species state.
  • +
+
+ + +
+ +
+ + CA( rows: int, cols: int, densities: Tuple[float, ...], neighborhood: str, params: Dict[str, object], cell_params: Dict[str, object], seed: Optional[int] = None) + + + +
+ +
 71    def __init__(
+ 72        self,
+ 73        rows: int,
+ 74        cols: int,
+ 75        densities: Tuple[float, ...],
+ 76        neighborhood: str,
+ 77        params: Dict[str, object],
+ 78        cell_params: Dict[str, object],
+ 79        seed: Optional[int] = None,
+ 80    ) -> None:
+ 81        """
+ 82        Initialize the cellular automaton grid and configurations.
+ 83
+ 84        Parameters
+ 85        ----------
+ 86        rows : int
+ 87            Number of rows in the grid (must be > 0).
+ 88        cols : int
+ 89            Number of columns in the grid (must be > 0).
+ 90        densities : Tuple[float, ...]
+ 91            Initial density for each species. Length defines `n_species`.
+ 92            Values must sum to <= 1.0.
+ 93        neighborhood : {'neumann', 'moore'}
+ 94            Type of neighborhood connectivity.
+ 95        params : Dict[str, Any]
+ 96            Initial global parameter values.
+ 97        cell_params : Dict[str, Any]
+ 98            Initial local per-cell parameters.
+ 99        seed : int, optional
+100            Seed for the random number generator.
+101        """
+102        assert isinstance(rows, int) and rows > 0, "rows must be positive int"
+103        assert isinstance(cols, int) and cols > 0, "cols must be positive int"
+104        assert (
+105            isinstance(densities, tuple) and len(densities) > 0
+106        ), "densities must be a non-empty tuple"
+107        for d in densities:
+108            assert (
+109                isinstance(d, (float, int)) and d >= 0
+110            ), "each density must be non-negative"
+111        total_density = float(sum(densities))
+112        assert total_density <= 1.0 + 1e-12, "sum of densities must not exceed 1"
+113        assert neighborhood in (
+114            "neumann",
+115            "moore",
+116        ), "neighborhood must be 'neumann' or 'moore'"
+117
+118        self._n_species: int = len(densities)
+119        # store protected size/density attributes (read-only properties exposed)
+120        self._rows: int = rows
+121        self._cols: int = cols
+122        self._densities: Tuple[float, ...] = tuple(densities)
+123        self.params: Dict[str, object] = dict(params) if params is not None else {}
+124        self.cell_params: Dict[str, object] = (
+125            dict(cell_params) if cell_params is not None else {}
+126        )
+127
+128        # per-parameter evolve metadata and evolution state
+129        # maps parameter name -> dict with keys 'sd','min','max','species'
+130        self._evolve_info: Dict[str, Dict[str, float]] = {}
+131        # when True, inheritance uses deterministic copy from parent (no mutation)
+132        self._evolution_stopped: bool = False
+133
+134        # human-readable species names (useful for visualization). Default
+135        # generates generic names based on n_species; subclasses may override.
+136        self.species_names: Tuple[str, ...] = tuple(
+137            f"species{i+1}" for i in range(self._n_species)
+138        )
+139        self.neighborhood: str = neighborhood
+140        self.generator: np.random.Generator = np.random.default_rng(seed)
+141
+142        self.grid: np.ndarray = np.zeros((rows, cols), dtype=int)
+143
+144        total_cells = rows * cols
+145        # Fill grid with species states 1..n_species according to densities.
+146        for i, dens in enumerate(densities):
+147            if dens <= 0:
+148                continue
+149            n_to_fill = int(round(total_cells * float(dens)))
+150            if n_to_fill <= 0:
+151                continue
+152            empty_flat = np.flatnonzero(self.grid.ravel() == 0)
+153            if len(empty_flat) == 0:
+154                break
+155            n_choice = min(n_to_fill, len(empty_flat))
+156            chosen = self.generator.choice(empty_flat, size=n_choice, replace=False)
+157            # assign chosen flattened indices to state i+1
+158            r = chosen // cols
+159            c = chosen % cols
+160            self.grid[r, c] = i + 1
+
+ + +

Initialize the cellular automaton grid and configurations.

+ +
Parameters
+ +
    +
  • rows (int): +Number of rows in the grid (must be > 0).
  • +
  • cols (int): +Number of columns in the grid (must be > 0).
  • +
  • densities (Tuple[float, ...]): +Initial density for each species. Length defines n_species. +Values must sum to <= 1.0.
  • +
  • neighborhood ({'neumann', 'moore'}): +Type of neighborhood connectivity.
  • +
  • params (Dict[str, Any]): +Initial global parameter values.
  • +
  • cell_params (Dict[str, Any]): +Initial local per-cell parameters.
  • +
  • seed (int, optional): +Seed for the random number generator.
  • +
+
+ + +
+
+ +
+ rows: int + + + +
+ +
50    @property
+51    def rows(self) -> int:
+52        """int: Number of rows in the grid."""
+53        return getattr(self, "_rows")
+
+ + +

int: Number of rows in the grid.

+
+ + +
+
+ +
+ cols: int + + + +
+ +
55    @property
+56    def cols(self) -> int:
+57        """int: Number of columns in the grid."""
+58        return getattr(self, "_cols")
+
+ + +

int: Number of columns in the grid.

+
+ + +
+
+ +
+ densities: Tuple[float, ...] + + + +
+ +
60    @property
+61    def densities(self) -> Tuple[float, ...]:
+62        """Tuple[float, ...]: Initial density fraction for each species."""
+63        return tuple(getattr(self, "_densities"))
+
+ + +

Tuple[float, ...]: Initial density fraction for each species.

+
+ + +
+
+ +
+ n_species: int + + + +
+ +
66    @property
+67    def n_species(self) -> int:
+68        """int: Number of distinct species states (excluding empty state 0)."""
+69        return int(getattr(self, "_n_species"))
+
+ + +

int: Number of distinct species states (excluding empty state 0).

+
+ + +
+
+
+ params: Dict[str, object] + + +
+ + + + +
+
+
+ cell_params: Dict[str, object] + + +
+ + + + +
+
+
+ species_names: Tuple[str, ...] + + +
+ + + + +
+
+
+ neighborhood: str + + +
+ + + + +
+
+
+ generator: numpy.random._generator.Generator + + +
+ + + + +
+
+
+ grid: numpy.ndarray + + +
+ + + + +
+
+ +
+ + def + validate(self) -> None: + + + +
+ +
162    def validate(self) -> None:
+163        """
+164        Validate core CA invariants and grid dimensions.
+165
+166        Checks that the neighborhood is valid, the grid matches initialized dimensions,
+167        and that local parameter arrays match the grid shape.
+168
+169        Raises
+170        ------
+171        ValueError
+172            If any structural invariant is violated.
+173        """
+174        if self.neighborhood not in ("neumann", "moore"):
+175            raise ValueError("neighborhood must be 'neumann' or 'moore'")
+176
+177        expected_shape = (int(getattr(self, "_rows")), int(getattr(self, "_cols")))
+178        if self.grid.shape != expected_shape:
+179            raise ValueError(
+180                f"grid shape {self.grid.shape} does not match expected {expected_shape}"
+181            )
+182
+183        # Ensure any array in cell_params matches grid shape
+184        for k, v in (self.cell_params or {}).items():
+185            if isinstance(v, np.ndarray) and v.shape != expected_shape:
+186                raise ValueError(f"cell_params['{k}'] must have shape equal to grid")
+
+ + +

Validate core CA invariants and grid dimensions.

+ +

Checks that the neighborhood is valid, the grid matches initialized dimensions, +and that local parameter arrays match the grid shape.

+ +
Raises
+ +
    +
  • ValueError: If any structural invariant is violated.
  • +
+
+ + +
+
+ +
+ + def + evolve( self, param: str, species: Optional[int] = None, sd: float = 0.05, min_val: Optional[float] = None, max_val: Optional[float] = None) -> None: + + + +
+ +
220    def evolve(
+221        self,
+222        param: str,
+223        species: Optional[int] = None,
+224        sd: float = 0.05,
+225        min_val: Optional[float] = None,
+226        max_val: Optional[float] = None,
+227    ) -> None:
+228        """
+229        Enable per-cell evolution for a specific parameter on a given species.
+230
+231        This method initializes a spatial parameter array (local parameter map)
+232        for a global parameter. It allows individual cells to carry their own
+233        values for that parameter, which can then mutate and evolve during
+234        the simulation.
+235
+236        Parameters
+237        ----------
+238        param : str
+239            The name of the global parameter to enable for evolution.
+240            Must exist in `self.params`.
+241        species : int, optional
+242            The 1-based index of the species to which this parameter applies.
+243            If None, the method attempts to infer the species from the
+244            parameter name prefix.
+245        sd : float, default 0.05
+246            The standard deviation of the Gaussian mutation applied during
+247            inheritance/reproduction.
+248        min_val : float, optional
+249            The minimum allowable value for the parameter (clamping).
+250            Defaults to 0.01 if not provided.
+251        max_val : float, optional
+252            The maximum allowable value for the parameter (clamping).
+253            Defaults to 0.99 if not provided.
+254
+255        Raises
+256        ------
+257        ValueError
+258            If the parameter is not in `self.params`, the species cannot be
+259            inferred, or the species index is out of bounds.
+260
+261        Notes
+262        -----
+263        The local parameter is stored in `self.cell_params` as a 2D numpy
+264        array initialized with the current global value for all cells of
+265        the target species, and `NaN` elsewhere.
+266        """
+267        if min_val is None:
+268            min_val = 0.01
+269        if max_val is None:
+270            max_val = 0.99
+271        if param not in self.params:
+272            raise ValueError(f"Unknown parameter '{param}'")
+273        if species is None:
+274            species = self._infer_species_from_param_name(param)
+275            if species is None:
+276                raise ValueError(
+277                    "species must be provided or inferable from param name and species_names"
+278                )
+279        if not isinstance(species, int) or species <= 0 or species > self._n_species:
+280            raise ValueError("species must be an integer between 1 and n_species")
+281
+282        arr = np.full(self.grid.shape, np.nan, dtype=float)
+283        mask = self.grid == int(species)
+284        arr[mask] = float(self.params[param])
+285        self.cell_params[param] = arr
+286        self._evolve_info[param] = {
+287            "sd": float(sd),
+288            "min": float(min_val),
+289            "max": float(max_val),
+290            "species": int(species),
+291        }
+
+ + +

Enable per-cell evolution for a specific parameter on a given species.

+ +

This method initializes a spatial parameter array (local parameter map) +for a global parameter. It allows individual cells to carry their own +values for that parameter, which can then mutate and evolve during +the simulation.

+ +
Parameters
+ +
    +
  • param (str): +The name of the global parameter to enable for evolution. +Must exist in self.params.
  • +
  • species (int, optional): +The 1-based index of the species to which this parameter applies. +If None, the method attempts to infer the species from the +parameter name prefix.
  • +
  • sd (float, default 0.05): +The standard deviation of the Gaussian mutation applied during +inheritance/reproduction.
  • +
  • min_val (float, optional): +The minimum allowable value for the parameter (clamping). +Defaults to 0.01 if not provided.
  • +
  • max_val (float, optional): +The maximum allowable value for the parameter (clamping). +Defaults to 0.99 if not provided.
  • +
+ +
Raises
+ +
    +
  • ValueError: If the parameter is not in self.params, the species cannot be +inferred, or the species index is out of bounds.
  • +
+ +
Notes
+ +

The local parameter is stored in self.cell_params as a 2D numpy +array initialized with the current global value for all cells of +the target species, and NaN elsewhere.

+
+ + +
+
+ +
+ + def + update(self) -> None: + + + +
+ +
293    def update(self) -> None:
+294        """
+295        Perform one update step of the cellular automaton.
+296
+297        This is an abstract method that defines the transition rules of the
+298        system. It must be implemented by concrete subclasses to specify
+299        how cell states and parameters change over time based on their
+300        current state and neighborhood.
+301
+302        Raises
+303        ------
+304        NotImplementedError
+305            If called directly on the base class instead of an implementation.
+306
+307        Returns
+308        -------
+309        None
+310
+311        Notes
+312        -----
+313        In a typical implementation, this method handles the logic for
+314        stochastic transitions, movement, or predator-prey interactions.
+315        """
+316        raise NotImplementedError(
+317            "Override update() in a subclass to define CA dynamics"
+318        )
+
+ + +

Perform one update step of the cellular automaton.

+ +

This is an abstract method that defines the transition rules of the +system. It must be implemented by concrete subclasses to specify +how cell states and parameters change over time based on their +current state and neighborhood.

+ +
Raises
+ +
    +
  • NotImplementedError: If called directly on the base class instead of an implementation.
  • +
+ +
Returns
+ +
    +
  • None
  • +
+ +
Notes
+ +

In a typical implementation, this method handles the logic for +stochastic transitions, movement, or predator-prey interactions.

+
+ + +
+
+ +
+ + def + run( self, steps: int, stop_evolution_at: Optional[int] = None, snapshot_iters: Optional[list] = None) -> None: + + + +
+ +
320    def run(
+321        self,
+322        steps: int,
+323        stop_evolution_at: Optional[int] = None,
+324        snapshot_iters: Optional[list] = None,
+325    ) -> None:
+326        """
+327        Execute the cellular automaton simulation for a specified number of steps.
+328
+329        This method drives the simulation loop, calling `update()` at each
+330        iteration. It manages visualization updates, directory creation for
+331        data persistence, and handles the freezing of evolving parameters
+332        at a specific time step.
+333
+334        Parameters
+335        ----------
+336        steps : int
+337            The total number of iterations to run (must be non-negative).
+338        stop_evolution_at : int, optional
+339            The 1-based iteration index after which parameter mutation is
+340            disabled. Useful for observing system stability after a period
+341            of adaptation.
+342        snapshot_iters : List[int], optional
+343            A list of specific 1-based iteration indices at which to save
+344            the current grid state to the results directory.
+345
+346        Returns
+347        -------
+348        None
+349
+350        Notes
+351        -----
+352        If snapshots are requested, a results directory is automatically created
+353        using a timestamped subfolder (e.g., 'results/run-1700000000/').
+354        Visualization errors are logged but do not terminate the simulation.
+355        """
+356        assert (
+357            isinstance(steps, int) and steps >= 0
+358        ), "steps must be a non-negative integer"
+359
+360        # normalize snapshot iteration list
+361        snapshot_set = set(snapshot_iters) if snapshot_iters is not None else set()
+362
+363        for i in range(steps):
+364            self.update()
+365            # Update visualization if enabled every `interval` iterations
+366            if getattr(self, "_viz_on", False):
+367                # iteration number is 1-based for display
+368                try:
+369                    self._viz_update(i + 1)
+370                except Exception:
+371                    # Log visualization errors but don't stop the simulation
+372                    logger.exception(
+373                        "Visualization update failed at iteration %d", i + 1
+374                    )
+375
+376            # create snapshots if requested at this iteration
+377            if (i + 1) in snapshot_set:
+378                try:
+379                    # create snapshot folder if not present
+380                    if (
+381                        not hasattr(self, "_viz_snapshot_dir")
+382                        or self._viz_snapshot_dir is None
+383                    ):
+384                        import os, time
+385
+386                        base = "results"
+387                        ts = int(time.time())
+388                        run_folder = f"run-{ts}"
+389                        full = os.path.join(base, run_folder)
+390                        os.makedirs(full, exist_ok=True)
+391                        self._viz_snapshot_dir = full
+392                    self._viz_save_snapshot(i + 1)
+393                except (OSError, PermissionError):
+394                    logger.exception(
+395                        "Failed to create or write snapshot at iteration %d", i + 1
+396                    )
+397
+398            # stop evolution at specified time-step (disable further evolution)
+399            if stop_evolution_at is not None and (i + 1) == int(stop_evolution_at):
+400                # mark evolution as stopped; do not erase evolve metadata so
+401                # deterministic inheritance can still use parent values
+402                self._evolution_stopped = True
+
+ + +

Execute the cellular automaton simulation for a specified number of steps.

+ +

This method drives the simulation loop, calling update() at each +iteration. It manages visualization updates, directory creation for +data persistence, and handles the freezing of evolving parameters +at a specific time step.

+ +
Parameters
+ +
    +
  • steps (int): +The total number of iterations to run (must be non-negative).
  • +
  • stop_evolution_at (int, optional): +The 1-based iteration index after which parameter mutation is +disabled. Useful for observing system stability after a period +of adaptation.
  • +
  • snapshot_iters (List[int], optional): +A list of specific 1-based iteration indices at which to save +the current grid state to the results directory.
  • +
+ +
Returns
+ +
    +
  • None
  • +
+ +
Notes
+ +

If snapshots are requested, a results directory is automatically created +using a timestamped subfolder (e.g., 'results/run-1700000000/'). +Visualization errors are logged but do not terminate the simulation.

+
+ + +
+
+
+ +
+ + class + PP(CA): + + + +
+ +
405class PP(CA):
+406    """
+407    Predator-Prey Cellular Automaton model with Numba-accelerated kernels.
+408
+409    This model simulates a stochastic predator-prey system where species
+410    interact on a 2D grid. It supports evolving per-cell death rates,
+411    periodic boundary conditions, and both random and directed hunting
+412    behaviors.
+413
+414    Parameters
+415    ----------
+416    rows : int, default 10
+417        Number of rows in the simulation grid.
+418    cols : int, default 10
+419        Number of columns in the simulation grid.
+420    densities : Tuple[float, ...], default (0.2, 0.1)
+421        Initial population densities for (prey, predator).
+422    neighborhood : {'moore', 'neumann'}, default 'moore'
+423        The neighborhood type for cell interactions.
+424    params : Dict[str, object], optional
+425        Global parameters: "prey_death", "predator_death", "prey_birth",
+426        "predator_birth".
+427    cell_params : Dict[str, object], optional
+428        Initial local parameter maps (2D arrays).
+429    seed : int, optional
+430        Random seed for reproducibility.
+431    synchronous : bool, default True
+432        If True, updates the entire grid at once. If False, updates
+433        cells asynchronously.
+434    directed_hunting : bool, default False
+435        If True, predators selectively hunt prey rather than choosing
+436        neighbors at random.
+437
+438    Attributes
+439    ----------
+440    species_names : Tuple[str, ...]
+441        Labels for the species ('prey', 'predator').
+442    synchronous : bool
+443        Current update mode.
+444    directed_hunting : bool
+445        Current hunting strategy logic.
+446    """
+447
+448    # Default colors: 0=empty black, 1=prey green, 2=predator red
+449    _default_cmap = ("black", "green", "red")
+450
+451    def __init__(
+452        self,
+453        rows: int = 10,
+454        cols: int = 10,
+455        densities: Tuple[float, ...] = (0.2, 0.1),
+456        neighborhood: str = "moore",
+457        params: Dict[str, object] = None,
+458        cell_params: Dict[str, object] = None,
+459        seed: Optional[int] = None,
+460        synchronous: bool = True,
+461        directed_hunting: bool = False,  # New directed hunting option
+462    ) -> None:
+463        """
+464        Initialize the Predator-Prey CA with validated parameters and kernels.
+465        """
+466        # Allowed params and defaults
+467        _defaults = {
+468            "prey_death": 0.05,
+469            "predator_death": 0.1,
+470            "prey_birth": 0.25,
+471            "predator_birth": 0.2,
+472        }
+473
+474        # Validate user-supplied params: only allowed keys
+475        if params is None:
+476            merged_params = dict(_defaults)
+477        else:
+478            if not isinstance(params, dict):
+479                raise TypeError("params must be a dict or None")
+480            extra = set(params.keys()) - set(_defaults.keys())
+481            if extra:
+482                raise ValueError(f"Unexpected parameter keys: {sorted(list(extra))}")
+483            # Do not override user-set values: start from defaults then update with user values
+484            merged_params = dict(_defaults)
+485            merged_params.update(params)
+486
+487        # Validate numerical ranges
+488        for k, v in merged_params.items():
+489            if not isinstance(v, (int, float)):
+490                raise TypeError(f"Parameter '{k}' must be a number between 0 and 1")
+491            if not (0.0 <= float(v) <= 1.0):
+492                raise ValueError(f"Parameter '{k}' must be between 0 and 1")
+493
+494        # Call base initializer with merged params
+495        super().__init__(
+496            rows, cols, densities, neighborhood, merged_params, cell_params, seed
+497        )
+498
+499        self.synchronous: bool = bool(synchronous)
+500        self.directed_hunting: bool = bool(directed_hunting)
+501
+502        # set human-friendly species names for PP
+503        self.species_names = ("prey", "predator")
+504
+505        if seed is not None:
+506            # This sets the seed for all @njit functions globally
+507            set_numba_seed(seed)
+508
+509        self._kernel = PPKernel(
+510            rows, cols, neighborhood, directed_hunting=directed_hunting
+511        )
+512
+513    # Remove PP-specific evolve wrapper; use CA.evolve with optional species
+514
+515    def validate(self) -> None:
+516        """
+517        Validate Predator-Prey specific invariants and spatial parameter arrays.
+518
+519        Extends the base CA validation to ensure that numerical parameters are
+520        within the [0, 1] probability range and that evolved parameter maps
+521        (e.g., prey_death) correctly align with the species locations.
+522
+523        Raises
+524        ------
+525        ValueError
+526            If grid shapes, parameter ranges, or species masks are inconsistent.
+527        TypeError
+528            If parameters are non-numeric.
+529        """
+530        super().validate()
+531
+532        # Validate global params
+533        for k, v in (self.params or {}).items():
+534            if not isinstance(v, (int, float)):
+535                raise TypeError(f"Parameter '{k}' must be numeric")
+536            if not (0.0 <= float(v) <= 1.0):
+537                raise ValueError(f"Parameter '{k}' must be between 0 and 1")
+538
+539        # Validate per-cell evolve arrays
+540        for pname, meta in (self._evolve_info or {}).items():
+541            arr = self.cell_params.get(pname)
+542            if not isinstance(arr, np.ndarray):
+543                # absent or non-array per-cell params are allowed; skip
+544                continue
+545            # shape already checked in super().validate(), but be explicit
+546            if arr.shape != self.grid.shape:
+547                raise ValueError(f"cell_params['{pname}'] must match grid shape")
+548            # expected non-NaN positions correspond to species stored in metadata
+549            species = None
+550            if isinstance(meta, dict) and "species" in meta:
+551                species = int(meta.get("species"))
+552            else:
+553                # try to infer species from parameter name using species_names
+554                species = self._infer_species_from_param_name(pname)
+555                if species is None:
+556                    raise ValueError(
+557                        f"cell_params['{pname}'] missing species metadata and could not infer from name"
+558                    )
+559            nonnan = ~np.isnan(arr)
+560            expected = self.grid == species
+561            if not np.array_equal(nonnan, expected):
+562                raise ValueError(
+563                    f"cell_params['{pname}'] non-NaN entries must match positions of species {species}"
+564                )
+565            # values must be within configured range where not NaN
+566            mn = float(meta.get("min", 0.0))
+567            mx = float(meta.get("max", 1.0))
+568            vals = arr[~np.isnan(arr)]
+569            if vals.size > 0:
+570                if np.any(vals < mn) or np.any(vals > mx):
+571                    raise ValueError(
+572                        f"cell_params['{pname}'] contains values outside [{mn}, {mx}]"
+573                    )
+574
+575    def update_async(self) -> None:
+576        """
+577        Execute an asynchronous update using the optimized Numba kernel.
+578
+579        This method retrieves the evolved parameter maps and delegates the
+580        stochastic transitions to the `PPKernel`. Asynchronous updates
+581        typically handle cell-by-cell logic where changes can be
+582        immediately visible to neighbors.
+583        """
+584        # Get the evolved prey death map
+585        # Fallback to a full array of the global param if it doesn't exist yet
+586        p_death_arr = self.cell_params.get("prey_death")
+587        if p_death_arr is None:
+588            p_death_arr = np.full(
+589                self.grid.shape, self.params["prey_death"], dtype=np.float64
+590            )
+591
+592        meta = self._evolve_info.get(
+593            "prey_death", {"sd": 0.05, "min": 0.001, "max": 0.1}
+594        )
+595
+596        # Call the optimized kernel (uses pre-allocated buffers)
+597        self._kernel.update(
+598            self.grid,
+599            p_death_arr,
+600            float(self.params["prey_birth"]),
+601            float(self.params["prey_death"]),
+602            float(self.params["predator_birth"]),
+603            float(self.params["predator_death"]),
+604            float(meta["sd"]),
+605            float(meta["min"]),
+606            float(meta["max"]),
+607            self._evolution_stopped,
+608        )
+609
+610    def update(self) -> None:
+611        """
+612        Dispatch the simulation step based on the configured update mode.
+613        """
+614        if self.synchronous:
+615            self.update_sync()
+616        else:
+617            self.update_async()
+
+ + +

Predator-Prey Cellular Automaton model with Numba-accelerated kernels.

+ +

This model simulates a stochastic predator-prey system where species +interact on a 2D grid. It supports evolving per-cell death rates, +periodic boundary conditions, and both random and directed hunting +behaviors.

+ +
Parameters
+ +
    +
  • rows (int, default 10): +Number of rows in the simulation grid.
  • +
  • cols (int, default 10): +Number of columns in the simulation grid.
  • +
  • densities (Tuple[float, ...], default (0.2, 0.1)): +Initial population densities for (prey, predator).
  • +
  • neighborhood ({'moore', 'neumann'}, default 'moore'): +The neighborhood type for cell interactions.
  • +
  • params (Dict[str, object], optional): +Global parameters: "prey_death", "predator_death", "prey_birth", +"predator_birth".
  • +
  • cell_params (Dict[str, object], optional): +Initial local parameter maps (2D arrays).
  • +
  • seed (int, optional): +Random seed for reproducibility.
  • +
  • synchronous (bool, default True): +If True, updates the entire grid at once. If False, updates +cells asynchronously.
  • +
  • directed_hunting (bool, default False): +If True, predators selectively hunt prey rather than choosing +neighbors at random.
  • +
+ +
Attributes
+ +
    +
  • species_names (Tuple[str, ...]): +Labels for the species ('prey', 'predator').
  • +
  • synchronous (bool): +Current update mode.
  • +
  • directed_hunting (bool): +Current hunting strategy logic.
  • +
+
+ + +
+ +
+ + PP( rows: int = 10, cols: int = 10, densities: Tuple[float, ...] = (0.2, 0.1), neighborhood: str = 'moore', params: Dict[str, object] = None, cell_params: Dict[str, object] = None, seed: Optional[int] = None, synchronous: bool = True, directed_hunting: bool = False) + + + +
+ +
451    def __init__(
+452        self,
+453        rows: int = 10,
+454        cols: int = 10,
+455        densities: Tuple[float, ...] = (0.2, 0.1),
+456        neighborhood: str = "moore",
+457        params: Dict[str, object] = None,
+458        cell_params: Dict[str, object] = None,
+459        seed: Optional[int] = None,
+460        synchronous: bool = True,
+461        directed_hunting: bool = False,  # New directed hunting option
+462    ) -> None:
+463        """
+464        Initialize the Predator-Prey CA with validated parameters and kernels.
+465        """
+466        # Allowed params and defaults
+467        _defaults = {
+468            "prey_death": 0.05,
+469            "predator_death": 0.1,
+470            "prey_birth": 0.25,
+471            "predator_birth": 0.2,
+472        }
+473
+474        # Validate user-supplied params: only allowed keys
+475        if params is None:
+476            merged_params = dict(_defaults)
+477        else:
+478            if not isinstance(params, dict):
+479                raise TypeError("params must be a dict or None")
+480            extra = set(params.keys()) - set(_defaults.keys())
+481            if extra:
+482                raise ValueError(f"Unexpected parameter keys: {sorted(list(extra))}")
+483            # Do not override user-set values: start from defaults then update with user values
+484            merged_params = dict(_defaults)
+485            merged_params.update(params)
+486
+487        # Validate numerical ranges
+488        for k, v in merged_params.items():
+489            if not isinstance(v, (int, float)):
+490                raise TypeError(f"Parameter '{k}' must be a number between 0 and 1")
+491            if not (0.0 <= float(v) <= 1.0):
+492                raise ValueError(f"Parameter '{k}' must be between 0 and 1")
+493
+494        # Call base initializer with merged params
+495        super().__init__(
+496            rows, cols, densities, neighborhood, merged_params, cell_params, seed
+497        )
+498
+499        self.synchronous: bool = bool(synchronous)
+500        self.directed_hunting: bool = bool(directed_hunting)
+501
+502        # set human-friendly species names for PP
+503        self.species_names = ("prey", "predator")
+504
+505        if seed is not None:
+506            # This sets the seed for all @njit functions globally
+507            set_numba_seed(seed)
+508
+509        self._kernel = PPKernel(
+510            rows, cols, neighborhood, directed_hunting=directed_hunting
+511        )
+
+ + +

Initialize the Predator-Prey CA with validated parameters and kernels.

+
+ + +
+
+
+ synchronous: bool + + +
+ + + + +
+
+
+ directed_hunting: bool + + +
+ + + + +
+
+
+ species_names + + +
+ + + + +
+
+ +
+ + def + validate(self) -> None: + + + +
+ +
515    def validate(self) -> None:
+516        """
+517        Validate Predator-Prey specific invariants and spatial parameter arrays.
+518
+519        Extends the base CA validation to ensure that numerical parameters are
+520        within the [0, 1] probability range and that evolved parameter maps
+521        (e.g., prey_death) correctly align with the species locations.
+522
+523        Raises
+524        ------
+525        ValueError
+526            If grid shapes, parameter ranges, or species masks are inconsistent.
+527        TypeError
+528            If parameters are non-numeric.
+529        """
+530        super().validate()
+531
+532        # Validate global params
+533        for k, v in (self.params or {}).items():
+534            if not isinstance(v, (int, float)):
+535                raise TypeError(f"Parameter '{k}' must be numeric")
+536            if not (0.0 <= float(v) <= 1.0):
+537                raise ValueError(f"Parameter '{k}' must be between 0 and 1")
+538
+539        # Validate per-cell evolve arrays
+540        for pname, meta in (self._evolve_info or {}).items():
+541            arr = self.cell_params.get(pname)
+542            if not isinstance(arr, np.ndarray):
+543                # absent or non-array per-cell params are allowed; skip
+544                continue
+545            # shape already checked in super().validate(), but be explicit
+546            if arr.shape != self.grid.shape:
+547                raise ValueError(f"cell_params['{pname}'] must match grid shape")
+548            # expected non-NaN positions correspond to species stored in metadata
+549            species = None
+550            if isinstance(meta, dict) and "species" in meta:
+551                species = int(meta.get("species"))
+552            else:
+553                # try to infer species from parameter name using species_names
+554                species = self._infer_species_from_param_name(pname)
+555                if species is None:
+556                    raise ValueError(
+557                        f"cell_params['{pname}'] missing species metadata and could not infer from name"
+558                    )
+559            nonnan = ~np.isnan(arr)
+560            expected = self.grid == species
+561            if not np.array_equal(nonnan, expected):
+562                raise ValueError(
+563                    f"cell_params['{pname}'] non-NaN entries must match positions of species {species}"
+564                )
+565            # values must be within configured range where not NaN
+566            mn = float(meta.get("min", 0.0))
+567            mx = float(meta.get("max", 1.0))
+568            vals = arr[~np.isnan(arr)]
+569            if vals.size > 0:
+570                if np.any(vals < mn) or np.any(vals > mx):
+571                    raise ValueError(
+572                        f"cell_params['{pname}'] contains values outside [{mn}, {mx}]"
+573                    )
+
+ + +

Validate Predator-Prey specific invariants and spatial parameter arrays.

+ +

Extends the base CA validation to ensure that numerical parameters are +within the [0, 1] probability range and that evolved parameter maps +(e.g., prey_death) correctly align with the species locations.

+ +
Raises
+ +
    +
  • ValueError: If grid shapes, parameter ranges, or species masks are inconsistent.
  • +
  • TypeError: If parameters are non-numeric.
  • +
+
+ + +
+
+ +
+ + def + update_async(self) -> None: + + + +
+ +
575    def update_async(self) -> None:
+576        """
+577        Execute an asynchronous update using the optimized Numba kernel.
+578
+579        This method retrieves the evolved parameter maps and delegates the
+580        stochastic transitions to the `PPKernel`. Asynchronous updates
+581        typically handle cell-by-cell logic where changes can be
+582        immediately visible to neighbors.
+583        """
+584        # Get the evolved prey death map
+585        # Fallback to a full array of the global param if it doesn't exist yet
+586        p_death_arr = self.cell_params.get("prey_death")
+587        if p_death_arr is None:
+588            p_death_arr = np.full(
+589                self.grid.shape, self.params["prey_death"], dtype=np.float64
+590            )
+591
+592        meta = self._evolve_info.get(
+593            "prey_death", {"sd": 0.05, "min": 0.001, "max": 0.1}
+594        )
+595
+596        # Call the optimized kernel (uses pre-allocated buffers)
+597        self._kernel.update(
+598            self.grid,
+599            p_death_arr,
+600            float(self.params["prey_birth"]),
+601            float(self.params["prey_death"]),
+602            float(self.params["predator_birth"]),
+603            float(self.params["predator_death"]),
+604            float(meta["sd"]),
+605            float(meta["min"]),
+606            float(meta["max"]),
+607            self._evolution_stopped,
+608        )
+
+ + +

Execute an asynchronous update using the optimized Numba kernel.

+ +

This method retrieves the evolved parameter maps and delegates the +stochastic transitions to the PPKernel. Asynchronous updates +typically handle cell-by-cell logic where changes can be +immediately visible to neighbors.

+
+ + +
+
+ +
+ + def + update(self) -> None: + + + +
+ +
610    def update(self) -> None:
+611        """
+612        Dispatch the simulation step based on the configured update mode.
+613        """
+614        if self.synchronous:
+615            self.update_sync()
+616        else:
+617            self.update_async()
+
+ + +

Dispatch the simulation step based on the configured update mode.

+
+ + +
+
+
Inherited Members
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/docs/models/config.html b/docs/models/config.html new file mode 100644 index 0000000..2870f45 --- /dev/null +++ b/docs/models/config.html @@ -0,0 +1,2057 @@ + + + + + + + models.config API documentation + + + + + + + + + +
+
+

+models.config

+ +

Configuration for Predator-Prey Hydra Effect Experiments

+ +

Single Config dataclass with pre-defined instances for each experimental phase.

+ +

Usage: + from config import PHASE1_CONFIG, PHASE2_CONFIG, Config

+ +
# Use pre-defined config
+cfg = PHASE1_CONFIG
+
+# Or create custom config
+cfg = Config(grid_size=150, n_replicates=20)
+
+# Or modify existing
+cfg = Config(**{**asdict(PHASE1_CONFIG), 'n_replicates': 30})
+
+
+ + + + + +
  1#!/usr/bin/env python3
+  2"""
+  3Configuration for Predator-Prey Hydra Effect Experiments
+  4
+  5Single Config dataclass with pre-defined instances for each experimental phase.
+  6
+  7Usage:
+  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
+ 20from dataclasses import dataclass, field, asdict
+ 21from typing import Tuple, Optional
+ 22import numpy as np
+ 23
+ 24# FIXME: Tidy up config file for submission
+ 25
+ 26
+ 27@dataclass
+ 28class Config:
+ 29    """
+ 30    Central configuration for Predator-Prey Hydra Effect experiments.
+ 31
+ 32    This dataclass aggregates all hyperparameters, grid settings, and
+ 33    experimental phase definitions. It includes helper methods for
+ 34    parameter sweep generation and runtime estimation.
+ 35
+ 36    Attributes
+ 37    ----------
+ 38    grid_size : int, default 1000
+ 39        The side length of the square simulation grid.
+ 40    densities : Tuple[float, float], default (0.30, 0.15)
+ 41        Initial population fractions for (prey, predator).
+ 42    grid_sizes : Tuple[int, ...], default (50, 100, 250, 500, 1000, 2500)
+ 43        Grid dimensions used for Finite-Size Scaling (FSS) analysis.
+ 44    prey_birth : float, default 0.2
+ 45        Default global birth rate for the prey species.
+ 46    prey_death : float, default 0.05
+ 47        Default global death rate for the prey species.
+ 48    predator_birth : float, default 0.8
+ 49        Default global birth rate for the predator species.
+ 50    predator_death : float, default 0.05
+ 51        Default global death rate for the predator species.
+ 52    critical_prey_birth : float, default 0.20
+ 53        Identified critical birth rate for prey (Phase 1 result).
+ 54    critical_prey_death : float, default 0.947
+ 55        Identified critical death rate for prey (Phase 1 result).
+ 56    prey_death_range : Tuple[float, float], default (0.0, 0.2)
+ 57        Bounds for the prey death rate sweep in Phase 1.
+ 58    n_prey_birth : int, default 15
+ 59        Number of points along the prey birth rate axis for sweeps.
+ 60    n_prey_death : int, default 5
+ 61        Number of points along the prey death rate axis for sweeps.
+ 62    predator_birth_values : Tuple[float, ...]
+ 63        Discrete predator birth rates used for sensitivity analysis.
+ 64    predator_death_values : Tuple[float, ...]
+ 65        Discrete predator death rates used for sensitivity analysis.
+ 66    prey_death_offsets : Tuple[float, ...]
+ 67        Delta values applied to critical death rates for perturbation tests.
+ 68    n_replicates : int, default 15
+ 69        Number of independent stochastic runs per parameter set.
+ 70    warmup_steps : int, default 300
+ 71        Iterations to run before beginning data collection.
+ 72    measurement_steps : int, default 500
+ 73        Iterations spent collecting statistics after warmup.
+ 74    with_evolution : bool, default False
+ 75        Toggle for enabling per-cell parameter mutation.
+ 76    evolve_sd : float, default 0.10
+ 77        Standard deviation for parameter mutation (Gaussian).
+ 78    evolve_min : float, default 0.0
+ 79        Lower bound clamp for evolving parameters.
+ 80    evolve_max : float, default 0.10
+ 81        Upper bound clamp for evolving parameters.
+ 82    sensitivity_sd_values : Tuple[float, ...]
+ 83        Range of mutation strengths tested in sensitivity phases.
+ 84    synchronous : bool, default False
+ 85        If True, use synchronous grid updates (not recommended).
+ 86    directed_hunting : bool, default False
+ 87        Toggle for targeted predator movement logic.
+ 88    directed_hunting_values : Tuple[bool, ...]
+ 89        Options compared during Phase 6 extensions.
+ 90    save_timeseries : bool, default False
+ 91        Toggle for recording step-by-step population data.
+ 92    timeseries_subsample : int, default 10
+ 93        Frequency of temporal data points (e.g., every 10 steps).
+ 94    collect_pcf : bool, default True
+ 95        Toggle for Pair Correlation Function analysis.
+ 96    pcf_sample_rate : float, default 0.2
+ 97        Probability that a specific replicate will compute PCFs.
+ 98    pcf_max_distance : float, default 20.0
+ 99        Maximum radial distance for spatial correlation analysis.
+100    pcf_n_bins : int, default 20
+101        Number of bins in the PCF histogram.
+102    min_density_for_analysis : float, default 0.002
+103        Population threshold below which spatial analysis is skipped.
+104    perturbation_magnitude : float, default 0.1
+105        Strength of external shocks applied in Phase 5.
+106    n_jobs : int, default -1
+107        Number of CPU cores for parallelization (-1 uses all available).
+108    """
+109
+110    # Grid settings
+111    grid_size: int = 1000
+112    densities: Tuple[float, float] = (
+113        0.30,
+114        0.15,
+115    )  # (prey, predator)
+116
+117    # For FSS experiments: multiple grid sizes
+118    grid_sizes: Tuple[int, ...] = (50, 100, 250, 500, 1000, 2500)
+119
+120    # Default/fixed parameters
+121    prey_birth: float = 0.2
+122    prey_death: float = 0.05
+123    predator_birth: float = 0.8
+124    predator_death: float = 0.05
+125
+126    # Critical point (UPDATE AFTER PHASE 1)
+127    critical_prey_birth: float = 0.20
+128    critical_prey_death: float = 0.947
+129
+130    # Prey parameter sweep (Phase 1)
+131    prey_death_range: Tuple[float, float] = (0.0, 0.2)
+132    n_prey_birth: int = 15
+133    n_prey_death: int = 5
+134
+135    # Predator parameter sweep (Phase 4 sensitivity)
+136    predator_birth_values: Tuple[float, ...] = (
+137        0.15,
+138        0.20,
+139        0.25,
+140        0.30,
+141    )
+142    predator_death_values: Tuple[float, ...] = (
+143        0.05,
+144        0.10,
+145        0.15,
+146        0.20,
+147    )
+148
+149    # Perturbation offsets from critical point (Phase 5)
+150    prey_death_offsets: Tuple[float, ...] = (
+151        -0.02,
+152        -0.01,
+153        0.0,
+154        0.01,
+155        0.02,
+156    )
+157
+158    # Number of replicates per parameter configuration
+159    n_replicates: int = 15
+160
+161    # Simulation steps
+162    warmup_steps: int = 300
+163    measurement_steps: int = 500
+164
+165    # Evo
+166    with_evolution: bool = False
+167    evolve_sd: float = 0.10
+168    evolve_min: float = 0.0
+169    evolve_max: float = 0.10
+170
+171    # Sensitivity: mutation strength values to test
+172    sensitivity_sd_values: Tuple[float, ...] = (
+173        0.02,
+174        0.05,
+175        0.10,
+176        0.15,
+177        0.20,
+178    )
+179
+180    # Update mode
+181    synchronous: bool = False  # Always False for this model
+182    directed_hunting: bool = False
+183
+184    # For Phase 6: compare model variants
+185    directed_hunting_values: Tuple[bool, ...] = (False, True)
+186
+187    # Temporal data collection (time series)
+188    save_timeseries: bool = False
+189    timeseries_subsample: int = 10
+190
+191    # PCF settings
+192    collect_pcf: bool = True
+193    pcf_sample_rate: float = 0.2  # Fraction of runs to compute PCF
+194    pcf_max_distance: float = 20.0
+195    pcf_n_bins: int = 20
+196
+197    # Cluster analysis
+198    min_density_for_analysis: float = (
+199        0.002  # FIXME: Minimum prey density (fraction of grid) to analyze clusters/PCF
+200    )
+201
+202    # Perturbation settings (Phase 5)
+203    perturbation_magnitude: float = (
+204        0.1  # FIXME: Fractional change to apply at perturbation time
+205    )
+206
+207    # Parallelization
+208    n_jobs: int = -1  # Use all available cores by default
+209
+210    # Helpers
+211    def get_prey_births(self) -> np.ndarray:
+212        """
+213        Generate a linear range of prey birth rates for experimental sweeps.
+214
+215        Returns
+216        -------
+217        np.ndarray
+218            1D array of birth rates based on `prey_birth_range` and `n_prey_birth`.
+219        """
+220        return np.linspace(
+221            self.prey_birth_range[0], self.prey_birth_range[1], self.n_prey_birth
+222        )
+223
+224    def get_prey_deaths(self) -> np.ndarray:
+225        """
+226        Generate a linear range of prey death rates for experimental sweeps.
+227
+228        Returns
+229        -------
+230        np.ndarray
+231            1D array of death rates based on `prey_death_range` and `n_prey_death`.
+232        """
+233        return np.linspace(
+234            self.prey_death_range[0], self.prey_death_range[1], self.n_prey_death
+235        )
+236
+237    def get_warmup_steps(
+238        self, L: int
+239    ) -> int:  # FIXME: This method will be updated depending on Sary's results.
+240        """
+241        Calculate the required warmup steps scaled by grid size.
+242
+243        Parameters
+244        ----------
+245        L : int
+246            The side length of the current grid.
+247
+248        Returns
+249        -------
+250        int
+251            The number of steps to discard before measurement.
+252        """
+253        return self.warmup_steps
+254
+255    def get_measurement_steps(self, L: int) -> int:
+256        """
+257        Determine the number of measurement steps based on the grid side length.
+258
+259        This method allows for dynamic scaling of data collection duration relative
+260        to the system size. Currently, it returns a fixed value, but it is
+261        designed to be overridden for studies where measurement time must
+262        scale with the grid size (e.g., $L^z$ scaling in critical dynamics).
+263
+264        Parameters
+265        ----------
+266        L : int
+267            The side length of the current simulation grid.
+268
+269        Returns
+270        -------
+271        int
+272            The number of iterations to perform for statistical measurement.
+273        """
+274        return self.measurement_steps
+275
+276    def estimate_runtime(self, n_cores: int = 32) -> str:
+277        """
+278        Estimate the wall-clock time required to complete the experiment.
+279
+280        Calculations account for grid size scaling, PCF overhead,
+281        replicate counts, and available parallel resources.
+282
+283        Parameters
+284        ----------
+285        n_cores : int, default 32
+286            The number of CPU cores available for execution.
+287
+288        Returns
+289        -------
+290        str
+291            A human-readable summary of simulation count and estimated hours.
+292        """
+293        # Benchmark: ~1182 steps/sec for 100x100 grid
+294        ref_size = 100
+295        ref_steps_per_sec = 1182
+296
+297        size_scaling = (self.grid_size / ref_size) ** 2
+298        actual_steps_per_sec = ref_steps_per_sec / size_scaling
+299
+300        total_steps = self.warmup_steps + self.measurement_steps
+301        base_time_s = total_steps / actual_steps_per_sec
+302
+303        # PCF overhead (~8ms for 100x100)
+304        pcf_time_s = (0.008 * size_scaling) if self.collect_pcf else 0
+305
+306        # Count simulations
+307        n_sims = self.n_prey_birth * self.n_prey_death * self.n_replicates
+308        if self.with_evolution:
+309            n_sims *= 2  # Both evo and non-evo runs
+310
+311        total_seconds = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate)
+312        total_seconds /= n_cores
+313
+314        hours = total_seconds / 3600
+315        core_hours = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate) / 3600
+316
+317        return f"{n_sims:,} sims, ~{hours:.1f}h on {n_cores} cores (~{core_hours:.0f} core-hours)"
+318
+319
+320############################################################################################
+321# Experimental Phase Configurations
+322############################################################################################
+323
+324
+325PHASE1_CONFIG = Config(
+326    grid_size=1000,
+327    n_prey_death=20,
+328    prey_birth=0.2,
+329    prey_death_range=(0.0963, 0.0973),
+330    predator_birth=0.8,
+331    predator_death=0.05,
+332    n_replicates=30,
+333    warmup_steps=1000,
+334    measurement_steps=1000,
+335    collect_pcf=False,
+336    pcf_sample_rate=0.2,
+337    save_timeseries=False,
+338    directed_hunting=False,
+339)
+340
+341# Phase 2: Self-organization (evolution toward criticality)
+342PHASE2_CONFIG = Config(
+343    grid_size=1000,
+344    n_prey_birth=1,  # Fixed at cfg.prey_birth (0.2)
+345    n_replicates=10,
+346    warmup_steps=1000,  # Shorter warmup (evolution starts immediately)
+347    measurement_steps=10000,  # Longer measurement to see convergence
+348    # Evolution settings
+349    with_evolution=True,
+350    evolve_sd=0.01,  # Smaller mutation rate for smoother convergence
+351    evolve_min=0.0,
+352    evolve_max=0.20,  # Allow full range
+353    collect_pcf=False,
+354    save_timeseries=False,  # Track evolution trajectory
+355)
+356
+357# Phase 3: Finite-size scaling at critical point
+358PHASE3_CONFIG = Config(
+359    grid_sizes=(50, 100, 250, 500, 1000, 2500),
+360    n_replicates=20,
+361    warmup_steps=1000,
+362    measurement_steps=1000,
+363    critical_prey_birth=0.20,  # Add explicitly
+364    critical_prey_death=0.947,  # Add explicitly - verify from Phase 1!
+365    collect_pcf=True,
+366    pcf_sample_rate=1.0,
+367    save_timeseries=False,
+368    with_evolution=False,
+369    directed_hunting=False,
+370)
+371
+372# Phase 4: Sensitivity analysis
+373PHASE4_CONFIG = Config(
+374    grid_size=250,  # As requested
+375    n_replicates=10,  # As requested
+376    warmup_steps=500,  # As requested
+377    measurement_steps=500,  # As requested
+378    with_evolution=False,
+379    collect_pcf=False,
+380    save_timeseries=False,
+381    timeseries_subsample=10,
+382    directed_hunting=False,
+383)
+384
+385# Phase 6: Model extensions (directed reproduction); same config as phase 4 but with directed reproduction
+386PHASE5_CONFIG = Config(
+387    grid_size=250,
+388    n_replicates=10,
+389    warmup_steps=500,
+390    measurement_steps=500,
+391    with_evolution=False,
+392    collect_pcf=False,
+393    save_timeseries=False,
+394    timeseries_subsample=10,
+395    directed_hunting=True,
+396)
+397
+398PHASE_CONFIGS = {
+399    1: PHASE1_CONFIG,
+400    2: PHASE2_CONFIG,
+401    3: PHASE3_CONFIG,
+402    4: PHASE4_CONFIG,
+403    5: PHASE5_CONFIG,
+404}
+405
+406
+407def get_phase_config(phase: int) -> Config:
+408    """
+409    Retrieve the configuration object for a specific experimental phase.
+410
+411    Parameters
+412    ----------
+413    phase : int
+414        The phase number (1 through 6) to retrieve.
+415
+416    Returns
+417    -------
+418    Config
+419        The configuration instance associated with the requested phase.
+420
+421    Raises
+422    ------
+423    ValueError
+424        If the phase number is not found in the pre-defined PHASE_CONFIGS.
+425    """
+426    if phase not in PHASE_CONFIGS:
+427        raise ValueError(
+428            f"Unknown phase {phase}. Valid phases: {list(PHASE_CONFIGS.keys())}"
+429        )
+430    return PHASE_CONFIGS[phase]
+
+ + +
+
+ +
+
@dataclass
+ + class + Config: + + + +
+ +
 28@dataclass
+ 29class Config:
+ 30    """
+ 31    Central configuration for Predator-Prey Hydra Effect experiments.
+ 32
+ 33    This dataclass aggregates all hyperparameters, grid settings, and
+ 34    experimental phase definitions. It includes helper methods for
+ 35    parameter sweep generation and runtime estimation.
+ 36
+ 37    Attributes
+ 38    ----------
+ 39    grid_size : int, default 1000
+ 40        The side length of the square simulation grid.
+ 41    densities : Tuple[float, float], default (0.30, 0.15)
+ 42        Initial population fractions for (prey, predator).
+ 43    grid_sizes : Tuple[int, ...], default (50, 100, 250, 500, 1000, 2500)
+ 44        Grid dimensions used for Finite-Size Scaling (FSS) analysis.
+ 45    prey_birth : float, default 0.2
+ 46        Default global birth rate for the prey species.
+ 47    prey_death : float, default 0.05
+ 48        Default global death rate for the prey species.
+ 49    predator_birth : float, default 0.8
+ 50        Default global birth rate for the predator species.
+ 51    predator_death : float, default 0.05
+ 52        Default global death rate for the predator species.
+ 53    critical_prey_birth : float, default 0.20
+ 54        Identified critical birth rate for prey (Phase 1 result).
+ 55    critical_prey_death : float, default 0.947
+ 56        Identified critical death rate for prey (Phase 1 result).
+ 57    prey_death_range : Tuple[float, float], default (0.0, 0.2)
+ 58        Bounds for the prey death rate sweep in Phase 1.
+ 59    n_prey_birth : int, default 15
+ 60        Number of points along the prey birth rate axis for sweeps.
+ 61    n_prey_death : int, default 5
+ 62        Number of points along the prey death rate axis for sweeps.
+ 63    predator_birth_values : Tuple[float, ...]
+ 64        Discrete predator birth rates used for sensitivity analysis.
+ 65    predator_death_values : Tuple[float, ...]
+ 66        Discrete predator death rates used for sensitivity analysis.
+ 67    prey_death_offsets : Tuple[float, ...]
+ 68        Delta values applied to critical death rates for perturbation tests.
+ 69    n_replicates : int, default 15
+ 70        Number of independent stochastic runs per parameter set.
+ 71    warmup_steps : int, default 300
+ 72        Iterations to run before beginning data collection.
+ 73    measurement_steps : int, default 500
+ 74        Iterations spent collecting statistics after warmup.
+ 75    with_evolution : bool, default False
+ 76        Toggle for enabling per-cell parameter mutation.
+ 77    evolve_sd : float, default 0.10
+ 78        Standard deviation for parameter mutation (Gaussian).
+ 79    evolve_min : float, default 0.0
+ 80        Lower bound clamp for evolving parameters.
+ 81    evolve_max : float, default 0.10
+ 82        Upper bound clamp for evolving parameters.
+ 83    sensitivity_sd_values : Tuple[float, ...]
+ 84        Range of mutation strengths tested in sensitivity phases.
+ 85    synchronous : bool, default False
+ 86        If True, use synchronous grid updates (not recommended).
+ 87    directed_hunting : bool, default False
+ 88        Toggle for targeted predator movement logic.
+ 89    directed_hunting_values : Tuple[bool, ...]
+ 90        Options compared during Phase 6 extensions.
+ 91    save_timeseries : bool, default False
+ 92        Toggle for recording step-by-step population data.
+ 93    timeseries_subsample : int, default 10
+ 94        Frequency of temporal data points (e.g., every 10 steps).
+ 95    collect_pcf : bool, default True
+ 96        Toggle for Pair Correlation Function analysis.
+ 97    pcf_sample_rate : float, default 0.2
+ 98        Probability that a specific replicate will compute PCFs.
+ 99    pcf_max_distance : float, default 20.0
+100        Maximum radial distance for spatial correlation analysis.
+101    pcf_n_bins : int, default 20
+102        Number of bins in the PCF histogram.
+103    min_density_for_analysis : float, default 0.002
+104        Population threshold below which spatial analysis is skipped.
+105    perturbation_magnitude : float, default 0.1
+106        Strength of external shocks applied in Phase 5.
+107    n_jobs : int, default -1
+108        Number of CPU cores for parallelization (-1 uses all available).
+109    """
+110
+111    # Grid settings
+112    grid_size: int = 1000
+113    densities: Tuple[float, float] = (
+114        0.30,
+115        0.15,
+116    )  # (prey, predator)
+117
+118    # For FSS experiments: multiple grid sizes
+119    grid_sizes: Tuple[int, ...] = (50, 100, 250, 500, 1000, 2500)
+120
+121    # Default/fixed parameters
+122    prey_birth: float = 0.2
+123    prey_death: float = 0.05
+124    predator_birth: float = 0.8
+125    predator_death: float = 0.05
+126
+127    # Critical point (UPDATE AFTER PHASE 1)
+128    critical_prey_birth: float = 0.20
+129    critical_prey_death: float = 0.947
+130
+131    # Prey parameter sweep (Phase 1)
+132    prey_death_range: Tuple[float, float] = (0.0, 0.2)
+133    n_prey_birth: int = 15
+134    n_prey_death: int = 5
+135
+136    # Predator parameter sweep (Phase 4 sensitivity)
+137    predator_birth_values: Tuple[float, ...] = (
+138        0.15,
+139        0.20,
+140        0.25,
+141        0.30,
+142    )
+143    predator_death_values: Tuple[float, ...] = (
+144        0.05,
+145        0.10,
+146        0.15,
+147        0.20,
+148    )
+149
+150    # Perturbation offsets from critical point (Phase 5)
+151    prey_death_offsets: Tuple[float, ...] = (
+152        -0.02,
+153        -0.01,
+154        0.0,
+155        0.01,
+156        0.02,
+157    )
+158
+159    # Number of replicates per parameter configuration
+160    n_replicates: int = 15
+161
+162    # Simulation steps
+163    warmup_steps: int = 300
+164    measurement_steps: int = 500
+165
+166    # Evo
+167    with_evolution: bool = False
+168    evolve_sd: float = 0.10
+169    evolve_min: float = 0.0
+170    evolve_max: float = 0.10
+171
+172    # Sensitivity: mutation strength values to test
+173    sensitivity_sd_values: Tuple[float, ...] = (
+174        0.02,
+175        0.05,
+176        0.10,
+177        0.15,
+178        0.20,
+179    )
+180
+181    # Update mode
+182    synchronous: bool = False  # Always False for this model
+183    directed_hunting: bool = False
+184
+185    # For Phase 6: compare model variants
+186    directed_hunting_values: Tuple[bool, ...] = (False, True)
+187
+188    # Temporal data collection (time series)
+189    save_timeseries: bool = False
+190    timeseries_subsample: int = 10
+191
+192    # PCF settings
+193    collect_pcf: bool = True
+194    pcf_sample_rate: float = 0.2  # Fraction of runs to compute PCF
+195    pcf_max_distance: float = 20.0
+196    pcf_n_bins: int = 20
+197
+198    # Cluster analysis
+199    min_density_for_analysis: float = (
+200        0.002  # FIXME: Minimum prey density (fraction of grid) to analyze clusters/PCF
+201    )
+202
+203    # Perturbation settings (Phase 5)
+204    perturbation_magnitude: float = (
+205        0.1  # FIXME: Fractional change to apply at perturbation time
+206    )
+207
+208    # Parallelization
+209    n_jobs: int = -1  # Use all available cores by default
+210
+211    # Helpers
+212    def get_prey_births(self) -> np.ndarray:
+213        """
+214        Generate a linear range of prey birth rates for experimental sweeps.
+215
+216        Returns
+217        -------
+218        np.ndarray
+219            1D array of birth rates based on `prey_birth_range` and `n_prey_birth`.
+220        """
+221        return np.linspace(
+222            self.prey_birth_range[0], self.prey_birth_range[1], self.n_prey_birth
+223        )
+224
+225    def get_prey_deaths(self) -> np.ndarray:
+226        """
+227        Generate a linear range of prey death rates for experimental sweeps.
+228
+229        Returns
+230        -------
+231        np.ndarray
+232            1D array of death rates based on `prey_death_range` and `n_prey_death`.
+233        """
+234        return np.linspace(
+235            self.prey_death_range[0], self.prey_death_range[1], self.n_prey_death
+236        )
+237
+238    def get_warmup_steps(
+239        self, L: int
+240    ) -> int:  # FIXME: This method will be updated depending on Sary's results.
+241        """
+242        Calculate the required warmup steps scaled by grid size.
+243
+244        Parameters
+245        ----------
+246        L : int
+247            The side length of the current grid.
+248
+249        Returns
+250        -------
+251        int
+252            The number of steps to discard before measurement.
+253        """
+254        return self.warmup_steps
+255
+256    def get_measurement_steps(self, L: int) -> int:
+257        """
+258        Determine the number of measurement steps based on the grid side length.
+259
+260        This method allows for dynamic scaling of data collection duration relative
+261        to the system size. Currently, it returns a fixed value, but it is
+262        designed to be overridden for studies where measurement time must
+263        scale with the grid size (e.g., $L^z$ scaling in critical dynamics).
+264
+265        Parameters
+266        ----------
+267        L : int
+268            The side length of the current simulation grid.
+269
+270        Returns
+271        -------
+272        int
+273            The number of iterations to perform for statistical measurement.
+274        """
+275        return self.measurement_steps
+276
+277    def estimate_runtime(self, n_cores: int = 32) -> str:
+278        """
+279        Estimate the wall-clock time required to complete the experiment.
+280
+281        Calculations account for grid size scaling, PCF overhead,
+282        replicate counts, and available parallel resources.
+283
+284        Parameters
+285        ----------
+286        n_cores : int, default 32
+287            The number of CPU cores available for execution.
+288
+289        Returns
+290        -------
+291        str
+292            A human-readable summary of simulation count and estimated hours.
+293        """
+294        # Benchmark: ~1182 steps/sec for 100x100 grid
+295        ref_size = 100
+296        ref_steps_per_sec = 1182
+297
+298        size_scaling = (self.grid_size / ref_size) ** 2
+299        actual_steps_per_sec = ref_steps_per_sec / size_scaling
+300
+301        total_steps = self.warmup_steps + self.measurement_steps
+302        base_time_s = total_steps / actual_steps_per_sec
+303
+304        # PCF overhead (~8ms for 100x100)
+305        pcf_time_s = (0.008 * size_scaling) if self.collect_pcf else 0
+306
+307        # Count simulations
+308        n_sims = self.n_prey_birth * self.n_prey_death * self.n_replicates
+309        if self.with_evolution:
+310            n_sims *= 2  # Both evo and non-evo runs
+311
+312        total_seconds = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate)
+313        total_seconds /= n_cores
+314
+315        hours = total_seconds / 3600
+316        core_hours = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate) / 3600
+317
+318        return f"{n_sims:,} sims, ~{hours:.1f}h on {n_cores} cores (~{core_hours:.0f} core-hours)"
+
+ + +

Central configuration for Predator-Prey Hydra Effect experiments.

+ +

This dataclass aggregates all hyperparameters, grid settings, and +experimental phase definitions. It includes helper methods for +parameter sweep generation and runtime estimation.

+ +
Attributes
+ +
    +
  • grid_size (int, default 1000): +The side length of the square simulation grid.
  • +
  • densities (Tuple[float, float], default (0.30, 0.15)): +Initial population fractions for (prey, predator).
  • +
  • grid_sizes (Tuple[int, ...], default (50, 100, 250, 500, 1000, 2500)): +Grid dimensions used for Finite-Size Scaling (FSS) analysis.
  • +
  • prey_birth (float, default 0.2): +Default global birth rate for the prey species.
  • +
  • prey_death (float, default 0.05): +Default global death rate for the prey species.
  • +
  • predator_birth (float, default 0.8): +Default global birth rate for the predator species.
  • +
  • predator_death (float, default 0.05): +Default global death rate for the predator species.
  • +
  • critical_prey_birth (float, default 0.20): +Identified critical birth rate for prey (Phase 1 result).
  • +
  • critical_prey_death (float, default 0.947): +Identified critical death rate for prey (Phase 1 result).
  • +
  • prey_death_range (Tuple[float, float], default (0.0, 0.2)): +Bounds for the prey death rate sweep in Phase 1.
  • +
  • n_prey_birth (int, default 15): +Number of points along the prey birth rate axis for sweeps.
  • +
  • n_prey_death (int, default 5): +Number of points along the prey death rate axis for sweeps.
  • +
  • predator_birth_values (Tuple[float, ...]): +Discrete predator birth rates used for sensitivity analysis.
  • +
  • predator_death_values (Tuple[float, ...]): +Discrete predator death rates used for sensitivity analysis.
  • +
  • prey_death_offsets (Tuple[float, ...]): +Delta values applied to critical death rates for perturbation tests.
  • +
  • n_replicates (int, default 15): +Number of independent stochastic runs per parameter set.
  • +
  • warmup_steps (int, default 300): +Iterations to run before beginning data collection.
  • +
  • measurement_steps (int, default 500): +Iterations spent collecting statistics after warmup.
  • +
  • with_evolution (bool, default False): +Toggle for enabling per-cell parameter mutation.
  • +
  • evolve_sd (float, default 0.10): +Standard deviation for parameter mutation (Gaussian).
  • +
  • evolve_min (float, default 0.0): +Lower bound clamp for evolving parameters.
  • +
  • evolve_max (float, default 0.10): +Upper bound clamp for evolving parameters.
  • +
  • sensitivity_sd_values (Tuple[float, ...]): +Range of mutation strengths tested in sensitivity phases.
  • +
  • synchronous (bool, default False): +If True, use synchronous grid updates (not recommended).
  • +
  • directed_hunting (bool, default False): +Toggle for targeted predator movement logic.
  • +
  • directed_hunting_values (Tuple[bool, ...]): +Options compared during Phase 6 extensions.
  • +
  • save_timeseries (bool, default False): +Toggle for recording step-by-step population data.
  • +
  • timeseries_subsample (int, default 10): +Frequency of temporal data points (e.g., every 10 steps).
  • +
  • collect_pcf (bool, default True): +Toggle for Pair Correlation Function analysis.
  • +
  • pcf_sample_rate (float, default 0.2): +Probability that a specific replicate will compute PCFs.
  • +
  • pcf_max_distance (float, default 20.0): +Maximum radial distance for spatial correlation analysis.
  • +
  • pcf_n_bins (int, default 20): +Number of bins in the PCF histogram.
  • +
  • min_density_for_analysis (float, default 0.002): +Population threshold below which spatial analysis is skipped.
  • +
  • perturbation_magnitude (float, default 0.1): +Strength of external shocks applied in Phase 5.
  • +
  • n_jobs (int, default -1): +Number of CPU cores for parallelization (-1 uses all available).
  • +
+
+ + +
+
+ + Config( grid_size: int = 1000, densities: Tuple[float, float] = (0.3, 0.15), grid_sizes: Tuple[int, ...] = (50, 100, 250, 500, 1000, 2500), prey_birth: float = 0.2, prey_death: float = 0.05, predator_birth: float = 0.8, predator_death: float = 0.05, critical_prey_birth: float = 0.2, critical_prey_death: float = 0.947, prey_death_range: Tuple[float, float] = (0.0, 0.2), n_prey_birth: int = 15, n_prey_death: int = 5, predator_birth_values: Tuple[float, ...] = (0.15, 0.2, 0.25, 0.3), predator_death_values: Tuple[float, ...] = (0.05, 0.1, 0.15, 0.2), prey_death_offsets: Tuple[float, ...] = (-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates: int = 15, warmup_steps: int = 300, measurement_steps: int = 500, with_evolution: bool = False, evolve_sd: float = 0.1, evolve_min: float = 0.0, evolve_max: float = 0.1, sensitivity_sd_values: Tuple[float, ...] = (0.02, 0.05, 0.1, 0.15, 0.2), synchronous: bool = False, directed_hunting: bool = False, directed_hunting_values: Tuple[bool, ...] = (False, True), save_timeseries: bool = False, timeseries_subsample: int = 10, collect_pcf: bool = True, pcf_sample_rate: float = 0.2, pcf_max_distance: float = 20.0, pcf_n_bins: int = 20, min_density_for_analysis: float = 0.002, perturbation_magnitude: float = 0.1, n_jobs: int = -1) + + +
+ + + + +
+
+
+ grid_size: int = +1000 + + +
+ + + + +
+
+
+ densities: Tuple[float, float] = +(0.3, 0.15) + + +
+ + + + +
+
+
+ grid_sizes: Tuple[int, ...] = +(50, 100, 250, 500, 1000, 2500) + + +
+ + + + +
+
+
+ prey_birth: float = +0.2 + + +
+ + + + +
+
+
+ prey_death: float = +0.05 + + +
+ + + + +
+
+
+ predator_birth: float = +0.8 + + +
+ + + + +
+
+
+ predator_death: float = +0.05 + + +
+ + + + +
+
+
+ critical_prey_birth: float = +0.2 + + +
+ + + + +
+
+
+ critical_prey_death: float = +0.947 + + +
+ + + + +
+
+
+ prey_death_range: Tuple[float, float] = +(0.0, 0.2) + + +
+ + + + +
+
+
+ n_prey_birth: int = +15 + + +
+ + + + +
+
+
+ n_prey_death: int = +5 + + +
+ + + + +
+
+
+ predator_birth_values: Tuple[float, ...] = +(0.15, 0.2, 0.25, 0.3) + + +
+ + + + +
+
+
+ predator_death_values: Tuple[float, ...] = +(0.05, 0.1, 0.15, 0.2) + + +
+ + + + +
+
+
+ prey_death_offsets: Tuple[float, ...] = +(-0.02, -0.01, 0.0, 0.01, 0.02) + + +
+ + + + +
+
+
+ n_replicates: int = +15 + + +
+ + + + +
+
+
+ warmup_steps: int = +300 + + +
+ + + + +
+
+
+ measurement_steps: int = +500 + + +
+ + + + +
+
+
+ with_evolution: bool = +False + + +
+ + + + +
+
+
+ evolve_sd: float = +0.1 + + +
+ + + + +
+
+
+ evolve_min: float = +0.0 + + +
+ + + + +
+
+
+ evolve_max: float = +0.1 + + +
+ + + + +
+
+
+ sensitivity_sd_values: Tuple[float, ...] = +(0.02, 0.05, 0.1, 0.15, 0.2) + + +
+ + + + +
+
+
+ synchronous: bool = +False + + +
+ + + + +
+
+
+ directed_hunting: bool = +False + + +
+ + + + +
+
+
+ directed_hunting_values: Tuple[bool, ...] = +(False, True) + + +
+ + + + +
+
+
+ save_timeseries: bool = +False + + +
+ + + + +
+
+
+ timeseries_subsample: int = +10 + + +
+ + + + +
+
+
+ collect_pcf: bool = +True + + +
+ + + + +
+
+
+ pcf_sample_rate: float = +0.2 + + +
+ + + + +
+
+
+ pcf_max_distance: float = +20.0 + + +
+ + + + +
+
+
+ pcf_n_bins: int = +20 + + +
+ + + + +
+
+
+ min_density_for_analysis: float = +0.002 + + +
+ + + + +
+
+
+ perturbation_magnitude: float = +0.1 + + +
+ + + + +
+
+
+ n_jobs: int = +-1 + + +
+ + + + +
+
+ +
+ + def + get_prey_births(self) -> numpy.ndarray: + + + +
+ +
212    def get_prey_births(self) -> np.ndarray:
+213        """
+214        Generate a linear range of prey birth rates for experimental sweeps.
+215
+216        Returns
+217        -------
+218        np.ndarray
+219            1D array of birth rates based on `prey_birth_range` and `n_prey_birth`.
+220        """
+221        return np.linspace(
+222            self.prey_birth_range[0], self.prey_birth_range[1], self.n_prey_birth
+223        )
+
+ + +

Generate a linear range of prey birth rates for experimental sweeps.

+ +
Returns
+ +
    +
  • np.ndarray: 1D array of birth rates based on prey_birth_range and n_prey_birth.
  • +
+
+ + +
+
+ +
+ + def + get_prey_deaths(self) -> numpy.ndarray: + + + +
+ +
225    def get_prey_deaths(self) -> np.ndarray:
+226        """
+227        Generate a linear range of prey death rates for experimental sweeps.
+228
+229        Returns
+230        -------
+231        np.ndarray
+232            1D array of death rates based on `prey_death_range` and `n_prey_death`.
+233        """
+234        return np.linspace(
+235            self.prey_death_range[0], self.prey_death_range[1], self.n_prey_death
+236        )
+
+ + +

Generate a linear range of prey death rates for experimental sweeps.

+ +
Returns
+ + +
+ + +
+
+ +
+ + def + get_warmup_steps(self, L: int) -> int: + + + +
+ +
238    def get_warmup_steps(
+239        self, L: int
+240    ) -> int:  # FIXME: This method will be updated depending on Sary's results.
+241        """
+242        Calculate the required warmup steps scaled by grid size.
+243
+244        Parameters
+245        ----------
+246        L : int
+247            The side length of the current grid.
+248
+249        Returns
+250        -------
+251        int
+252            The number of steps to discard before measurement.
+253        """
+254        return self.warmup_steps
+
+ + +

Calculate the required warmup steps scaled by grid size.

+ +
Parameters
+ +
    +
  • L (int): +The side length of the current grid.
  • +
+ +
Returns
+ +
    +
  • int: The number of steps to discard before measurement.
  • +
+
+ + +
+
+ +
+ + def + get_measurement_steps(self, L: int) -> int: + + + +
+ +
256    def get_measurement_steps(self, L: int) -> int:
+257        """
+258        Determine the number of measurement steps based on the grid side length.
+259
+260        This method allows for dynamic scaling of data collection duration relative
+261        to the system size. Currently, it returns a fixed value, but it is
+262        designed to be overridden for studies where measurement time must
+263        scale with the grid size (e.g., $L^z$ scaling in critical dynamics).
+264
+265        Parameters
+266        ----------
+267        L : int
+268            The side length of the current simulation grid.
+269
+270        Returns
+271        -------
+272        int
+273            The number of iterations to perform for statistical measurement.
+274        """
+275        return self.measurement_steps
+
+ + +

Determine the number of measurement steps based on the grid side length.

+ +

This method allows for dynamic scaling of data collection duration relative +to the system size. Currently, it returns a fixed value, but it is +designed to be overridden for studies where measurement time must +scale with the grid size (e.g., $L^z$ scaling in critical dynamics).

+ +
Parameters
+ +
    +
  • L (int): +The side length of the current simulation grid.
  • +
+ +
Returns
+ +
    +
  • int: The number of iterations to perform for statistical measurement.
  • +
+
+ + +
+
+ +
+ + def + estimate_runtime(self, n_cores: int = 32) -> str: + + + +
+ +
277    def estimate_runtime(self, n_cores: int = 32) -> str:
+278        """
+279        Estimate the wall-clock time required to complete the experiment.
+280
+281        Calculations account for grid size scaling, PCF overhead,
+282        replicate counts, and available parallel resources.
+283
+284        Parameters
+285        ----------
+286        n_cores : int, default 32
+287            The number of CPU cores available for execution.
+288
+289        Returns
+290        -------
+291        str
+292            A human-readable summary of simulation count and estimated hours.
+293        """
+294        # Benchmark: ~1182 steps/sec for 100x100 grid
+295        ref_size = 100
+296        ref_steps_per_sec = 1182
+297
+298        size_scaling = (self.grid_size / ref_size) ** 2
+299        actual_steps_per_sec = ref_steps_per_sec / size_scaling
+300
+301        total_steps = self.warmup_steps + self.measurement_steps
+302        base_time_s = total_steps / actual_steps_per_sec
+303
+304        # PCF overhead (~8ms for 100x100)
+305        pcf_time_s = (0.008 * size_scaling) if self.collect_pcf else 0
+306
+307        # Count simulations
+308        n_sims = self.n_prey_birth * self.n_prey_death * self.n_replicates
+309        if self.with_evolution:
+310            n_sims *= 2  # Both evo and non-evo runs
+311
+312        total_seconds = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate)
+313        total_seconds /= n_cores
+314
+315        hours = total_seconds / 3600
+316        core_hours = n_sims * (base_time_s + pcf_time_s * self.pcf_sample_rate) / 3600
+317
+318        return f"{n_sims:,} sims, ~{hours:.1f}h on {n_cores} cores (~{core_hours:.0f} core-hours)"
+
+ + +

Estimate the wall-clock time required to complete the experiment.

+ +

Calculations account for grid size scaling, PCF overhead, +replicate counts, and available parallel resources.

+ +
Parameters
+ +
    +
  • n_cores (int, default 32): +The number of CPU cores available for execution.
  • +
+ +
Returns
+ +
    +
  • str: A human-readable summary of simulation count and estimated hours.
  • +
+
+ + +
+
+
+
+ PHASE1_CONFIG = + + Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0963, 0.0973), n_prey_birth=15, n_prey_death=20, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=30, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1) + + +
+ + + + +
+
+
+ PHASE2_CONFIG = + + Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=1, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=1000, measurement_steps=10000, with_evolution=True, evolve_sd=0.01, evolve_min=0.0, evolve_max=0.2, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1) + + +
+ + + + +
+
+
+ PHASE3_CONFIG = + + Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=20, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=True, pcf_sample_rate=1.0, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1) + + +
+ + + + +
+
+
+ PHASE4_CONFIG = + + Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1) + + +
+ + + + +
+
+
+ PHASE5_CONFIG = + + Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=True, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1) + + +
+ + + + +
+
+
+ PHASE_CONFIGS = + + {1: Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0963, 0.0973), n_prey_birth=15, n_prey_death=20, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=30, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 2: Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=1, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=1000, measurement_steps=10000, with_evolution=True, evolve_sd=0.01, evolve_min=0.0, evolve_max=0.2, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 3: Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=20, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=True, pcf_sample_rate=1.0, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 4: Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 5: Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=True, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)} + + +
+ + + + +
+
+ +
+ + def + get_phase_config(phase: int) -> Config: + + + +
+ +
408def get_phase_config(phase: int) -> Config:
+409    """
+410    Retrieve the configuration object for a specific experimental phase.
+411
+412    Parameters
+413    ----------
+414    phase : int
+415        The phase number (1 through 6) to retrieve.
+416
+417    Returns
+418    -------
+419    Config
+420        The configuration instance associated with the requested phase.
+421
+422    Raises
+423    ------
+424    ValueError
+425        If the phase number is not found in the pre-defined PHASE_CONFIGS.
+426    """
+427    if phase not in PHASE_CONFIGS:
+428        raise ValueError(
+429            f"Unknown phase {phase}. Valid phases: {list(PHASE_CONFIGS.keys())}"
+430        )
+431    return PHASE_CONFIGS[phase]
+
+ + +

Retrieve the configuration object for a specific experimental phase.

+ +
Parameters
+ +
    +
  • phase (int): +The phase number (1 through 6) to retrieve.
  • +
+ +
Returns
+ +
    +
  • Config: The configuration instance associated with the requested phase.
  • +
+ +
Raises
+ +
    +
  • ValueError: If the phase number is not found in the pre-defined PHASE_CONFIGS.
  • +
+
+ + +
+
+ + \ No newline at end of file diff --git a/docs/models/numba_optimized.html b/docs/models/numba_optimized.html new file mode 100644 index 0000000..c98e69f --- /dev/null +++ b/docs/models/numba_optimized.html @@ -0,0 +1,3327 @@ + + + + + + + models.numba_optimized API documentation + + + + + + + + + +
+
+

+models.numba_optimized

+ +

Numba-optimized kernels for predator-prey cellular automaton.

+ +

Optimizations:

+ +
    +
  1. Cell-list PCF: O(N) average instead of O(N²) brute force
  2. +
  3. Pre-allocated work buffers for async kernel
  4. +
  5. Consistent dtypes throughout
  6. +
  7. cache=True for persistent JIT compilation
  8. +
+ +

Usage: + from numba_optimized import ( + PPKernel, + compute_all_pcfs_fast, + measure_cluster_sizes_fast, # Sizes only (fastest) + detect_clusters_fast, # Labels + sizes dict + get_cluster_stats_fast, # Full statistics + get_percolating_cluster_fast, # Percolation detection + NUMBA_AVAILABLE + )

+
+ + + + + +
   1#!/usr/bin/env python3
+   2"""
+   3Numba-optimized kernels for predator-prey cellular automaton.
+   4
+   5Optimizations:
+   61. Cell-list PCF: O(N) average instead of O(N²) brute force
+   72. Pre-allocated work buffers for async kernel
+   83. Consistent dtypes throughout
+   94. cache=True for persistent JIT compilation
+  10
+  11Usage:
+  12    from numba_optimized import (
+  13        PPKernel,
+  14        compute_all_pcfs_fast,
+  15        measure_cluster_sizes_fast,      # Sizes only (fastest)
+  16        detect_clusters_fast,            # Labels + sizes dict
+  17        get_cluster_stats_fast,          # Full statistics
+  18        get_percolating_cluster_fast,    # Percolation detection
+  19        NUMBA_AVAILABLE
+  20    )
+  21"""
+  22
+  23import numpy as np
+  24from typing import Tuple, Dict, Optional
+  25
+  26try:
+  27    from numba import njit, prange
+  28
+  29    NUMBA_AVAILABLE = True
+  30except ImportError:
+  31    NUMBA_AVAILABLE = False
+  32
+  33    def njit(*args, **kwargs):
+  34        def decorator(func):
+  35            return func
+  36
+  37        return decorator
+  38
+  39    def prange(*args):
+  40        return range(*args)
+  41
+  42
+  43# ============================================================================
+  44# RNG SEEDING
+  45# ============================================================================
+  46
+  47
+  48@njit(cache=True)
+  49def set_numba_seed(seed: int) -> None:
+  50    """
+  51    Seed Numba's internal random number generator from within a JIT context.
+  52
+  53    This function ensures that Numba's independent random number generator
+  54    is synchronized with the provided seed, enabling reproducibility for
+  55    jit-compiled functions that use NumPy's random operations.
+  56
+  57    Parameters
+  58    ----------
+  59    seed : int
+  60        The integer value used to initialize the random number generator.
+  61
+  62    Returns
+  63    -------
+  64    None
+  65
+  66    Notes
+  67    -----
+  68    Because Numba maintains its own internal state for random number
+  69    generation, calling `np.random.seed()` in standard Python code will not
+  70    affect jit-compiled functions. This helper must be called to bridge
+  71    that gap.
+  72    """
+  73    np.random.seed(seed)
+  74
+  75
+  76# ============================================================================
+  77# PREDATOR-PREY KERNELS
+  78# ============================================================================
+  79
+  80
+  81@njit(cache=True)
+  82def _pp_async_kernel_random(
+  83    grid: np.ndarray,
+  84    prey_death_arr: np.ndarray,
+  85    p_birth_val: float,
+  86    p_death_val: float,
+  87    pred_birth_val: float,
+  88    pred_death_val: float,
+  89    dr_arr: np.ndarray,
+  90    dc_arr: np.ndarray,
+  91    evolve_sd: float,
+  92    evolve_min: float,
+  93    evolve_max: float,
+  94    evolution_stopped: bool,
+  95    occupied_buffer: np.ndarray,
+  96) -> np.ndarray:
+  97    """
+  98    Asynchronous predator-prey update kernel with random neighbor selection.
+  99
+ 100    This Numba-accelerated kernel performs an asynchronous update of the
+ 101    simulation grid. It identifies all occupied cells, shuffles them to
+ 102    ensure unbiased processing, and applies stochastic rules for prey
+ 103    mortality, prey reproduction (with optional parameter evolution),
+ 104    predator mortality, and predation.
+ 105
+ 106    Parameters
+ 107    ----------
+ 108    grid : np.ndarray
+ 109        2D integer array representing the simulation grid (0: Empty, 1: Prey, 2: Predator).
+ 110    prey_death_arr : np.ndarray
+ 111        2D float array storing the individual prey death rates for evolution tracking.
+ 112    p_birth_val : float
+ 113        Base probability of prey reproduction into an adjacent empty cell.
+ 114    p_death_val : float
+ 115        Base probability of prey death (though individual rates in `prey_death_arr` are used).
+ 116    pred_birth_val : float
+ 117        Probability of a predator reproducing after consuming prey.
+ 118    pred_death_val : float
+ 119        Probability of a predator dying.
+ 120    dr_arr : np.ndarray
+ 121        Array of row offsets defining the neighborhood.
+ 122    dc_arr : np.ndarray
+ 123        Array of column offsets defining the neighborhood.
+ 124    evolve_sd : float
+ 125        Standard deviation of the mutation applied to the prey death rate during reproduction.
+ 126    evolve_min : float
+ 127        Lower bound for the evolved prey death rate.
+ 128    evolve_max : float
+ 129        Upper bound for the evolved prey death rate.
+ 130    evolution_stopped : bool
+ 131        If True, offspring inherit the parent's death rate without mutation.
+ 132    occupied_buffer : np.ndarray
+ 133        Pre-allocated 2D array used to store and shuffle coordinates of occupied cells.
+ 134
+ 135    Returns
+ 136    -------
+ 137    grid : np.ndarray
+ 138        The updated simulation grid.
+ 139
+ 140    Notes
+ 141    -----
+ 142    The kernel uses periodic boundary conditions. The Fisher-Yates shuffle on
+ 143    `occupied_buffer` ensures that the asynchronous updates do not introduce
+ 144    directional bias.
+ 145    """
+ 146    rows, cols = grid.shape
+ 147    n_shifts = len(dr_arr)
+ 148
+ 149    # Collect occupied cells
+ 150    count = 0
+ 151    for r in range(rows):
+ 152        for c in range(cols):
+ 153            if grid[r, c] != 0:
+ 154                occupied_buffer[count, 0] = r
+ 155                occupied_buffer[count, 1] = c
+ 156                count += 1
+ 157
+ 158    # Fisher-Yates shuffle
+ 159    for i in range(count - 1, 0, -1):
+ 160        j = np.random.randint(0, i + 1)
+ 161        occupied_buffer[i, 0], occupied_buffer[j, 0] = (
+ 162            occupied_buffer[j, 0],
+ 163            occupied_buffer[i, 0],
+ 164        )
+ 165        occupied_buffer[i, 1], occupied_buffer[j, 1] = (
+ 166            occupied_buffer[j, 1],
+ 167            occupied_buffer[i, 1],
+ 168        )
+ 169
+ 170    # Process each occupied cell
+ 171    for i in range(count):
+ 172        r = occupied_buffer[i, 0]
+ 173        c = occupied_buffer[i, 1]
+ 174
+ 175        state = grid[r, c]
+ 176        if state == 0:
+ 177            continue
+ 178
+ 179        # Random neighbor selection
+ 180        nbi = np.random.randint(0, n_shifts)
+ 181        nr = (r + dr_arr[nbi]) % rows
+ 182        nc = (c + dc_arr[nbi]) % cols
+ 183
+ 184        if state == 1:  # PREY
+ 185            if np.random.random() < prey_death_arr[r, c]:
+ 186                grid[r, c] = 0
+ 187                prey_death_arr[r, c] = np.nan
+ 188            elif grid[nr, nc] == 0:
+ 189                if np.random.random() < p_birth_val:
+ 190                    grid[nr, nc] = 1
+ 191                    parent_val = prey_death_arr[r, c]
+ 192                    if not evolution_stopped:
+ 193                        child_val = parent_val + np.random.normal(0, evolve_sd)
+ 194                        if child_val < evolve_min:
+ 195                            child_val = evolve_min
+ 196                        if child_val > evolve_max:
+ 197                            child_val = evolve_max
+ 198                        prey_death_arr[nr, nc] = child_val
+ 199                    else:
+ 200                        prey_death_arr[nr, nc] = parent_val
+ 201
+ 202        elif state == 2:  # PREDATOR
+ 203            if np.random.random() < pred_death_val:
+ 204                grid[r, c] = 0
+ 205            elif grid[nr, nc] == 1:
+ 206                if np.random.random() < pred_birth_val:
+ 207                    grid[nr, nc] = 2
+ 208                    prey_death_arr[nr, nc] = np.nan
+ 209
+ 210    return grid
+ 211
+ 212
+ 213@njit(cache=True)
+ 214def _pp_async_kernel_directed(
+ 215    grid: np.ndarray,
+ 216    prey_death_arr: np.ndarray,
+ 217    p_birth_val: float,
+ 218    p_death_val: float,
+ 219    pred_birth_val: float,
+ 220    pred_death_val: float,
+ 221    dr_arr: np.ndarray,
+ 222    dc_arr: np.ndarray,
+ 223    evolve_sd: float,
+ 224    evolve_min: float,
+ 225    evolve_max: float,
+ 226    evolution_stopped: bool,
+ 227    occupied_buffer: np.ndarray,
+ 228) -> np.ndarray:
+ 229    """
+ 230    Asynchronous predator-prey update kernel with directed behavior.
+ 231
+ 232    This kernel implements "intelligent" species behavior: prey actively search
+ 233    for empty spaces to reproduce, and predators actively search for nearby
+ 234    prey to hunt. A two-pass approach is used to stochastically select a
+ 235    valid target from the neighborhood without heap allocation.
+ 236
+ 237    Parameters
+ 238    ----------
+ 239    grid : np.ndarray
+ 240        2D integer array representing the simulation grid (0: Empty, 1: Prey, 2: Predator).
+ 241    prey_death_arr : np.ndarray
+ 242        2D float array storing individual prey mortality rates for evolution.
+ 243    p_birth_val : float
+ 244        Probability of prey reproduction attempt.
+ 245    p_death_val : float
+ 246        Base probability of prey mortality.
+ 247    pred_birth_val : float
+ 248        Probability of a predator reproduction attempt (hunting success).
+ 249    pred_death_val : float
+ 250        Probability of predator mortality.
+ 251    dr_arr : np.ndarray
+ 252        Row offsets defining the spatial neighborhood (e.g., Moore or von Neumann).
+ 253    dc_arr : np.ndarray
+ 254        Column offsets defining the spatial neighborhood.
+ 255    evolve_sd : float
+ 256        Standard deviation for mutations in prey death rates.
+ 257    evolve_min : float
+ 258        Minimum allowable value for evolved prey death rates.
+ 259    evolve_max : float
+ 260        Maximum allowable value for evolved prey death rates.
+ 261    evolution_stopped : bool
+ 262        If True, prevents mutation during prey reproduction.
+ 263    occupied_buffer : np.ndarray
+ 264        Pre-allocated array for storing and shuffling active cell coordinates.
+ 265
+ 266    Returns
+ 267    -------
+ 268    grid : np.ndarray
+ 269        The updated simulation grid.
+ 270
+ 271    Notes
+ 272    -----
+ 273    The directed behavior significantly changes the system dynamics compared to
+ 274    random neighbor selection, often leading to different critical thresholds
+ 275    and spatial patterning. Periodic boundary conditions are applied.
+ 276    """
+ 277    rows, cols = grid.shape
+ 278    n_shifts = len(dr_arr)
+ 279
+ 280    # Collect occupied cells
+ 281    count = 0
+ 282    for r in range(rows):
+ 283        for c in range(cols):
+ 284            if grid[r, c] != 0:
+ 285                occupied_buffer[count, 0] = r
+ 286                occupied_buffer[count, 1] = c
+ 287                count += 1
+ 288
+ 289    # Fisher-Yates shuffle
+ 290    for i in range(count - 1, 0, -1):
+ 291        j = np.random.randint(0, i + 1)
+ 292        occupied_buffer[i, 0], occupied_buffer[j, 0] = (
+ 293            occupied_buffer[j, 0],
+ 294            occupied_buffer[i, 0],
+ 295        )
+ 296        occupied_buffer[i, 1], occupied_buffer[j, 1] = (
+ 297            occupied_buffer[j, 1],
+ 298            occupied_buffer[i, 1],
+ 299        )
+ 300
+ 301    # Process each occupied cell
+ 302    for i in range(count):
+ 303        r = occupied_buffer[i, 0]
+ 304        c = occupied_buffer[i, 1]
+ 305
+ 306        state = grid[r, c]
+ 307        if state == 0:
+ 308            continue
+ 309
+ 310        if state == 1:  # PREY - directed reproduction into empty cells
+ 311            # Check for death first
+ 312            if np.random.random() < prey_death_arr[r, c]:
+ 313                grid[r, c] = 0
+ 314                prey_death_arr[r, c] = np.nan
+ 315                continue
+ 316
+ 317            # Attempt reproduction with directed selection
+ 318            if np.random.random() < p_birth_val:
+ 319                # Pass 1: Count empty neighbors
+ 320                empty_count = 0
+ 321                for k in range(n_shifts):
+ 322                    check_r = (r + dr_arr[k]) % rows
+ 323                    check_c = (c + dc_arr[k]) % cols
+ 324                    if grid[check_r, check_c] == 0:
+ 325                        empty_count += 1
+ 326
+ 327                # Pass 2: Select random empty neighbor
+ 328                if empty_count > 0:
+ 329                    target_idx = np.random.randint(0, empty_count)
+ 330                    found = 0
+ 331                    nr, nc = r, c  # Initialize (will be overwritten)
+ 332                    for k in range(n_shifts):
+ 333                        check_r = (r + dr_arr[k]) % rows
+ 334                        check_c = (c + dc_arr[k]) % cols
+ 335                        if grid[check_r, check_c] == 0:
+ 336                            if found == target_idx:
+ 337                                nr, nc = check_r, check_c
+ 338                                break
+ 339                            found += 1
+ 340
+ 341                    # Reproduce into selected empty cell
+ 342                    grid[nr, nc] = 1
+ 343                    parent_val = prey_death_arr[r, c]
+ 344                    if not evolution_stopped:
+ 345                        child_val = parent_val + np.random.normal(0, evolve_sd)
+ 346                        if child_val < evolve_min:
+ 347                            child_val = evolve_min
+ 348                        if child_val > evolve_max:
+ 349                            child_val = evolve_max
+ 350                        prey_death_arr[nr, nc] = child_val
+ 351                    else:
+ 352                        prey_death_arr[nr, nc] = parent_val
+ 353
+ 354        elif state == 2:  # PREDATOR - directed hunting
+ 355            # Check for death first
+ 356            if np.random.random() < pred_death_val:
+ 357                grid[r, c] = 0
+ 358                continue
+ 359
+ 360            # Attempt hunting with directed selection
+ 361            if np.random.random() < pred_birth_val:
+ 362                # Pass 1: Count prey neighbors
+ 363                prey_count = 0
+ 364                for k in range(n_shifts):
+ 365                    check_r = (r + dr_arr[k]) % rows
+ 366                    check_c = (c + dc_arr[k]) % cols
+ 367                    if grid[check_r, check_c] == 1:
+ 368                        prey_count += 1
+ 369
+ 370                # Pass 2: Select random prey neighbor
+ 371                if prey_count > 0:
+ 372                    target_idx = np.random.randint(0, prey_count)
+ 373                    found = 0
+ 374                    nr, nc = r, c  # Initialize (will be overwritten)
+ 375                    for k in range(n_shifts):
+ 376                        check_r = (r + dr_arr[k]) % rows
+ 377                        check_c = (c + dc_arr[k]) % cols
+ 378                        if grid[check_r, check_c] == 1:
+ 379                            if found == target_idx:
+ 380                                nr, nc = check_r, check_c
+ 381                                break
+ 382                            found += 1
+ 383
+ 384                    # Hunt: prey cell becomes predator
+ 385                    grid[nr, nc] = 2
+ 386                    prey_death_arr[nr, nc] = np.nan
+ 387
+ 388    return grid
+ 389
+ 390
+ 391class PPKernel:
+ 392    """
+ 393    Wrapper for predator-prey kernel with pre-allocated buffers.
+ 394
+ 395    This class manages the spatial configuration and memory buffers required
+ 396    for the Numba-accelerated update kernels. By pre-allocating the
+ 397    `occupied_buffer`, it avoids expensive memory allocations during the
+ 398    simulation loop.
+ 399
+ 400    Parameters
+ 401    ----------
+ 402    rows : int
+ 403        Number of rows in the simulation grid.
+ 404    cols : int
+ 405        Number of columns in the simulation grid.
+ 406    neighborhood : {'moore', 'von_neumann'}, optional
+ 407        The neighborhood type determining adjacent cells. 'moore' includes
+ 408        diagonals (8 neighbors), 'von_neumann' does not (4 neighbors).
+ 409        Default is 'moore'.
+ 410    directed_hunting : bool, optional
+ 411        If True, uses the directed behavior kernel where species search for
+ 412        targets. If False, uses random neighbor selection. Default is False.
+ 413
+ 414    Attributes
+ 415    ----------
+ 416    rows : int
+ 417        Grid row count.
+ 418    cols : int
+ 419        Grid column count.
+ 420    directed_hunting : bool
+ 421        Toggle for intelligent behavior logic.
+ 422    """
+ 423
+ 424    def __init__(
+ 425        self,
+ 426        rows: int,
+ 427        cols: int,
+ 428        neighborhood: str = "moore",
+ 429        directed_hunting: bool = False,
+ 430    ):
+ 431        self.rows = rows
+ 432        self.cols = cols
+ 433        self.directed_hunting = directed_hunting
+ 434        self._occupied_buffer = np.empty((rows * cols, 2), dtype=np.int32)
+ 435
+ 436        if neighborhood == "moore":
+ 437            self._dr = np.array([-1, -1, -1, 0, 0, 1, 1, 1], dtype=np.int32)
+ 438            self._dc = np.array([-1, 0, 1, -1, 1, -1, 0, 1], dtype=np.int32)
+ 439        else:  # von Neumann
+ 440            self._dr = np.array([-1, 1, 0, 0], dtype=np.int32)
+ 441            self._dc = np.array([0, 0, -1, 1], dtype=np.int32)
+ 442
+ 443    def update(
+ 444        self,
+ 445        grid: np.ndarray,
+ 446        prey_death_arr: np.ndarray,
+ 447        prey_birth: float,
+ 448        prey_death: float,
+ 449        pred_birth: float,
+ 450        pred_death: float,
+ 451        evolve_sd: float = 0.1,
+ 452        evolve_min: float = 0.001,
+ 453        evolve_max: float = 0.1,
+ 454        evolution_stopped: bool = True,
+ 455    ) -> np.ndarray:
+ 456        """
+ 457        Execute a single asynchronous update step using the configured kernel.
+ 458
+ 459        Parameters
+ 460        ----------
+ 461        grid : np.ndarray
+ 462            The current 2D simulation grid.
+ 463        prey_death_arr : np.ndarray
+ 464            2D array of individual prey mortality rates.
+ 465        prey_birth : float
+ 466            Prey reproduction probability.
+ 467        prey_death : float
+ 468            Base prey mortality probability.
+ 469        pred_birth : float
+ 470            Predator reproduction (hunting success) probability.
+ 471        pred_death : float
+ 472            Predator mortality probability.
+ 473        evolve_sd : float, optional
+ 474            Mutation standard deviation (default 0.1).
+ 475        evolve_min : float, optional
+ 476            Minimum evolved death rate (default 0.001).
+ 477        evolve_max : float, optional
+ 478            Maximum evolved death rate (default 0.1).
+ 479        evolution_stopped : bool, optional
+ 480            Whether to disable mutation during this step (default True).
+ 481
+ 482        Returns
+ 483        -------
+ 484        np.ndarray
+ 485            The updated grid after one full asynchronous pass.
+ 486        """
+ 487        if self.directed_hunting:
+ 488            return _pp_async_kernel_directed(
+ 489                grid,
+ 490                prey_death_arr,
+ 491                prey_birth,
+ 492                prey_death,
+ 493                pred_birth,
+ 494                pred_death,
+ 495                self._dr,
+ 496                self._dc,
+ 497                evolve_sd,
+ 498                evolve_min,
+ 499                evolve_max,
+ 500                evolution_stopped,
+ 501                self._occupied_buffer,
+ 502            )
+ 503        else:
+ 504            return _pp_async_kernel_random(
+ 505                grid,
+ 506                prey_death_arr,
+ 507                prey_birth,
+ 508                prey_death,
+ 509                pred_birth,
+ 510                pred_death,
+ 511                self._dr,
+ 512                self._dc,
+ 513                evolve_sd,
+ 514                evolve_min,
+ 515                evolve_max,
+ 516                evolution_stopped,
+ 517                self._occupied_buffer,
+ 518            )
+ 519
+ 520
+ 521# ============================================================================
+ 522# CLUSTER DETECTION (ENHANCED)
+ 523# ============================================================================
+ 524
+ 525
+ 526@njit(cache=True)
+ 527def _flood_fill(
+ 528    grid: np.ndarray,
+ 529    visited: np.ndarray,
+ 530    start_r: int,
+ 531    start_c: int,
+ 532    target: int,
+ 533    rows: int,
+ 534    cols: int,
+ 535    moore: bool,
+ 536) -> int:
+ 537    """
+ 538    Perform a stack-based flood fill to measure the size of a connected cluster.
+ 539
+ 540    This Numba-accelerated function identifies all contiguous cells of a
+ 541    specific target value starting from a given coordinate. It supports
+ 542    both Moore and von Neumann neighborhoods and implements periodic
+ 543    boundary conditions (toroidal topology).
+ 544
+ 545    Parameters
+ 546    ----------
+ 547    grid : np.ndarray
+ 548        2D integer array representing the simulation environment.
+ 549    visited : np.ndarray
+ 550        2D boolean array tracked across calls to avoid re-processing cells.
+ 551    start_r : int
+ 552        Starting row index for the flood fill.
+ 553    start_c : int
+ 554        Starting column index for the flood fill.
+ 555    target : int
+ 556        The cell value (e.g., 1 for Prey, 2 for Predator) to include in the cluster.
+ 557    rows : int
+ 558        Total number of rows in the grid.
+ 559    cols : int
+ 560        Total number of columns in the grid.
+ 561    moore : bool
+ 562        If True, use a Moore neighborhood (8 neighbors). If False, use a
+ 563        von Neumann neighborhood (4 neighbors).
+ 564
+ 565    Returns
+ 566    -------
+ 567    size : int
+ 568        The total number of connected cells belonging to the cluster.
+ 569
+ 570    Notes
+ 571    -----
+ 572    The function uses a manual stack implementation to avoid recursion limit
+ 573    issues and is optimized for use within JIT-compiled loops.
+ 574    """
+ 575    max_stack = rows * cols
+ 576    stack_r = np.empty(max_stack, dtype=np.int32)
+ 577    stack_c = np.empty(max_stack, dtype=np.int32)
+ 578    stack_ptr = 0
+ 579
+ 580    stack_r[stack_ptr] = start_r
+ 581    stack_c[stack_ptr] = start_c
+ 582    stack_ptr += 1
+ 583    visited[start_r, start_c] = True
+ 584
+ 585    size = 0
+ 586
+ 587    if moore:
+ 588        dr = np.array([-1, -1, -1, 0, 0, 1, 1, 1], dtype=np.int32)
+ 589        dc = np.array([-1, 0, 1, -1, 1, -1, 0, 1], dtype=np.int32)
+ 590        n_neighbors = 8
+ 591    else:
+ 592        dr = np.array([-1, 1, 0, 0], dtype=np.int32)
+ 593        dc = np.array([0, 0, -1, 1], dtype=np.int32)
+ 594        n_neighbors = 4
+ 595
+ 596    while stack_ptr > 0:
+ 597        stack_ptr -= 1
+ 598        r = stack_r[stack_ptr]
+ 599        c = stack_c[stack_ptr]
+ 600        size += 1
+ 601
+ 602        for k in range(n_neighbors):
+ 603            nr = (r + dr[k]) % rows
+ 604            nc = (c + dc[k]) % cols
+ 605
+ 606            if not visited[nr, nc] and grid[nr, nc] == target:
+ 607                visited[nr, nc] = True
+ 608                stack_r[stack_ptr] = nr
+ 609                stack_c[stack_ptr] = nc
+ 610                stack_ptr += 1
+ 611
+ 612    return size
+ 613
+ 614
+ 615@njit(cache=True)
+ 616def _measure_clusters(grid: np.ndarray, species: int, moore: bool = True) -> np.ndarray:
+ 617    """
+ 618    Identify and measure the sizes of all connected clusters for a specific species.
+ 619
+ 620    This function scans the entire grid and initiates a flood-fill algorithm
+ 621    whenever an unvisited cell of the target species is encountered. It
+ 622    returns an array containing the size (cell count) of each identified cluster.
+ 623
+ 624    Parameters
+ 625    ----------
+ 626    grid : np.ndarray
+ 627        2D integer array representing the simulation environment.
+ 628    species : int
+ 629        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+ 630    moore : bool, optional
+ 631        Determines the connectivity logic. If True, uses the Moore neighborhood
+ 632        (8 neighbors); if False, uses the von Neumann neighborhood (4 neighbors).
+ 633        Default is True.
+ 634
+ 635    Returns
+ 636    -------
+ 637    cluster_sizes : np.ndarray
+ 638        A 1D array of integers where each element represents the size of
+ 639        one connected cluster.
+ 640
+ 641    Notes
+ 642    -----
+ 643    This function is Numba-optimized and utilizes an internal `visited` mask
+ 644    to ensure each cell is processed only once, maintaining $O(N)$
+ 645    complexity relative to the number of cells.
+ 646    """
+ 647    rows, cols = grid.shape
+ 648    visited = np.zeros((rows, cols), dtype=np.bool_)
+ 649
+ 650    max_clusters = rows * cols
+ 651    sizes = np.empty(max_clusters, dtype=np.int32)
+ 652    n_clusters = 0
+ 653
+ 654    for r in range(rows):
+ 655        for c in range(cols):
+ 656            if grid[r, c] == species and not visited[r, c]:
+ 657                size = _flood_fill(grid, visited, r, c, species, rows, cols, moore)
+ 658                sizes[n_clusters] = size
+ 659                n_clusters += 1
+ 660
+ 661    return sizes[:n_clusters]
+ 662
+ 663
+ 664@njit(cache=True)
+ 665def _detect_clusters_numba(
+ 666    grid: np.ndarray,
+ 667    species: int,
+ 668    moore: bool,
+ 669) -> Tuple[np.ndarray, np.ndarray]:
+ 670    """
+ 671    Full cluster detection returning labels and sizes (Numba-accelerated).
+ 672
+ 673    Returns:
+ 674        labels: 2D int32 array where each cell contains its cluster ID (0 = non-target)
+ 675        sizes: 1D int32 array of cluster sizes (index i = size of cluster i+1)
+ 676    """
+ 677    rows, cols = grid.shape
+ 678    labels = np.zeros((rows, cols), dtype=np.int32)
+ 679
+ 680    if moore:
+ 681        dr = np.array([-1, -1, -1, 0, 0, 1, 1, 1], dtype=np.int32)
+ 682        dc = np.array([-1, 0, 1, -1, 1, -1, 0, 1], dtype=np.int32)
+ 683        n_neighbors = 8
+ 684    else:
+ 685        dr = np.array([-1, 1, 0, 0], dtype=np.int32)
+ 686        dc = np.array([0, 0, -1, 1], dtype=np.int32)
+ 687        n_neighbors = 4
+ 688
+ 689    max_clusters = rows * cols
+ 690    sizes = np.empty(max_clusters, dtype=np.int32)
+ 691    n_clusters = 0
+ 692    current_label = 1
+ 693
+ 694    max_stack = rows * cols
+ 695    stack_r = np.empty(max_stack, dtype=np.int32)
+ 696    stack_c = np.empty(max_stack, dtype=np.int32)
+ 697
+ 698    for start_r in range(rows):
+ 699        for start_c in range(cols):
+ 700            if grid[start_r, start_c] != species or labels[start_r, start_c] != 0:
+ 701                continue
+ 702
+ 703            stack_ptr = 0
+ 704            stack_r[stack_ptr] = start_r
+ 705            stack_c[stack_ptr] = start_c
+ 706            stack_ptr += 1
+ 707            labels[start_r, start_c] = current_label
+ 708            size = 0
+ 709
+ 710            while stack_ptr > 0:
+ 711                stack_ptr -= 1
+ 712                r = stack_r[stack_ptr]
+ 713                c = stack_c[stack_ptr]
+ 714                size += 1
+ 715
+ 716                for k in range(n_neighbors):
+ 717                    nr = (r + dr[k]) % rows
+ 718                    nc = (c + dc[k]) % cols
+ 719
+ 720                    if grid[nr, nc] == species and labels[nr, nc] == 0:
+ 721                        labels[nr, nc] = current_label
+ 722                        stack_r[stack_ptr] = nr
+ 723                        stack_c[stack_ptr] = nc
+ 724                        stack_ptr += 1
+ 725
+ 726            sizes[n_clusters] = size
+ 727            n_clusters += 1
+ 728            current_label += 1
+ 729
+ 730    return labels, sizes[:n_clusters]
+ 731
+ 732
+ 733# ============================================================================
+ 734# PUBLIC API - CLUSTER DETECTION
+ 735# ============================================================================
+ 736
+ 737
+ 738def measure_cluster_sizes_fast(
+ 739    grid: np.ndarray,
+ 740    species: int,
+ 741    neighborhood: str = "moore",
+ 742) -> np.ndarray:
+ 743    """
+ 744    Measure cluster sizes for a specific species using Numba-accelerated flood fill.
+ 745
+ 746    This function provides a high-performance interface for calculating cluster
+ 747    size statistics without the overhead of generating a full label map. It is
+ 748    optimized for large-scale simulation analysis where only distribution
+ 749    metrics (e.g., mean size, max size) are required.
+ 750
+ 751    Parameters
+ 752    ----------
+ 753    grid : np.ndarray
+ 754        A 2D array representing the simulation environment.
+ 755    species : int
+ 756        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+ 757    neighborhood : {'moore', 'neumann'}, optional
+ 758        The connectivity rule. 'moore' uses 8-way connectivity (including diagonals);
+ 759        'neumann' uses 4-way connectivity. Default is 'moore'.
+ 760
+ 761    Returns
+ 762    -------
+ 763    cluster_sizes : np.ndarray
+ 764        A 1D array of integers, where each element is the cell count of an
+ 765        identified cluster.
+ 766
+ 767    Notes
+ 768    -----
+ 769    The input grid is cast to `int32` to ensure compatibility with the
+ 770    underlying JIT-compiled `_measure_clusters` kernel.
+ 771
+ 772    Examples
+ 773    --------
+ 774    >>> sizes = measure_cluster_sizes_fast(grid, species=1, neighborhood='moore')
+ 775    >>> if sizes.size > 0:
+ 776    ...     print(f"Largest cluster: {sizes.max()}")
+ 777    """
+ 778    grid_int = np.asarray(grid, dtype=np.int32)
+ 779    moore = neighborhood == "moore"
+ 780    return _measure_clusters(grid_int, np.int32(species), moore)
+ 781
+ 782
+ 783def detect_clusters_fast(
+ 784    grid: np.ndarray,
+ 785    species: int,
+ 786    neighborhood: str = "moore",
+ 787) -> Tuple[np.ndarray, Dict[int, int]]:
+ 788    """
+ 789    Perform full cluster detection with labels using Numba acceleration.
+ 790
+ 791    This function returns a label array for spatial analysis and a dictionary
+ 792    of cluster sizes. It is significantly faster than standard Python or
+ 793    SciPy equivalents for large simulation grids.
+ 794
+ 795    Parameters
+ 796    ----------
+ 797    grid : np.ndarray
+ 798        A 2D array representing the simulation environment.
+ 799    species : int
+ 800        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+ 801    neighborhood : {'moore', 'neumann'}, optional
+ 802        The connectivity rule. 'moore' uses 8-way connectivity; 'neumann'
+ 803        uses 4-way connectivity. Default is 'moore'.
+ 804
+ 805    Returns
+ 806    -------
+ 807    labels : np.ndarray
+ 808        A 2D int32 array where each cell contains its unique cluster ID.
+ 809        Cells not belonging to the target species are 0.
+ 810    sizes : dict
+ 811        A dictionary mapping cluster IDs to their respective cell counts.
+ 812
+ 813    Notes
+ 814    -----
+ 815    The underlying Numba kernel uses a stack-based flood fill to avoid
+ 816    recursion limits and handles periodic boundary conditions.
+ 817
+ 818    Examples
+ 819    --------
+ 820    >>> labels, sizes = detect_clusters_fast(grid, species=1)
+ 821    >>> if sizes:
+ 822    ...     largest_id = max(sizes, key=sizes.get)
+ 823    ...     print(f"Cluster {largest_id} size: {sizes[largest_id]}")
+ 824    """
+ 825    grid_int = np.asarray(grid, dtype=np.int32)
+ 826    moore = neighborhood == "moore"
+ 827    labels, sizes_arr = _detect_clusters_numba(grid_int, np.int32(species), moore)
+ 828    sizes_dict = {i + 1: int(sizes_arr[i]) for i in range(len(sizes_arr))}
+ 829    return labels, sizes_dict
+ 830
+ 831
+ 832def get_cluster_stats_fast(
+ 833    grid: np.ndarray,
+ 834    species: int,
+ 835    neighborhood: str = "moore",
+ 836) -> Dict:
+ 837    """
+ 838    Compute comprehensive cluster statistics for a species using Numba acceleration.
+ 839
+ 840    This function integrates cluster detection and labeling to provide a
+ 841    full suite of spatial metrics. It calculates the cluster size distribution
+ 842    and the largest cluster fraction, which often serves as an order
+ 843    parameter in percolation theory and Phase 1-3 analyses.
+ 844
+ 845    Parameters
+ 846    ----------
+ 847    grid : np.ndarray
+ 848        A 2D array representing the simulation environment.
+ 849    species : int
+ 850        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+ 851    neighborhood : {'moore', 'neumann'}, optional
+ 852        The connectivity rule. 'moore' uses 8-way connectivity; 'neumann'
+ 853        uses 4-way connectivity. Default is 'moore'.
+ 854
+ 855    Returns
+ 856    -------
+ 857    stats : dict
+ 858        A dictionary containing:
+ 859        - 'n_clusters': Total count of isolated clusters.
+ 860        - 'sizes': Sorted array (descending) of all cluster sizes.
+ 861        - 'largest': Size of the single largest cluster.
+ 862        - 'largest_fraction': Size of the largest cluster divided by
+ 863          the total population of the species.
+ 864        - 'mean_size': Average size of all clusters.
+ 865        - 'size_distribution': Frequency mapping of {size: count}.
+ 866        - 'labels': 2D array of unique cluster IDs.
+ 867        - 'size_dict': Mapping of {label_id: size}.
+ 868
+ 869    Examples
+ 870    --------
+ 871    >>> stats = get_cluster_stats_fast(grid, species=1)
+ 872    >>> print(f"Found {stats['n_clusters']} prey clusters.")
+ 873    >>> print(f"Order parameter: {stats['largest_fraction']:.3f}")
+ 874    """
+ 875    labels, size_dict = detect_clusters_fast(grid, species, neighborhood)
+ 876
+ 877    if len(size_dict) == 0:
+ 878        return {
+ 879            "n_clusters": 0,
+ 880            "sizes": np.array([], dtype=np.int32),
+ 881            "largest": 0,
+ 882            "largest_fraction": 0.0,
+ 883            "mean_size": 0.0,
+ 884            "size_distribution": {},
+ 885            "labels": labels,
+ 886            "size_dict": size_dict,
+ 887        }
+ 888
+ 889    sizes = np.array(list(size_dict.values()), dtype=np.int32)
+ 890    sizes_sorted = np.sort(sizes)[::-1]
+ 891    total_pop = int(np.sum(sizes))
+ 892    largest = int(sizes_sorted[0])
+ 893
+ 894    size_dist = {}
+ 895    for s in sizes:
+ 896        s_int = int(s)
+ 897        size_dist[s_int] = size_dist.get(s_int, 0) + 1
+ 898
+ 899    return {
+ 900        "n_clusters": len(size_dict),
+ 901        "sizes": sizes_sorted,
+ 902        "largest": largest,
+ 903        "largest_fraction": float(largest) / total_pop if total_pop > 0 else 0.0,
+ 904        "mean_size": float(np.mean(sizes)),
+ 905        "size_distribution": size_dist,
+ 906        "labels": labels,
+ 907        "size_dict": size_dict,
+ 908    }
+ 909
+ 910
+ 911# ============================================================================
+ 912# PCF COMPUTATION (Cell-list accelerated)
+ 913# ============================================================================
+ 914
+ 915
+ 916@njit(cache=True)
+ 917def _build_cell_list(
+ 918    positions: np.ndarray,
+ 919    n_cells: int,
+ 920    L_row: float,
+ 921    L_col: float,
+ 922) -> Tuple[np.ndarray, np.ndarray, np.ndarray, float, float]:
+ 923    """
+ 924    Build a cell list for spatial hashing to accelerate neighbor lookups.
+ 925
+ 926    This Numba-optimized function partitions a set of coordinates into a
+ 927    grid of cells. It uses a three-pass approach to calculate cell occupancy,
+ 928    compute starting offsets for each cell in a flat index array, and finally
+ 929    populate that array with position indices.
+ 930
+ 931    Parameters
+ 932    ----------
+ 933    positions : np.ndarray
+ 934        An (N, 2) float array of coordinates within the simulation domain.
+ 935    n_cells : int
+ 936        The number of cells along one dimension of the square grid.
+ 937    L_row : float
+ 938        The total height (row extent) of the simulation domain.
+ 939    L_col : float
+ 940        The total width (column extent) of the simulation domain.
+ 941
+ 942    Returns
+ 943    -------
+ 944    indices : np.ndarray
+ 945        A 1D array of original position indices, reordered so that indices
+ 946        belonging to the same cell are contiguous.
+ 947    offsets : np.ndarray
+ 948        A 2D array where `offsets[r, c]` is the starting index in the
+ 949        `indices` array for cell (r, c).
+ 950    cell_counts : np.ndarray
+ 951        A 2D array where `cell_counts[r, c]` is the number of points
+ 952        contained in cell (r, c).
+ 953    cell_size_r : float
+ 954        The calculated height of an individual cell.
+ 955    cell_size_c : float
+ 956        The calculated width of an individual cell.
+ 957
+ 958    Notes
+ 959    -----
+ 960    This implementation assumes periodic boundary conditions via the
+ 961    modulo operator during coordinate-to-cell mapping. It is designed to
+ 962    eliminate heap allocations within the main simulation loop by using
+ 963    Numba's efficient array handling.
+ 964    """
+ 965    n_pos = len(positions)
+ 966    cell_size_r = L_row / n_cells
+ 967    cell_size_c = L_col / n_cells
+ 968
+ 969    cell_counts = np.zeros((n_cells, n_cells), dtype=np.int32)
+ 970    for i in range(n_pos):
+ 971        cr = int(positions[i, 0] / cell_size_r) % n_cells
+ 972        cc = int(positions[i, 1] / cell_size_c) % n_cells
+ 973        cell_counts[cr, cc] += 1
+ 974
+ 975    offsets = np.zeros((n_cells, n_cells), dtype=np.int32)
+ 976    running = 0
+ 977    for cr in range(n_cells):
+ 978        for cc in range(n_cells):
+ 979            offsets[cr, cc] = running
+ 980            running += cell_counts[cr, cc]
+ 981
+ 982    indices = np.empty(n_pos, dtype=np.int32)
+ 983    fill_counts = np.zeros((n_cells, n_cells), dtype=np.int32)
+ 984    for i in range(n_pos):
+ 985        cr = int(positions[i, 0] / cell_size_r) % n_cells
+ 986        cc = int(positions[i, 1] / cell_size_c) % n_cells
+ 987        idx = offsets[cr, cc] + fill_counts[cr, cc]
+ 988        indices[idx] = i
+ 989        fill_counts[cr, cc] += 1
+ 990
+ 991    return indices, offsets, cell_counts, cell_size_r, cell_size_c
+ 992
+ 993
+ 994@njit(cache=True)
+ 995def _periodic_dist_sq(
+ 996    r1: float,
+ 997    c1: float,
+ 998    r2: float,
+ 999    c2: float,
+1000    L_row: float,
+1001    L_col: float,
+1002) -> float:
+1003    """
+1004    Calculate the squared Euclidean distance between two points with periodic boundary conditions.
+1005
+1006    This Numba-optimized function accounts for toroidal topology by finding the
+1007    shortest path between coordinates across the grid edges. Using the squared
+1008    distance avoids the computational expense of a square root operation,
+1009    making it ideal for high-frequency spatial queries.
+1010
+1011    Parameters
+1012    ----------
+1013    r1 : float
+1014        Row coordinate of the first point.
+1015    c1 : float
+1016        Column coordinate of the first point.
+1017    r2 : float
+1018        Row coordinate of the second point.
+1019    c2 : float
+1020        Column coordinate of the second point.
+1021    L_row : float
+1022        Total height (row extent) of the periodic domain.
+1023    L_col : float
+1024        Total width (column extent) of the periodic domain.
+1025
+1026    Returns
+1027    -------
+1028    dist_sq : float
+1029        The squared shortest distance between the two points.
+1030
+1031    Notes
+1032    -----
+1033    The function applies the minimum image convention, ensuring that the
+1034    distance never exceeds half the domain length in any dimension.
+1035    """
+1036    dr = abs(r1 - r2)
+1037    dc = abs(c1 - c2)
+1038    if dr > L_row * 0.5:
+1039        dr = L_row - dr
+1040    if dc > L_col * 0.5:
+1041        dc = L_col - dc
+1042    return dr * dr + dc * dc
+1043
+1044
+1045@njit(parallel=True, cache=True)
+1046def _pcf_cell_list(
+1047    pos_i: np.ndarray,
+1048    pos_j: np.ndarray,
+1049    indices_j: np.ndarray,
+1050    offsets_j: np.ndarray,
+1051    counts_j: np.ndarray,
+1052    cell_size_r: float,
+1053    cell_size_c: float,
+1054    L_row: float,
+1055    L_col: float,
+1056    max_distance: float,
+1057    n_bins: int,
+1058    self_correlation: bool,
+1059    n_cells: int,
+1060) -> np.ndarray:
+1061    """
+1062    Compute a Pair Correlation Function (PCF) histogram using spatial cell lists.
+1063
+1064    This Numba-accelerated parallel kernel calculates distances between two sets
+1065    of points (pos_i and pos_j). It uses a cell list (spatial hashing) to
+1066    restrict distance calculations to neighboring cells within the maximum
+1067    specified distance, significantly improving performance from $O(N^2)$
+1068    to $O(N)$.
+1069
+1070    Parameters
+1071    ----------
+1072    pos_i : np.ndarray
+1073        (N, 2) float array of coordinates for the primary species.
+1074    pos_j : np.ndarray
+1075        (M, 2) float array of coordinates for the secondary species.
+1076    indices_j : np.ndarray
+1077        Flattened indices of pos_j sorted by cell, produced by `_build_cell_list`.
+1078    offsets_j : np.ndarray
+1079        2D array of starting offsets for each cell in `indices_j`.
+1080    counts_j : np.ndarray
+1081        2D array of particle counts within each cell for species J.
+1082    cell_size_r : float
+1083        Height of a single spatial cell.
+1084    cell_size_c : float
+1085        Width of a single spatial cell.
+1086    L_row : float
+1087        Total height of the periodic domain.
+1088    L_col : float
+1089        Total width of the periodic domain.
+1090    max_distance : float
+1091        Maximum radial distance (r) to consider for the correlation.
+1092    n_bins : int
+1093        Number of bins in the distance histogram.
+1094    self_correlation : bool
+1095        If True, assumes species I and J are the same and avoids double-counting
+1096        or self-interaction.
+1097    n_cells : int
+1098        Number of cells per dimension in the spatial hash grid.
+1099
+1100    Returns
+1101    -------
+1102    hist : np.ndarray
+1103        A 1D array of length `n_bins` containing the counts of pairs found
+1104        at each radial distance.
+1105
+1106    Notes
+1107    -----
+1108    The kernel uses `prange` for parallel execution across points in `pos_i`.
+1109    Local histograms are used per thread to prevent race conditions during
+1110    reduction. Periodic boundary conditions are handled via `_periodic_dist_sq`.
+1111    """
+1112    n_i = len(pos_i)
+1113    bin_width = max_distance / n_bins
+1114    max_dist_sq = max_distance * max_distance
+1115    cells_to_check = int(np.ceil(max_distance / min(cell_size_r, cell_size_c))) + 1
+1116
+1117    hist = np.zeros(n_bins, dtype=np.int64)
+1118
+1119    for i in prange(n_i):
+1120        local_hist = np.zeros(n_bins, dtype=np.int64)
+1121        r1, c1 = pos_i[i, 0], pos_i[i, 1]
+1122
+1123        cell_r = int(r1 / cell_size_r) % n_cells
+1124        cell_c = int(c1 / cell_size_c) % n_cells
+1125
+1126        for dcr in range(-cells_to_check, cells_to_check + 1):
+1127            for dcc in range(-cells_to_check, cells_to_check + 1):
+1128                ncr = (cell_r + dcr) % n_cells
+1129                ncc = (cell_c + dcc) % n_cells
+1130
+1131                start = offsets_j[ncr, ncc]
+1132                end = start + counts_j[ncr, ncc]
+1133
+1134                for idx in range(start, end):
+1135                    j = indices_j[idx]
+1136
+1137                    if self_correlation and j <= i:
+1138                        continue
+1139
+1140                    r2, c2 = pos_j[j, 0], pos_j[j, 1]
+1141                    d_sq = _periodic_dist_sq(r1, c1, r2, c2, L_row, L_col)
+1142
+1143                    if 0 < d_sq < max_dist_sq:
+1144                        d = np.sqrt(d_sq)
+1145                        bin_idx = int(d / bin_width)
+1146                        if bin_idx >= n_bins:
+1147                            bin_idx = n_bins - 1
+1148                        local_hist[bin_idx] += 1
+1149
+1150        for b in range(n_bins):
+1151            hist[b] += local_hist[b]
+1152
+1153    if self_correlation:
+1154        for b in range(n_bins):
+1155            hist[b] *= 2
+1156
+1157    return hist
+1158
+1159
+1160def compute_pcf_periodic_fast(
+1161    positions_i: np.ndarray,
+1162    positions_j: np.ndarray,
+1163    grid_shape: Tuple[int, int],
+1164    max_distance: float,
+1165    n_bins: int = 50,
+1166    self_correlation: bool = False,
+1167) -> Tuple[np.ndarray, np.ndarray, int]:
+1168    """
+1169    Compute the Pair Correlation Function (PCF) using cell-list acceleration.
+1170
+1171    This high-level function coordinates the spatial hashing and histogram
+1172    calculation to determine the $g(r)$ function. It normalizes the resulting
+1173    histogram by the expected number of pairs in an ideal gas of the same
+1174    density, accounting for the toroidal area of each radial bin.
+1175
+1176    Parameters
+1177    ----------
+1178    positions_i : np.ndarray
+1179        (N, 2) array of coordinates for species I.
+1180    positions_j : np.ndarray
+1181        (M, 2) array of coordinates for species J.
+1182    grid_shape : tuple of int
+1183        The (rows, cols) dimensions of the simulation grid.
+1184    max_distance : float
+1185        The maximum radius to calculate correlations for.
+1186    n_bins : int, optional
+1187        Number of bins for the radial distribution (default 50).
+1188    self_correlation : bool, optional
+1189        Set to True if computing the correlation of a species with itself
+1190        to avoid self-counting (default False).
+1191
+1192    Returns
+1193    -------
+1194    bin_centers : np.ndarray
+1195        The central radial distance for each histogram bin.
+1196    pcf : np.ndarray
+1197        The normalized $g(r)$ values. A value of 1.0 indicates no spatial
+1198        correlation; > 1.0 indicates clustering; < 1.0 indicates repulsion.
+1199    total_pairs : int
+1200        The total count of pairs found within the `max_distance`.
+1201
+1202    Notes
+1203    -----
+1204    The function dynamically determines the optimal number of cells for the
+1205    spatial hash based on the `max_distance` and grid dimensions to maintain
+1206    linear time complexity.
+1207    """
+1208    rows, cols = grid_shape
+1209    L_row, L_col = float(rows), float(cols)
+1210    area = L_row * L_col
+1211
+1212    bin_width = max_distance / n_bins
+1213    bin_centers = np.linspace(bin_width / 2, max_distance - bin_width / 2, n_bins)
+1214
+1215    if len(positions_i) == 0 or len(positions_j) == 0:
+1216        return bin_centers, np.ones(n_bins), 0
+1217
+1218    n_cells = max(4, int(min(rows, cols) / max_distance))
+1219
+1220    pos_i = np.ascontiguousarray(positions_i, dtype=np.float64)
+1221    pos_j = np.ascontiguousarray(positions_j, dtype=np.float64)
+1222
+1223    indices_j, offsets_j, counts_j, cell_size_r, cell_size_c = _build_cell_list(
+1224        pos_j, n_cells, L_row, L_col
+1225    )
+1226
+1227    hist = _pcf_cell_list(
+1228        pos_i,
+1229        pos_j,
+1230        indices_j,
+1231        offsets_j,
+1232        counts_j,
+1233        cell_size_r,
+1234        cell_size_c,
+1235        L_row,
+1236        L_col,
+1237        max_distance,
+1238        n_bins,
+1239        self_correlation,
+1240        n_cells,
+1241    )
+1242
+1243    n_i, n_j = len(positions_i), len(positions_j)
+1244    if self_correlation:
+1245        density_product = n_i * (n_i - 1) / (area * area)
+1246    else:
+1247        density_product = n_i * n_j / (area * area)
+1248
+1249    expected = np.zeros(n_bins)
+1250    for i in range(n_bins):
+1251        r = bin_centers[i]
+1252        annulus_area = 2 * np.pi * r * bin_width
+1253        expected[i] = density_product * annulus_area * area
+1254
+1255    pcf = np.ones(n_bins)
+1256    mask = expected > 1.0
+1257    pcf[mask] = hist[mask] / expected[mask]
+1258
+1259    return bin_centers, pcf, int(np.sum(hist))
+1260
+1261
+1262def compute_all_pcfs_fast(
+1263    grid: np.ndarray,
+1264    max_distance: Optional[float] = None,
+1265    n_bins: int = 50,
+1266) -> Dict[str, Tuple[np.ndarray, np.ndarray, int]]:
+1267    """
+1268    Compute all three species Pair Correlation Functions (PCFs) using cell-list acceleration.
+1269
+1270    This function calculates the spatial auto-correlations (Prey-Prey,
+1271    Predator-Predator) and the cross-correlation (Prey-Predator) for a given
+1272    simulation grid. It identifies particle positions and leverages
+1273    Numba-accelerated cell lists to handle the computations efficiently.
+1274
+1275    Parameters
+1276    ----------
+1277    grid : np.ndarray
+1278        2D integer array where 1 represents prey and 2 represents predators.
+1279    max_distance : float, optional
+1280        The maximum radial distance for the correlation. Defaults to 1/4
+1281        of the minimum grid dimension if not provided.
+1282    n_bins : int, optional
+1283        Number of distance bins for the histogram. Default is 50.
+1284
+1285    Returns
+1286    -------
+1287    results : dict
+1288        A dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'.
+1289        Each value is a tuple containing:
+1290        - bin_centers (np.ndarray): Radial distances.
+1291        - pcf_values (np.ndarray): Normalized g(r) values.
+1292        - pair_count (int): Total number of pairs found.
+1293
+1294    Notes
+1295    -----
+1296    The PCF provides insight into the spatial organization of the system.
+1297    g(r) > 1 at short distances indicates aggregation (clustering),
+1298    while g(r) < 1 indicates exclusion or repulsion.
+1299    """
+1300    rows, cols = grid.shape
+1301    if max_distance is None:
+1302        max_distance = min(rows, cols) / 4.0
+1303
+1304    prey_pos = np.argwhere(grid == 1)
+1305    pred_pos = np.argwhere(grid == 2)
+1306
+1307    results = {}
+1308
+1309    dist, pcf, n = compute_pcf_periodic_fast(
+1310        prey_pos,
+1311        prey_pos,
+1312        (rows, cols),
+1313        max_distance,
+1314        n_bins,
+1315        self_correlation=True,
+1316    )
+1317    results["prey_prey"] = (dist, pcf, n)
+1318
+1319    dist, pcf, n = compute_pcf_periodic_fast(
+1320        pred_pos,
+1321        pred_pos,
+1322        (rows, cols),
+1323        max_distance,
+1324        n_bins,
+1325        self_correlation=True,
+1326    )
+1327    results["pred_pred"] = (dist, pcf, n)
+1328
+1329    dist, pcf, n = compute_pcf_periodic_fast(
+1330        prey_pos,
+1331        pred_pos,
+1332        (rows, cols),
+1333        max_distance,
+1334        n_bins,
+1335        self_correlation=False,
+1336    )
+1337    results["prey_pred"] = (dist, pcf, n)
+1338
+1339    return results
+1340
+1341
+1342# ============================================================================
+1343# WARMUP & BENCHMARKS
+1344# ============================================================================
+1345
+1346
+1347def warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False):
+1348    """
+1349    Pre-compile all Numba-accelerated kernels to avoid first-run latency.
+1350
+1351    This function executes a single step of the simulation and each analysis
+1352    routine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation,
+1353    the first call to a decorated function incurs a compilation overhead.
+1354    Running this warmup ensures that subsequent experimental runs are timed
+1355    accurately and perform at full speed.
+1356
+1357    Parameters
+1358    ----------
+1359    grid_size : int, optional
+1360        The side length of the dummy grid used for warmup (default 100).
+1361    directed_hunting : bool, optional
+1362        If True, also warms up the directed behavior update kernel (default False).
+1363
+1364    Returns
+1365    -------
+1366    None
+1367
+1368    Notes
+1369    -----
+1370    This function checks for `NUMBA_AVAILABLE` before execution. It warms up
+1371    the `PPKernel` (random and optionally directed), as well as the
+1372    spatial analysis functions (`compute_all_pcfs_fast`, `detect_clusters_fast`, etc.).
+1373    """
+1374    if not NUMBA_AVAILABLE:
+1375        return
+1376
+1377    set_numba_seed(0)
+1378
+1379    grid = np.zeros((grid_size, grid_size), dtype=np.int32)
+1380    grid[::3, ::3] = 1
+1381    grid[::5, ::5] = 2
+1382
+1383    prey_death_arr = np.full((grid_size, grid_size), 0.05, dtype=np.float64)
+1384    prey_death_arr[grid != 1] = np.nan
+1385
+1386    # Always warmup random kernel
+1387    kernel_random = PPKernel(grid_size, grid_size, directed_hunting=False)
+1388    kernel_random.update(grid.copy(), prey_death_arr.copy(), 0.2, 0.05, 0.2, 0.1)
+1389
+1390    # Warmup directed kernel if requested
+1391    if directed_hunting:
+1392        kernel_directed = PPKernel(grid_size, grid_size, directed_hunting=True)
+1393        kernel_directed.update(grid.copy(), prey_death_arr.copy(), 0.2, 0.05, 0.2, 0.1)
+1394
+1395    # Warmup analysis functions
+1396    _ = compute_all_pcfs_fast(grid, max_distance=20.0, n_bins=20)
+1397    _ = measure_cluster_sizes_fast(grid, 1)
+1398    _ = detect_clusters_fast(grid, 1)
+1399    _ = get_cluster_stats_fast(grid, 1)
+1400
+1401
+1402def benchmark_kernels(grid_size: int = 100, n_runs: int = 20):
+1403    """
+1404    Benchmark the execution performance of random vs. directed update kernels.
+1405
+1406    This utility measures the average time per simulation step for both the
+1407    stochastic (random neighbor) and heuristic (directed hunting/reproduction)
+1408    update strategies. It accounts for the computational overhead introduced
+1409    by the "intelligent" search logic used in directed mode.
+1410
+1411    Parameters
+1412    ----------
+1413    grid_size : int, optional
+1414        The side length of the square simulation grid (default 100).
+1415    n_runs : int, optional
+1416        The number of iterations to perform for averaging performance (default 20).
+1417
+1418    Returns
+1419    -------
+1420    t_random : float
+1421        Average time per step for the random kernel in milliseconds.
+1422    t_directed : float
+1423        Average time per step for the directed kernel in milliseconds.
+1424
+1425    Notes
+1426    -----
+1427    The function ensures a fair comparison by:
+1428    1. Using a fixed seed for reproducible initial grid states.
+1429    2. Warming up Numba kernels before timing to exclude JIT compilation latency.
+1430    3. Copying the grid and death arrays for each iteration to maintain
+1431       consistent population densities throughout the benchmark.
+1432    """
+1433    import time
+1434
+1435    print("=" * 60)
+1436    print(f"KERNEL BENCHMARK ({grid_size}x{grid_size}, {n_runs} runs)")
+1437    print(f"Numba available: {NUMBA_AVAILABLE}")
+1438    print("=" * 60)
+1439
+1440    np.random.seed(42)
+1441    grid = np.zeros((grid_size, grid_size), dtype=np.int32)
+1442    n_prey = int(grid_size * grid_size * 0.30)
+1443    n_pred = int(grid_size * grid_size * 0.15)
+1444    positions = np.random.permutation(grid_size * grid_size)
+1445    for pos in positions[:n_prey]:
+1446        grid[pos // grid_size, pos % grid_size] = 1
+1447    for pos in positions[n_prey : n_prey + n_pred]:
+1448        grid[pos // grid_size, pos % grid_size] = 2
+1449
+1450    prey_death_arr = np.full((grid_size, grid_size), 0.05, dtype=np.float64)
+1451    prey_death_arr[grid != 1] = np.nan
+1452
+1453    print(f"Initial: {np.sum(grid == 1)} prey, {np.sum(grid == 2)} predators")
+1454
+1455    # Warmup both kernels
+1456    warmup_numba_kernels(grid_size, directed_hunting=True)
+1457
+1458    # Benchmark random kernel
+1459    kernel_random = PPKernel(grid_size, grid_size, directed_hunting=False)
+1460    t0 = time.perf_counter()
+1461    for _ in range(n_runs):
+1462        test_grid = grid.copy()
+1463        test_arr = prey_death_arr.copy()
+1464        kernel_random.update(test_grid, test_arr, 0.2, 0.05, 0.2, 0.1)
+1465    t_random = (time.perf_counter() - t0) / n_runs * 1000
+1466
+1467    # Benchmark directed kernel
+1468    kernel_directed = PPKernel(grid_size, grid_size, directed_hunting=True)
+1469    t0 = time.perf_counter()
+1470    for _ in range(n_runs):
+1471        test_grid = grid.copy()
+1472        test_arr = prey_death_arr.copy()
+1473        kernel_directed.update(test_grid, test_arr, 0.2, 0.05, 0.2, 0.1)
+1474    t_directed = (time.perf_counter() - t0) / n_runs * 1000
+1475
+1476    print(f"\nRandom kernel:   {t_random:.2f} ms/step")
+1477    print(f"Directed kernel: {t_directed:.2f} ms/step")
+1478    print(
+1479        f"Overhead:        {t_directed - t_random:.2f} ms (+{100*(t_directed/t_random - 1):.1f}%)"
+1480    )
+1481
+1482    return t_random, t_directed
+1483
+1484
+1485def benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20):
+1486    """
+1487    Benchmark the performance of different cluster detection and analysis routines.
+1488
+1489    This function evaluates three levels of spatial analysis:
+1490    1. Size measurement only (fastest, no label map).
+1491    2. Full detection (returns label map and size dictionary).
+1492    3. Comprehensive statistics (calculates distributions, means, and order parameters).
+1493
+1494    Parameters
+1495    ----------
+1496    grid_size : int, optional
+1497        Side length of the square grid for benchmarking (default 100).
+1498    n_runs : int, optional
+1499        Number of iterations to average for performance results (default 20).
+1500
+1501    Returns
+1502    -------
+1503    stats : dict
+1504        The result dictionary from the final comprehensive statistics run.
+1505
+1506    Notes
+1507    -----
+1508    The benchmark uses a fixed prey density of 30% to ensure a representative
+1509    distribution of clusters. It pre-warms the Numba kernels to ensure that
+1510    the measurements reflect execution speed rather than compilation time.
+1511    """
+1512    import time
+1513
+1514    print("=" * 60)
+1515    print(f"CLUSTER DETECTION BENCHMARK ({grid_size}x{grid_size})")
+1516    print(f"Numba available: {NUMBA_AVAILABLE}")
+1517    print("=" * 60)
+1518
+1519    np.random.seed(42)
+1520    grid = np.zeros((grid_size, grid_size), dtype=np.int32)
+1521    n_prey = int(grid_size * grid_size * 0.30)
+1522    positions = np.random.permutation(grid_size * grid_size)[:n_prey]
+1523    for pos in positions:
+1524        grid[pos // grid_size, pos % grid_size] = 1
+1525
+1526    print(f"Prey cells: {np.sum(grid == 1)}")
+1527
+1528    # Warmup
+1529    _ = measure_cluster_sizes_fast(grid, 1)
+1530    _ = detect_clusters_fast(grid, 1)
+1531    _ = get_cluster_stats_fast(grid, 1)
+1532
+1533    # Benchmark sizes only
+1534    t0 = time.perf_counter()
+1535    for _ in range(n_runs):
+1536        sizes = measure_cluster_sizes_fast(grid, 1)
+1537    t_sizes = (time.perf_counter() - t0) / n_runs * 1000
+1538    print(f"\nmeasure_cluster_sizes_fast: {t_sizes:.2f} ms  ({len(sizes)} clusters)")
+1539
+1540    # Benchmark full detection
+1541    t0 = time.perf_counter()
+1542    for _ in range(n_runs):
+1543        labels, size_dict = detect_clusters_fast(grid, 1)
+1544    t_detect = (time.perf_counter() - t0) / n_runs * 1000
+1545    print(f"detect_clusters_fast:       {t_detect:.2f} ms  ({len(size_dict)} clusters)")
+1546
+1547    # Benchmark full stats
+1548    t0 = time.perf_counter()
+1549    for _ in range(n_runs):
+1550        stats = get_cluster_stats_fast(grid, 1)
+1551    t_stats = (time.perf_counter() - t0) / n_runs * 1000
+1552    print(f"get_cluster_stats_fast:     {t_stats:.2f} ms")
+1553
+1554    print(
+1555        f"\nOverhead for labels: {t_detect - t_sizes:.2f} ms (+{100*(t_detect/t_sizes - 1):.0f}%)"
+1556    )
+1557
+1558    return stats
+1559
+1560
+1561if __name__ == "__main__":
+1562    print("\n" + "=" * 60)
+1563    print("NUMBA-OPTIMIZED PP MODULE - BENCHMARKS")
+1564    print("=" * 60 + "\n")
+1565
+1566    # Run kernel benchmarks
+1567    benchmark_kernels(100)
+1568
+1569    print("\n")
+1570
+1571    # Run cluster benchmarks
+1572    stats = benchmark_cluster_detection(100)
+1573    print(
+1574        f"\nSample stats: largest={stats['largest']}, "
+1575        f"largest_fraction={stats['largest_fraction']:.3f}, "
+1576        f"n_clusters={stats['n_clusters']}"
+1577    )
+
+ + +
+
+ +
+
@njit(cache=True)
+ + def + set_numba_seed(seed: int) -> None: + + + +
+ +
49@njit(cache=True)
+50def set_numba_seed(seed: int) -> None:
+51    """
+52    Seed Numba's internal random number generator from within a JIT context.
+53
+54    This function ensures that Numba's independent random number generator
+55    is synchronized with the provided seed, enabling reproducibility for
+56    jit-compiled functions that use NumPy's random operations.
+57
+58    Parameters
+59    ----------
+60    seed : int
+61        The integer value used to initialize the random number generator.
+62
+63    Returns
+64    -------
+65    None
+66
+67    Notes
+68    -----
+69    Because Numba maintains its own internal state for random number
+70    generation, calling `np.random.seed()` in standard Python code will not
+71    affect jit-compiled functions. This helper must be called to bridge
+72    that gap.
+73    """
+74    np.random.seed(seed)
+
+ + +

Seed Numba's internal random number generator from within a JIT context.

+ +

This function ensures that Numba's independent random number generator +is synchronized with the provided seed, enabling reproducibility for +jit-compiled functions that use NumPy's random operations.

+ +
Parameters
+ +
    +
  • seed (int): +The integer value used to initialize the random number generator.
  • +
+ +
Returns
+ +
    +
  • None
  • +
+ +
Notes
+ +

Because Numba maintains its own internal state for random number +generation, calling np.random.seed() in standard Python code will not +affect jit-compiled functions. This helper must be called to bridge +that gap.

+
+ + +
+
+ +
+ + class + PPKernel: + + + +
+ +
392class PPKernel:
+393    """
+394    Wrapper for predator-prey kernel with pre-allocated buffers.
+395
+396    This class manages the spatial configuration and memory buffers required
+397    for the Numba-accelerated update kernels. By pre-allocating the
+398    `occupied_buffer`, it avoids expensive memory allocations during the
+399    simulation loop.
+400
+401    Parameters
+402    ----------
+403    rows : int
+404        Number of rows in the simulation grid.
+405    cols : int
+406        Number of columns in the simulation grid.
+407    neighborhood : {'moore', 'von_neumann'}, optional
+408        The neighborhood type determining adjacent cells. 'moore' includes
+409        diagonals (8 neighbors), 'von_neumann' does not (4 neighbors).
+410        Default is 'moore'.
+411    directed_hunting : bool, optional
+412        If True, uses the directed behavior kernel where species search for
+413        targets. If False, uses random neighbor selection. Default is False.
+414
+415    Attributes
+416    ----------
+417    rows : int
+418        Grid row count.
+419    cols : int
+420        Grid column count.
+421    directed_hunting : bool
+422        Toggle for intelligent behavior logic.
+423    """
+424
+425    def __init__(
+426        self,
+427        rows: int,
+428        cols: int,
+429        neighborhood: str = "moore",
+430        directed_hunting: bool = False,
+431    ):
+432        self.rows = rows
+433        self.cols = cols
+434        self.directed_hunting = directed_hunting
+435        self._occupied_buffer = np.empty((rows * cols, 2), dtype=np.int32)
+436
+437        if neighborhood == "moore":
+438            self._dr = np.array([-1, -1, -1, 0, 0, 1, 1, 1], dtype=np.int32)
+439            self._dc = np.array([-1, 0, 1, -1, 1, -1, 0, 1], dtype=np.int32)
+440        else:  # von Neumann
+441            self._dr = np.array([-1, 1, 0, 0], dtype=np.int32)
+442            self._dc = np.array([0, 0, -1, 1], dtype=np.int32)
+443
+444    def update(
+445        self,
+446        grid: np.ndarray,
+447        prey_death_arr: np.ndarray,
+448        prey_birth: float,
+449        prey_death: float,
+450        pred_birth: float,
+451        pred_death: float,
+452        evolve_sd: float = 0.1,
+453        evolve_min: float = 0.001,
+454        evolve_max: float = 0.1,
+455        evolution_stopped: bool = True,
+456    ) -> np.ndarray:
+457        """
+458        Execute a single asynchronous update step using the configured kernel.
+459
+460        Parameters
+461        ----------
+462        grid : np.ndarray
+463            The current 2D simulation grid.
+464        prey_death_arr : np.ndarray
+465            2D array of individual prey mortality rates.
+466        prey_birth : float
+467            Prey reproduction probability.
+468        prey_death : float
+469            Base prey mortality probability.
+470        pred_birth : float
+471            Predator reproduction (hunting success) probability.
+472        pred_death : float
+473            Predator mortality probability.
+474        evolve_sd : float, optional
+475            Mutation standard deviation (default 0.1).
+476        evolve_min : float, optional
+477            Minimum evolved death rate (default 0.001).
+478        evolve_max : float, optional
+479            Maximum evolved death rate (default 0.1).
+480        evolution_stopped : bool, optional
+481            Whether to disable mutation during this step (default True).
+482
+483        Returns
+484        -------
+485        np.ndarray
+486            The updated grid after one full asynchronous pass.
+487        """
+488        if self.directed_hunting:
+489            return _pp_async_kernel_directed(
+490                grid,
+491                prey_death_arr,
+492                prey_birth,
+493                prey_death,
+494                pred_birth,
+495                pred_death,
+496                self._dr,
+497                self._dc,
+498                evolve_sd,
+499                evolve_min,
+500                evolve_max,
+501                evolution_stopped,
+502                self._occupied_buffer,
+503            )
+504        else:
+505            return _pp_async_kernel_random(
+506                grid,
+507                prey_death_arr,
+508                prey_birth,
+509                prey_death,
+510                pred_birth,
+511                pred_death,
+512                self._dr,
+513                self._dc,
+514                evolve_sd,
+515                evolve_min,
+516                evolve_max,
+517                evolution_stopped,
+518                self._occupied_buffer,
+519            )
+
+ + +

Wrapper for predator-prey kernel with pre-allocated buffers.

+ +

This class manages the spatial configuration and memory buffers required +for the Numba-accelerated update kernels. By pre-allocating the +occupied_buffer, it avoids expensive memory allocations during the +simulation loop.

+ +
Parameters
+ +
    +
  • rows (int): +Number of rows in the simulation grid.
  • +
  • cols (int): +Number of columns in the simulation grid.
  • +
  • neighborhood ({'moore', 'von_neumann'}, optional): +The neighborhood type determining adjacent cells. 'moore' includes +diagonals (8 neighbors), 'von_neumann' does not (4 neighbors). +Default is 'moore'.
  • +
  • directed_hunting (bool, optional): +If True, uses the directed behavior kernel where species search for +targets. If False, uses random neighbor selection. Default is False.
  • +
+ +
Attributes
+ +
    +
  • rows (int): +Grid row count.
  • +
  • cols (int): +Grid column count.
  • +
  • directed_hunting (bool): +Toggle for intelligent behavior logic.
  • +
+
+ + +
+ +
+ + PPKernel( rows: int, cols: int, neighborhood: str = 'moore', directed_hunting: bool = False) + + + +
+ +
425    def __init__(
+426        self,
+427        rows: int,
+428        cols: int,
+429        neighborhood: str = "moore",
+430        directed_hunting: bool = False,
+431    ):
+432        self.rows = rows
+433        self.cols = cols
+434        self.directed_hunting = directed_hunting
+435        self._occupied_buffer = np.empty((rows * cols, 2), dtype=np.int32)
+436
+437        if neighborhood == "moore":
+438            self._dr = np.array([-1, -1, -1, 0, 0, 1, 1, 1], dtype=np.int32)
+439            self._dc = np.array([-1, 0, 1, -1, 1, -1, 0, 1], dtype=np.int32)
+440        else:  # von Neumann
+441            self._dr = np.array([-1, 1, 0, 0], dtype=np.int32)
+442            self._dc = np.array([0, 0, -1, 1], dtype=np.int32)
+
+ + + + +
+
+
+ rows + + +
+ + + + +
+
+
+ cols + + +
+ + + + +
+
+
+ directed_hunting + + +
+ + + + +
+
+ +
+ + def + update( self, grid: numpy.ndarray, prey_death_arr: numpy.ndarray, prey_birth: float, prey_death: float, pred_birth: float, pred_death: float, evolve_sd: float = 0.1, evolve_min: float = 0.001, evolve_max: float = 0.1, evolution_stopped: bool = True) -> numpy.ndarray: + + + +
+ +
444    def update(
+445        self,
+446        grid: np.ndarray,
+447        prey_death_arr: np.ndarray,
+448        prey_birth: float,
+449        prey_death: float,
+450        pred_birth: float,
+451        pred_death: float,
+452        evolve_sd: float = 0.1,
+453        evolve_min: float = 0.001,
+454        evolve_max: float = 0.1,
+455        evolution_stopped: bool = True,
+456    ) -> np.ndarray:
+457        """
+458        Execute a single asynchronous update step using the configured kernel.
+459
+460        Parameters
+461        ----------
+462        grid : np.ndarray
+463            The current 2D simulation grid.
+464        prey_death_arr : np.ndarray
+465            2D array of individual prey mortality rates.
+466        prey_birth : float
+467            Prey reproduction probability.
+468        prey_death : float
+469            Base prey mortality probability.
+470        pred_birth : float
+471            Predator reproduction (hunting success) probability.
+472        pred_death : float
+473            Predator mortality probability.
+474        evolve_sd : float, optional
+475            Mutation standard deviation (default 0.1).
+476        evolve_min : float, optional
+477            Minimum evolved death rate (default 0.001).
+478        evolve_max : float, optional
+479            Maximum evolved death rate (default 0.1).
+480        evolution_stopped : bool, optional
+481            Whether to disable mutation during this step (default True).
+482
+483        Returns
+484        -------
+485        np.ndarray
+486            The updated grid after one full asynchronous pass.
+487        """
+488        if self.directed_hunting:
+489            return _pp_async_kernel_directed(
+490                grid,
+491                prey_death_arr,
+492                prey_birth,
+493                prey_death,
+494                pred_birth,
+495                pred_death,
+496                self._dr,
+497                self._dc,
+498                evolve_sd,
+499                evolve_min,
+500                evolve_max,
+501                evolution_stopped,
+502                self._occupied_buffer,
+503            )
+504        else:
+505            return _pp_async_kernel_random(
+506                grid,
+507                prey_death_arr,
+508                prey_birth,
+509                prey_death,
+510                pred_birth,
+511                pred_death,
+512                self._dr,
+513                self._dc,
+514                evolve_sd,
+515                evolve_min,
+516                evolve_max,
+517                evolution_stopped,
+518                self._occupied_buffer,
+519            )
+
+ + +

Execute a single asynchronous update step using the configured kernel.

+ +
Parameters
+ +
    +
  • grid (np.ndarray): +The current 2D simulation grid.
  • +
  • prey_death_arr (np.ndarray): +2D array of individual prey mortality rates.
  • +
  • prey_birth (float): +Prey reproduction probability.
  • +
  • prey_death (float): +Base prey mortality probability.
  • +
  • pred_birth (float): +Predator reproduction (hunting success) probability.
  • +
  • pred_death (float): +Predator mortality probability.
  • +
  • evolve_sd (float, optional): +Mutation standard deviation (default 0.1).
  • +
  • evolve_min (float, optional): +Minimum evolved death rate (default 0.001).
  • +
  • evolve_max (float, optional): +Maximum evolved death rate (default 0.1).
  • +
  • evolution_stopped (bool, optional): +Whether to disable mutation during this step (default True).
  • +
+ +
Returns
+ +
    +
  • np.ndarray: The updated grid after one full asynchronous pass.
  • +
+
+ + +
+
+
+ +
+ + def + measure_cluster_sizes_fast( grid: numpy.ndarray, species: int, neighborhood: str = 'moore') -> numpy.ndarray: + + + +
+ +
739def measure_cluster_sizes_fast(
+740    grid: np.ndarray,
+741    species: int,
+742    neighborhood: str = "moore",
+743) -> np.ndarray:
+744    """
+745    Measure cluster sizes for a specific species using Numba-accelerated flood fill.
+746
+747    This function provides a high-performance interface for calculating cluster
+748    size statistics without the overhead of generating a full label map. It is
+749    optimized for large-scale simulation analysis where only distribution
+750    metrics (e.g., mean size, max size) are required.
+751
+752    Parameters
+753    ----------
+754    grid : np.ndarray
+755        A 2D array representing the simulation environment.
+756    species : int
+757        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+758    neighborhood : {'moore', 'neumann'}, optional
+759        The connectivity rule. 'moore' uses 8-way connectivity (including diagonals);
+760        'neumann' uses 4-way connectivity. Default is 'moore'.
+761
+762    Returns
+763    -------
+764    cluster_sizes : np.ndarray
+765        A 1D array of integers, where each element is the cell count of an
+766        identified cluster.
+767
+768    Notes
+769    -----
+770    The input grid is cast to `int32` to ensure compatibility with the
+771    underlying JIT-compiled `_measure_clusters` kernel.
+772
+773    Examples
+774    --------
+775    >>> sizes = measure_cluster_sizes_fast(grid, species=1, neighborhood='moore')
+776    >>> if sizes.size > 0:
+777    ...     print(f"Largest cluster: {sizes.max()}")
+778    """
+779    grid_int = np.asarray(grid, dtype=np.int32)
+780    moore = neighborhood == "moore"
+781    return _measure_clusters(grid_int, np.int32(species), moore)
+
+ + +

Measure cluster sizes for a specific species using Numba-accelerated flood fill.

+ +

This function provides a high-performance interface for calculating cluster +size statistics without the overhead of generating a full label map. It is +optimized for large-scale simulation analysis where only distribution +metrics (e.g., mean size, max size) are required.

+ +
Parameters
+ +
    +
  • grid (np.ndarray): +A 2D array representing the simulation environment.
  • +
  • species (int): +The target species identifier (e.g., 1 for Prey, 2 for Predator).
  • +
  • neighborhood ({'moore', 'neumann'}, optional): +The connectivity rule. 'moore' uses 8-way connectivity (including diagonals); +'neumann' uses 4-way connectivity. Default is 'moore'.
  • +
+ +
Returns
+ +
    +
  • cluster_sizes (np.ndarray): +A 1D array of integers, where each element is the cell count of an +identified cluster.
  • +
+ +
Notes
+ +

The input grid is cast to int32 to ensure compatibility with the +underlying JIT-compiled _measure_clusters kernel.

+ +
Examples
+ +
+
>>> sizes = measure_cluster_sizes_fast(grid, species=1, neighborhood='moore')
+>>> if sizes.size > 0:
+...     print(f"Largest cluster: {sizes.max()}")
+
+
+
+ + +
+
+ +
+ + def + detect_clusters_fast( grid: numpy.ndarray, species: int, neighborhood: str = 'moore') -> Tuple[numpy.ndarray, Dict[int, int]]: + + + +
+ +
784def detect_clusters_fast(
+785    grid: np.ndarray,
+786    species: int,
+787    neighborhood: str = "moore",
+788) -> Tuple[np.ndarray, Dict[int, int]]:
+789    """
+790    Perform full cluster detection with labels using Numba acceleration.
+791
+792    This function returns a label array for spatial analysis and a dictionary
+793    of cluster sizes. It is significantly faster than standard Python or
+794    SciPy equivalents for large simulation grids.
+795
+796    Parameters
+797    ----------
+798    grid : np.ndarray
+799        A 2D array representing the simulation environment.
+800    species : int
+801        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+802    neighborhood : {'moore', 'neumann'}, optional
+803        The connectivity rule. 'moore' uses 8-way connectivity; 'neumann'
+804        uses 4-way connectivity. Default is 'moore'.
+805
+806    Returns
+807    -------
+808    labels : np.ndarray
+809        A 2D int32 array where each cell contains its unique cluster ID.
+810        Cells not belonging to the target species are 0.
+811    sizes : dict
+812        A dictionary mapping cluster IDs to their respective cell counts.
+813
+814    Notes
+815    -----
+816    The underlying Numba kernel uses a stack-based flood fill to avoid
+817    recursion limits and handles periodic boundary conditions.
+818
+819    Examples
+820    --------
+821    >>> labels, sizes = detect_clusters_fast(grid, species=1)
+822    >>> if sizes:
+823    ...     largest_id = max(sizes, key=sizes.get)
+824    ...     print(f"Cluster {largest_id} size: {sizes[largest_id]}")
+825    """
+826    grid_int = np.asarray(grid, dtype=np.int32)
+827    moore = neighborhood == "moore"
+828    labels, sizes_arr = _detect_clusters_numba(grid_int, np.int32(species), moore)
+829    sizes_dict = {i + 1: int(sizes_arr[i]) for i in range(len(sizes_arr))}
+830    return labels, sizes_dict
+
+ + +

Perform full cluster detection with labels using Numba acceleration.

+ +

This function returns a label array for spatial analysis and a dictionary +of cluster sizes. It is significantly faster than standard Python or +SciPy equivalents for large simulation grids.

+ +
Parameters
+ +
    +
  • grid (np.ndarray): +A 2D array representing the simulation environment.
  • +
  • species (int): +The target species identifier (e.g., 1 for Prey, 2 for Predator).
  • +
  • neighborhood ({'moore', 'neumann'}, optional): +The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' +uses 4-way connectivity. Default is 'moore'.
  • +
+ +
Returns
+ +
    +
  • labels (np.ndarray): +A 2D int32 array where each cell contains its unique cluster ID. +Cells not belonging to the target species are 0.
  • +
  • sizes (dict): +A dictionary mapping cluster IDs to their respective cell counts.
  • +
+ +
Notes
+ +

The underlying Numba kernel uses a stack-based flood fill to avoid +recursion limits and handles periodic boundary conditions.

+ +
Examples
+ +
+
>>> labels, sizes = detect_clusters_fast(grid, species=1)
+>>> if sizes:
+...     largest_id = max(sizes, key=sizes.get)
+...     print(f"Cluster {largest_id} size: {sizes[largest_id]}")
+
+
+
+ + +
+
+ +
+ + def + get_cluster_stats_fast(grid: numpy.ndarray, species: int, neighborhood: str = 'moore') -> Dict: + + + +
+ +
833def get_cluster_stats_fast(
+834    grid: np.ndarray,
+835    species: int,
+836    neighborhood: str = "moore",
+837) -> Dict:
+838    """
+839    Compute comprehensive cluster statistics for a species using Numba acceleration.
+840
+841    This function integrates cluster detection and labeling to provide a
+842    full suite of spatial metrics. It calculates the cluster size distribution
+843    and the largest cluster fraction, which often serves as an order
+844    parameter in percolation theory and Phase 1-3 analyses.
+845
+846    Parameters
+847    ----------
+848    grid : np.ndarray
+849        A 2D array representing the simulation environment.
+850    species : int
+851        The target species identifier (e.g., 1 for Prey, 2 for Predator).
+852    neighborhood : {'moore', 'neumann'}, optional
+853        The connectivity rule. 'moore' uses 8-way connectivity; 'neumann'
+854        uses 4-way connectivity. Default is 'moore'.
+855
+856    Returns
+857    -------
+858    stats : dict
+859        A dictionary containing:
+860        - 'n_clusters': Total count of isolated clusters.
+861        - 'sizes': Sorted array (descending) of all cluster sizes.
+862        - 'largest': Size of the single largest cluster.
+863        - 'largest_fraction': Size of the largest cluster divided by
+864          the total population of the species.
+865        - 'mean_size': Average size of all clusters.
+866        - 'size_distribution': Frequency mapping of {size: count}.
+867        - 'labels': 2D array of unique cluster IDs.
+868        - 'size_dict': Mapping of {label_id: size}.
+869
+870    Examples
+871    --------
+872    >>> stats = get_cluster_stats_fast(grid, species=1)
+873    >>> print(f"Found {stats['n_clusters']} prey clusters.")
+874    >>> print(f"Order parameter: {stats['largest_fraction']:.3f}")
+875    """
+876    labels, size_dict = detect_clusters_fast(grid, species, neighborhood)
+877
+878    if len(size_dict) == 0:
+879        return {
+880            "n_clusters": 0,
+881            "sizes": np.array([], dtype=np.int32),
+882            "largest": 0,
+883            "largest_fraction": 0.0,
+884            "mean_size": 0.0,
+885            "size_distribution": {},
+886            "labels": labels,
+887            "size_dict": size_dict,
+888        }
+889
+890    sizes = np.array(list(size_dict.values()), dtype=np.int32)
+891    sizes_sorted = np.sort(sizes)[::-1]
+892    total_pop = int(np.sum(sizes))
+893    largest = int(sizes_sorted[0])
+894
+895    size_dist = {}
+896    for s in sizes:
+897        s_int = int(s)
+898        size_dist[s_int] = size_dist.get(s_int, 0) + 1
+899
+900    return {
+901        "n_clusters": len(size_dict),
+902        "sizes": sizes_sorted,
+903        "largest": largest,
+904        "largest_fraction": float(largest) / total_pop if total_pop > 0 else 0.0,
+905        "mean_size": float(np.mean(sizes)),
+906        "size_distribution": size_dist,
+907        "labels": labels,
+908        "size_dict": size_dict,
+909    }
+
+ + +

Compute comprehensive cluster statistics for a species using Numba acceleration.

+ +

This function integrates cluster detection and labeling to provide a +full suite of spatial metrics. It calculates the cluster size distribution +and the largest cluster fraction, which often serves as an order +parameter in percolation theory and Phase 1-3 analyses.

+ +
Parameters
+ +
    +
  • grid (np.ndarray): +A 2D array representing the simulation environment.
  • +
  • species (int): +The target species identifier (e.g., 1 for Prey, 2 for Predator).
  • +
  • neighborhood ({'moore', 'neumann'}, optional): +The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' +uses 4-way connectivity. Default is 'moore'.
  • +
+ +
Returns
+ +
    +
  • stats (dict): +A dictionary containing: +
      +
    • 'n_clusters': Total count of isolated clusters.
    • +
    • 'sizes': Sorted array (descending) of all cluster sizes.
    • +
    • 'largest': Size of the single largest cluster.
    • +
    • 'largest_fraction': Size of the largest cluster divided by +the total population of the species.
    • +
    • 'mean_size': Average size of all clusters.
    • +
    • 'size_distribution': Frequency mapping of {size: count}.
    • +
    • 'labels': 2D array of unique cluster IDs.
    • +
    • 'size_dict': Mapping of {label_id: size}.
    • +
  • +
+ +
Examples
+ +
+
>>> stats = get_cluster_stats_fast(grid, species=1)
+>>> print(f"Found {stats['n_clusters']} prey clusters.")
+>>> print(f"Order parameter: {stats['largest_fraction']:.3f}")
+
+
+
+ + +
+
+ +
+ + def + compute_pcf_periodic_fast( positions_i: numpy.ndarray, positions_j: numpy.ndarray, grid_shape: Tuple[int, int], max_distance: float, n_bins: int = 50, self_correlation: bool = False) -> Tuple[numpy.ndarray, numpy.ndarray, int]: + + + +
+ +
1161def compute_pcf_periodic_fast(
+1162    positions_i: np.ndarray,
+1163    positions_j: np.ndarray,
+1164    grid_shape: Tuple[int, int],
+1165    max_distance: float,
+1166    n_bins: int = 50,
+1167    self_correlation: bool = False,
+1168) -> Tuple[np.ndarray, np.ndarray, int]:
+1169    """
+1170    Compute the Pair Correlation Function (PCF) using cell-list acceleration.
+1171
+1172    This high-level function coordinates the spatial hashing and histogram
+1173    calculation to determine the $g(r)$ function. It normalizes the resulting
+1174    histogram by the expected number of pairs in an ideal gas of the same
+1175    density, accounting for the toroidal area of each radial bin.
+1176
+1177    Parameters
+1178    ----------
+1179    positions_i : np.ndarray
+1180        (N, 2) array of coordinates for species I.
+1181    positions_j : np.ndarray
+1182        (M, 2) array of coordinates for species J.
+1183    grid_shape : tuple of int
+1184        The (rows, cols) dimensions of the simulation grid.
+1185    max_distance : float
+1186        The maximum radius to calculate correlations for.
+1187    n_bins : int, optional
+1188        Number of bins for the radial distribution (default 50).
+1189    self_correlation : bool, optional
+1190        Set to True if computing the correlation of a species with itself
+1191        to avoid self-counting (default False).
+1192
+1193    Returns
+1194    -------
+1195    bin_centers : np.ndarray
+1196        The central radial distance for each histogram bin.
+1197    pcf : np.ndarray
+1198        The normalized $g(r)$ values. A value of 1.0 indicates no spatial
+1199        correlation; > 1.0 indicates clustering; < 1.0 indicates repulsion.
+1200    total_pairs : int
+1201        The total count of pairs found within the `max_distance`.
+1202
+1203    Notes
+1204    -----
+1205    The function dynamically determines the optimal number of cells for the
+1206    spatial hash based on the `max_distance` and grid dimensions to maintain
+1207    linear time complexity.
+1208    """
+1209    rows, cols = grid_shape
+1210    L_row, L_col = float(rows), float(cols)
+1211    area = L_row * L_col
+1212
+1213    bin_width = max_distance / n_bins
+1214    bin_centers = np.linspace(bin_width / 2, max_distance - bin_width / 2, n_bins)
+1215
+1216    if len(positions_i) == 0 or len(positions_j) == 0:
+1217        return bin_centers, np.ones(n_bins), 0
+1218
+1219    n_cells = max(4, int(min(rows, cols) / max_distance))
+1220
+1221    pos_i = np.ascontiguousarray(positions_i, dtype=np.float64)
+1222    pos_j = np.ascontiguousarray(positions_j, dtype=np.float64)
+1223
+1224    indices_j, offsets_j, counts_j, cell_size_r, cell_size_c = _build_cell_list(
+1225        pos_j, n_cells, L_row, L_col
+1226    )
+1227
+1228    hist = _pcf_cell_list(
+1229        pos_i,
+1230        pos_j,
+1231        indices_j,
+1232        offsets_j,
+1233        counts_j,
+1234        cell_size_r,
+1235        cell_size_c,
+1236        L_row,
+1237        L_col,
+1238        max_distance,
+1239        n_bins,
+1240        self_correlation,
+1241        n_cells,
+1242    )
+1243
+1244    n_i, n_j = len(positions_i), len(positions_j)
+1245    if self_correlation:
+1246        density_product = n_i * (n_i - 1) / (area * area)
+1247    else:
+1248        density_product = n_i * n_j / (area * area)
+1249
+1250    expected = np.zeros(n_bins)
+1251    for i in range(n_bins):
+1252        r = bin_centers[i]
+1253        annulus_area = 2 * np.pi * r * bin_width
+1254        expected[i] = density_product * annulus_area * area
+1255
+1256    pcf = np.ones(n_bins)
+1257    mask = expected > 1.0
+1258    pcf[mask] = hist[mask] / expected[mask]
+1259
+1260    return bin_centers, pcf, int(np.sum(hist))
+
+ + +

Compute the Pair Correlation Function (PCF) using cell-list acceleration.

+ +

This high-level function coordinates the spatial hashing and histogram +calculation to determine the $g(r)$ function. It normalizes the resulting +histogram by the expected number of pairs in an ideal gas of the same +density, accounting for the toroidal area of each radial bin.

+ +
Parameters
+ +
    +
  • positions_i (np.ndarray): +(N, 2) array of coordinates for species I.
  • +
  • positions_j (np.ndarray): +(M, 2) array of coordinates for species J.
  • +
  • grid_shape (tuple of int): +The (rows, cols) dimensions of the simulation grid.
  • +
  • max_distance (float): +The maximum radius to calculate correlations for.
  • +
  • n_bins (int, optional): +Number of bins for the radial distribution (default 50).
  • +
  • self_correlation (bool, optional): +Set to True if computing the correlation of a species with itself +to avoid self-counting (default False).
  • +
+ +
Returns
+ +
    +
  • bin_centers (np.ndarray): +The central radial distance for each histogram bin.
  • +
  • pcf (np.ndarray): +The normalized $g(r)$ values. A value of 1.0 indicates no spatial +correlation; > 1.0 indicates clustering; < 1.0 indicates repulsion.
  • +
  • total_pairs (int): +The total count of pairs found within the max_distance.
  • +
+ +
Notes
+ +

The function dynamically determines the optimal number of cells for the +spatial hash based on the max_distance and grid dimensions to maintain +linear time complexity.

+
+ + +
+
+ +
+ + def + compute_all_pcfs_fast( grid: numpy.ndarray, max_distance: Optional[float] = None, n_bins: int = 50) -> Dict[str, Tuple[numpy.ndarray, numpy.ndarray, int]]: + + + +
+ +
1263def compute_all_pcfs_fast(
+1264    grid: np.ndarray,
+1265    max_distance: Optional[float] = None,
+1266    n_bins: int = 50,
+1267) -> Dict[str, Tuple[np.ndarray, np.ndarray, int]]:
+1268    """
+1269    Compute all three species Pair Correlation Functions (PCFs) using cell-list acceleration.
+1270
+1271    This function calculates the spatial auto-correlations (Prey-Prey,
+1272    Predator-Predator) and the cross-correlation (Prey-Predator) for a given
+1273    simulation grid. It identifies particle positions and leverages
+1274    Numba-accelerated cell lists to handle the computations efficiently.
+1275
+1276    Parameters
+1277    ----------
+1278    grid : np.ndarray
+1279        2D integer array where 1 represents prey and 2 represents predators.
+1280    max_distance : float, optional
+1281        The maximum radial distance for the correlation. Defaults to 1/4
+1282        of the minimum grid dimension if not provided.
+1283    n_bins : int, optional
+1284        Number of distance bins for the histogram. Default is 50.
+1285
+1286    Returns
+1287    -------
+1288    results : dict
+1289        A dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'.
+1290        Each value is a tuple containing:
+1291        - bin_centers (np.ndarray): Radial distances.
+1292        - pcf_values (np.ndarray): Normalized g(r) values.
+1293        - pair_count (int): Total number of pairs found.
+1294
+1295    Notes
+1296    -----
+1297    The PCF provides insight into the spatial organization of the system.
+1298    g(r) > 1 at short distances indicates aggregation (clustering),
+1299    while g(r) < 1 indicates exclusion or repulsion.
+1300    """
+1301    rows, cols = grid.shape
+1302    if max_distance is None:
+1303        max_distance = min(rows, cols) / 4.0
+1304
+1305    prey_pos = np.argwhere(grid == 1)
+1306    pred_pos = np.argwhere(grid == 2)
+1307
+1308    results = {}
+1309
+1310    dist, pcf, n = compute_pcf_periodic_fast(
+1311        prey_pos,
+1312        prey_pos,
+1313        (rows, cols),
+1314        max_distance,
+1315        n_bins,
+1316        self_correlation=True,
+1317    )
+1318    results["prey_prey"] = (dist, pcf, n)
+1319
+1320    dist, pcf, n = compute_pcf_periodic_fast(
+1321        pred_pos,
+1322        pred_pos,
+1323        (rows, cols),
+1324        max_distance,
+1325        n_bins,
+1326        self_correlation=True,
+1327    )
+1328    results["pred_pred"] = (dist, pcf, n)
+1329
+1330    dist, pcf, n = compute_pcf_periodic_fast(
+1331        prey_pos,
+1332        pred_pos,
+1333        (rows, cols),
+1334        max_distance,
+1335        n_bins,
+1336        self_correlation=False,
+1337    )
+1338    results["prey_pred"] = (dist, pcf, n)
+1339
+1340    return results
+
+ + +

Compute all three species Pair Correlation Functions (PCFs) using cell-list acceleration.

+ +

This function calculates the spatial auto-correlations (Prey-Prey, +Predator-Predator) and the cross-correlation (Prey-Predator) for a given +simulation grid. It identifies particle positions and leverages +Numba-accelerated cell lists to handle the computations efficiently.

+ +
Parameters
+ +
    +
  • grid (np.ndarray): +2D integer array where 1 represents prey and 2 represents predators.
  • +
  • max_distance (float, optional): +The maximum radial distance for the correlation. Defaults to 1/4 +of the minimum grid dimension if not provided.
  • +
  • n_bins (int, optional): +Number of distance bins for the histogram. Default is 50.
  • +
+ +
Returns
+ +
    +
  • results (dict): +A dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'. +Each value is a tuple containing: +
      +
    • bin_centers (np.ndarray): Radial distances.
    • +
    • pcf_values (np.ndarray): Normalized g(r) values.
    • +
    • pair_count (int): Total number of pairs found.
    • +
  • +
+ +
Notes
+ +

The PCF provides insight into the spatial organization of the system. +g(r) > 1 at short distances indicates aggregation (clustering), +while g(r) < 1 indicates exclusion or repulsion.

+
+ + +
+
+ +
+ + def + warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False): + + + +
+ +
1348def warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False):
+1349    """
+1350    Pre-compile all Numba-accelerated kernels to avoid first-run latency.
+1351
+1352    This function executes a single step of the simulation and each analysis
+1353    routine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation,
+1354    the first call to a decorated function incurs a compilation overhead.
+1355    Running this warmup ensures that subsequent experimental runs are timed
+1356    accurately and perform at full speed.
+1357
+1358    Parameters
+1359    ----------
+1360    grid_size : int, optional
+1361        The side length of the dummy grid used for warmup (default 100).
+1362    directed_hunting : bool, optional
+1363        If True, also warms up the directed behavior update kernel (default False).
+1364
+1365    Returns
+1366    -------
+1367    None
+1368
+1369    Notes
+1370    -----
+1371    This function checks for `NUMBA_AVAILABLE` before execution. It warms up
+1372    the `PPKernel` (random and optionally directed), as well as the
+1373    spatial analysis functions (`compute_all_pcfs_fast`, `detect_clusters_fast`, etc.).
+1374    """
+1375    if not NUMBA_AVAILABLE:
+1376        return
+1377
+1378    set_numba_seed(0)
+1379
+1380    grid = np.zeros((grid_size, grid_size), dtype=np.int32)
+1381    grid[::3, ::3] = 1
+1382    grid[::5, ::5] = 2
+1383
+1384    prey_death_arr = np.full((grid_size, grid_size), 0.05, dtype=np.float64)
+1385    prey_death_arr[grid != 1] = np.nan
+1386
+1387    # Always warmup random kernel
+1388    kernel_random = PPKernel(grid_size, grid_size, directed_hunting=False)
+1389    kernel_random.update(grid.copy(), prey_death_arr.copy(), 0.2, 0.05, 0.2, 0.1)
+1390
+1391    # Warmup directed kernel if requested
+1392    if directed_hunting:
+1393        kernel_directed = PPKernel(grid_size, grid_size, directed_hunting=True)
+1394        kernel_directed.update(grid.copy(), prey_death_arr.copy(), 0.2, 0.05, 0.2, 0.1)
+1395
+1396    # Warmup analysis functions
+1397    _ = compute_all_pcfs_fast(grid, max_distance=20.0, n_bins=20)
+1398    _ = measure_cluster_sizes_fast(grid, 1)
+1399    _ = detect_clusters_fast(grid, 1)
+1400    _ = get_cluster_stats_fast(grid, 1)
+
+ + +

Pre-compile all Numba-accelerated kernels to avoid first-run latency.

+ +

This function executes a single step of the simulation and each analysis +routine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation, +the first call to a decorated function incurs a compilation overhead. +Running this warmup ensures that subsequent experimental runs are timed +accurately and perform at full speed.

+ +
Parameters
+ +
    +
  • grid_size (int, optional): +The side length of the dummy grid used for warmup (default 100).
  • +
  • directed_hunting (bool, optional): +If True, also warms up the directed behavior update kernel (default False).
  • +
+ +
Returns
+ +
    +
  • None
  • +
+ +
Notes
+ +

This function checks for NUMBA_AVAILABLE before execution. It warms up +the PPKernel (random and optionally directed), as well as the +spatial analysis functions (compute_all_pcfs_fast, detect_clusters_fast, etc.).

+
+ + +
+
+ +
+ + def + benchmark_kernels(grid_size: int = 100, n_runs: int = 20): + + + +
+ +
1403def benchmark_kernels(grid_size: int = 100, n_runs: int = 20):
+1404    """
+1405    Benchmark the execution performance of random vs. directed update kernels.
+1406
+1407    This utility measures the average time per simulation step for both the
+1408    stochastic (random neighbor) and heuristic (directed hunting/reproduction)
+1409    update strategies. It accounts for the computational overhead introduced
+1410    by the "intelligent" search logic used in directed mode.
+1411
+1412    Parameters
+1413    ----------
+1414    grid_size : int, optional
+1415        The side length of the square simulation grid (default 100).
+1416    n_runs : int, optional
+1417        The number of iterations to perform for averaging performance (default 20).
+1418
+1419    Returns
+1420    -------
+1421    t_random : float
+1422        Average time per step for the random kernel in milliseconds.
+1423    t_directed : float
+1424        Average time per step for the directed kernel in milliseconds.
+1425
+1426    Notes
+1427    -----
+1428    The function ensures a fair comparison by:
+1429    1. Using a fixed seed for reproducible initial grid states.
+1430    2. Warming up Numba kernels before timing to exclude JIT compilation latency.
+1431    3. Copying the grid and death arrays for each iteration to maintain
+1432       consistent population densities throughout the benchmark.
+1433    """
+1434    import time
+1435
+1436    print("=" * 60)
+1437    print(f"KERNEL BENCHMARK ({grid_size}x{grid_size}, {n_runs} runs)")
+1438    print(f"Numba available: {NUMBA_AVAILABLE}")
+1439    print("=" * 60)
+1440
+1441    np.random.seed(42)
+1442    grid = np.zeros((grid_size, grid_size), dtype=np.int32)
+1443    n_prey = int(grid_size * grid_size * 0.30)
+1444    n_pred = int(grid_size * grid_size * 0.15)
+1445    positions = np.random.permutation(grid_size * grid_size)
+1446    for pos in positions[:n_prey]:
+1447        grid[pos // grid_size, pos % grid_size] = 1
+1448    for pos in positions[n_prey : n_prey + n_pred]:
+1449        grid[pos // grid_size, pos % grid_size] = 2
+1450
+1451    prey_death_arr = np.full((grid_size, grid_size), 0.05, dtype=np.float64)
+1452    prey_death_arr[grid != 1] = np.nan
+1453
+1454    print(f"Initial: {np.sum(grid == 1)} prey, {np.sum(grid == 2)} predators")
+1455
+1456    # Warmup both kernels
+1457    warmup_numba_kernels(grid_size, directed_hunting=True)
+1458
+1459    # Benchmark random kernel
+1460    kernel_random = PPKernel(grid_size, grid_size, directed_hunting=False)
+1461    t0 = time.perf_counter()
+1462    for _ in range(n_runs):
+1463        test_grid = grid.copy()
+1464        test_arr = prey_death_arr.copy()
+1465        kernel_random.update(test_grid, test_arr, 0.2, 0.05, 0.2, 0.1)
+1466    t_random = (time.perf_counter() - t0) / n_runs * 1000
+1467
+1468    # Benchmark directed kernel
+1469    kernel_directed = PPKernel(grid_size, grid_size, directed_hunting=True)
+1470    t0 = time.perf_counter()
+1471    for _ in range(n_runs):
+1472        test_grid = grid.copy()
+1473        test_arr = prey_death_arr.copy()
+1474        kernel_directed.update(test_grid, test_arr, 0.2, 0.05, 0.2, 0.1)
+1475    t_directed = (time.perf_counter() - t0) / n_runs * 1000
+1476
+1477    print(f"\nRandom kernel:   {t_random:.2f} ms/step")
+1478    print(f"Directed kernel: {t_directed:.2f} ms/step")
+1479    print(
+1480        f"Overhead:        {t_directed - t_random:.2f} ms (+{100*(t_directed/t_random - 1):.1f}%)"
+1481    )
+1482
+1483    return t_random, t_directed
+
+ + +

Benchmark the execution performance of random vs. directed update kernels.

+ +

This utility measures the average time per simulation step for both the +stochastic (random neighbor) and heuristic (directed hunting/reproduction) +update strategies. It accounts for the computational overhead introduced +by the "intelligent" search logic used in directed mode.

+ +
Parameters
+ +
    +
  • grid_size (int, optional): +The side length of the square simulation grid (default 100).
  • +
  • n_runs (int, optional): +The number of iterations to perform for averaging performance (default 20).
  • +
+ +
Returns
+ +
    +
  • t_random (float): +Average time per step for the random kernel in milliseconds.
  • +
  • t_directed (float): +Average time per step for the directed kernel in milliseconds.
  • +
+ +
Notes
+ +

The function ensures a fair comparison by:

+ +
    +
  1. Using a fixed seed for reproducible initial grid states.
  2. +
  3. Warming up Numba kernels before timing to exclude JIT compilation latency.
  4. +
  5. Copying the grid and death arrays for each iteration to maintain +consistent population densities throughout the benchmark.
  6. +
+
+ + +
+
+ +
+ + def + benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20): + + + +
+ +
1486def benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20):
+1487    """
+1488    Benchmark the performance of different cluster detection and analysis routines.
+1489
+1490    This function evaluates three levels of spatial analysis:
+1491    1. Size measurement only (fastest, no label map).
+1492    2. Full detection (returns label map and size dictionary).
+1493    3. Comprehensive statistics (calculates distributions, means, and order parameters).
+1494
+1495    Parameters
+1496    ----------
+1497    grid_size : int, optional
+1498        Side length of the square grid for benchmarking (default 100).
+1499    n_runs : int, optional
+1500        Number of iterations to average for performance results (default 20).
+1501
+1502    Returns
+1503    -------
+1504    stats : dict
+1505        The result dictionary from the final comprehensive statistics run.
+1506
+1507    Notes
+1508    -----
+1509    The benchmark uses a fixed prey density of 30% to ensure a representative
+1510    distribution of clusters. It pre-warms the Numba kernels to ensure that
+1511    the measurements reflect execution speed rather than compilation time.
+1512    """
+1513    import time
+1514
+1515    print("=" * 60)
+1516    print(f"CLUSTER DETECTION BENCHMARK ({grid_size}x{grid_size})")
+1517    print(f"Numba available: {NUMBA_AVAILABLE}")
+1518    print("=" * 60)
+1519
+1520    np.random.seed(42)
+1521    grid = np.zeros((grid_size, grid_size), dtype=np.int32)
+1522    n_prey = int(grid_size * grid_size * 0.30)
+1523    positions = np.random.permutation(grid_size * grid_size)[:n_prey]
+1524    for pos in positions:
+1525        grid[pos // grid_size, pos % grid_size] = 1
+1526
+1527    print(f"Prey cells: {np.sum(grid == 1)}")
+1528
+1529    # Warmup
+1530    _ = measure_cluster_sizes_fast(grid, 1)
+1531    _ = detect_clusters_fast(grid, 1)
+1532    _ = get_cluster_stats_fast(grid, 1)
+1533
+1534    # Benchmark sizes only
+1535    t0 = time.perf_counter()
+1536    for _ in range(n_runs):
+1537        sizes = measure_cluster_sizes_fast(grid, 1)
+1538    t_sizes = (time.perf_counter() - t0) / n_runs * 1000
+1539    print(f"\nmeasure_cluster_sizes_fast: {t_sizes:.2f} ms  ({len(sizes)} clusters)")
+1540
+1541    # Benchmark full detection
+1542    t0 = time.perf_counter()
+1543    for _ in range(n_runs):
+1544        labels, size_dict = detect_clusters_fast(grid, 1)
+1545    t_detect = (time.perf_counter() - t0) / n_runs * 1000
+1546    print(f"detect_clusters_fast:       {t_detect:.2f} ms  ({len(size_dict)} clusters)")
+1547
+1548    # Benchmark full stats
+1549    t0 = time.perf_counter()
+1550    for _ in range(n_runs):
+1551        stats = get_cluster_stats_fast(grid, 1)
+1552    t_stats = (time.perf_counter() - t0) / n_runs * 1000
+1553    print(f"get_cluster_stats_fast:     {t_stats:.2f} ms")
+1554
+1555    print(
+1556        f"\nOverhead for labels: {t_detect - t_sizes:.2f} ms (+{100*(t_detect/t_sizes - 1):.0f}%)"
+1557    )
+1558
+1559    return stats
+
+ + +

Benchmark the performance of different cluster detection and analysis routines.

+ +

This function evaluates three levels of spatial analysis:

+ +
    +
  1. Size measurement only (fastest, no label map).
  2. +
  3. Full detection (returns label map and size dictionary).
  4. +
  5. Comprehensive statistics (calculates distributions, means, and order parameters).
  6. +
+ +
Parameters
+ +
    +
  • grid_size (int, optional): +Side length of the square grid for benchmarking (default 100).
  • +
  • n_runs (int, optional): +Number of iterations to average for performance results (default 20).
  • +
+ +
Returns
+ +
    +
  • stats (dict): +The result dictionary from the final comprehensive statistics run.
  • +
+ +
Notes
+ +

The benchmark uses a fixed prey density of 30% to ensure a representative +distribution of clusters. It pre-warms the Numba kernels to ensure that +the measurements reflect execution speed rather than compilation time.

+
+ + +
+
+ + \ No newline at end of file diff --git a/docs/scripts.html b/docs/scripts.html new file mode 100644 index 0000000..b8415c8 --- /dev/null +++ b/docs/scripts.html @@ -0,0 +1,327 @@ + + + + + + + scripts API documentation + + + + + + + + + +
+
+

+scripts

+ +

Execution and Orchestration Suite for Predator-Prey Hydra Effect Research.

+ +

This module provides the entry points for running multi-phase simulation +experiments. It is designed for high-performance computing (HPC) environments, +utilizing parallel processing and Numba-accelerated kernels to analyze +self-organized criticality and finite-size scaling.

+ +

Experimental Phases

+ +

The suite is divided into five sequential research phases:

+ +
    +
  1. Critical Point Identification: Parameter sweeps to locate +bifurcation points and phase transitions.
  2. +
  3. Self-Organization Analysis: Observations of evolutionary drift +toward critical states.
  4. +
  5. Finite-Size Scaling (FSS): Analysis of cluster size cutoffs +across varying grid dimensions ($L$).
  6. +
  7. Sensitivity Analysis: Comprehensive 4D parameter sweeps across +survival regimes.
  8. +
  9. Directed Hunting Comparisons: Sensitivity analysis using +non-random predator search kernels.
  10. +
+ +

Performance Features

+ +
    +
  • Parallelization: Automated CPU core detection and SLURM integration +via joblib.
  • +
  • Reproducibility: Deterministic seed generation using SHA-256 hashing +of parameter states.
  • +
  • Incremental Persistence: Results are saved in JSON Lines (JSONL) +format to ensure data recovery during long-running batches.
  • +
+ +

Usage

+ +

Experiments should be invoked from the project root: +```bash +python scripts/experiments.py --phase 1 --output results/

+
+ + + + + +
 1"""
+ 2Execution and Orchestration Suite for Predator-Prey Hydra Effect Research.
+ 3
+ 4This module provides the entry points for running multi-phase simulation 
+ 5experiments. It is designed for high-performance computing (HPC) environments,
+ 6utilizing parallel processing and Numba-accelerated kernels to analyze 
+ 7self-organized criticality and finite-size scaling.
+ 8
+ 9## Experimental Phases
+10The suite is divided into five sequential research phases:
+111.  **Critical Point Identification**: Parameter sweeps to locate 
+12    bifurcation points and phase transitions.
+132.  **Self-Organization Analysis**: Observations of evolutionary drift 
+14    toward critical states.
+153.  **Finite-Size Scaling (FSS)**: Analysis of cluster size cutoffs 
+16    across varying grid dimensions ($L$).
+174.  **Sensitivity Analysis**: Comprehensive 4D parameter sweeps across 
+18    survival regimes.
+195.  **Directed Hunting Comparisons**: Sensitivity analysis using 
+20    non-random predator search kernels.
+21
+22## Performance Features
+23- **Parallelization**: Automated CPU core detection and SLURM integration 
+24  via `joblib`.
+25- **Reproducibility**: Deterministic seed generation using SHA-256 hashing 
+26  of parameter states.
+27- **Incremental Persistence**: Results are saved in JSON Lines (JSONL) 
+28  format to ensure data recovery during long-running batches.
+29
+30## Usage
+31Experiments should be invoked from the project root:
+32```bash
+33python scripts/experiments.py --phase 1 --output results/
+34"""
+35
+36from models.numba_optimized import *
+
+ + +
+
+ + \ No newline at end of file diff --git a/docs/scripts/experiments.html b/docs/scripts/experiments.html new file mode 100644 index 0000000..b75cc52 --- /dev/null +++ b/docs/scripts/experiments.html @@ -0,0 +1,3382 @@ + + + + + + + scripts.experiments API documentation + + + + + + + + + +
+
+

+scripts.experiments

+ +

Predator-Prey Hydra Effect Experiments - HPC Version

+ +

Experimental phases (run sequentially): + Phase 1: Parameter sweep to find critical point (bifurcation + cluster analysis) + Phase 2: Self-organization analysis (evolution toward criticality) + Phase 3: Finite-size scaling at critical point + Phase 4: Sensitivity analysis across parameter regimes + Phase 5: Model extensions (directed hunting comparison)

+ +

Usage: + python experiments.py --phase 1 # Run phase 1 + python experiments.py --phase 1 --dry-run # Estimate runtime + python experiments.py --phase all # Run all phases + python experiments.py --phase 1 --output results/ # Custom output

+
+ + + + + +
   1#!/usr/bin/env python3
+   2"""
+   3Predator-Prey Hydra Effect Experiments - HPC Version
+   4
+   5Experimental phases (run sequentially):
+   6  Phase 1: Parameter sweep to find critical point (bifurcation + cluster analysis)
+   7  Phase 2: Self-organization analysis (evolution toward criticality)
+   8  Phase 3: Finite-size scaling at critical point
+   9  Phase 4: Sensitivity analysis across parameter regimes
+  10  Phase 5: Model extensions (directed hunting comparison)
+  11
+  12Usage:
+  13    python experiments.py --phase 1                    # Run phase 1
+  14    python experiments.py --phase 1 --dry-run          # Estimate runtime
+  15    python experiments.py --phase all                  # Run all phases
+  16    python experiments.py --phase 1 --output results/  # Custom output
+  17"""
+  18
+  19import argparse
+  20import hashlib
+  21import json
+  22import logging
+  23import os
+  24import sys
+  25import time
+  26from dataclasses import asdict
+  27from pathlib import Path
+  28from typing import Dict, List, Tuple, Optional
+  29import warnings
+  30
+  31import numpy as np
+  32from tqdm import tqdm
+  33
+  34warnings.filterwarnings("ignore")
+  35
+  36# Project imports
+  37project_root = str(Path(__file__).parent.parent)
+  38if project_root not in sys.path:
+  39    sys.path.insert(0, project_root)
+  40
+  41from models.config import Config, get_phase_config, PHASE_CONFIGS
+  42
+  43# Numba imports
+  44try:
+  45    from models.numba_optimized import (
+  46        compute_all_pcfs_fast,
+  47        get_cluster_stats_fast,
+  48        get_percolating_cluster_fast,
+  49        warmup_numba_kernels,
+  50        set_numba_seed,
+  51        NUMBA_AVAILABLE,
+  52    )
+  53
+  54    USE_NUMBA = NUMBA_AVAILABLE
+  55except ImportError:
+  56    USE_NUMBA = False
+  57
+  58    def warmup_numba_kernels(size, **kwargs):
+  59        pass
+  60
+  61    def set_numba_seed(seed):
+  62        pass
+  63
+  64
+  65# =============================================================================
+  66# Utility Functions
+  67# =============================================================================
+  68
+  69
+  70def generate_unique_seed(params: dict, rep: int) -> int:
+  71    """
+  72    Create a deterministic seed from a dictionary of parameters and a repetition index.
+  73
+  74    This function serializes the input dictionary into a sorted JSON string,
+  75    appends the repetition count, and hashes the resulting string using SHA-256.
+  76    The first 8 characters of the hex digest are then converted to an integer
+  77    to provide a stable, unique seed for random number generators.
+  78
+  79    Parameters
+  80    ----------
+  81    params : dict
+  82        A dictionary of configuration parameters. Keys are sorted to ensure
+  83        determinism regardless of insertion order.
+  84    rep : int
+  85        The repetition or iteration index, used to ensure different seeds
+  86        are generated for the same parameter set across multiple runs.
+  87
+  88    Returns
+  89    -------
+  90    int
+  91        A unique integer seed derived from the input parameters.
+  92
+  93    Examples
+  94    --------
+  95    >>> params = {'learning_rate': 0.01, 'batch_size': 32}
+  96    >>> generate_unique_seed(params, 1)
+  97    3432571217
+  98    >>> generate_unique_seed(params, 2)
+  99    3960013583
+ 100    """
+ 101    identifier = json.dumps(params, sort_keys=True) + f"_{rep}"
+ 102    return int(hashlib.sha256(identifier.encode()).hexdigest()[:8], 16)
+ 103
+ 104
+ 105def count_populations(grid: np.ndarray) -> Tuple[int, int, int]:
+ 106    """
+ 107    Count the number of empty, prey, and predator cells in the simulation grid.
+ 108
+ 109    Parameters
+ 110    ----------
+ 111    grid : np.ndarray
+ 112        A 2D NumPy array representing the simulation environment, where:
+ 113        - 0: Empty cell
+ 114        - 1: Prey
+ 115        - 2: Predator
+ 116
+ 117    Returns
+ 118    -------
+ 119    empty_count : int
+ 120        Total number of cells with a value of 0.
+ 121    prey_count : int
+ 122        Total number of cells with a value of 1.
+ 123    predator_count : int
+ 124        Total number of cells with a value of 2.
+ 125
+ 126    Examples
+ 127    --------
+ 128    >>> grid = np.array([[0, 1], [2, 1]])
+ 129    >>> count_populations(grid)
+ 130    (1, 2, 1)
+ 131    """
+ 132    return int(np.sum(grid == 0)), int(np.sum(grid == 1)), int(np.sum(grid == 2))
+ 133
+ 134
+ 135def get_evolved_stats(model, param: str) -> Dict:
+ 136    """
+ 137    Get statistics of an evolved parameter from the model.
+ 138
+ 139    This function retrieves parameter values from the model's internal storage,
+ 140    filters out NaN values, and calculates basic descriptive statistics.
+ 141
+ 142    Parameters
+ 143    ----------
+ 144    model : object
+ 145        The simulation model instance containing a `cell_params` attribute
+ 146        with a `.get()` method.
+ 147    param : str
+ 148        The name of the parameter to calculate statistics for.
+ 149
+ 150    Returns
+ 151    -------
+ 152    stats : dict
+ 153        A dictionary containing the following keys:
+ 154        - 'mean': Arithmetic mean of valid values.
+ 155        - 'std': Standard deviation of valid values.
+ 156        - 'min': Minimum valid value.
+ 157        - 'max': Maximum valid value.
+ 158        - 'n': Count of non-NaN values.
+ 159        If no valid data is found, all stats return NaN and n returns 0.
+ 160
+ 161    Examples
+ 162    --------
+ 163    >>> stats = get_evolved_stats(my_model, "speed")
+ 164    >>> print(stats['mean'])
+ 165    1.25
+ 166    """
+ 167    arr = model.cell_params.get(param)
+ 168    if arr is None:
+ 169        return {"mean": np.nan, "std": np.nan, "min": np.nan, "max": np.nan, "n": 0}
+ 170    valid = arr[~np.isnan(arr)]
+ 171    if len(valid) == 0:
+ 172        return {"mean": np.nan, "std": np.nan, "min": np.nan, "max": np.nan, "n": 0}
+ 173    return {
+ 174        "mean": float(np.mean(valid)),
+ 175        "std": float(np.std(valid)),
+ 176        "min": float(np.min(valid)),
+ 177        "max": float(np.max(valid)),
+ 178        "n": len(valid),
+ 179    }
+ 180
+ 181
+ 182def average_pcfs(
+ 183    pcf_list: List[Tuple[np.ndarray, np.ndarray, int]],
+ 184) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
+ 185    """
+ 186    Average multiple Pair Correlation Function (PCF) measurements and calculate standard error.
+ 187
+ 188    Parameters
+ 189    ----------
+ 190    pcf_list : list of tuple
+ 191        A list where each element is a tuple containing:
+ 192        - distances (np.ndarray): The radial distances (r).
+ 193        - pcf_values (np.ndarray): The correlation values g(r).
+ 194        - count (int): Metadata or weight (not used in current calculation).
+ 195
+ 196    Returns
+ 197    -------
+ 198    distances : np.ndarray
+ 199        The radial distances from the first entry in the list.
+ 200    pcf_mean : np.ndarray
+ 201        The element-wise mean of the PCF values across all measurements.
+ 202    pcf_se : np.ndarray
+ 203        The standard error of the mean for the PCF values.
+ 204
+ 205    Examples
+ 206    --------
+ 207    >>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10),
+ 208    ...         (np.array([0, 1]), np.array([1.2, 1.8]), 12)]
+ 209    >>> dist, mean, se = average_pcfs(data)
+ 210    >>> mean
+ 211    array([1.1, 1.9])
+ 212    """
+ 213    if len(pcf_list) == 0:
+ 214        return np.array([]), np.array([]), np.array([])
+ 215
+ 216    distances = pcf_list[0][0]
+ 217    pcfs = np.array([p[1] for p in pcf_list])
+ 218
+ 219    pcf_mean = np.mean(pcfs, axis=0)
+ 220    pcf_se = np.std(pcfs, axis=0) / np.sqrt(len(pcfs))
+ 221
+ 222    return distances, pcf_mean, pcf_se
+ 223
+ 224
+ 225def save_results_jsonl(results: List[Dict], output_path: Path):
+ 226    """
+ 227    Save a list of dictionaries to a file in JSON Lines (JSONL) format.
+ 228
+ 229    Each dictionary in the list is serialized into a single JSON string and
+ 230    written as a new line. Non-serializable objects are converted to strings
+ 231    using the default string representation.
+ 232
+ 233    Parameters
+ 234    ----------
+ 235    results : list of dict
+ 236        The collection of result dictionaries to be saved.
+ 237    output_path : Path
+ 238        The file system path (pathlib.Path) where the JSONL file will be created.
+ 239
+ 240    Returns
+ 241    -------
+ 242    None
+ 243
+ 244    Notes
+ 245    -----
+ 246    The file is opened in 'w' (write) mode, which will overwrite any existing
+ 247    content at the specified path.
+ 248
+ 249    Examples
+ 250    --------
+ 251    >>> data = [{"id": 1, "score": 0.95}, {"id": 2, "score": 0.88}]
+ 252    >>> save_results_jsonl(data, Path("results.jsonl"))
+ 253    """
+ 254    with open(output_path, "w", encoding="utf-8") as f:
+ 255        for result in results:
+ 256            f.write(json.dumps(result, default=str) + "\n")
+ 257
+ 258
+ 259def save_results_npz(results: List[Dict], output_path: Path):
+ 260    """
+ 261    Save simulation results to a compressed NumPy (.npz) binary file.
+ 262
+ 263    This function flattens a list of result dictionaries into a single
+ 264    dictionary of NumPy arrays, prefixing keys with the run index to
+ 265    maintain data separation. The resulting file is compressed to
+ 266    reduce storage space.
+ 267
+ 268    Parameters
+ 269    ----------
+ 270    results : list of dict
+ 271        A list where each dictionary contains key-value pairs of
+ 272        simulation data (e.g., arrays, lists, or scalars).
+ 273    output_path : Path
+ 274        The file system path (pathlib.Path) where the compressed
+ 275        NPZ file will be saved.
+ 276
+ 277    Returns
+ 278    -------
+ 279    None
+ 280
+ 281    Notes
+ 282    -----
+ 283    The keys in the saved file follow the format 'run_{index}_{original_key}'.
+ 284    Values are automatically converted to NumPy arrays if they are not
+ 285    already.
+ 286
+ 287    Examples
+ 288    --------
+ 289    >>> results = [{"energy": [1, 2]}, {"energy": [3, 4]}]
+ 290    >>> save_results_npz(results, Path("output.npz"))
+ 291    """
+ 292    data = {}
+ 293    for i, res in enumerate(results):
+ 294        for key, val in res.items():
+ 295            data[f"run_{i}_{key}"] = np.array(val)
+ 296    np.savez_compressed(output_path, **data)
+ 297
+ 298
+ 299def load_results_jsonl(input_path: Path) -> List[Dict]:
+ 300    """
+ 301    Load simulation results from a JSON Lines (JSONL) formatted file.
+ 302
+ 303    This function reads a file line-by-line, parsing each line as an
+ 304    independent JSON object and aggregating them into a list of dictionaries.
+ 305
+ 306    Parameters
+ 307    ----------
+ 308    input_path : Path
+ 309        The file system path (pathlib.Path) to the JSONL file.
+ 310
+ 311    Returns
+ 312    -------
+ 313    results : list of dict
+ 314        A list of dictionaries reconstructed from the file content.
+ 315
+ 316    Raises
+ 317    ------
+ 318    FileNotFoundError
+ 319        If the specified input path does not exist.
+ 320    json.JSONDecodeError
+ 321        If a line in the file is not valid JSON.
+ 322
+ 323    Examples
+ 324    --------
+ 325    >>> data = load_results_jsonl(Path("results.jsonl"))
+ 326    >>> len(data)
+ 327    2
+ 328    """
+ 329    results = []
+ 330    with open(input_path, "r", encoding="utf-8") as f:
+ 331        for line in f:
+ 332            results.append(json.loads(line.strip()))
+ 333    return results
+ 334
+ 335
+ 336# =============================================================================
+ 337# Simulation Functionality
+ 338# =============================================================================
+ 339
+ 340
+ 341def run_single_simulation(
+ 342    prey_birth: float,
+ 343    prey_death: float,
+ 344    predator_birth: float,
+ 345    predator_death: float,
+ 346    grid_size: int,
+ 347    seed: int,
+ 348    cfg: Config,
+ 349    with_evolution: bool = False,
+ 350    compute_pcf: Optional[bool] = None,
+ 351) -> Dict:
+ 352    """
+ 353    Run a single Predator-Prey (PP) simulation and collect comprehensive metrics.
+ 354
+ 355    This function initializes a Cellular Automata model, executes a warmup phase
+ 356    to reach steady state, and then performs a measurement phase to track
+ 357    population dynamics, spatial clustering, and evolutionary changes.
+ 358
+ 359    Parameters
+ 360    ----------
+ 361    prey_birth : float
+ 362        The probability or rate of prey reproduction.
+ 363    prey_death : float
+ 364        The base probability or rate of prey mortality.
+ 365    predator_birth : float
+ 366        The probability or rate of predator reproduction upon consuming prey.
+ 367    predator_death : float
+ 368        The probability or rate of predator mortality.
+ 369    grid_size : int
+ 370        The side length of the square simulation grid.
+ 371    seed : int
+ 372        Random seed for ensuring reproducibility of the simulation run.
+ 373    cfg : Config
+ 374        A configuration object containing simulation hyperparameters (densities,
+ 375        sampling rates, timing, etc.).
+ 376    with_evolution : bool, optional
+ 377        If True, enables the evolution of the 'prey_death' parameter within
+ 378        the model (default is False).
+ 379    compute_pcf : bool, optional
+ 380        Explicit toggle for Pair Correlation Function calculation. If None,
+ 381        it is determined by `cfg.pcf_sample_rate` (default is None).
+ 382
+ 383    Returns
+ 384    -------
+ 385    result : dict
+ 386        A dictionary containing simulation results including:
+ 387        - Input parameters and survival flags.
+ 388        - Population mean and standard deviation for both species.
+ 389        - Cluster statistics (number of clusters, sizes, largest fractions).
+ 390        - Evolutionary statistics (mean, std, min, max, and final values).
+ 391        - PCF data and spatial indices (segregation and clustering).
+ 392        - Optional time series for populations and evolved parameters.
+ 393
+ 394    Notes
+ 395    -----
+ 396    The function relies on several external utilities: `count_populations`,
+ 397    `get_evolved_stats`, `get_cluster_stats_fast`, `compute_all_pcfs_fast`,
+ 398    and `average_pcfs`.
+ 399    """
+ 400
+ 401    from models.CA import PP
+ 402
+ 403    if USE_NUMBA:
+ 404        set_numba_seed(seed)
+ 405
+ 406    if compute_pcf is None:
+ 407        compute_pcf = cfg.collect_pcf and (np.random.random() < cfg.pcf_sample_rate)
+ 408
+ 409    # Initialize model
+ 410    model = PP(
+ 411        rows=grid_size,
+ 412        cols=grid_size,
+ 413        densities=cfg.densities,
+ 414        neighborhood="moore",  # NOTE: Default neighborhood
+ 415        params={
+ 416            "prey_birth": prey_birth,
+ 417            "prey_death": prey_death,
+ 418            "predator_death": predator_death,
+ 419            "predator_birth": predator_birth,
+ 420        },
+ 421        seed=seed,
+ 422        synchronous=cfg.synchronous,
+ 423        directed_hunting=cfg.directed_hunting,
+ 424    )
+ 425
+ 426    if with_evolution:
+ 427        model.evolve(
+ 428            "prey_death",
+ 429            sd=cfg.evolve_sd,
+ 430            min_val=cfg.evolve_min,
+ 431            max_val=cfg.evolve_max,
+ 432        )
+ 433
+ 434    # Scale timing with grid size
+ 435    warmup_steps = cfg.get_warmup_steps(grid_size)
+ 436    measurement_steps = cfg.get_measurement_steps(grid_size)
+ 437
+ 438    # Warmup phase
+ 439    for _ in range(warmup_steps):
+ 440        model.update()
+ 441
+ 442    # Measurement phase: start collecting our mertics
+ 443    prey_pops, pred_pops = [], []  # Prey populations and predator populations
+ 444    evolved_means, evolved_stds = [], []  # Evolution stats over time
+ 445    cluster_sizes_prey, cluster_sizes_pred = [], []  # Cluster sizes
+ 446    largest_fractions_prey, largest_fractions_pred = (
+ 447        [],
+ 448        [],
+ 449    )  # Largest cluster fractions = size of largest cluster / total population
+ 450    pcf_samples = {"prey_prey": [], "pred_pred": [], "prey_pred": []}
+ 451
+ 452    # Determine minimum count for analysis
+ 453    min_count = int(cfg.min_density_for_analysis * (grid_size**2))
+ 454
+ 455    for step in range(measurement_steps):
+ 456        model.update()
+ 457
+ 458        _, prey, pred = count_populations(model.grid)
+ 459        prey_pops.append(prey)
+ 460        pred_pops.append(pred)
+ 461
+ 462        # Track evolution
+ 463        if with_evolution:
+ 464            stats = get_evolved_stats(model, "prey_death")
+ 465            evolved_means.append(stats["mean"])
+ 466            evolved_stds.append(stats["std"])
+ 467
+ 468        # Cluster analysis (at end of measurement)
+ 469        if step == measurement_steps - 1:
+ 470            prey_survived = prey_pops[-1] > min_count
+ 471            pred_survived = pred_pops[-1] > (min_count // 4)
+ 472
+ 473            if prey_survived:
+ 474                prey_stats = get_cluster_stats_fast(model.grid, 1)
+ 475                cluster_sizes_prey = prey_stats["sizes"].tolist()
+ 476                largest_fractions_prey.append(prey_stats["largest_fraction"])
+ 477
+ 478            if pred_survived:
+ 479                pred_stats = get_cluster_stats_fast(model.grid, 2)
+ 480                cluster_sizes_pred = pred_stats["sizes"].tolist()
+ 481                largest_fractions_pred.append(pred_stats["largest_fraction"])
+ 482
+ 483            # PCF requires both
+ 484            if compute_pcf and prey_survived and pred_survived:
+ 485                max_dist = min(grid_size / 2, cfg.pcf_max_distance)
+ 486                pcf_data = compute_all_pcfs_fast(model.grid, max_dist, cfg.pcf_n_bins)
+ 487                pcf_samples["prey_prey"].append(pcf_data["prey_prey"])
+ 488                pcf_samples["pred_pred"].append(pcf_data["pred_pred"])
+ 489                pcf_samples["prey_pred"].append(pcf_data["prey_pred"])
+ 490
+ 491    # Compile results
+ 492    result = {
+ 493        # Parameters
+ 494        "prey_birth": prey_birth,
+ 495        "prey_death": prey_death,
+ 496        "predator_birth": predator_birth,
+ 497        "predator_death": predator_death,
+ 498        "grid_size": grid_size,
+ 499        "with_evolution": with_evolution,
+ 500        "seed": seed,
+ 501        # Population dynamics
+ 502        "prey_mean": float(np.mean(prey_pops)),
+ 503        "prey_std": float(np.std(prey_pops)),
+ 504        "pred_mean": float(np.mean(pred_pops)),
+ 505        "pred_std": float(np.std(pred_pops)),
+ 506        "prey_survived": prey_pops[-1] > min_count,
+ 507        "pred_survived": pred_pops[-1] > (min_count // 4),
+ 508        # Cluster statistics
+ 509        "prey_n_clusters": len(cluster_sizes_prey),
+ 510        "pred_n_clusters": len(cluster_sizes_pred),
+ 511        "prey_cluster_sizes": cluster_sizes_prey,
+ 512        "pred_cluster_sizes": cluster_sizes_pred,
+ 513        # Order parameters
+ 514        "prey_largest_fraction": (
+ 515            float(np.mean(largest_fractions_prey)) if largest_fractions_prey else np.nan
+ 516        ),
+ 517        "pred_largest_fraction": (
+ 518            float(np.mean(largest_fractions_pred)) if largest_fractions_pred else np.nan
+ 519        ),
+ 520    }
+ 521
+ 522    # Time series (if requested)
+ 523    if cfg.save_timeseries:
+ 524        subsample = cfg.timeseries_subsample
+ 525        result["prey_timeseries"] = prey_pops[
+ 526            ::subsample
+ 527        ]  # NOTE: Sample temporal data every 'subsample' steps
+ 528        result["pred_timeseries"] = pred_pops[::subsample]
+ 529
+ 530    # Evolution statistics
+ 531    if with_evolution and evolved_means:
+ 532        valid_means = [v for v in evolved_means if not np.isnan(v)]
+ 533        result["evolved_prey_death_mean"] = (
+ 534            float(np.mean(valid_means)) if valid_means else np.nan
+ 535        )
+ 536        result["evolved_prey_death_std"] = (
+ 537            float(np.mean([v for v in evolved_stds if not np.isnan(v)]))
+ 538            if evolved_stds
+ 539            else np.nan
+ 540        )
+ 541        result["evolved_prey_death_final"] = valid_means[-1] if valid_means else np.nan
+ 542        result["evolved_prey_death_min"] = (
+ 543            float(np.min(valid_means)) if valid_means else np.nan
+ 544        )
+ 545        result["evolved_prey_death_max"] = (
+ 546            float(np.max(valid_means)) if valid_means else np.nan
+ 547        )
+ 548        result["evolve_sd"] = cfg.evolve_sd
+ 549
+ 550        if cfg.save_timeseries:
+ 551            result["evolved_prey_death_timeseries"] = evolved_means[
+ 552                :: cfg.timeseries_subsample
+ 553            ]
+ 554
+ 555    # PCF statistics
+ 556    if pcf_samples["prey_prey"]:
+ 557        dist, pcf_rr, _ = average_pcfs(pcf_samples["prey_prey"])
+ 558        _, pcf_cc, _ = average_pcfs(pcf_samples["pred_pred"])
+ 559        _, pcf_cr, _ = average_pcfs(pcf_samples["prey_pred"])
+ 560
+ 561        result["pcf_distances"] = dist.tolist()
+ 562        result["pcf_prey_prey"] = pcf_rr.tolist()
+ 563        result["pcf_pred_pred"] = pcf_cc.tolist()
+ 564        result["pcf_prey_pred"] = pcf_cr.tolist()
+ 565
+ 566        # Short-range indices
+ 567        short_mask = dist < 3.0
+ 568        if np.any(short_mask):
+ 569            result["segregation_index"] = float(np.mean(pcf_cr[short_mask]))
+ 570            result["prey_clustering_index"] = float(np.mean(pcf_rr[short_mask]))
+ 571            result["pred_clustering_index"] = float(np.mean(pcf_cc[short_mask]))
+ 572
+ 573    return result
+ 574
+ 575
+ 576# =============================================================================
+ 577# Experiment Phases
+ 578# =============================================================================
+ 579
+ 580
+ 581def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+ 582    """
+ 583    Execute Phase 1 of the simulation: a parameter sweep to identify critical points.
+ 584
+ 585    This function performs a 1D sweep across varying prey mortality rates while
+ 586    keeping other parameters fixed. It utilizes parallel execution via joblib
+ 587    and saves results incrementally to a JSONL file to ensure data integrity
+ 588    during long-running batches.
+ 589
+ 590    Parameters
+ 591    ----------
+ 592    cfg : Config
+ 593        Configuration object containing simulation hyperparameters, sweep
+ 594        ranges, and execution settings (n_jobs, grid_size, etc.).
+ 595    output_dir : Path
+ 596        Directory where result files (JSONL) and metadata (JSON) will be stored.
+ 597    logger : logging.Logger
+ 598        Logger instance for tracking simulation progress and recording
+ 599        operational metadata.
+ 600
+ 601    Returns
+ 602    -------
+ 603    all_results : list of dict
+ 604        A list of dictionaries containing the metrics collected from every
+ 605        individual simulation run in the sweep.
+ 606
+ 607    Notes
+ 608    -----
+ 609    The function performs the following steps:
+ 610    1. Pre-warms Numba kernels for performance.
+ 611    2. Generates a deterministic set of simulation jobs using unique seeds.
+ 612    3. Executes simulations in parallel using a generator for memory efficiency.
+ 613    4. Records metadata including a timestamp and a serialized snapshot of
+ 614       the configuration.
+ 615    """
+ 616    from joblib import Parallel, delayed
+ 617
+ 618    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+ 619
+ 620    prey_deaths = cfg.get_prey_deaths()
+ 621
+ 622    # Build job list
+ 623    jobs = []
+ 624    # Sweep through prey_death only (prey_birth is fixed)
+ 625    for pd in prey_deaths:
+ 626        for rep in range(cfg.n_replicates):
+ 627            params = {"pd": pd}
+ 628
+ 629            seed = generate_unique_seed(params, rep)
+ 630            jobs.append(
+ 631                (
+ 632                    cfg.prey_birth,
+ 633                    pd,
+ 634                    cfg.predator_birth,
+ 635                    cfg.predator_death,
+ 636                    cfg.grid_size,
+ 637                    seed,
+ 638                    cfg,
+ 639                    False,
+ 640                )
+ 641            )
+ 642
+ 643    logger.info(f"Phase 1: {len(jobs):,} simulations")
+ 644    logger.info(
+ 645        f"  Grid: {cfg.n_prey_death} prey_death values × {cfg.n_replicates} reps (prey_birth={cfg.prey_birth})"
+ 646    )
+ 647    # Run with incremental saving
+ 648    output_jsonl = output_dir / "phase1_results.jsonl"
+ 649    all_results = []
+ 650
+ 651    with open(output_jsonl, "w", encoding="utf-8") as f:
+ 652        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+ 653        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+ 654
+ 655        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 1"):
+ 656            f.write(json.dumps(result, default=str) + "\n")
+ 657            f.flush()
+ 658            all_results.append(result)
+ 659
+ 660    # Save metadata
+ 661    meta = {
+ 662        "phase": 1,
+ 663        "description": "Parameter sweep for critical point",
+ 664        "n_sims": len(all_results),
+ 665        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+ 666        "config": asdict(cfg),
+ 667    }
+ 668    with open(output_dir / "phase1_metadata.json", "w") as f:
+ 669        json.dump(meta, f, indent=2, default=str)
+ 670
+ 671    logger.info(f"Phase 1 complete. Results: {output_jsonl}")
+ 672    return all_results
+ 673
+ 674
+ 675def run_phase2(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+ 676    """
+ 677    Execute Phase 2 of the simulation: self-organization and criticality analysis.
+ 678
+ 679    This phase tests the Self-Organized Criticality (SOC) hypothesis by
+ 680    initializing simulations at different points in the parameter space and
+ 681    observing whether evolutionary pressure drives the system toward a
+ 682    common critical point, regardless of initial prey mortality rates.
+ 683
+ 684    Parameters
+ 685    ----------
+ 686    cfg : Config
+ 687        Configuration object containing simulation hyperparameters, evolution
+ 688        settings, and execution constraints.
+ 689    output_dir : Path
+ 690        Directory where result files (JSONL) and metadata (JSON) will be stored.
+ 691    logger : logging.Logger
+ 692        Logger instance for tracking progress and evolutionary convergence.
+ 693
+ 694    Returns
+ 695    -------
+ 696    all_results : list of dict
+ 697        A list of dictionaries containing metrics from the evolutionary
+ 698        simulation runs.
+ 699
+ 700    Notes
+ 701    -----
+ 702    The function captures:
+ 703    1. Convergence of 'prey_death' across multiple replicates.
+ 704    2. Final steady-state population distributions.
+ 705    3. Incremental saving of results to prevent data loss.
+ 706    """
+ 707    from joblib import Parallel, delayed
+ 708
+ 709    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+ 710
+ 711    # Test at multiple prey_birth values
+ 712    pb = 0.2
+ 713    # Vary intial prey_death
+ 714    initial_prey_deaths = np.linspace(
+ 715        cfg.prey_death_range[0], cfg.prey_death_range[1], cfg.n_prey_death
+ 716    )
+ 717
+ 718    jobs = []
+ 719    for initial_pd in initial_prey_deaths:
+ 720        for rep in range(cfg.n_replicates):
+ 721            params = {"pb": pb, "initial_pd": initial_pd, "phase": 2}
+ 722            seed = generate_unique_seed(params, rep)
+ 723            jobs.append(
+ 724                (
+ 725                    pb,
+ 726                    initial_pd,
+ 727                    cfg.predator_birth,
+ 728                    cfg.predator_death,
+ 729                    cfg.grid_size,
+ 730                    seed,
+ 731                    cfg,
+ 732                    True,
+ 733                )
+ 734            )
+ 735
+ 736    logger.info(f"Phase 2: {len(jobs):,} simulations")
+ 737    logger.info(f"  prey_birth value: {pb}")
+ 738    logger.info(f"  initial prey_death values: {len(initial_prey_deaths)}")
+ 739    logger.info(f"  Replicates: {cfg.n_replicates}")
+ 740
+ 741    output_jsonl = output_dir / "phase2_results.jsonl"
+ 742    all_results = []
+ 743
+ 744    with open(output_jsonl, "w", encoding="utf-8") as f:
+ 745        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+ 746        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+ 747
+ 748        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 2"):
+ 749            f.write(json.dumps(result, default=str) + "\n")
+ 750            f.flush()
+ 751            all_results.append(result)
+ 752
+ 753    meta = {
+ 754        "phase": 2,
+ 755        "description": "Self-organization toward criticality",
+ 756        "n_sims": len(all_results),
+ 757        "initial_prey_deaths": initial_prey_deaths.tolist(),
+ 758        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+ 759    }
+ 760    with open(output_dir / "phase2_metadata.json", "w") as f:
+ 761        json.dump(meta, f, indent=2, default=str)
+ 762
+ 763    logger.info(f"Phase 2 complete. Results: {output_jsonl}")
+ 764    return all_results
+ 765
+ 766
+ 767def run_phase3(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+ 768    """
+ 769    Phase 3: Finite-size scaling at critical point.
+ 770
+ 771    - Multiple grid sizes at (critical_prey_birth, critical_prey_death)
+ 772    - Analyze cluster size cutoffs vs L
+ 773    """
+ 774    from joblib import Parallel, delayed
+ 775
+ 776    # NOTE: Tuned to critical points from phase 1
+ 777    pb = cfg.critical_prey_birth
+ 778    pd = cfg.critical_prey_death
+ 779
+ 780    logger.info(f"Phase 3: FSS at critical point (pb={pb}, pd={pd})")
+ 781
+ 782    for L in cfg.grid_sizes:
+ 783        warmup_numba_kernels(L, directed_hunting=cfg.directed_hunting)
+ 784
+ 785    jobs = []
+ 786    for L in cfg.grid_sizes:  # Sweep through grid sizes
+ 787        for rep in range(cfg.n_replicates):
+ 788            params = {"L": L, "phase": 3}
+ 789            seed = generate_unique_seed(params, rep)
+ 790            jobs.append(
+ 791                (pb, pd, cfg.predator_birth, cfg.predator_death, L, seed, cfg, False)
+ 792            )
+ 793
+ 794    logger.info(f"  Grid sizes: {cfg.grid_sizes}")
+ 795    logger.info(f"  Total simulations: {len(jobs):,}")
+ 796
+ 797    output_jsonl = output_dir / "phase3_results.jsonl"
+ 798    all_results = []
+ 799
+ 800    with open(output_jsonl, "w", encoding="utf-8") as f:
+ 801        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+ 802        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+ 803
+ 804        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 3"):
+ 805            f.write(json.dumps(result, default=str) + "\n")
+ 806            f.flush()
+ 807            all_results.append(result)
+ 808
+ 809    # Post-run metadata: postprocessing will fit cluster cutoffs vs L
+ 810    meta = {
+ 811        "phase": 3,
+ 812        "description": "Finite-size scaling",
+ 813        "critical_point": {"prey_birth": pb, "prey_death": pd},
+ 814        "grid_sizes": cfg.grid_sizes,
+ 815        "n_sims": len(all_results),
+ 816        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+ 817    }
+ 818    with open(output_dir / "phase3_metadata.json", "w") as f:
+ 819        json.dump(meta, f, indent=2, default=str)
+ 820
+ 821    logger.info(f"Phase 3 complete. Results: {output_jsonl}")
+ 822    return all_results
+ 823
+ 824
+ 825def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+ 826    """
+ 827    Execute Phase 3 of the simulation: Finite-Size Scaling (FSS) analysis.
+ 828
+ 829    This phase investigates how spatial structures, specifically cluster size
+ 830    cutoffs, scale with the system size (L) at the critical point identified
+ 831    in Phase 1. This is essential for determining the universality class of
+ 832    the phase transition.
+ 833
+ 834    Parameters
+ 835    ----------
+ 836    cfg : Config
+ 837        Configuration object containing critical point parameters, the list of
+ 838        grid sizes to test, and execution settings.
+ 839    output_dir : Path
+ 840        Directory where result files (JSONL) and FSS metadata (JSON) will be
+ 841        stored.
+ 842    logger : logging.Logger
+ 843        Logger instance for tracking progress across different grid sizes.
+ 844
+ 845    Returns
+ 846    -------
+ 847    all_results : list of dict
+ 848        A list of dictionaries containing metrics and cluster statistics for
+ 849        each grid size and replicate.
+ 850
+ 851    Notes
+ 852    -----
+ 853    The function performs the following:
+ 854    1. Iterates through multiple grid sizes defined in `cfg.grid_sizes`.
+ 855    2. Generates parallel jobs for each size using critical birth/death rates.
+ 856    3. Saves results incrementally to allow for post-simulation analysis of
+ 857       power-law exponents.
+ 858    """
+ 859    from joblib import Parallel, delayed
+ 860    import itertools
+ 861
+ 862    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+ 863
+ 864    # Define sweep values
+ 865    prey_death_values = np.linspace(0.05, 0.95, 10)  # 10 values for prey_death
+ 866    other_param_values = np.linspace(0.0, 1.0, 11)  # 11 values for the rest
+ 867
+ 868    # Logging
+ 869    logger.info(f"Phase 4: Full 4D Parameter Sweep")
+ 870    logger.info(f"  prey_death: 10 values from 0.05 to 0.95")
+ 871    logger.info(f"  prey_birth, pred_birth, pred_death: 11 values each from 0 to 1")
+ 872    logger.info(f"  Grid Size: {cfg.grid_size}")
+ 873    logger.info(f"  Replicates: {cfg.n_replicates}")
+ 874
+ 875    # Build parameter grid
+ 876    param_grid = itertools.product(
+ 877        other_param_values,  # prey_birth (11 values)
+ 878        prey_death_values,  # prey_death (10 values)
+ 879        other_param_values,  # predator_birth (11 values)
+ 880        other_param_values,  # predator_death (11 values)
+ 881    )
+ 882
+ 883    jobs = []
+ 884
+ 885    for pb, pd, pred_b, pred_d in param_grid:
+ 886        for rep in range(cfg.n_replicates):
+ 887            params_id = {
+ 888                "pb": pb,
+ 889                "pd": pd,
+ 890                "pred_b": pred_b,
+ 891                "pred_d": pred_d,
+ 892                "rep": rep,
+ 893            }
+ 894            seed = generate_unique_seed(params_id, rep)
+ 895
+ 896            jobs.append(
+ 897                (
+ 898                    pb,  # prey_birth
+ 899                    pd,  # prey_death
+ 900                    pred_b,  # predator_birth
+ 901                    pred_d,  # predator_death
+ 902                    cfg.grid_size,
+ 903                    seed,
+ 904                    cfg,
+ 905                    False,
+ 906                )
+ 907            )
+ 908
+ 909    logger.info(
+ 910        f"  Total simulations: {len(jobs):,}"
+ 911    )  # 11 * 10 * 11 * 11 * n_reps = 13,310 * n_reps
+ 912
+ 913    output_jsonl = output_dir / "phase4_results.jsonl"
+ 914    all_results = []
+ 915
+ 916    with open(output_jsonl, "w", encoding="utf-8") as f:
+ 917        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+ 918        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+ 919
+ 920        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 4 (4D Sweep)"):
+ 921            f.write(json.dumps(result, default=str) + "\n")
+ 922            f.flush()
+ 923            all_results.append(result)
+ 924
+ 925    # Save Metadata
+ 926    meta = {
+ 927        "phase": 4,
+ 928        "description": "Global 4D Sensitivity Analysis",
+ 929        "prey_death_values": prey_death_values.tolist(),
+ 930        "other_param_values": other_param_values.tolist(),
+ 931        "parameters_varied": [
+ 932            "prey_birth",
+ 933            "prey_death",
+ 934            "predator_birth",
+ 935            "predator_death",
+ 936        ],
+ 937        "n_sims": len(all_results),
+ 938        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+ 939        "config": asdict(cfg),
+ 940    }
+ 941    with open(output_dir / "phase4_metadata.json", "w") as f:
+ 942        json.dump(meta, f, indent=2, default=str)
+ 943
+ 944    logger.info(f"Phase 4 complete. Results: {output_jsonl}")
+ 945    return all_results
+ 946
+ 947
+ 948def run_phase5(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+ 949    """
+ 950    Execute Phase 5 of the simulation: Global 4D parameter sweep with directed hunting.
+ 951
+ 952    This phase performs a comprehensive sensitivity analysis by varying four key
+ 953    parameters (prey birth/death and predator birth/death) while directed
+ 954    hunting is enabled. The results allow for a direct comparison with Phase 4
+ 955    to determine how predator search behavior shifts the system's critical
+ 956    thresholds and stability.
+ 957
+ 958    Parameters
+ 959    ----------
+ 960    cfg : Config
+ 961        Configuration object containing simulation hyperparameters, parallel
+ 962        execution settings, and the fixed grid size for this phase.
+ 963    output_dir : Path
+ 964        Directory where the result JSONL file and execution metadata will
+ 965        be stored.
+ 966    logger : logging.Logger
+ 967        Logger instance for tracking the progress of the high-volume
+ 968        simulation batch.
+ 969
+ 970    Returns
+ 971    -------
+ 972    all_results : list of dict
+ 973        A list of dictionaries containing metrics for every simulation in
+ 974        the 4D parameter grid.
+ 975
+ 976    Notes
+ 977    -----
+ 978    The function utilizes a Cartesian product of parameter ranges to build a
+ 979    job list of over 13,000 unique parameter sets (multiplied by replicates).
+ 980    Seeds are uniquely generated to distinguish these runs from other phases
+ 981    even if parameter values overlap.
+ 982    """
+ 983    from joblib import Parallel, delayed
+ 984    import itertools
+ 985
+ 986    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+ 987
+ 988    # Define sweep values (same as Phase 4)
+ 989    prey_death_values = np.linspace(0.05, 0.95, 10)  # 10 values for prey_death
+ 990    other_param_values = np.linspace(0.0, 1.0, 11)  # 11 values for the rest
+ 991
+ 992    # Logging
+ 993    logger.info(f"Phase 5: Full 4D Parameter Sweep (Directed Hunting)")
+ 994    logger.info(f"  prey_death: 10 values from 0.05 to 0.95")
+ 995    logger.info(f"  prey_birth, pred_birth, pred_death: 11 values each from 0 to 1")
+ 996    logger.info(f"  Grid Size: {cfg.grid_size}")
+ 997    logger.info(f"  Replicates: {cfg.n_replicates}")
+ 998    logger.info(f"  Directed Hunting: {cfg.directed_hunting}")
+ 999
+1000    # Build parameter grid
+1001    param_grid = itertools.product(
+1002        other_param_values,  # prey_birth (11 values)
+1003        prey_death_values,  # prey_death (10 values)
+1004        other_param_values,  # predator_birth (11 values)
+1005        other_param_values,  # predator_death (11 values)
+1006    )
+1007
+1008    jobs = []
+1009
+1010    for pb, pd, pred_b, pred_d in param_grid:
+1011        for rep in range(cfg.n_replicates):
+1012            # Include phase identifier to ensure different seeds from Phase 4
+1013            params_id = {
+1014                "pb": pb,
+1015                "pd": pd,
+1016                "pred_b": pred_b,
+1017                "pred_d": pred_d,
+1018                "phase": 6,
+1019                "rep": rep,
+1020            }
+1021            seed = generate_unique_seed(params_id, rep)
+1022
+1023            jobs.append(
+1024                (
+1025                    pb,  # prey_birth
+1026                    pd,  # prey_death
+1027                    pred_b,  # predator_birth
+1028                    pred_d,  # predator_death
+1029                    cfg.grid_size,
+1030                    seed,
+1031                    cfg,
+1032                    False,
+1033                )
+1034            )
+1035
+1036    logger.info(
+1037        f"  Total simulations: {len(jobs):,}"
+1038    )  # 11 * 10 * 11 * 11 * n_reps = 13,310 * n_reps
+1039
+1040    output_jsonl = output_dir / "phase5_results.jsonl"
+1041    all_results = []
+1042
+1043    with open(output_jsonl, "w", encoding="utf-8") as f:
+1044        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+1045        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+1046
+1047        for result in tqdm(
+1048            executor(tasks), total=len(jobs), desc="Phase 6 (4D Sweep + Directed)"
+1049        ):
+1050            f.write(json.dumps(result, default=str) + "\n")
+1051            f.flush()
+1052            all_results.append(result)
+1053
+1054    # Save Metadata
+1055    meta = {
+1056        "phase": 5,
+1057        "description": "Global 4D Sensitivity Analysis with Directed Hunting",
+1058        "prey_death_values": prey_death_values.tolist(),
+1059        "other_param_values": other_param_values.tolist(),
+1060        "parameters_varied": [
+1061            "prey_birth",
+1062            "prey_death",
+1063            "predator_birth",
+1064            "predator_death",
+1065        ],
+1066        "directed_hunting": cfg.directed_hunting,
+1067        "n_sims": len(all_results),
+1068        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+1069        "config": asdict(cfg),
+1070    }
+1071    with open(output_dir / "phase6_metadata.json", "w") as f:
+1072        json.dump(meta, f, indent=2, default=str)
+1073
+1074    logger.info(f"Phase 5 complete. Results: {output_jsonl}")
+1075    return all_results
+1076
+1077
+1078# =============================================================================
+1079# Main:
+1080# =============================================================================
+1081
+1082PHASE_RUNNERS = {
+1083    1: run_phase1,
+1084    2: run_phase2,
+1085    3: run_phase3,
+1086    4: run_phase4,
+1087    5: run_phase5,
+1088}
+1089
+1090
+1091def main():
+1092    """
+1093    Organize the predator-prey experimental suite across multiple phases.
+1094
+1095    This entry point handles command-line arguments, sets up logging and output
+1096    directories, and executes the requested simulation phases (1-5). It
+1097    supports parallel execution, dry runs for runtime estimation, and
+1098    automated configuration persistence.
+1099
+1100    Notes
+1101    -----
+1102    The script dynamically retrieves phase-specific configurations using
+1103    `get_phase_config` and dispatches execution to the corresponding runner
+1104    in the `PHASE_RUNNERS` mapping.
+1105    """
+1106    parser = argparse.ArgumentParser(
+1107        description="Predator-Prey Hydra Effect Experiments",
+1108        formatter_class=argparse.RawDescriptionHelpFormatter,
+1109        epilog="""
+1110Phases:
+1111  1  Parameter sweep to find critical point
+1112  2  Self-organization (evolution toward criticality)
+1113  3  Finite-size scaling at critical point
+1114  4  Sensitivity analysis across parameter regimes
+1115  5  Model extensions (directed hunting comparison)
+1116        """,
+1117    )
+1118    parser.add_argument(
+1119        "--phase", type=str, required=True, help="Phase to run: 1-6 or 'all'"
+1120    )
+1121    parser.add_argument(
+1122        "--output",
+1123        type=Path,
+1124        default=Path("results"),
+1125        help="Output directory (default: results)",
+1126    )
+1127    parser.add_argument(
+1128        "--cores", type=int, default=-1, help="Number of cores (-1 for all)"
+1129    )
+1130    parser.add_argument(
+1131        "--dry-run", action="store_true", help="Estimate runtime without running"
+1132    )
+1133    args = parser.parse_args()
+1134
+1135    # Parse phase argument
+1136    if args.phase.lower() == "all":
+1137        phases = list(PHASE_RUNNERS.keys())
+1138    else:
+1139        try:
+1140            phases = [int(args.phase)]
+1141        except ValueError:
+1142            print(f"Invalid phase: {args.phase}. Use 1-6 or 'all'")
+1143            sys.exit(1)
+1144
+1145    # Setup output directory
+1146    args.output.mkdir(parents=True, exist_ok=True)
+1147
+1148    # Setup logging
+1149    logging.basicConfig(
+1150        level=logging.INFO,
+1151        format="%(asctime)s [%(levelname)s] %(message)s",
+1152        handlers=[
+1153            logging.FileHandler(args.output / "experiments.log"),
+1154            logging.StreamHandler(),
+1155        ],
+1156    )
+1157    logger = logging.getLogger(__name__)
+1158
+1159    # Header
+1160    logger.info("=" * 60)
+1161    logger.info("PREDATOR-PREY HYDRA EFFECT EXPERIMENTS")
+1162    logger.info("=" * 60)
+1163    logger.info(f"Phases: {phases}")
+1164    logger.info(f"Output: {args.output}")
+1165    logger.info(f"Cores: {args.cores}")
+1166    logger.info(f"Numba: {'ENABLED' if USE_NUMBA else 'DISABLED'}")
+1167
+1168    # Process each phase
+1169    for phase in phases:
+1170        cfg = get_phase_config(phase)
+1171        cfg.n_jobs = (
+1172            args.cores
+1173            if args.cores > 0
+1174            else int(os.environ.get("SLURM_CPUS_PER_TASK", -1))
+1175        )
+1176
+1177        logger.info("")
+1178        logger.info(f"{'='*60}")
+1179        logger.info(f"PHASE {phase}")
+1180        logger.info(f"{'='*60}")
+1181
+1182        n_cores = cfg.n_jobs if cfg.n_jobs > 0 else os.cpu_count()
+1183        logger.info(f"Estimated: {cfg.estimate_runtime(n_cores)}")
+1184
+1185        if args.dry_run:
+1186            logger.info("Dry run - skipping execution")
+1187            continue
+1188
+1189        # Save config
+1190        with open(args.output / f"phase{phase}_config.json", "w") as f:
+1191            json.dump(asdict(cfg), f, indent=2, default=str)
+1192
+1193        # Run phase
+1194        start_time = time.time()
+1195        runner = PHASE_RUNNERS[phase]
+1196        runner(cfg, args.output, logger)
+1197        elapsed = time.time() - start_time
+1198
+1199        logger.info(f"Phase {phase} runtime: {elapsed/60:.1f} minutes")
+1200
+1201    logger.info("")
+1202    logger.info("=" * 60)
+1203    logger.info("EXPERIMENTS COMPLETE")
+1204    logger.info("=" * 60)
+1205
+1206
+1207if __name__ == "__main__":
+1208    main()
+
+ + +
+
+
+ project_root = +$OLDPWD + + +
+ + + + +
+
+ +
+ + def + generate_unique_seed(params: dict, rep: int) -> int: + + + +
+ +
 71def generate_unique_seed(params: dict, rep: int) -> int:
+ 72    """
+ 73    Create a deterministic seed from a dictionary of parameters and a repetition index.
+ 74
+ 75    This function serializes the input dictionary into a sorted JSON string,
+ 76    appends the repetition count, and hashes the resulting string using SHA-256.
+ 77    The first 8 characters of the hex digest are then converted to an integer
+ 78    to provide a stable, unique seed for random number generators.
+ 79
+ 80    Parameters
+ 81    ----------
+ 82    params : dict
+ 83        A dictionary of configuration parameters. Keys are sorted to ensure
+ 84        determinism regardless of insertion order.
+ 85    rep : int
+ 86        The repetition or iteration index, used to ensure different seeds
+ 87        are generated for the same parameter set across multiple runs.
+ 88
+ 89    Returns
+ 90    -------
+ 91    int
+ 92        A unique integer seed derived from the input parameters.
+ 93
+ 94    Examples
+ 95    --------
+ 96    >>> params = {'learning_rate': 0.01, 'batch_size': 32}
+ 97    >>> generate_unique_seed(params, 1)
+ 98    3432571217
+ 99    >>> generate_unique_seed(params, 2)
+100    3960013583
+101    """
+102    identifier = json.dumps(params, sort_keys=True) + f"_{rep}"
+103    return int(hashlib.sha256(identifier.encode()).hexdigest()[:8], 16)
+
+ + +

Create a deterministic seed from a dictionary of parameters and a repetition index.

+ +

This function serializes the input dictionary into a sorted JSON string, +appends the repetition count, and hashes the resulting string using SHA-256. +The first 8 characters of the hex digest are then converted to an integer +to provide a stable, unique seed for random number generators.

+ +
Parameters
+ +
    +
  • params (dict): +A dictionary of configuration parameters. Keys are sorted to ensure +determinism regardless of insertion order.
  • +
  • rep (int): +The repetition or iteration index, used to ensure different seeds +are generated for the same parameter set across multiple runs.
  • +
+ +
Returns
+ +
    +
  • int: A unique integer seed derived from the input parameters.
  • +
+ +
Examples
+ +
+
>>> params = {'learning_rate': 0.01, 'batch_size': 32}
+>>> generate_unique_seed(params, 1)
+3432571217
+>>> generate_unique_seed(params, 2)
+3960013583
+
+
+
+ + +
+
+ +
+ + def + count_populations(grid: numpy.ndarray) -> Tuple[int, int, int]: + + + +
+ +
106def count_populations(grid: np.ndarray) -> Tuple[int, int, int]:
+107    """
+108    Count the number of empty, prey, and predator cells in the simulation grid.
+109
+110    Parameters
+111    ----------
+112    grid : np.ndarray
+113        A 2D NumPy array representing the simulation environment, where:
+114        - 0: Empty cell
+115        - 1: Prey
+116        - 2: Predator
+117
+118    Returns
+119    -------
+120    empty_count : int
+121        Total number of cells with a value of 0.
+122    prey_count : int
+123        Total number of cells with a value of 1.
+124    predator_count : int
+125        Total number of cells with a value of 2.
+126
+127    Examples
+128    --------
+129    >>> grid = np.array([[0, 1], [2, 1]])
+130    >>> count_populations(grid)
+131    (1, 2, 1)
+132    """
+133    return int(np.sum(grid == 0)), int(np.sum(grid == 1)), int(np.sum(grid == 2))
+
+ + +

Count the number of empty, prey, and predator cells in the simulation grid.

+ +
Parameters
+ +
    +
  • grid (np.ndarray): +A 2D NumPy array representing the simulation environment, where: +
      +
    • 0: Empty cell
    • +
    • 1: Prey
    • +
    • 2: Predator
    • +
  • +
+ +
Returns
+ +
    +
  • empty_count (int): +Total number of cells with a value of 0.
  • +
  • prey_count (int): +Total number of cells with a value of 1.
  • +
  • predator_count (int): +Total number of cells with a value of 2.
  • +
+ +
Examples
+ +
+
>>> grid = np.array([[0, 1], [2, 1]])
+>>> count_populations(grid)
+(1, 2, 1)
+
+
+
+ + +
+
+ +
+ + def + get_evolved_stats(model, param: str) -> Dict: + + + +
+ +
136def get_evolved_stats(model, param: str) -> Dict:
+137    """
+138    Get statistics of an evolved parameter from the model.
+139
+140    This function retrieves parameter values from the model's internal storage,
+141    filters out NaN values, and calculates basic descriptive statistics.
+142
+143    Parameters
+144    ----------
+145    model : object
+146        The simulation model instance containing a `cell_params` attribute
+147        with a `.get()` method.
+148    param : str
+149        The name of the parameter to calculate statistics for.
+150
+151    Returns
+152    -------
+153    stats : dict
+154        A dictionary containing the following keys:
+155        - 'mean': Arithmetic mean of valid values.
+156        - 'std': Standard deviation of valid values.
+157        - 'min': Minimum valid value.
+158        - 'max': Maximum valid value.
+159        - 'n': Count of non-NaN values.
+160        If no valid data is found, all stats return NaN and n returns 0.
+161
+162    Examples
+163    --------
+164    >>> stats = get_evolved_stats(my_model, "speed")
+165    >>> print(stats['mean'])
+166    1.25
+167    """
+168    arr = model.cell_params.get(param)
+169    if arr is None:
+170        return {"mean": np.nan, "std": np.nan, "min": np.nan, "max": np.nan, "n": 0}
+171    valid = arr[~np.isnan(arr)]
+172    if len(valid) == 0:
+173        return {"mean": np.nan, "std": np.nan, "min": np.nan, "max": np.nan, "n": 0}
+174    return {
+175        "mean": float(np.mean(valid)),
+176        "std": float(np.std(valid)),
+177        "min": float(np.min(valid)),
+178        "max": float(np.max(valid)),
+179        "n": len(valid),
+180    }
+
+ + +

Get statistics of an evolved parameter from the model.

+ +

This function retrieves parameter values from the model's internal storage, +filters out NaN values, and calculates basic descriptive statistics.

+ +
Parameters
+ +
    +
  • model (object): +The simulation model instance containing a cell_params attribute +with a .get() method.
  • +
  • param (str): +The name of the parameter to calculate statistics for.
  • +
+ +
Returns
+ +
    +
  • stats (dict): +A dictionary containing the following keys: +
      +
    • 'mean': Arithmetic mean of valid values.
    • +
    • 'std': Standard deviation of valid values.
    • +
    • 'min': Minimum valid value.
    • +
    • 'max': Maximum valid value.
    • +
    • 'n': Count of non-NaN values. +If no valid data is found, all stats return NaN and n returns 0.
    • +
  • +
+ +
Examples
+ +
+
>>> stats = get_evolved_stats(my_model, "speed")
+>>> print(stats['mean'])
+1.25
+
+
+
+ + +
+
+ +
+ + def + average_pcfs( pcf_list: List[Tuple[numpy.ndarray, numpy.ndarray, int]]) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]: + + + +
+ +
183def average_pcfs(
+184    pcf_list: List[Tuple[np.ndarray, np.ndarray, int]],
+185) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
+186    """
+187    Average multiple Pair Correlation Function (PCF) measurements and calculate standard error.
+188
+189    Parameters
+190    ----------
+191    pcf_list : list of tuple
+192        A list where each element is a tuple containing:
+193        - distances (np.ndarray): The radial distances (r).
+194        - pcf_values (np.ndarray): The correlation values g(r).
+195        - count (int): Metadata or weight (not used in current calculation).
+196
+197    Returns
+198    -------
+199    distances : np.ndarray
+200        The radial distances from the first entry in the list.
+201    pcf_mean : np.ndarray
+202        The element-wise mean of the PCF values across all measurements.
+203    pcf_se : np.ndarray
+204        The standard error of the mean for the PCF values.
+205
+206    Examples
+207    --------
+208    >>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10),
+209    ...         (np.array([0, 1]), np.array([1.2, 1.8]), 12)]
+210    >>> dist, mean, se = average_pcfs(data)
+211    >>> mean
+212    array([1.1, 1.9])
+213    """
+214    if len(pcf_list) == 0:
+215        return np.array([]), np.array([]), np.array([])
+216
+217    distances = pcf_list[0][0]
+218    pcfs = np.array([p[1] for p in pcf_list])
+219
+220    pcf_mean = np.mean(pcfs, axis=0)
+221    pcf_se = np.std(pcfs, axis=0) / np.sqrt(len(pcfs))
+222
+223    return distances, pcf_mean, pcf_se
+
+ + +

Average multiple Pair Correlation Function (PCF) measurements and calculate standard error.

+ +
Parameters
+ +
    +
  • pcf_list (list of tuple): +A list where each element is a tuple containing: +
      +
    • distances (np.ndarray): The radial distances (r).
    • +
    • pcf_values (np.ndarray): The correlation values g(r).
    • +
    • count (int): Metadata or weight (not used in current calculation).
    • +
  • +
+ +
Returns
+ +
    +
  • distances (np.ndarray): +The radial distances from the first entry in the list.
  • +
  • pcf_mean (np.ndarray): +The element-wise mean of the PCF values across all measurements.
  • +
  • pcf_se (np.ndarray): +The standard error of the mean for the PCF values.
  • +
+ +
Examples
+ +
+
>>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10),
+...         (np.array([0, 1]), np.array([1.2, 1.8]), 12)]
+>>> dist, mean, se = average_pcfs(data)
+>>> mean
+array([1.1, 1.9])
+
+
+
+ + +
+
+ +
+ + def + save_results_jsonl(results: List[Dict], output_path: pathlib.Path): + + + +
+ +
226def save_results_jsonl(results: List[Dict], output_path: Path):
+227    """
+228    Save a list of dictionaries to a file in JSON Lines (JSONL) format.
+229
+230    Each dictionary in the list is serialized into a single JSON string and
+231    written as a new line. Non-serializable objects are converted to strings
+232    using the default string representation.
+233
+234    Parameters
+235    ----------
+236    results : list of dict
+237        The collection of result dictionaries to be saved.
+238    output_path : Path
+239        The file system path (pathlib.Path) where the JSONL file will be created.
+240
+241    Returns
+242    -------
+243    None
+244
+245    Notes
+246    -----
+247    The file is opened in 'w' (write) mode, which will overwrite any existing
+248    content at the specified path.
+249
+250    Examples
+251    --------
+252    >>> data = [{"id": 1, "score": 0.95}, {"id": 2, "score": 0.88}]
+253    >>> save_results_jsonl(data, Path("results.jsonl"))
+254    """
+255    with open(output_path, "w", encoding="utf-8") as f:
+256        for result in results:
+257            f.write(json.dumps(result, default=str) + "\n")
+
+ + +

Save a list of dictionaries to a file in JSON Lines (JSONL) format.

+ +

Each dictionary in the list is serialized into a single JSON string and +written as a new line. Non-serializable objects are converted to strings +using the default string representation.

+ +
Parameters
+ +
    +
  • results (list of dict): +The collection of result dictionaries to be saved.
  • +
  • output_path (Path): +The file system path (pathlib.Path) where the JSONL file will be created.
  • +
+ +
Returns
+ +
    +
  • None
  • +
+ +
Notes
+ +

The file is opened in 'w' (write) mode, which will overwrite any existing +content at the specified path.

+ +
Examples
+ +
+
>>> data = [{"id": 1, "score": 0.95}, {"id": 2, "score": 0.88}]
+>>> save_results_jsonl(data, Path("results.jsonl"))
+
+
+
+ + +
+
+ +
+ + def + save_results_npz(results: List[Dict], output_path: pathlib.Path): + + + +
+ +
260def save_results_npz(results: List[Dict], output_path: Path):
+261    """
+262    Save simulation results to a compressed NumPy (.npz) binary file.
+263
+264    This function flattens a list of result dictionaries into a single
+265    dictionary of NumPy arrays, prefixing keys with the run index to
+266    maintain data separation. The resulting file is compressed to
+267    reduce storage space.
+268
+269    Parameters
+270    ----------
+271    results : list of dict
+272        A list where each dictionary contains key-value pairs of
+273        simulation data (e.g., arrays, lists, or scalars).
+274    output_path : Path
+275        The file system path (pathlib.Path) where the compressed
+276        NPZ file will be saved.
+277
+278    Returns
+279    -------
+280    None
+281
+282    Notes
+283    -----
+284    The keys in the saved file follow the format 'run_{index}_{original_key}'.
+285    Values are automatically converted to NumPy arrays if they are not
+286    already.
+287
+288    Examples
+289    --------
+290    >>> results = [{"energy": [1, 2]}, {"energy": [3, 4]}]
+291    >>> save_results_npz(results, Path("output.npz"))
+292    """
+293    data = {}
+294    for i, res in enumerate(results):
+295        for key, val in res.items():
+296            data[f"run_{i}_{key}"] = np.array(val)
+297    np.savez_compressed(output_path, **data)
+
+ + +

Save simulation results to a compressed NumPy (.npz) binary file.

+ +

This function flattens a list of result dictionaries into a single +dictionary of NumPy arrays, prefixing keys with the run index to +maintain data separation. The resulting file is compressed to +reduce storage space.

+ +
Parameters
+ +
    +
  • results (list of dict): +A list where each dictionary contains key-value pairs of +simulation data (e.g., arrays, lists, or scalars).
  • +
  • output_path (Path): +The file system path (pathlib.Path) where the compressed +NPZ file will be saved.
  • +
+ +
Returns
+ +
    +
  • None
  • +
+ +
Notes
+ +

The keys in the saved file follow the format 'run_{index}_{original_key}'. +Values are automatically converted to NumPy arrays if they are not +already.

+ +
Examples
+ +
+
>>> results = [{"energy": [1, 2]}, {"energy": [3, 4]}]
+>>> save_results_npz(results, Path("output.npz"))
+
+
+
+ + +
+
+ +
+ + def + load_results_jsonl(input_path: pathlib.Path) -> List[Dict]: + + + +
+ +
300def load_results_jsonl(input_path: Path) -> List[Dict]:
+301    """
+302    Load simulation results from a JSON Lines (JSONL) formatted file.
+303
+304    This function reads a file line-by-line, parsing each line as an
+305    independent JSON object and aggregating them into a list of dictionaries.
+306
+307    Parameters
+308    ----------
+309    input_path : Path
+310        The file system path (pathlib.Path) to the JSONL file.
+311
+312    Returns
+313    -------
+314    results : list of dict
+315        A list of dictionaries reconstructed from the file content.
+316
+317    Raises
+318    ------
+319    FileNotFoundError
+320        If the specified input path does not exist.
+321    json.JSONDecodeError
+322        If a line in the file is not valid JSON.
+323
+324    Examples
+325    --------
+326    >>> data = load_results_jsonl(Path("results.jsonl"))
+327    >>> len(data)
+328    2
+329    """
+330    results = []
+331    with open(input_path, "r", encoding="utf-8") as f:
+332        for line in f:
+333            results.append(json.loads(line.strip()))
+334    return results
+
+ + +

Load simulation results from a JSON Lines (JSONL) formatted file.

+ +

This function reads a file line-by-line, parsing each line as an +independent JSON object and aggregating them into a list of dictionaries.

+ +
Parameters
+ +
    +
  • input_path (Path): +The file system path (pathlib.Path) to the JSONL file.
  • +
+ +
Returns
+ +
    +
  • results (list of dict): +A list of dictionaries reconstructed from the file content.
  • +
+ +
Raises
+ +
    +
  • FileNotFoundError: If the specified input path does not exist.
  • +
  • json.JSONDecodeError: If a line in the file is not valid JSON.
  • +
+ +
Examples
+ +
+
>>> data = load_results_jsonl(Path("results.jsonl"))
+>>> len(data)
+2
+
+
+
+ + +
+
+ +
+ + def + run_single_simulation( prey_birth: float, prey_death: float, predator_birth: float, predator_death: float, grid_size: int, seed: int, cfg: models.config.Config, with_evolution: bool = False, compute_pcf: Optional[bool] = None) -> Dict: + + + +
+ +
342def run_single_simulation(
+343    prey_birth: float,
+344    prey_death: float,
+345    predator_birth: float,
+346    predator_death: float,
+347    grid_size: int,
+348    seed: int,
+349    cfg: Config,
+350    with_evolution: bool = False,
+351    compute_pcf: Optional[bool] = None,
+352) -> Dict:
+353    """
+354    Run a single Predator-Prey (PP) simulation and collect comprehensive metrics.
+355
+356    This function initializes a Cellular Automata model, executes a warmup phase
+357    to reach steady state, and then performs a measurement phase to track
+358    population dynamics, spatial clustering, and evolutionary changes.
+359
+360    Parameters
+361    ----------
+362    prey_birth : float
+363        The probability or rate of prey reproduction.
+364    prey_death : float
+365        The base probability or rate of prey mortality.
+366    predator_birth : float
+367        The probability or rate of predator reproduction upon consuming prey.
+368    predator_death : float
+369        The probability or rate of predator mortality.
+370    grid_size : int
+371        The side length of the square simulation grid.
+372    seed : int
+373        Random seed for ensuring reproducibility of the simulation run.
+374    cfg : Config
+375        A configuration object containing simulation hyperparameters (densities,
+376        sampling rates, timing, etc.).
+377    with_evolution : bool, optional
+378        If True, enables the evolution of the 'prey_death' parameter within
+379        the model (default is False).
+380    compute_pcf : bool, optional
+381        Explicit toggle for Pair Correlation Function calculation. If None,
+382        it is determined by `cfg.pcf_sample_rate` (default is None).
+383
+384    Returns
+385    -------
+386    result : dict
+387        A dictionary containing simulation results including:
+388        - Input parameters and survival flags.
+389        - Population mean and standard deviation for both species.
+390        - Cluster statistics (number of clusters, sizes, largest fractions).
+391        - Evolutionary statistics (mean, std, min, max, and final values).
+392        - PCF data and spatial indices (segregation and clustering).
+393        - Optional time series for populations and evolved parameters.
+394
+395    Notes
+396    -----
+397    The function relies on several external utilities: `count_populations`,
+398    `get_evolved_stats`, `get_cluster_stats_fast`, `compute_all_pcfs_fast`,
+399    and `average_pcfs`.
+400    """
+401
+402    from models.CA import PP
+403
+404    if USE_NUMBA:
+405        set_numba_seed(seed)
+406
+407    if compute_pcf is None:
+408        compute_pcf = cfg.collect_pcf and (np.random.random() < cfg.pcf_sample_rate)
+409
+410    # Initialize model
+411    model = PP(
+412        rows=grid_size,
+413        cols=grid_size,
+414        densities=cfg.densities,
+415        neighborhood="moore",  # NOTE: Default neighborhood
+416        params={
+417            "prey_birth": prey_birth,
+418            "prey_death": prey_death,
+419            "predator_death": predator_death,
+420            "predator_birth": predator_birth,
+421        },
+422        seed=seed,
+423        synchronous=cfg.synchronous,
+424        directed_hunting=cfg.directed_hunting,
+425    )
+426
+427    if with_evolution:
+428        model.evolve(
+429            "prey_death",
+430            sd=cfg.evolve_sd,
+431            min_val=cfg.evolve_min,
+432            max_val=cfg.evolve_max,
+433        )
+434
+435    # Scale timing with grid size
+436    warmup_steps = cfg.get_warmup_steps(grid_size)
+437    measurement_steps = cfg.get_measurement_steps(grid_size)
+438
+439    # Warmup phase
+440    for _ in range(warmup_steps):
+441        model.update()
+442
+443    # Measurement phase: start collecting our mertics
+444    prey_pops, pred_pops = [], []  # Prey populations and predator populations
+445    evolved_means, evolved_stds = [], []  # Evolution stats over time
+446    cluster_sizes_prey, cluster_sizes_pred = [], []  # Cluster sizes
+447    largest_fractions_prey, largest_fractions_pred = (
+448        [],
+449        [],
+450    )  # Largest cluster fractions = size of largest cluster / total population
+451    pcf_samples = {"prey_prey": [], "pred_pred": [], "prey_pred": []}
+452
+453    # Determine minimum count for analysis
+454    min_count = int(cfg.min_density_for_analysis * (grid_size**2))
+455
+456    for step in range(measurement_steps):
+457        model.update()
+458
+459        _, prey, pred = count_populations(model.grid)
+460        prey_pops.append(prey)
+461        pred_pops.append(pred)
+462
+463        # Track evolution
+464        if with_evolution:
+465            stats = get_evolved_stats(model, "prey_death")
+466            evolved_means.append(stats["mean"])
+467            evolved_stds.append(stats["std"])
+468
+469        # Cluster analysis (at end of measurement)
+470        if step == measurement_steps - 1:
+471            prey_survived = prey_pops[-1] > min_count
+472            pred_survived = pred_pops[-1] > (min_count // 4)
+473
+474            if prey_survived:
+475                prey_stats = get_cluster_stats_fast(model.grid, 1)
+476                cluster_sizes_prey = prey_stats["sizes"].tolist()
+477                largest_fractions_prey.append(prey_stats["largest_fraction"])
+478
+479            if pred_survived:
+480                pred_stats = get_cluster_stats_fast(model.grid, 2)
+481                cluster_sizes_pred = pred_stats["sizes"].tolist()
+482                largest_fractions_pred.append(pred_stats["largest_fraction"])
+483
+484            # PCF requires both
+485            if compute_pcf and prey_survived and pred_survived:
+486                max_dist = min(grid_size / 2, cfg.pcf_max_distance)
+487                pcf_data = compute_all_pcfs_fast(model.grid, max_dist, cfg.pcf_n_bins)
+488                pcf_samples["prey_prey"].append(pcf_data["prey_prey"])
+489                pcf_samples["pred_pred"].append(pcf_data["pred_pred"])
+490                pcf_samples["prey_pred"].append(pcf_data["prey_pred"])
+491
+492    # Compile results
+493    result = {
+494        # Parameters
+495        "prey_birth": prey_birth,
+496        "prey_death": prey_death,
+497        "predator_birth": predator_birth,
+498        "predator_death": predator_death,
+499        "grid_size": grid_size,
+500        "with_evolution": with_evolution,
+501        "seed": seed,
+502        # Population dynamics
+503        "prey_mean": float(np.mean(prey_pops)),
+504        "prey_std": float(np.std(prey_pops)),
+505        "pred_mean": float(np.mean(pred_pops)),
+506        "pred_std": float(np.std(pred_pops)),
+507        "prey_survived": prey_pops[-1] > min_count,
+508        "pred_survived": pred_pops[-1] > (min_count // 4),
+509        # Cluster statistics
+510        "prey_n_clusters": len(cluster_sizes_prey),
+511        "pred_n_clusters": len(cluster_sizes_pred),
+512        "prey_cluster_sizes": cluster_sizes_prey,
+513        "pred_cluster_sizes": cluster_sizes_pred,
+514        # Order parameters
+515        "prey_largest_fraction": (
+516            float(np.mean(largest_fractions_prey)) if largest_fractions_prey else np.nan
+517        ),
+518        "pred_largest_fraction": (
+519            float(np.mean(largest_fractions_pred)) if largest_fractions_pred else np.nan
+520        ),
+521    }
+522
+523    # Time series (if requested)
+524    if cfg.save_timeseries:
+525        subsample = cfg.timeseries_subsample
+526        result["prey_timeseries"] = prey_pops[
+527            ::subsample
+528        ]  # NOTE: Sample temporal data every 'subsample' steps
+529        result["pred_timeseries"] = pred_pops[::subsample]
+530
+531    # Evolution statistics
+532    if with_evolution and evolved_means:
+533        valid_means = [v for v in evolved_means if not np.isnan(v)]
+534        result["evolved_prey_death_mean"] = (
+535            float(np.mean(valid_means)) if valid_means else np.nan
+536        )
+537        result["evolved_prey_death_std"] = (
+538            float(np.mean([v for v in evolved_stds if not np.isnan(v)]))
+539            if evolved_stds
+540            else np.nan
+541        )
+542        result["evolved_prey_death_final"] = valid_means[-1] if valid_means else np.nan
+543        result["evolved_prey_death_min"] = (
+544            float(np.min(valid_means)) if valid_means else np.nan
+545        )
+546        result["evolved_prey_death_max"] = (
+547            float(np.max(valid_means)) if valid_means else np.nan
+548        )
+549        result["evolve_sd"] = cfg.evolve_sd
+550
+551        if cfg.save_timeseries:
+552            result["evolved_prey_death_timeseries"] = evolved_means[
+553                :: cfg.timeseries_subsample
+554            ]
+555
+556    # PCF statistics
+557    if pcf_samples["prey_prey"]:
+558        dist, pcf_rr, _ = average_pcfs(pcf_samples["prey_prey"])
+559        _, pcf_cc, _ = average_pcfs(pcf_samples["pred_pred"])
+560        _, pcf_cr, _ = average_pcfs(pcf_samples["prey_pred"])
+561
+562        result["pcf_distances"] = dist.tolist()
+563        result["pcf_prey_prey"] = pcf_rr.tolist()
+564        result["pcf_pred_pred"] = pcf_cc.tolist()
+565        result["pcf_prey_pred"] = pcf_cr.tolist()
+566
+567        # Short-range indices
+568        short_mask = dist < 3.0
+569        if np.any(short_mask):
+570            result["segregation_index"] = float(np.mean(pcf_cr[short_mask]))
+571            result["prey_clustering_index"] = float(np.mean(pcf_rr[short_mask]))
+572            result["pred_clustering_index"] = float(np.mean(pcf_cc[short_mask]))
+573
+574    return result
+
+ + +

Run a single Predator-Prey (PP) simulation and collect comprehensive metrics.

+ +

This function initializes a Cellular Automata model, executes a warmup phase +to reach steady state, and then performs a measurement phase to track +population dynamics, spatial clustering, and evolutionary changes.

+ +
Parameters
+ +
    +
  • prey_birth (float): +The probability or rate of prey reproduction.
  • +
  • prey_death (float): +The base probability or rate of prey mortality.
  • +
  • predator_birth (float): +The probability or rate of predator reproduction upon consuming prey.
  • +
  • predator_death (float): +The probability or rate of predator mortality.
  • +
  • grid_size (int): +The side length of the square simulation grid.
  • +
  • seed (int): +Random seed for ensuring reproducibility of the simulation run.
  • +
  • cfg (Config): +A configuration object containing simulation hyperparameters (densities, +sampling rates, timing, etc.).
  • +
  • with_evolution (bool, optional): +If True, enables the evolution of the 'prey_death' parameter within +the model (default is False).
  • +
  • compute_pcf (bool, optional): +Explicit toggle for Pair Correlation Function calculation. If None, +it is determined by cfg.pcf_sample_rate (default is None).
  • +
+ +
Returns
+ +
    +
  • result (dict): +A dictionary containing simulation results including: +
      +
    • Input parameters and survival flags.
    • +
    • Population mean and standard deviation for both species.
    • +
    • Cluster statistics (number of clusters, sizes, largest fractions).
    • +
    • Evolutionary statistics (mean, std, min, max, and final values).
    • +
    • PCF data and spatial indices (segregation and clustering).
    • +
    • Optional time series for populations and evolved parameters.
    • +
  • +
+ +
Notes
+ +

The function relies on several external utilities: count_populations, +get_evolved_stats, get_cluster_stats_fast, compute_all_pcfs_fast, +and average_pcfs.

+
+ + +
+
+ +
+ + def + run_phase1( cfg: models.config.Config, output_dir: pathlib.Path, logger: logging.Logger) -> List[Dict]: + + + +
+ +
582def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+583    """
+584    Execute Phase 1 of the simulation: a parameter sweep to identify critical points.
+585
+586    This function performs a 1D sweep across varying prey mortality rates while
+587    keeping other parameters fixed. It utilizes parallel execution via joblib
+588    and saves results incrementally to a JSONL file to ensure data integrity
+589    during long-running batches.
+590
+591    Parameters
+592    ----------
+593    cfg : Config
+594        Configuration object containing simulation hyperparameters, sweep
+595        ranges, and execution settings (n_jobs, grid_size, etc.).
+596    output_dir : Path
+597        Directory where result files (JSONL) and metadata (JSON) will be stored.
+598    logger : logging.Logger
+599        Logger instance for tracking simulation progress and recording
+600        operational metadata.
+601
+602    Returns
+603    -------
+604    all_results : list of dict
+605        A list of dictionaries containing the metrics collected from every
+606        individual simulation run in the sweep.
+607
+608    Notes
+609    -----
+610    The function performs the following steps:
+611    1. Pre-warms Numba kernels for performance.
+612    2. Generates a deterministic set of simulation jobs using unique seeds.
+613    3. Executes simulations in parallel using a generator for memory efficiency.
+614    4. Records metadata including a timestamp and a serialized snapshot of
+615       the configuration.
+616    """
+617    from joblib import Parallel, delayed
+618
+619    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+620
+621    prey_deaths = cfg.get_prey_deaths()
+622
+623    # Build job list
+624    jobs = []
+625    # Sweep through prey_death only (prey_birth is fixed)
+626    for pd in prey_deaths:
+627        for rep in range(cfg.n_replicates):
+628            params = {"pd": pd}
+629
+630            seed = generate_unique_seed(params, rep)
+631            jobs.append(
+632                (
+633                    cfg.prey_birth,
+634                    pd,
+635                    cfg.predator_birth,
+636                    cfg.predator_death,
+637                    cfg.grid_size,
+638                    seed,
+639                    cfg,
+640                    False,
+641                )
+642            )
+643
+644    logger.info(f"Phase 1: {len(jobs):,} simulations")
+645    logger.info(
+646        f"  Grid: {cfg.n_prey_death} prey_death values × {cfg.n_replicates} reps (prey_birth={cfg.prey_birth})"
+647    )
+648    # Run with incremental saving
+649    output_jsonl = output_dir / "phase1_results.jsonl"
+650    all_results = []
+651
+652    with open(output_jsonl, "w", encoding="utf-8") as f:
+653        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+654        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+655
+656        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 1"):
+657            f.write(json.dumps(result, default=str) + "\n")
+658            f.flush()
+659            all_results.append(result)
+660
+661    # Save metadata
+662    meta = {
+663        "phase": 1,
+664        "description": "Parameter sweep for critical point",
+665        "n_sims": len(all_results),
+666        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+667        "config": asdict(cfg),
+668    }
+669    with open(output_dir / "phase1_metadata.json", "w") as f:
+670        json.dump(meta, f, indent=2, default=str)
+671
+672    logger.info(f"Phase 1 complete. Results: {output_jsonl}")
+673    return all_results
+
+ + +

Execute Phase 1 of the simulation: a parameter sweep to identify critical points.

+ +

This function performs a 1D sweep across varying prey mortality rates while +keeping other parameters fixed. It utilizes parallel execution via joblib +and saves results incrementally to a JSONL file to ensure data integrity +during long-running batches.

+ +
Parameters
+ +
    +
  • cfg (Config): +Configuration object containing simulation hyperparameters, sweep +ranges, and execution settings (n_jobs, grid_size, etc.).
  • +
  • output_dir (Path): +Directory where result files (JSONL) and metadata (JSON) will be stored.
  • +
  • logger (logging.Logger): +Logger instance for tracking simulation progress and recording +operational metadata.
  • +
+ +
Returns
+ +
    +
  • all_results (list of dict): +A list of dictionaries containing the metrics collected from every +individual simulation run in the sweep.
  • +
+ +
Notes
+ +

The function performs the following steps:

+ +
    +
  1. Pre-warms Numba kernels for performance.
  2. +
  3. Generates a deterministic set of simulation jobs using unique seeds.
  4. +
  5. Executes simulations in parallel using a generator for memory efficiency.
  6. +
  7. Records metadata including a timestamp and a serialized snapshot of +the configuration.
  8. +
+
+ + +
+
+ +
+ + def + run_phase2( cfg: models.config.Config, output_dir: pathlib.Path, logger: logging.Logger) -> List[Dict]: + + + +
+ +
676def run_phase2(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+677    """
+678    Execute Phase 2 of the simulation: self-organization and criticality analysis.
+679
+680    This phase tests the Self-Organized Criticality (SOC) hypothesis by
+681    initializing simulations at different points in the parameter space and
+682    observing whether evolutionary pressure drives the system toward a
+683    common critical point, regardless of initial prey mortality rates.
+684
+685    Parameters
+686    ----------
+687    cfg : Config
+688        Configuration object containing simulation hyperparameters, evolution
+689        settings, and execution constraints.
+690    output_dir : Path
+691        Directory where result files (JSONL) and metadata (JSON) will be stored.
+692    logger : logging.Logger
+693        Logger instance for tracking progress and evolutionary convergence.
+694
+695    Returns
+696    -------
+697    all_results : list of dict
+698        A list of dictionaries containing metrics from the evolutionary
+699        simulation runs.
+700
+701    Notes
+702    -----
+703    The function captures:
+704    1. Convergence of 'prey_death' across multiple replicates.
+705    2. Final steady-state population distributions.
+706    3. Incremental saving of results to prevent data loss.
+707    """
+708    from joblib import Parallel, delayed
+709
+710    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+711
+712    # Test at multiple prey_birth values
+713    pb = 0.2
+714    # Vary intial prey_death
+715    initial_prey_deaths = np.linspace(
+716        cfg.prey_death_range[0], cfg.prey_death_range[1], cfg.n_prey_death
+717    )
+718
+719    jobs = []
+720    for initial_pd in initial_prey_deaths:
+721        for rep in range(cfg.n_replicates):
+722            params = {"pb": pb, "initial_pd": initial_pd, "phase": 2}
+723            seed = generate_unique_seed(params, rep)
+724            jobs.append(
+725                (
+726                    pb,
+727                    initial_pd,
+728                    cfg.predator_birth,
+729                    cfg.predator_death,
+730                    cfg.grid_size,
+731                    seed,
+732                    cfg,
+733                    True,
+734                )
+735            )
+736
+737    logger.info(f"Phase 2: {len(jobs):,} simulations")
+738    logger.info(f"  prey_birth value: {pb}")
+739    logger.info(f"  initial prey_death values: {len(initial_prey_deaths)}")
+740    logger.info(f"  Replicates: {cfg.n_replicates}")
+741
+742    output_jsonl = output_dir / "phase2_results.jsonl"
+743    all_results = []
+744
+745    with open(output_jsonl, "w", encoding="utf-8") as f:
+746        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+747        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+748
+749        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 2"):
+750            f.write(json.dumps(result, default=str) + "\n")
+751            f.flush()
+752            all_results.append(result)
+753
+754    meta = {
+755        "phase": 2,
+756        "description": "Self-organization toward criticality",
+757        "n_sims": len(all_results),
+758        "initial_prey_deaths": initial_prey_deaths.tolist(),
+759        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+760    }
+761    with open(output_dir / "phase2_metadata.json", "w") as f:
+762        json.dump(meta, f, indent=2, default=str)
+763
+764    logger.info(f"Phase 2 complete. Results: {output_jsonl}")
+765    return all_results
+
+ + +

Execute Phase 2 of the simulation: self-organization and criticality analysis.

+ +

This phase tests the Self-Organized Criticality (SOC) hypothesis by +initializing simulations at different points in the parameter space and +observing whether evolutionary pressure drives the system toward a +common critical point, regardless of initial prey mortality rates.

+ +
Parameters
+ +
    +
  • cfg (Config): +Configuration object containing simulation hyperparameters, evolution +settings, and execution constraints.
  • +
  • output_dir (Path): +Directory where result files (JSONL) and metadata (JSON) will be stored.
  • +
  • logger (logging.Logger): +Logger instance for tracking progress and evolutionary convergence.
  • +
+ +
Returns
+ +
    +
  • all_results (list of dict): +A list of dictionaries containing metrics from the evolutionary +simulation runs.
  • +
+ +
Notes
+ +

The function captures:

+ +
    +
  1. Convergence of 'prey_death' across multiple replicates.
  2. +
  3. Final steady-state population distributions.
  4. +
  5. Incremental saving of results to prevent data loss.
  6. +
+
+ + +
+
+ +
+ + def + run_phase3( cfg: models.config.Config, output_dir: pathlib.Path, logger: logging.Logger) -> List[Dict]: + + + +
+ +
768def run_phase3(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+769    """
+770    Phase 3: Finite-size scaling at critical point.
+771
+772    - Multiple grid sizes at (critical_prey_birth, critical_prey_death)
+773    - Analyze cluster size cutoffs vs L
+774    """
+775    from joblib import Parallel, delayed
+776
+777    # NOTE: Tuned to critical points from phase 1
+778    pb = cfg.critical_prey_birth
+779    pd = cfg.critical_prey_death
+780
+781    logger.info(f"Phase 3: FSS at critical point (pb={pb}, pd={pd})")
+782
+783    for L in cfg.grid_sizes:
+784        warmup_numba_kernels(L, directed_hunting=cfg.directed_hunting)
+785
+786    jobs = []
+787    for L in cfg.grid_sizes:  # Sweep through grid sizes
+788        for rep in range(cfg.n_replicates):
+789            params = {"L": L, "phase": 3}
+790            seed = generate_unique_seed(params, rep)
+791            jobs.append(
+792                (pb, pd, cfg.predator_birth, cfg.predator_death, L, seed, cfg, False)
+793            )
+794
+795    logger.info(f"  Grid sizes: {cfg.grid_sizes}")
+796    logger.info(f"  Total simulations: {len(jobs):,}")
+797
+798    output_jsonl = output_dir / "phase3_results.jsonl"
+799    all_results = []
+800
+801    with open(output_jsonl, "w", encoding="utf-8") as f:
+802        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+803        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+804
+805        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 3"):
+806            f.write(json.dumps(result, default=str) + "\n")
+807            f.flush()
+808            all_results.append(result)
+809
+810    # Post-run metadata: postprocessing will fit cluster cutoffs vs L
+811    meta = {
+812        "phase": 3,
+813        "description": "Finite-size scaling",
+814        "critical_point": {"prey_birth": pb, "prey_death": pd},
+815        "grid_sizes": cfg.grid_sizes,
+816        "n_sims": len(all_results),
+817        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+818    }
+819    with open(output_dir / "phase3_metadata.json", "w") as f:
+820        json.dump(meta, f, indent=2, default=str)
+821
+822    logger.info(f"Phase 3 complete. Results: {output_jsonl}")
+823    return all_results
+
+ + +

Phase 3: Finite-size scaling at critical point.

+ +
    +
  • Multiple grid sizes at (critical_prey_birth, critical_prey_death)
  • +
  • Analyze cluster size cutoffs vs L
  • +
+
+ + +
+
+ +
+ + def + run_phase4( cfg: models.config.Config, output_dir: pathlib.Path, logger: logging.Logger) -> List[Dict]: + + + +
+ +
826def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+827    """
+828    Execute Phase 3 of the simulation: Finite-Size Scaling (FSS) analysis.
+829
+830    This phase investigates how spatial structures, specifically cluster size
+831    cutoffs, scale with the system size (L) at the critical point identified
+832    in Phase 1. This is essential for determining the universality class of
+833    the phase transition.
+834
+835    Parameters
+836    ----------
+837    cfg : Config
+838        Configuration object containing critical point parameters, the list of
+839        grid sizes to test, and execution settings.
+840    output_dir : Path
+841        Directory where result files (JSONL) and FSS metadata (JSON) will be
+842        stored.
+843    logger : logging.Logger
+844        Logger instance for tracking progress across different grid sizes.
+845
+846    Returns
+847    -------
+848    all_results : list of dict
+849        A list of dictionaries containing metrics and cluster statistics for
+850        each grid size and replicate.
+851
+852    Notes
+853    -----
+854    The function performs the following:
+855    1. Iterates through multiple grid sizes defined in `cfg.grid_sizes`.
+856    2. Generates parallel jobs for each size using critical birth/death rates.
+857    3. Saves results incrementally to allow for post-simulation analysis of
+858       power-law exponents.
+859    """
+860    from joblib import Parallel, delayed
+861    import itertools
+862
+863    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+864
+865    # Define sweep values
+866    prey_death_values = np.linspace(0.05, 0.95, 10)  # 10 values for prey_death
+867    other_param_values = np.linspace(0.0, 1.0, 11)  # 11 values for the rest
+868
+869    # Logging
+870    logger.info(f"Phase 4: Full 4D Parameter Sweep")
+871    logger.info(f"  prey_death: 10 values from 0.05 to 0.95")
+872    logger.info(f"  prey_birth, pred_birth, pred_death: 11 values each from 0 to 1")
+873    logger.info(f"  Grid Size: {cfg.grid_size}")
+874    logger.info(f"  Replicates: {cfg.n_replicates}")
+875
+876    # Build parameter grid
+877    param_grid = itertools.product(
+878        other_param_values,  # prey_birth (11 values)
+879        prey_death_values,  # prey_death (10 values)
+880        other_param_values,  # predator_birth (11 values)
+881        other_param_values,  # predator_death (11 values)
+882    )
+883
+884    jobs = []
+885
+886    for pb, pd, pred_b, pred_d in param_grid:
+887        for rep in range(cfg.n_replicates):
+888            params_id = {
+889                "pb": pb,
+890                "pd": pd,
+891                "pred_b": pred_b,
+892                "pred_d": pred_d,
+893                "rep": rep,
+894            }
+895            seed = generate_unique_seed(params_id, rep)
+896
+897            jobs.append(
+898                (
+899                    pb,  # prey_birth
+900                    pd,  # prey_death
+901                    pred_b,  # predator_birth
+902                    pred_d,  # predator_death
+903                    cfg.grid_size,
+904                    seed,
+905                    cfg,
+906                    False,
+907                )
+908            )
+909
+910    logger.info(
+911        f"  Total simulations: {len(jobs):,}"
+912    )  # 11 * 10 * 11 * 11 * n_reps = 13,310 * n_reps
+913
+914    output_jsonl = output_dir / "phase4_results.jsonl"
+915    all_results = []
+916
+917    with open(output_jsonl, "w", encoding="utf-8") as f:
+918        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+919        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+920
+921        for result in tqdm(executor(tasks), total=len(jobs), desc="Phase 4 (4D Sweep)"):
+922            f.write(json.dumps(result, default=str) + "\n")
+923            f.flush()
+924            all_results.append(result)
+925
+926    # Save Metadata
+927    meta = {
+928        "phase": 4,
+929        "description": "Global 4D Sensitivity Analysis",
+930        "prey_death_values": prey_death_values.tolist(),
+931        "other_param_values": other_param_values.tolist(),
+932        "parameters_varied": [
+933            "prey_birth",
+934            "prey_death",
+935            "predator_birth",
+936            "predator_death",
+937        ],
+938        "n_sims": len(all_results),
+939        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+940        "config": asdict(cfg),
+941    }
+942    with open(output_dir / "phase4_metadata.json", "w") as f:
+943        json.dump(meta, f, indent=2, default=str)
+944
+945    logger.info(f"Phase 4 complete. Results: {output_jsonl}")
+946    return all_results
+
+ + +

Execute Phase 3 of the simulation: Finite-Size Scaling (FSS) analysis.

+ +

This phase investigates how spatial structures, specifically cluster size +cutoffs, scale with the system size (L) at the critical point identified +in Phase 1. This is essential for determining the universality class of +the phase transition.

+ +
Parameters
+ +
    +
  • cfg (Config): +Configuration object containing critical point parameters, the list of +grid sizes to test, and execution settings.
  • +
  • output_dir (Path): +Directory where result files (JSONL) and FSS metadata (JSON) will be +stored.
  • +
  • logger (logging.Logger): +Logger instance for tracking progress across different grid sizes.
  • +
+ +
Returns
+ +
    +
  • all_results (list of dict): +A list of dictionaries containing metrics and cluster statistics for +each grid size and replicate.
  • +
+ +
Notes
+ +

The function performs the following:

+ +
    +
  1. Iterates through multiple grid sizes defined in cfg.grid_sizes.
  2. +
  3. Generates parallel jobs for each size using critical birth/death rates.
  4. +
  5. Saves results incrementally to allow for post-simulation analysis of +power-law exponents.
  6. +
+
+ + +
+
+ +
+ + def + run_phase5( cfg: models.config.Config, output_dir: pathlib.Path, logger: logging.Logger) -> List[Dict]: + + + +
+ +
 949def run_phase5(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Dict]:
+ 950    """
+ 951    Execute Phase 5 of the simulation: Global 4D parameter sweep with directed hunting.
+ 952
+ 953    This phase performs a comprehensive sensitivity analysis by varying four key
+ 954    parameters (prey birth/death and predator birth/death) while directed
+ 955    hunting is enabled. The results allow for a direct comparison with Phase 4
+ 956    to determine how predator search behavior shifts the system's critical
+ 957    thresholds and stability.
+ 958
+ 959    Parameters
+ 960    ----------
+ 961    cfg : Config
+ 962        Configuration object containing simulation hyperparameters, parallel
+ 963        execution settings, and the fixed grid size for this phase.
+ 964    output_dir : Path
+ 965        Directory where the result JSONL file and execution metadata will
+ 966        be stored.
+ 967    logger : logging.Logger
+ 968        Logger instance for tracking the progress of the high-volume
+ 969        simulation batch.
+ 970
+ 971    Returns
+ 972    -------
+ 973    all_results : list of dict
+ 974        A list of dictionaries containing metrics for every simulation in
+ 975        the 4D parameter grid.
+ 976
+ 977    Notes
+ 978    -----
+ 979    The function utilizes a Cartesian product of parameter ranges to build a
+ 980    job list of over 13,000 unique parameter sets (multiplied by replicates).
+ 981    Seeds are uniquely generated to distinguish these runs from other phases
+ 982    even if parameter values overlap.
+ 983    """
+ 984    from joblib import Parallel, delayed
+ 985    import itertools
+ 986
+ 987    warmup_numba_kernels(cfg.grid_size, directed_hunting=cfg.directed_hunting)
+ 988
+ 989    # Define sweep values (same as Phase 4)
+ 990    prey_death_values = np.linspace(0.05, 0.95, 10)  # 10 values for prey_death
+ 991    other_param_values = np.linspace(0.0, 1.0, 11)  # 11 values for the rest
+ 992
+ 993    # Logging
+ 994    logger.info(f"Phase 5: Full 4D Parameter Sweep (Directed Hunting)")
+ 995    logger.info(f"  prey_death: 10 values from 0.05 to 0.95")
+ 996    logger.info(f"  prey_birth, pred_birth, pred_death: 11 values each from 0 to 1")
+ 997    logger.info(f"  Grid Size: {cfg.grid_size}")
+ 998    logger.info(f"  Replicates: {cfg.n_replicates}")
+ 999    logger.info(f"  Directed Hunting: {cfg.directed_hunting}")
+1000
+1001    # Build parameter grid
+1002    param_grid = itertools.product(
+1003        other_param_values,  # prey_birth (11 values)
+1004        prey_death_values,  # prey_death (10 values)
+1005        other_param_values,  # predator_birth (11 values)
+1006        other_param_values,  # predator_death (11 values)
+1007    )
+1008
+1009    jobs = []
+1010
+1011    for pb, pd, pred_b, pred_d in param_grid:
+1012        for rep in range(cfg.n_replicates):
+1013            # Include phase identifier to ensure different seeds from Phase 4
+1014            params_id = {
+1015                "pb": pb,
+1016                "pd": pd,
+1017                "pred_b": pred_b,
+1018                "pred_d": pred_d,
+1019                "phase": 6,
+1020                "rep": rep,
+1021            }
+1022            seed = generate_unique_seed(params_id, rep)
+1023
+1024            jobs.append(
+1025                (
+1026                    pb,  # prey_birth
+1027                    pd,  # prey_death
+1028                    pred_b,  # predator_birth
+1029                    pred_d,  # predator_death
+1030                    cfg.grid_size,
+1031                    seed,
+1032                    cfg,
+1033                    False,
+1034                )
+1035            )
+1036
+1037    logger.info(
+1038        f"  Total simulations: {len(jobs):,}"
+1039    )  # 11 * 10 * 11 * 11 * n_reps = 13,310 * n_reps
+1040
+1041    output_jsonl = output_dir / "phase5_results.jsonl"
+1042    all_results = []
+1043
+1044    with open(output_jsonl, "w", encoding="utf-8") as f:
+1045        executor = Parallel(n_jobs=cfg.n_jobs, return_as="generator")
+1046        tasks = (delayed(run_single_simulation)(*job) for job in jobs)
+1047
+1048        for result in tqdm(
+1049            executor(tasks), total=len(jobs), desc="Phase 6 (4D Sweep + Directed)"
+1050        ):
+1051            f.write(json.dumps(result, default=str) + "\n")
+1052            f.flush()
+1053            all_results.append(result)
+1054
+1055    # Save Metadata
+1056    meta = {
+1057        "phase": 5,
+1058        "description": "Global 4D Sensitivity Analysis with Directed Hunting",
+1059        "prey_death_values": prey_death_values.tolist(),
+1060        "other_param_values": other_param_values.tolist(),
+1061        "parameters_varied": [
+1062            "prey_birth",
+1063            "prey_death",
+1064            "predator_birth",
+1065            "predator_death",
+1066        ],
+1067        "directed_hunting": cfg.directed_hunting,
+1068        "n_sims": len(all_results),
+1069        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
+1070        "config": asdict(cfg),
+1071    }
+1072    with open(output_dir / "phase6_metadata.json", "w") as f:
+1073        json.dump(meta, f, indent=2, default=str)
+1074
+1075    logger.info(f"Phase 5 complete. Results: {output_jsonl}")
+1076    return all_results
+
+ + +

Execute Phase 5 of the simulation: Global 4D parameter sweep with directed hunting.

+ +

This phase performs a comprehensive sensitivity analysis by varying four key +parameters (prey birth/death and predator birth/death) while directed +hunting is enabled. The results allow for a direct comparison with Phase 4 +to determine how predator search behavior shifts the system's critical +thresholds and stability.

+ +
Parameters
+ +
    +
  • cfg (Config): +Configuration object containing simulation hyperparameters, parallel +execution settings, and the fixed grid size for this phase.
  • +
  • output_dir (Path): +Directory where the result JSONL file and execution metadata will +be stored.
  • +
  • logger (logging.Logger): +Logger instance for tracking the progress of the high-volume +simulation batch.
  • +
+ +
Returns
+ +
    +
  • all_results (list of dict): +A list of dictionaries containing metrics for every simulation in +the 4D parameter grid.
  • +
+ +
Notes
+ +

The function utilizes a Cartesian product of parameter ranges to build a +job list of over 13,000 unique parameter sets (multiplied by replicates). +Seeds are uniquely generated to distinguish these runs from other phases +even if parameter values overlap.

+
+ + +
+
+
+ PHASE_RUNNERS = + + {1: <function run_phase1>, 2: <function run_phase2>, 3: <function run_phase3>, 4: <function run_phase4>, 5: <function run_phase5>} + + +
+ + + + +
+
+ +
+ + def + main(): + + + +
+ +
1092def main():
+1093    """
+1094    Organize the predator-prey experimental suite across multiple phases.
+1095
+1096    This entry point handles command-line arguments, sets up logging and output
+1097    directories, and executes the requested simulation phases (1-5). It
+1098    supports parallel execution, dry runs for runtime estimation, and
+1099    automated configuration persistence.
+1100
+1101    Notes
+1102    -----
+1103    The script dynamically retrieves phase-specific configurations using
+1104    `get_phase_config` and dispatches execution to the corresponding runner
+1105    in the `PHASE_RUNNERS` mapping.
+1106    """
+1107    parser = argparse.ArgumentParser(
+1108        description="Predator-Prey Hydra Effect Experiments",
+1109        formatter_class=argparse.RawDescriptionHelpFormatter,
+1110        epilog="""
+1111Phases:
+1112  1  Parameter sweep to find critical point
+1113  2  Self-organization (evolution toward criticality)
+1114  3  Finite-size scaling at critical point
+1115  4  Sensitivity analysis across parameter regimes
+1116  5  Model extensions (directed hunting comparison)
+1117        """,
+1118    )
+1119    parser.add_argument(
+1120        "--phase", type=str, required=True, help="Phase to run: 1-6 or 'all'"
+1121    )
+1122    parser.add_argument(
+1123        "--output",
+1124        type=Path,
+1125        default=Path("results"),
+1126        help="Output directory (default: results)",
+1127    )
+1128    parser.add_argument(
+1129        "--cores", type=int, default=-1, help="Number of cores (-1 for all)"
+1130    )
+1131    parser.add_argument(
+1132        "--dry-run", action="store_true", help="Estimate runtime without running"
+1133    )
+1134    args = parser.parse_args()
+1135
+1136    # Parse phase argument
+1137    if args.phase.lower() == "all":
+1138        phases = list(PHASE_RUNNERS.keys())
+1139    else:
+1140        try:
+1141            phases = [int(args.phase)]
+1142        except ValueError:
+1143            print(f"Invalid phase: {args.phase}. Use 1-6 or 'all'")
+1144            sys.exit(1)
+1145
+1146    # Setup output directory
+1147    args.output.mkdir(parents=True, exist_ok=True)
+1148
+1149    # Setup logging
+1150    logging.basicConfig(
+1151        level=logging.INFO,
+1152        format="%(asctime)s [%(levelname)s] %(message)s",
+1153        handlers=[
+1154            logging.FileHandler(args.output / "experiments.log"),
+1155            logging.StreamHandler(),
+1156        ],
+1157    )
+1158    logger = logging.getLogger(__name__)
+1159
+1160    # Header
+1161    logger.info("=" * 60)
+1162    logger.info("PREDATOR-PREY HYDRA EFFECT EXPERIMENTS")
+1163    logger.info("=" * 60)
+1164    logger.info(f"Phases: {phases}")
+1165    logger.info(f"Output: {args.output}")
+1166    logger.info(f"Cores: {args.cores}")
+1167    logger.info(f"Numba: {'ENABLED' if USE_NUMBA else 'DISABLED'}")
+1168
+1169    # Process each phase
+1170    for phase in phases:
+1171        cfg = get_phase_config(phase)
+1172        cfg.n_jobs = (
+1173            args.cores
+1174            if args.cores > 0
+1175            else int(os.environ.get("SLURM_CPUS_PER_TASK", -1))
+1176        )
+1177
+1178        logger.info("")
+1179        logger.info(f"{'='*60}")
+1180        logger.info(f"PHASE {phase}")
+1181        logger.info(f"{'='*60}")
+1182
+1183        n_cores = cfg.n_jobs if cfg.n_jobs > 0 else os.cpu_count()
+1184        logger.info(f"Estimated: {cfg.estimate_runtime(n_cores)}")
+1185
+1186        if args.dry_run:
+1187            logger.info("Dry run - skipping execution")
+1188            continue
+1189
+1190        # Save config
+1191        with open(args.output / f"phase{phase}_config.json", "w") as f:
+1192            json.dump(asdict(cfg), f, indent=2, default=str)
+1193
+1194        # Run phase
+1195        start_time = time.time()
+1196        runner = PHASE_RUNNERS[phase]
+1197        runner(cfg, args.output, logger)
+1198        elapsed = time.time() - start_time
+1199
+1200        logger.info(f"Phase {phase} runtime: {elapsed/60:.1f} minutes")
+1201
+1202    logger.info("")
+1203    logger.info("=" * 60)
+1204    logger.info("EXPERIMENTS COMPLETE")
+1205    logger.info("=" * 60)
+
+ + +

Organize the predator-prey experimental suite across multiple phases.

+ +

This entry point handles command-line arguments, sets up logging and output +directories, and executes the requested simulation phases (1-5). It +supports parallel execution, dry runs for runtime estimation, and +automated configuration persistence.

+ +
Notes
+ +

The script dynamically retrieves phase-specific configurations using +get_phase_config and dispatches execution to the corresponding runner +in the PHASE_RUNNERS mapping.

+
+ + +
+
+ +
+ + def + warmup_numba_kernels(size, **kwargs): + + + +
+ +
59    def warmup_numba_kernels(size, **kwargs):
+60        pass
+
+ + + + +
+
+ +
+ + def + set_numba_seed(seed): + + + +
+ +
62    def set_numba_seed(seed):
+63        pass
+
+ + + + +
+
+ + \ No newline at end of file diff --git a/docs/search.js b/docs/search.js new file mode 100644 index 0000000..7ccc858 --- /dev/null +++ b/docs/search.js @@ -0,0 +1,46 @@ +window.pdocSearch = (function(){ +/** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oModels Package\n\n

This package contains the core simulation logic for the Predator-Prey \nCellular Automata.

\n\n

Key Components

\n\n
    \n
  • CA: The base Cellular Automata class.
  • \n
  • experiment: Tools for running batches and collecting data.
  • \n
  • numba_optimized: High-performance kernels for HPC execution.
  • \n
\n\n

Example

\n\n

```python\nfrom models.CA import PredatorPreyCA\nsim = PredatorPreyCA(rows=100, cols=100)

\n"}, "models.CA": {"fullname": "models.CA", "modulename": "models.CA", "kind": "module", "doc": "

Cellular automaton base class.

\n"}, "models.CA.logger": {"fullname": "models.CA.logger", "modulename": "models.CA", "qualname": "logger", "kind": "variable", "doc": "

\n", "default_value": "<Logger models.CA (WARNING)>"}, "models.CA.CA": {"fullname": "models.CA.CA", "modulename": "models.CA", "qualname": "CA", "kind": "class", "doc": "

Base cellular automaton class for spatial simulations.

\n\n

This class provides a framework for multi-species cellular automata with\nsupport for global parameters, per-cell evolving parameters, and\ngrid initialization based on density.

\n\n
Attributes
\n\n
    \n
  • grid (np.ndarray):\n2D numpy array containing integers in range [0, n_species].
  • \n
  • params (Dict[str, Any]):\nGlobal parameters shared by all cells.
  • \n
  • cell_params (Dict[str, Any]):\nLocal per-cell parameters, typically stored as numpy arrays matching the grid shape.
  • \n
  • neighborhood (str):\nThe adjacency rule used ('neumann' or 'moore').
  • \n
  • generator (np.random.Generator):\nThe random number generator instance for reproducibility.
  • \n
  • species_names (Tuple[str, ...]):\nHuman-readable names for each species state.
  • \n
\n"}, "models.CA.CA.__init__": {"fullname": "models.CA.CA.__init__", "modulename": "models.CA", "qualname": "CA.__init__", "kind": "function", "doc": "

Initialize the cellular automaton grid and configurations.

\n\n
Parameters
\n\n
    \n
  • rows (int):\nNumber of rows in the grid (must be > 0).
  • \n
  • cols (int):\nNumber of columns in the grid (must be > 0).
  • \n
  • densities (Tuple[float, ...]):\nInitial density for each species. Length defines n_species.\nValues must sum to <= 1.0.
  • \n
  • neighborhood ({'neumann', 'moore'}):\nType of neighborhood connectivity.
  • \n
  • params (Dict[str, Any]):\nInitial global parameter values.
  • \n
  • cell_params (Dict[str, Any]):\nInitial local per-cell parameters.
  • \n
  • seed (int, optional):\nSeed for the random number generator.
  • \n
\n", "signature": "(\trows: int,\tcols: int,\tdensities: Tuple[float, ...],\tneighborhood: str,\tparams: Dict[str, object],\tcell_params: Dict[str, object],\tseed: Optional[int] = None)"}, "models.CA.CA.rows": {"fullname": "models.CA.CA.rows", "modulename": "models.CA", "qualname": "CA.rows", "kind": "variable", "doc": "

int: Number of rows in the grid.

\n", "annotation": ": int"}, "models.CA.CA.cols": {"fullname": "models.CA.CA.cols", "modulename": "models.CA", "qualname": "CA.cols", "kind": "variable", "doc": "

int: Number of columns in the grid.

\n", "annotation": ": int"}, "models.CA.CA.densities": {"fullname": "models.CA.CA.densities", "modulename": "models.CA", "qualname": "CA.densities", "kind": "variable", "doc": "

Tuple[float, ...]: Initial density fraction for each species.

\n", "annotation": ": Tuple[float, ...]"}, "models.CA.CA.n_species": {"fullname": "models.CA.CA.n_species", "modulename": "models.CA", "qualname": "CA.n_species", "kind": "variable", "doc": "

int: Number of distinct species states (excluding empty state 0).

\n", "annotation": ": int"}, "models.CA.CA.params": {"fullname": "models.CA.CA.params", "modulename": "models.CA", "qualname": "CA.params", "kind": "variable", "doc": "

\n", "annotation": ": Dict[str, object]"}, "models.CA.CA.cell_params": {"fullname": "models.CA.CA.cell_params", "modulename": "models.CA", "qualname": "CA.cell_params", "kind": "variable", "doc": "

\n", "annotation": ": Dict[str, object]"}, "models.CA.CA.species_names": {"fullname": "models.CA.CA.species_names", "modulename": "models.CA", "qualname": "CA.species_names", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[str, ...]"}, "models.CA.CA.neighborhood": {"fullname": "models.CA.CA.neighborhood", "modulename": "models.CA", "qualname": "CA.neighborhood", "kind": "variable", "doc": "

\n", "annotation": ": str"}, "models.CA.CA.generator": {"fullname": "models.CA.CA.generator", "modulename": "models.CA", "qualname": "CA.generator", "kind": "variable", "doc": "

\n", "annotation": ": numpy.random._generator.Generator"}, "models.CA.CA.grid": {"fullname": "models.CA.CA.grid", "modulename": "models.CA", "qualname": "CA.grid", "kind": "variable", "doc": "

\n", "annotation": ": numpy.ndarray"}, "models.CA.CA.validate": {"fullname": "models.CA.CA.validate", "modulename": "models.CA", "qualname": "CA.validate", "kind": "function", "doc": "

Validate core CA invariants and grid dimensions.

\n\n

Checks that the neighborhood is valid, the grid matches initialized dimensions,\nand that local parameter arrays match the grid shape.

\n\n
Raises
\n\n
    \n
  • ValueError: If any structural invariant is violated.
  • \n
\n", "signature": "(self) -> None:", "funcdef": "def"}, "models.CA.CA.evolve": {"fullname": "models.CA.CA.evolve", "modulename": "models.CA", "qualname": "CA.evolve", "kind": "function", "doc": "

Enable per-cell evolution for a specific parameter on a given species.

\n\n

This method initializes a spatial parameter array (local parameter map)\nfor a global parameter. It allows individual cells to carry their own\nvalues for that parameter, which can then mutate and evolve during\nthe simulation.

\n\n
Parameters
\n\n
    \n
  • param (str):\nThe name of the global parameter to enable for evolution.\nMust exist in self.params.
  • \n
  • species (int, optional):\nThe 1-based index of the species to which this parameter applies.\nIf None, the method attempts to infer the species from the\nparameter name prefix.
  • \n
  • sd (float, default 0.05):\nThe standard deviation of the Gaussian mutation applied during\ninheritance/reproduction.
  • \n
  • min_val (float, optional):\nThe minimum allowable value for the parameter (clamping).\nDefaults to 0.01 if not provided.
  • \n
  • max_val (float, optional):\nThe maximum allowable value for the parameter (clamping).\nDefaults to 0.99 if not provided.
  • \n
\n\n
Raises
\n\n
    \n
  • ValueError: If the parameter is not in self.params, the species cannot be\ninferred, or the species index is out of bounds.
  • \n
\n\n
Notes
\n\n

The local parameter is stored in self.cell_params as a 2D numpy\narray initialized with the current global value for all cells of\nthe target species, and NaN elsewhere.

\n", "signature": "(\tself,\tparam: str,\tspecies: Optional[int] = None,\tsd: float = 0.05,\tmin_val: Optional[float] = None,\tmax_val: Optional[float] = None) -> None:", "funcdef": "def"}, "models.CA.CA.update": {"fullname": "models.CA.CA.update", "modulename": "models.CA", "qualname": "CA.update", "kind": "function", "doc": "

Perform one update step of the cellular automaton.

\n\n

This is an abstract method that defines the transition rules of the\nsystem. It must be implemented by concrete subclasses to specify\nhow cell states and parameters change over time based on their\ncurrent state and neighborhood.

\n\n
Raises
\n\n
    \n
  • NotImplementedError: If called directly on the base class instead of an implementation.
  • \n
\n\n
Returns
\n\n
    \n
  • None
  • \n
\n\n
Notes
\n\n

In a typical implementation, this method handles the logic for\nstochastic transitions, movement, or predator-prey interactions.

\n", "signature": "(self) -> None:", "funcdef": "def"}, "models.CA.CA.run": {"fullname": "models.CA.CA.run", "modulename": "models.CA", "qualname": "CA.run", "kind": "function", "doc": "

Execute the cellular automaton simulation for a specified number of steps.

\n\n

This method drives the simulation loop, calling update() at each\niteration. It manages visualization updates, directory creation for\ndata persistence, and handles the freezing of evolving parameters\nat a specific time step.

\n\n
Parameters
\n\n
    \n
  • steps (int):\nThe total number of iterations to run (must be non-negative).
  • \n
  • stop_evolution_at (int, optional):\nThe 1-based iteration index after which parameter mutation is\ndisabled. Useful for observing system stability after a period\nof adaptation.
  • \n
  • snapshot_iters (List[int], optional):\nA list of specific 1-based iteration indices at which to save\nthe current grid state to the results directory.
  • \n
\n\n
Returns
\n\n
    \n
  • None
  • \n
\n\n
Notes
\n\n

If snapshots are requested, a results directory is automatically created\nusing a timestamped subfolder (e.g., 'results/run-1700000000/').\nVisualization errors are logged but do not terminate the simulation.

\n", "signature": "(\tself,\tsteps: int,\tstop_evolution_at: Optional[int] = None,\tsnapshot_iters: Optional[list] = None) -> None:", "funcdef": "def"}, "models.CA.PP": {"fullname": "models.CA.PP", "modulename": "models.CA", "qualname": "PP", "kind": "class", "doc": "

Predator-Prey Cellular Automaton model with Numba-accelerated kernels.

\n\n

This model simulates a stochastic predator-prey system where species\ninteract on a 2D grid. It supports evolving per-cell death rates,\nperiodic boundary conditions, and both random and directed hunting\nbehaviors.

\n\n
Parameters
\n\n
    \n
  • rows (int, default 10):\nNumber of rows in the simulation grid.
  • \n
  • cols (int, default 10):\nNumber of columns in the simulation grid.
  • \n
  • densities (Tuple[float, ...], default (0.2, 0.1)):\nInitial population densities for (prey, predator).
  • \n
  • neighborhood ({'moore', 'neumann'}, default 'moore'):\nThe neighborhood type for cell interactions.
  • \n
  • params (Dict[str, object], optional):\nGlobal parameters: \"prey_death\", \"predator_death\", \"prey_birth\",\n\"predator_birth\".
  • \n
  • cell_params (Dict[str, object], optional):\nInitial local parameter maps (2D arrays).
  • \n
  • seed (int, optional):\nRandom seed for reproducibility.
  • \n
  • synchronous (bool, default True):\nIf True, updates the entire grid at once. If False, updates\ncells asynchronously.
  • \n
  • directed_hunting (bool, default False):\nIf True, predators selectively hunt prey rather than choosing\nneighbors at random.
  • \n
\n\n
Attributes
\n\n
    \n
  • species_names (Tuple[str, ...]):\nLabels for the species ('prey', 'predator').
  • \n
  • synchronous (bool):\nCurrent update mode.
  • \n
  • directed_hunting (bool):\nCurrent hunting strategy logic.
  • \n
\n", "bases": "CA"}, "models.CA.PP.__init__": {"fullname": "models.CA.PP.__init__", "modulename": "models.CA", "qualname": "PP.__init__", "kind": "function", "doc": "

Initialize the Predator-Prey CA with validated parameters and kernels.

\n", "signature": "(\trows: int = 10,\tcols: int = 10,\tdensities: Tuple[float, ...] = (0.2, 0.1),\tneighborhood: str = 'moore',\tparams: Dict[str, object] = None,\tcell_params: Dict[str, object] = None,\tseed: Optional[int] = None,\tsynchronous: bool = True,\tdirected_hunting: bool = False)"}, "models.CA.PP.synchronous": {"fullname": "models.CA.PP.synchronous", "modulename": "models.CA", "qualname": "PP.synchronous", "kind": "variable", "doc": "

\n", "annotation": ": bool"}, "models.CA.PP.directed_hunting": {"fullname": "models.CA.PP.directed_hunting", "modulename": "models.CA", "qualname": "PP.directed_hunting", "kind": "variable", "doc": "

\n", "annotation": ": bool"}, "models.CA.PP.species_names": {"fullname": "models.CA.PP.species_names", "modulename": "models.CA", "qualname": "PP.species_names", "kind": "variable", "doc": "

\n"}, "models.CA.PP.validate": {"fullname": "models.CA.PP.validate", "modulename": "models.CA", "qualname": "PP.validate", "kind": "function", "doc": "

Validate Predator-Prey specific invariants and spatial parameter arrays.

\n\n

Extends the base CA validation to ensure that numerical parameters are\nwithin the [0, 1] probability range and that evolved parameter maps\n(e.g., prey_death) correctly align with the species locations.

\n\n
Raises
\n\n
    \n
  • ValueError: If grid shapes, parameter ranges, or species masks are inconsistent.
  • \n
  • TypeError: If parameters are non-numeric.
  • \n
\n", "signature": "(self) -> None:", "funcdef": "def"}, "models.CA.PP.update_async": {"fullname": "models.CA.PP.update_async", "modulename": "models.CA", "qualname": "PP.update_async", "kind": "function", "doc": "

Execute an asynchronous update using the optimized Numba kernel.

\n\n

This method retrieves the evolved parameter maps and delegates the\nstochastic transitions to the PPKernel. Asynchronous updates\ntypically handle cell-by-cell logic where changes can be\nimmediately visible to neighbors.

\n", "signature": "(self) -> None:", "funcdef": "def"}, "models.CA.PP.update": {"fullname": "models.CA.PP.update", "modulename": "models.CA", "qualname": "PP.update", "kind": "function", "doc": "

Dispatch the simulation step based on the configured update mode.

\n", "signature": "(self) -> None:", "funcdef": "def"}, "models.config": {"fullname": "models.config", "modulename": "models.config", "kind": "module", "doc": "

Configuration for Predator-Prey Hydra Effect Experiments

\n\n

Single Config dataclass with pre-defined instances for each experimental phase.

\n\n

Usage:\n from config import PHASE1_CONFIG, PHASE2_CONFIG, Config

\n\n
# Use pre-defined config\ncfg = PHASE1_CONFIG\n\n# Or create custom config\ncfg = Config(grid_size=150, n_replicates=20)\n\n# Or modify existing\ncfg = Config(**{**asdict(PHASE1_CONFIG), 'n_replicates': 30})\n
\n"}, "models.config.Config": {"fullname": "models.config.Config", "modulename": "models.config", "qualname": "Config", "kind": "class", "doc": "

Central configuration for Predator-Prey Hydra Effect experiments.

\n\n

This dataclass aggregates all hyperparameters, grid settings, and\nexperimental phase definitions. It includes helper methods for\nparameter sweep generation and runtime estimation.

\n\n
Attributes
\n\n
    \n
  • grid_size (int, default 1000):\nThe side length of the square simulation grid.
  • \n
  • densities (Tuple[float, float], default (0.30, 0.15)):\nInitial population fractions for (prey, predator).
  • \n
  • grid_sizes (Tuple[int, ...], default (50, 100, 250, 500, 1000, 2500)):\nGrid dimensions used for Finite-Size Scaling (FSS) analysis.
  • \n
  • prey_birth (float, default 0.2):\nDefault global birth rate for the prey species.
  • \n
  • prey_death (float, default 0.05):\nDefault global death rate for the prey species.
  • \n
  • predator_birth (float, default 0.8):\nDefault global birth rate for the predator species.
  • \n
  • predator_death (float, default 0.05):\nDefault global death rate for the predator species.
  • \n
  • critical_prey_birth (float, default 0.20):\nIdentified critical birth rate for prey (Phase 1 result).
  • \n
  • critical_prey_death (float, default 0.947):\nIdentified critical death rate for prey (Phase 1 result).
  • \n
  • prey_death_range (Tuple[float, float], default (0.0, 0.2)):\nBounds for the prey death rate sweep in Phase 1.
  • \n
  • n_prey_birth (int, default 15):\nNumber of points along the prey birth rate axis for sweeps.
  • \n
  • n_prey_death (int, default 5):\nNumber of points along the prey death rate axis for sweeps.
  • \n
  • predator_birth_values (Tuple[float, ...]):\nDiscrete predator birth rates used for sensitivity analysis.
  • \n
  • predator_death_values (Tuple[float, ...]):\nDiscrete predator death rates used for sensitivity analysis.
  • \n
  • prey_death_offsets (Tuple[float, ...]):\nDelta values applied to critical death rates for perturbation tests.
  • \n
  • n_replicates (int, default 15):\nNumber of independent stochastic runs per parameter set.
  • \n
  • warmup_steps (int, default 300):\nIterations to run before beginning data collection.
  • \n
  • measurement_steps (int, default 500):\nIterations spent collecting statistics after warmup.
  • \n
  • with_evolution (bool, default False):\nToggle for enabling per-cell parameter mutation.
  • \n
  • evolve_sd (float, default 0.10):\nStandard deviation for parameter mutation (Gaussian).
  • \n
  • evolve_min (float, default 0.0):\nLower bound clamp for evolving parameters.
  • \n
  • evolve_max (float, default 0.10):\nUpper bound clamp for evolving parameters.
  • \n
  • sensitivity_sd_values (Tuple[float, ...]):\nRange of mutation strengths tested in sensitivity phases.
  • \n
  • synchronous (bool, default False):\nIf True, use synchronous grid updates (not recommended).
  • \n
  • directed_hunting (bool, default False):\nToggle for targeted predator movement logic.
  • \n
  • directed_hunting_values (Tuple[bool, ...]):\nOptions compared during Phase 6 extensions.
  • \n
  • save_timeseries (bool, default False):\nToggle for recording step-by-step population data.
  • \n
  • timeseries_subsample (int, default 10):\nFrequency of temporal data points (e.g., every 10 steps).
  • \n
  • collect_pcf (bool, default True):\nToggle for Pair Correlation Function analysis.
  • \n
  • pcf_sample_rate (float, default 0.2):\nProbability that a specific replicate will compute PCFs.
  • \n
  • pcf_max_distance (float, default 20.0):\nMaximum radial distance for spatial correlation analysis.
  • \n
  • pcf_n_bins (int, default 20):\nNumber of bins in the PCF histogram.
  • \n
  • min_density_for_analysis (float, default 0.002):\nPopulation threshold below which spatial analysis is skipped.
  • \n
  • perturbation_magnitude (float, default 0.1):\nStrength of external shocks applied in Phase 5.
  • \n
  • n_jobs (int, default -1):\nNumber of CPU cores for parallelization (-1 uses all available).
  • \n
\n"}, "models.config.Config.__init__": {"fullname": "models.config.Config.__init__", "modulename": "models.config", "qualname": "Config.__init__", "kind": "function", "doc": "

\n", "signature": "(\tgrid_size: int = 1000,\tdensities: Tuple[float, float] = (0.3, 0.15),\tgrid_sizes: Tuple[int, ...] = (50, 100, 250, 500, 1000, 2500),\tprey_birth: float = 0.2,\tprey_death: float = 0.05,\tpredator_birth: float = 0.8,\tpredator_death: float = 0.05,\tcritical_prey_birth: float = 0.2,\tcritical_prey_death: float = 0.947,\tprey_death_range: Tuple[float, float] = (0.0, 0.2),\tn_prey_birth: int = 15,\tn_prey_death: int = 5,\tpredator_birth_values: Tuple[float, ...] = (0.15, 0.2, 0.25, 0.3),\tpredator_death_values: Tuple[float, ...] = (0.05, 0.1, 0.15, 0.2),\tprey_death_offsets: Tuple[float, ...] = (-0.02, -0.01, 0.0, 0.01, 0.02),\tn_replicates: int = 15,\twarmup_steps: int = 300,\tmeasurement_steps: int = 500,\twith_evolution: bool = False,\tevolve_sd: float = 0.1,\tevolve_min: float = 0.0,\tevolve_max: float = 0.1,\tsensitivity_sd_values: Tuple[float, ...] = (0.02, 0.05, 0.1, 0.15, 0.2),\tsynchronous: bool = False,\tdirected_hunting: bool = False,\tdirected_hunting_values: Tuple[bool, ...] = (False, True),\tsave_timeseries: bool = False,\ttimeseries_subsample: int = 10,\tcollect_pcf: bool = True,\tpcf_sample_rate: float = 0.2,\tpcf_max_distance: float = 20.0,\tpcf_n_bins: int = 20,\tmin_density_for_analysis: float = 0.002,\tperturbation_magnitude: float = 0.1,\tn_jobs: int = -1)"}, "models.config.Config.grid_size": {"fullname": "models.config.Config.grid_size", "modulename": "models.config", "qualname": "Config.grid_size", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "1000"}, "models.config.Config.densities": {"fullname": "models.config.Config.densities", "modulename": "models.config", "qualname": "Config.densities", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[float, float]", "default_value": "(0.3, 0.15)"}, "models.config.Config.grid_sizes": {"fullname": "models.config.Config.grid_sizes", "modulename": "models.config", "qualname": "Config.grid_sizes", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[int, ...]", "default_value": "(50, 100, 250, 500, 1000, 2500)"}, "models.config.Config.prey_birth": {"fullname": "models.config.Config.prey_birth", "modulename": "models.config", "qualname": "Config.prey_birth", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.2"}, "models.config.Config.prey_death": {"fullname": "models.config.Config.prey_death", "modulename": "models.config", "qualname": "Config.prey_death", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.05"}, "models.config.Config.predator_birth": {"fullname": "models.config.Config.predator_birth", "modulename": "models.config", "qualname": "Config.predator_birth", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.8"}, "models.config.Config.predator_death": {"fullname": "models.config.Config.predator_death", "modulename": "models.config", "qualname": "Config.predator_death", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.05"}, "models.config.Config.critical_prey_birth": {"fullname": "models.config.Config.critical_prey_birth", "modulename": "models.config", "qualname": "Config.critical_prey_birth", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.2"}, "models.config.Config.critical_prey_death": {"fullname": "models.config.Config.critical_prey_death", "modulename": "models.config", "qualname": "Config.critical_prey_death", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.947"}, "models.config.Config.prey_death_range": {"fullname": "models.config.Config.prey_death_range", "modulename": "models.config", "qualname": "Config.prey_death_range", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[float, float]", "default_value": "(0.0, 0.2)"}, "models.config.Config.n_prey_birth": {"fullname": "models.config.Config.n_prey_birth", "modulename": "models.config", "qualname": "Config.n_prey_birth", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "15"}, "models.config.Config.n_prey_death": {"fullname": "models.config.Config.n_prey_death", "modulename": "models.config", "qualname": "Config.n_prey_death", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "5"}, "models.config.Config.predator_birth_values": {"fullname": "models.config.Config.predator_birth_values", "modulename": "models.config", "qualname": "Config.predator_birth_values", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[float, ...]", "default_value": "(0.15, 0.2, 0.25, 0.3)"}, "models.config.Config.predator_death_values": {"fullname": "models.config.Config.predator_death_values", "modulename": "models.config", "qualname": "Config.predator_death_values", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[float, ...]", "default_value": "(0.05, 0.1, 0.15, 0.2)"}, "models.config.Config.prey_death_offsets": {"fullname": "models.config.Config.prey_death_offsets", "modulename": "models.config", "qualname": "Config.prey_death_offsets", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[float, ...]", "default_value": "(-0.02, -0.01, 0.0, 0.01, 0.02)"}, "models.config.Config.n_replicates": {"fullname": "models.config.Config.n_replicates", "modulename": "models.config", "qualname": "Config.n_replicates", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "15"}, "models.config.Config.warmup_steps": {"fullname": "models.config.Config.warmup_steps", "modulename": "models.config", "qualname": "Config.warmup_steps", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "300"}, "models.config.Config.measurement_steps": {"fullname": "models.config.Config.measurement_steps", "modulename": "models.config", "qualname": "Config.measurement_steps", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "500"}, "models.config.Config.with_evolution": {"fullname": "models.config.Config.with_evolution", "modulename": "models.config", "qualname": "Config.with_evolution", "kind": "variable", "doc": "

\n", "annotation": ": bool", "default_value": "False"}, "models.config.Config.evolve_sd": {"fullname": "models.config.Config.evolve_sd", "modulename": "models.config", "qualname": "Config.evolve_sd", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.1"}, "models.config.Config.evolve_min": {"fullname": "models.config.Config.evolve_min", "modulename": "models.config", "qualname": "Config.evolve_min", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.0"}, "models.config.Config.evolve_max": {"fullname": "models.config.Config.evolve_max", "modulename": "models.config", "qualname": "Config.evolve_max", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.1"}, "models.config.Config.sensitivity_sd_values": {"fullname": "models.config.Config.sensitivity_sd_values", "modulename": "models.config", "qualname": "Config.sensitivity_sd_values", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[float, ...]", "default_value": "(0.02, 0.05, 0.1, 0.15, 0.2)"}, "models.config.Config.synchronous": {"fullname": "models.config.Config.synchronous", "modulename": "models.config", "qualname": "Config.synchronous", "kind": "variable", "doc": "

\n", "annotation": ": bool", "default_value": "False"}, "models.config.Config.directed_hunting": {"fullname": "models.config.Config.directed_hunting", "modulename": "models.config", "qualname": "Config.directed_hunting", "kind": "variable", "doc": "

\n", "annotation": ": bool", "default_value": "False"}, "models.config.Config.directed_hunting_values": {"fullname": "models.config.Config.directed_hunting_values", "modulename": "models.config", "qualname": "Config.directed_hunting_values", "kind": "variable", "doc": "

\n", "annotation": ": Tuple[bool, ...]", "default_value": "(False, True)"}, "models.config.Config.save_timeseries": {"fullname": "models.config.Config.save_timeseries", "modulename": "models.config", "qualname": "Config.save_timeseries", "kind": "variable", "doc": "

\n", "annotation": ": bool", "default_value": "False"}, "models.config.Config.timeseries_subsample": {"fullname": "models.config.Config.timeseries_subsample", "modulename": "models.config", "qualname": "Config.timeseries_subsample", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "10"}, "models.config.Config.collect_pcf": {"fullname": "models.config.Config.collect_pcf", "modulename": "models.config", "qualname": "Config.collect_pcf", "kind": "variable", "doc": "

\n", "annotation": ": bool", "default_value": "True"}, "models.config.Config.pcf_sample_rate": {"fullname": "models.config.Config.pcf_sample_rate", "modulename": "models.config", "qualname": "Config.pcf_sample_rate", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.2"}, "models.config.Config.pcf_max_distance": {"fullname": "models.config.Config.pcf_max_distance", "modulename": "models.config", "qualname": "Config.pcf_max_distance", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "20.0"}, "models.config.Config.pcf_n_bins": {"fullname": "models.config.Config.pcf_n_bins", "modulename": "models.config", "qualname": "Config.pcf_n_bins", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "20"}, "models.config.Config.min_density_for_analysis": {"fullname": "models.config.Config.min_density_for_analysis", "modulename": "models.config", "qualname": "Config.min_density_for_analysis", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.002"}, "models.config.Config.perturbation_magnitude": {"fullname": "models.config.Config.perturbation_magnitude", "modulename": "models.config", "qualname": "Config.perturbation_magnitude", "kind": "variable", "doc": "

\n", "annotation": ": float", "default_value": "0.1"}, "models.config.Config.n_jobs": {"fullname": "models.config.Config.n_jobs", "modulename": "models.config", "qualname": "Config.n_jobs", "kind": "variable", "doc": "

\n", "annotation": ": int", "default_value": "-1"}, "models.config.Config.get_prey_births": {"fullname": "models.config.Config.get_prey_births", "modulename": "models.config", "qualname": "Config.get_prey_births", "kind": "function", "doc": "

Generate a linear range of prey birth rates for experimental sweeps.

\n\n
Returns
\n\n
    \n
  • np.ndarray: 1D array of birth rates based on prey_birth_range and n_prey_birth.
  • \n
\n", "signature": "(self) -> numpy.ndarray:", "funcdef": "def"}, "models.config.Config.get_prey_deaths": {"fullname": "models.config.Config.get_prey_deaths", "modulename": "models.config", "qualname": "Config.get_prey_deaths", "kind": "function", "doc": "

Generate a linear range of prey death rates for experimental sweeps.

\n\n
Returns
\n\n
    \n
  • np.ndarray: 1D array of death rates based on prey_death_range and n_prey_death.
  • \n
\n", "signature": "(self) -> numpy.ndarray:", "funcdef": "def"}, "models.config.Config.get_warmup_steps": {"fullname": "models.config.Config.get_warmup_steps", "modulename": "models.config", "qualname": "Config.get_warmup_steps", "kind": "function", "doc": "

Calculate the required warmup steps scaled by grid size.

\n\n
Parameters
\n\n
    \n
  • L (int):\nThe side length of the current grid.
  • \n
\n\n
Returns
\n\n
    \n
  • int: The number of steps to discard before measurement.
  • \n
\n", "signature": "(self, L: int) -> int:", "funcdef": "def"}, "models.config.Config.get_measurement_steps": {"fullname": "models.config.Config.get_measurement_steps", "modulename": "models.config", "qualname": "Config.get_measurement_steps", "kind": "function", "doc": "

Determine the number of measurement steps based on the grid side length.

\n\n

This method allows for dynamic scaling of data collection duration relative\nto the system size. Currently, it returns a fixed value, but it is\ndesigned to be overridden for studies where measurement time must\nscale with the grid size (e.g., $L^z$ scaling in critical dynamics).

\n\n
Parameters
\n\n
    \n
  • L (int):\nThe side length of the current simulation grid.
  • \n
\n\n
Returns
\n\n
    \n
  • int: The number of iterations to perform for statistical measurement.
  • \n
\n", "signature": "(self, L: int) -> int:", "funcdef": "def"}, "models.config.Config.estimate_runtime": {"fullname": "models.config.Config.estimate_runtime", "modulename": "models.config", "qualname": "Config.estimate_runtime", "kind": "function", "doc": "

Estimate the wall-clock time required to complete the experiment.

\n\n

Calculations account for grid size scaling, PCF overhead,\nreplicate counts, and available parallel resources.

\n\n
Parameters
\n\n
    \n
  • n_cores (int, default 32):\nThe number of CPU cores available for execution.
  • \n
\n\n
Returns
\n\n
    \n
  • str: A human-readable summary of simulation count and estimated hours.
  • \n
\n", "signature": "(self, n_cores: int = 32) -> str:", "funcdef": "def"}, "models.config.PHASE1_CONFIG": {"fullname": "models.config.PHASE1_CONFIG", "modulename": "models.config", "qualname": "PHASE1_CONFIG", "kind": "variable", "doc": "

\n", "default_value": "Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0963, 0.0973), n_prey_birth=15, n_prey_death=20, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=30, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)"}, "models.config.PHASE2_CONFIG": {"fullname": "models.config.PHASE2_CONFIG", "modulename": "models.config", "qualname": "PHASE2_CONFIG", "kind": "variable", "doc": "

\n", "default_value": "Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=1, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=1000, measurement_steps=10000, with_evolution=True, evolve_sd=0.01, evolve_min=0.0, evolve_max=0.2, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)"}, "models.config.PHASE3_CONFIG": {"fullname": "models.config.PHASE3_CONFIG", "modulename": "models.config", "qualname": "PHASE3_CONFIG", "kind": "variable", "doc": "

\n", "default_value": "Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=20, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=True, pcf_sample_rate=1.0, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)"}, "models.config.PHASE4_CONFIG": {"fullname": "models.config.PHASE4_CONFIG", "modulename": "models.config", "qualname": "PHASE4_CONFIG", "kind": "variable", "doc": "

\n", "default_value": "Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)"}, "models.config.PHASE5_CONFIG": {"fullname": "models.config.PHASE5_CONFIG", "modulename": "models.config", "qualname": "PHASE5_CONFIG", "kind": "variable", "doc": "

\n", "default_value": "Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=True, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)"}, "models.config.PHASE_CONFIGS": {"fullname": "models.config.PHASE_CONFIGS", "modulename": "models.config", "qualname": "PHASE_CONFIGS", "kind": "variable", "doc": "

\n", "default_value": "{1: Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0963, 0.0973), n_prey_birth=15, n_prey_death=20, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=30, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 2: Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=1, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=1000, measurement_steps=10000, with_evolution=True, evolve_sd=0.01, evolve_min=0.0, evolve_max=0.2, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 3: Config(grid_size=1000, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=20, warmup_steps=1000, measurement_steps=1000, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=True, pcf_sample_rate=1.0, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 4: Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=False, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1), 5: Config(grid_size=250, densities=(0.3, 0.15), grid_sizes=(50, 100, 250, 500, 1000, 2500), prey_birth=0.2, prey_death=0.05, predator_birth=0.8, predator_death=0.05, critical_prey_birth=0.2, critical_prey_death=0.947, prey_death_range=(0.0, 0.2), n_prey_birth=15, n_prey_death=5, predator_birth_values=(0.15, 0.2, 0.25, 0.3), predator_death_values=(0.05, 0.1, 0.15, 0.2), prey_death_offsets=(-0.02, -0.01, 0.0, 0.01, 0.02), n_replicates=10, warmup_steps=500, measurement_steps=500, with_evolution=False, evolve_sd=0.1, evolve_min=0.0, evolve_max=0.1, sensitivity_sd_values=(0.02, 0.05, 0.1, 0.15, 0.2), synchronous=False, directed_hunting=True, directed_hunting_values=(False, True), save_timeseries=False, timeseries_subsample=10, collect_pcf=False, pcf_sample_rate=0.2, pcf_max_distance=20.0, pcf_n_bins=20, min_density_for_analysis=0.002, perturbation_magnitude=0.1, n_jobs=-1)}"}, "models.config.get_phase_config": {"fullname": "models.config.get_phase_config", "modulename": "models.config", "qualname": "get_phase_config", "kind": "function", "doc": "

Retrieve the configuration object for a specific experimental phase.

\n\n
Parameters
\n\n
    \n
  • phase (int):\nThe phase number (1 through 6) to retrieve.
  • \n
\n\n
Returns
\n\n
    \n
  • Config: The configuration instance associated with the requested phase.
  • \n
\n\n
Raises
\n\n
    \n
  • ValueError: If the phase number is not found in the pre-defined PHASE_CONFIGS.
  • \n
\n", "signature": "(phase: int) -> models.config.Config:", "funcdef": "def"}, "models.numba_optimized": {"fullname": "models.numba_optimized", "modulename": "models.numba_optimized", "kind": "module", "doc": "

Numba-optimized kernels for predator-prey cellular automaton.

\n\n

Optimizations:

\n\n
    \n
  1. Cell-list PCF: O(N) average instead of O(N\u00b2) brute force
  2. \n
  3. Pre-allocated work buffers for async kernel
  4. \n
  5. Consistent dtypes throughout
  6. \n
  7. cache=True for persistent JIT compilation
  8. \n
\n\n

Usage:\n from numba_optimized import (\n PPKernel,\n compute_all_pcfs_fast,\n measure_cluster_sizes_fast, # Sizes only (fastest)\n detect_clusters_fast, # Labels + sizes dict\n get_cluster_stats_fast, # Full statistics\n get_percolating_cluster_fast, # Percolation detection\n NUMBA_AVAILABLE\n )

\n"}, "models.numba_optimized.set_numba_seed": {"fullname": "models.numba_optimized.set_numba_seed", "modulename": "models.numba_optimized", "qualname": "set_numba_seed", "kind": "function", "doc": "

Seed Numba's internal random number generator from within a JIT context.

\n\n

This function ensures that Numba's independent random number generator\nis synchronized with the provided seed, enabling reproducibility for\njit-compiled functions that use NumPy's random operations.

\n\n
Parameters
\n\n
    \n
  • seed (int):\nThe integer value used to initialize the random number generator.
  • \n
\n\n
Returns
\n\n
    \n
  • None
  • \n
\n\n
Notes
\n\n

Because Numba maintains its own internal state for random number\ngeneration, calling np.random.seed() in standard Python code will not\naffect jit-compiled functions. This helper must be called to bridge\nthat gap.

\n", "signature": "(seed: int) -> None:", "funcdef": "def"}, "models.numba_optimized.PPKernel": {"fullname": "models.numba_optimized.PPKernel", "modulename": "models.numba_optimized", "qualname": "PPKernel", "kind": "class", "doc": "

Wrapper for predator-prey kernel with pre-allocated buffers.

\n\n

This class manages the spatial configuration and memory buffers required\nfor the Numba-accelerated update kernels. By pre-allocating the\noccupied_buffer, it avoids expensive memory allocations during the\nsimulation loop.

\n\n
Parameters
\n\n
    \n
  • rows (int):\nNumber of rows in the simulation grid.
  • \n
  • cols (int):\nNumber of columns in the simulation grid.
  • \n
  • neighborhood ({'moore', 'von_neumann'}, optional):\nThe neighborhood type determining adjacent cells. 'moore' includes\ndiagonals (8 neighbors), 'von_neumann' does not (4 neighbors).\nDefault is 'moore'.
  • \n
  • directed_hunting (bool, optional):\nIf True, uses the directed behavior kernel where species search for\ntargets. If False, uses random neighbor selection. Default is False.
  • \n
\n\n
Attributes
\n\n
    \n
  • rows (int):\nGrid row count.
  • \n
  • cols (int):\nGrid column count.
  • \n
  • directed_hunting (bool):\nToggle for intelligent behavior logic.
  • \n
\n"}, "models.numba_optimized.PPKernel.__init__": {"fullname": "models.numba_optimized.PPKernel.__init__", "modulename": "models.numba_optimized", "qualname": "PPKernel.__init__", "kind": "function", "doc": "

\n", "signature": "(\trows: int,\tcols: int,\tneighborhood: str = 'moore',\tdirected_hunting: bool = False)"}, "models.numba_optimized.PPKernel.rows": {"fullname": "models.numba_optimized.PPKernel.rows", "modulename": "models.numba_optimized", "qualname": "PPKernel.rows", "kind": "variable", "doc": "

\n"}, "models.numba_optimized.PPKernel.cols": {"fullname": "models.numba_optimized.PPKernel.cols", "modulename": "models.numba_optimized", "qualname": "PPKernel.cols", "kind": "variable", "doc": "

\n"}, "models.numba_optimized.PPKernel.directed_hunting": {"fullname": "models.numba_optimized.PPKernel.directed_hunting", "modulename": "models.numba_optimized", "qualname": "PPKernel.directed_hunting", "kind": "variable", "doc": "

\n"}, "models.numba_optimized.PPKernel.update": {"fullname": "models.numba_optimized.PPKernel.update", "modulename": "models.numba_optimized", "qualname": "PPKernel.update", "kind": "function", "doc": "

Execute a single asynchronous update step using the configured kernel.

\n\n
Parameters
\n\n
    \n
  • grid (np.ndarray):\nThe current 2D simulation grid.
  • \n
  • prey_death_arr (np.ndarray):\n2D array of individual prey mortality rates.
  • \n
  • prey_birth (float):\nPrey reproduction probability.
  • \n
  • prey_death (float):\nBase prey mortality probability.
  • \n
  • pred_birth (float):\nPredator reproduction (hunting success) probability.
  • \n
  • pred_death (float):\nPredator mortality probability.
  • \n
  • evolve_sd (float, optional):\nMutation standard deviation (default 0.1).
  • \n
  • evolve_min (float, optional):\nMinimum evolved death rate (default 0.001).
  • \n
  • evolve_max (float, optional):\nMaximum evolved death rate (default 0.1).
  • \n
  • evolution_stopped (bool, optional):\nWhether to disable mutation during this step (default True).
  • \n
\n\n
Returns
\n\n
    \n
  • np.ndarray: The updated grid after one full asynchronous pass.
  • \n
\n", "signature": "(\tself,\tgrid: numpy.ndarray,\tprey_death_arr: numpy.ndarray,\tprey_birth: float,\tprey_death: float,\tpred_birth: float,\tpred_death: float,\tevolve_sd: float = 0.1,\tevolve_min: float = 0.001,\tevolve_max: float = 0.1,\tevolution_stopped: bool = True) -> numpy.ndarray:", "funcdef": "def"}, "models.numba_optimized.measure_cluster_sizes_fast": {"fullname": "models.numba_optimized.measure_cluster_sizes_fast", "modulename": "models.numba_optimized", "qualname": "measure_cluster_sizes_fast", "kind": "function", "doc": "

Measure cluster sizes for a specific species using Numba-accelerated flood fill.

\n\n

This function provides a high-performance interface for calculating cluster\nsize statistics without the overhead of generating a full label map. It is\noptimized for large-scale simulation analysis where only distribution\nmetrics (e.g., mean size, max size) are required.

\n\n
Parameters
\n\n
    \n
  • grid (np.ndarray):\nA 2D array representing the simulation environment.
  • \n
  • species (int):\nThe target species identifier (e.g., 1 for Prey, 2 for Predator).
  • \n
  • neighborhood ({'moore', 'neumann'}, optional):\nThe connectivity rule. 'moore' uses 8-way connectivity (including diagonals);\n'neumann' uses 4-way connectivity. Default is 'moore'.
  • \n
\n\n
Returns
\n\n
    \n
  • cluster_sizes (np.ndarray):\nA 1D array of integers, where each element is the cell count of an\nidentified cluster.
  • \n
\n\n
Notes
\n\n

The input grid is cast to int32 to ensure compatibility with the\nunderlying JIT-compiled _measure_clusters kernel.

\n\n
Examples
\n\n
\n
>>> sizes = measure_cluster_sizes_fast(grid, species=1, neighborhood='moore')\n>>> if sizes.size > 0:\n...     print(f"Largest cluster: {sizes.max()}")\n
\n
\n", "signature": "(\tgrid: numpy.ndarray,\tspecies: int,\tneighborhood: str = 'moore') -> numpy.ndarray:", "funcdef": "def"}, "models.numba_optimized.detect_clusters_fast": {"fullname": "models.numba_optimized.detect_clusters_fast", "modulename": "models.numba_optimized", "qualname": "detect_clusters_fast", "kind": "function", "doc": "

Perform full cluster detection with labels using Numba acceleration.

\n\n

This function returns a label array for spatial analysis and a dictionary\nof cluster sizes. It is significantly faster than standard Python or\nSciPy equivalents for large simulation grids.

\n\n
Parameters
\n\n
    \n
  • grid (np.ndarray):\nA 2D array representing the simulation environment.
  • \n
  • species (int):\nThe target species identifier (e.g., 1 for Prey, 2 for Predator).
  • \n
  • neighborhood ({'moore', 'neumann'}, optional):\nThe connectivity rule. 'moore' uses 8-way connectivity; 'neumann'\nuses 4-way connectivity. Default is 'moore'.
  • \n
\n\n
Returns
\n\n
    \n
  • labels (np.ndarray):\nA 2D int32 array where each cell contains its unique cluster ID.\nCells not belonging to the target species are 0.
  • \n
  • sizes (dict):\nA dictionary mapping cluster IDs to their respective cell counts.
  • \n
\n\n
Notes
\n\n

The underlying Numba kernel uses a stack-based flood fill to avoid\nrecursion limits and handles periodic boundary conditions.

\n\n
Examples
\n\n
\n
>>> labels, sizes = detect_clusters_fast(grid, species=1)\n>>> if sizes:\n...     largest_id = max(sizes, key=sizes.get)\n...     print(f"Cluster {largest_id} size: {sizes[largest_id]}")\n
\n
\n", "signature": "(\tgrid: numpy.ndarray,\tspecies: int,\tneighborhood: str = 'moore') -> Tuple[numpy.ndarray, Dict[int, int]]:", "funcdef": "def"}, "models.numba_optimized.get_cluster_stats_fast": {"fullname": "models.numba_optimized.get_cluster_stats_fast", "modulename": "models.numba_optimized", "qualname": "get_cluster_stats_fast", "kind": "function", "doc": "

Compute comprehensive cluster statistics for a species using Numba acceleration.

\n\n

This function integrates cluster detection and labeling to provide a\nfull suite of spatial metrics. It calculates the cluster size distribution\nand the largest cluster fraction, which often serves as an order\nparameter in percolation theory and Phase 1-3 analyses.

\n\n
Parameters
\n\n
    \n
  • grid (np.ndarray):\nA 2D array representing the simulation environment.
  • \n
  • species (int):\nThe target species identifier (e.g., 1 for Prey, 2 for Predator).
  • \n
  • neighborhood ({'moore', 'neumann'}, optional):\nThe connectivity rule. 'moore' uses 8-way connectivity; 'neumann'\nuses 4-way connectivity. Default is 'moore'.
  • \n
\n\n
Returns
\n\n
    \n
  • stats (dict):\nA dictionary containing:\n
      \n
    • 'n_clusters': Total count of isolated clusters.
    • \n
    • 'sizes': Sorted array (descending) of all cluster sizes.
    • \n
    • 'largest': Size of the single largest cluster.
    • \n
    • 'largest_fraction': Size of the largest cluster divided by\nthe total population of the species.
    • \n
    • 'mean_size': Average size of all clusters.
    • \n
    • 'size_distribution': Frequency mapping of {size: count}.
    • \n
    • 'labels': 2D array of unique cluster IDs.
    • \n
    • 'size_dict': Mapping of {label_id: size}.
    • \n
  • \n
\n\n
Examples
\n\n
\n
>>> stats = get_cluster_stats_fast(grid, species=1)\n>>> print(f"Found {stats['n_clusters']} prey clusters.")\n>>> print(f"Order parameter: {stats['largest_fraction']:.3f}")\n
\n
\n", "signature": "(grid: numpy.ndarray, species: int, neighborhood: str = 'moore') -> Dict:", "funcdef": "def"}, "models.numba_optimized.compute_pcf_periodic_fast": {"fullname": "models.numba_optimized.compute_pcf_periodic_fast", "modulename": "models.numba_optimized", "qualname": "compute_pcf_periodic_fast", "kind": "function", "doc": "

Compute the Pair Correlation Function (PCF) using cell-list acceleration.

\n\n

This high-level function coordinates the spatial hashing and histogram\ncalculation to determine the $g(r)$ function. It normalizes the resulting\nhistogram by the expected number of pairs in an ideal gas of the same\ndensity, accounting for the toroidal area of each radial bin.

\n\n
Parameters
\n\n
    \n
  • positions_i (np.ndarray):\n(N, 2) array of coordinates for species I.
  • \n
  • positions_j (np.ndarray):\n(M, 2) array of coordinates for species J.
  • \n
  • grid_shape (tuple of int):\nThe (rows, cols) dimensions of the simulation grid.
  • \n
  • max_distance (float):\nThe maximum radius to calculate correlations for.
  • \n
  • n_bins (int, optional):\nNumber of bins for the radial distribution (default 50).
  • \n
  • self_correlation (bool, optional):\nSet to True if computing the correlation of a species with itself\nto avoid self-counting (default False).
  • \n
\n\n
Returns
\n\n
    \n
  • bin_centers (np.ndarray):\nThe central radial distance for each histogram bin.
  • \n
  • pcf (np.ndarray):\nThe normalized $g(r)$ values. A value of 1.0 indicates no spatial\ncorrelation; > 1.0 indicates clustering; < 1.0 indicates repulsion.
  • \n
  • total_pairs (int):\nThe total count of pairs found within the max_distance.
  • \n
\n\n
Notes
\n\n

The function dynamically determines the optimal number of cells for the\nspatial hash based on the max_distance and grid dimensions to maintain\nlinear time complexity.

\n", "signature": "(\tpositions_i: numpy.ndarray,\tpositions_j: numpy.ndarray,\tgrid_shape: Tuple[int, int],\tmax_distance: float,\tn_bins: int = 50,\tself_correlation: bool = False) -> Tuple[numpy.ndarray, numpy.ndarray, int]:", "funcdef": "def"}, "models.numba_optimized.compute_all_pcfs_fast": {"fullname": "models.numba_optimized.compute_all_pcfs_fast", "modulename": "models.numba_optimized", "qualname": "compute_all_pcfs_fast", "kind": "function", "doc": "

Compute all three species Pair Correlation Functions (PCFs) using cell-list acceleration.

\n\n

This function calculates the spatial auto-correlations (Prey-Prey,\nPredator-Predator) and the cross-correlation (Prey-Predator) for a given\nsimulation grid. It identifies particle positions and leverages\nNumba-accelerated cell lists to handle the computations efficiently.

\n\n
Parameters
\n\n
    \n
  • grid (np.ndarray):\n2D integer array where 1 represents prey and 2 represents predators.
  • \n
  • max_distance (float, optional):\nThe maximum radial distance for the correlation. Defaults to 1/4\nof the minimum grid dimension if not provided.
  • \n
  • n_bins (int, optional):\nNumber of distance bins for the histogram. Default is 50.
  • \n
\n\n
Returns
\n\n
    \n
  • results (dict):\nA dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'.\nEach value is a tuple containing:\n
      \n
    • bin_centers (np.ndarray): Radial distances.
    • \n
    • pcf_values (np.ndarray): Normalized g(r) values.
    • \n
    • pair_count (int): Total number of pairs found.
    • \n
  • \n
\n\n
Notes
\n\n

The PCF provides insight into the spatial organization of the system.\ng(r) > 1 at short distances indicates aggregation (clustering),\nwhile g(r) < 1 indicates exclusion or repulsion.

\n", "signature": "(\tgrid: numpy.ndarray,\tmax_distance: Optional[float] = None,\tn_bins: int = 50) -> Dict[str, Tuple[numpy.ndarray, numpy.ndarray, int]]:", "funcdef": "def"}, "models.numba_optimized.warmup_numba_kernels": {"fullname": "models.numba_optimized.warmup_numba_kernels", "modulename": "models.numba_optimized", "qualname": "warmup_numba_kernels", "kind": "function", "doc": "

Pre-compile all Numba-accelerated kernels to avoid first-run latency.

\n\n

This function executes a single step of the simulation and each analysis\nroutine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation,\nthe first call to a decorated function incurs a compilation overhead.\nRunning this warmup ensures that subsequent experimental runs are timed\naccurately and perform at full speed.

\n\n
Parameters
\n\n
    \n
  • grid_size (int, optional):\nThe side length of the dummy grid used for warmup (default 100).
  • \n
  • directed_hunting (bool, optional):\nIf True, also warms up the directed behavior update kernel (default False).
  • \n
\n\n
Returns
\n\n
    \n
  • None
  • \n
\n\n
Notes
\n\n

This function checks for NUMBA_AVAILABLE before execution. It warms up\nthe PPKernel (random and optionally directed), as well as the\nspatial analysis functions (compute_all_pcfs_fast, detect_clusters_fast, etc.).

\n", "signature": "(grid_size: int = 100, directed_hunting: bool = False):", "funcdef": "def"}, "models.numba_optimized.benchmark_kernels": {"fullname": "models.numba_optimized.benchmark_kernels", "modulename": "models.numba_optimized", "qualname": "benchmark_kernels", "kind": "function", "doc": "

Benchmark the execution performance of random vs. directed update kernels.

\n\n

This utility measures the average time per simulation step for both the\nstochastic (random neighbor) and heuristic (directed hunting/reproduction)\nupdate strategies. It accounts for the computational overhead introduced\nby the \"intelligent\" search logic used in directed mode.

\n\n
Parameters
\n\n
    \n
  • grid_size (int, optional):\nThe side length of the square simulation grid (default 100).
  • \n
  • n_runs (int, optional):\nThe number of iterations to perform for averaging performance (default 20).
  • \n
\n\n
Returns
\n\n
    \n
  • t_random (float):\nAverage time per step for the random kernel in milliseconds.
  • \n
  • t_directed (float):\nAverage time per step for the directed kernel in milliseconds.
  • \n
\n\n
Notes
\n\n

The function ensures a fair comparison by:

\n\n
    \n
  1. Using a fixed seed for reproducible initial grid states.
  2. \n
  3. Warming up Numba kernels before timing to exclude JIT compilation latency.
  4. \n
  5. Copying the grid and death arrays for each iteration to maintain\nconsistent population densities throughout the benchmark.
  6. \n
\n", "signature": "(grid_size: int = 100, n_runs: int = 20):", "funcdef": "def"}, "models.numba_optimized.benchmark_cluster_detection": {"fullname": "models.numba_optimized.benchmark_cluster_detection", "modulename": "models.numba_optimized", "qualname": "benchmark_cluster_detection", "kind": "function", "doc": "

Benchmark the performance of different cluster detection and analysis routines.

\n\n

This function evaluates three levels of spatial analysis:

\n\n
    \n
  1. Size measurement only (fastest, no label map).
  2. \n
  3. Full detection (returns label map and size dictionary).
  4. \n
  5. Comprehensive statistics (calculates distributions, means, and order parameters).
  6. \n
\n\n
Parameters
\n\n
    \n
  • grid_size (int, optional):\nSide length of the square grid for benchmarking (default 100).
  • \n
  • n_runs (int, optional):\nNumber of iterations to average for performance results (default 20).
  • \n
\n\n
Returns
\n\n
    \n
  • stats (dict):\nThe result dictionary from the final comprehensive statistics run.
  • \n
\n\n
Notes
\n\n

The benchmark uses a fixed prey density of 30% to ensure a representative\ndistribution of clusters. It pre-warms the Numba kernels to ensure that\nthe measurements reflect execution speed rather than compilation time.

\n", "signature": "(grid_size: int = 100, n_runs: int = 20):", "funcdef": "def"}, "scripts": {"fullname": "scripts", "modulename": "scripts", "kind": "module", "doc": "

Execution and Orchestration Suite for Predator-Prey Hydra Effect Research.

\n\n

This module provides the entry points for running multi-phase simulation \nexperiments. It is designed for high-performance computing (HPC) environments,\nutilizing parallel processing and Numba-accelerated kernels to analyze \nself-organized criticality and finite-size scaling.

\n\n

Experimental Phases

\n\n

The suite is divided into five sequential research phases:

\n\n
    \n
  1. Critical Point Identification: Parameter sweeps to locate \nbifurcation points and phase transitions.
  2. \n
  3. Self-Organization Analysis: Observations of evolutionary drift \ntoward critical states.
  4. \n
  5. Finite-Size Scaling (FSS): Analysis of cluster size cutoffs \nacross varying grid dimensions ($L$).
  6. \n
  7. Sensitivity Analysis: Comprehensive 4D parameter sweeps across \nsurvival regimes.
  8. \n
  9. Directed Hunting Comparisons: Sensitivity analysis using \nnon-random predator search kernels.
  10. \n
\n\n

Performance Features

\n\n
    \n
  • Parallelization: Automated CPU core detection and SLURM integration \nvia joblib.
  • \n
  • Reproducibility: Deterministic seed generation using SHA-256 hashing \nof parameter states.
  • \n
  • Incremental Persistence: Results are saved in JSON Lines (JSONL) \nformat to ensure data recovery during long-running batches.
  • \n
\n\n

Usage

\n\n

Experiments should be invoked from the project root:\n```bash\npython scripts/experiments.py --phase 1 --output results/

\n"}, "scripts.experiments": {"fullname": "scripts.experiments", "modulename": "scripts.experiments", "kind": "module", "doc": "

Predator-Prey Hydra Effect Experiments - HPC Version

\n\n

Experimental phases (run sequentially):\n Phase 1: Parameter sweep to find critical point (bifurcation + cluster analysis)\n Phase 2: Self-organization analysis (evolution toward criticality)\n Phase 3: Finite-size scaling at critical point\n Phase 4: Sensitivity analysis across parameter regimes\n Phase 5: Model extensions (directed hunting comparison)

\n\n

Usage:\n python experiments.py --phase 1 # Run phase 1\n python experiments.py --phase 1 --dry-run # Estimate runtime\n python experiments.py --phase all # Run all phases\n python experiments.py --phase 1 --output results/ # Custom output

\n"}, "scripts.experiments.project_root": {"fullname": "scripts.experiments.project_root", "modulename": "scripts.experiments", "qualname": "project_root", "kind": "variable", "doc": "

\n", "default_value": "$OLDPWD"}, "scripts.experiments.generate_unique_seed": {"fullname": "scripts.experiments.generate_unique_seed", "modulename": "scripts.experiments", "qualname": "generate_unique_seed", "kind": "function", "doc": "

Create a deterministic seed from a dictionary of parameters and a repetition index.

\n\n

This function serializes the input dictionary into a sorted JSON string,\nappends the repetition count, and hashes the resulting string using SHA-256.\nThe first 8 characters of the hex digest are then converted to an integer\nto provide a stable, unique seed for random number generators.

\n\n
Parameters
\n\n
    \n
  • params (dict):\nA dictionary of configuration parameters. Keys are sorted to ensure\ndeterminism regardless of insertion order.
  • \n
  • rep (int):\nThe repetition or iteration index, used to ensure different seeds\nare generated for the same parameter set across multiple runs.
  • \n
\n\n
Returns
\n\n
    \n
  • int: A unique integer seed derived from the input parameters.
  • \n
\n\n
Examples
\n\n
\n
>>> params = {'learning_rate': 0.01, 'batch_size': 32}\n>>> generate_unique_seed(params, 1)\n3432571217\n>>> generate_unique_seed(params, 2)\n3960013583\n
\n
\n", "signature": "(params: dict, rep: int) -> int:", "funcdef": "def"}, "scripts.experiments.count_populations": {"fullname": "scripts.experiments.count_populations", "modulename": "scripts.experiments", "qualname": "count_populations", "kind": "function", "doc": "

Count the number of empty, prey, and predator cells in the simulation grid.

\n\n
Parameters
\n\n
    \n
  • grid (np.ndarray):\nA 2D NumPy array representing the simulation environment, where:\n
      \n
    • 0: Empty cell
    • \n
    • 1: Prey
    • \n
    • 2: Predator
    • \n
  • \n
\n\n
Returns
\n\n
    \n
  • empty_count (int):\nTotal number of cells with a value of 0.
  • \n
  • prey_count (int):\nTotal number of cells with a value of 1.
  • \n
  • predator_count (int):\nTotal number of cells with a value of 2.
  • \n
\n\n
Examples
\n\n
\n
>>> grid = np.array([[0, 1], [2, 1]])\n>>> count_populations(grid)\n(1, 2, 1)\n
\n
\n", "signature": "(grid: numpy.ndarray) -> Tuple[int, int, int]:", "funcdef": "def"}, "scripts.experiments.get_evolved_stats": {"fullname": "scripts.experiments.get_evolved_stats", "modulename": "scripts.experiments", "qualname": "get_evolved_stats", "kind": "function", "doc": "

Get statistics of an evolved parameter from the model.

\n\n

This function retrieves parameter values from the model's internal storage,\nfilters out NaN values, and calculates basic descriptive statistics.

\n\n
Parameters
\n\n
    \n
  • model (object):\nThe simulation model instance containing a cell_params attribute\nwith a .get() method.
  • \n
  • param (str):\nThe name of the parameter to calculate statistics for.
  • \n
\n\n
Returns
\n\n
    \n
  • stats (dict):\nA dictionary containing the following keys:\n
      \n
    • 'mean': Arithmetic mean of valid values.
    • \n
    • 'std': Standard deviation of valid values.
    • \n
    • 'min': Minimum valid value.
    • \n
    • 'max': Maximum valid value.
    • \n
    • 'n': Count of non-NaN values.\nIf no valid data is found, all stats return NaN and n returns 0.
    • \n
  • \n
\n\n
Examples
\n\n
\n
>>> stats = get_evolved_stats(my_model, "speed")\n>>> print(stats['mean'])\n1.25\n
\n
\n", "signature": "(model, param: str) -> Dict:", "funcdef": "def"}, "scripts.experiments.average_pcfs": {"fullname": "scripts.experiments.average_pcfs", "modulename": "scripts.experiments", "qualname": "average_pcfs", "kind": "function", "doc": "

Average multiple Pair Correlation Function (PCF) measurements and calculate standard error.

\n\n
Parameters
\n\n
    \n
  • pcf_list (list of tuple):\nA list where each element is a tuple containing:\n
      \n
    • distances (np.ndarray): The radial distances (r).
    • \n
    • pcf_values (np.ndarray): The correlation values g(r).
    • \n
    • count (int): Metadata or weight (not used in current calculation).
    • \n
  • \n
\n\n
Returns
\n\n
    \n
  • distances (np.ndarray):\nThe radial distances from the first entry in the list.
  • \n
  • pcf_mean (np.ndarray):\nThe element-wise mean of the PCF values across all measurements.
  • \n
  • pcf_se (np.ndarray):\nThe standard error of the mean for the PCF values.
  • \n
\n\n
Examples
\n\n
\n
>>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10),\n...         (np.array([0, 1]), np.array([1.2, 1.8]), 12)]\n>>> dist, mean, se = average_pcfs(data)\n>>> mean\narray([1.1, 1.9])\n
\n
\n", "signature": "(\tpcf_list: List[Tuple[numpy.ndarray, numpy.ndarray, int]]) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]:", "funcdef": "def"}, "scripts.experiments.save_results_jsonl": {"fullname": "scripts.experiments.save_results_jsonl", "modulename": "scripts.experiments", "qualname": "save_results_jsonl", "kind": "function", "doc": "

Save a list of dictionaries to a file in JSON Lines (JSONL) format.

\n\n

Each dictionary in the list is serialized into a single JSON string and\nwritten as a new line. Non-serializable objects are converted to strings\nusing the default string representation.

\n\n
Parameters
\n\n
    \n
  • results (list of dict):\nThe collection of result dictionaries to be saved.
  • \n
  • output_path (Path):\nThe file system path (pathlib.Path) where the JSONL file will be created.
  • \n
\n\n
Returns
\n\n
    \n
  • None
  • \n
\n\n
Notes
\n\n

The file is opened in 'w' (write) mode, which will overwrite any existing\ncontent at the specified path.

\n\n
Examples
\n\n
\n
>>> data = [{"id": 1, "score": 0.95}, {"id": 2, "score": 0.88}]\n>>> save_results_jsonl(data, Path("results.jsonl"))\n
\n
\n", "signature": "(results: List[Dict], output_path: pathlib.Path):", "funcdef": "def"}, "scripts.experiments.save_results_npz": {"fullname": "scripts.experiments.save_results_npz", "modulename": "scripts.experiments", "qualname": "save_results_npz", "kind": "function", "doc": "

Save simulation results to a compressed NumPy (.npz) binary file.

\n\n

This function flattens a list of result dictionaries into a single\ndictionary of NumPy arrays, prefixing keys with the run index to\nmaintain data separation. The resulting file is compressed to\nreduce storage space.

\n\n
Parameters
\n\n
    \n
  • results (list of dict):\nA list where each dictionary contains key-value pairs of\nsimulation data (e.g., arrays, lists, or scalars).
  • \n
  • output_path (Path):\nThe file system path (pathlib.Path) where the compressed\nNPZ file will be saved.
  • \n
\n\n
Returns
\n\n
    \n
  • None
  • \n
\n\n
Notes
\n\n

The keys in the saved file follow the format 'run_{index}_{original_key}'.\nValues are automatically converted to NumPy arrays if they are not\nalready.

\n\n
Examples
\n\n
\n
>>> results = [{"energy": [1, 2]}, {"energy": [3, 4]}]\n>>> save_results_npz(results, Path("output.npz"))\n
\n
\n", "signature": "(results: List[Dict], output_path: pathlib.Path):", "funcdef": "def"}, "scripts.experiments.load_results_jsonl": {"fullname": "scripts.experiments.load_results_jsonl", "modulename": "scripts.experiments", "qualname": "load_results_jsonl", "kind": "function", "doc": "

Load simulation results from a JSON Lines (JSONL) formatted file.

\n\n

This function reads a file line-by-line, parsing each line as an\nindependent JSON object and aggregating them into a list of dictionaries.

\n\n
Parameters
\n\n
    \n
  • input_path (Path):\nThe file system path (pathlib.Path) to the JSONL file.
  • \n
\n\n
Returns
\n\n
    \n
  • results (list of dict):\nA list of dictionaries reconstructed from the file content.
  • \n
\n\n
Raises
\n\n
    \n
  • FileNotFoundError: If the specified input path does not exist.
  • \n
  • json.JSONDecodeError: If a line in the file is not valid JSON.
  • \n
\n\n
Examples
\n\n
\n
>>> data = load_results_jsonl(Path("results.jsonl"))\n>>> len(data)\n2\n
\n
\n", "signature": "(input_path: pathlib.Path) -> List[Dict]:", "funcdef": "def"}, "scripts.experiments.run_single_simulation": {"fullname": "scripts.experiments.run_single_simulation", "modulename": "scripts.experiments", "qualname": "run_single_simulation", "kind": "function", "doc": "

Run a single Predator-Prey (PP) simulation and collect comprehensive metrics.

\n\n

This function initializes a Cellular Automata model, executes a warmup phase\nto reach steady state, and then performs a measurement phase to track\npopulation dynamics, spatial clustering, and evolutionary changes.

\n\n
Parameters
\n\n
    \n
  • prey_birth (float):\nThe probability or rate of prey reproduction.
  • \n
  • prey_death (float):\nThe base probability or rate of prey mortality.
  • \n
  • predator_birth (float):\nThe probability or rate of predator reproduction upon consuming prey.
  • \n
  • predator_death (float):\nThe probability or rate of predator mortality.
  • \n
  • grid_size (int):\nThe side length of the square simulation grid.
  • \n
  • seed (int):\nRandom seed for ensuring reproducibility of the simulation run.
  • \n
  • cfg (Config):\nA configuration object containing simulation hyperparameters (densities,\nsampling rates, timing, etc.).
  • \n
  • with_evolution (bool, optional):\nIf True, enables the evolution of the 'prey_death' parameter within\nthe model (default is False).
  • \n
  • compute_pcf (bool, optional):\nExplicit toggle for Pair Correlation Function calculation. If None,\nit is determined by cfg.pcf_sample_rate (default is None).
  • \n
\n\n
Returns
\n\n
    \n
  • result (dict):\nA dictionary containing simulation results including:\n
      \n
    • Input parameters and survival flags.
    • \n
    • Population mean and standard deviation for both species.
    • \n
    • Cluster statistics (number of clusters, sizes, largest fractions).
    • \n
    • Evolutionary statistics (mean, std, min, max, and final values).
    • \n
    • PCF data and spatial indices (segregation and clustering).
    • \n
    • Optional time series for populations and evolved parameters.
    • \n
  • \n
\n\n
Notes
\n\n

The function relies on several external utilities: count_populations,\nget_evolved_stats, get_cluster_stats_fast, compute_all_pcfs_fast,\nand average_pcfs.

\n", "signature": "(\tprey_birth: float,\tprey_death: float,\tpredator_birth: float,\tpredator_death: float,\tgrid_size: int,\tseed: int,\tcfg: models.config.Config,\twith_evolution: bool = False,\tcompute_pcf: Optional[bool] = None) -> Dict:", "funcdef": "def"}, "scripts.experiments.run_phase1": {"fullname": "scripts.experiments.run_phase1", "modulename": "scripts.experiments", "qualname": "run_phase1", "kind": "function", "doc": "

Execute Phase 1 of the simulation: a parameter sweep to identify critical points.

\n\n

This function performs a 1D sweep across varying prey mortality rates while\nkeeping other parameters fixed. It utilizes parallel execution via joblib\nand saves results incrementally to a JSONL file to ensure data integrity\nduring long-running batches.

\n\n
Parameters
\n\n
    \n
  • cfg (Config):\nConfiguration object containing simulation hyperparameters, sweep\nranges, and execution settings (n_jobs, grid_size, etc.).
  • \n
  • output_dir (Path):\nDirectory where result files (JSONL) and metadata (JSON) will be stored.
  • \n
  • logger (logging.Logger):\nLogger instance for tracking simulation progress and recording\noperational metadata.
  • \n
\n\n
Returns
\n\n
    \n
  • all_results (list of dict):\nA list of dictionaries containing the metrics collected from every\nindividual simulation run in the sweep.
  • \n
\n\n
Notes
\n\n

The function performs the following steps:

\n\n
    \n
  1. Pre-warms Numba kernels for performance.
  2. \n
  3. Generates a deterministic set of simulation jobs using unique seeds.
  4. \n
  5. Executes simulations in parallel using a generator for memory efficiency.
  6. \n
  7. Records metadata including a timestamp and a serialized snapshot of\nthe configuration.
  8. \n
\n", "signature": "(\tcfg: models.config.Config,\toutput_dir: pathlib.Path,\tlogger: logging.Logger) -> List[Dict]:", "funcdef": "def"}, "scripts.experiments.run_phase2": {"fullname": "scripts.experiments.run_phase2", "modulename": "scripts.experiments", "qualname": "run_phase2", "kind": "function", "doc": "

Execute Phase 2 of the simulation: self-organization and criticality analysis.

\n\n

This phase tests the Self-Organized Criticality (SOC) hypothesis by\ninitializing simulations at different points in the parameter space and\nobserving whether evolutionary pressure drives the system toward a\ncommon critical point, regardless of initial prey mortality rates.

\n\n
Parameters
\n\n
    \n
  • cfg (Config):\nConfiguration object containing simulation hyperparameters, evolution\nsettings, and execution constraints.
  • \n
  • output_dir (Path):\nDirectory where result files (JSONL) and metadata (JSON) will be stored.
  • \n
  • logger (logging.Logger):\nLogger instance for tracking progress and evolutionary convergence.
  • \n
\n\n
Returns
\n\n
    \n
  • all_results (list of dict):\nA list of dictionaries containing metrics from the evolutionary\nsimulation runs.
  • \n
\n\n
Notes
\n\n

The function captures:

\n\n
    \n
  1. Convergence of 'prey_death' across multiple replicates.
  2. \n
  3. Final steady-state population distributions.
  4. \n
  5. Incremental saving of results to prevent data loss.
  6. \n
\n", "signature": "(\tcfg: models.config.Config,\toutput_dir: pathlib.Path,\tlogger: logging.Logger) -> List[Dict]:", "funcdef": "def"}, "scripts.experiments.run_phase3": {"fullname": "scripts.experiments.run_phase3", "modulename": "scripts.experiments", "qualname": "run_phase3", "kind": "function", "doc": "

Phase 3: Finite-size scaling at critical point.

\n\n
    \n
  • Multiple grid sizes at (critical_prey_birth, critical_prey_death)
  • \n
  • Analyze cluster size cutoffs vs L
  • \n
\n", "signature": "(\tcfg: models.config.Config,\toutput_dir: pathlib.Path,\tlogger: logging.Logger) -> List[Dict]:", "funcdef": "def"}, "scripts.experiments.run_phase4": {"fullname": "scripts.experiments.run_phase4", "modulename": "scripts.experiments", "qualname": "run_phase4", "kind": "function", "doc": "

Execute Phase 3 of the simulation: Finite-Size Scaling (FSS) analysis.

\n\n

This phase investigates how spatial structures, specifically cluster size\ncutoffs, scale with the system size (L) at the critical point identified\nin Phase 1. This is essential for determining the universality class of\nthe phase transition.

\n\n
Parameters
\n\n
    \n
  • cfg (Config):\nConfiguration object containing critical point parameters, the list of\ngrid sizes to test, and execution settings.
  • \n
  • output_dir (Path):\nDirectory where result files (JSONL) and FSS metadata (JSON) will be\nstored.
  • \n
  • logger (logging.Logger):\nLogger instance for tracking progress across different grid sizes.
  • \n
\n\n
Returns
\n\n
    \n
  • all_results (list of dict):\nA list of dictionaries containing metrics and cluster statistics for\neach grid size and replicate.
  • \n
\n\n
Notes
\n\n

The function performs the following:

\n\n
    \n
  1. Iterates through multiple grid sizes defined in cfg.grid_sizes.
  2. \n
  3. Generates parallel jobs for each size using critical birth/death rates.
  4. \n
  5. Saves results incrementally to allow for post-simulation analysis of\npower-law exponents.
  6. \n
\n", "signature": "(\tcfg: models.config.Config,\toutput_dir: pathlib.Path,\tlogger: logging.Logger) -> List[Dict]:", "funcdef": "def"}, "scripts.experiments.run_phase5": {"fullname": "scripts.experiments.run_phase5", "modulename": "scripts.experiments", "qualname": "run_phase5", "kind": "function", "doc": "

Execute Phase 5 of the simulation: Global 4D parameter sweep with directed hunting.

\n\n

This phase performs a comprehensive sensitivity analysis by varying four key\nparameters (prey birth/death and predator birth/death) while directed\nhunting is enabled. The results allow for a direct comparison with Phase 4\nto determine how predator search behavior shifts the system's critical\nthresholds and stability.

\n\n
Parameters
\n\n
    \n
  • cfg (Config):\nConfiguration object containing simulation hyperparameters, parallel\nexecution settings, and the fixed grid size for this phase.
  • \n
  • output_dir (Path):\nDirectory where the result JSONL file and execution metadata will\nbe stored.
  • \n
  • logger (logging.Logger):\nLogger instance for tracking the progress of the high-volume\nsimulation batch.
  • \n
\n\n
Returns
\n\n
    \n
  • all_results (list of dict):\nA list of dictionaries containing metrics for every simulation in\nthe 4D parameter grid.
  • \n
\n\n
Notes
\n\n

The function utilizes a Cartesian product of parameter ranges to build a\njob list of over 13,000 unique parameter sets (multiplied by replicates).\nSeeds are uniquely generated to distinguish these runs from other phases\neven if parameter values overlap.

\n", "signature": "(\tcfg: models.config.Config,\toutput_dir: pathlib.Path,\tlogger: logging.Logger) -> List[Dict]:", "funcdef": "def"}, "scripts.experiments.PHASE_RUNNERS": {"fullname": "scripts.experiments.PHASE_RUNNERS", "modulename": "scripts.experiments", "qualname": "PHASE_RUNNERS", "kind": "variable", "doc": "

\n", "default_value": "{1: <function run_phase1>, 2: <function run_phase2>, 3: <function run_phase3>, 4: <function run_phase4>, 5: <function run_phase5>}"}, "scripts.experiments.main": {"fullname": "scripts.experiments.main", "modulename": "scripts.experiments", "qualname": "main", "kind": "function", "doc": "

Organize the predator-prey experimental suite across multiple phases.

\n\n

This entry point handles command-line arguments, sets up logging and output\ndirectories, and executes the requested simulation phases (1-5). It\nsupports parallel execution, dry runs for runtime estimation, and\nautomated configuration persistence.

\n\n
Notes
\n\n

The script dynamically retrieves phase-specific configurations using\nget_phase_config and dispatches execution to the corresponding runner\nin the PHASE_RUNNERS mapping.

\n", "signature": "():", "funcdef": "def"}, "scripts.experiments.warmup_numba_kernels": {"fullname": "scripts.experiments.warmup_numba_kernels", "modulename": "scripts.experiments", "qualname": "warmup_numba_kernels", "kind": "function", "doc": "

\n", "signature": "(size, **kwargs):", "funcdef": "def"}, "scripts.experiments.set_numba_seed": {"fullname": "scripts.experiments.set_numba_seed", "modulename": "scripts.experiments", "qualname": "set_numba_seed", "kind": "function", "doc": "

\n", "signature": "(seed):", "funcdef": "def"}}, "docInfo": {"models": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 83}, "models.CA": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 7}, "models.CA.logger": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 151}, "models.CA.CA.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 132, "bases": 0, "doc": 138}, "models.CA.CA.rows": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 10}, "models.CA.CA.cols": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 10}, "models.CA.CA.densities": {"qualname": 2, "fullname": 4, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 11}, "models.CA.CA.n_species": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 13}, "models.CA.CA.params": {"qualname": 2, "fullname": 4, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA.cell_params": {"qualname": 3, "fullname": 5, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA.species_names": {"qualname": 3, "fullname": 5, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA.neighborhood": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA.generator": {"qualname": 2, "fullname": 4, "annotation": 5, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA.grid": {"qualname": 2, "fullname": 4, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.CA.validate": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 52}, "models.CA.CA.evolve": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 119, "bases": 0, "doc": 267}, "models.CA.CA.update": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 110}, "models.CA.CA.run": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 77, "bases": 0, "doc": 186}, "models.CA.PP": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 1, "doc": 270}, "models.CA.PP.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 229, "bases": 0, "doc": 13}, "models.CA.PP.synchronous": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.PP.directed_hunting": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.PP.species_names": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.CA.PP.validate": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 81}, "models.CA.PP.update_async": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 48}, "models.CA.PP.update": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 13}, "models.config": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 73}, "models.config.Config": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 740}, "models.config.Config.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 1008, "bases": 0, "doc": 3}, "models.config.Config.grid_size": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.densities": {"qualname": 2, "fullname": 4, "annotation": 3, "default_value": 6, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.grid_sizes": {"qualname": 3, "fullname": 5, "annotation": 3, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.prey_birth": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.prey_death": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.predator_birth": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.predator_death": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.critical_prey_birth": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.critical_prey_death": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.prey_death_range": {"qualname": 4, "fullname": 6, "annotation": 3, "default_value": 6, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.n_prey_birth": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.n_prey_death": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.predator_birth_values": {"qualname": 4, "fullname": 6, "annotation": 3, "default_value": 10, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.predator_death_values": {"qualname": 4, "fullname": 6, "annotation": 3, "default_value": 10, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.prey_death_offsets": {"qualname": 4, "fullname": 6, "annotation": 3, "default_value": 12, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.n_replicates": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.warmup_steps": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.measurement_steps": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.with_evolution": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.evolve_sd": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.evolve_min": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.evolve_max": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.sensitivity_sd_values": {"qualname": 4, "fullname": 6, "annotation": 3, "default_value": 12, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.synchronous": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.directed_hunting": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.directed_hunting_values": {"qualname": 4, "fullname": 6, "annotation": 3, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.save_timeseries": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.timeseries_subsample": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.collect_pcf": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.pcf_sample_rate": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.pcf_max_distance": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.pcf_n_bins": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.min_density_for_analysis": {"qualname": 5, "fullname": 7, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.perturbation_magnitude": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.n_jobs": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "models.config.Config.get_prey_births": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 46}, "models.config.Config.get_prey_deaths": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 46}, "models.config.Config.get_warmup_steps": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 55}, "models.config.Config.get_measurement_steps": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 109}, "models.config.Config.estimate_runtime": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 32, "bases": 0, "doc": 79}, "models.config.PHASE1_CONFIG": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 177, "signature": 0, "bases": 0, "doc": 3}, "models.config.PHASE2_CONFIG": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 177, "signature": 0, "bases": 0, "doc": 3}, "models.config.PHASE3_CONFIG": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 177, "signature": 0, "bases": 0, "doc": 3}, "models.config.PHASE4_CONFIG": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 177, "signature": 0, "bases": 0, "doc": 3}, "models.config.PHASE5_CONFIG": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 177, "signature": 0, "bases": 0, "doc": 3}, "models.config.PHASE_CONFIGS": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 886, "signature": 0, "bases": 0, "doc": 3}, "models.config.get_phase_config": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 29, "bases": 0, "doc": 82}, "models.numba_optimized": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 99}, "models.numba_optimized.set_numba_seed": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 126}, "models.numba_optimized.PPKernel": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 192}, "models.numba_optimized.PPKernel.__init__": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 67, "bases": 0, "doc": 3}, "models.numba_optimized.PPKernel.rows": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.numba_optimized.PPKernel.cols": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.numba_optimized.PPKernel.directed_hunting": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "models.numba_optimized.PPKernel.update": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 181, "bases": 0, "doc": 194}, "models.numba_optimized.measure_cluster_sizes_fast": {"qualname": 4, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 63, "bases": 0, "doc": 327}, "models.numba_optimized.detect_clusters_fast": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 85, "bases": 0, "doc": 353}, "models.numba_optimized.get_cluster_stats_fast": {"qualname": 4, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 55, "bases": 0, "doc": 400}, "models.numba_optimized.compute_pcf_periodic_fast": {"qualname": 4, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 145, "bases": 0, "doc": 301}, "models.numba_optimized.compute_all_pcfs_fast": {"qualname": 4, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 107, "bases": 0, "doc": 235}, "models.numba_optimized.warmup_numba_kernels": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 178}, "models.numba_optimized.benchmark_kernels": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 208}, "models.numba_optimized.benchmark_cluster_detection": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 170}, "scripts": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 239}, "scripts.experiments": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 100}, "scripts.experiments.project_root": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "scripts.experiments.generate_unique_seed": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 29, "bases": 0, "doc": 263}, "scripts.experiments.count_populations": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 206}, "scripts.experiments.get_evolved_stats": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 255}, "scripts.experiments.average_pcfs": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 86, "bases": 0, "doc": 344}, "scripts.experiments.save_results_jsonl": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 38, "bases": 0, "doc": 259}, "scripts.experiments.save_results_npz": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 38, "bases": 0, "doc": 262}, "scripts.experiments.load_results_jsonl": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 31, "bases": 0, "doc": 200}, "scripts.experiments.run_single_simulation": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 145, "bases": 0, "doc": 360}, "scripts.experiments.run_phase1": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 69, "bases": 0, "doc": 225}, "scripts.experiments.run_phase2": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 69, "bases": 0, "doc": 187}, "scripts.experiments.run_phase3": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 69, "bases": 0, "doc": 36}, "scripts.experiments.run_phase4": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 69, "bases": 0, "doc": 214}, "scripts.experiments.run_phase5": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 69, "bases": 0, "doc": 216}, "scripts.experiments.PHASE_RUNNERS": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 31, "signature": 0, "bases": 0, "doc": 3}, "scripts.experiments.main": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 7, "bases": 0, "doc": 82}, "scripts.experiments.warmup_numba_kernels": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 18, "bases": 0, "doc": 3}, "scripts.experiments.set_numba_seed": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}}, "length": 113, "save": true}, "index": {"qualname": {"root": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}}, "df": 4, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.logger": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.densities": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}, "models.CA.CA.species_names": {"tf": 1}, "models.CA.CA.neighborhood": {"tf": 1}, "models.CA.CA.generator": {"tf": 1}, "models.CA.CA.grid": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}}, "df": 16}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.cols": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}}, "df": 2}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.collect_pcf": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.config.Config.grid_size": {"tf": 1}, "models.config.Config.densities": {"tf": 1}, "models.config.Config.grid_sizes": {"tf": 1}, "models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.with_evolution": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.Config.synchronous": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.config.Config.save_timeseries": {"tf": 1}, "models.config.Config.timeseries_subsample": {"tf": 1}, "models.config.Config.collect_pcf": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}, "models.config.Config.perturbation_magnitude": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}}, "df": 48, "s": {"docs": {"models.config.PHASE_CONFIGS": {"tf": 1}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.count_populations": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.cell_params": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}}, "df": 2}}}}}}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 3, "s": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}}, "df": 4}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.rows": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}}, "df": 2}}, "o": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.project_root": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.prey_death_range": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.pcf_sample_rate": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.n_replicates": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3}}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.densities": {"tf": 1}, "models.config.Config.densities": {"tf": 1}}, "df": 2}}}, "y": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}}, "df": 7, "s": {"docs": {"models.config.Config.get_prey_deaths": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.PP.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}}, "df": 4}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.pcf_max_distance": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {"models.CA.CA.n_species": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}}, "df": 6, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.species_names": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.neighborhood": {"tf": 1}}, "df": 1}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 4}}}}, "p": {"docs": {}, "df": 0, "z": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.species_names": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}}, "df": 3}}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.synchronous": {"tf": 1}, "models.config.Config.synchronous": {"tf": 1}}, "df": 2}}}}}}}}}}, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.grid_size": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 4}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2}}}}, "d": {"docs": {"models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.sensitivity_sd_values": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 3}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.save_timeseries": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.pcf_sample_rate": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.timeseries_subsample": {"tf": 1}}, "df": 1}}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}}, "df": 2}}}}}, "p": {"docs": {"models.CA.PP": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.synchronous": {"tf": 1}, "models.CA.PP.directed_hunting": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}}, "df": 8, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 6}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}}, "df": 10}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}}, "df": 4}}}}}}, "o": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.project_root": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "f": {"docs": {"models.config.Config.collect_pcf": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 5, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.perturbation_magnitude": {"tf": 1}}, "df": 1}}}}}}}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"1": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}, "2": {"docs": {"models.config.PHASE2_CONFIG": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}, "3": {"docs": {"models.config.PHASE3_CONFIG": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}}, "df": 2}, "4": {"docs": {"models.config.PHASE4_CONFIG": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2}, "5": {"docs": {"models.config.PHASE5_CONFIG": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2}, "docs": {"models.config.PHASE_CONFIGS": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 3}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.count_populations": {"tf": 1}}, "df": 1}}}}}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.generator": {"tf": 1}}, "df": 1}}, "e": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 7}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.grid": {"tf": 1}, "models.config.Config.grid_size": {"tf": 1}, "models.config.Config.grid_sizes": {"tf": 1}}, "df": 3}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}}, "df": 4}}}}}}, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}}, "df": 4, "d": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.with_evolution": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 4}}}}}, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.PP.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}}, "df": 4}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"models.CA.PP.update_async": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1}}}}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}}, "df": 5, "s": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.pcf_n_bins": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 2}}}}}}}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.prey_death_offsets": {"tf": 1}}, "df": 1}}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}}, "df": 4}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.with_evolution": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 2}}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}}, "df": 2}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.perturbation_magnitude": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.save_timeseries": {"tf": 1}, "models.config.Config.timeseries_subsample": {"tf": 1}}, "df": 2}}}}}}}}}}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 5}}}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.n_jobs": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 2}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}}, "df": 3}}}}}}}}}, "fullname": {"root": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}}, "df": 4, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}, "models.CA": {"tf": 1}, "models.CA.logger": {"tf": 1}, "models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.densities": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}, "models.CA.CA.species_names": {"tf": 1}, "models.CA.CA.neighborhood": {"tf": 1}, "models.CA.CA.generator": {"tf": 1}, "models.CA.CA.grid": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.synchronous": {"tf": 1}, "models.CA.PP.directed_hunting": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.config.Config.grid_size": {"tf": 1}, "models.config.Config.densities": {"tf": 1}, "models.config.Config.grid_sizes": {"tf": 1}, "models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.with_evolution": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.Config.synchronous": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.config.Config.save_timeseries": {"tf": 1}, "models.config.Config.timeseries_subsample": {"tf": 1}, "models.config.Config.collect_pcf": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}, "models.config.Config.perturbation_magnitude": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 93}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 2}}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}}, "df": 2}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.perturbation_magnitude": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {"models.CA": {"tf": 1}, "models.CA.logger": {"tf": 1}, "models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.rows": {"tf": 1.4142135623730951}, "models.CA.CA.cols": {"tf": 1.4142135623730951}, "models.CA.CA.densities": {"tf": 1.4142135623730951}, "models.CA.CA.n_species": {"tf": 1.4142135623730951}, "models.CA.CA.params": {"tf": 1.4142135623730951}, "models.CA.CA.cell_params": {"tf": 1.4142135623730951}, "models.CA.CA.species_names": {"tf": 1.4142135623730951}, "models.CA.CA.neighborhood": {"tf": 1.4142135623730951}, "models.CA.CA.generator": {"tf": 1.4142135623730951}, "models.CA.CA.grid": {"tf": 1.4142135623730951}, "models.CA.CA.validate": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.update": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.synchronous": {"tf": 1}, "models.CA.PP.directed_hunting": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}}, "df": 26}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.cols": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}}, "df": 2}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.collect_pcf": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.config.Config.grid_size": {"tf": 1.4142135623730951}, "models.config.Config.densities": {"tf": 1.4142135623730951}, "models.config.Config.grid_sizes": {"tf": 1.4142135623730951}, "models.config.Config.prey_birth": {"tf": 1.4142135623730951}, "models.config.Config.prey_death": {"tf": 1.4142135623730951}, "models.config.Config.predator_birth": {"tf": 1.4142135623730951}, "models.config.Config.predator_death": {"tf": 1.4142135623730951}, "models.config.Config.critical_prey_birth": {"tf": 1.4142135623730951}, "models.config.Config.critical_prey_death": {"tf": 1.4142135623730951}, "models.config.Config.prey_death_range": {"tf": 1.4142135623730951}, "models.config.Config.n_prey_birth": {"tf": 1.4142135623730951}, "models.config.Config.n_prey_death": {"tf": 1.4142135623730951}, "models.config.Config.predator_birth_values": {"tf": 1.4142135623730951}, "models.config.Config.predator_death_values": {"tf": 1.4142135623730951}, "models.config.Config.prey_death_offsets": {"tf": 1.4142135623730951}, "models.config.Config.n_replicates": {"tf": 1.4142135623730951}, "models.config.Config.warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.with_evolution": {"tf": 1.4142135623730951}, "models.config.Config.evolve_sd": {"tf": 1.4142135623730951}, "models.config.Config.evolve_min": {"tf": 1.4142135623730951}, "models.config.Config.evolve_max": {"tf": 1.4142135623730951}, "models.config.Config.sensitivity_sd_values": {"tf": 1.4142135623730951}, "models.config.Config.synchronous": {"tf": 1.4142135623730951}, "models.config.Config.directed_hunting": {"tf": 1.4142135623730951}, "models.config.Config.directed_hunting_values": {"tf": 1.4142135623730951}, "models.config.Config.save_timeseries": {"tf": 1.4142135623730951}, "models.config.Config.timeseries_subsample": {"tf": 1.4142135623730951}, "models.config.Config.collect_pcf": {"tf": 1.4142135623730951}, "models.config.Config.pcf_sample_rate": {"tf": 1.4142135623730951}, "models.config.Config.pcf_max_distance": {"tf": 1.4142135623730951}, "models.config.Config.pcf_n_bins": {"tf": 1.4142135623730951}, "models.config.Config.min_density_for_analysis": {"tf": 1.4142135623730951}, "models.config.Config.perturbation_magnitude": {"tf": 1.4142135623730951}, "models.config.Config.n_jobs": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_births": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_deaths": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1.4142135623730951}, "models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 1}, "models.config.get_phase_config": {"tf": 1.4142135623730951}}, "df": 50, "s": {"docs": {"models.config.PHASE_CONFIGS": {"tf": 1}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.count_populations": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.cell_params": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}}, "df": 2}}}}}}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 3, "s": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.logger": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}}, "df": 4}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.rows": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}}, "df": 2}}, "o": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.project_root": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.prey_death_range": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.pcf_sample_rate": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.n_replicates": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3}}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.densities": {"tf": 1}, "models.config.Config.densities": {"tf": 1}}, "df": 2}}}, "y": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}}, "df": 7, "s": {"docs": {"models.config.Config.get_prey_deaths": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.PP.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}}, "df": 4}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.pcf_max_distance": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {"models.CA.CA.n_species": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}}, "df": 6, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.species_names": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.neighborhood": {"tf": 1}}, "df": 1}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 18}}}}, "p": {"docs": {}, "df": 0, "z": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.species_names": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}}, "df": 3}}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.synchronous": {"tf": 1}, "models.config.Config.synchronous": {"tf": 1}}, "df": 2}}}}}}}}}}, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.grid_size": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 4}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2}}}}, "d": {"docs": {"models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.sensitivity_sd_values": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 3}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.save_timeseries": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.pcf_sample_rate": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.timeseries_subsample": {"tf": 1}}, "df": 1}}}}}}}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.project_root": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}, "scripts.experiments.main": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 20}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}}, "df": 2}}}}}, "p": {"docs": {"models.CA.PP": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.synchronous": {"tf": 1}, "models.CA.PP.directed_hunting": {"tf": 1}, "models.CA.PP.species_names": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}}, "df": 8, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 6}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}}, "df": 10}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}}, "df": 4}}}}}}, "o": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.project_root": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "f": {"docs": {"models.config.Config.collect_pcf": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 5, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.perturbation_magnitude": {"tf": 1}}, "df": 1}}}}}}}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"1": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}, "2": {"docs": {"models.config.PHASE2_CONFIG": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}, "3": {"docs": {"models.config.PHASE3_CONFIG": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}}, "df": 2}, "4": {"docs": {"models.config.PHASE4_CONFIG": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2}, "5": {"docs": {"models.config.PHASE5_CONFIG": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2}, "docs": {"models.config.PHASE_CONFIGS": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 3}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.count_populations": {"tf": 1}}, "df": 1}}}}}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.generator": {"tf": 1}}, "df": 1}}, "e": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 7}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.grid": {"tf": 1}, "models.config.Config.grid_size": {"tf": 1}, "models.config.Config.grid_sizes": {"tf": 1}}, "df": 3}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}}, "df": 4}}}}}}, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}}, "df": 4, "d": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.with_evolution": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}}}}, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments": {"tf": 1}, "scripts.experiments.project_root": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}, "scripts.experiments.main": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 19}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 4}}}}}, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.PP.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}}, "df": 4}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"models.CA.PP.update_async": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1}}}}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}}, "df": 5, "s": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.pcf_n_bins": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 2}}}}}}}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.prey_death_offsets": {"tf": 1}}, "df": 1}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.rows": {"tf": 1}, "models.numba_optimized.PPKernel.cols": {"tf": 1}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 16}}}}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}}, "df": 4}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.with_evolution": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.save_timeseries": {"tf": 1}, "models.config.Config.timeseries_subsample": {"tf": 1}}, "df": 2}}}}}}}}}}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 5}}}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.n_jobs": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 2}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}}, "df": 3}}}}}}}}}, "annotation": {"root": {"docs": {"models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.densities": {"tf": 1.4142135623730951}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}, "models.CA.CA.species_names": {"tf": 1.4142135623730951}, "models.CA.CA.neighborhood": {"tf": 1}, "models.CA.CA.generator": {"tf": 1}, "models.CA.CA.grid": {"tf": 1}, "models.CA.PP.synchronous": {"tf": 1}, "models.CA.PP.directed_hunting": {"tf": 1}, "models.config.Config.grid_size": {"tf": 1}, "models.config.Config.densities": {"tf": 1}, "models.config.Config.grid_sizes": {"tf": 1.4142135623730951}, "models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1.4142135623730951}, "models.config.Config.predator_death_values": {"tf": 1.4142135623730951}, "models.config.Config.prey_death_offsets": {"tf": 1.4142135623730951}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.with_evolution": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1.4142135623730951}, "models.config.Config.synchronous": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1.4142135623730951}, "models.config.Config.save_timeseries": {"tf": 1}, "models.config.Config.timeseries_subsample": {"tf": 1}, "models.config.Config.collect_pcf": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}, "models.config.Config.perturbation_magnitude": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}}, "df": 47, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.config.Config.grid_size": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.n_prey_death": {"tf": 1}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.warmup_steps": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.Config.timeseries_subsample": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}}, "df": 12}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.densities": {"tf": 1}, "models.config.Config.densities": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.prey_death_offsets": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}}, "df": 7}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.species_names": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.grid_sizes": {"tf": 1}}, "df": 1}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.directed_hunting_values": {"tf": 1}}, "df": 1}}}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}}, "df": 2}}}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.params": {"tf": 1}, "models.CA.CA.cell_params": {"tf": 1}}, "df": 2}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.neighborhood": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.generator": {"tf": 1}, "models.CA.CA.grid": {"tf": 1}}, "df": 2}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.grid": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.generator": {"tf": 1}}, "df": 1}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.generator": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.PP.synchronous": {"tf": 1}, "models.CA.PP.directed_hunting": {"tf": 1}, "models.config.Config.with_evolution": {"tf": 1}, "models.config.Config.synchronous": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.save_timeseries": {"tf": 1}, "models.config.Config.collect_pcf": {"tf": 1}}, "df": 7}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.densities": {"tf": 1}, "models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}, "models.config.Config.perturbation_magnitude": {"tf": 1}}, "df": 15}}}}}}}, "default_value": {"root": {"0": {"0": {"2": {"docs": {"models.config.Config.min_density_for_analysis": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "docs": {}, "df": 0}, "1": {"docs": {"models.config.Config.prey_death_offsets": {"tf": 1.4142135623730951}, "models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.3166247903554}}, "df": 7}, "2": {"docs": {"models.config.Config.prey_death_offsets": {"tf": 1.4142135623730951}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE2_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE3_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE4_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE5_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE_CONFIGS": {"tf": 3.872983346207417}}, "df": 8}, "5": {"docs": {"models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 2}, "models.config.PHASE2_CONFIG": {"tf": 2}, "models.config.PHASE3_CONFIG": {"tf": 2}, "models.config.PHASE4_CONFIG": {"tf": 2}, "models.config.PHASE5_CONFIG": {"tf": 2}, "models.config.PHASE_CONFIGS": {"tf": 4.47213595499958}}, "df": 10}, "9": {"6": {"3": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 1}}, "df": 2}, "docs": {}, "df": 0}, "7": {"3": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 1}}, "df": 2}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"models.config.Config.densities": {"tf": 1.4142135623730951}, "models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.prey_death": {"tf": 1}, "models.config.Config.predator_birth": {"tf": 1}, "models.config.Config.predator_death": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.critical_prey_death": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1.7320508075688772}, "models.config.Config.predator_birth_values": {"tf": 2}, "models.config.Config.predator_death_values": {"tf": 2}, "models.config.Config.prey_death_offsets": {"tf": 2.449489742783178}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_min": {"tf": 1.4142135623730951}, "models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 2.23606797749979}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.min_density_for_analysis": {"tf": 1}, "models.config.Config.perturbation_magnitude": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 6.082762530298219}, "models.config.PHASE2_CONFIG": {"tf": 6.164414002968976}, "models.config.PHASE3_CONFIG": {"tf": 6.164414002968976}, "models.config.PHASE4_CONFIG": {"tf": 6.164414002968976}, "models.config.PHASE5_CONFIG": {"tf": 6.164414002968976}, "models.config.PHASE_CONFIGS": {"tf": 13.74772708486752}}, "df": 25}, "1": {"0": {"0": {"0": {"0": {"docs": {"models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 1}}, "df": 2}, "docs": {"models.config.Config.grid_size": {"tf": 1}, "models.config.Config.grid_sizes": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 2}, "models.config.PHASE2_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE3_CONFIG": {"tf": 2}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 3.605551275463989}}, "df": 8}, "docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "docs": {"models.config.Config.timeseries_subsample": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 2.8284271247461903}}, "df": 7}, "5": {"docs": {"models.config.Config.densities": {"tf": 1}, "models.config.Config.n_prey_birth": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.n_replicates": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE2_CONFIG": {"tf": 2}, "models.config.PHASE3_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE4_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE5_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE_CONFIGS": {"tf": 4.898979485566356}}, "df": 12}, "docs": {"models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.evolve_sd": {"tf": 1}, "models.config.Config.evolve_max": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.Config.perturbation_magnitude": {"tf": 1}, "models.config.Config.n_jobs": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE2_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE3_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE4_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE5_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE_CONFIGS": {"tf": 5.5677643628300215}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 13}, "2": {"0": {"docs": {"models.config.Config.pcf_max_distance": {"tf": 1}, "models.config.Config.pcf_n_bins": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.4641016151377544}}, "df": 8}, "5": {"0": {"0": {"docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 2.6457513110645907}}, "df": 7}, "docs": {"models.config.Config.predator_birth_values": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "docs": {"models.config.Config.prey_birth": {"tf": 1}, "models.config.Config.critical_prey_birth": {"tf": 1}, "models.config.Config.prey_death_range": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.Config.predator_death_values": {"tf": 1}, "models.config.Config.sensitivity_sd_values": {"tf": 1}, "models.config.Config.pcf_sample_rate": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE2_CONFIG": {"tf": 2.8284271247461903}, "models.config.PHASE3_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE4_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE5_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE_CONFIGS": {"tf": 5.916079783099616}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 14}, "3": {"0": {"0": {"docs": {"models.config.Config.warmup_steps": {"tf": 1}}, "df": 1}, "docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 1}}, "df": 2}, "docs": {"models.config.Config.densities": {"tf": 1}, "models.config.Config.predator_birth_values": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.3166247903554}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 9}, "4": {"docs": {"models.config.PHASE_CONFIGS": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 2}, "5": {"0": {"0": {"docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.config.Config.measurement_steps": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE5_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE_CONFIGS": {"tf": 3}}, "df": 8}, "docs": {"models.config.Config.grid_sizes": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "docs": {"models.config.Config.n_prey_death": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 7}, "8": {"docs": {"models.config.Config.predator_birth": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "9": {"4": {"7": {"docs": {"models.config.Config.critical_prey_death": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 7}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"models.CA.logger": {"tf": 1.4142135623730951}, "models.config.Config.densities": {"tf": 1.4142135623730951}, "models.config.Config.grid_sizes": {"tf": 1.4142135623730951}, "models.config.Config.prey_death_range": {"tf": 1.4142135623730951}, "models.config.Config.predator_birth_values": {"tf": 1.4142135623730951}, "models.config.Config.predator_death_values": {"tf": 1.4142135623730951}, "models.config.Config.prey_death_offsets": {"tf": 1.4142135623730951}, "models.config.Config.sensitivity_sd_values": {"tf": 1.4142135623730951}, "models.config.Config.directed_hunting_values": {"tf": 1.4142135623730951}, "models.config.Config.n_jobs": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 17, "l": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.logger": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 2.23606797749979}}, "df": 2}, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.logger": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.logger": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {"models.CA.logger": {"tf": 1}}, "df": 1}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.logger": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}, "g": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.logger": {"tf": 1}, "scripts.experiments.PHASE_RUNNERS": {"tf": 2.23606797749979}}, "df": 2}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.with_evolution": {"tf": 1}, "models.config.Config.synchronous": {"tf": 1}, "models.config.Config.directed_hunting": {"tf": 1}, "models.config.Config.directed_hunting_values": {"tf": 1}, "models.config.Config.save_timeseries": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE2_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE3_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE4_CONFIG": {"tf": 2.449489742783178}, "models.config.PHASE5_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE_CONFIGS": {"tf": 5.196152422706632}}, "df": 11}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 2.23606797749979}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.directed_hunting_values": {"tf": 1}, "models.config.Config.collect_pcf": {"tf": 1}, "models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 2.8284271247461903}}, "df": 8}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}}}}}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}}}, "d": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}, "y": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE2_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE3_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE4_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE5_CONFIG": {"tf": 2.6457513110645907}, "models.config.PHASE_CONFIGS": {"tf": 5.916079783099616}}, "df": 6}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2.8284271247461903}, "models.config.PHASE2_CONFIG": {"tf": 2.8284271247461903}, "models.config.PHASE3_CONFIG": {"tf": 2.8284271247461903}, "models.config.PHASE4_CONFIG": {"tf": 2.8284271247461903}, "models.config.PHASE5_CONFIG": {"tf": 2.8284271247461903}, "models.config.PHASE_CONFIGS": {"tf": 6.324555320336759}}, "df": 6}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2}, "models.config.PHASE2_CONFIG": {"tf": 2}, "models.config.PHASE3_CONFIG": {"tf": 2}, "models.config.PHASE4_CONFIG": {"tf": 2}, "models.config.PHASE5_CONFIG": {"tf": 2}, "models.config.PHASE_CONFIGS": {"tf": 4.47213595499958}}, "df": 6}}}}}}}, "c": {"docs": {}, "df": 0, "f": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2}, "models.config.PHASE2_CONFIG": {"tf": 2}, "models.config.PHASE3_CONFIG": {"tf": 2}, "models.config.PHASE4_CONFIG": {"tf": 2}, "models.config.PHASE5_CONFIG": {"tf": 2}, "models.config.PHASE_CONFIGS": {"tf": 4.47213595499958}}, "df": 6}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"1": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}, "2": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}, "3": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}, "4": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}, "5": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE2_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE3_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE4_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE5_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE_CONFIGS": {"tf": 5}}, "df": 6}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.PHASE_RUNNERS": {"tf": 2.23606797749979}}, "df": 1}}}, "n": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE2_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE3_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE4_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE5_CONFIG": {"tf": 2.23606797749979}, "models.config.PHASE_CONFIGS": {"tf": 5}}, "df": 6}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 2}, "models.config.PHASE2_CONFIG": {"tf": 2}, "models.config.PHASE3_CONFIG": {"tf": 2}, "models.config.PHASE4_CONFIG": {"tf": 2}, "models.config.PHASE5_CONFIG": {"tf": 2}, "models.config.PHASE_CONFIGS": {"tf": 4.47213595499958}}, "df": 6}}}}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.project_root": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE2_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE3_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE4_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE5_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE_CONFIGS": {"tf": 3.872983346207417}}, "df": 6}}}}}}, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE2_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE3_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE4_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE5_CONFIG": {"tf": 1.4142135623730951}, "models.config.PHASE_CONFIGS": {"tf": 3.1622776601683795}}, "df": 6}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {"models.config.PHASE1_CONFIG": {"tf": 1}, "models.config.PHASE2_CONFIG": {"tf": 1}, "models.config.PHASE3_CONFIG": {"tf": 1}, "models.config.PHASE4_CONFIG": {"tf": 1}, "models.config.PHASE5_CONFIG": {"tf": 1}, "models.config.PHASE_CONFIGS": {"tf": 2.23606797749979}}, "df": 6}}}}}}, "signature": {"root": {"0": {"0": {"1": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}, "2": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "1": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 1}, "2": {"docs": {"models.config.Config.__init__": {"tf": 1.7320508075688772}}, "df": 1}, "5": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.__init__": {"tf": 2}}, "df": 2}, "docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1.4142135623730951}, "models.config.Config.__init__": {"tf": 6.164414002968976}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}}, "df": 4}, "1": {"0": {"0": {"0": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 4}, "docs": {"models.CA.PP.__init__": {"tf": 1.4142135623730951}, "models.config.Config.__init__": {"tf": 1}}, "df": 2}, "5": {"docs": {"models.config.Config.__init__": {"tf": 2.449489742783178}}, "df": 1}, "docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 2.449489742783178}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}}, "df": 3}, "2": {"0": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 3}, "5": {"0": {"0": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 2.6457513110645907}}, "df": 2}, "3": {"0": {"0": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "2": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}, "9": {"docs": {"models.CA.PP.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}}, "df": 5}, "docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 1}, "5": {"0": {"0": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}, "docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "8": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "9": {"4": {"7": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"models.CA.CA.__init__": {"tf": 10.488088481701515}, "models.CA.CA.validate": {"tf": 3.4641016151377544}, "models.CA.CA.evolve": {"tf": 9.848857801796104}, "models.CA.CA.update": {"tf": 3.4641016151377544}, "models.CA.CA.run": {"tf": 7.874007874011811}, "models.CA.PP.__init__": {"tf": 13.74772708486752}, "models.CA.PP.validate": {"tf": 3.4641016151377544}, "models.CA.PP.update_async": {"tf": 3.4641016151377544}, "models.CA.PP.update": {"tf": 3.4641016151377544}, "models.config.Config.__init__": {"tf": 28.071337695236398}, "models.config.Config.get_prey_births": {"tf": 4}, "models.config.Config.get_prey_deaths": {"tf": 4}, "models.config.Config.get_warmup_steps": {"tf": 4.47213595499958}, "models.config.Config.get_measurement_steps": {"tf": 4.47213595499958}, "models.config.Config.estimate_runtime": {"tf": 5.0990195135927845}, "models.config.get_phase_config": {"tf": 4.898979485566356}, "models.numba_optimized.set_numba_seed": {"tf": 4}, "models.numba_optimized.PPKernel.__init__": {"tf": 7.3484692283495345}, "models.numba_optimized.PPKernel.update": {"tf": 11.789826122551595}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 7.14142842854285}, "models.numba_optimized.detect_clusters_fast": {"tf": 8.306623862918075}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 6.6332495807108}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 10.723805294763608}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 9.327379053088816}, "models.numba_optimized.warmup_numba_kernels": {"tf": 5.830951894845301}, "models.numba_optimized.benchmark_kernels": {"tf": 5.830951894845301}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 5.830951894845301}, "scripts.experiments.generate_unique_seed": {"tf": 4.898979485566356}, "scripts.experiments.count_populations": {"tf": 5.916079783099616}, "scripts.experiments.get_evolved_stats": {"tf": 4.47213595499958}, "scripts.experiments.average_pcfs": {"tf": 8.366600265340756}, "scripts.experiments.save_results_jsonl": {"tf": 5.5677643628300215}, "scripts.experiments.save_results_npz": {"tf": 5.5677643628300215}, "scripts.experiments.load_results_jsonl": {"tf": 5}, "scripts.experiments.run_single_simulation": {"tf": 10.677078252031311}, "scripts.experiments.run_phase1": {"tf": 7.483314773547883}, "scripts.experiments.run_phase2": {"tf": 7.483314773547883}, "scripts.experiments.run_phase3": {"tf": 7.483314773547883}, "scripts.experiments.run_phase4": {"tf": 7.483314773547883}, "scripts.experiments.run_phase5": {"tf": 7.483314773547883}, "scripts.experiments.main": {"tf": 2.6457513110645907}, "scripts.experiments.warmup_numba_kernels": {"tf": 4}, "scripts.experiments.set_numba_seed": {"tf": 3.1622776601683795}}, "df": 43, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}}, "df": 3}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 2}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 2}}}}, "i": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1.7320508075688772}, "models.config.Config.__init__": {"tf": 3.1622776601683795}, "models.config.Config.get_warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 23}, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}}, "df": 3}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}}}}}, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {"models.config.get_phase_config": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase3": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 7}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 6}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}}, "df": 3}}}, "y": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.__init__": {"tf": 2.6457513110645907}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 3}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 16}}, "r": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 4}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}}}}}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 2.8284271247461903}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 8}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 3}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 4.58257569495584}, "models.numba_optimized.PPKernel.update": {"tf": 2.6457513110645907}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 2}}, "df": 8}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 2.23606797749979}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 6}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}, "n": {"docs": {"models.config.Config.__init__": {"tf": 2.23606797749979}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 6, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 6}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1.7320508075688772}, "models.CA.PP.__init__": {"tf": 1.7320508075688772}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 12}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 2.23606797749979}}, "df": 10}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 2.23606797749979}}, "df": 10}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1.7320508075688772}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 10}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}}, "df": 2}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.set_numba_seed": {"tf": 1}}, "df": 5}}, "l": {"docs": {}, "df": 0, "f": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 14}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 4}}}}}}, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 3}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}}, "df": 2}}}}}}}}}}, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.warmup_numba_kernels": {"tf": 1}}, "df": 6, "s": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 3}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 8, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 8}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.__init__": {"tf": 2.8284271247461903}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 3}, "d": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}}, "df": 1, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.__init__": {"tf": 2}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}, "c": {"docs": {}, "df": 0, "f": {"docs": {"models.config.Config.__init__": {"tf": 2}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 3}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models.config.get_phase_config": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 6}}}}}}}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 3}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 5}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 5}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.get_phase_config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}}, "df": 1, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 2}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 4}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.__init__": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}}}, "l": {"docs": {"models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 10}}}, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase3": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 5}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.PP.__init__": {"tf": 1.4142135623730951}, "models.config.Config.__init__": {"tf": 2.449489742783178}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 7}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.__init__": {"tf": 2.23606797749979}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 3}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}}}}, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.PP.__init__": {"tf": 1}, "models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.__init__": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 4}}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 12}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config.__init__": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}}}, "j": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.__init__": {"tf": 1}}, "df": 1}}}}, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}}}}}}, "bases": {"root": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}}, "doc": {"root": {"0": {"0": {"0": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}, "1": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}, "2": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "1": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2}, "5": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}}, "df": 2}, "docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 4.358898943540674}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 2}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}}, "df": 16}, "1": {"0": {"0": {"0": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"models": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 5}, "docs": {"models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 2}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 3}, "2": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1}, "3": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}, "5": {"0": {"docs": {"models.config": {"tf": 1}}, "df": 1}, "docs": {"models.config.Config": {"tf": 1.7320508075688772}}, "df": 1}, "7": {"0": {"0": {"0": {"0": {"0": {"0": {"0": {"0": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 2.449489742783178}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 2.23606797749979}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 2.449489742783178}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 2.8284271247461903}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 24, "d": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 4}, "/": {"4": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}}, "2": {"0": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 4}, "5": {"0": {"0": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}, "docs": {"models.config.Config": {"tf": 1}}, "df": 1}, "6": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2}, "docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}, "docs": {"models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 2}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 15, "d": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}}, "df": 9}}, "3": {"0": {"0": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}, "docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 3}, "2": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2}, "4": {"3": {"2": {"5": {"7": {"1": {"2": {"1": {"7": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "9": {"6": {"0": {"0": {"1": {"3": {"5": {"8": {"3": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2}, "scripts.experiments.generate_unique_seed": {"tf": 2}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}}, "df": 4}, "docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 5, "f": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}, "4": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7, "d": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 2}}, "5": {"0": {"0": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}, "docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}, "docs": {"models.config.Config": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 4}, "6": {"docs": {"models.config.Config": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}}, "df": 2}, "8": {"8": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}, "docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 7}, "9": {"4": {"7": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "5": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}, "9": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}, "docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1}, "docs": {"models": {"tf": 5.5677643628300215}, "models.CA": {"tf": 1.7320508075688772}, "models.CA.logger": {"tf": 1.7320508075688772}, "models.CA.CA": {"tf": 7}, "models.CA.CA.__init__": {"tf": 7.483314773547883}, "models.CA.CA.rows": {"tf": 1.7320508075688772}, "models.CA.CA.cols": {"tf": 1.7320508075688772}, "models.CA.CA.densities": {"tf": 2}, "models.CA.CA.n_species": {"tf": 1.7320508075688772}, "models.CA.CA.params": {"tf": 1.7320508075688772}, "models.CA.CA.cell_params": {"tf": 1.7320508075688772}, "models.CA.CA.species_names": {"tf": 1.7320508075688772}, "models.CA.CA.neighborhood": {"tf": 1.7320508075688772}, "models.CA.CA.generator": {"tf": 1.7320508075688772}, "models.CA.CA.grid": {"tf": 1.7320508075688772}, "models.CA.CA.validate": {"tf": 4.123105625617661}, "models.CA.CA.evolve": {"tf": 8.12403840463596}, "models.CA.CA.update": {"tf": 5.656854249492381}, "models.CA.CA.run": {"tf": 6.928203230275509}, "models.CA.PP": {"tf": 9.695359714832659}, "models.CA.PP.__init__": {"tf": 1.7320508075688772}, "models.CA.PP.synchronous": {"tf": 1.7320508075688772}, "models.CA.PP.directed_hunting": {"tf": 1.7320508075688772}, "models.CA.PP.species_names": {"tf": 1.7320508075688772}, "models.CA.PP.validate": {"tf": 4.69041575982343}, "models.CA.PP.update_async": {"tf": 2.8284271247461903}, "models.CA.PP.update": {"tf": 1.7320508075688772}, "models.config": {"tf": 4}, "models.config.Config": {"tf": 15.0996688705415}, "models.config.Config.__init__": {"tf": 1.7320508075688772}, "models.config.Config.grid_size": {"tf": 1.7320508075688772}, "models.config.Config.densities": {"tf": 1.7320508075688772}, "models.config.Config.grid_sizes": {"tf": 1.7320508075688772}, "models.config.Config.prey_birth": {"tf": 1.7320508075688772}, "models.config.Config.prey_death": {"tf": 1.7320508075688772}, "models.config.Config.predator_birth": {"tf": 1.7320508075688772}, "models.config.Config.predator_death": {"tf": 1.7320508075688772}, "models.config.Config.critical_prey_birth": {"tf": 1.7320508075688772}, "models.config.Config.critical_prey_death": {"tf": 1.7320508075688772}, "models.config.Config.prey_death_range": {"tf": 1.7320508075688772}, "models.config.Config.n_prey_birth": {"tf": 1.7320508075688772}, "models.config.Config.n_prey_death": {"tf": 1.7320508075688772}, "models.config.Config.predator_birth_values": {"tf": 1.7320508075688772}, "models.config.Config.predator_death_values": {"tf": 1.7320508075688772}, "models.config.Config.prey_death_offsets": {"tf": 1.7320508075688772}, "models.config.Config.n_replicates": {"tf": 1.7320508075688772}, "models.config.Config.warmup_steps": {"tf": 1.7320508075688772}, "models.config.Config.measurement_steps": {"tf": 1.7320508075688772}, "models.config.Config.with_evolution": {"tf": 1.7320508075688772}, "models.config.Config.evolve_sd": {"tf": 1.7320508075688772}, "models.config.Config.evolve_min": {"tf": 1.7320508075688772}, "models.config.Config.evolve_max": {"tf": 1.7320508075688772}, "models.config.Config.sensitivity_sd_values": {"tf": 1.7320508075688772}, "models.config.Config.synchronous": {"tf": 1.7320508075688772}, "models.config.Config.directed_hunting": {"tf": 1.7320508075688772}, "models.config.Config.directed_hunting_values": {"tf": 1.7320508075688772}, "models.config.Config.save_timeseries": {"tf": 1.7320508075688772}, "models.config.Config.timeseries_subsample": {"tf": 1.7320508075688772}, "models.config.Config.collect_pcf": {"tf": 1.7320508075688772}, "models.config.Config.pcf_sample_rate": {"tf": 1.7320508075688772}, "models.config.Config.pcf_max_distance": {"tf": 1.7320508075688772}, "models.config.Config.pcf_n_bins": {"tf": 1.7320508075688772}, "models.config.Config.min_density_for_analysis": {"tf": 1.7320508075688772}, "models.config.Config.perturbation_magnitude": {"tf": 1.7320508075688772}, "models.config.Config.n_jobs": {"tf": 1.7320508075688772}, "models.config.Config.get_prey_births": {"tf": 4.242640687119285}, "models.config.Config.get_prey_deaths": {"tf": 4.242640687119285}, "models.config.Config.get_warmup_steps": {"tf": 5.0990195135927845}, "models.config.Config.get_measurement_steps": {"tf": 5.385164807134504}, "models.config.Config.estimate_runtime": {"tf": 5.385164807134504}, "models.config.PHASE1_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE2_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE3_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE4_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE5_CONFIG": {"tf": 1.7320508075688772}, "models.config.PHASE_CONFIGS": {"tf": 1.7320508075688772}, "models.config.get_phase_config": {"tf": 6.082762530298219}, "models.numba_optimized": {"tf": 5}, "models.numba_optimized.set_numba_seed": {"tf": 6}, "models.numba_optimized.PPKernel": {"tf": 8}, "models.numba_optimized.PPKernel.__init__": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.rows": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.cols": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.directed_hunting": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.update": {"tf": 8.94427190999916}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 12.449899597988733}, "models.numba_optimized.detect_clusters_fast": {"tf": 13.341664064126334}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 13.820274961085254}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 9.1104335791443}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 7.874007874011811}, "models.numba_optimized.warmup_numba_kernels": {"tf": 6.855654600401044}, "models.numba_optimized.benchmark_kernels": {"tf": 7.681145747868608}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 7.280109889280518}, "scripts": {"tf": 8.18535277187245}, "scripts.experiments": {"tf": 3.605551275463989}, "scripts.experiments.project_root": {"tf": 1.7320508075688772}, "scripts.experiments.generate_unique_seed": {"tf": 10.862780491200215}, "scripts.experiments.count_populations": {"tf": 10.770329614269007}, "scripts.experiments.get_evolved_stats": {"tf": 11.269427669584644}, "scripts.experiments.average_pcfs": {"tf": 14.2828568570857}, "scripts.experiments.save_results_jsonl": {"tf": 11.445523142259598}, "scripts.experiments.save_results_npz": {"tf": 11.090536506409418}, "scripts.experiments.load_results_jsonl": {"tf": 9.848857801796104}, "scripts.experiments.run_single_simulation": {"tf": 10.770329614269007}, "scripts.experiments.run_phase1": {"tf": 7.874007874011811}, "scripts.experiments.run_phase2": {"tf": 7.681145747868608}, "scripts.experiments.run_phase3": {"tf": 3.4641016151377544}, "scripts.experiments.run_phase4": {"tf": 7.810249675906654}, "scripts.experiments.run_phase5": {"tf": 6.855654600401044}, "scripts.experiments.PHASE_RUNNERS": {"tf": 1.7320508075688772}, "scripts.experiments.main": {"tf": 3.872983346207417}, "scripts.experiments.warmup_numba_kernels": {"tf": 1.7320508075688772}, "scripts.experiments.set_numba_seed": {"tf": 1.7320508075688772}}, "df": 113, "m": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 4, "l": {"docs": {"models.CA.PP": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 2.23606797749979}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 4, "s": {"docs": {"models": {"tf": 1.4142135623730951}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "y": {"docs": {"models.config": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}}, "df": 7}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.config.Config": {"tf": 1}}, "df": 2}}}}}}, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 4}}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {"models.CA.CA": {"tf": 1}, "scripts": {"tf": 1}}, "df": 2, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 6}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 6}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.CA.validate": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.validate": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}}, "df": 3, "s": {"docs": {"models.CA.PP": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}}, "df": 3}, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}}}}, "x": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 9, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 6}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 2}}}}}, "s": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3, "s": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.update": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 6, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.7320508075688772}}, "df": 2, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5, "s": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "s": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 2.23606797749979}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 5, "s": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}, "y": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 3.4641016151377544}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.validate": {"tf": 1.7320508075688772}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 2}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 2.23606797749979}}, "df": 17, "s": {"docs": {"models.CA.CA": {"tf": 2}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 2}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 36}}}}}, "s": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.PP": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 2}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 6}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 6, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1}, "scripts": {"tf": 1}}, "df": 2}}}}}}}}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 2.449489742783178}, "scripts.experiments.save_results_npz": {"tf": 2.23606797749979}, "scripts.experiments.load_results_jsonl": {"tf": 2.449489742783178}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.config": {"tf": 1.4142135623730951}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 7, "d": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}}, "df": 2, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 2.449489742783178}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config": {"tf": 1}, "models.config.Config": {"tf": 3.3166247903554}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 2.23606797749979}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1}}, "df": 20, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {"models": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "s": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}, "y": {"docs": {"models": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 2.6457513110645907}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1.4142135623730951}, "models.config": {"tf": 1}, "models.config.Config": {"tf": 4.123105625617661}, "models.config.Config.get_prey_births": {"tf": 1.7320508075688772}, "models.config.Config.get_prey_deaths": {"tf": 1.7320508075688772}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 2.449489742783178}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 2.6457513110645907}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 2.6457513110645907}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase3": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 26}, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "x": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts": {"tf": 1}}, "df": 4}, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}}}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 2}, "scripts.experiments.run_single_simulation": {"tf": 2}}, "df": 4}}}}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4}}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}}, "df": 6, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 5, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 6}}}}, "s": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}, "t": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 2}}}}}}}}}}, "y": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 2}}, "df": 2, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 2}}, "df": 5}}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 6, "s": {"docs": {"scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1}}, "df": 6, "s": {"docs": {"models.config.Config": {"tf": 1.7320508075688772}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 4}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}, "t": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 3}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"1": {"docs": {"models.config": {"tf": 1.7320508075688772}}, "df": 1}, "2": {"docs": {"models.config": {"tf": 1}}, "df": 1}, "docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 2.449489742783178}, "models.config.get_phase_config": {"tf": 2.449489742783178}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments": {"tf": 3.1622776601683795}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 2}, "scripts.experiments.run_phase5": {"tf": 2}, "scripts.experiments.main": {"tf": 1.7320508075688772}}, "df": 13, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1.4142135623730951}}, "df": 5}}}}}, "c": {"docs": {}, "df": 0, "f": {"docs": {"models.config.Config": {"tf": 2.23606797749979}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 2.6457513110645907}, "scripts.experiments.run_single_simulation": {"tf": 1.7320508075688772}}, "df": 7, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 6}}}}, "t": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}}, "df": 1, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}, "models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.update": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1}}, "df": 31}}, "e": {"docs": {"models": {"tf": 1.7320508075688772}, "models.CA.CA": {"tf": 1.7320508075688772}, "models.CA.CA.__init__": {"tf": 2}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.validate": {"tf": 1.7320508075688772}, "models.CA.CA.evolve": {"tf": 4.47213595499958}, "models.CA.CA.update": {"tf": 2.23606797749979}, "models.CA.CA.run": {"tf": 2.8284271247461903}, "models.CA.PP": {"tf": 2.23606797749979}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1.7320508075688772}, "models.CA.PP.update_async": {"tf": 2}, "models.CA.PP.update": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 3.1622776601683795}, "models.config.Config.get_warmup_steps": {"tf": 2}, "models.config.Config.get_measurement_steps": {"tf": 2.6457513110645907}, "models.config.Config.estimate_runtime": {"tf": 1.7320508075688772}, "models.config.get_phase_config": {"tf": 2.449489742783178}, "models.numba_optimized.set_numba_seed": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel": {"tf": 2.8284271247461903}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2.6457513110645907}, "models.numba_optimized.detect_clusters_fast": {"tf": 2.23606797749979}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 3}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 4.47213595499958}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 3.1622776601683795}, "models.numba_optimized.warmup_numba_kernels": {"tf": 2.6457513110645907}, "models.numba_optimized.benchmark_kernels": {"tf": 3.605551275463989}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 2.6457513110645907}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments.generate_unique_seed": {"tf": 2.8284271247461903}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.get_evolved_stats": {"tf": 2.449489742783178}, "scripts.experiments.average_pcfs": {"tf": 3.1622776601683795}, "scripts.experiments.save_results_jsonl": {"tf": 2.6457513110645907}, "scripts.experiments.save_results_npz": {"tf": 2.6457513110645907}, "scripts.experiments.load_results_jsonl": {"tf": 2.23606797749979}, "scripts.experiments.run_single_simulation": {"tf": 3.3166247903554}, "scripts.experiments.run_phase1": {"tf": 2.449489742783178}, "scripts.experiments.run_phase2": {"tf": 2.449489742783178}, "scripts.experiments.run_phase4": {"tf": 2.8284271247461903}, "scripts.experiments.run_phase5": {"tf": 3}, "scripts.experiments.main": {"tf": 2.23606797749979}}, "df": 44, "i": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 3}}, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 3}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}, "m": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}, "s": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.validate": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP.validate": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1.7320508075688772}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 8}, "n": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 3}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1, "s": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 2}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {"models.config.get_phase_config": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 2}}}}}}}}}, "o": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2.449489742783178}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1.7320508075688772}, "models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.7320508075688772}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2.23606797749979}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.7320508075688772}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 2}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_npz": {"tf": 2}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.7320508075688772}, "scripts.experiments.main": {"tf": 1}}, "df": 34, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}}, "df": 5}}}, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 2}, "models.numba_optimized.PPKernel": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 3}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 3}}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}}, "df": 2}}}}}}, "e": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 3, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.PP": {"tf": 1}}, "df": 2}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.densities": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 2.449489742783178}}, "df": 4}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 4, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}, "s": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.update": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "scripts": {"tf": 1}}, "df": 3}}}}}}}}, "c": {"docs": {}, "df": 0, "k": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4}}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP": {"tf": 1.7320508075688772}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 8}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 9, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "d": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 10}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 2}}}}, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {"models.config": {"tf": 3.3166247903554}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 8, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.get_phase_config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 11, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.PP.update": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 2}}}}, "s": {"docs": {"models.config.get_phase_config": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}}, "df": 4}}}}}}}}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}}}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 2}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_phase2": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"models": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "scripts": {"tf": 1}}, "df": 3, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1.4142135623730951}}, "df": 2}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3, "s": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1}}}}}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 7}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}, "x": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 4}}}}}, "e": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1, "d": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 2}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}}}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1.7320508075688772}}, "df": 1}}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models": {"tf": 1}, "models.config.Config": {"tf": 1}}, "df": 2}}, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 3}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {"models": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 5}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 4}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 2.23606797749979}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 11, "s": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}}, "df": 1}}}}}}}}}, "p": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA": {"tf": 1.7320508075688772}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 1.7320508075688772}, "models.CA.PP.update_async": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 14, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {"models": {"tf": 1.4142135623730951}, "models.CA": {"tf": 1}, "models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 9}}}}, "s": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 2}}, "df": 7}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {"models": {"tf": 1.4142135623730951}, "models.CA.CA.validate": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}}, "df": 4, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}}}, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}}, "df": 2, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.get_warmup_steps": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 4, "s": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 4}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 3, "s": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1}}}}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}, "models.CA": {"tf": 1}, "models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.update": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 6}}, "m": {"docs": {}, "df": 0, "p": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2.449489742783178}, "models.numba_optimized.detect_clusters_fast": {"tf": 2.23606797749979}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 3}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}}, "df": 10, "s": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2.23606797749979}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 7}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 3}}}}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.PP.update_async": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 8, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"models.config": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"models.config": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2, "d": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 2.23606797749979}, "models.config.Config.get_measurement_steps": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase4": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 9, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}}, "df": 3}}}}}}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "g": {"docs": {"models.config": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 6}}, "p": {"docs": {}, "df": 0, "u": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "scripts": {"tf": 1}}, "df": 3}}}, "s": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1.7320508075688772}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3, "i": {"docs": {}, "df": 0, "m": {"docs": {"models": {"tf": 1}}, "df": 1, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.run": {"tf": 1.7320508075688772}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.CA.PP.update": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "scripts": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 2.23606797749979}, "scripts.experiments.run_phase1": {"tf": 2.23606797749979}, "scripts.experiments.run_phase2": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 2}, "scripts.experiments.main": {"tf": 1}}, "df": 28, "s": {"docs": {"models.CA.CA": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}}}}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 7}}}}, "z": {"docs": {}, "df": 0, "e": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 3}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.7320508075688772}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 2.23606797749979}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 19, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2.449489742783178}, "models.numba_optimized.detect_clusters_fast": {"tf": 2.6457513110645907}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 2}}, "df": 8}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 7}}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 13}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA": {"tf": 2}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.densities": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2.6457513110645907}, "models.CA.PP": {"tf": 1.7320508075688772}, "models.CA.PP.validate": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 2}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2}, "models.numba_optimized.detect_clusters_fast": {"tf": 2}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2.23606797749979}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 15}}, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 7, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3}}}, "y": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 3}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.PP": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}}}}}, "m": {"docs": {"models.CA.CA.__init__": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}}}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}}, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA": {"tf": 1}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 3, "s": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 6}}, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 5}}}}}}}, "p": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 4, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.validate": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}}, "df": 2, "s": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 7, "s": {"docs": {"models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}}, "df": 4}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 8}, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2.23606797749979}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 2.23606797749979}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 5}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 8}}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "k": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}}, "df": 7, "s": {"docs": {"models.CA.CA.run": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1.7320508075688772}, "models.config.Config.get_warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 5}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}}, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}, "e": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.numba_optimized.set_numba_seed": {"tf": 2}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 2.23606797749979}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 7, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3}}}, "l": {"docs": {}, "df": 0, "f": {"docs": {"models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}}, "df": 5}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 4, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}}}}, "s": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config": {"tf": 2}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4}}}}, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}, "d": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments": {"tf": 1}}, "df": 1}}}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 3}, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 11}}}}, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1.4142135623730951}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}}}}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}}, "df": 4, "d": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}}, "df": 3}, "s": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 2}}}, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 2}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4, "s": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}}, "df": 4}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 4}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 7}}}, "e": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3, "d": {"docs": {"models.config.Config.get_warmup_steps": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}}, "df": 1}}}}, "k": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}}, "df": 2}}}}, "c": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {"models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 5, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 7}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}, "r": {"docs": {"scripts.experiments.run_phase1": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase2": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase4": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase5": {"tf": 1.7320508075688772}}, "df": 4}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 5}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1}}, "df": 5}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 2}}, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}}, "df": 1}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1, "g": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 8}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1, "s": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 2}, "scripts.experiments.save_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_npz": {"tf": 1.7320508075688772}, "scripts.experiments.load_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase5": {"tf": 1.7320508075688772}}, "df": 12, "[": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 2}, "scripts.experiments.main": {"tf": 1}}, "df": 3, "a": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 3}}, "s": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}}, "df": 4, "s": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 4}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2.449489742783178}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 4}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 2}}}}}, "w": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}, "^": {"docs": {}, "df": 0, "z": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}, "f": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}}, "df": 3, "o": {"docs": {}, "df": 0, "r": {"docs": {"models": {"tf": 1.7320508075688772}, "models.CA.CA": {"tf": 2.23606797749979}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.densities": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2.6457513110645907}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1.7320508075688772}, "models.CA.PP": {"tf": 2}, "models.config": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 5.0990195135927845}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.7320508075688772}, "models.config.Config.estimate_runtime": {"tf": 1.4142135623730951}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized": {"tf": 1.7320508075688772}, "models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 2}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2.23606797749979}, "models.numba_optimized.detect_clusters_fast": {"tf": 2}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2.6457513110645907}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 2.6457513110645907}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 2}, "scripts.experiments.run_phase1": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 2.23606797749979}, "scripts.experiments.run_phase5": {"tf": 2}, "scripts.experiments.main": {"tf": 1}}, "df": 36, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 5}}, "r": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"models": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 14}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"models.CA.CA": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.densities": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}}, "df": 2, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}}}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 2}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.config.Config": {"tf": 3.872983346207417}, "models.numba_optimized.PPKernel.update": {"tf": 2.6457513110645907}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 2}}, "df": 7}}, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 2}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 6}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 2.23606797749979}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 6, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 2}}, "r": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 5}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 3}}, "d": {"docs": {"scripts.experiments": {"tf": 1}}, "df": 1}}, "x": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 2}, "scripts.experiments.save_results_npz": {"tf": 2.23606797749979}, "scripts.experiments.load_results_jsonl": {"tf": 2.449489742783178}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "s": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3}}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 3}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}}, "df": 3}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 20, "s": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 3}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 7}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2.23606797749979}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 2.449489742783178}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2.23606797749979}, "models.numba_optimized.detect_clusters_fast": {"tf": 2.449489742783178}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.warmup_numba_kernels": {"tf": 2}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 2.6457513110645907}, "scripts.experiments.count_populations": {"tf": 2}, "scripts.experiments.get_evolved_stats": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 2}, "scripts.experiments.save_results_npz": {"tf": 2}, "scripts.experiments.load_results_jsonl": {"tf": 2.23606797749979}, "scripts.experiments.run_single_simulation": {"tf": 2.449489742783178}, "scripts.experiments.run_phase1": {"tf": 2.8284271247461903}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 2.23606797749979}}, "df": 33, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"models": {"tf": 1.4142135623730951}, "models.CA.CA": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 3}, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA": {"tf": 1}, "models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized": {"tf": 1}}, "df": 7}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 2}}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}}}}}}}, "n": {"docs": {"models.CA.CA.update": {"tf": 1.4142135623730951}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 8, "d": {"docs": {"models": {"tf": 1}, "models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.validate": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.update": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1.4142135623730951}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 2}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.7320508075688772}, "scripts": {"tf": 2.23606797749979}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 3.1622776601683795}, "scripts.experiments.run_phase1": {"tf": 2.23606797749979}, "scripts.experiments.run_phase2": {"tf": 2.23606797749979}, "scripts.experiments.run_phase4": {"tf": 2}, "scripts.experiments.run_phase5": {"tf": 2}, "scripts.experiments.main": {"tf": 2}}, "df": 36}, "y": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.validate": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 4}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 2.6457513110645907}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 2}, "scripts.experiments": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 10}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}, "z": {"docs": {}, "df": 0, "e": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}}, "df": 2}}}}}}, "t": {"docs": {"models.CA.CA.run": {"tf": 2}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 9, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 4}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 2.23606797749979}}, "df": 12, "s": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.validate": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.7320508075688772}}, "df": 6}}}}, "e": {"docs": {"models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP.validate": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 10, "a": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}}}}}}}, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 15, "o": {"docs": {}, "df": 0, "w": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 2}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}}}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 6, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.update_async": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}}, "df": 2, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.get_phase_config": {"tf": 1}}, "df": 1}}}}}}}}}, "d": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1}}, "df": 1}}, "t": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.run": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 3}}}, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts": {"tf": 1}}, "df": 6}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 4}}}}}}}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}, "s": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 8}}}}}, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}}}}, "x": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1.4142135623730951}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 4}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 6}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 3, "s": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"models": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}}, "df": 4}}, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}}, "df": 8, "s": {"docs": {"models": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 10}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}}}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models": {"tf": 1}, "models.CA": {"tf": 1}, "models.CA.CA": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 7, "d": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP.update": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 10}}, "h": {"docs": {"scripts": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "c": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "s": {"docs": {"models": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 3}}}}}}, "y": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 13}, "e": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 14, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3, "s": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 4}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 2}}}}}, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}}, "df": 2, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1}}, "df": 2}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 3}}, "o": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.PP": {"tf": 2}, "models.config.Config": {"tf": 2.23606797749979}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 7}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 2}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1, "s": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 3.1622776601683795}, "models.config.Config.get_prey_births": {"tf": 2}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase3": {"tf": 1}}, "df": 6, "/": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}}, "n": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2, "s": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}}, "df": 3}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 2}}}}}}}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 8, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}}, "df": 2, "s": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 2.23606797749979}}, "df": 4}, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 9}}}}}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1.4142135623730951}}, "df": 11}}}, "e": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7, "s": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 4}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models": {"tf": 1}}, "df": 1, "s": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 10}}}}}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.n_species": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 2}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 2}}}}}}, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}}}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}}, "df": 4}}}, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}}, "df": 3, "d": {"docs": {"models.CA.PP.validate": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 5}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 7, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1.7320508075688772}}, "df": 3}}}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3}}, "n": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.densities": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.config": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}}, "df": 16}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.n_species": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}, "d": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 2}}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "y": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP.validate": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 6, "s": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 3}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}}, "df": 4, "s": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 4}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}}, "e": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 2, "d": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}}}}, "t": {"docs": {}, "df": 0, "c": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 3}}}, "r": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 3, "u": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments": {"tf": 2}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 8, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 4}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1, "s": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 8}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 4, "s": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1, "s": {"docs": {"models": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.rows": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 6}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1, "s": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_births": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_deaths": {"tf": 1.4142135623730951}}, "df": 5, "s": {"docs": {"models.CA.PP.validate": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3}}}, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.PP": {"tf": 1.7320508075688772}, "models.numba_optimized.set_numba_seed": {"tf": 2.449489742783178}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 2}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 10}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 6}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 3.1622776601683795}, "models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 2.23606797749979}}, "df": 4, "s": {"docs": {"models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.config.Config.get_prey_births": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_deaths": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 9}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.PP": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 2}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}}, "df": 4}}, "u": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}}, "df": 4}}}, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}}, "df": 1}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3, "s": {"docs": {"models.config": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1.7320508075688772}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}}, "df": 2}}}}, "s": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "h": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 30}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.get_phase_config": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"models.CA.PP.update_async": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 9, "s": {"docs": {"models.CA.CA.run": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_npz": {"tf": 2.23606797749979}, "scripts.experiments.load_results_jsonl": {"tf": 2}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 13, "/": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 3}}}}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"scripts": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 3}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 4}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}}}, "s": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}}}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"models": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.config.Config.get_measurement_steps": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 13, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}}, "df": 2}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.densities": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 6}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5}}}}}}}, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.update": {"tf": 1}}, "df": 2}, "d": {"docs": {"models.config": {"tf": 1.4142135623730951}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.PP": {"tf": 2.449489742783178}, "models.config.Config": {"tf": 5.830951894845301}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 2}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 16, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 5}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.PP": {"tf": 1.7320508075688772}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 3.7416573867739413}, "models.config.Config.get_prey_deaths": {"tf": 2}, "models.numba_optimized.PPKernel.update": {"tf": 2.23606797749979}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}}, "df": 9}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.PP.update_async": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}, "d": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 3}}}, "m": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 3, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 1}}, "df": 5}}}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}, "scripts": {"tf": 1}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 1}}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 15, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1.4142135623730951}}, "df": 3}}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1.7320508075688772}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 9}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7}}}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.n_species": {"tf": 1}}, "df": 1}}, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}}, "df": 3, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 2}}, "df": 2}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 4, "s": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}}}}}}}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1, "d": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.PP.update": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config.get_warmup_steps": {"tf": 1}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.CA.validate": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "scripts": {"tf": 1}}, "df": 4}}}}}}}}, "r": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 4, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA.run": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.PP": {"tf": 1.7320508075688772}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1.7320508075688772}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 2.23606797749979}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 8}}}}}}, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 2}}}}}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts": {"tf": 1}}, "df": 2}}}}}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 4}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 6}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"scripts.experiments": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}, "o": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 2}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 2}}}}}}}}}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.config": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 2.23606797749979}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 15, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {"models": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized": {"tf": 1.7320508075688772}, "models.numba_optimized.set_numba_seed": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 15}, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 2.23606797749979}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1.4142135623730951}, "models.numba_optimized.set_numba_seed": {"tf": 2}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 2}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 21}}}, "p": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.7320508075688772}}, "df": 5}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.count_populations": {"tf": 1.4142135623730951}, "scripts.experiments.average_pcfs": {"tf": 3}}, "df": 12, "z": {"docs": {"scripts.experiments.save_results_npz": {"tf": 2}}, "df": 1}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.CA": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 2}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 2.23606797749979}}, "df": 11}}}}}}, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 2, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.validate": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 9}}}}, "s": {"docs": {"models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}}, "df": 3}}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}}, "df": 7}}}}}, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}}}}, "w": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1}}, "df": 2}}}, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.7320508075688772}}, "df": 2}}, "o": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 3, "n": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 5, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 8}}, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}}, "df": 11, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 19}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}, "d": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}}}}}}}, "o": {"docs": {"models.numba_optimized": {"tf": 1.4142135623730951}}, "df": 1, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 4}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1.7320508075688772}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 2}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.7320508075688772}}, "df": 15, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 11, "e": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 2}, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 3}}}, "r": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 2}}, "df": 11, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 3}}}, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 4}}}}}, "e": {"docs": {"scripts.experiments.main": {"tf": 1}}, "df": 1, "d": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts.experiments.save_results_npz": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {"models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2.23606797749979}, "models.CA.CA.update": {"tf": 1.7320508075688772}, "models.CA.CA.run": {"tf": 2.23606797749979}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 3}, "models.config.Config.get_prey_births": {"tf": 1.4142135623730951}, "models.config.Config.get_prey_deaths": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 2}, "models.config.Config.estimate_runtime": {"tf": 1.4142135623730951}, "models.numba_optimized": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 3.1622776601683795}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 3.4641016151377544}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 2}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 2.449489742783178}, "scripts": {"tf": 1.7320508075688772}, "scripts.experiments.generate_unique_seed": {"tf": 2}, "scripts.experiments.count_populations": {"tf": 2.6457513110645907}, "scripts.experiments.get_evolved_stats": {"tf": 2.23606797749979}, "scripts.experiments.average_pcfs": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_npz": {"tf": 2}, "scripts.experiments.load_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 2.8284271247461903}, "scripts.experiments.run_phase1": {"tf": 2.23606797749979}, "scripts.experiments.run_phase2": {"tf": 2.449489742783178}, "scripts.experiments.run_phase4": {"tf": 2.449489742783178}, "scripts.experiments.run_phase5": {"tf": 2.449489742783178}}, "df": 38, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}}, "w": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 2}}, "u": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"scripts": {"tf": 1}, "scripts.experiments": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 9}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.update": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config.get_measurement_steps": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 4}}}}, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}}}}, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.PP": {"tf": 1.4142135623730951}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 9, "s": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2}}}}}, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {"models": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 3}}}}}}}}, "p": {"docs": {}, "df": 0, "c": {"docs": {"models": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 3}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA": {"tf": 1}, "models.config.Config.estimate_runtime": {"tf": 1}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.PP": {"tf": 2}, "models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 8, "/": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"models.CA.CA.update": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2, "s": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 4}}}}}, "s": {"docs": {}, "df": 0, "h": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}, "y": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 4}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 2}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}}, "x": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}, "i": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}}, "df": 1, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"models": {"tf": 1}, "models.config": {"tf": 1}, "models.numba_optimized": {"tf": 1}}, "df": 3}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.update": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.update": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"models.CA.PP.update_async": {"tf": 1}}, "df": 1}}}}}}}}}}, "n": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 2}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.7320508075688772}, "scripts": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 1.7320508075688772}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 27, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.densities": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 6, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {"models.CA.CA.__init__": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 3, "d": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}}, "df": 2}, "s": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts.experiments.run_phase2": {"tf": 1}}, "df": 1}}}}}}}}}}, "t": {"3": {"2": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}, "docs": {}, "df": 0}, "docs": {"models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.n_species": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.CA.PP": {"tf": 1.7320508075688772}, "models.config.Config": {"tf": 3}, "models.config.Config.get_warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 2}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}}, "df": 26, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}}, "df": 3, "s": {"docs": {"models.CA.CA": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.PP": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.PP": {"tf": 1}}, "df": 2}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}}, "df": 2}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 2}}}}}}}}, "o": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 6}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7, "s": {"docs": {"models.config": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.numba_optimized": {"tf": 1}}, "df": 2}}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.CA.validate": {"tf": 1}}, "df": 1, "s": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}}, "df": 2}}}}}}}, "o": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 3}}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.CA.CA.run": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}}, "df": 4}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3}}}}}}}}}, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}}}}}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 3}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2}}}}}}}}}}}, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 4}}}}, "s": {"docs": {"models.CA.CA.validate": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 22, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 2}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1.7320508075688772}, "models.CA.PP.validate": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 19}, "t": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1.4142135623730951}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 19, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 3, "s": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 5}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}, "s": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 2}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}}, "df": 3, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3}, "r": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 3}, "s": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}}}}}, "y": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}, "s": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}}, "df": 2}}}, "w": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.CA": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.__init__": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.get_evolved_stats": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 21, "i": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.PP.validate": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 4}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 8}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1}}}, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}, "models.CA.CA.run": {"tf": 1.4142135623730951}, "models.config.Config": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 5}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 15}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"models.config.Config": {"tf": 1.4142135623730951}, "models.config.Config.get_warmup_steps": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 4}}, "s": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 3}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.config.Config.estimate_runtime": {"tf": 1}}, "df": 1}}, "y": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}}, "df": 3}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"models.numba_optimized": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"scripts.experiments.save_results_jsonl": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"scripts.experiments.average_pcfs": {"tf": 1}}, "df": 1}}}}}}, "g": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "scripts.experiments.average_pcfs": {"tf": 1}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 11, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA": {"tf": 1.4142135623730951}, "models.CA.CA.__init__": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.CA.PP": {"tf": 1}, "models.config.Config": {"tf": 2}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 6}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA": {"tf": 1.7320508075688772}, "models.CA.CA.__init__": {"tf": 1.7320508075688772}, "models.CA.CA.rows": {"tf": 1}, "models.CA.CA.cols": {"tf": 1}, "models.CA.CA.validate": {"tf": 1.7320508075688772}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 2}, "models.CA.PP.validate": {"tf": 1}, "models.config": {"tf": 1}, "models.config.Config": {"tf": 2.449489742783178}, "models.config.Config.get_warmup_steps": {"tf": 1.4142135623730951}, "models.config.Config.get_measurement_steps": {"tf": 1.7320508075688772}, "models.config.Config.estimate_runtime": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 2}, "models.numba_optimized.PPKernel.update": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1.7320508075688772}, "models.numba_optimized.benchmark_kernels": {"tf": 2}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1.4142135623730951}, "scripts": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 2}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 2.23606797749979}, "scripts.experiments.run_phase5": {"tf": 1.4142135623730951}}, "df": 30, "s": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA": {"tf": 1.7320508075688772}, "models.CA.CA.__init__": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1.7320508075688772}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 4, "s": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "scripts": {"tf": 1}}, "df": 3}}, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"models.config.Config.get_prey_births": {"tf": 1}, "models.config.Config.get_prey_deaths": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1.4142135623730951}}, "df": 3, "d": {"docs": {"scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2}, "s": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 2}}}}}}}, "t": {"docs": {"models.numba_optimized": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 1.7320508075688772}, "scripts.experiments.run_single_simulation": {"tf": 1.4142135623730951}, "scripts.experiments.main": {"tf": 1}}, "df": 6}}, "t": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 2.6457513110645907}, "models.numba_optimized.detect_clusters_fast": {"tf": 2.449489742783178}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 3}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 3}, "scripts.experiments.count_populations": {"tf": 2.449489742783178}, "scripts.experiments.get_evolved_stats": {"tf": 2.449489742783178}, "scripts.experiments.average_pcfs": {"tf": 3}, "scripts.experiments.save_results_jsonl": {"tf": 2.449489742783178}, "scripts.experiments.save_results_npz": {"tf": 2.449489742783178}, "scripts.experiments.load_results_jsonl": {"tf": 2.449489742783178}}, "df": 13}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 1}}, "df": 2}}}}}}, "p": {"docs": {"models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 1}, "s": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"models.config": {"tf": 1}, "models.config.Config": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}}, "df": 3, "d": {"docs": {"models.CA.CA": {"tf": 1}, "models.config.Config": {"tf": 1.7320508075688772}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.average_pcfs": {"tf": 1}}, "df": 7}, "f": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.run": {"tf": 1}}, "df": 1}}}, "s": {"docs": {"models.config.Config": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.7320508075688772}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_cluster_detection": {"tf": 1}}, "df": 7}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts": {"tf": 1.4142135623730951}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 15}}}, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"models.config": {"tf": 1}, "models.numba_optimized": {"tf": 1}, "scripts": {"tf": 1}, "scripts.experiments": {"tf": 1}}, "df": 4}}}}, "p": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1.4142135623730951}, "models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.main": {"tf": 1}}, "df": 3, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.update": {"tf": 1}, "models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1}, "models.CA.PP.update_async": {"tf": 1}, "models.CA.PP.update": {"tf": 1}, "models.numba_optimized.PPKernel": {"tf": 1}, "models.numba_optimized.PPKernel.update": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1.4142135623730951}}, "df": 9, "s": {"docs": {"models.CA.CA.run": {"tf": 1}, "models.CA.PP": {"tf": 1.4142135623730951}, "models.CA.PP.update_async": {"tf": 1}, "models.config.Config": {"tf": 1}}, "df": 4}, "d": {"docs": {"models.numba_optimized.PPKernel.update": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"models.config.Config": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.detect_clusters_fast": {"tf": 1}}, "df": 2}}}}}}}}, "i": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"models.numba_optimized.detect_clusters_fast": {"tf": 1}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 2}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 5, "l": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"scripts.experiments.run_phase4": {"tf": 1}}, "df": 1}}}}}}}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_single_simulation": {"tf": 1}}, "df": 1}}}}, "z": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 2}}}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"models.CA.CA.evolve": {"tf": 1.4142135623730951}}, "df": 1, "u": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.evolve": {"tf": 1.7320508075688772}, "models.config.Config.get_measurement_steps": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1}, "scripts.experiments.count_populations": {"tf": 1.7320508075688772}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_npz": {"tf": 1}}, "df": 8, "s": {"docs": {"models.CA.CA.__init__": {"tf": 1.4142135623730951}, "models.CA.CA.evolve": {"tf": 1}, "models.config.Config": {"tf": 2.23606797749979}, "models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1}, "models.numba_optimized.compute_all_pcfs_fast": {"tf": 1.4142135623730951}, "scripts.experiments.get_evolved_stats": {"tf": 2.23606797749979}, "scripts.experiments.average_pcfs": {"tf": 2}, "scripts.experiments.save_results_npz": {"tf": 1}, "scripts.experiments.run_single_simulation": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 10}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.CA.evolve": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}, "models.config.get_phase_config": {"tf": 1}}, "df": 4}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.validate": {"tf": 1}, "scripts.experiments.get_evolved_stats": {"tf": 2.23606797749979}, "scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 3, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.CA.validate": {"tf": 1}, "models.CA.PP.validate": {"tf": 1}}, "df": 2, "d": {"docs": {"models.CA.PP.__init__": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.PP.validate": {"tf": 1}}, "df": 1}}}}}}}}, "r": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 3}}}}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"models.CA.CA.validate": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.CA.CA.run": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"models.CA.PP.update_async": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}}, "o": {"docs": {}, "df": 0, "n": {"docs": {"models.numba_optimized.PPKernel": {"tf": 1.4142135623730951}}, "df": 1}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"models.numba_optimized.benchmark_kernels": {"tf": 1}, "scripts.experiments.run_phase3": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts.experiments": {"tf": 1}}, "df": 1}}}}}}}, "j": {"docs": {"models.numba_optimized.compute_pcf_periodic_fast": {"tf": 1.4142135623730951}}, "df": 1, "o": {"docs": {}, "df": 0, "b": {"docs": {"scripts.experiments.run_phase5": {"tf": 1}}, "df": 1, "s": {"docs": {"models.config.Config": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 3}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.run_phase1": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized": {"tf": 1}, "models.numba_optimized.set_numba_seed": {"tf": 1.7320508075688772}, "models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1}, "models.numba_optimized.warmup_numba_kernels": {"tf": 1}, "models.numba_optimized.benchmark_kernels": {"tf": 1}}, "df": 5}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.warmup_numba_kernels": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.generate_unique_seed": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 1.4142135623730951}, "scripts.experiments.load_results_jsonl": {"tf": 2}, "scripts.experiments.run_phase1": {"tf": 1}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}}, "df": 7, "l": {"docs": {"scripts": {"tf": 1}, "scripts.experiments.save_results_jsonl": {"tf": 2}, "scripts.experiments.load_results_jsonl": {"tf": 2}, "scripts.experiments.run_phase1": {"tf": 1.4142135623730951}, "scripts.experiments.run_phase2": {"tf": 1}, "scripts.experiments.run_phase4": {"tf": 1}, "scripts.experiments.run_phase5": {"tf": 1}}, "df": 7}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"scripts.experiments.load_results_jsonl": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"models.numba_optimized.measure_cluster_sizes_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.detect_clusters_fast": {"tf": 1.4142135623730951}, "models.numba_optimized.get_cluster_stats_fast": {"tf": 2}, "scripts.experiments.get_evolved_stats": {"tf": 1.4142135623730951}, "scripts.experiments.save_results_jsonl": {"tf": 3.1622776601683795}, "scripts.experiments.save_results_npz": {"tf": 2.449489742783178}, "scripts.experiments.load_results_jsonl": {"tf": 1.4142135623730951}}, "df": 7}}}}}}}, "pipeline": ["trimmer"], "_isPrebuiltIndex": true}; + + // mirrored in build-search-index.js (part 1) + // Also split on html tags. this is a cheap heuristic, but good enough. + elasticlunr.tokenizer.setSeperator(/[\s\-.;&_'"=,()]+|<[^>]*>/); + + let searchIndex; + if (docs._isPrebuiltIndex) { + console.info("using precompiled search index"); + searchIndex = elasticlunr.Index.load(docs); + } else { + console.time("building search index"); + // mirrored in build-search-index.js (part 2) + searchIndex = elasticlunr(function () { + this.pipeline.remove(elasticlunr.stemmer); + this.pipeline.remove(elasticlunr.stopWordFilter); + this.addField("qualname"); + this.addField("fullname"); + this.addField("annotation"); + this.addField("default_value"); + this.addField("signature"); + this.addField("bases"); + this.addField("doc"); + this.setRef("fullname"); + }); + for (let doc of docs) { + searchIndex.addDoc(doc); + } + console.timeEnd("building search index"); + } + + return (term) => searchIndex.search(term, { + fields: { + qualname: {boost: 4}, + fullname: {boost: 2}, + annotation: {boost: 2}, + default_value: {boost: 2}, + signature: {boost: 2}, + bases: {boost: 2}, + doc: {boost: 1}, + }, + expand: true + }); +})(); \ No newline at end of file diff --git a/docs/GEN_AI.md b/genai_usage/GEN_AI.md similarity index 100% rename from docs/GEN_AI.md rename to genai_usage/GEN_AI.md diff --git a/docs/kimon_prompts.md b/genai_usage/kimon_prompts.md similarity index 100% rename from docs/kimon_prompts.md rename to genai_usage/kimon_prompts.md diff --git a/docs/sary_prompts.md b/genai_usage/sary_prompts.md similarity index 100% rename from docs/sary_prompts.md rename to genai_usage/sary_prompts.md diff --git a/docs/sofronia_prompts.md b/genai_usage/sofronia_prompts.md similarity index 100% rename from docs/sofronia_prompts.md rename to genai_usage/sofronia_prompts.md diff --git a/docs/storm_prompts.md b/genai_usage/storm_prompts.md similarity index 100% rename from docs/storm_prompts.md rename to genai_usage/storm_prompts.md diff --git a/misc/benchmark.py b/misc/benchmark.py index 83a51f1..2dda897 100644 --- a/misc/benchmark.py +++ b/misc/benchmark.py @@ -4,7 +4,7 @@ Measures and compares performance of: 1. Numba-optimized kernel vs pure Python baseline -2. Cell-list PCF vs brute-force PCF +2. Cell-list PCF vs brute-force PCF 3. Grid size scaling behavior 4. Random vs directed hunting overhead 5. Full simulation pipeline diff --git a/misc/profile_sim.py b/misc/profile_sim.py deleted file mode 100644 index af28176..0000000 --- a/misc/profile_sim.py +++ /dev/null @@ -1,26 +0,0 @@ -import cProfile, pstats -from pathlib import Path -import sys - -# Ensure we can find our modules -sys.path.insert(0, str(Path(__file__).parent.parent)) -from scripts.experiments import Config, run_single_simulation - -# 1. Setup a single simulation configuration -cfg = Config() -cfg.default_grid = 150 -cfg.warmup_steps = 200 -cfg.measurement_steps = 300 - -# 2. Profile the function -profiler = cProfile.Profile() -profiler.enable() - -# Run a single simulation (no parallelization) -run_single_simulation(0.2, 0.05, 150, 42, True, cfg) - -profiler.disable() - -# 3. Print the top 15 time-consumers -stats = pstats.Stats(profiler).sort_stats("tottime") -stats.print_stats(15) diff --git a/misc/soc_analysis.py b/misc/soc_analysis.py index 45c7ff6..7f7cbf9 100644 --- a/misc/soc_analysis.py +++ b/misc/soc_analysis.py @@ -26,7 +26,6 @@ # Import directly from models to avoid __init__ issues from models.CA import PP - # ============================================================================ # 1. STRESS METRIC & PERTURBATION DYNAMICS # ============================================================================ diff --git a/models/CA.py b/models/CA.py index fe6b30d..13b84e1 100644 --- a/models/CA.py +++ b/models/CA.py @@ -22,8 +22,8 @@ class CA: """ Base cellular automaton class for spatial simulations. - This class provides a framework for multi-species cellular automata with - support for global parameters, per-cell evolving parameters, and + This class provides a framework for multi-species cellular automata with + support for global parameters, per-cell evolving parameters, and grid initialization based on density. Attributes @@ -188,9 +188,9 @@ def _infer_species_from_param_name(self, param_name: str) -> Optional[int]: """ Infer the 1-based species index from a parameter name using `species_names`. - This method checks if the given parameter name starts with any of the - defined species names followed by an underscore (e.g., 'prey_birth'). - It is used to automatically route global parameters to the correct + This method checks if the given parameter name starts with any of the + defined species names followed by an underscore (e.g., 'prey_birth'). + It is used to automatically route global parameters to the correct species' local parameter arrays. Parameters @@ -201,12 +201,12 @@ def _infer_species_from_param_name(self, param_name: str) -> Optional[int]: Returns ------- Optional[int] - The 1-based index of the species if a matching prefix is found; + The 1-based index of the species if a matching prefix is found; otherwise, None. Notes ----- - The method expects `self.species_names` to be a collection of strings. + The method expects `self.species_names` to be a collection of strings. If `param_name` is not a string or no match is found, it returns None. """ if not isinstance(param_name, str): @@ -227,40 +227,40 @@ def evolve( """ Enable per-cell evolution for a specific parameter on a given species. - This method initializes a spatial parameter array (local parameter map) - for a global parameter. It allows individual cells to carry their own - values for that parameter, which can then mutate and evolve during + This method initializes a spatial parameter array (local parameter map) + for a global parameter. It allows individual cells to carry their own + values for that parameter, which can then mutate and evolve during the simulation. Parameters ---------- param : str - The name of the global parameter to enable for evolution. + The name of the global parameter to enable for evolution. Must exist in `self.params`. species : int, optional - The 1-based index of the species to which this parameter applies. - If None, the method attempts to infer the species from the + The 1-based index of the species to which this parameter applies. + If None, the method attempts to infer the species from the parameter name prefix. sd : float, default 0.05 - The standard deviation of the Gaussian mutation applied during + The standard deviation of the Gaussian mutation applied during inheritance/reproduction. min_val : float, optional - The minimum allowable value for the parameter (clamping). + The minimum allowable value for the parameter (clamping). Defaults to 0.01 if not provided. max_val : float, optional - The maximum allowable value for the parameter (clamping). + The maximum allowable value for the parameter (clamping). Defaults to 0.99 if not provided. Raises ------ ValueError - If the parameter is not in `self.params`, the species cannot be + If the parameter is not in `self.params`, the species cannot be inferred, or the species index is out of bounds. Notes ----- - The local parameter is stored in `self.cell_params` as a 2D numpy - array initialized with the current global value for all cells of + The local parameter is stored in `self.cell_params` as a 2D numpy + array initialized with the current global value for all cells of the target species, and `NaN` elsewhere. """ if min_val is None: @@ -293,9 +293,9 @@ def update(self) -> None: """ Perform one update step of the cellular automaton. - This is an abstract method that defines the transition rules of the - system. It must be implemented by concrete subclasses to specify - how cell states and parameters change over time based on their + This is an abstract method that defines the transition rules of the + system. It must be implemented by concrete subclasses to specify + how cell states and parameters change over time based on their current state and neighborhood. Raises @@ -309,7 +309,7 @@ def update(self) -> None: Notes ----- - In a typical implementation, this method handles the logic for + In a typical implementation, this method handles the logic for stochastic transitions, movement, or predator-prey interactions. """ raise NotImplementedError( @@ -325,9 +325,9 @@ def run( """ Execute the cellular automaton simulation for a specified number of steps. - This method drives the simulation loop, calling `update()` at each - iteration. It manages visualization updates, directory creation for - data persistence, and handles the freezing of evolving parameters + This method drives the simulation loop, calling `update()` at each + iteration. It manages visualization updates, directory creation for + data persistence, and handles the freezing of evolving parameters at a specific time step. Parameters @@ -335,11 +335,11 @@ def run( steps : int The total number of iterations to run (must be non-negative). stop_evolution_at : int, optional - The 1-based iteration index after which parameter mutation is - disabled. Useful for observing system stability after a period + The 1-based iteration index after which parameter mutation is + disabled. Useful for observing system stability after a period of adaptation. snapshot_iters : List[int], optional - A list of specific 1-based iteration indices at which to save + A list of specific 1-based iteration indices at which to save the current grid state to the results directory. Returns @@ -348,8 +348,8 @@ def run( Notes ----- - If snapshots are requested, a results directory is automatically created - using a timestamped subfolder (e.g., 'results/run-1700000000/'). + If snapshots are requested, a results directory is automatically created + using a timestamped subfolder (e.g., 'results/run-1700000000/'). Visualization errors are logged but do not terminate the simulation. """ assert ( @@ -405,9 +405,9 @@ class PP(CA): """ Predator-Prey Cellular Automaton model with Numba-accelerated kernels. - This model simulates a stochastic predator-prey system where species - interact on a 2D grid. It supports evolving per-cell death rates, - periodic boundary conditions, and both random and directed hunting + This model simulates a stochastic predator-prey system where species + interact on a 2D grid. It supports evolving per-cell death rates, + periodic boundary conditions, and both random and directed hunting behaviors. Parameters @@ -421,17 +421,17 @@ class PP(CA): neighborhood : {'moore', 'neumann'}, default 'moore' The neighborhood type for cell interactions. params : Dict[str, object], optional - Global parameters: "prey_death", "predator_death", "prey_birth", + Global parameters: "prey_death", "predator_death", "prey_birth", "predator_birth". cell_params : Dict[str, object], optional Initial local parameter maps (2D arrays). seed : int, optional Random seed for reproducibility. synchronous : bool, default True - If True, updates the entire grid at once. If False, updates + If True, updates the entire grid at once. If False, updates cells asynchronously. directed_hunting : bool, default False - If True, predators selectively hunt prey rather than choosing + If True, predators selectively hunt prey rather than choosing neighbors at random. Attributes @@ -515,8 +515,8 @@ def validate(self) -> None: """ Validate Predator-Prey specific invariants and spatial parameter arrays. - Extends the base CA validation to ensure that numerical parameters are - within the [0, 1] probability range and that evolved parameter maps + Extends the base CA validation to ensure that numerical parameters are + within the [0, 1] probability range and that evolved parameter maps (e.g., prey_death) correctly align with the species locations. Raises @@ -575,9 +575,9 @@ def update_async(self) -> None: """ Execute an asynchronous update using the optimized Numba kernel. - This method retrieves the evolved parameter maps and delegates the - stochastic transitions to the `PPKernel`. Asynchronous updates - typically handle cell-by-cell logic where changes can be + This method retrieves the evolved parameter maps and delegates the + stochastic transitions to the `PPKernel`. Asynchronous updates + typically handle cell-by-cell logic where changes can be immediately visible to neighbors. """ # Get the evolved prey death map diff --git a/models/__init__.py b/models/__init__.py index 279bbd8..2d4f765 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,2 +1,19 @@ -from ..misc.mean_field import MeanFieldModel +""" +# Models Package + +This package contains the core simulation logic for the Predator-Prey +Cellular Automata. + +## Key Components +- `CA`: The base Cellular Automata class. +- `experiment`: Tools for running batches and collecting data. +- `numba_optimized`: High-performance kernels for HPC execution. + +## Example +```python +from models.CA import PredatorPreyCA +sim = PredatorPreyCA(rows=100, cols=100) +""" + from .CA import CA, PP + diff --git a/models/config.py b/models/config.py index ce4c3c6..5519577 100644 --- a/models/config.py +++ b/models/config.py @@ -6,13 +6,13 @@ Usage: from config import PHASE1_CONFIG, PHASE2_CONFIG, Config - + # Use pre-defined config cfg = PHASE1_CONFIG - + # Or create custom config cfg = Config(grid_size=150, n_replicates=20) - + # Or modify existing cfg = Config(**{**asdict(PHASE1_CONFIG), 'n_replicates': 30}) """ @@ -21,7 +21,7 @@ from typing import Tuple, Optional import numpy as np -#FIXME: Tidy up config file for submission +# FIXME: Tidy up config file for submission @dataclass @@ -29,8 +29,8 @@ class Config: """ Central configuration for Predator-Prey Hydra Effect experiments. - This dataclass aggregates all hyperparameters, grid settings, and - experimental phase definitions. It includes helper methods for + This dataclass aggregates all hyperparameters, grid settings, and + experimental phase definitions. It includes helper methods for parameter sweep generation and runtime estimation. Attributes @@ -108,11 +108,11 @@ class Config: """ # Grid settings - grid_size: int = 1000 + grid_size: int = 1000 densities: Tuple[float, float] = ( 0.30, 0.15, - ) # (prey, predator) + ) # (prey, predator) # For FSS experiments: multiple grid sizes grid_sizes: Tuple[int, ...] = (50, 100, 250, 500, 1000, 2500) @@ -120,8 +120,8 @@ class Config: # Default/fixed parameters prey_birth: float = 0.2 prey_death: float = 0.05 - predator_birth: float = 0.8 - predator_death: float = 0.05 + predator_birth: float = 0.8 + predator_death: float = 0.05 # Critical point (UPDATE AFTER PHASE 1) critical_prey_birth: float = 0.20 @@ -129,7 +129,7 @@ class Config: # Prey parameter sweep (Phase 1) prey_death_range: Tuple[float, float] = (0.0, 0.2) - n_prey_birth: int = 15 + n_prey_birth: int = 15 n_prey_death: int = 5 # Predator parameter sweep (Phase 4 sensitivity) @@ -138,13 +138,13 @@ class Config: 0.20, 0.25, 0.30, - ) + ) predator_death_values: Tuple[float, ...] = ( 0.05, 0.10, 0.15, 0.20, - ) + ) # Perturbation offsets from critical point (Phase 5) prey_death_offsets: Tuple[float, ...] = ( @@ -153,14 +153,14 @@ class Config: 0.0, 0.01, 0.02, - ) + ) # Number of replicates per parameter configuration - n_replicates: int = 15 + n_replicates: int = 15 # Simulation steps - warmup_steps: int = 300 - measurement_steps: int = 500 + warmup_steps: int = 300 + measurement_steps: int = 500 # Evo with_evolution: bool = False @@ -175,7 +175,7 @@ class Config: 0.10, 0.15, 0.20, - ) + ) # Update mode synchronous: bool = False # Always False for this model @@ -186,7 +186,7 @@ class Config: # Temporal data collection (time series) save_timeseries: bool = False - timeseries_subsample: int = 10 + timeseries_subsample: int = 10 # PCF settings collect_pcf: bool = True @@ -256,9 +256,9 @@ def get_measurement_steps(self, L: int) -> int: """ Determine the number of measurement steps based on the grid side length. - This method allows for dynamic scaling of data collection duration relative - to the system size. Currently, it returns a fixed value, but it is - designed to be overridden for studies where measurement time must + This method allows for dynamic scaling of data collection duration relative + to the system size. Currently, it returns a fixed value, but it is + designed to be overridden for studies where measurement time must scale with the grid size (e.g., $L^z$ scaling in critical dynamics). Parameters @@ -277,7 +277,7 @@ def estimate_runtime(self, n_cores: int = 32) -> str: """ Estimate the wall-clock time required to complete the experiment. - Calculations account for grid size scaling, PCF overhead, + Calculations account for grid size scaling, PCF overhead, replicate counts, and available parallel resources. Parameters diff --git a/models/numba_optimized.py b/models/numba_optimized.py index 83c78bb..bb2d882 100644 --- a/models/numba_optimized.py +++ b/models/numba_optimized.py @@ -50,8 +50,8 @@ def set_numba_seed(seed: int) -> None: """ Seed Numba's internal random number generator from within a JIT context. - This function ensures that Numba's independent random number generator - is synchronized with the provided seed, enabling reproducibility for + This function ensures that Numba's independent random number generator + is synchronized with the provided seed, enabling reproducibility for jit-compiled functions that use NumPy's random operations. Parameters @@ -65,9 +65,9 @@ def set_numba_seed(seed: int) -> None: Notes ----- - Because Numba maintains its own internal state for random number - generation, calling `np.random.seed()` in standard Python code will not - affect jit-compiled functions. This helper must be called to bridge + Because Numba maintains its own internal state for random number + generation, calling `np.random.seed()` in standard Python code will not + affect jit-compiled functions. This helper must be called to bridge that gap. """ np.random.seed(seed) @@ -97,10 +97,10 @@ def _pp_async_kernel_random( """ Asynchronous predator-prey update kernel with random neighbor selection. - This Numba-accelerated kernel performs an asynchronous update of the - simulation grid. It identifies all occupied cells, shuffles them to - ensure unbiased processing, and applies stochastic rules for prey - mortality, prey reproduction (with optional parameter evolution), + This Numba-accelerated kernel performs an asynchronous update of the + simulation grid. It identifies all occupied cells, shuffles them to + ensure unbiased processing, and applies stochastic rules for prey + mortality, prey reproduction (with optional parameter evolution), predator mortality, and predation. Parameters @@ -139,8 +139,8 @@ def _pp_async_kernel_random( Notes ----- - The kernel uses periodic boundary conditions. The Fisher-Yates shuffle on - `occupied_buffer` ensures that the asynchronous updates do not introduce + The kernel uses periodic boundary conditions. The Fisher-Yates shuffle on + `occupied_buffer` ensures that the asynchronous updates do not introduce directional bias. """ rows, cols = grid.shape @@ -229,9 +229,9 @@ def _pp_async_kernel_directed( """ Asynchronous predator-prey update kernel with directed behavior. - This kernel implements "intelligent" species behavior: prey actively search - for empty spaces to reproduce, and predators actively search for nearby - prey to hunt. A two-pass approach is used to stochastically select a + This kernel implements "intelligent" species behavior: prey actively search + for empty spaces to reproduce, and predators actively search for nearby + prey to hunt. A two-pass approach is used to stochastically select a valid target from the neighborhood without heap allocation. Parameters @@ -270,8 +270,8 @@ def _pp_async_kernel_directed( Notes ----- - The directed behavior significantly changes the system dynamics compared to - random neighbor selection, often leading to different critical thresholds + The directed behavior significantly changes the system dynamics compared to + random neighbor selection, often leading to different critical thresholds and spatial patterning. Periodic boundary conditions are applied. """ rows, cols = grid.shape @@ -392,9 +392,9 @@ class PPKernel: """ Wrapper for predator-prey kernel with pre-allocated buffers. - This class manages the spatial configuration and memory buffers required - for the Numba-accelerated update kernels. By pre-allocating the - `occupied_buffer`, it avoids expensive memory allocations during the + This class manages the spatial configuration and memory buffers required + for the Numba-accelerated update kernels. By pre-allocating the + `occupied_buffer`, it avoids expensive memory allocations during the simulation loop. Parameters @@ -404,11 +404,11 @@ class PPKernel: cols : int Number of columns in the simulation grid. neighborhood : {'moore', 'von_neumann'}, optional - The neighborhood type determining adjacent cells. 'moore' includes - diagonals (8 neighbors), 'von_neumann' does not (4 neighbors). + The neighborhood type determining adjacent cells. 'moore' includes + diagonals (8 neighbors), 'von_neumann' does not (4 neighbors). Default is 'moore'. directed_hunting : bool, optional - If True, uses the directed behavior kernel where species search for + If True, uses the directed behavior kernel where species search for targets. If False, uses random neighbor selection. Default is False. Attributes @@ -537,9 +537,9 @@ def _flood_fill( """ Perform a stack-based flood fill to measure the size of a connected cluster. - This Numba-accelerated function identifies all contiguous cells of a - specific target value starting from a given coordinate. It supports - both Moore and von Neumann neighborhoods and implements periodic + This Numba-accelerated function identifies all contiguous cells of a + specific target value starting from a given coordinate. It supports + both Moore and von Neumann neighborhoods and implements periodic boundary conditions (toroidal topology). Parameters @@ -559,7 +559,7 @@ def _flood_fill( cols : int Total number of columns in the grid. moore : bool - If True, use a Moore neighborhood (8 neighbors). If False, use a + If True, use a Moore neighborhood (8 neighbors). If False, use a von Neumann neighborhood (4 neighbors). Returns @@ -569,7 +569,7 @@ def _flood_fill( Notes ----- - The function uses a manual stack implementation to avoid recursion limit + The function uses a manual stack implementation to avoid recursion limit issues and is optimized for use within JIT-compiled loops. """ max_stack = rows * cols @@ -617,8 +617,8 @@ def _measure_clusters(grid: np.ndarray, species: int, moore: bool = True) -> np. """ Identify and measure the sizes of all connected clusters for a specific species. - This function scans the entire grid and initiates a flood-fill algorithm - whenever an unvisited cell of the target species is encountered. It + This function scans the entire grid and initiates a flood-fill algorithm + whenever an unvisited cell of the target species is encountered. It returns an array containing the size (cell count) of each identified cluster. Parameters @@ -628,20 +628,20 @@ def _measure_clusters(grid: np.ndarray, species: int, moore: bool = True) -> np. species : int The target species identifier (e.g., 1 for Prey, 2 for Predator). moore : bool, optional - Determines the connectivity logic. If True, uses the Moore neighborhood - (8 neighbors); if False, uses the von Neumann neighborhood (4 neighbors). + Determines the connectivity logic. If True, uses the Moore neighborhood + (8 neighbors); if False, uses the von Neumann neighborhood (4 neighbors). Default is True. Returns ------- cluster_sizes : np.ndarray - A 1D array of integers where each element represents the size of + A 1D array of integers where each element represents the size of one connected cluster. Notes ----- - This function is Numba-optimized and utilizes an internal `visited` mask - to ensure each cell is processed only once, maintaining $O(N)$ + This function is Numba-optimized and utilizes an internal `visited` mask + to ensure each cell is processed only once, maintaining $O(N)$ complexity relative to the number of cells. """ rows, cols = grid.shape @@ -729,6 +729,7 @@ def _detect_clusters_numba( return labels, sizes[:n_clusters] + # ============================================================================ # PUBLIC API - CLUSTER DETECTION # ============================================================================ @@ -742,9 +743,9 @@ def measure_cluster_sizes_fast( """ Measure cluster sizes for a specific species using Numba-accelerated flood fill. - This function provides a high-performance interface for calculating cluster - size statistics without the overhead of generating a full label map. It is - optimized for large-scale simulation analysis where only distribution + This function provides a high-performance interface for calculating cluster + size statistics without the overhead of generating a full label map. It is + optimized for large-scale simulation analysis where only distribution metrics (e.g., mean size, max size) are required. Parameters @@ -754,18 +755,18 @@ def measure_cluster_sizes_fast( species : int The target species identifier (e.g., 1 for Prey, 2 for Predator). neighborhood : {'moore', 'neumann'}, optional - The connectivity rule. 'moore' uses 8-way connectivity (including diagonals); + The connectivity rule. 'moore' uses 8-way connectivity (including diagonals); 'neumann' uses 4-way connectivity. Default is 'moore'. Returns ------- cluster_sizes : np.ndarray - A 1D array of integers, where each element is the cell count of an + A 1D array of integers, where each element is the cell count of an identified cluster. Notes ----- - The input grid is cast to `int32` to ensure compatibility with the + The input grid is cast to `int32` to ensure compatibility with the underlying JIT-compiled `_measure_clusters` kernel. Examples @@ -787,8 +788,8 @@ def detect_clusters_fast( """ Perform full cluster detection with labels using Numba acceleration. - This function returns a label array for spatial analysis and a dictionary - of cluster sizes. It is significantly faster than standard Python or + This function returns a label array for spatial analysis and a dictionary + of cluster sizes. It is significantly faster than standard Python or SciPy equivalents for large simulation grids. Parameters @@ -798,20 +799,20 @@ def detect_clusters_fast( species : int The target species identifier (e.g., 1 for Prey, 2 for Predator). neighborhood : {'moore', 'neumann'}, optional - The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' + The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' uses 4-way connectivity. Default is 'moore'. Returns ------- labels : np.ndarray - A 2D int32 array where each cell contains its unique cluster ID. + A 2D int32 array where each cell contains its unique cluster ID. Cells not belonging to the target species are 0. sizes : dict A dictionary mapping cluster IDs to their respective cell counts. Notes ----- - The underlying Numba kernel uses a stack-based flood fill to avoid + The underlying Numba kernel uses a stack-based flood fill to avoid recursion limits and handles periodic boundary conditions. Examples @@ -836,9 +837,9 @@ def get_cluster_stats_fast( """ Compute comprehensive cluster statistics for a species using Numba acceleration. - This function integrates cluster detection and labeling to provide a - full suite of spatial metrics. It calculates the cluster size distribution - and the largest cluster fraction, which often serves as an order + This function integrates cluster detection and labeling to provide a + full suite of spatial metrics. It calculates the cluster size distribution + and the largest cluster fraction, which often serves as an order parameter in percolation theory and Phase 1-3 analyses. Parameters @@ -848,7 +849,7 @@ def get_cluster_stats_fast( species : int The target species identifier (e.g., 1 for Prey, 2 for Predator). neighborhood : {'moore', 'neumann'}, optional - The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' + The connectivity rule. 'moore' uses 8-way connectivity; 'neumann' uses 4-way connectivity. Default is 'moore'. Returns @@ -858,7 +859,7 @@ def get_cluster_stats_fast( - 'n_clusters': Total count of isolated clusters. - 'sizes': Sorted array (descending) of all cluster sizes. - 'largest': Size of the single largest cluster. - - 'largest_fraction': Size of the largest cluster divided by + - 'largest_fraction': Size of the largest cluster divided by the total population of the species. - 'mean_size': Average size of all clusters. - 'size_distribution': Frequency mapping of {size: count}. @@ -906,6 +907,7 @@ def get_cluster_stats_fast( "size_dict": size_dict, } + # ============================================================================ # PCF COMPUTATION (Cell-list accelerated) # ============================================================================ @@ -921,9 +923,9 @@ def _build_cell_list( """ Build a cell list for spatial hashing to accelerate neighbor lookups. - This Numba-optimized function partitions a set of coordinates into a - grid of cells. It uses a three-pass approach to calculate cell occupancy, - compute starting offsets for each cell in a flat index array, and finally + This Numba-optimized function partitions a set of coordinates into a + grid of cells. It uses a three-pass approach to calculate cell occupancy, + compute starting offsets for each cell in a flat index array, and finally populate that array with position indices. Parameters @@ -940,13 +942,13 @@ def _build_cell_list( Returns ------- indices : np.ndarray - A 1D array of original position indices, reordered so that indices + A 1D array of original position indices, reordered so that indices belonging to the same cell are contiguous. offsets : np.ndarray - A 2D array where `offsets[r, c]` is the starting index in the + A 2D array where `offsets[r, c]` is the starting index in the `indices` array for cell (r, c). cell_counts : np.ndarray - A 2D array where `cell_counts[r, c]` is the number of points + A 2D array where `cell_counts[r, c]` is the number of points contained in cell (r, c). cell_size_r : float The calculated height of an individual cell. @@ -955,9 +957,9 @@ def _build_cell_list( Notes ----- - This implementation assumes periodic boundary conditions via the - modulo operator during coordinate-to-cell mapping. It is designed to - eliminate heap allocations within the main simulation loop by using + This implementation assumes periodic boundary conditions via the + modulo operator during coordinate-to-cell mapping. It is designed to + eliminate heap allocations within the main simulation loop by using Numba's efficient array handling. """ n_pos = len(positions) @@ -1001,9 +1003,9 @@ def _periodic_dist_sq( """ Calculate the squared Euclidean distance between two points with periodic boundary conditions. - This Numba-optimized function accounts for toroidal topology by finding the - shortest path between coordinates across the grid edges. Using the squared - distance avoids the computational expense of a square root operation, + This Numba-optimized function accounts for toroidal topology by finding the + shortest path between coordinates across the grid edges. Using the squared + distance avoids the computational expense of a square root operation, making it ideal for high-frequency spatial queries. Parameters @@ -1028,7 +1030,7 @@ def _periodic_dist_sq( Notes ----- - The function applies the minimum image convention, ensuring that the + The function applies the minimum image convention, ensuring that the distance never exceeds half the domain length in any dimension. """ dr = abs(r1 - r2) @@ -1059,10 +1061,10 @@ def _pcf_cell_list( """ Compute a Pair Correlation Function (PCF) histogram using spatial cell lists. - This Numba-accelerated parallel kernel calculates distances between two sets - of points (pos_i and pos_j). It uses a cell list (spatial hashing) to - restrict distance calculations to neighboring cells within the maximum - specified distance, significantly improving performance from $O(N^2)$ + This Numba-accelerated parallel kernel calculates distances between two sets + of points (pos_i and pos_j). It uses a cell list (spatial hashing) to + restrict distance calculations to neighboring cells within the maximum + specified distance, significantly improving performance from $O(N^2)$ to $O(N)$. Parameters @@ -1090,7 +1092,7 @@ def _pcf_cell_list( n_bins : int Number of bins in the distance histogram. self_correlation : bool - If True, assumes species I and J are the same and avoids double-counting + If True, assumes species I and J are the same and avoids double-counting or self-interaction. n_cells : int Number of cells per dimension in the spatial hash grid. @@ -1098,13 +1100,13 @@ def _pcf_cell_list( Returns ------- hist : np.ndarray - A 1D array of length `n_bins` containing the counts of pairs found + A 1D array of length `n_bins` containing the counts of pairs found at each radial distance. Notes ----- - The kernel uses `prange` for parallel execution across points in `pos_i`. - Local histograms are used per thread to prevent race conditions during + The kernel uses `prange` for parallel execution across points in `pos_i`. + Local histograms are used per thread to prevent race conditions during reduction. Periodic boundary conditions are handled via `_periodic_dist_sq`. """ n_i = len(pos_i) @@ -1166,9 +1168,9 @@ def compute_pcf_periodic_fast( """ Compute the Pair Correlation Function (PCF) using cell-list acceleration. - This high-level function coordinates the spatial hashing and histogram - calculation to determine the $g(r)$ function. It normalizes the resulting - histogram by the expected number of pairs in an ideal gas of the same + This high-level function coordinates the spatial hashing and histogram + calculation to determine the $g(r)$ function. It normalizes the resulting + histogram by the expected number of pairs in an ideal gas of the same density, accounting for the toroidal area of each radial bin. Parameters @@ -1184,7 +1186,7 @@ def compute_pcf_periodic_fast( n_bins : int, optional Number of bins for the radial distribution (default 50). self_correlation : bool, optional - Set to True if computing the correlation of a species with itself + Set to True if computing the correlation of a species with itself to avoid self-counting (default False). Returns @@ -1192,15 +1194,15 @@ def compute_pcf_periodic_fast( bin_centers : np.ndarray The central radial distance for each histogram bin. pcf : np.ndarray - The normalized $g(r)$ values. A value of 1.0 indicates no spatial + The normalized $g(r)$ values. A value of 1.0 indicates no spatial correlation; > 1.0 indicates clustering; < 1.0 indicates repulsion. total_pairs : int The total count of pairs found within the `max_distance`. Notes ----- - The function dynamically determines the optimal number of cells for the - spatial hash based on the `max_distance` and grid dimensions to maintain + The function dynamically determines the optimal number of cells for the + spatial hash based on the `max_distance` and grid dimensions to maintain linear time complexity. """ rows, cols = grid_shape @@ -1265,9 +1267,9 @@ def compute_all_pcfs_fast( """ Compute all three species Pair Correlation Functions (PCFs) using cell-list acceleration. - This function calculates the spatial auto-correlations (Prey-Prey, - Predator-Predator) and the cross-correlation (Prey-Predator) for a given - simulation grid. It identifies particle positions and leverages + This function calculates the spatial auto-correlations (Prey-Prey, + Predator-Predator) and the cross-correlation (Prey-Predator) for a given + simulation grid. It identifies particle positions and leverages Numba-accelerated cell lists to handle the computations efficiently. Parameters @@ -1275,7 +1277,7 @@ def compute_all_pcfs_fast( grid : np.ndarray 2D integer array where 1 represents prey and 2 represents predators. max_distance : float, optional - The maximum radial distance for the correlation. Defaults to 1/4 + The maximum radial distance for the correlation. Defaults to 1/4 of the minimum grid dimension if not provided. n_bins : int, optional Number of distance bins for the histogram. Default is 50. @@ -1283,7 +1285,7 @@ def compute_all_pcfs_fast( Returns ------- results : dict - A dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'. + A dictionary with keys 'prey_prey', 'pred_pred', and 'prey_pred'. Each value is a tuple containing: - bin_centers (np.ndarray): Radial distances. - pcf_values (np.ndarray): Normalized g(r) values. @@ -1291,8 +1293,8 @@ def compute_all_pcfs_fast( Notes ----- - The PCF provides insight into the spatial organization of the system. - g(r) > 1 at short distances indicates aggregation (clustering), + The PCF provides insight into the spatial organization of the system. + g(r) > 1 at short distances indicates aggregation (clustering), while g(r) < 1 indicates exclusion or repulsion. """ rows, cols = grid.shape @@ -1346,10 +1348,10 @@ def warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False): """ Pre-compile all Numba-accelerated kernels to avoid first-run latency. - This function executes a single step of the simulation and each analysis - routine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation, - the first call to a decorated function incurs a compilation overhead. - Running this warmup ensures that subsequent experimental runs are timed + This function executes a single step of the simulation and each analysis + routine on a dummy grid. Because Numba uses Just-In-Time (JIT) compilation, + the first call to a decorated function incurs a compilation overhead. + Running this warmup ensures that subsequent experimental runs are timed accurately and perform at full speed. Parameters @@ -1365,8 +1367,8 @@ def warmup_numba_kernels(grid_size: int = 100, directed_hunting: bool = False): Notes ----- - This function checks for `NUMBA_AVAILABLE` before execution. It warms up - the `PPKernel` (random and optionally directed), as well as the + This function checks for `NUMBA_AVAILABLE` before execution. It warms up + the `PPKernel` (random and optionally directed), as well as the spatial analysis functions (`compute_all_pcfs_fast`, `detect_clusters_fast`, etc.). """ if not NUMBA_AVAILABLE: @@ -1401,9 +1403,9 @@ def benchmark_kernels(grid_size: int = 100, n_runs: int = 20): """ Benchmark the execution performance of random vs. directed update kernels. - This utility measures the average time per simulation step for both the - stochastic (random neighbor) and heuristic (directed hunting/reproduction) - update strategies. It accounts for the computational overhead introduced + This utility measures the average time per simulation step for both the + stochastic (random neighbor) and heuristic (directed hunting/reproduction) + update strategies. It accounts for the computational overhead introduced by the "intelligent" search logic used in directed mode. Parameters @@ -1425,7 +1427,7 @@ def benchmark_kernels(grid_size: int = 100, n_runs: int = 20): The function ensures a fair comparison by: 1. Using a fixed seed for reproducible initial grid states. 2. Warming up Numba kernels before timing to exclude JIT compilation latency. - 3. Copying the grid and death arrays for each iteration to maintain + 3. Copying the grid and death arrays for each iteration to maintain consistent population densities throughout the benchmark. """ import time @@ -1503,8 +1505,8 @@ def benchmark_cluster_detection(grid_size: int = 100, n_runs: int = 20): Notes ----- - The benchmark uses a fixed prey density of 30% to ensure a representative - distribution of clusters. It pre-warms the Numba kernels to ensure that + The benchmark uses a fixed prey density of 30% to ensure a representative + distribution of clusters. It pre-warms the Numba kernels to ensure that the measurements reflect execution speed rather than compilation time. """ import time diff --git a/notebooks/plots.ipynb b/notebooks/plots.ipynb index ea33e96..cd569bf 100644 --- a/notebooks/plots.ipynb +++ b/notebooks/plots.ipynb @@ -1444,160 +1444,6 @@ "plt.show()" ] }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABWwAAAG2CAYAAADr489yAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA78FJREFUeJzs3Qd4k1X7BvA76R7QUkrZGwFBkK04QHGgskEQFFBAHJ8DBffCiX6K/v3cIioqQ1A2ihNFBARRlCV779G9R/K/7gOpaUhLW9Kmae/fdeVqM5q+eZO85z3Pec5zLHa73Q4RERERERERERER8TqrtzdARERERERERERERE5SwFZERERERERERESkjFDAVkRERERERERERKSMUMBWREREREREREREpIxQwFZERERERERERESkjFDAVkRERERERERERKSMUMBWREREREREREREpIxQwFZERERERERERESkjFDAVkRERERERERERKSMUMBWypRu3bqhWbNmuZdzzz0XnTt3xgMPPIDY2FiP/q8ffvjB/I/COnLkCL7++muUhCVLluDmm29Gx44dzWX48OFYuXJl7v2rVq0y2+qJfZCTk4NPP/0UnrR///4871vz5s3Rtm1bDB48GD/++GOexw4bNgzPPvtsoZ533bp1+OOPP874f9evX1/k5y7MvnnzzTfRs2fPYj+fiIhrG/fhhx+63Sk8ln3zzTeF2mGebBM8zfW47IrHVef2wvVSXiQmJmL27NlnfNzLL7+Mzz77DN6wZ88enH/++YX+HGVmZqJXr155PqeOz6K7y1tvveWxbeW+vPLKK832jhgxAnv37i3w8X/99Reuv/56tG7d2mzzsmXL8tz/3Xffmdt5/7XXXou5c+fm3sfn7t+/P7Kysjy2/SJSunhOP3nyZFx33XVo1aoVLrjgAtx5553YtGmTx9ozT/dDSgr7YuyTsW/m6J8tXrzYY8//yCOP4Pbbby/V85PNmzeb12G32+GLCvNZ4T7kZ3fmzJmn3Tdp0iRceumlaNOmDe69994z7m/2b7t3724e36dPHxMHccbrvXv3zv18OPe/16xZg1GjRhX5NYpnKGArZc5dd92FX3/91Vx+/vlnvPPOO9i4cSMefPBBr24XD6q//PKLx5+XHRoGpC+//HJ8/vnnmD59Olq2bGkOjOxQeNpPP/2EF154ASWBJ0Z8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8cgjj6BXr17o0aMHpkyZknvf2LFj0b17d/Tp08dceDJAW7duxaBBg3DNNdfg3nvvRWpqqrk9OTkZd955J6677joMGDAAu3fvLoFXKCIiIiIiUrHl5ORg/vz5WLlyJXbs2GEuUrKUYStylrJtFsBihcVqN9dzoNElkfx8+umnZrrMggULTOCVtY46duyIli1bYsOGDZg1axYiIyPz/M2DDz6Ixx9/HJ06dcL//vc/vPvuuxg3bpz5nX/H6zxxYCD4888/184XERERERHxkKysLHzxxRfYtm2byabt3bs3mjZtqv1bwpRhK3KWYq69CkGVQgGrBfCzovoFbbVPpUzJTEjA9kmTsenFl3Ho+x9K/P8dOXIkN0vW+cLbzz//fNx2222moQ8LCzOriR46dAhxcXGIjY3FQw89ZLJv33rrLTPlhvclJSWZYC0NHDgQX3/9tfn9p59+Qr9+/czvnTt3xrFjx3Dw4MESf30iIiIiIiIVQVpaGj777DMTrPX398fgwYPRpk0bb29WhaAMW5GzFOxvR5t7huL4ui0IighH1Q6ttU+lTNkzfSaStm4zvx/6+luE1KyJyPNaltj/q169upkuk999DmvWrMG6devw8ssvm2DrRRddhGeeeQZBQUG44447UKNGDTRp0iTP38TExJjAL/Gn632HDx9GrVq1Suy1iYiIiIiIVARMnJk6daqpWRscHGzq1jLhRkqHArYiZ6tyFQRVqYzaXTuaq5aIqtqnUqZknDiR9/rxvNc9jYFUZtG6mjRpUm6AdcWKFabUwauvvoqIiAhzeeONN3IfO2zYMBP0bdSo0WnP4yhqzwxcV1qdVERERERE5OxxzZD4+HiEh4dj6NCheZJlpOQpYCtylixRMbC27wr77i1AcAgsrS7UPpUypUqb83Hkx5/M79agQESc27xE/19BGbY0b948vPLKK6YGbYcOHcxt69evN1m2jgXMbDabWXWUWba83YG/8zbH/+H1mjVrnnafiIiIiIiIFB/7WTfeeKNJrnFdZ0RKnmrYiniApW4TWC/tAWvHbrAEhxb67xKW/IC/LrsMay+9FLFfzNJ7ISWids/r0GDYjah13TVodt+9CK4e47U9zcXBmFXLxcccwVpHIfsJEyaYUdzMzEyzeNhVV11lyhuEhIRg9erV5nGzZ89G165dze+XXXaZuU6rVq1CaGioArYi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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "α = 1.17 ± 0.03\n", - "β = 0.29 ± 0.02, R² = 0.992\n" - ] - } - ], - "source": [ - "# === Predator Finite-Size Scaling: 3 Panels ===\n", - "\n", - "from scipy import stats as scipy_stats\n", - "\n", - "# Color palette for different L values\n", - "colors_L = plt.cm.Reds(np.linspace(0.35, 0.85, len(grid_sizes)))\n", - "\n", - "# Collect data\n", - "alphas = []\n", - "max_sizes = []\n", - "Ls = []\n", - "Rs = []\n", - "cluster_data = []\n", - "\n", - "for i, L in enumerate(grid_sizes):\n", - " subset = df_phase3[df_phase3[\"grid_size\"] == L]\n", - "\n", - " clusters = []\n", - " for sizes in subset[\"pred_cluster_sizes\"]:\n", - " if isinstance(sizes, list) and len(sizes) > 0:\n", - " clusters.extend(sizes)\n", - "\n", - " if len(clusters) < 50:\n", - " continue\n", - "\n", - " clusters = np.array(clusters)\n", - " fit = powerlaw.Fit(clusters, discrete=True, xmin=1, verbose=False)\n", - " R_tpl_ln, _ = fit.distribution_compare(\n", - " \"truncated_power_law\", \"lognormal\", normalized_ratio=True\n", - " )\n", - "\n", - " # Compute PDF\n", - " unique, counts = np.unique(clusters, return_counts=True)\n", - " probs = counts / len(clusters)\n", - "\n", - " alphas.append(fit.truncated_power_law.alpha)\n", - " max_sizes.append(clusters.max())\n", - " Ls.append(L)\n", - " Rs.append(R_tpl_ln)\n", - " cluster_data.append(\n", - " {\"L\": L, \"unique\": unique, \"probs\": probs, \"color\": colors_L[i]}\n", - " )\n", - "\n", - "# Convert to arrays\n", - "Ls = np.array(Ls)\n", - "alphas = np.array(alphas)\n", - "max_sizes = np.array(max_sizes)\n", - "\n", - "# Fit scaling exponent β\n", - "slope, intercept, r, p, se = scipy_stats.linregress(np.log(Ls), np.log(max_sizes))\n", - "\n", - "# === Plot (3 panels) ===\n", - "fig, axes = plt.subplots(1, 3, figsize=(14, 4.5))\n", - "\n", - "# Left: Cluster distributions\n", - "ax1 = axes[0]\n", - "for data in cluster_data:\n", - " ax1.scatter(\n", - " data[\"unique\"],\n", - " data[\"probs\"],\n", - " color=data[\"color\"],\n", - " s=10,\n", - " alpha=0.6,\n", - " edgecolors=\"none\",\n", - " label=f'L={data[\"L\"]}',\n", - " )\n", - "\n", - "ax1.set_xscale(\"log\")\n", - "ax1.set_yscale(\"log\")\n", - "ax1.set_xlabel(\"Cluster Size\")\n", - "ax1.set_ylabel(\"P(s)\")\n", - "ax1.set_title(\"Predator Cluster Distributions\")\n", - "ax1.legend(loc=\"upper right\", fontsize=8, framealpha=0.95)\n", - "\n", - "# Middle: α vs L\n", - "ax2 = axes[1]\n", - "ax2.plot(Ls, alphas, \"o-\", color=COLORS[\"predator\"], markersize=8, linewidth=1.5)\n", - "ax2.axhline(\n", - " np.mean(alphas),\n", - " color=\"0.5\",\n", - " linestyle=\"--\",\n", - " linewidth=1,\n", - " label=f\"Mean α = {np.mean(alphas):.2f}\",\n", - ")\n", - "ax2.fill_between(\n", - " [min(Ls) * 0.8, max(Ls) * 1.2],\n", - " np.mean(alphas) - np.std(alphas),\n", - " np.mean(alphas) + np.std(alphas),\n", - " color=\"0.5\",\n", - " alpha=0.15,\n", - ")\n", - "\n", - "ax2.set_xscale(\"log\")\n", - "ax2.set_xlabel(\"System Size L\")\n", - "ax2.set_ylabel(\"Exponent α\")\n", - "ax2.set_title(f\"Universal Exponent (α = {np.mean(alphas):.2f} ± {np.std(alphas):.2f})\")\n", - "ax2.set_xlim(min(Ls) * 0.8, max(Ls) * 1.2)\n", - "ax2.set_ylim(1.0, 1.35)\n", - "ax2.legend(loc=\"upper right\", framealpha=0.95)\n", - "\n", - "# Right: max_size scaling\n", - "ax3 = axes[2]\n", - "ax3.loglog(Ls, max_sizes, \"o\", color=COLORS[\"predator\"], markersize=8, label=\"Data\")\n", - "\n", - "# Fit line\n", - "L_fit = np.logspace(np.log10(min(Ls) * 0.8), np.log10(max(Ls) * 1.2), 100)\n", - "max_fit = np.exp(intercept) * L_fit**slope\n", - "ax3.loglog(\n", - " L_fit,\n", - " max_fit,\n", - " \"--\",\n", - " color=\"0.5\",\n", - " linewidth=1.5,\n", - " label=f\"β = {slope:.2f} ± {se:.2f} (R² = {r**2:.2f})\",\n", - ")\n", - "\n", - "ax3.set_xlabel(\"System Size L\")\n", - "ax3.set_ylabel(\"Max Cluster Size\")\n", - "ax3.set_title(f\"Sublinear Scaling (max ~ L^{slope:.2f})\")\n", - "ax3.legend(loc=\"upper left\", framealpha=0.95)\n", - "\n", - "plt.tight_layout()\n", - "plt.savefig(\"plot_predator_fss.png\", dpi=150, bbox_inches=\"tight\")\n", - "plt.show()\n", - "\n", - "print(f\"α = {np.mean(alphas):.2f} ± {np.std(alphas):.2f}\")\n", - "print(f\"β = {slope:.2f} ± {se:.2f}, R² = {r**2:.3f}\")" - ] - }, { "cell_type": "code", "execution_count": 26, @@ -1800,17 +1646,6 @@ "plt.show()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "NOTE:\n", - "\n", - "The Hydra effect does not appear to be associated with spatial criticality. Despite the sharp population transition, prey clustesr follow lognormal distribution across all parameters. Finit-size scaling analysis confirms this is not a finite-size result. The lognormal signature strenghthens with increasing system size L. This is the opposite behavior near a critical point.\n", - "\n", - "Conclusion (?): The Hydra effect represents a population-level transition driven by local birth-death dynamics without scale free structure characteristics of SOC." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -2689,7 +2524,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Phase 6" + "### Phase 5" ] }, { diff --git a/requirements.txt b/requirements.txt index 6a14b5a..5dcd13e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ seaborn black tqdm numba -powerlaw \ No newline at end of file +powerlaw +pdoc \ No newline at end of file diff --git a/scripts/__init__.py b/scripts/__init__.py index 9531307..f19b34a 100644 --- a/scripts/__init__.py +++ b/scripts/__init__.py @@ -1 +1,36 @@ -from ..models.numba_optimized import * +""" +Execution and Orchestration Suite for Predator-Prey Hydra Effect Research. + +This module provides the entry points for running multi-phase simulation +experiments. It is designed for high-performance computing (HPC) environments, +utilizing parallel processing and Numba-accelerated kernels to analyze +self-organized criticality and finite-size scaling. + +## Experimental Phases +The suite is divided into five sequential research phases: +1. **Critical Point Identification**: Parameter sweeps to locate + bifurcation points and phase transitions. +2. **Self-Organization Analysis**: Observations of evolutionary drift + toward critical states. +3. **Finite-Size Scaling (FSS)**: Analysis of cluster size cutoffs + across varying grid dimensions ($L$). +4. **Sensitivity Analysis**: Comprehensive 4D parameter sweeps across + survival regimes. +5. **Directed Hunting Comparisons**: Sensitivity analysis using + non-random predator search kernels. + +## Performance Features +- **Parallelization**: Automated CPU core detection and SLURM integration + via `joblib`. +- **Reproducibility**: Deterministic seed generation using SHA-256 hashing + of parameter states. +- **Incremental Persistence**: Results are saved in JSON Lines (JSONL) + format to ensure data recovery during long-running batches. + +## Usage +Experiments should be invoked from the project root: +```bash +python scripts/experiments.py --phase 1 --output results/ +""" + +from models.numba_optimized import * \ No newline at end of file diff --git a/scripts/experiments.py b/scripts/experiments.py index 1aa8916..832e4b0 100644 --- a/scripts/experiments.py +++ b/scripts/experiments.py @@ -15,6 +15,7 @@ python experiments.py --phase all # Run all phases python experiments.py --phase 1 --output results/ # Custom output """ + import argparse import hashlib import json @@ -65,28 +66,29 @@ def set_numba_seed(seed): # Utility Functions # ============================================================================= + def generate_unique_seed(params: dict, rep: int) -> int: """ Create a deterministic seed from a dictionary of parameters and a repetition index. - This function serializes the input dictionary into a sorted JSON string, - appends the repetition count, and hashes the resulting string using SHA-256. - The first 8 characters of the hex digest are then converted to an integer + This function serializes the input dictionary into a sorted JSON string, + appends the repetition count, and hashes the resulting string using SHA-256. + The first 8 characters of the hex digest are then converted to an integer to provide a stable, unique seed for random number generators. Parameters ---------- params : dict - A dictionary of configuration parameters. Keys are sorted to ensure - determinism regardless of insertion order. + A dictionary of configuration parameters. Keys are sorted to ensure + determinism regardless of insertion order. rep : int - The repetition or iteration index, used to ensure different seeds - are generated for the same parameter set across multiple runs. + The repetition or iteration index, used to ensure different seeds + are generated for the same parameter set across multiple runs. Returns ------- int - A unique integer seed derived from the input parameters. + A unique integer seed derived from the input parameters. Examples -------- @@ -140,7 +142,7 @@ def get_evolved_stats(model, param: str) -> Dict: Parameters ---------- model : object - The simulation model instance containing a `cell_params` attribute + The simulation model instance containing a `cell_params` attribute with a `.get()` method. param : str The name of the parameter to calculate statistics for. @@ -178,7 +180,7 @@ def get_evolved_stats(model, param: str) -> Dict: def average_pcfs( - pcf_list: List[Tuple[np.ndarray, np.ndarray, int]] + pcf_list: List[Tuple[np.ndarray, np.ndarray, int]], ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ Average multiple Pair Correlation Function (PCF) measurements and calculate standard error. @@ -202,7 +204,7 @@ def average_pcfs( Examples -------- - >>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10), + >>> data = [(np.array([0, 1]), np.array([1.0, 2.0]), 10), ... (np.array([0, 1]), np.array([1.2, 1.8]), 12)] >>> dist, mean, se = average_pcfs(data) >>> mean @@ -241,7 +243,7 @@ def save_results_jsonl(results: List[Dict], output_path: Path): Notes ----- - The file is opened in 'w' (write) mode, which will overwrite any existing + The file is opened in 'w' (write) mode, which will overwrite any existing content at the specified path. Examples @@ -258,18 +260,18 @@ def save_results_npz(results: List[Dict], output_path: Path): """ Save simulation results to a compressed NumPy (.npz) binary file. - This function flattens a list of result dictionaries into a single - dictionary of NumPy arrays, prefixing keys with the run index to - maintain data separation. The resulting file is compressed to + This function flattens a list of result dictionaries into a single + dictionary of NumPy arrays, prefixing keys with the run index to + maintain data separation. The resulting file is compressed to reduce storage space. Parameters ---------- results : list of dict - A list where each dictionary contains key-value pairs of + A list where each dictionary contains key-value pairs of simulation data (e.g., arrays, lists, or scalars). output_path : Path - The file system path (pathlib.Path) where the compressed + The file system path (pathlib.Path) where the compressed NPZ file will be saved. Returns @@ -279,7 +281,7 @@ def save_results_npz(results: List[Dict], output_path: Path): Notes ----- The keys in the saved file follow the format 'run_{index}_{original_key}'. - Values are automatically converted to NumPy arrays if they are not + Values are automatically converted to NumPy arrays if they are not already. Examples @@ -298,7 +300,7 @@ def load_results_jsonl(input_path: Path) -> List[Dict]: """ Load simulation results from a JSON Lines (JSONL) formatted file. - This function reads a file line-by-line, parsing each line as an + This function reads a file line-by-line, parsing each line as an independent JSON object and aggregating them into a list of dictionaries. Parameters @@ -350,8 +352,8 @@ def run_single_simulation( """ Run a single Predator-Prey (PP) simulation and collect comprehensive metrics. - This function initializes a Cellular Automata model, executes a warmup phase - to reach steady state, and then performs a measurement phase to track + This function initializes a Cellular Automata model, executes a warmup phase + to reach steady state, and then performs a measurement phase to track population dynamics, spatial clustering, and evolutionary changes. Parameters @@ -369,13 +371,13 @@ def run_single_simulation( seed : int Random seed for ensuring reproducibility of the simulation run. cfg : Config - A configuration object containing simulation hyperparameters (densities, + A configuration object containing simulation hyperparameters (densities, sampling rates, timing, etc.). with_evolution : bool, optional - If True, enables the evolution of the 'prey_death' parameter within + If True, enables the evolution of the 'prey_death' parameter within the model (default is False). compute_pcf : bool, optional - Explicit toggle for Pair Correlation Function calculation. If None, + Explicit toggle for Pair Correlation Function calculation. If None, it is determined by `cfg.pcf_sample_rate` (default is None). Returns @@ -391,8 +393,8 @@ def run_single_simulation( Notes ----- - The function relies on several external utilities: `count_populations`, - `get_evolved_stats`, `get_cluster_stats_fast`, `compute_all_pcfs_fast`, + The function relies on several external utilities: `count_populations`, + `get_evolved_stats`, `get_cluster_stats_fast`, `compute_all_pcfs_fast`, and `average_pcfs`. """ @@ -580,26 +582,26 @@ def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di """ Execute Phase 1 of the simulation: a parameter sweep to identify critical points. - This function performs a 1D sweep across varying prey mortality rates while - keeping other parameters fixed. It utilizes parallel execution via joblib - and saves results incrementally to a JSONL file to ensure data integrity + This function performs a 1D sweep across varying prey mortality rates while + keeping other parameters fixed. It utilizes parallel execution via joblib + and saves results incrementally to a JSONL file to ensure data integrity during long-running batches. Parameters ---------- cfg : Config - Configuration object containing simulation hyperparameters, sweep + Configuration object containing simulation hyperparameters, sweep ranges, and execution settings (n_jobs, grid_size, etc.). output_dir : Path Directory where result files (JSONL) and metadata (JSON) will be stored. logger : logging.Logger - Logger instance for tracking simulation progress and recording + Logger instance for tracking simulation progress and recording operational metadata. Returns ------- all_results : list of dict - A list of dictionaries containing the metrics collected from every + A list of dictionaries containing the metrics collected from every individual simulation run in the sweep. Notes @@ -608,7 +610,7 @@ def run_phase1(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di 1. Pre-warms Numba kernels for performance. 2. Generates a deterministic set of simulation jobs using unique seeds. 3. Executes simulations in parallel using a generator for memory efficiency. - 4. Records metadata including a timestamp and a serialized snapshot of + 4. Records metadata including a timestamp and a serialized snapshot of the configuration. """ from joblib import Parallel, delayed @@ -674,15 +676,15 @@ def run_phase2(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di """ Execute Phase 2 of the simulation: self-organization and criticality analysis. - This phase tests the Self-Organized Criticality (SOC) hypothesis by - initializing simulations at different points in the parameter space and - observing whether evolutionary pressure drives the system toward a + This phase tests the Self-Organized Criticality (SOC) hypothesis by + initializing simulations at different points in the parameter space and + observing whether evolutionary pressure drives the system toward a common critical point, regardless of initial prey mortality rates. Parameters ---------- cfg : Config - Configuration object containing simulation hyperparameters, evolution + Configuration object containing simulation hyperparameters, evolution settings, and execution constraints. output_dir : Path Directory where result files (JSONL) and metadata (JSON) will be stored. @@ -692,7 +694,7 @@ def run_phase2(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di Returns ------- all_results : list of dict - A list of dictionaries containing metrics from the evolutionary + A list of dictionaries containing metrics from the evolutionary simulation runs. Notes @@ -824,18 +826,18 @@ def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di """ Execute Phase 3 of the simulation: Finite-Size Scaling (FSS) analysis. - This phase investigates how spatial structures, specifically cluster size - cutoffs, scale with the system size (L) at the critical point identified - in Phase 1. This is essential for determining the universality class of + This phase investigates how spatial structures, specifically cluster size + cutoffs, scale with the system size (L) at the critical point identified + in Phase 1. This is essential for determining the universality class of the phase transition. Parameters ---------- cfg : Config - Configuration object containing critical point parameters, the list of + Configuration object containing critical point parameters, the list of grid sizes to test, and execution settings. output_dir : Path - Directory where result files (JSONL) and FSS metadata (JSON) will be + Directory where result files (JSONL) and FSS metadata (JSON) will be stored. logger : logging.Logger Logger instance for tracking progress across different grid sizes. @@ -843,7 +845,7 @@ def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di Returns ------- all_results : list of dict - A list of dictionaries containing metrics and cluster statistics for + A list of dictionaries containing metrics and cluster statistics for each grid size and replicate. Notes @@ -851,7 +853,7 @@ def run_phase4(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di The function performs the following: 1. Iterates through multiple grid sizes defined in `cfg.grid_sizes`. 2. Generates parallel jobs for each size using critical birth/death rates. - 3. Saves results incrementally to allow for post-simulation analysis of + 3. Saves results incrementally to allow for post-simulation analysis of power-law exponents. """ from joblib import Parallel, delayed @@ -947,35 +949,35 @@ def run_phase5(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di """ Execute Phase 5 of the simulation: Global 4D parameter sweep with directed hunting. - This phase performs a comprehensive sensitivity analysis by varying four key - parameters (prey birth/death and predator birth/death) while directed - hunting is enabled. The results allow for a direct comparison with Phase 4 - to determine how predator search behavior shifts the system's critical + This phase performs a comprehensive sensitivity analysis by varying four key + parameters (prey birth/death and predator birth/death) while directed + hunting is enabled. The results allow for a direct comparison with Phase 4 + to determine how predator search behavior shifts the system's critical thresholds and stability. Parameters ---------- cfg : Config - Configuration object containing simulation hyperparameters, parallel + Configuration object containing simulation hyperparameters, parallel execution settings, and the fixed grid size for this phase. output_dir : Path - Directory where the result JSONL file and execution metadata will + Directory where the result JSONL file and execution metadata will be stored. logger : logging.Logger - Logger instance for tracking the progress of the high-volume + Logger instance for tracking the progress of the high-volume simulation batch. Returns ------- all_results : list of dict - A list of dictionaries containing metrics for every simulation in + A list of dictionaries containing metrics for every simulation in the 4D parameter grid. Notes ----- - The function utilizes a Cartesian product of parameter ranges to build a - job list of over 13,000 unique parameter sets (multiplied by replicates). - Seeds are uniquely generated to distinguish these runs from other phases + The function utilizes a Cartesian product of parameter ranges to build a + job list of over 13,000 unique parameter sets (multiplied by replicates). + Seeds are uniquely generated to distinguish these runs from other phases even if parameter values overlap. """ from joblib import Parallel, delayed @@ -1088,17 +1090,17 @@ def run_phase5(cfg: Config, output_dir: Path, logger: logging.Logger) -> List[Di def main(): """ - Orchestrate the predator-prey experimental suite across multiple phases. + Organize the predator-prey experimental suite across multiple phases. - This entry point handles command-line arguments, sets up logging and output - directories, and executes the requested simulation phases (1-6). It - supports parallel execution, dry runs for runtime estimation, and + This entry point handles command-line arguments, sets up logging and output + directories, and executes the requested simulation phases (1-5). It + supports parallel execution, dry runs for runtime estimation, and automated configuration persistence. Notes ----- - The script dynamically retrieves phase-specific configurations using - `get_phase_config` and dispatches execution to the corresponding runner + The script dynamically retrieves phase-specific configurations using + `get_phase_config` and dispatches execution to the corresponding runner in the `PHASE_RUNNERS` mapping. """ parser = argparse.ArgumentParser( From 6b35639909923ff0d5b0065430946f4a9ae88c99 Mon Sep 17 00:00:00 2001 From: Kimon Anagnostopoulos <96205326+codegithubka@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:34:37 +0100 Subject: [PATCH 5/7] Clean up README by removing references section Removed references section and optional dependencies. --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 97ba3fb..437c284 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,3 @@ Metadata files (`phase{N}_metadata.json`) accompany each results file with confi **Optional:** - matplotlib (visualization) - scipy (additional analysis) - ---- - -## References From 3554dd5a37939a9337cd3b20bca0cc70ba18f8a0 Mon Sep 17 00:00:00 2001 From: Kimon Date: Sat, 31 Jan 2026 13:41:00 +0100 Subject: [PATCH 6/7] Add documentation --- docs/{scripts => }/experiments.html | 4714 +++++++++++++------------- docs/index.html | 4 +- docs/models.html | 291 -- docs/models/CA.html | 3539 ++++++++++---------- docs/models/config.html | 2261 ++++--------- docs/models/numba_optimized.html | 4806 +++++++++++++-------------- docs/scripts.html | 327 -- docs/search.js | 2 +- 8 files changed, 7123 insertions(+), 8821 deletions(-) rename docs/{scripts => }/experiments.html (68%) delete mode 100644 docs/models.html delete mode 100644 docs/scripts.html diff --git a/docs/scripts/experiments.html b/docs/experiments.html similarity index 68% rename from docs/scripts/experiments.html rename to docs/experiments.html index b75cc52..1f081d6 100644 --- a/docs/scripts/experiments.html +++ b/docs/experiments.html @@ -4,7 +4,7 @@ - scripts.experiments API documentation + experiments API documentation @@ -16,23 +16,27 @@