-
Notifications
You must be signed in to change notification settings - Fork 639
feat(pt_expt): auto-select O(N) NeighborGraph builder by default #5903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
01253d9
00facb6
1838368
804d663
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,47 @@ | |
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def resolve_auto_graph_builder( | ||
| device: torch.device | str, | ||
| ) -> str: | ||
| """Resolve ``neighbor_graph_method="auto"`` to a concrete inference builder. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing the numpydoc sections the rest of this module uses. This is a public function -- no leading underscore, imported by
|
||
|
|
||
| Single owner of the inference / DeepEval auto ladder (training uses | ||
| :func:`resolve_neighbor_graph_method`, which keeps CPU on ``dense`` because | ||
| vesin loops frames in Python and is not safe as a multi-frame training | ||
| default): | ||
|
|
||
| * CUDA: ``nv`` if ``nvalchemiops`` is importable, else ``vesin`` if | ||
| ``vesin.torch`` is importable, else ``dense``. | ||
| * CPU: ``vesin`` if ``vesin.torch`` is importable, else ``dense``. | ||
|
|
||
| ``ase`` is never chosen automatically. All builders emit the same carry-all | ||
| neighbor set; the choice is performance-only. Builders run eagerly outside | ||
| traced / compiled regions, so this does not change ``.pt2`` artifacts. | ||
| """ | ||
| from deepmd.pt.utils.nv_nlist import ( | ||
| is_nv_available, | ||
| ) | ||
| from deepmd.pt_expt.utils.vesin_neighbor_list import ( | ||
| is_vesin_torch_available, | ||
| ) | ||
|
|
||
| dev = torch.device(device) | ||
| if dev.type == "cuda": | ||
| if is_nv_available(): | ||
| return "nv" | ||
| if is_vesin_torch_available(): | ||
| return "vesin" | ||
| log.warning( | ||
| "nvalchemi-toolkit-ops and vesin[torch] are unavailable; falling " | ||
| "back from neighbor_graph_method='auto' to the dense graph builder." | ||
| ) | ||
| return "dense" | ||
| if is_vesin_torch_available(): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the change the PR really makes, and it re-adds what #5912 removed. On #5912 I raised this against an identical ladder:
That rung was dropped and #5912 merged without it. This commit restores it and takes the second half of that either/or -- the module docstring is rewritten -- but not the first: there is no The reason I do not think the docstring rewrite settles it is that the repository already has an answer to this exact question, and it goes the other way. if device.type == "cpu" and nf == 1 and is_vesin_torch_available():
return VesinNeighborList()with the rationale stated directly above it: "Every other case -- any CUDA input or any multi-frame batch -- uses Three things make the exposure wider than it looks. What would resolve it, in order of preference: move the resolution to call time and gate on |
||
| return "vesin" | ||
| return "dense" | ||
|
|
||
|
|
||
| def resolve_neighbor_graph_method( | ||
| requested: str, | ||
| device: torch.device, | ||
|
|
@@ -36,6 +77,8 @@ def resolve_neighbor_graph_method( | |
| ------- | ||
| str | ||
| The concrete builder name, either ``"dense"`` or ``"nv"``. | ||
| Training auto never selects ``vesin`` (per-frame Python loop); use | ||
| :func:`resolve_auto_graph_builder` for inference auto selection. | ||
|
|
||
| Raises | ||
| ------ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -138,6 +138,33 @@ def test_explicit_nv_rejects_cpu(): | |
| resolve_neighbor_graph_method("nv", torch.device("cpu")) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These five cases pin the ladder, and I want to say they are the right shape -- patching availability and device rather than recomputing the cascade in the assertion is exactly what was missing from the equivalent test on #5912. The gap is that they are the only new coverage, and they are pure resolver assertions: they never build a graph. The cross-builder numerical checks that do build one, coord = torch.tensor(rng.random((1, 6, 3)) * 4.0, ...)So parity between vesin and dense is established only at A parametrization of |
||
| ("device", "nv", "vesin", "expected"), | ||
| [ | ||
| ("cpu", False, True, "vesin"), | ||
| ("cpu", True, False, "dense"), | ||
| ("cuda", True, True, "nv"), | ||
| ("cuda", False, True, "vesin"), | ||
| ("cuda", False, False, "dense"), | ||
| ], | ||
| ) | ||
| def test_resolve_auto_graph_builder_ladder( | ||
| device: str, nv: bool, vesin: bool, expected: str | ||
| ) -> None: | ||
| from deepmd.pt_expt.utils.graph_builder import ( | ||
| resolve_auto_graph_builder, | ||
| ) | ||
|
|
||
| with ( | ||
| patch("deepmd.pt.utils.nv_nlist.is_nv_available", return_value=nv), | ||
| patch( | ||
| "deepmd.pt_expt.utils.vesin_neighbor_list.is_vesin_torch_available", | ||
| return_value=vesin, | ||
| ), | ||
| ): | ||
| assert resolve_auto_graph_builder(device) == expected | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not is_vesin_torch_available(), reason="vesin[torch] not installed") | ||
| def test_vesin_matches_dense_energy_force(): | ||
| torch.manual_seed(0) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The summary describes two changes that are not in this diff.
It says the PR will "Flip the pt_expt model-level default (
None/"auto") from hard-coded"dense"to that ladder" and "use the same helper in ... compiled training's eager_forward_graph". Neitherdeepmd/pt_expt/model/make_model.pynordeepmd/pt_expt/train/training.pyappears in the changed files, and both still resolve the same way they did before:make_model.py_resolve_graph_methodstill ends atgetattr(self, "neighbor_graph_method", "dense"), so a model driven directly still defaults todenseon every device;training.pystill imports onlyresolve_neighbor_graph_method, and_forward_graphstill readsgetattr(_model, "neighbor_graph_method", "dense").build_neighbor_graph_for_methodalso has no"auto"branch -- it raisesValueErroron anything it does not recognise -- so"auto"could not reach it even if the model default did produce it.I think this is stale text rather than a missing change: those two flips landed in #5912 and #5913, which are already ancestors of this branch. The "Why existing tests missed this" paragraph has the same problem, since it describes fixing compiled training's hardcoded dense. Worth rewriting the body to what the commit does -- add vesin to the inference auto ladder and extract the shared helper -- because as written a reviewer would look for a model-level behaviour change that is not here, and a bisect later would be misled about where the flip came from.