Spaces:
Sleeping
Sleeping
Upload 5 files
Browse files- README.md +75 -14
- app.py +141 -0
- requirements.txt +0 -0
- semillas.json +12 -0
- translate_to_geneforgelang.py +49 -0
README.md
CHANGED
@@ -1,14 +1,75 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🧬 GeneForgeLang: Symbolic-to-Sequence Protein Design Toolkit
|
2 |
+
|
3 |
+
GeneForgeLang is a symbolic language and toolset for generative biology. It connects high-level biological design intentions to low-level amino acid sequences via AI, rules, and natural language.
|
4 |
+
|
5 |
+
---
|
6 |
+
|
7 |
+
## 🚀 Features
|
8 |
+
|
9 |
+
| Module | Description |
|
10 |
+
|----------------------------|-------------|
|
11 |
+
| 🧠 Phrase → Protein | Generate realistic protein sequences from symbolic phrases |
|
12 |
+
| 🧪 Protein → Phrase | Infer functional motifs from amino acid sequences |
|
13 |
+
| 📖 Phrase → Description | Translate symbolic design into scientific English |
|
14 |
+
| 🧬 Mutate Protein | Generate variants of proteins from the same symbolic seed |
|
15 |
+
| 📦 Export to FASTA | Download generated proteins for downstream use |
|
16 |
+
| 📊 Analyze Protein | Visualize amino acid composition as bar plot |
|
17 |
+
| 📚 Symbolic Language | GeneForgeLang syntax allows structured protein definitions |
|
18 |
+
|
19 |
+
---
|
20 |
+
|
21 |
+
## 🧪 Example
|
22 |
+
|
23 |
+
### Input Phrase:
|
24 |
+
^p:Dom(Kin)-Mot(NLS)*AcK@147=Localize(Nucleus)
|
25 |
+
|
26 |
+
yaml
|
27 |
+
Copiar
|
28 |
+
Editar
|
29 |
+
|
30 |
+
### Output:
|
31 |
+
- Seed: `MKKK`
|
32 |
+
- Generated protein: realistic sequence (via ProtGPT2)
|
33 |
+
- Properties: length, charge, MW
|
34 |
+
- Description: *“This protein contains a kinase domain, a nuclear localization signal, and lysine acetylation at a specific position.”*
|
35 |
+
- Export: `.fasta` format
|
36 |
+
- Graph: bar plot of amino acid composition
|
37 |
+
|
38 |
+
---
|
39 |
+
|
40 |
+
## ▶️ How to Use
|
41 |
+
|
42 |
+
1. Clone this repo
|
43 |
+
2. Install dependencies:
|
44 |
+
```bash
|
45 |
+
pip install -r requirements.txt
|
46 |
+
Launch the interface:
|
47 |
+
|
48 |
+
bash
|
49 |
+
Copiar
|
50 |
+
Editar
|
51 |
+
python app.py
|
52 |
+
Navigate to:
|
53 |
+
|
54 |
+
cpp
|
55 |
+
Copiar
|
56 |
+
Editar
|
57 |
+
http://127.0.0.1:7860
|
58 |
+
📁 Repository Structure
|
59 |
+
|
60 |
+
File Description
|
61 |
+
app.py Main UI app with all functionality
|
62 |
+
semillas.json Phrase-to-seed dictionary
|
63 |
+
translate_to_geneforgelang.py Reverse translator
|
64 |
+
README.md This file
|
65 |
+
requirements.txt Python dependencies
|
66 |
+
🧠 Developed by
|
67 |
+
Fundación de Neurociencias
|
68 |
+
Licensed under the MIT License
|
69 |
+
|
70 |
+
Join us in shaping symbolic bio-AI.
|
71 |
+
|
72 |
+
yaml
|
73 |
+
Copiar
|
74 |
+
Editar
|
75 |
+
|
app.py
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
3 |
+
import torch
|
4 |
+
import json
|
5 |
+
import re
|
6 |
+
import tempfile
|
7 |
+
|
8 |
+
# Load symbolic phrase dictionary
|
9 |
+
with open("semillas.json", "r", encoding="utf-8") as f:
|
10 |
+
diccionario_semillas = json.load(f)
|
11 |
+
|
12 |
+
def phrase_to_seed(phrase):
|
13 |
+
phrase = phrase.lower()
|
14 |
+
for key, seed in diccionario_semillas.items():
|
15 |
+
if key.lower() in phrase:
|
16 |
+
return seed
|
17 |
+
return "M"
|
18 |
+
|
19 |
+
tokenizer = AutoTokenizer.from_pretrained("nferruz/ProtGPT2", do_lower_case=False)
|
20 |
+
tokenizer.pad_token = tokenizer.eos_token
|
21 |
+
model = AutoModelForCausalLM.from_pretrained("nferruz/ProtGPT2")
|
22 |
+
|
23 |
+
def generate_protein_and_props(phrase):
|
24 |
+
seed = phrase_to_seed(phrase)
|
25 |
+
inputs = tokenizer(seed, return_tensors="pt", padding=True)
|
26 |
+
input_ids = inputs["input_ids"]
|
27 |
+
attention_mask = inputs.get("attention_mask", torch.ones_like(input_ids))
|
28 |
+
|
29 |
+
with torch.no_grad():
|
30 |
+
output = model.generate(
|
31 |
+
input_ids=input_ids,
|
32 |
+
attention_mask=attention_mask,
|
33 |
+
max_length=100,
|
34 |
+
min_length=20,
|
35 |
+
do_sample=True,
|
36 |
+
top_k=50,
|
37 |
+
temperature=0.9,
|
38 |
+
pad_token_id=tokenizer.eos_token_id,
|
39 |
+
num_return_sequences=1
|
40 |
+
)
|
41 |
+
|
42 |
+
seq = tokenizer.decode(output[0], skip_special_tokens=True)
|
43 |
+
|
44 |
+
# Calculate properties
|
45 |
+
length = len(seq)
|
46 |
+
aa_count = {aa: seq.count(aa) for aa in "ACDEFGHIKLMNPQRSTVWY"}
|
47 |
+
charge = sum([aa_count.get(a, 0) for a in "KR"]) - sum([aa_count.get(a, 0) for a in "DE"])
|
48 |
+
mw = sum([aa_count[a]*w for a, w in {
|
49 |
+
"A": 89.1, "C": 121.2, "D": 133.1, "E": 147.1, "F": 165.2,
|
50 |
+
"G": 75.1, "H": 155.2, "I": 131.2, "K": 146.2, "L": 131.2,
|
51 |
+
"M": 149.2, "N": 132.1, "P": 115.1, "Q": 146.2, "R": 174.2,
|
52 |
+
"S": 105.1, "T": 119.1, "V": 117.1, "W": 204.2, "Y": 181.2
|
53 |
+
}.items()])
|
54 |
+
|
55 |
+
props = f"🧪 Seed: {seed}\n🧬 Protein: {seq}\n\n🔬 Properties:\n- Length: {length} aa\n- Charge: {charge}\n- MW: {mw:.1f} Da"
|
56 |
+
|
57 |
+
# Save to FASTA
|
58 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".fasta", mode="w", encoding="utf-8") as f:
|
59 |
+
f.write(f">Generated_Protein\n{seq}\n")
|
60 |
+
fasta_path = f.name
|
61 |
+
|
62 |
+
return props, fasta_path
|
63 |
+
|
64 |
+
def sequence_to_phrase(seq):
|
65 |
+
seq = seq.upper()
|
66 |
+
tags = []
|
67 |
+
if re.search(r"^M*K{3,}", seq):
|
68 |
+
tags.append("Dom(Kin)")
|
69 |
+
if re.search(r"[RK]{3,}", seq):
|
70 |
+
tags.append("Mot(NLS)")
|
71 |
+
if len(re.findall(r"E", seq)) >= 5 or "DEG" in seq:
|
72 |
+
tags.append("Mot(PEST)")
|
73 |
+
if re.search(r"KQAK|QAK", seq):
|
74 |
+
tags.append("*AcK@X")
|
75 |
+
if re.search(r"[RST]P", seq):
|
76 |
+
tags.append("*P@X")
|
77 |
+
if "PRKRK" in seq or "PKKKRKV" in seq:
|
78 |
+
tags.append("Localize(Nucleus)")
|
79 |
+
if re.search(r"(AILFL|LAGGAV|LVLL|AAVL)", seq):
|
80 |
+
tags.append("Localize(Membrane)")
|
81 |
+
return "^p:" + "-".join(sorted(set(tags))) if tags else "// No symbolic motifs found"
|
82 |
+
|
83 |
+
def phrase_to_description(phrase):
|
84 |
+
phrase = phrase.replace("^p:", "")
|
85 |
+
fragments = phrase.split("-")
|
86 |
+
translation = {
|
87 |
+
"Dom(Kin)": "a kinase domain",
|
88 |
+
"Mot(NLS)": "a nuclear localization signal",
|
89 |
+
"Mot(PEST)": "a PEST motif indicating protein degradation",
|
90 |
+
"*AcK@X": "lysine acetylation at a specific position",
|
91 |
+
"*P@X": "a phosphorylation site",
|
92 |
+
"Localize(Nucleus)": "localizes to the cell nucleus",
|
93 |
+
"Localize(Membrane)": "localizes to the cell membrane"
|
94 |
+
}
|
95 |
+
phrases = [translation.get(tag, tag) for tag in fragments if tag]
|
96 |
+
if not phrases:
|
97 |
+
return "No interpretable symbolic elements found."
|
98 |
+
return "This protein contains " + ", ".join(phrases[:-1]) + (
|
99 |
+
f", and {phrases[-1]}." if len(phrases) > 1 else f"{phrases[0]}.")
|
100 |
+
|
101 |
+
with gr.Blocks() as demo:
|
102 |
+
gr.Markdown("# 🧬 GeneForgeLang AI Tools")
|
103 |
+
gr.Markdown("Design, interpret, describe, and export proteins using symbolic language and AI.")
|
104 |
+
|
105 |
+
with gr.Tab("🧠 Phrase → Protein"):
|
106 |
+
inp = gr.Textbox(label="GeneForgeLang Phrase", placeholder="^p:Dom(Kin)-Mot(NLS)*AcK@147")
|
107 |
+
out = gr.Textbox(label="Protein + Properties")
|
108 |
+
fasta = gr.File(label="Download FASTA")
|
109 |
+
btn = gr.Button("Generate")
|
110 |
+
btn.click(fn=generate_protein_and_props, inputs=inp, outputs=[out, fasta])
|
111 |
+
|
112 |
+
with gr.Tab("🧪 Protein → Phrase"):
|
113 |
+
inp2 = gr.Textbox(label="Protein Sequence", placeholder="MKKKPRRRDEEGEK...")
|
114 |
+
out2 = gr.Textbox(label="Interpreted GeneForgeLang")
|
115 |
+
btn2 = gr.Button("Translate")
|
116 |
+
btn2.click(fn=sequence_to_phrase, inputs=inp2, outputs=out2)
|
117 |
+
|
118 |
+
|
119 |
+
with gr.Tab("🧬 Mutate Protein"):
|
120 |
+
inp4 = gr.Textbox(label="GeneForgeLang Phrase", placeholder="^p:Dom(Kin)-Mot(NLS)*AcK@147")
|
121 |
+
out4 = gr.Textbox(label="Mutated Protein")
|
122 |
+
btn4 = gr.Button("Mutate")
|
123 |
+
btn4.click(fn=generate_protein_and_props, inputs=inp4, outputs=[out4, gr.File(visible=False)])
|
124 |
+
|
125 |
+
|
126 |
+
with gr.Tab("📊 Analyze Protein"):
|
127 |
+
inp5 = gr.Textbox(label="Protein Sequence", placeholder="Paste sequence to analyze")
|
128 |
+
out5 = gr.Image(label="Amino Acid Composition")
|
129 |
+
btn5 = gr.Button("Analyze")
|
130 |
+
def analyze_graph(seq): return generar_composicion_grafico(seq)
|
131 |
+
btn5.click(fn=analyze_graph, inputs=inp5, outputs=out5)
|
132 |
+
|
133 |
+
with gr.Tab("📖 Phrase → Natural Language"):
|
134 |
+
|
135 |
+
inp3 = gr.Textbox(label="GeneForgeLang Phrase", placeholder="^p:Dom(Kin)-Mot(NLS)*AcK@147")
|
136 |
+
out3 = gr.Textbox(label="Scientific Description")
|
137 |
+
btn3 = gr.Button("Describe")
|
138 |
+
btn3.click(fn=phrase_to_description, inputs=inp3, outputs=out3)
|
139 |
+
|
140 |
+
if __name__ == "__main__":
|
141 |
+
demo.launch()
|
requirements.txt
ADDED
Binary file (78 Bytes). View file
|
|
semillas.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"Dom(Kin)": "MKKK",
|
3 |
+
"Mot(NLS)": "MPRRR",
|
4 |
+
"Mot(PEST)": "MDGQL",
|
5 |
+
"TF(GATA1)": "MKTFG",
|
6 |
+
"*AcK": "MKQAK",
|
7 |
+
"*Ac": "MKQAK",
|
8 |
+
"*P": "MKRP",
|
9 |
+
"*Phos": "MKRP",
|
10 |
+
"Localize(Nucleus)": "MPKRK",
|
11 |
+
"Localize(Membrane)": "MAIFL"
|
12 |
+
}
|
translate_to_geneforgelang.py
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
import re
|
3 |
+
|
4 |
+
# Nuevas reglas de reconocimiento con patrones suaves
|
5 |
+
def traducir_a_geneforge(secuencia):
|
6 |
+
motivos = []
|
7 |
+
|
8 |
+
# Dom(Kin): 3 o más K seguidos al principio
|
9 |
+
if re.search(r"^M*K{3,}", secuencia):
|
10 |
+
motivos.append("Dom(Kin)")
|
11 |
+
|
12 |
+
# Mot(NLS): presencia de varias R o K juntas, típica señal nuclear
|
13 |
+
if re.search(r"[RK]{3,}", secuencia):
|
14 |
+
motivos.append("Mot(NLS)")
|
15 |
+
|
16 |
+
# Mot(PEST): alta densidad de E o D (glutámico o aspártico)
|
17 |
+
if len(re.findall(r"E", secuencia)) >= 5 or "DEG" in secuencia:
|
18 |
+
motivos.append("Mot(PEST)")
|
19 |
+
|
20 |
+
# *AcK@X: presencia de "KQAK" o "QAK" como motivo de acetilación
|
21 |
+
if re.search(r"KQAK|QAK", secuencia):
|
22 |
+
motivos.append("*AcK@X")
|
23 |
+
|
24 |
+
# *P@X: motivos de fosforilación comunes
|
25 |
+
if re.search(r"[RST]P", secuencia):
|
26 |
+
motivos.append("*P@X")
|
27 |
+
|
28 |
+
# Localize(Nucleus): presencia de PRKRK, PKKKRKV
|
29 |
+
if "PRKRK" in secuencia or "PKKKRKV" in secuencia:
|
30 |
+
motivos.append("Localize(Nucleus)")
|
31 |
+
|
32 |
+
# Localize(Membrane): patrones hidrofóbicos como AILFL o LAGGAV
|
33 |
+
if re.search(r"(AILFL|LAGGAV|LVLL|AAVL)", secuencia):
|
34 |
+
motivos.append("Localize(Membrane)")
|
35 |
+
|
36 |
+
if not motivos:
|
37 |
+
return "// No se encontraron motivos reconocibles"
|
38 |
+
|
39 |
+
return "^p:" + "-".join(sorted(set(motivos)))
|
40 |
+
|
41 |
+
if __name__ == "__main__":
|
42 |
+
if len(sys.argv) < 2:
|
43 |
+
print("Uso: python traducir_a_geneforgelang_v2.py <secuencia_proteina>")
|
44 |
+
sys.exit(1)
|
45 |
+
|
46 |
+
secuencia = sys.argv[1].upper()
|
47 |
+
resultado = traducir_a_geneforge(secuencia)
|
48 |
+
print("🔍 GeneForgeLang:")
|
49 |
+
print(resultado)
|