File size: 4,152 Bytes
cab60db
3ffebe8
 
 
 
cab60db
3ffebe8
 
 
 
 
cab60db
3ffebe8
 
 
 
 
 
 
 
 
 
 
 
 
 
cab60db
3ffebe8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cab60db
3ffebe8
 
 
 
 
 
cab60db
3ffebe8
 
 
 
cab60db
3ffebe8
 
 
cab60db
3ffebe8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
import random
from scipy.stats import entropy as scipy_entropy

# Настройки
seqlen = 60
min_run, max_run = 1, 2
ANGLE_MAP = {'A': 60.0, 'C': 180.0, 'G': -60.0, 'T': -180.0, 'N': 0.0}
bases = ['A', 'C', 'G', 'T']

# --- Логика ---
def find_local_min_runs(profile, min_run=1, max_run=2):
    result = []
    N = len(profile)
    i = 0
    while i < N:
        run_val = profile[i]
        run_length = 1
        while i + run_length < N and profile[i + run_length] == run_val:
            run_length += 1
        if min_run <= run_length <= max_run:
            result.append((i, i + run_length - 1, run_val))
        i += run_length
    return result

def bio_mutate(seq):
    r = random.random()
    if r < 0.70:
        idx = random.randint(0, len(seq)-1)
        orig = seq[idx]
        prob = random.random()
        if orig in 'AG':
            newbase = 'C' if prob < 0.65 else random.choice(['T', 'C'])
        elif orig in 'CT':
            newbase = 'G' if prob < 0.65 else random.choice(['A', 'G'])
        else:
            newbase = random.choice([b for b in bases if b != orig])
        seq = seq[:idx] + newbase + seq[idx+1:]
    elif r < 0.80:
        idx = random.randint(0, len(seq)-1)
        ins = ''.join(random.choices(bases, k=random.randint(1, 3)))
        seq = seq[:idx] + ins + seq[idx:]
        seq = seq[:seqlen]
    elif r < 0.90:
        if len(seq) > 4:
            idx = random.randint(0, len(seq)-2)
            dell = random.randint(1, min(3, len(seq)-idx))
            seq = seq[:idx] + seq[idx+dell:]
    else:
        if len(seq) > 10:
            start = random.randint(0, len(seq)-6)
            end = start + random.randint(3,6)
            subseq = seq[start:end][::-1]
            seq = seq[:start] + subseq + seq[end:]
    while len(seq) < seqlen:
        seq += random.choice(bases)
    if len(seq) > seqlen:
        seq = seq[:seqlen]
    return seq

def compute_autocorr(profile):
    profile = profile - np.mean(profile)
    result = np.correlate(profile, profile, mode='full')
    result = result[result.size // 2:]
    norm = np.max(result) if np.max(result)!=0 else 1
    return result[:10]/norm

def compute_entropy(profile):
    vals, counts = np.unique(profile, return_counts=True)
    p = counts / counts.sum()
    return scipy_entropy(p, base=2)

# --- Streamlit UI ---
st.title("🧬 Эволюция ДНК-подобной цепи с мутациями")
steps = st.slider("Число шагов симуляции", 1, 200, 60)

if st.button("▶️ Запустить симуляцию"):
    seq = ''.join(random.choices(bases, k=seqlen))
    stat_bist_counts = []
    stat_entropy = []
    stat_autocorr = []

    fig, axs = plt.subplots(3, 1, figsize=(10, 8))
    plt.subplots_adjust(hspace=0.45)

    for i in range(steps):
        torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in seq])
        runs = find_local_min_runs(torsion_profile, min_run, max_run)
        stat_bist_counts.append(len(runs))
        stat_entropy.append(compute_entropy(torsion_profile))
        stat_autocorr.append(compute_autocorr(torsion_profile))
        seq = bio_mutate(seq)

    # Отрисовка последнего состояния
    axs[0].plot(torsion_profile, color='royalblue', label="Торсионный угол")
    for start, end, val in runs:
        axs[0].axvspan(start, end, color="red", alpha=0.3)
        axs[0].plot(range(start, end+1), torsion_profile[start:end+1], 'ro', markersize=5)
    axs[0].set_ylim(-200, 200)
    axs[0].set_title(f"Шаг {steps}: {seq}\nЧисло машин: {len(runs)}, энтропия: {stat_entropy[-1]:.2f}")
    axs[0].legend()

    axs[1].plot(stat_bist_counts, '-o', color='crimson', markersize=4)
    axs[1].set_title("Динамика: число 'биомашин'")

    axs[2].bar(np.arange(6), stat_autocorr[-1][:6], color='teal', alpha=0.7)
    axs[2].set_title("Автокорреляция и энтропия")
    axs[2].text(0.7, 0.7, f"Энтропия: {stat_entropy[-1]:.2f}", transform=axs[2].transAxes)

    st.pyplot(fig)