Source code for nvalchemi.distributed.output_kinds
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Output kind classification for distributed consolidation.
Each model output is declared on
:attr:`~nvalchemi.distributed.spec.MLIPSpec.output_kinds`, making
:class:`MLIPSpec` the single source of truth that
:mod:`nvalchemi.distributed.output_consolidation` reads directly
(rather than inferring per-atom vs per-system from tensor shapes).
Output classification combines two axes:
1. **Shape**: per-atom (one row per node, ``n_padded``-aligned) vs
per-system (one row per graph, ``n_systems``-aligned).
2. **Globalness**: each rank's value is a partial that needs combining
across the mesh, vs already-globally-correct.
:data:`PER_NODE` and :data:`PER_GRAPH` cover the shape axis; the
:attr:`MLIPSpec.owned_only_outputs` /
:attr:`MLIPSpec.all_reduce_outputs` sets cover the (orthogonal)
globalness axis. :data:`GLOBAL` is the convenience kind for outputs
that are already correct on every rank and pass through untouched
(rare; typically scalar metadata or replicated config tensors).
:data:`UNKNOWN` lets a wrapper omit declarations — the consolidation
falls back to the shape heuristic and logs a warning.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
__all__ = ["OutputKind", "OutputSpec", "Reduce"]
[docs]
class OutputKind(Enum):
"""Per-output classification used by consolidation.
See module docstring for the design rationale.
Members
-------
PER_NODE
One row per atom. Halo storage: ``shape[0] == n_padded`` (owned
+ halo rows). Sharded storage: ``shape[0] == n_owned`` (owned
only). Combine rule depends on
:attr:`MLIPSpec.owned_only_outputs` /
:attr:`MLIPSpec.all_reduce_outputs` membership and whether the
key is in :attr:`ModelConfig.autograd_outputs`.
PER_GRAPH
One row per system. ``shape[0] == n_systems``. Combine rule
depends on autograd / all_reduce membership.
GLOBAL
Already globally-correct on every rank; passthrough. Rare —
typically scalar metadata or replicated config tensors that
come out of the wrapper unchanged.
UNKNOWN
Undeclared default. Consolidation falls back to the shape-based
heuristic and logs a warning so the wrapper author knows to
declare. Also accepted for non-tensor output values (which
always pass through anyway).
"""
PER_NODE = "per_node"
PER_GRAPH = "per_graph"
GLOBAL = "global"
UNKNOWN = "unknown"
[docs]
class Reduce(Enum):
"""How an output's per-rank value is combined into the global value.
Passed inside :class:`OutputSpec`. Mirrors the three consolidation
branches in :mod:`~nvalchemi.distributed.output_consolidation`.
Members
-------
NONE
Default per-kind consolidation (e.g. an autograd per-node output is
halo-reverse-summed to owners; a per-graph output passes through).
ALL_REDUCE
Each rank holds a partial; sum across the mesh to the global value.
(Maps to ``MLIPSpec.all_reduce_outputs``.)
OWNED_ONLY
Already globally-correct on every rank; slice/passthrough, no
cross-rank reduce. (Maps to ``MLIPSpec.owned_only_outputs``.)
"""
NONE = "none"
ALL_REDUCE = "all_reduce"
OWNED_ONLY = "owned_only"
[docs]
@dataclass(frozen=True)
class OutputSpec:
"""How one named model output is shaped and combined under DD.
The single per-output declaration that collapses the three parallel sets
``output_kinds`` / ``all_reduce_outputs`` / ``owned_only_outputs`` into one
place::
outputs={"stress": OutputSpec(kind=OutputKind.PER_GRAPH,
reduce=Reduce.ALL_REDUCE)}
:class:`MLIPSpec` accepts ``outputs={name: OutputSpec}`` and lowers it onto
those legacy fields, so consolidation and serialization are unchanged.
"""
kind: OutputKind = OutputKind.UNKNOWN
reduce: Reduce = Reduce.NONE