File size: 7,829 Bytes
7a081f6
 
 
73ac8c4
985d408
3f6b5d5
9d0b7c6
cb10cd7
985d408
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555ec33
985d408
 
 
 
 
 
 
b9cf7a5
12e552b
 
 
 
 
0eafe37
12e552b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3f0119
890a1ee
 
 
 
 
 
 
 
a470b92
985d408
69f3090
73ac8c4
985d408
 
 
cb10cd7
73ac8c4
985d408
73ac8c4
 
8b5a02b
 
69f3090
73ac8c4
 
 
a470b92
51cb03c
dfa6270
 
 
 
 
51cb03c
 
a470b92
51cb03c
 
40a5916
0724f2d
985d408
0724f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
985d408
0724f2d
40a5916
 
 
 
0724f2d
40a5916
 
 
b9cf7a5
0724f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
2948e20
f3f0119
0724f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3f0119
 
 
0724f2d
f3f0119
 
 
0724f2d
f3f0119
 
0724f2d
f3f0119
 
 
0724f2d
f3f0119
 
 
0724f2d
f3f0119
de9e615
890a1ee
 
 
 
 
829bdf3
890a1ee
 
 
985d408
40a5916
985d408
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import gradio as gr
import os
import subprocess
from pathlib import Path
import shutil
import spaces


# Function to check that the files are in the same directory
def check_files_in_same_directory(file1, file2):
    if file1 is None or file2 is None:
        raise ValueError("Both the YAML config and target PDB/CIF files must be provided.")
    if Path(file1.name).parent != Path(file2.name).parent:
        raise ValueError("YAML config and target PDB/CIF must be in the same directory.")
    
def move_file_to_output(file, output_dir):
    if file is None:
        return None
    src_path = Path(file.name)          # path to the temp-uploaded file
    dest_path = Path(output_dir) / src_path.name

    dest_path.parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(src_path, dest_path)   # preserves metadata

    return dest_path

def move_files_to_output(yaml_file, target_file, output_dir):
    yaml_path_dest = move_file_to_output(yaml_file, output_dir)
    target_path_dest = move_file_to_output(target_file, output_dir)
    check_files_in_same_directory(yaml_path_dest, target_path_dest)
    return yaml_path_dest, target_path_dest

def download_results(results_dir, zip_name):
    """
    Creates a ZIP of `results_dir` and returns (zip_path or None, message).
    """
    results_dir = Path(results_dir)

    if not results_dir.exists() or not results_dir.is_dir():
        return None, "No results available for download."

    # Ensure zip_name is a Path and has .zip extension
    zip_path = Path(zip_name)
    if zip_path.suffix != ".zip":
        zip_path = zip_path.with_suffix(".zip")

    # Make parent directory if needed
    if zip_path.parent != Path(""):
        zip_path.parent.mkdir(parents=True, exist_ok=True)

    # shutil.make_archive wants base_name without extension
    base_name = str(zip_path.with_suffix(""))
    shutil.make_archive(
        base_name=base_name,
        format="zip",
        root_dir=str(results_dir),
    )

    zip_path = Path(base_name + ".zip")
    return str(zip_path), "Download ready!"

def find_log_file(output_dir):
    # log files start with "boltzgen" and end with ".log"
    log_files = list(Path(output_dir).glob("boltzgen*.log"))
    if log_files:
        # return the most recently modified log file
        return max(log_files, key=lambda f: f.stat().st_mtime)
    return None

@spaces.GPU(duration=300)  
def run_boltzgen(yaml_file, target_file, protocol, num_designs, budget):
    output_dir = "/tmp/output"
    os.makedirs(output_dir, exist_ok=True)
   
    # Move config and target files to the output directory
    yaml_path_dest, target_path_dest = move_files_to_output(yaml_file, target_file, output_dir)

    cmd = [
        "boltzgen", "run", str(yaml_path_dest),
        "--output", output_dir,
        "--protocol", protocol,
        "--num_designs", str(num_designs),  
        "--budget", str(budget),
        "--cache", "/tmp/cache"
    ]
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, cwd=output_dir, timeout=300)
        if result.returncode == 0:
            final_designs_dir = Path(output_dir) / "final_ranked_designs"
            if final_designs_dir.exists() and final_designs_dir.is_dir():
                return f"Success!\nDesigns saved to {final_designs_dir}/"
            else:
                return "No designs passed the filtering criteria."
        return f"Error:\n{result.stderr}"
    except subprocess.TimeoutExpired:
        return "Timeout (300s limit)"
    except Exception as e:
        return f"Failed: {str(e)}"

# Interface
with gr.Blocks() as demo:
    # Markdown with image at the beginning
    gr.Markdown(""" <h1 style="font-size: 48px;">BoltzGen Design Space</h1>""")

    gr.Markdown(
        """
        <span style="font-size:20px">

        **Design novel protein, peptide, and nanobody binders** against any biomolecular target (proteins, DNA, small molecules) using the BoltzGen diffusion model.
        
        **BoltzGen** unifies structure prediction and binder design in a single all-atom generative model with flexible design constraints. 
        
        Developed by Hannes Stärk and team at MIT: [description](https://jclinic.mit.edu/boltzgen/) [git](https://github.com/HannesStark/boltzgen) 
        </span>
        """)
    gr.Markdown(
        """
        ### Instructions
        1. Prepare a YAML config file specifying your design task. See [BoltzGen Docs](https://github.com/HannesStark/boltzgen/tree/main/example) for examples.
        2. Upload the target structure file (PDB or CIF) corresponding to your design as needed (by the YAML config).
        3. Select the design protocol (chose "protein-anything" for general results).
        4. Specify the number of designs to generate. !Warning: high numbers may take longer to compute than your session allows.
        5. Specify your budget (# of designs wanted in the final diversified set). Note that the budget must be less than or equal to the number of designs.
        6. Click "Run BoltzGen" and wait for the results!
        7. Download your results using the buttons at the bottom: 
            - Final Ranked Designs: structures, metrics and report summary of the best designs after filtering and ranking (interaction score and diversity).
            - Intermediate Designs: all generated trajectories before filtering/ranking.
        """
    )

    with gr.Row():
        yaml_file = gr.File(
            label="Design YAML Config",
            file_types=[".yaml", ".yml"],
            file_count="single"
        )
        target_file = gr.File(
            label="Target PDB/CIF",
            file_types=[".pdb", ".cif"],
            file_count="single"
        )

    with gr.Row():
        num_designs = gr.Number(
            value=10,
            label="Number of Designs",
            precision=0,
            minimum=1,
            maximum=1000
        )
        budget = gr.Number(
            value=5,
            label="Budget (in tokens)",
            precision=0,
            minimum=1,
            maximum=1000
        )

    protocol = gr.Dropdown(
        choices=["protein-anything", "peptide-anything", "nanobody-anything"],
        value="protein-anything",
        label="Protocol"
    )

    run_button = gr.Button("Run BoltzGen", variant="primary")
    output_text = gr.Textbox(label="Run Status", lines=2)

    run_button.click(
        run_boltzgen,
        inputs=[yaml_file, target_file, protocol, num_designs, budget],
        outputs=[output_text]
    )

    gr.Markdown("## Download the results")
    with gr.Row():
        download_final_ranked_designs_button = gr.Button("Download Final Results")
        download_intermediate_results_button = gr.Button("Download Intermediate Designs")

    with gr.Row():    
        download_final_ranked_designs_button.click(
            download_results,
            inputs=[
                gr.State("/tmp/output/final_ranked_designs"),
                gr.State("final_ranked_designs.zip")
            ],
            outputs=[
                gr.File(label="Download Final Ranked Designs")
            ]
        )

        download_intermediate_results_button.click(
            download_results,
            inputs=[
                gr.State("/tmp/output"),
                gr.State("intermediate_results.zip")
            ],
            outputs=[
                gr.File(label="Download Intermediate Designs")
            ]
        )

    # Show the log output in a textbox for debugging
    with gr.Row():
        log_output = gr.Textbox(label="BoltzGen Log Output", lines=10)
        run_button.click(
            lambda: find_log_file("/tmp").read_text() if find_log_file("/tmp") else "No log file found.",
            inputs=[],
            outputs=log_output
        )
    
demo.queue()
demo.launch()