Update app.py
Browse files
app.py
CHANGED
@@ -1,136 +1,127 @@
|
|
1 |
import numpy as np
|
2 |
import matplotlib.pyplot as plt
|
3 |
-
import random
|
4 |
-
from scipy.stats import entropy as scipy_entropy
|
5 |
import streamlit as st
|
6 |
|
7 |
-
#
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
if orig in 'AG':
|
36 |
-
newbase = 'C' if prob < 0.65 else random.choice(['T', 'C'])
|
37 |
-
elif orig in 'CT':
|
38 |
-
newbase = 'G' if prob < 0.65 else random.choice(['A', 'G'])
|
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)
|
56 |
-
end = start + random.randint(3,6)
|
57 |
-
subseq = seq[start:end]
|
58 |
-
subseq = subseq[::-1]
|
59 |
-
seq = seq[:start] + subseq + seq[end:]
|
60 |
-
while len(seq) < seqlen:
|
61 |
-
seq += random.choice(bases)
|
62 |
-
if len(seq) > seqlen:
|
63 |
-
seq = seq[:seqlen]
|
64 |
-
return seq
|
65 |
|
66 |
def compute_autocorr(profile):
|
|
|
67 |
profile = profile - np.mean(profile)
|
68 |
result = np.correlate(profile, profile, mode='full')
|
69 |
-
|
70 |
-
|
71 |
-
return result[:10]/norm # только лаги 0..9
|
72 |
|
73 |
def compute_entropy(profile):
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
# --- Начальная цепь ---
|
79 |
-
seq = ''.join(random.choices(bases, k=seqlen))
|
80 |
-
stat_bist_counts = []
|
81 |
-
stat_entropy = []
|
82 |
-
stat_autocorr = []
|
83 |
-
|
84 |
-
fig, axs = plt.subplots(3, 1, figsize=(10, 8))
|
85 |
-
plt.subplots_adjust(hspace=0.45)
|
86 |
-
lags_shown = 6
|
87 |
|
|
|
88 |
def draw_world(seq, axs, step, stat_bist_counts, stat_entropy, stat_autocorr):
|
89 |
axs[0].cla()
|
90 |
axs[1].cla()
|
91 |
axs[2].cla()
|
92 |
axs[3].cla()
|
93 |
|
94 |
-
# Отображение
|
95 |
-
axs[0].plot(seq, label=f"
|
96 |
axs[0].set_title("Торсионный профиль (углы)")
|
97 |
axs[0].set_ylim(-180, 180)
|
98 |
-
|
99 |
-
#
|
100 |
stable_zones = find_local_min_runs(seq)
|
101 |
for start, end in stable_zones:
|
102 |
axs[0].axvspan(start, end, color='yellow', alpha=0.3)
|
103 |
-
axs[0].text((start + end) // 2, 160, f'▲
|
104 |
|
105 |
-
#
|
106 |
-
axs[1].plot(stat_bist_counts,
|
107 |
axs[1].set_title("Кол-во устойчивых машин (стромбистов)")
|
108 |
|
109 |
# Энтропия
|
110 |
-
axs[2].plot(stat_entropy,
|
111 |
axs[2].set_title("Энтропия торсионного поля")
|
112 |
|
113 |
# Автокорреляция
|
114 |
-
axs[3].plot(stat_autocorr,
|
115 |
axs[3].set_title("Автокорреляция (память)")
|
116 |
|
117 |
for ax in axs:
|
118 |
ax.legend()
|
119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
stat_autocorr.append(acorr)
|
133 |
-
draw_world(seq, axs, step, stat_bist_counts, stat_entropy, stat_autocorr)
|
134 |
-
|
135 |
-
chart_placeholder.pyplot(fig) # обновляем график
|
136 |
-
# После каждого шага Streamlit перерисует график
|
|
|
1 |
import numpy as np
|
2 |
import matplotlib.pyplot as plt
|
|
|
|
|
3 |
import streamlit as st
|
4 |
|
5 |
+
# === Параметры модели ===
|
6 |
+
SEQ_LEN = 100 # длина последовательности
|
7 |
+
HISTORY_LEN = 200 # сколько шагов хранить в статистике
|
8 |
+
JUMP = 10 # максимальный шаг изменения угла
|
9 |
+
ENTROPY_BINS = 36 # шаг 10 градусов
|
10 |
+
|
11 |
+
# === Инициализация состояния ===
|
12 |
+
if "seq" not in st.session_state:
|
13 |
+
st.session_state.seq = np.random.randint(-180, 180, size=SEQ_LEN)
|
14 |
+
st.session_state.history = []
|
15 |
+
st.session_state.bist_counts = []
|
16 |
+
st.session_state.entropy = []
|
17 |
+
st.session_state.autocorr = []
|
18 |
+
st.session_state.step = 0
|
19 |
+
|
20 |
+
# === Функции анализа ===
|
21 |
+
def find_local_min_runs(profile, threshold=10):
|
22 |
+
"""Находит устойчивые участки ("машины"), где значения углов почти не меняются."""
|
23 |
+
runs = []
|
24 |
+
start = 0
|
25 |
+
for i in range(1, len(profile)):
|
26 |
+
if abs(profile[i] - profile[i - 1]) > threshold:
|
27 |
+
if i - start > 2:
|
28 |
+
runs.append((start, i - 1))
|
29 |
+
start = i
|
30 |
+
if len(profile) - start > 2:
|
31 |
+
runs.append((start, len(profile) - 1))
|
32 |
+
return runs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
def compute_autocorr(profile):
|
35 |
+
"""Автокорреляция – структурность, насколько повторяется рисунок"""
|
36 |
profile = profile - np.mean(profile)
|
37 |
result = np.correlate(profile, profile, mode='full')
|
38 |
+
mid = len(result) // 2
|
39 |
+
return result[mid + 1] / result[mid]
|
|
|
40 |
|
41 |
def compute_entropy(profile):
|
42 |
+
"""Энтропия – мера хаотичности, насколько случайна структура"""
|
43 |
+
hist, _ = np.histogram(profile, bins=ENTROPY_BINS, range=(-180, 180), density=True)
|
44 |
+
hist = hist[hist > 0]
|
45 |
+
return -np.sum(hist * np.log2(hist))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
+
# === Функция отрисовки ===
|
48 |
def draw_world(seq, axs, step, stat_bist_counts, stat_entropy, stat_autocorr):
|
49 |
axs[0].cla()
|
50 |
axs[1].cla()
|
51 |
axs[2].cla()
|
52 |
axs[3].cla()
|
53 |
|
54 |
+
# Отображение профиля
|
55 |
+
axs[0].plot(seq, label=f"Шаг {step}", color='skyblue')
|
56 |
axs[0].set_title("Торсионный профиль (углы)")
|
57 |
axs[0].set_ylim(-180, 180)
|
58 |
+
|
59 |
+
# Визуальное выделение устойчивых "машин"
|
60 |
stable_zones = find_local_min_runs(seq)
|
61 |
for start, end in stable_zones:
|
62 |
axs[0].axvspan(start, end, color='yellow', alpha=0.3)
|
63 |
+
axs[0].text((start + end) // 2, 160, f'▲{end - start}', ha='center', va='center', fontsize=8, color='darkgreen')
|
64 |
|
65 |
+
# График количества "стромбистов"
|
66 |
+
axs[1].plot(stat_bist_counts, color='orchid')
|
67 |
axs[1].set_title("Кол-во устойчивых машин (стромбистов)")
|
68 |
|
69 |
# Энтропия
|
70 |
+
axs[2].plot(stat_entropy, color='crimson')
|
71 |
axs[2].set_title("Энтропия торсионного поля")
|
72 |
|
73 |
# Автокорреляция
|
74 |
+
axs[3].plot(stat_autocorr, color='seagreen')
|
75 |
axs[3].set_title("Автокорреляция (память)")
|
76 |
|
77 |
for ax in axs:
|
78 |
ax.legend()
|
79 |
|
80 |
+
# === Функция обновления ===
|
81 |
+
def update_step():
|
82 |
+
seq = st.session_state.seq
|
83 |
+
history = st.session_state.history
|
84 |
+
stat_bist_counts = st.session_state.bist_counts
|
85 |
+
stat_entropy = st.session_state.entropy
|
86 |
+
stat_autocorr = st.session_state.autocorr
|
87 |
+
step = st.session_state.step
|
88 |
+
|
89 |
+
# Случайная мутация
|
90 |
+
i = np.random.randint(0, len(seq))
|
91 |
+
delta = np.random.randint(-JUMP, JUMP + 1)
|
92 |
+
seq[i] = (seq[i] + delta + 180) % 360 - 180
|
93 |
+
|
94 |
+
history.append(seq.copy())
|
95 |
+
if len(history) > HISTORY_LEN:
|
96 |
+
history.pop(0)
|
97 |
+
|
98 |
+
stable_regions = find_local_min_runs(seq)
|
99 |
+
stat_bist_counts.append(len(stable_regions))
|
100 |
+
stat_entropy.append(compute_entropy(seq))
|
101 |
+
stat_autocorr.append(compute_autocorr(seq))
|
102 |
+
st.session_state.step += 1
|
103 |
+
|
104 |
+
# === Интерфейс Streamlit ===
|
105 |
+
st.set_page_config(layout="wide")
|
106 |
+
st.title("🧬 Стромбистный анализ торсионного поля")
|
107 |
+
col1, col2 = st.columns([1, 2])
|
108 |
+
|
109 |
+
with col1:
|
110 |
+
if st.button("🔁 Следующий шаг"):
|
111 |
+
update_step()
|
112 |
+
st.markdown(f"**Текущий шаг**: {st.session_state.step}")
|
113 |
+
st.markdown("**Стромбисты** — устойчивые участки структуры, подобные памяти или машинам.")
|
114 |
+
st.markdown("**Автокорреляция** — отражает повторяемость паттерна.")
|
115 |
+
st.markdown("**Энтропия** — мера хаоса.")
|
116 |
|
117 |
+
with col2:
|
118 |
+
fig, axs = plt.subplots(4, 1, figsize=(10, 10), sharex=True)
|
119 |
+
draw_world(
|
120 |
+
st.session_state.seq,
|
121 |
+
axs,
|
122 |
+
st.session_state.step,
|
123 |
+
st.session_state.bist_counts,
|
124 |
+
st.session_state.entropy,
|
125 |
+
st.session_state.autocorr
|
126 |
+
)
|
127 |
+
st.pyplot(fig)
|
|
|
|
|
|
|
|
|
|