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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -84
app.py CHANGED
@@ -1,51 +1,16 @@
1
- import streamlit as st
2
  import numpy as np
3
  import matplotlib.pyplot as plt
 
4
  import random
5
- import time
6
  from scipy.stats import entropy as scipy_entropy
7
 
8
- # Константы
9
  seqlen = 60
 
10
  min_run, max_run = 1, 2
11
  ANGLE_MAP = {'A': 60.0, 'C': 180.0, 'G': -60.0, 'T': -180.0, 'N': 0.0}
12
  bases = ['A', 'C', 'G', 'T']
13
- lags_shown = 6
14
 
15
- st.set_page_config(layout="wide")
16
- st.title("🌌 Визуализация торсионных биомашин")
17
-
18
- # UI
19
- col1, col2, col3 = st.columns([1,1,2])
20
- with col1:
21
- if 'running' not in st.session_state:
22
- st.session_state.running = False
23
- if st.button("▶️ Старт / ⏸ Стоп"):
24
- st.session_state.running = not st.session_state.running
25
-
26
- with col2:
27
- if st.button("🔄 Сброс"):
28
- st.session_state.running = False
29
- st.session_state.step = 0
30
- st.session_state.seq = ''.join(random.choices(bases, k=seqlen))
31
- st.session_state.stat_bist_counts = []
32
- st.session_state.stat_entropy = []
33
- st.session_state.stat_autocorr = []
34
-
35
- with col3:
36
- speed = st.slider("⏱ Скорость обновления (мс)", 10, 1000, 200, step=10)
37
-
38
- # Init
39
- if 'seq' not in st.session_state:
40
- st.session_state.seq = ''.join(random.choices(bases, k=seqlen))
41
- if 'step' not in st.session_state:
42
- st.session_state.step = 0
43
- if 'stat_bist_counts' not in st.session_state:
44
- st.session_state.stat_bist_counts = []
45
- st.session_state.stat_entropy = []
46
- st.session_state.stat_autocorr = []
47
-
48
- # Функции
49
  def find_local_min_runs(profile, min_run=1, max_run=2):
50
  result = []
51
  N = len(profile)
@@ -60,21 +25,10 @@ def find_local_min_runs(profile, min_run=1, max_run=2):
60
  i += run_length
61
  return result
62
 
63
- def compute_autocorr(profile):
64
- profile = profile - np.mean(profile)
65
- result = np.correlate(profile, profile, mode='full')
66
- result = result[result.size // 2:]
67
- norm = np.max(result) if np.max(result) != 0 else 1
68
- return result[:10] / norm
69
-
70
- def compute_entropy(profile):
71
- vals, counts = np.unique(profile, return_counts=True)
72
- p = counts / counts.sum()
73
- return scipy_entropy(p, base=2)
74
-
75
  def bio_mutate(seq):
76
  r = random.random()
77
- if r < 0.70:
78
  idx = random.randint(0, len(seq)-1)
79
  orig = seq[idx]
80
  prob = random.random()
@@ -85,60 +39,133 @@ def bio_mutate(seq):
85
  else:
86
  newbase = random.choice([b for b in bases if b != orig])
87
  seq = seq[:idx] + newbase + seq[idx+1:]
88
- elif r < 0.80:
 
89
  idx = random.randint(0, len(seq)-1)
90
  ins = ''.join(random.choices(bases, k=random.randint(1, 3)))
91
  seq = seq[:idx] + ins + seq[idx:]
92
  if len(seq) > seqlen:
93
  seq = seq[:seqlen]
94
- elif r < 0.90:
 
95
  if len(seq) > 4:
96
  idx = random.randint(0, len(seq)-2)
97
  dell = random.randint(1, min(3, len(seq)-idx))
98
  seq = seq[:idx] + seq[idx+dell:]
99
- else:
 
100
  if len(seq) > 10:
101
  start = random.randint(0, len(seq)-6)
102
  end = start + random.randint(3,6)
103
- subseq = seq[start:end][::-1]
 
104
  seq = seq[:start] + subseq + seq[end:]
105
  while len(seq) < seqlen:
106
  seq += random.choice(bases)
107
- return seq[:seqlen]
108
-
109
- # Визуализация
110
- plot_area = st.empty()
111
 
112
- while st.session_state.running:
113
- # Мутация
114
- st.session_state.seq = bio_mutate(st.session_state.seq)
115
- torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in st.session_state.seq])
116
- runs = find_local_min_runs(torsion_profile, min_run, max_run)
117
- ent = compute_entropy(torsion_profile)
118
- acorr = compute_autocorr(torsion_profile)
119
 
120
- # Сохраняем последние 50
121
- st.session_state.stat_bist_counts = st.session_state.stat_bist_counts[-50:] + [len(runs)]
122
- st.session_state.stat_entropy = st.session_state.stat_entropy[-50:] + [ent]
123
- st.session_state.stat_autocorr = st.session_state.stat_autocorr[-50:] + [acorr]
124
 
125
- # График
126
- fig, axs = plt.subplots(3, 1, figsize=(10, 8))
127
- plt.subplots_adjust(hspace=0.5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- axs[0].plot(torsion_profile, color='royalblue')
 
 
 
 
 
 
 
 
 
 
130
  for start, end, val in runs:
131
  axs[0].axvspan(start, end, color="red", alpha=0.3)
132
- axs[0].plot(range(start, end+1), torsion_profile[start:end+1], 'ro', markersize=4)
133
  axs[0].set_ylim(-200, 200)
134
- axs[0].set_title(f"Шаг {st.session_state.step}: {st.session_state.seq}\nМашин: {len(runs)}, Энтропия: {ent:.2f}")
135
-
136
- axs[1].plot(st.session_state.stat_bist_counts, '-o', color='crimson', markersize=4)
137
- axs[1].set_title("Число 'биомашин'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- axs[2].bar(np.arange(lags_shown), acorr[:lags_shown], color='teal')
140
- axs[2].set_title("Автокорреляция")
 
141
 
142
- plot_area.pyplot(fig)
143
- st.session_state.step += 1
144
- time.sleep(speed / 1000.0)
 
 
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
9
+ steps = 120
10
  min_run, max_run = 1, 2
11
  ANGLE_MAP = {'A': 60.0, 'C': 180.0, 'G': -60.0, 'T': -180.0, 'N': 0.0}
12
  bases = ['A', 'C', 'G', 'T']
 
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  def find_local_min_runs(profile, min_run=1, max_run=2):
15
  result = []
16
  N = len(profile)
 
25
  i += run_length
26
  return result
27
 
28
+ # --- Более биологичные мутации ---
 
 
 
 
 
 
 
 
 
 
 
29
  def bio_mutate(seq):
30
  r = random.random()
31
+ if r < 0.70: # Точечная мутация
32
  idx = random.randint(0, len(seq)-1)
33
  orig = seq[idx]
34
  prob = random.random()
 
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)
59
  end = start + random.randint(3,6)
60
+ subseq = seq[start:end]
61
+ subseq = subseq[::-1]
62
  seq = seq[:start] + subseq + seq[end:]
63
  while len(seq) < seqlen:
64
  seq += random.choice(bases)
65
+ if len(seq) > seqlen:
66
+ seq = seq[:seqlen]
67
+ return seq
 
68
 
69
+ def compute_autocorr(profile):
70
+ profile = profile - np.mean(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()