File size: 6,760 Bytes
1f88cea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from boltzgen.utils.quiet import quiet_startup

quiet_startup()

import os

# Disable Triton auto-tuning during inference
os.environ.setdefault("CUEQ_DEFAULT_CONFIG", "1")
os.environ.setdefault("CUEQ_DISABLE_AOT_TUNING", "1")

from typing import List, Optional, Union

import torch
from omegaconf import OmegaConf, listconfig
from pytorch_lightning import LightningModule, Trainer

from pytorch_lightning.strategies import DDPStrategy

from boltzgen.task.predict.data_from_generated import FromGeneratedDataModule
from boltzgen.task.predict.writer import (
    DesignWriter,
    FoldingWriter,
)
from boltzgen.task.task import Task
from boltzgen.utils.pipeline_progress_bar import PipelineProgressBar
from boltzgen.model.models.boltz import Boltz


class Predict(Task):
    """A task to run model inference."""

    def __init__(
        self,
        data: Union[FromGeneratedDataModule],
        writer: Union[DesignWriter, FoldingWriter],
        checkpoint: str,
        output: str,
        name: str,
        recycling_steps: int,
        sampling_steps: int,
        diffusion_samples: int = 1,
        keys_dict_out: Optional[List] = None,
        keys_dict_batch: Optional[List] = None,
        slurm: bool = False,
        matmul_precision: Optional[str] = None,
        trainer: Optional[dict] = None,
        override: Optional[dict] = None,
        debug: bool = False,
        use_ema: bool = False,
        write_manifest: bool = False,
        compile_pairformer: bool = False,
        compile_structure: bool = False,
        checkpoint_diffusion_conditioning: bool = False,
    ) -> None:
        """Initialize the task.

        Parameters
        ----------
        checkpoint : str
            The path to the model checkpoint.
        output : str
            The path to save the inference results.
        slurm : bool, optional
            Whether to run on SLURM, by default False
        matmul_precision : Optional[str], optional
            The matmul precision, by default None
        trainer : Optional[dict], optional
            The configuration for the trainer, by default None
        override : Optional[dict], optional
            The override configuration for the model, by default None

        """
        self.data = data
        self.checkpoint = checkpoint
        self.output = output
        self.slurm = slurm
        self.matmul_precision = matmul_precision
        self.trainer = trainer
        self.override = override if override is not None else {}
        self.predict_args = {
            "recycling_steps": recycling_steps,
            "sampling_steps": sampling_steps,
            "diffusion_samples": diffusion_samples,
        }
        if keys_dict_batch is not None:
            self.predict_args["keys_dict_batch"] = keys_dict_batch
        if keys_dict_out is not None:
            self.predict_args["keys_dict_out"] = keys_dict_out
        self.debug = debug
        self.use_ema = use_ema
        self.write_manifest = write_manifest
        self.writer = writer
        self.compile_pairformer = compile_pairformer
        self.compile_structure = compile_structure
        self.checkpoint_diffusion_conditioning = checkpoint_diffusion_conditioning

    def run(self, config: OmegaConf = None, run_prediction=True) -> None:  # noqa: ARG002
        # Silence warnings and pytorch lightning tips
        quiet_startup()

        # Exit quickly if no predictions are needed
        if len(self.data.predict_set) == 0:
            print("No predictions required")
            return

        # Set no grad
        torch.set_grad_enabled(False)

        # Experiment with this during training (high or medium)
        if self.matmul_precision is not None:
            torch.set_float32_matmul_precision(self.matmul_precision)

        # Create trainer dict
        if self.trainer is None:
            self.trainer = {}

        # Flip some arguments in debug mode
        devices = self.trainer.get("devices", 1)

        if self.debug:
            if isinstance(devices, int):
                devices = 1
            elif isinstance(devices, (list, listconfig.ListConfig)):
                devices = [devices[0]]
            self.trainer["devices"] = devices
            self.data.num_workers = 0

        # slurm
        if self.slurm:
            self.trainer["devices"] = int(
                os.environ.get("SLURM_NTASKS_PER_NODE", "auto")
            )
            self.trainer["num_nodes"] = int(os.environ.get("SLURM_NNODES", 1))

        # Load model
        self.model_module: LightningModule = Boltz.load_from_checkpoint(
            self.checkpoint,
            strict=True,
            use_ema=self.use_ema,
            checkpoint_diffusion_conditioning=self.checkpoint_diffusion_conditioning,
            map_location="cpu",
            weights_only=False,
            predict_args=self.predict_args,
            **self.override,
        )
        self.model_module.eval()

        if self.compile_pairformer:
            self.model_module.is_pairformer_compiled = True
            self.model_module.pairformer_module = torch.compile(
                self.model_module.pairformer_module, dynamic=True, fullgraph=False
            )
        if self.compile_structure:
            self.model_module.structure_module.score_model.is_token_transformer_compiled = True
            self.model_module.structure_module.score_model.token_transformer = (
                torch.compile(
                    self.model_module.structure_module.score_model.token_transformer,
                    dynamic=True,
                    fullgraph=False,
                )
            )

        # Set up trainer
        strategy = "auto"
        num_devices = (
            len(devices)
            if isinstance(devices, (list, listconfig.ListConfig))
            else devices
        )
        if num_devices > 1:
            strategy = DDPStrategy()
            if num_devices > len(self.data.predict_set):
                devices = max(1, len(self.data.predict_set))
                msg = f"Fewer designs than devices. Setting devices to {devices}."
                print(msg)
                self.trainer["devices"] = devices

        self.lightning_trainer = Trainer(
            default_root_dir=self.output,
            strategy=strategy,
            callbacks=[self.writer]
            + (
                [PipelineProgressBar()]
                if os.environ.get("BOLTZGEN_PIPELINE_STEP")
                else []
            ),
            **self.trainer,
        )
        if run_prediction:
            # Run training
            self.lightning_trainer.predict(
                self.model_module, datamodule=self.data, return_predictions=False
            )
            del self.model_module