Update app.py
Browse files
app.py
CHANGED
@@ -4,30 +4,18 @@ import streamlit as st
|
|
4 |
import plotly.graph_objects as go
|
5 |
from plotly.subplots import make_subplots
|
6 |
import time
|
7 |
-
import threading
|
8 |
-
import queue
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
# Your classes and functions...
|
13 |
|
14 |
class Organelle:
|
15 |
def __init__(self, type):
|
16 |
self.type = type
|
17 |
|
18 |
-
class Modification:
|
19 |
-
def __init__(self, name, effect):
|
20 |
-
self.name = name
|
21 |
-
self.effect = effect
|
22 |
-
|
23 |
class Cell:
|
24 |
def __init__(self, x, y, cell_type="prokaryote"):
|
25 |
self.x = x
|
26 |
self.y = y
|
27 |
self.energy = 100
|
28 |
self.cell_type = cell_type
|
29 |
-
self.organelles =
|
30 |
-
self.modifications = []
|
31 |
self.size = 1
|
32 |
self.color = "lightblue"
|
33 |
self.division_threshold = 150
|
@@ -36,33 +24,32 @@ class Cell:
|
|
36 |
|
37 |
def update_properties(self):
|
38 |
if self.cell_type == "early_eukaryote":
|
39 |
-
self.organelles.
|
40 |
self.color = "green"
|
41 |
self.size = 2
|
42 |
elif self.cell_type == "advanced_eukaryote":
|
43 |
-
self.organelles.
|
44 |
self.color = "red"
|
45 |
self.size = 3
|
46 |
elif self.cell_type == "plant_like":
|
47 |
-
self.organelles.
|
48 |
self.color = "darkgreen"
|
49 |
self.size = 4
|
|
|
|
|
|
|
|
|
50 |
|
51 |
def move(self, environment):
|
52 |
-
dx = random.uniform(-1, 1)
|
53 |
-
dy = random.uniform(-1, 1)
|
54 |
self.x = max(0, min(environment.width - 1, self.x + dx))
|
55 |
self.y = max(0, min(environment.height - 1, self.y + dy))
|
56 |
self.energy -= 0.5 * self.size
|
57 |
|
58 |
def feed(self, environment):
|
59 |
base_energy = environment.grid[int(self.y)][int(self.x)] * 0.1
|
60 |
-
if "chloroplast" in
|
61 |
base_energy += environment.light_level * 2
|
62 |
-
|
63 |
-
for mod in self.modifications:
|
64 |
-
base_energy *= mod.effect
|
65 |
-
|
66 |
self.energy += base_energy
|
67 |
environment.grid[int(self.y)][int(self.x)] *= 0.9
|
68 |
|
@@ -74,7 +61,6 @@ class Cell:
|
|
74 |
self.energy /= 2
|
75 |
new_cell = Cell(self.x, self.y, self.cell_type)
|
76 |
new_cell.organelles = self.organelles.copy()
|
77 |
-
new_cell.modifications = self.modifications.copy()
|
78 |
return new_cell
|
79 |
return None
|
80 |
|
@@ -88,30 +74,17 @@ class Cell:
|
|
88 |
new_cell_type = "early_eukaryote"
|
89 |
elif self.cell_type == "early_eukaryote":
|
90 |
new_cell_type = "advanced_eukaryote"
|
|
|
|
|
|
|
|
|
91 |
|
92 |
-
new_cell = Cell(
|
93 |
-
(self.x + other.x) / 2,
|
94 |
-
(self.y + other.y) / 2,
|
95 |
-
new_cell_type
|
96 |
-
)
|
97 |
new_cell.energy = self.energy + other.energy
|
98 |
-
new_cell.organelles =
|
99 |
-
new_cell.modifications = list(set(self.modifications + other.modifications))
|
100 |
new_cell.update_properties()
|
101 |
return new_cell
|
102 |
|
103 |
-
def acquire_modification(self):
|
104 |
-
possible_mods = [
|
105 |
-
Modification("Enhanced metabolism", 1.2),
|
106 |
-
Modification("Thick cell wall", 0.8),
|
107 |
-
Modification("Efficient energy storage", 1.1),
|
108 |
-
Modification("Rapid division", 0.9)
|
109 |
-
]
|
110 |
-
new_mod = random.choice(possible_mods)
|
111 |
-
if new_mod not in self.modifications:
|
112 |
-
self.modifications.append(new_mod)
|
113 |
-
self.color = "purple" # Visual indicator of modification
|
114 |
-
|
115 |
class Environment:
|
116 |
def __init__(self, width, height):
|
117 |
self.width = width
|
@@ -122,7 +95,7 @@ class Environment:
|
|
122 |
self.time = 0
|
123 |
self.population_history = {
|
124 |
"prokaryote": [], "early_eukaryote": [],
|
125 |
-
"advanced_eukaryote": [], "plant_like": [], "
|
126 |
}
|
127 |
|
128 |
def add_cell(self, cell):
|
@@ -159,102 +132,89 @@ class Environment:
|
|
159 |
self.cells.extend(new_cells)
|
160 |
self.cells = [cell for cell in self.cells if cell not in cells_to_remove]
|
161 |
|
162 |
-
# Introduce mutations and modifications
|
163 |
-
for cell in self.cells:
|
164 |
-
if random.random() < 0.0001: # 0.01% chance of mutation
|
165 |
-
if cell.cell_type == "early_eukaryote":
|
166 |
-
cell.cell_type = "advanced_eukaryote"
|
167 |
-
elif cell.cell_type == "advanced_eukaryote" and random.random() < 0.5:
|
168 |
-
cell.cell_type = "plant_like"
|
169 |
-
cell.update_properties()
|
170 |
-
|
171 |
-
if random.random() < 0.0005: # 0.05% chance of acquiring a modification
|
172 |
-
cell.acquire_modification()
|
173 |
-
|
174 |
# Record population counts
|
175 |
for cell_type in self.population_history.keys():
|
176 |
-
if cell_type
|
177 |
-
count = len([cell for cell in self.cells if cell.cell_type == cell_type and not cell.modifications])
|
178 |
-
else:
|
179 |
-
count = len([cell for cell in self.cells if cell.modifications])
|
180 |
self.population_history[cell_type].append(count)
|
181 |
|
182 |
def get_visualization_data(self):
|
183 |
-
cell_data = {
|
184 |
-
|
185 |
-
"early_eukaryote": {"x": [], "y": [], "size": [], "color": "green", "symbol": "square"},
|
186 |
-
"advanced_eukaryote": {"x": [], "y": [], "size": [], "color": "red", "symbol": "diamond"},
|
187 |
-
"plant_like": {"x": [], "y": [], "size": [], "color": "darkgreen", "symbol": "star"},
|
188 |
-
"modified": {"x": [], "y": [], "size": [], "color": "purple", "symbol": "cross"}
|
189 |
-
}
|
190 |
|
191 |
for cell in self.cells:
|
192 |
-
cell_type
|
193 |
-
cell_data[cell_type]["
|
194 |
-
cell_data[cell_type]["
|
195 |
-
cell_data[cell_type]["size"].append(cell.size * 3)
|
196 |
|
197 |
-
return cell_data, self.population_history
|
198 |
|
199 |
def setup_figure(env):
|
200 |
-
cell_types =
|
201 |
-
fig = make_subplots(rows=2, cols=
|
202 |
subplot_titles=("Cell Distribution", "Total Population",
|
203 |
-
"
|
204 |
-
"Advanced Eukaryotes", "Plant-like & Modified"),
|
205 |
vertical_spacing=0.1,
|
206 |
horizontal_spacing=0.05)
|
207 |
|
|
|
|
|
208 |
# Cell distribution
|
209 |
-
for cell_type, data in
|
210 |
fig.add_trace(go.Scatter(
|
211 |
x=data["x"], y=data["y"], mode='markers',
|
212 |
-
marker=dict(color=
|
213 |
name=cell_type
|
214 |
), row=1, col=1)
|
215 |
|
216 |
# Total population over time
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
#
|
221 |
-
for
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
|
232 |
fig.update_xaxes(title_text="X", row=1, col=1)
|
233 |
fig.update_yaxes(title_text="Y", row=1, col=1)
|
234 |
fig.update_xaxes(title_text="Time", row=1, col=2)
|
235 |
fig.update_yaxes(title_text="Population", row=1, col=2)
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
|
241 |
fig.update_layout(height=800, width=1200, title_text="Advanced Cell Evolution Simulation")
|
242 |
|
243 |
return fig
|
244 |
|
245 |
-
# Create a queue to communicate between the threads
|
246 |
-
q = queue.Queue()
|
247 |
-
|
248 |
# Streamlit app
|
249 |
st.title("Advanced Cell Evolution Simulation")
|
250 |
|
251 |
initial_cells = st.slider("Initial number of cells", 10, 200, 100)
|
252 |
update_interval = st.slider("Update interval (seconds)", 0.1, 5.0, 1.0)
|
253 |
|
254 |
-
# Create
|
255 |
chart_placeholder = st.empty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
|
257 |
-
|
|
|
|
|
258 |
env = Environment(100, 100)
|
259 |
for _ in range(initial_cells):
|
260 |
cell = Cell(random.uniform(0, env.width), random.uniform(0, env.height))
|
@@ -262,23 +222,40 @@ if st.button("Start Simulation"):
|
|
262 |
|
263 |
fig = setup_figure(env)
|
264 |
|
265 |
-
|
266 |
env.update()
|
267 |
|
268 |
with fig.batch_update():
|
269 |
-
cell_data, population_history = env.get_visualization_data()
|
|
|
|
|
270 |
for i, (cell_type, data) in enumerate(cell_data.items()):
|
271 |
fig.data[i].x = data["x"]
|
272 |
fig.data[i].y = data["y"]
|
273 |
fig.data[i].marker.size = data["size"]
|
274 |
|
|
|
|
|
|
|
|
|
|
|
275 |
for i, (cell_type, counts) in enumerate(population_history.items()):
|
276 |
-
fig.data[i
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
|
|
|
|
282 |
|
283 |
chart_placeholder.plotly_chart(fig, use_container_width=True)
|
284 |
-
time.sleep(update_interval)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import plotly.graph_objects as go
|
5 |
from plotly.subplots import make_subplots
|
6 |
import time
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
class Organelle:
|
9 |
def __init__(self, type):
|
10 |
self.type = type
|
11 |
|
|
|
|
|
|
|
|
|
|
|
12 |
class Cell:
|
13 |
def __init__(self, x, y, cell_type="prokaryote"):
|
14 |
self.x = x
|
15 |
self.y = y
|
16 |
self.energy = 100
|
17 |
self.cell_type = cell_type
|
18 |
+
self.organelles = set()
|
|
|
19 |
self.size = 1
|
20 |
self.color = "lightblue"
|
21 |
self.division_threshold = 150
|
|
|
24 |
|
25 |
def update_properties(self):
|
26 |
if self.cell_type == "early_eukaryote":
|
27 |
+
self.organelles.add("nucleus")
|
28 |
self.color = "green"
|
29 |
self.size = 2
|
30 |
elif self.cell_type == "advanced_eukaryote":
|
31 |
+
self.organelles.update(["nucleus", "mitochondria"])
|
32 |
self.color = "red"
|
33 |
self.size = 3
|
34 |
elif self.cell_type == "plant_like":
|
35 |
+
self.organelles.update(["nucleus", "mitochondria", "chloroplast"])
|
36 |
self.color = "darkgreen"
|
37 |
self.size = 4
|
38 |
+
elif self.cell_type == "complete":
|
39 |
+
self.organelles.update(["nucleus", "mitochondria", "chloroplast", "endoplasmic_reticulum", "golgi_apparatus"])
|
40 |
+
self.color = "purple"
|
41 |
+
self.size = 5
|
42 |
|
43 |
def move(self, environment):
|
44 |
+
dx, dy = random.uniform(-1, 1), random.uniform(-1, 1)
|
|
|
45 |
self.x = max(0, min(environment.width - 1, self.x + dx))
|
46 |
self.y = max(0, min(environment.height - 1, self.y + dy))
|
47 |
self.energy -= 0.5 * self.size
|
48 |
|
49 |
def feed(self, environment):
|
50 |
base_energy = environment.grid[int(self.y)][int(self.x)] * 0.1
|
51 |
+
if "chloroplast" in self.organelles:
|
52 |
base_energy += environment.light_level * 2
|
|
|
|
|
|
|
|
|
53 |
self.energy += base_energy
|
54 |
environment.grid[int(self.y)][int(self.x)] *= 0.9
|
55 |
|
|
|
61 |
self.energy /= 2
|
62 |
new_cell = Cell(self.x, self.y, self.cell_type)
|
63 |
new_cell.organelles = self.organelles.copy()
|
|
|
64 |
return new_cell
|
65 |
return None
|
66 |
|
|
|
74 |
new_cell_type = "early_eukaryote"
|
75 |
elif self.cell_type == "early_eukaryote":
|
76 |
new_cell_type = "advanced_eukaryote"
|
77 |
+
elif self.cell_type == "advanced_eukaryote":
|
78 |
+
new_cell_type = "plant_like"
|
79 |
+
elif self.cell_type == "plant_like":
|
80 |
+
new_cell_type = "complete"
|
81 |
|
82 |
+
new_cell = Cell((self.x + other.x) / 2, (self.y + other.y) / 2, new_cell_type)
|
|
|
|
|
|
|
|
|
83 |
new_cell.energy = self.energy + other.energy
|
84 |
+
new_cell.organelles = self.organelles.union(other.organelles)
|
|
|
85 |
new_cell.update_properties()
|
86 |
return new_cell
|
87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
class Environment:
|
89 |
def __init__(self, width, height):
|
90 |
self.width = width
|
|
|
95 |
self.time = 0
|
96 |
self.population_history = {
|
97 |
"prokaryote": [], "early_eukaryote": [],
|
98 |
+
"advanced_eukaryote": [], "plant_like": [], "complete": []
|
99 |
}
|
100 |
|
101 |
def add_cell(self, cell):
|
|
|
132 |
self.cells.extend(new_cells)
|
133 |
self.cells = [cell for cell in self.cells if cell not in cells_to_remove]
|
134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
# Record population counts
|
136 |
for cell_type in self.population_history.keys():
|
137 |
+
count = len([cell for cell in self.cells if cell.cell_type == cell_type])
|
|
|
|
|
|
|
138 |
self.population_history[cell_type].append(count)
|
139 |
|
140 |
def get_visualization_data(self):
|
141 |
+
cell_data = {cell_type: {"x": [], "y": [], "size": []} for cell_type in self.population_history.keys()}
|
142 |
+
colors = {"prokaryote": "lightblue", "early_eukaryote": "green", "advanced_eukaryote": "red", "plant_like": "darkgreen", "complete": "purple"}
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
for cell in self.cells:
|
145 |
+
cell_data[cell.cell_type]["x"].append(cell.x)
|
146 |
+
cell_data[cell.cell_type]["y"].append(cell.y)
|
147 |
+
cell_data[cell.cell_type]["size"].append(cell.size * 3)
|
|
|
148 |
|
149 |
+
return cell_data, self.population_history, colors
|
150 |
|
151 |
def setup_figure(env):
|
152 |
+
cell_types = list(env.population_history.keys())
|
153 |
+
fig = make_subplots(rows=2, cols=2,
|
154 |
subplot_titles=("Cell Distribution", "Total Population",
|
155 |
+
"Population by Cell Type", "Organelle Distribution"),
|
|
|
156 |
vertical_spacing=0.1,
|
157 |
horizontal_spacing=0.05)
|
158 |
|
159 |
+
cell_data, population_history, colors = env.get_visualization_data()
|
160 |
+
|
161 |
# Cell distribution
|
162 |
+
for cell_type, data in cell_data.items():
|
163 |
fig.add_trace(go.Scatter(
|
164 |
x=data["x"], y=data["y"], mode='markers',
|
165 |
+
marker=dict(color=colors[cell_type], size=data["size"]),
|
166 |
name=cell_type
|
167 |
), row=1, col=1)
|
168 |
|
169 |
# Total population over time
|
170 |
+
total_population = [sum(counts) for counts in zip(*population_history.values())]
|
171 |
+
fig.add_trace(go.Scatter(y=total_population, mode='lines', name="Total"), row=1, col=2)
|
172 |
+
|
173 |
+
# Population by cell type
|
174 |
+
for cell_type, counts in population_history.items():
|
175 |
+
fig.add_trace(go.Scatter(y=counts, mode='lines', name=cell_type, line=dict(color=colors[cell_type])), row=2, col=1)
|
176 |
+
|
177 |
+
# Organelle distribution
|
178 |
+
organelle_counts = {"nucleus": 0, "mitochondria": 0, "chloroplast": 0, "endoplasmic_reticulum": 0, "golgi_apparatus": 0}
|
179 |
+
for cell in env.cells:
|
180 |
+
for organelle in cell.organelles:
|
181 |
+
organelle_counts[organelle] += 1
|
182 |
+
|
183 |
+
fig.add_trace(go.Bar(x=list(organelle_counts.keys()), y=list(organelle_counts.values()), name="Organelles"), row=2, col=2)
|
184 |
|
185 |
fig.update_xaxes(title_text="X", row=1, col=1)
|
186 |
fig.update_yaxes(title_text="Y", row=1, col=1)
|
187 |
fig.update_xaxes(title_text="Time", row=1, col=2)
|
188 |
fig.update_yaxes(title_text="Population", row=1, col=2)
|
189 |
+
fig.update_xaxes(title_text="Time", row=2, col=1)
|
190 |
+
fig.update_yaxes(title_text="Population", row=2, col=1)
|
191 |
+
fig.update_xaxes(title_text="Organelle", row=2, col=2)
|
192 |
+
fig.update_yaxes(title_text="Count", row=2, col=2)
|
193 |
|
194 |
fig.update_layout(height=800, width=1200, title_text="Advanced Cell Evolution Simulation")
|
195 |
|
196 |
return fig
|
197 |
|
|
|
|
|
|
|
198 |
# Streamlit app
|
199 |
st.title("Advanced Cell Evolution Simulation")
|
200 |
|
201 |
initial_cells = st.slider("Initial number of cells", 10, 200, 100)
|
202 |
update_interval = st.slider("Update interval (seconds)", 0.1, 5.0, 1.0)
|
203 |
|
204 |
+
# Create placeholders for the chart and stop button
|
205 |
chart_placeholder = st.empty()
|
206 |
+
stop_button_placeholder = st.empty()
|
207 |
+
|
208 |
+
# Initialize session state
|
209 |
+
if 'running' not in st.session_state:
|
210 |
+
st.session_state.running = False
|
211 |
+
|
212 |
+
def toggle_simulation():
|
213 |
+
st.session_state.running = not st.session_state.running
|
214 |
|
215 |
+
start_stop_button = st.button("Start/Stop Simulation", on_click=toggle_simulation)
|
216 |
+
|
217 |
+
if st.session_state.running:
|
218 |
env = Environment(100, 100)
|
219 |
for _ in range(initial_cells):
|
220 |
cell = Cell(random.uniform(0, env.width), random.uniform(0, env.height))
|
|
|
222 |
|
223 |
fig = setup_figure(env)
|
224 |
|
225 |
+
while st.session_state.running:
|
226 |
env.update()
|
227 |
|
228 |
with fig.batch_update():
|
229 |
+
cell_data, population_history, colors = env.get_visualization_data()
|
230 |
+
|
231 |
+
# Update cell distribution
|
232 |
for i, (cell_type, data) in enumerate(cell_data.items()):
|
233 |
fig.data[i].x = data["x"]
|
234 |
fig.data[i].y = data["y"]
|
235 |
fig.data[i].marker.size = data["size"]
|
236 |
|
237 |
+
# Update total population
|
238 |
+
total_population = [sum(counts) for counts in zip(*population_history.values())]
|
239 |
+
fig.data[len(cell_data)].y = total_population
|
240 |
+
|
241 |
+
# Update population by cell type
|
242 |
for i, (cell_type, counts) in enumerate(population_history.items()):
|
243 |
+
fig.data[len(cell_data) + 1 + i].y = counts
|
244 |
+
|
245 |
+
# Update organelle distribution
|
246 |
+
organelle_counts = {"nucleus": 0, "mitochondria": 0, "chloroplast": 0, "endoplasmic_reticulum": 0, "golgi_apparatus": 0}
|
247 |
+
for cell in env.cells:
|
248 |
+
for organelle in cell.organelles:
|
249 |
+
organelle_counts[organelle] += 1
|
250 |
+
fig.data[-1].y = list(organelle_counts.values())
|
251 |
|
252 |
chart_placeholder.plotly_chart(fig, use_container_width=True)
|
253 |
+
time.sleep(update_interval)
|
254 |
+
|
255 |
+
# Check if the stop button has been pressed
|
256 |
+
if not st.session_state.running:
|
257 |
+
break
|
258 |
+
|
259 |
+
# Display final state if simulation has been stopped
|
260 |
+
if not st.session_state.running and 'fig' in locals():
|
261 |
+
chart_placeholder.plotly_chart(fig, use_container_width=True)
|