Skip to content

Commit 9747539

Browse files
authored
Prepare UMA inference gradients before compilation (#2143)
This standalone PR replaces [#2126](#2126). The ghstack PR was marked merged into its temporary `gh/mlazos/4/base` branch, but its patch did not land on `main`. UMA computes forces and stress by differentiating energy with respect to positions and cells. The backbone previously enabled those gradients with `requires_grad_` inside its compiled forward. Dynamo cannot represent that tensor metadata mutation, so it stopped and resumed tracing at each occurrence. This change prepares position and cell gradient metadata in `MLIPPredictUnit` after device and dtype conversion but before entering the model. Guarded fallback calls remain in the backbone for direct users; predictor-prepared inputs already satisfy those guards, so normal compiled inference performs no metadata mutation inside the captured region. The fix is independent of topology source. It applies with both `external_graph_gen=True` and the final validation configuration using `external_graph_gen=False`, `internal_graph_gen_version=3`, `merge_mole=True`, and `compile_dynamic_shapes=False`. No independent flag enables it: ```python settings = InferenceSettings(compile=True) ``` A compiled UMA-S-1p2 run previously reported five `requires_grad_` graph-break sites and none after this change. **Test plan** ```bash PYTHONPATH=$PWD/src:$PYTHONPATH /home/mlazos/.conda/envs/pytorch-3.12/bin/python -m pytest -q tests/core/units/mlip_unit/test_predict.py -k test_prepare_inference_gradients /home/mlazos/.conda/envs/pytorch-3.12/bin/pre-commit run --files src/fairchem/core/models/uma/escn_md.py src/fairchem/core/units/mlip_unit/predict.py tests/core/units/mlip_unit/test_predict.py PYTHONPATH=$PWD/src:$PYTHONPATH /home/mlazos/.conda/envs/pytorch-3.12/bin/python -m compileall -q src/fairchem/core/models/uma/escn_md.py src/fairchem/core/units/mlip_unit/predict.py tests/core/units/mlip_unit/test_predict.py ``` Authored with assistance from Codex.
1 parent 4ac989d commit 9747539

3 files changed

Lines changed: 47 additions & 3 deletions

File tree

src/fairchem/core/models/uma/escn_md.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -780,9 +780,11 @@ def forward(self, data_dict: AtomicData) -> dict[str, torch.Tensor]:
780780
# Must be set before graph generation so the computation graph
781781
# tracks positions and cell through edge distance calculations.
782782
if not self.regress_config.direct_forces:
783-
if self.regress_config.forces or self.regress_config.stress:
783+
if (
784+
self.regress_config.forces or self.regress_config.stress
785+
) and not data_dict["pos"].requires_grad:
784786
data_dict["pos"].requires_grad_(True)
785-
if self.regress_config.stress:
787+
if self.regress_config.stress and not data_dict["cell"].requires_grad:
786788
data_dict["cell"].requires_grad_(True)
787789

788790
with record_function("generate_graph"):

src/fairchem/core/units/mlip_unit/predict.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ def collated_predict(
9090
return collated_predict
9191

9292

93+
def _prepare_inference_gradients(backbone, data: AtomicData) -> None:
94+
regress_config = getattr(backbone, "regress_config", None)
95+
if regress_config is None or regress_config.direct_forces:
96+
return
97+
if regress_config.forces or regress_config.stress:
98+
data["pos"].requires_grad_(True)
99+
if regress_config.stress:
100+
data["cell"].requires_grad_(True)
101+
102+
93103
class MLIPPredictUnitProtocol(Protocol):
94104
def predict(self, data: AtomicData, undo_element_references: bool) -> dict: ...
95105

@@ -494,6 +504,9 @@ def predict(
494504
if torch.is_tensor(val) and val.is_floating_point():
495505
data_device[key] = val.to(dtype)
496506

507+
backbone = self.model.module.backbone
508+
_prepare_inference_gradients(backbone, data_device)
509+
497510
# Model handles any per-prediction checks (e.g., MOLE consistency)
498511
try:
499512
self.model.module.on_predict_check(data_device)

tests/core/units/mlip_unit/test_predict.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@
4343
from fairchem.core.models.uma.nn.execution_backends import UMASFastGPUBackend
4444
from fairchem.core.units.mlip_unit import InferenceSettings, MLIPPredictUnit
4545
from fairchem.core.units.mlip_unit.mlip_unit import initialize_finetuning_model
46-
from fairchem.core.units.mlip_unit.predict import ParallelMLIPPredictUnit
46+
from fairchem.core.units.mlip_unit.predict import (
47+
ParallelMLIPPredictUnit,
48+
_prepare_inference_gradients,
49+
)
4750
from fairchem.core.units.mlip_unit.single_atom_patch import (
4851
single_atom_prediction_from_lookup,
4952
)
@@ -67,6 +70,32 @@ def _resolve_checkpoint_path(name_or_path: str) -> str:
6770
ATOL = 5e-4
6871

6972

73+
@pytest.mark.parametrize(
74+
("forces", "stress", "pos_grad", "cell_grad"),
75+
[
76+
(False, False, False, False),
77+
(True, False, True, False),
78+
(True, True, True, True),
79+
],
80+
)
81+
def test_prepare_inference_gradients(forces, stress, pos_grad, cell_grad):
82+
backbone = SimpleNamespace(
83+
regress_config=SimpleNamespace(
84+
direct_forces=False, forces=forces, stress=stress
85+
)
86+
)
87+
data = {
88+
"pos": torch.randn(4, 3),
89+
"cell": torch.randn(1, 3, 3),
90+
}
91+
92+
_prepare_inference_gradients(backbone, data)
93+
94+
assert data["pos"].requires_grad is pos_grad
95+
assert data["cell"].requires_grad is cell_grad
96+
_prepare_inference_gradients(SimpleNamespace(), data)
97+
98+
7099
_REPRESENTATIVE_ELEMENTS = [
71100
(1, 0, 2), # H: charge=0, spin=2
72101
(6, 0, 3), # C: charge=0, spin=3

0 commit comments

Comments
 (0)