Skip to content

Commit d9d2df8

Browse files
memory efficient game of life
1 parent 7bf619c commit d9d2df8

File tree

1 file changed

+45
-34
lines changed

1 file changed

+45
-34
lines changed

content/game_of_life.py

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
#!/usr/bin/env python3
12
import argparse
23
from pathlib import Path
34

45
import numpy as np
56
import cupy as cp
67

8+
79
# ─────────────────────────────────────────────────────────────────────────────
810
# 1) Core update functions (no plotting/animation)
911
# ─────────────────────────────────────────────────────────────────────────────
@@ -59,29 +61,32 @@ def life_step_naive(grid: np.ndarray) -> np.ndarray:
5961
# 2) Simulation functions (no animation)
6062
# ─────────────────────────────────────────────────────────────────────────────
6163

62-
def simulate_life_numpy(N: int, timesteps: int, p_alive: float = 0.2):
63-
grid = np.random.choice([0, 1], size=(N, N), p=[1-p_alive, p_alive])
64-
history = []
64+
def simulate_life_numpy(N: int, timesteps: int, p_alive: float = 0.2, record_history: bool = False):
65+
grid = np.random.choice([0, 1], size=(N, N), p=[1 - p_alive, p_alive])
66+
history = [] if record_history else None
6567
for _ in range(timesteps):
66-
history.append(grid.copy())
68+
if record_history:
69+
history.append(grid.copy())
6770
grid = life_step_numpy(grid)
6871
return history
6972

7073

71-
def simulate_life_cupy(N: int, timesteps: int, p_alive: float = 0.2):
74+
def simulate_life_cupy(N: int, timesteps: int, p_alive: float = 0.2, record_history: bool = False):
7275
grid_gpu = (cp.random.random((N, N)) < p_alive).astype(cp.int32)
73-
history = []
76+
history = [] if record_history else None
7477
for _ in range(timesteps):
75-
history.append(cp.asnumpy(grid_gpu))
78+
if record_history:
79+
history.append(cp.asnumpy(grid_gpu))
7680
grid_gpu = life_step_gpu(grid_gpu)
7781
return history
7882

7983

80-
def simulate_life_naive(N: int, timesteps: int, p_alive: float = 0.2):
81-
grid = np.random.choice([0, 1], size=(N, N), p=[1-p_alive, p_alive])
82-
history = []
84+
def simulate_life_naive(N: int, timesteps: int, p_alive: float = 0.2, record_history: bool = False):
85+
grid = np.random.choice([0, 1], size=(N, N), p=[1 - p_alive, p_alive])
86+
history = [] if record_history else None
8387
for _ in range(timesteps):
84-
history.append(grid.copy())
88+
if record_history:
89+
history.append(grid.copy())
8590
grid = life_step_naive(grid)
8691
return history
8792

@@ -113,7 +118,7 @@ def _update(idx):
113118

114119

115120
# ─────────────────────────────────────────────────────────────────────────────
116-
# 4) CLI entry-points (only --size and --timesteps)
121+
# 4) CLI entry-points (only --size, --timesteps, --save-gif)
117122
# ─────────────────────────────────────────────────────────────────────────────
118123

119124
def run_life_numpy():
@@ -124,16 +129,18 @@ def run_life_numpy():
124129
args = p.parse_args()
125130

126131
print(f"[NumPy] Args received: {args}")
127-
128-
history = simulate_life_numpy(args.size, args.timesteps)
132+
record = args.save_gif and args.size <= 100
133+
history = simulate_life_numpy(args.size, args.timesteps, record_history=record)
129134

130135
if args.save_gif:
131-
output = Path("game_of_life_cpu.gif")
132-
animate_life(history, output)
133-
print(f"Saved CPU GIF to {output}")
136+
if record:
137+
output = Path("game_of_life_cpu.gif")
138+
animate_life(history, output)
139+
print(f"Saved CPU GIF to {output}")
140+
else:
141+
print("[NumPy] Problem size > 100: cannot save history or create GIF.")
134142
else:
135-
print("[NumPy] GIF creation skipped.")
136-
143+
print("[NumPy] GIF creation skipped; history not saved.")
137144

138145

139146
def run_life_cupy():
@@ -144,16 +151,18 @@ def run_life_cupy():
144151
args = p.parse_args()
145152

146153
print(f"[CuPy] Args received: {args}")
147-
148-
history = simulate_life_cupy(args.size, args.timesteps)
154+
record = args.save_gif and args.size <= 100
155+
history = simulate_life_cupy(args.size, args.timesteps, record_history=record)
149156

150157
if args.save_gif:
151-
output = Path("game_of_life_gpu.gif")
152-
animate_life(history, output)
153-
print(f"Saved GPU GIF to {output}")
158+
if record:
159+
output = Path("game_of_life_gpu.gif")
160+
animate_life(history, output)
161+
print(f"Saved GPU GIF to {output}")
162+
else:
163+
print("[CuPy] Problem size > 100: cannot save history or create GIF.")
154164
else:
155-
print("[CuPy] GIF creation skipped.")
156-
165+
print("[CuPy] GIF creation skipped; history not saved.")
157166

158167

159168
def run_life_naive():
@@ -163,14 +172,16 @@ def run_life_naive():
163172
p.add_argument("--save-gif", action="store_true", help="Save GIF animation")
164173
args = p.parse_args()
165174

166-
print(f"[Naive] Args received: {args}")
167-
168-
history = simulate_life_naive(args.size, args.timesteps)
175+
print(f"[Naive] Args received: {args}")
176+
record = args.save_gif and args.size <= 100
177+
history = simulate_life_naive(args.size, args.timesteps, record_history=record)
169178

170179
if args.save_gif:
171-
output = Path("game_of_life_naive.gif")
172-
animate_life(history, output)
173-
print(f"Saved Naive GIF to {output}")
180+
if record:
181+
output = Path("game_of_life_naive.gif")
182+
animate_life(history, output)
183+
print(f"Saved Naive GIF to {output}")
184+
else:
185+
print("[Naive] Problem size > 100: cannot save history or create GIF.")
174186
else:
175-
print("[Naive] GIF creation skipped.")
176-
187+
print("[Naive] GIF creation skipped; history not saved.")

0 commit comments

Comments
 (0)