@@ -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 )
61120def _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
0 commit comments