Skip to content

Commit 7586061

Browse files
authored
Merge pull request #20 from codegithubka/storm
Directed reproduction and phase 6 config
2 parents ff0410b + 82fe3ad commit 7586061

4 files changed

Lines changed: 100 additions & 166 deletions

File tree

docs/experiments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ These phases should be completed sequentially, deepening our understanding at ea
3232
- Look for critical slowing down: perturbations to states closer to the critical point should more slowly return to the steady state
3333
- This requires time series data
3434
### Phase 6: model extensions
35-
- Investigate whether hydra effect and SOC still occur with diffusion and directed movement
35+
- Investigate whether hydra effect and SOC still occur with diffusion and directed reproduction
3636

3737
# Todo
3838
The main functionality is all complete. Thus, the models folder should be relatively untouched.

models/config.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -263,20 +263,16 @@ def estimate_runtime(self, n_cores: int = 32) -> str:
263263
timeseries_subsample=1, # Full resolution for autocorrelation
264264
)
265265

266-
# Phase 6: Model extensions (directed hunting); same config as phase 1 but with directed hunting
266+
# Phase 6: Model extensions (directed reproduction); same config as phase 4 but with directed reproduction
267267
PHASE6_CONFIG = Config(
268-
grid_size=1000,
269-
n_prey_death=20,
270-
prey_birth=0.2,
271-
prey_death_range=(0.09, 0.12),
272-
predator_birth=0.8,
273-
predator_death=0.05,
274-
n_replicates=30,
275-
warmup_steps=1000,
276-
measurement_steps=1000,
277-
collect_pcf=True,
278-
pcf_sample_rate=0.2,
279-
save_timeseries=False,
268+
grid_size=250,
269+
n_replicates=10,
270+
warmup_steps=500,
271+
measurement_steps=500,
272+
with_evolution=False,
273+
collect_pcf=False,
274+
save_timeseries=True,
275+
timeseries_subsample=10,
280276
directed_hunting=True,
281277
)
282278

models/numba_optimized.py

Lines changed: 82 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,65 @@ def set_numba_seed(seed: int) -> None:
5656
# ============================================================================
5757
# PREDATOR-PREY KERNELS
5858
# ============================================================================
59+
@njit(cache=True)
60+
def _pp_async_kernel_fast(
61+
grid: np.ndarray,
62+
prey_death_arr: np.ndarray,
63+
p_birth_val: float,
64+
p_death_val: float,
65+
pred_birth_val: float,
66+
pred_death_val: float,
67+
dr_arr: np.ndarray,
68+
dc_arr: np.ndarray,
69+
evolve_sd: float,
70+
evolve_min: float,
71+
evolve_max: float,
72+
evolution_stopped: bool,
73+
occupied_buffer: np.ndarray,
74+
) -> np.ndarray:
75+
"""Partially synchronous predator-prey update kernel."""
76+
rows, cols = grid.shape
77+
n_shifts = len(dr_arr)
78+
grid_copy = grid.copy()
79+
prey_death_arr_copy = prey_death_arr.copy()
80+
81+
prey_death = np.random.random(size=grid.shape)
82+
grid_copy[(grid == 1) & (prey_death < prey_death_arr)] = 0
83+
prey_death_arr_copy[(grid == 1) & (prey_death < prey_death_arr)] = np.nan
84+
85+
pred_death = np.random.random(size=grid.shape)
86+
grid_copy[(grid == 2) & (pred_death < pred_death_val)] = 0
87+
88+
count = np.count_nonzero(grid)
89+
indices = np.random.permutation(count)
90+
rs = indices // cols
91+
cs = indices % cols
92+
93+
nb = np.random.randint(0, n_shifts, size=count)
94+
nrs = (rs + dr_arr[nb]) % rows
95+
ncs = (cs + dc_arr[nb]) % cols
96+
97+
for r, c, nr, nc in zip(rs, cs, nrs, ncs):
98+
state = grid[r, c]
99+
nstate = grid[nr, nc]
100+
101+
if state == 1 and nstate == 0 and np.random.random() < p_birth_val:
102+
grid_copy[nr, nc] = 1
103+
parent_val = prey_death_arr[r, c]
104+
if not evolution_stopped:
105+
child_val = parent_val + np.random.normal(0, evolve_sd)
106+
prey_death_arr_copy[nr, nc] = np.clip(child_val, evolve_min, evolve_max)
107+
else:
108+
prey_death_arr_copy[nr, nc] = parent_val
109+
110+
elif state == 2 and nstate == 1 and np.random.random() < pred_birth_val:
111+
grid_copy[nr, nc] = 2
112+
prey_death_arr_copy[nr, nc] = np.nan
113+
114+
grid = grid_copy
115+
prey_death_arr = prey_death_arr_copy
116+
117+
return grid
59118

60119
@njit(cache=True)
61120
def _pp_async_kernel_random(
@@ -148,7 +207,7 @@ def _pp_async_kernel_directed(
148207
evolution_stopped: bool,
149208
occupied_buffer: np.ndarray,
150209
) -> np.ndarray:
151-
"""Async predator-prey update kernel with directed hunting."""
210+
"""Async predator-prey update kernel with directed reproduction."""
152211
rows, cols = grid.shape
153212
n_shifts = len(dr_arr)
154213

@@ -170,19 +229,22 @@ def _pp_async_kernel_directed(
170229
c = occupied_buffer[i, 1]
171230

172231
state = grid[r, c]
173-
if state == 0:
174-
continue
175232

176233
if state == 1: # PREY
177-
nbi = np.random.randint(0, n_shifts)
178-
nr = (r + dr_arr[nbi]) % rows
179-
nc = (c + dc_arr[nbi]) % cols
180-
181234
if np.random.random() < prey_death_arr[r, c]:
182235
grid[r, c] = 0
183236
prey_death_arr[r, c] = np.nan
184-
elif grid[nr, nc] == 0:
185-
if np.random.random() < p_birth_val:
237+
elif np.random.random() < p_birth_val:
238+
valid = []
239+
for k in range(n_shifts):
240+
nr = (r + dr_arr[k]) % rows
241+
nc = (c + dc_arr[k]) % cols
242+
if grid[nr, nc] == 0:
243+
valid.append(k)
244+
if len(valid) > 0:
245+
choice = np.random.choice(valid)
246+
nr = (r + dr_arr[choice]) % rows
247+
nc = (c + dc_arr[choice]) % cols
186248
grid[nr, nc] = 1
187249
parent_val = prey_death_arr[r, c]
188250
if not evolution_stopped:
@@ -195,44 +257,22 @@ def _pp_async_kernel_directed(
195257
else:
196258
prey_death_arr[nr, nc] = parent_val
197259

198-
elif state == 2: # PREDATOR - directed hunting
260+
elif state == 2: # PREDATOR
199261
if np.random.random() < pred_death_val:
200262
grid[r, c] = 0
201-
continue
202-
203-
prey_count = 0
204-
for k in range(n_shifts):
205-
check_r = (r + dr_arr[k]) % rows
206-
check_c = (c + dc_arr[k]) % cols
207-
if grid[check_r, check_c] == 1:
208-
prey_count += 1
209-
210-
if prey_count > 0:
211-
target_idx = np.random.randint(0, prey_count)
212-
found = 0
213-
nr, nc = 0, 0
263+
elif np.random.random() < pred_birth_val:
264+
valid = []
214265
for k in range(n_shifts):
215-
check_r = (r + dr_arr[k]) % rows
216-
check_c = (c + dc_arr[k]) % cols
217-
if grid[check_r, check_c] == 1:
218-
if found == target_idx:
219-
nr = check_r
220-
nc = check_c
221-
break
222-
found += 1
223-
224-
if np.random.random() < pred_birth_val:
266+
nr = (r + dr_arr[k]) % rows
267+
nc = (c + dc_arr[k]) % cols
268+
if grid[nr, nc] == 1:
269+
valid.append(k)
270+
if len(valid) > 0:
271+
choice = np.random.choice(valid)
272+
nr = (r + dr_arr[choice]) % rows
273+
nc = (c + dc_arr[choice]) % cols
225274
grid[nr, nc] = 2
226275
prey_death_arr[nr, nc] = np.nan
227-
else:
228-
nbi = np.random.randint(0, n_shifts)
229-
nr = (r + dr_arr[nbi]) % rows
230-
nc = (c + dc_arr[nbi]) % cols
231-
232-
if grid[nr, nc] == 1:
233-
if np.random.random() < pred_birth_val:
234-
grid[nr, nc] = 2
235-
prey_death_arr[nr, nc] = np.nan
236276

237277
return grid
238278

notebooks/phase1.ipynb

Lines changed: 8 additions & 110 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)