nvalchemi.dynamics.FusedStage#
- class nvalchemi.dynamics.FusedStage(sub_stages, *, entry_status=0, exit_status=-1, compile_step=False, compile_kwargs=None, init_fn=None, **kwargs)[source]#
Composite dynamics engine fusing multiple sub-stages on a single GPU.
FusedStagecomposes multipleBaseDynamicssub-stages to share oneBatchand one model forward pass per step, avoiding redundant forward passes when multiple simulation phases (e.g., relaxation then MD) operate on the same batch.Unlike
BaseDynamics, ``step(batch)`` is overridden. Instead of the standardpre_update → compute → post_updateloop,FusedStageperforms: (1) a singlecompute()call on the full batch, then (2) iterates over sub-stages, applyingmasked_update(batch, mask)on each sub-stage’s dynamics for samples whosebatch.statusmatches that sub-stage’s status code. Only ONE forward pass happens per step regardless of the number of sub-stages. ``run(batch)`` is also overridden — then_stepsattribute (inherited fromBaseDynamics) and anyn_stepsargument passed torun()are both the maximum number of steps; the loop runs until all samples have migrated to theexit_status, the sampler is exhausted, orn_stepsis reached. Convergence-driven migration is handled byConvergenceHookinstances auto-registered between adjacent sub-stages: when samples converge in sub-stage i, theirbatch.statusis updated to sub-stage i+1’s code, causing them to be processed by the next dynamics on the following step. The+operator composes sub-stages:dyn_a + dyn_bcreates aFusedStage, andfused + dyn_cappends a third sub-stage. The|operator (inherited fromBaseDynamicsvia_CommunicationMixin) creates aDistributedPipelinefor multi-rank execution instead.Developers generally do NOT subclass
FusedStage. Instead, createBaseDynamicssubclasses (integrators) and compose them using+.FusedStagehandles orchestration automatically. The key requirement is that sub-stage dynamics must implementmasked_updatecorrectly (inherited fromBaseDynamics) and that the batch must have astatustensor.Hook Firing Semantics#
Because
FusedStageshares a single forward pass across all sub-stages, hook firing differs from standaloneBaseDynamicsexecution. The following hooks fire on each sub-stage during_step_impl:Fired on sub-stages (in order):
BEFORE_STEP— at the start of each fused step, before any work.AFTER_COMPUTE— after the shared model forward pass completes.BEFORE_PRE_UPDATE— before each sub-stage’smasked_update(fires even when no samples match the sub-stage’s status code).AFTER_POST_UPDATE— after each sub-stage’smasked_update(fires even when no samples match the sub-stage’s status code).AFTER_STEP— after all masked updates are complete.ON_CONVERGE— when a sub-stage’s_check_convergencedetects converged samples.
NOT fired on sub-stages:
BEFORE_COMPUTE— the forward pass is shared across all sub-stages, not executed per-sub-stage; there is no meaningful “before compute” point for individual sub-stages.AFTER_PRE_UPDATE—masked_updatecombinespre_updateandpost_updateatomically; there is no intermediate hook point.BEFORE_POST_UPDATE— same reason asAFTER_PRE_UPDATE.
Step count semantics: Each sub-stage’s
step_countis incremented alongside theFusedStage’s ownstep_countafter every fused step, ensuring that hook frequency (e.g.,every_n_steps) is respected correctly across all sub-stages.- param sub_stages:
Ordered
(status_code, dynamics)pairs. Status codes are auto-assigned starting from 0 when using the+operator.- type sub_stages:
list[tuple[int, BaseDynamics]]
- param entry_status:
Status code assigned to incoming samples (default: 0).
- type entry_status:
int
- param exit_status:
Status code that triggers graduation to the next pipeline stage. Auto-set to
len(sub_stages)(one past the last sub-stage code).- type exit_status:
int
- param compile_step:
If
True, replaceself.stepwithtorch.compile(self.step, **compile_kwargs).- type compile_step:
bool
- param compile_kwargs:
Keyword arguments forwarded to
torch.compile.- type compile_kwargs:
dict
- param **kwargs:
Additional keyword arguments forwarded to
BaseDynamics.
- sub_stages#
Ordered
(status_code, dynamics)pairs.- Type:
list[tuple[int, BaseDynamics]]
- entry_status#
Status code for incoming samples.
- Type:
int
- exit_status#
Status code that triggers graduation.
- Type:
int
- compile_step#
Whether the step method is compiled.
- Type:
bool
- compile_kwargs#
Arguments passed to
torch.compile.- Type:
dict
- __needs_keys__#
Union of all sub-stage
__needs_keys__sets. Populated automatically during__init__.- Type:
set[str]
- __provides_keys__#
Union of all sub-stage
__provides_keys__sets. Populated automatically during__init__.- Type:
set[str]
Examples
>>> from nvalchemi.dynamics import FusedStage, BaseDynamics >>> dynamics0 = BaseDynamics(model=model) >>> dynamics1 = BaseDynamics(model=model) >>> fused = FusedStage(sub_stages=[(0, dynamics0), (1, dynamics1)]) >>> fused.exit_status 2
- static all_complete(batch, exit_status)[source]#
Check if all samples have reached the exit status.
- Parameters:
batch (Batch) – The current batch.
exit_status (int) – The status code that indicates completion.
- Returns:
Trueif every sample hasstatus == exit_status.- Return type:
bool
- compile(**kwargs)[source]#
Compile the fused step with
torch.compile.Merges kwargs with any
compile_kwargsstored at construction time (values passed here take precedence), then wraps_step_implwithtorch.compile. Calling this method also setscompile_step = Trueso that thestepdispatch path uses the compiled callable.This method is idempotent in intent but will re-compile if called again (e.g. with different kwargs).
- Parameters:
**kwargs (Any) – Keyword arguments forwarded to
torch.compile. Merged withcompile_kwargsfrom__init__; values here win.- Returns:
This instance, enabling fluent chaining such as
fused.compile(fullgraph=True).run(batch).- Return type:
- refill_check(batch, exit_status)[source]#
Replace graduated samples and clear stale convergence indices.
Delegates to the parent
BaseDynamics.refill_check()to remove graduated graphs and append replacements from the sampler. When the batch composition changes,_last_convergedis cleared on thisFusedStageand all its sub-stages so that subsequent hooks do not receive an invalidconverged_mask.
- register_fused_hook(hook)[source]#
Register a hook that fires at the FusedStage level on the full batch.
Unlike hooks registered on individual sub-stages (which only receive the sub-batched view), fused hooks observe the complete batch at the
BEFORE_STEP,AFTER_STEP,BEFORE_COMPUTE, andAFTER_COMPUTEstages of every fused step.- Parameters:
hook (Hook) – The hook to register. Only
BEFORE_STEPandAFTER_STEPstages are meaningful at the fused level; other stages are silently accepted but will not fire during normal execution.- Raises:
ValueError – If
hook.frequencyis not a positive integer.- Return type:
None
- run(batch=None, n_steps=None)[source]#
Run the fused stage until all samples converge or the sampler is exhausted.
Supports two modes of execution:
Mode 1 (external batch loop): When
batchis provided, runs the dynamics untilall_completeor untiln_stepshave been executed (whichever comes first).Mode 2 (inflight batching): When
batch is Noneand a sampler is configured, builds the initial batch from the sampler and replaces graduated samples everyrefill_frequencysteps.Note
In Mode 2,
refill_checkreplaces graduated samples by extracting remaining graphs viaBatch.index_select(), requesting replacements from the sampler, and appending them viaBatch.append(). This produces a newBatchobject; thebatch = resultreassignment in the loop body updates the local reference.Noneis returned when the sampler is exhausted and no active samples remain, which triggers termination.- Parameters:
batch (Batch | None, optional) – The initial batch. If
None, uses the sampler to build one.n_steps (int | None, optional) – Maximum number of steps to run. When
None, falls back toself.n_steps. When both areNone, the loop runs untilall_complete(Mode 1) or sampler exhaustion (Mode 2). Sub-stages that have no exit criterion (e.g. a plain MD stage) will loop forever without a step limit, so always passn_stepswhen such a stage is the final sub-stage. Note: sub-stages withn_stepsset use that value as a per-system step budget for automatic migration to the next stage.
- Returns:
The batch after all steps, or
Noneif the sampler was exhausted and all samples graduated.- Return type:
Batch | None
- Raises:
ValueError – If
batch is Noneand no sampler is configured.
- step(batch)[source]#
Execute one fused step: single forward pass + masked updates.
If
compile_step=Truewas set, this delegates to the compiled step implementation.- Parameters:
batch (Batch) – The batch with a
statusfield.- Returns:
The updated batch, and a 1-D integer tensor of sample indices that newly graduated (reached
exit_status) during this step, orNoneif no samples graduated.- Return type:
tuple[Batch, torch.Tensor | None]
- Parameters:
sub_stages (list[tuple[int, BaseDynamics]])
entry_status (int)
exit_status (int)
compile_step (bool)
compile_kwargs (dict[str, Any] | None)
init_fn (Callable[[Batch], None] | None)
kwargs (Any)