lstm

Deep learning models to predict the population of VR (Variable Region) sequences generated by a given TR design. Uses LSTM neural networks trained on DGRec experimental data to model bRT’s position-dependent, context-dependent error-prone reverse transcription, including the snowball effect where prior mutations increase subsequent error rates.

Model components

Custom loss function, metrics, and model separation utilities.


source

generate_sequences_oneTR

def generate_sequences_oneTR(
    TR:str, # TR sequence
    n:int=1000, # Number of VR to generate
)->list: # List of `n` generated VR sequences.

Generate multiple VR sequences from a single TR sequence.


source

generate_sequences

def generate_sequences(
    X_seq:list, # list of TR sequences
)->list: # List of generated VR sequences, in the same order as `X_seq`.

Generate VR sequences from a list of TR sequences.

Each TR sequence produces exactly one VR sequence.

generate_sequences_oneTR('CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC',n=10)
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1776349626.679766   51632 gpu_device.cc:2020] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 1197 MB memory:  -> device: 0, name: NVIDIA RTX 2000 Ada Generation Laptop GPU, pci bus id: 0000:01:00.0, compute capability: 8.9
2026-04-16 16:27:10.191331: I external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:473] Loaded cuDNN version 90501
1/1 ━━━━━━━━━━━━━━━━━━━━ 2s 2s/step
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:05<00:00, 12.73it/s]

VR sequence generation

Generate predicted VR sequences from TR inputs using the LSTM model.

Protein diversity evaluation

Evaluate and visualize protein-level diversity from generated sequences.


source

EvaluateTR_to_prot

def EvaluateTR_to_prot(
    TR:str, # The TR sequence
    NDGR:int=100, # Number of VR to generate for the protein logo
    offset:int=0, # The offset for protein translation
)->Counter: # Counts of translated protein sequences.

Evaluate protein diversity accessible from a TR sequence via DGR.

Generates VR sequences from a single TR, translates them into proteins, and displays a protein sequence logo based on amino-acid frequencies.

c = EvaluateTR_to_prot('CGTAAACCGGACCTAGTTAACTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC')
    c.most_common(5)
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 13ms/step
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:05<00:00, 13.12it/s]
<Figure size 1200x400 with 0 Axes>


source

optimize_sequence_display_proteins

def optimize_sequence_display_proteins(
    original_seq:str, # Original DNA sequence to optimize.
    frame_offset:int=0, # Reading-frame offset (0, 1, or 2) used when grouping codons.
    dict_allowed_AAs:NoneType=None, # Dictionary of positions (keys) and AAs (values) where you want to reach all AAs in the list with the codon. If not mentioned, does as before.
    # Selects for codons which do not reach (by adenine mutation) stop codons. If not possible, allow them anyway.
    dict_allowed_AAs_max_min:NoneType=None, # Dictionary of positions (keys) and either you want maximum diversity ('max') or mimimum diversity ('min')  at the positions mentionned in dict_allowed_AAs. Diversity = number of AAs reachable by adenine mutations (already removed codons reaching stop codons). 
    # If not mentioned, any sequence that fullfills dict_allowed_AAs[i] is accepted.
    CHANGES:int=6, # Maximum number of codon substitutions allowed (on top of the AAs requirements from the previous argument).
    freq_min:float=0.2, # Lowest usage frequency acceptable.
    N:int=1, # Number of putative TR to output.
    forbidden_positions:list=[], # Nucleotide positions that must not be modified.
    threshold:float=0.7, # Minimum required value for both `Score_TRSp` and `Score_TRSpAvd` to
    # accept a sequence as optimal.
    codon_usage:dict={'F': {'TTT': 0.57, 'TTC': 0.43}, 'L': {'TTA': 0.15, 'TTG': 0.12, 'CTT': 0.12, 'CTC': 0.1, 'CTA': 0.05, 'CTG': 0.46}, 'S': {'TCT': 0.11, 'TCC': 0.11, 'TCA': 0.15, 'TCG': 0.16, 'AGT': 0.14, 'AGC': 0.33}, 'Y': {'TAT': 0.53, 'TAC': 0.47}, '*': {'TAA': 0.64, 'TAG': 0.0, 'TGA': 0.36}, 'C': {'TGT': 0.42, 'TGC': 0.58}, 'W': {'TGG': 1.0}, 'P': {'CCT': 0.17, 'CCC': 0.13, 'CCA': 0.14, 'CCG': 0.55}, 'H': {'CAT': 0.55, 'CAC': 0.45}, 'Q': {'CAA': 0.3, 'CAG': 0.7}, 'R': {'CGT': 0.36, 'CGC': 0.44, 'CGA': 0.07, 'CGG': 0.07, 'AGA': 0.07, 'AGG': 0.0}, 'I': {'ATT': 0.58, 'ATC': 0.35, 'ATA': 0.07}, 'M': {'ATG': 1.0}, 'T': {'ACT': 0.16, 'ACC': 0.47, 'ACA': 0.13, 'ACG': 0.24}, 'N': {'AAT': 0.47, 'AAC': 0.53}, 'K': {'AAA': 0.73, 'AAG': 0.27}, 'V': {'GTT': 0.25, 'GTC': 0.18, 'GTA': 0.17, 'GTG': 0.4}, 'A': {'GCT': 0.11, 'GCC': 0.31, 'GCA': 0.2, 'GCG': 0.38}, 'D': {'GAT': 0.65, 'GAC': 0.35}, 'E': {'GAA': 0.7, 'GAG': 0.3}, 'G': {'GGT': 0.29, 'GGC': 0.46, 'GGA': 0.13, 'GGG': 0.12}}, # Codon usage table of E. Coli mapping amino acids to codons and frequencies.
    NDGR:int=100, # Number of sequences to generate via the LSTM for sequence logo estimation. 
):

Optimize a DNA sequence via synonymous codon substitutions and shows the sequence logo for each of the optimal sequences.

This function performs a beam-search–based optimization of a nucleotide sequence by iteratively proposing single-codon synonymous changes and evaluating them with the two scoring functions. The search stops early if a variant meets the specified score thresholds, otherwise the best Pareto- optimal solution is returned.

seq = 'GACACCTGCTATGGATTAAAAAGGCGCTCCCGTTGGGTACCAGGTCGCGGCACCTAACTGCAGGCACATC'
    dict_allowed={10:['R','Y']}
    dict_allowed_min_max={10:'max'}
    optimize_sequence_display_proteins(seq,N=5,CHANGES=6,dict_allowed_AAs=dict_allowed,dict_allowed_AAs_max_min=dict_allowed_min_max)

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:07<00:00,  8.78it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step 
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:05<00:00, 11.75it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step 
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:06<00:00, 10.07it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step 
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:07<00:00,  9.29it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step 
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:06<00:00, 10.79it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

seq = 'GACACCTGCTATGGATTAAAAAGGCGCTCCCGTTGGGTACCAGGTCGCGGCACCTAACTGCAGGCACATC'
    dict_allowed={10:['R','Y']}
    dict_allowed_min_max={10:'min'}
    optimize_sequence_display_proteins(seq,N=5,CHANGES=6,dict_allowed_AAs=dict_allowed,dict_allowed_AAs_max_min=dict_allowed_min_max)

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:06<00:00, 11.20it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step 
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:06<00:00, 10.61it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step 
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:07<00:00,  9.99it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:06<00:00, 10.02it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step
Generating sequence: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 70/70 [00:07<00:00,  9.84it/s]
/home/regnier/miniconda3/envs/lstm_new/lib/python3.12/site-packages/logomaker/src/error_handling.py:58: UserWarning:  Warning: Character '*' is not in color_dict. Using black.
  warnings.warn(str(Error))
<Figure size 1200x400 with 0 Axes>

Likelihood computation

Compute log-likelihood of observed VR sequences given a TR.


source

compute_likelihood

def compute_likelihood(
    TR, # Reference/template sequence.
    VR, # Variant sequence to score.
): # Log-likelihood of VR given TR.

Compute the log-likelihood of generating a variant sequence (VR) given a template/reference sequence (TR).

TR='CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC'
    VR='CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC'
    compute_likelihood(TR, VR)
np.float32(-4.534629)

source

compute_likelihood_list

def compute_likelihood_list(
    TR_list, # Reference/template sequences.
    VR_list, # Variant sequences.
): # Log-likelihoods in the same order as input.

Compute log-likelihoods for (TR, VR) pairs.

TR_list=['CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC']*2
    VR_list=['CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC','CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTATCATAAGACGCGACTGGGCCC']
    compute_likelihood_list(TR_list, VR_list)
[np.float32(-4.5359917), np.float32(-8.759228)]

source

compute_likelihood_matrix

def compute_likelihood_matrix(
    TR_list, # List of reference/template sequences.
    VR_list, # List of variant sequences.
    batch_size:int=64, # (Currently unused) Intended batch size for future optimization.
): # Log-likelihood matrix of shape (len(TR_list), len(VR_list)).

Compute a matrix of log-likelihoods where each entry (i, j) corresponds to the log-likelihood of generating VR_list[j] from TR_list[i].

Sequences with mismatched lengths are assigned -inf.

TR_list=['CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC','CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTATCATAAGACGCGACTGGGCCC']
    VR_list=['CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTAACATAAGACGCGACTGGGCCC','CGTAAACCGGACCTAGTTTAGTTCTTAGACCAAGGTACATATCCCCGTATCATAAGACGCGACTGGGCCC']
    compute_likelihood_matrix(TR_list, VR_list)
[[np.float32(-4.5359917), np.float32(-8.759228)],
 [np.float32(-16.45727), np.float32(-5.387972)]]