Skip to content

Keep UMA atom and edge dimensions dynamic - #2127

Draft
mlazos wants to merge 6 commits into
gh/mlazos/5/basefrom
gh/mlazos/5/head
Draft

Keep UMA atom and edge dimensions dynamic#2127
mlazos wants to merge 6 commits into
gh/mlazos/5/basefrom
gh/mlazos/5/head

Conversation

@mlazos

@mlazos mlazos commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Stack from ghstack (oldest at bottom):

This PR remains targeted at torch.compile(dynamic=True) size sweeps. The current
final-stack benchmark deliberately sets compile_dynamic_shapes=False and uses
bounded static edge-capacity specializations with captured internal-v3 graph
generation. Consequently, this PR's dynamic-dimension marking is not active and
should not be attributed to the internal-graph result. The same PR also scoped
recompile_limit=32 and specialize_float=True with the _run_inference
decorator; that configuration scoping remains active in the final path.

Custom-operator boundaries do not expose enough shape relationships for Dynamo
to infer that every atom and edge axis should remain dynamic. For example, if
the first edge_index has shape [2, 156], Dynamo can guard the edge count as
156; a later [2, 2496] input invalidates that guard and recompiles.

Explicitly mark the atom axes and edge axes of graph tensors before the first
compiled call. edge_index dimension 1 and cell_offsets dimension 0 then
remain symbolic, as do dimension 0 of atom-indexed tensors. Scope
recompile_limit and float specialization with a Dynamo config decorator on
inference instead of leaking process-global assignments from lazy setup.

Activation

Dynamic atom and edge dimensions are enabled automatically for a compiled backend that advertises the fused edgewise path, including umas_fast_gpu:

settings = InferenceSettings(
    compile=True,
    execution_mode="umas_fast_gpu",
)

The predictor calls torch.compile(dynamic=True) for this configuration.

Test Plan:

PYTHONPATH=$PWD/src:$PYTHONPATH pytest -q tests/core/units/mlip_unit/test_predict.py -k test_mark_dynamic_input_dimensions_requires_fast_backend

The real checkpoint retained nine unique graphs when changing from two to four
atoms instead of compiling shape-specialized replacements.

Authored with assistance from Codex.

Custom operator boundaries caused Dynamo to specialize the first several edge counts despite dynamic compilation. Mark UMA atom and edge dimensions explicitly and scope inference-only Dynamo configuration to the inference call so one compiled predictor can serve changing system sizes.

Test Plan:
```
TORCHINDUCTOR_CACHE_DIR=$PWD/../dynamic_branch_cache PYTHONPATH=$PWD/src:$PWD/..:$PYTHONPATH python <two-size uma-s-1p2 no-recompile check>
ruff check src/fairchem/core/units/mlip_unit/predict.py
```

The real checkpoint retained 9 unique graphs when changing from 2 to 4 atoms.

Authored with assistance from Codex.

[ghstack-poisoned]
mlazos added 5 commits July 31, 2026 12:01
Target configuration: uma-s-1p2 with `torch.compile(dynamic=True)`,
`external_graph_gen=True`, `merge_mole=True`, and `umas_fast_gpu`, reusing one
predictor across the 2-to-2048-atom size sweep. This issue is active in that
target sweep, but causes guard failures and recompilation rather than a Dynamo
graph break in a single fixed-size invocation.

Custom-operator boundaries do not expose enough shape relationships for Dynamo
to infer that every atom and edge axis should remain dynamic. For example, if
the first `edge_index` has shape `[2, 156]`, Dynamo can guard the edge count as
156; a later `[2, 2496]` input invalidates that guard and recompiles.

Explicitly mark the atom axes and edge axes of graph tensors before the first
compiled call. `edge_index` dimension 1 and `cell_offsets` dimension 0 then
remain symbolic, as do dimension 0 of atom-indexed tensors. Scope
`recompile_limit` and float specialization with a Dynamo config decorator on
inference instead of leaking process-global assignments from lazy setup.

Test Plan:
```
TORCHINDUCTOR_CACHE_DIR=$PWD/../dynamic_branch_cache PYTHONPATH=$PWD/src:$PWD/..:$PYTHONPATH python <two-size uma-s-1p2 no-recompile check>
ruff check src/fairchem/core/units/mlip_unit/predict.py
```

The real checkpoint retained nine unique graphs when changing from two to four
atoms instead of compiling shape-specialized replacements.

Authored with assistance from Codex.

[ghstack-poisoned]
Target configuration: uma-s-1p2 with `torch.compile(dynamic=True)`,
`external_graph_gen=True`, `merge_mole=True`, and `umas_fast_gpu`, reusing one
predictor across the 2-to-2048-atom size sweep. This issue is active in that
target sweep, but causes guard failures and recompilation rather than a Dynamo
graph break in a single fixed-size invocation.

Custom-operator boundaries do not expose enough shape relationships for Dynamo
to infer that every atom and edge axis should remain dynamic. For example, if
the first `edge_index` has shape `[2, 156]`, Dynamo can guard the edge count as
156; a later `[2, 2496]` input invalidates that guard and recompiles.

Explicitly mark the atom axes and edge axes of graph tensors before the first
compiled call. `edge_index` dimension 1 and `cell_offsets` dimension 0 then
remain symbolic, as do dimension 0 of atom-indexed tensors. Scope
`recompile_limit` and float specialization with a Dynamo config decorator on
inference instead of leaking process-global assignments from lazy setup.

**Activation**

Dynamic atom and edge dimensions are enabled automatically for a compiled backend that advertises the fused edgewise path, including `umas_fast_gpu`:

```python
settings = InferenceSettings(
    compile=True,
    execution_mode="umas_fast_gpu",
)
```

The predictor calls `torch.compile(dynamic=True)` unless fixed-shape `full_cudagraph=True` is selected. With `full_cudagraph=True`, set `full_cudagraph_dynamic=True` to retain dynamic compilation and use the per-shape CUDA graph cache.

Test Plan:
```
TORCHINDUCTOR_CACHE_DIR=$PWD/../dynamic_branch_cache PYTHONPATH=$PWD/src:$PWD/..:$PYTHONPATH python <two-size uma-s-1p2 no-recompile check>
ruff check src/fairchem/core/units/mlip_unit/predict.py
```

The real checkpoint retained nine unique graphs when changing from two to four
atoms instead of compiling shape-specialized replacements.

Authored with assistance from Codex.

[ghstack-poisoned]
Target configuration: uma-s-1p2 with `torch.compile(dynamic=True)`,
`external_graph_gen=True`, `merge_mole=True`, and `umas_fast_gpu`, reusing one
predictor across the 2-to-2048-atom size sweep. Changing atom or edge counts in that target sweep causes guard failures
and recompilation rather than a Dynamo
graph break in a single fixed-size invocation.

Custom-operator boundaries do not expose enough shape relationships for Dynamo
to infer that every atom and edge axis should remain dynamic. For example, if
the first `edge_index` has shape `[2, 156]`, Dynamo can guard the edge count as
156; a later `[2, 2496]` input invalidates that guard and recompiles.

Explicitly mark the atom axes and edge axes of graph tensors before the first
compiled call. `edge_index` dimension 1 and `cell_offsets` dimension 0 then
remain symbolic, as do dimension 0 of atom-indexed tensors. Scope
`recompile_limit` and float specialization with a Dynamo config decorator on
inference instead of leaking process-global assignments from lazy setup.

**Activation**

Dynamic atom and edge dimensions are enabled automatically for a compiled backend that advertises the fused edgewise path, including `umas_fast_gpu`:

```python
settings = InferenceSettings(
    compile=True,
    execution_mode="umas_fast_gpu",
)
```

The predictor calls `torch.compile(dynamic=True)` unless fixed-shape `full_cudagraph=True` is selected. With `full_cudagraph=True`, set `full_cudagraph_dynamic=True` to retain dynamic compilation and use the per-shape CUDA graph cache.

Test Plan:
```
TORCHINDUCTOR_CACHE_DIR=$PWD/../dynamic_branch_cache PYTHONPATH=$PWD/src:$PWD/..:$PYTHONPATH python <two-size uma-s-1p2 no-recompile check>
ruff check src/fairchem/core/units/mlip_unit/predict.py
```

The real checkpoint retained nine unique graphs when changing from two to four
atoms instead of compiling shape-specialized replacements.

Authored with assistance from Codex.

[ghstack-poisoned]
Target configuration: uma-s-1p2 with `torch.compile(dynamic=True)`,
`external_graph_gen=True`, `merge_mole=True`, and `umas_fast_gpu`, reusing one
predictor across the 2-to-2048-atom size sweep. Changing atom or edge counts in that target sweep causes guard failures
and recompilation rather than a Dynamo
graph break in a single fixed-size invocation.

Custom-operator boundaries do not expose enough shape relationships for Dynamo
to infer that every atom and edge axis should remain dynamic. For example, if
the first `edge_index` has shape `[2, 156]`, Dynamo can guard the edge count as
156; a later `[2, 2496]` input invalidates that guard and recompiles.

Explicitly mark the atom axes and edge axes of graph tensors before the first
compiled call. `edge_index` dimension 1 and `cell_offsets` dimension 0 then
remain symbolic, as do dimension 0 of atom-indexed tensors. Scope
`recompile_limit` and float specialization with a Dynamo config decorator on
inference instead of leaking process-global assignments from lazy setup.

**Activation**

Dynamic atom and edge dimensions are enabled automatically for a compiled backend that advertises the fused edgewise path, including `umas_fast_gpu`:

```python
settings = InferenceSettings(
    compile=True,
    execution_mode="umas_fast_gpu",
)
```

The predictor calls `torch.compile(dynamic=True)` unless fixed-shape `full_cudagraph=True` is selected. With `full_cudagraph=True`, set `full_cudagraph_dynamic=True` to retain dynamic compilation and use the per-shape CUDA graph cache.

Test Plan:
```
TORCHINDUCTOR_CACHE_DIR=$PWD/../dynamic_branch_cache PYTHONPATH=$PWD/src:$PWD/..:$PYTHONPATH python <two-size uma-s-1p2 no-recompile check>
ruff check src/fairchem/core/units/mlip_unit/predict.py
```

The real checkpoint retained nine unique graphs when changing from two to four
atoms instead of compiling shape-specialized replacements.

Authored with assistance from Codex.

[ghstack-poisoned]
Target configuration: uma-s-1p2 with `torch.compile(dynamic=True)`,
`external_graph_gen=True`, `merge_mole=True`, and `umas_fast_gpu`, reusing one
predictor across the 2-to-2048-atom size sweep. Changing atom or edge counts in that target sweep causes guard failures
and recompilation rather than a Dynamo
graph break in a single fixed-size invocation.

Custom-operator boundaries do not expose enough shape relationships for Dynamo
to infer that every atom and edge axis should remain dynamic. For example, if
the first `edge_index` has shape `[2, 156]`, Dynamo can guard the edge count as
156; a later `[2, 2496]` input invalidates that guard and recompiles.

Explicitly mark the atom axes and edge axes of graph tensors before the first
compiled call. `edge_index` dimension 1 and `cell_offsets` dimension 0 then
remain symbolic, as do dimension 0 of atom-indexed tensors. Scope
`recompile_limit` and float specialization with a Dynamo config decorator on
inference instead of leaking process-global assignments from lazy setup.

**Activation**

Dynamic atom and edge dimensions are enabled automatically for a compiled backend that advertises the fused edgewise path, including `umas_fast_gpu`:

```python
settings = InferenceSettings(
    compile=True,
    execution_mode="umas_fast_gpu",
)
```

The predictor calls `torch.compile(dynamic=True)` for this configuration.

Test Plan:
```
PYTHONPATH=$PWD/src:$PYTHONPATH pytest -q tests/core/units/mlip_unit/test_predict.py -k test_mark_dynamic_input_dimensions_requires_fast_backend
```

The real checkpoint retained nine unique graphs when changing from two to four
atoms instead of compiling shape-specialized replacements.

Authored with assistance from Codex.

[ghstack-poisoned]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant