Dmtlant commited on
Commit
bfca1f5
·
verified ·
1 Parent(s): 5628a71

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -97
app.py CHANGED
@@ -1,8 +1,9 @@
 
1
  import numpy as np
2
  import matplotlib.pyplot as plt
3
- from matplotlib.animation import FuncAnimation
4
  import random
5
  from scipy.stats import entropy as scipy_entropy
 
6
 
7
  # --- НАСТРОЙКИ ---
8
  seqlen = 60
@@ -25,7 +26,6 @@ def find_local_min_runs(profile, min_run=1, max_run=2):
25
  i += run_length
26
  return result
27
 
28
- # --- Более биологичные мутации ---
29
  def bio_mutate(seq):
30
  r = random.random()
31
  if r < 0.70: # Точечная мутация
@@ -39,20 +39,17 @@ def bio_mutate(seq):
39
  else:
40
  newbase = random.choice([b for b in bases if b != orig])
41
  seq = seq[:idx] + newbase + seq[idx+1:]
42
-
43
  elif r < 0.80: # Инсерция короткого блока
44
  idx = random.randint(0, len(seq)-1)
45
  ins = ''.join(random.choices(bases, k=random.randint(1, 3)))
46
  seq = seq[:idx] + ins + seq[idx:]
47
  if len(seq) > seqlen:
48
  seq = seq[:seqlen]
49
-
50
  elif r < 0.90: # Делеция
51
  if len(seq) > 4:
52
  idx = random.randint(0, len(seq)-2)
53
  dell = random.randint(1, min(3, len(seq)-idx))
54
  seq = seq[:idx] + seq[idx+dell:]
55
-
56
  else: # Блочная перестановка (инверсия)
57
  if len(seq) > 10:
58
  start = random.randint(0, len(seq)-6)
@@ -71,101 +68,66 @@ def compute_autocorr(profile):
71
  result = np.correlate(profile, profile, mode='full')
72
  result = result[result.size // 2:]
73
  norm = np.max(result) if np.max(result)!=0 else 1
74
- return result[:10]/norm # только лаги 0..9
75
 
76
  def compute_entropy(profile):
77
  vals, counts = np.unique(profile, return_counts=True)
78
  p = counts / counts.sum()
79
  return scipy_entropy(p, base=2)
80
 
81
- # --- Дополнительный анализ стромбистов ---
82
- def analyze_strombists(runs, seqlen):
83
- counts = len(runs)
84
- lengths = [end - start + 1 for start, end, _ in runs]
85
- angle_freq = {}
86
- heatmap_row = np.zeros(seqlen)
87
- for start, end, val in runs:
88
- for pos in range(start, end + 1):
89
- heatmap_row[pos] = 1
90
- angle_freq[val] = angle_freq.get(val, 0) + 1
91
- return counts, lengths, angle_freq, heatmap_row
92
-
93
- # --- Начальная цепь ---
94
- seq = ''.join(random.choices(bases, k=seqlen))
95
- stat_bist_counts = []
96
- stat_entropy = []
97
- stat_autocorr = []
98
- stat_strombists = []
99
-
100
- fig, axs = plt.subplots(4, 1, figsize=(10, 10))
101
- plt.subplots_adjust(hspace=0.45)
102
- lags_shown = 6
103
-
104
- def draw_world(seq, axs, step, cnt_hist, ent_hist, ac_hist, st_hist):
105
- torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in seq])
106
- runs = find_local_min_runs(torsion_profile, min_run, max_run)
107
- st_count, st_lengths, st_angle_freq, st_heatmap_row = analyze_strombists(runs, seqlen)
108
-
109
- axs[0].cla()
110
- axs[1].cla()
111
- axs[2].cla()
112
- axs[3].cla()
113
-
114
- axs[0].plot(torsion_profile, color='royalblue', label="Торсионный угол")
115
- for start, end, val in runs:
116
- axs[0].axvspan(start, end, color="red", alpha=0.3)
117
- axs[0].plot(range(start, end+1), torsion_profile[start:end+1], 'ro', markersize=5)
118
- axs[0].set_ylim(-200, 200)
119
- axs[0].set_xlabel("Позиция")
120
- axs[0].set_ylabel("Торсионный угол (град.)")
121
- axs[0].set_title(f"Шаг {step}: {seq}\nЧисло машин: {st_count}, энтропия: {ent_hist[-1]:.2f}")
122
- axs[0].legend()
123
-
124
- # История динамики "машин"
125
- axs[1].plot(cnt_hist, '-o', color='crimson', markersize=4)
126
- axs[1].set_xlabel("Шаг")
127
- axs[1].set_ylabel("Число машин")
128
- axs[1].set_ylim(0, max(10, max(cnt_hist)+1))
129
- axs[1].set_title("Динамика: число 'биомашин'")
130
-
131
- # Автокорреляция для текущего шага
132
- axs[2].bar(np.arange(lags_shown), ac_hist[-1][:lags_shown], color='teal', alpha=0.7)
133
- axs[2].set_xlabel("Лаг")
134
- axs[2].set_ylabel("Автокорреляция")
135
- axs[2].set_title("Автокорреляция углового профиля (структурность) и энтропия")
136
- axs[2].text(0.70, 0.70, f"Энтропия: {ent_hist[-1]:.2f}", transform=axs[2].transAxes)
137
-
138
- # Карта стромбистов
139
- axs[3].plot(st_heatmap_row, color='orange', label="Карта стромбистов", linewidth=2)
140
- axs[3].set_ylim(0, 1)
141
- axs[3].set_xlabel("Позиция")
142
- axs[3].set_ylabel("Стромбист (1 - стабильность)")
143
- axs[3].set_title(f"Карты стромбистов на шаге {step}")
144
- axs[3].legend()
145
-
146
- def animate(i):
147
- global seq, stat_bist_counts, stat_entropy, stat_autocorr, stat_strombists
148
- if i == 0:
149
- stat_bist_counts.clear()
150
- stat_entropy.clear()
151
- stat_autocorr.clear()
152
- stat_strombists.clear()
153
- else:
154
- seq = bio_mutate(seq)
155
- torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in seq])
156
- runs = find_local_min_runs(torsion_profile, min_run, max_run)
157
- stat_bist_counts.append(len(runs))
158
- ent = compute_entropy(torsion_profile)
159
- stat_entropy.append(ent)
160
- acorr = compute_autocorr(torsion_profile)
161
- stat_autocorr.append(acorr)
162
- st_count, st_lengths, st_angle_freq, st_heatmap_row = analyze_strombists(runs, seqlen)
163
- stat_strombists.append((st_count, st_lengths, st_angle_freq))
164
- draw_world(seq, axs, i, stat_bist_counts, stat_entropy, stat_autocorr, stat_strombists)
165
- return axs
166
-
167
- anim = FuncAnimation(
168
- fig, animate, frames=steps, interval=600, repeat=False, blit=False
169
- )
170
-
171
- plt.show()
 
1
+ import streamlit as st
2
  import numpy as np
3
  import matplotlib.pyplot as plt
 
4
  import random
5
  from scipy.stats import entropy as scipy_entropy
6
+ import time
7
 
8
  # --- НАСТРОЙКИ ---
9
  seqlen = 60
 
26
  i += run_length
27
  return result
28
 
 
29
  def bio_mutate(seq):
30
  r = random.random()
31
  if r < 0.70: # Точечная мутация
 
39
  else:
40
  newbase = random.choice([b for b in bases if b != orig])
41
  seq = seq[:idx] + newbase + seq[idx+1:]
 
42
  elif r < 0.80: # Инсерция короткого блока
43
  idx = random.randint(0, len(seq)-1)
44
  ins = ''.join(random.choices(bases, k=random.randint(1, 3)))
45
  seq = seq[:idx] + ins + seq[idx:]
46
  if len(seq) > seqlen:
47
  seq = seq[:seqlen]
 
48
  elif r < 0.90: # Делеция
49
  if len(seq) > 4:
50
  idx = random.randint(0, len(seq)-2)
51
  dell = random.randint(1, min(3, len(seq)-idx))
52
  seq = seq[:idx] + seq[idx+dell:]
 
53
  else: # Блочная перестановка (инверсия)
54
  if len(seq) > 10:
55
  start = random.randint(0, len(seq)-6)
 
68
  result = np.correlate(profile, profile, mode='full')
69
  result = result[result.size // 2:]
70
  norm = np.max(result) if np.max(result)!=0 else 1
71
+ return result[:10]/norm
72
 
73
  def compute_entropy(profile):
74
  vals, counts = np.unique(profile, return_counts=True)
75
  p = counts / counts.sum()
76
  return scipy_entropy(p, base=2)
77
 
78
+ # --- Streamlit интерфейс ---
79
+ st.title("🧬 Эволюция ДНК-подобной последовательности")
80
+ st.markdown("Модель визуализирует мутации и анализирует структуру последовательности во времени.")
81
+
82
+ # Кнопка запуска симуляции
83
+ if st.button("▶️ Запустить симуляцию"):
84
+ seq = ''.join(random.choices(bases, k=seqlen))
85
+ stat_bist_counts = []
86
+ stat_entropy = []
87
+ stat_autocorr = []
88
+
89
+ plot_placeholder = st.empty()
90
+
91
+ for step in range(steps):
92
+ if step != 0:
93
+ seq = bio_mutate(seq)
94
+ torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in seq])
95
+ runs = find_local_min_runs(torsion_profile, min_run, max_run)
96
+ stat_bist_counts.append(len(runs))
97
+ ent = compute_entropy(torsion_profile)
98
+ stat_entropy.append(ent)
99
+ acorr = compute_autocorr(torsion_profile)
100
+
101
+ # Визуализация
102
+ fig, axs = plt.subplots(3, 1, figsize=(10, 8))
103
+ plt.subplots_adjust(hspace=0.45)
104
+ lags_shown = 6
105
+
106
+ axs[0].cla()
107
+ axs[1].cla()
108
+ axs[2].cla()
109
+
110
+ axs[0].plot(torsion_profile, color='royalblue', label="Торсионный угол")
111
+ for start, end, val in runs:
112
+ axs[0].axvspan(start, end, color="red", alpha=0.3)
113
+ axs[0].plot(range(start, end+1), torsion_profile[start:end+1], 'ro', markersize=5)
114
+ axs[0].set_ylim(-200, 200)
115
+ axs[0].set_xlabel("Позиция")
116
+ axs[0].set_ylabel("Торсионный угол (град.)")
117
+ axs[0].set_title(f"Шаг {step}: {seq}\nЧисло машин: {len(runs)}, энтропия: {ent:.2f}")
118
+ axs[0].legend()
119
+
120
+ axs[1].plot(stat_bist_counts, '-o', color='crimson', markersize=4)
121
+ axs[1].set_xlabel("Шаг")
122
+ axs[1].set_ylabel("Число машин")
123
+ axs[1].set_ylim(0, max(10, max(stat_bist_counts)+1))
124
+ axs[1].set_title("Динамика: число 'биомашин'")
125
+
126
+ axs[2].bar(np.arange(lags_shown), acorr[:lags_shown], color='teal', alpha=0.7)
127
+ axs[2].set_xlabel("Лаг")
128
+ axs[2].set_ylabel("Автокорреляция")
129
+ axs[2].set_title("Автокорреляция углового профиля (структурность) и энтропия")
130
+ axs[2].text(0.70,0.70, f"Энтропия: {ent:.2f}", transform=axs[2].transAxes)
131
+
132
+ plot_placeholder.pyplot(fig)
133
+ time.sleep(0.5)