Как собрать ДНК с помощью кода: симуляция молекулярных операций на Python

от автора

Синтез ДНК кажется чем-то сугубо биологическим — с пробирками, центрифугами и белыми халатами. Но что, если попробовать собрать ДНК в коде? Не просто сгенерировать последовательность, а симулировать реальные процессы: лигирование, гибридизацию, ПЦР, ошибочные вставки, ферментативные сдвиги и многое другое. В этой статье — практическая попытка воссоздать молекулярную биологию средствами Python, без библиотек типа Biopython, с нуля. Много кода, немного шуток и один вопрос — можно ли построить in silico ДНК-лабораторию?

Слова “биоинформатика” и “программирование” обычно встречаются в одном предложении, когда речь идёт о парсинге геномов, анализе экспрессии генов или машинном обучении для диагностики. Но однажды захотелось большего. Хотелось не просто читать гены, а играть с ними. Моделировать их, собирать руками. Вернее, клавиатурой.

Идея: построить в коде лабораторную скамью, где можно будет “сшивать” фрагменты ДНК, копировать их, проверять на ошибки. Причём без привычных библиотек вроде Biopython — просто Python, NumPy и желание воссоздать реальный молекулярный процесс в виртуальной среде. Можно ли собрать виртуальную ДНК-плазмиду и отдать её синтетической биологии?

Да. Но сначала немного теории.


Минимум молекулярной биологии

Если вы не биолог — не страшно. Вот краткий набор знаний, который понадобится:

  • ДНК состоит из 4 оснований: A, T, C, G.

  • A соединяется с T, C с G. Это называется комплементарностью.

  • ДНК может быть одноцепочечной или двуцепочечной.

  • Ферменты могут копировать, соединять, резать или модифицировать ДНК.

  • Сборка ДНК — это как LEGO, только меньше и с более странными инструкциями.

Соберём свой набор инструментов.


1. Структура: последовательности и цепи

Начнём с основного объекта — молекулы ДНК. Представим её как последовательность символов, но добавим немного структуры.

Язык: Python 3.11+

from typing import List  class Strand:     def __init__(self, sequence: str):         assert all(base in "ATCG" for base in sequence), "Invalid DNA sequence"         self.sequence = sequence      def complement(self) -> str:         return self.sequence.translate(str.maketrans("ATCG", "TAGC"))      def reverse_complement(self) -> str:         return self.complement()[::-1]      def __repr__(self):         return f"5'-{self.sequence}-3'" 

Пример использования:

s = Strand("ATGCGT") print(s.reverse_complement())  # ACGCAT

Класс Strand — это строительный кирпич. Теперь можно моделировать гибридизацию, сшивку и другие операции.


2. Гибридизация: связываем цепи

Когда две цепи встречаются и могут быть комплементарными, они связываются — образуется “дуплекс”.

def can_hybridize(s1: Strand, s2: Strand, min_overlap: int = 5) -> bool:     rc = s2.reverse_complement()     for i in range(len(s1.sequence) - min_overlap + 1):         if s1.sequence[i:].startswith(rc[:len(s1.sequence)-i]):             return True     return False 

Пример:

a = Strand("ATGCGT") b = Strand("ACGCAT")  print(can_hybridize(a, b))  # True

Здесь мы проверяем: может ли вторая цепь при перевороте и комплементации хотя бы частично приклеиться к первой.


3. ПЦР: полимеразная цепная реакция

ПЦР — это способ быстро наращивать миллионы копий ДНК с помощью фермента и температурных циклов.

from random import randint  def pcr_amplify(template: Strand, primers: List[Strand], cycles: int = 30) -> List[Strand]:     amplified = []     for _ in range(cycles):         for primer in primers:             if primer.sequence in template.sequence:                 start = template.sequence.find(primer.sequence)                 amplified.append(Strand(template.sequence[start:]))     return amplified  template = Strand("ATGCGTACCGTTAAGT") primer1 = Strand("ATGCG") primer2 = Strand("AAGT") products = pcr_amplify(template, [primer1, primer2]) print(f"Products: {len(products)}") 

Это упрощённая модель, но она демонстрирует суть: каждый цикл удваивает фрагменты, на которые садятся праймеры.


4. Лигирование: склеивание фрагментов

Фермент лигаза соединяет два фрагмента ДНК, если они совместимы (например, если один заканчивается на последовательность, с которой начинается другой).

def ligate(s1: Strand, s2: Strand) -> Strand:     # простое сшивание, без проверки комплементарности     return Strand(s1.sequence + s2.sequence)  frag1 = Strand("ATGCGT") frag2 = Strand("ACCGTA")  ligated = ligate(frag1, frag2) print(ligated)  # 5'-ATGCGTACCGTA-3' 

В реальности нужна проверка на “липкие концы” (sticky ends). Но даже простая конкатенация уже позволяет моделировать базовое склеивание.


5. Ошибки и мутации

Биология не бывает идеальной. Мутации — часть игры.

from random import choice, random  def mutate(s: Strand, rate: float = 0.01) -> Strand:     new_seq = ""     for base in s.sequence:         if random() < rate:             new_seq += choice("ATCG".replace(base, ""))         else:             new_seq += base     return Strand(new_seq)  original = Strand("ATGCGTACCGTTAAGT") mutated = mutate(original, rate=0.1) print(f"Original: {original}") print(f"Mutated : {mutated}") 

С мутациями можно играть: вставки, делеции, инверсии, перестройки. Особенно забавно смотреть, как они влияют на “виртуальный геном”.


6. Построим плазмиду

Соберём кольцевую ДНК, как это делают в лабораториях. В простом случае представим кольцо как фрагмент, где 3′-конец соединяется с 5′-концом.

class Plasmid(Strand):     def __repr__(self):         return f"Circular 5'-{self.sequence}-3'"      def cut(self, site: str) -> List[Strand]:         if site not in self.sequence:             return [self]         idx = self.sequence.find(site)         return [Strand(self.sequence[:idx]), Strand(self.sequence[idx:])]  plasmid = Plasmid("ATGCGTACCGTTAAGTCCGATC") cut_fragments = plasmid.cut("CCGTTA") for f in cut_fragments:     print(f) 

Теперь мы можем разрезать плазмиды, вставлять гены и даже симулировать клонирование.


Заключение

Всё вышеприведённое — это даже не поверхность. Это царапина на внешнем слое синтетической биологии. Можно углубляться в:

  • Моделирование температурных профилей (анализ работы ПЦР при разных температурах).

  • Ферментативную кинетику (микроскопично эмулировать скорость работы ДНК-полимеразы).

  • Химию буферов (учёт pH, концентрации ионов взаимодействия).

  • Вероятность отжига праймеров (точное моделирование Tm).

И это будет только интереснее.

Главный вывод: биология прекрасно ложится на программирование. Более того, если вы программист — вы уже почти синтетический биолог. Вам просто нужно немного других библиотек… и чуть больше пипеток.

А пока можно собрать собственный геном, сшить его из фрагментов и “запустить” in silico.

P.S. Кода здесь много, но вы можете легко собрать из него полноценную симуляцию. Попробуйте, например, реализовать CRISPR. Или хотя бы фермент EcoRI.

Если нужен Jupyter Notebook с полным примером — напишите в комментариях, подготовим.


ссылка на оригинал статьи https://habr.com/ru/articles/916192/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *