neml2.es¶
Python-native equation-system assembly for implicit updates.
Layered:
_helpers– raw-tensor helpers shared across the layer.axis_layout–AxisLayout.assembled–AssembledVector,AssembledMatrix,norm()/norm_sq()(forwarded toTensor).sparse–SparseVector,SparseMatrix(typed per-variable duals of the assembled forms; the symmetric API boundary surface).system–LinearSystem,NonlinearSystem,ModelNonlinearSystem.implicit– AOTI export wrappers (RHS,NewtonStep,IFT) for the implicit-segment Newton path.
- class neml2.es.AssembledMatrix(row_layout, col_layout, tensors=<factory>)[source]¶
Bases:
object2D grid of per-(row_group, col_group) tensor blocks.
Per-block storage decision:
both row group AND col group dense -> shape
(*dyn, row_storage_with_sub_folded, col_storage_with_sub_folded);base_ndim=2,sub_batch_ndim=0.any side block -> that side’s sub_batch is preserved as intmd dims. Shape
(*dyn, *intmd_row, *intmd_col, row_storage, col_storage);base_ndim=2,sub_batch_ndim = len(intmd_row) + len(intmd_col).
matmul:
mm(aik, bkj)contracts the trailing two base axes.if
col_layout.structure[k] == "block"(the inner group is block), a sub_batch reduction (sumover the intmd dims contributed by group k) runs after the per-block mm. This is the v2-parity “block intmd_sum after mm” pattern.
- Parameters:
row_layout (AxisLayout)
col_layout (AxisLayout)
- col_layout: AxisLayout¶
- disassemble()[source]¶
Unpack per-block tensors into a
SparseMatrix.Each cell
cells[row_var][col_var]is a typed dynamic-baseTensorthat carries the block’sbatch_ndim/sub_batch_ndimverbatim. For"dense"blocks the sub_batch axes have been folded into base, so the slices are flat in row + col. Boundary callers (e.g. the pyzag interface) read the underlying dict-of-dict via.cellsand unwrap to raw.dataat their own framework boundary.- Return type:
- row_layout: AxisLayout¶
- static select_blocks(row_layout, col_layout, blocks)[source]¶
Build an
AssembledMatrixfrom per-(row_var, col_var) typed blocks.Inverse of
disassemble(). Eachblocks[row_name][col_name]is a typed dynamic-baseTensorwithbase_ndim=2andbase_shape == (row_storage, col_storage)sized to match_var_storage()for the given layout structure. Missing (row_var, col_var) pairs are zero-filled.Only the dense-x-dense case is supported (no intmd sub_batch dims on either side) – that’s the only path the pyzag interface and the round-trip test currently exercise. Block-structure layouts would need the corresponding sub_batch axes to live in each block’s storage and the cat axes shifted accordingly; raise so callers don’t silently get the wrong shape.
- Parameters:
row_layout (AxisLayout)
col_layout (AxisLayout)
- Return type:
- class neml2.es.AssembledVector(layout, tensors)[source]¶
Bases:
objectPer-group dense vector blocks.
Each group’s tensor follows the
SubBatchStructureof the owningAxisLayout:"block"->(*dyn, *sub_batch, group_storage_size),base_ndim=1,sub_batch_ndim=len(sub_batch_shape)."dense"->(*dyn, group_storage_size_with_sub_folded),base_ndim=1,sub_batch_ndim=0.
Arithmetic and dot products forward to
Tensorops.- Parameters:
layout (AxisLayout)
- disassemble()[source]¶
Unpack per-group tensors back into a
SparseVector.Inverse of
from_dict()– the per-group split point is the per-variable storage size; for"dense"groups the sub_batch is unfolded back to the declared shape before re-typing.Returns a
SparseVector(the typed dual of this object). The underlying name -> typed-wrapper dict is on.valuesfor callers that need the raw mapping.- Return type:
- classmethod from_dict(layout, values)[source]¶
Pack typed-wrapper
values(one per variable) into per-group tensors.For a
"block"group, the per-variable wrappers are flattened to base then concatenated along base (last) axis – sub_batch axes stay as intermediate dims on the resulting Tensor.For a
"dense"group, each wrapper’s sub_batch is folded INTO the variable’s flat base storage before concat: per-variable contribution becomes(*dyn, sub_total * base_size), then all contributions concat to(*dyn, sum_var (sub_total * base_size)).Per CLAUDE.md rule 1:
valuesis strictly typed – rawtorch.Tensoris rejected. External boundaries wrap with the appropriateTensorWrappersubclass at the construction site.- Parameters:
layout (AxisLayout)
values (Mapping[str, TensorWrapper])
- Return type:
- layout: AxisLayout¶
- class neml2.es.AxisLayout(groups, specs, sub_batch_shapes=None, structure=None)[source]¶
Bases:
objectOrdered variable groups, their tensor types, per-variable sub-batch shapes, and per-group
SubBatchStructure.- Parameters:
- group_sub_batch_shape(index)[source]¶
Common sub-batch shape across every variable in group
index.For a BLOCK group every variable must share the same sub_batch_shape (otherwise the block tensor can’t be a single rectangular tensor). Raises on disagreement. For a DENSE group the per-variable shapes may differ — they get folded into base on assembly — and this method returns the FIRST variable’s shape for shape inference only.
- specs: dict[str, type[TensorWrapper]]¶
- sub_batch_shape(name)[source]¶
Per-variable sub-batch shape (empty when the var is sub-batch-trivial).
- sub_layout(index)[source]¶
Single-group sub-layout containing only
self.groups[index].- Parameters:
index (int)
- Return type:
- class neml2.es.IFT(system, linear_solver, selected_pairs=None)[source]¶
Bases:
_SystemModuleExportable IFT Jacobian $du/dg = -A^{-1} B$ for a converged
ImplicitUpdate.Contract:
(*u_groups, *g_groups) -> *blockswhere each block is one per-variable-pair(unknown, given)entry of-du_dg, emitted inunknown_names(outer) ×given_names(inner) order – matching thejacobian_pairsmetadata in_compile_implicit_segment().The equation system assembles the dense
A/B(via the model’s per-variable chain rule), applies the IFT solve, thendisassemble()s the resultingAssembledMatrixinto per-(unknown, given) blocks. Each block keeps its natural per-structure shape ("dense"->(*B, u_storage, g_storage); a BLOCK side stays block-diagonal-compact, no N² fold). The C++ runtime then composes these blocks againstdg_dmasterexactly like a forward segment’s per-pair Jacobian blocks – one uniform per-pair path for forward and implicit.- Parameters:
system (ModelNonlinearSystem)
- forward(*args)[source]¶
Define the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Moduleinstance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
- class neml2.es.LinearSystem[source]¶
Bases:
objectBase class for systems with assembled operators.
- SECTION = 'EquationSystems'¶
HIT section for
neml2-syntaxclassification – inherited by every registered subclass (ModelNonlinearSystemlives under[EquationSystems]in the input file).
- assemble(need_A, need_B, need_b)[source]¶
- Parameters:
- Return type:
tuple[AssembledMatrix | None, AssembledMatrix | None, AssembledVector | None]
- property blayout: AxisLayout¶
- property glayout: AxisLayout¶
- set_g(g)[source]¶
- Parameters:
g (AssembledVector | SparseVector)
- Return type:
None
- set_u(u)[source]¶
- Parameters:
u (AssembledVector | SparseVector)
- Return type:
None
- property ulayout: AxisLayout¶
- class neml2.es.ModelNonlinearSystem(model, unknowns, residuals=None, structure=None)[source]¶
Bases:
NonlinearSystemA nonlinear system defined by a Model.
- Parameters:
- assemble(need_A, need_B, need_b)[source]¶
- Parameters:
- Return type:
tuple[AssembledMatrix | None, AssembledMatrix | None, AssembledVector | None]
- classmethod from_hit(node, factory)[source]¶
- Parameters:
node (nmhit.Node)
factory (_NativeInputFile)
- Return type:
- hit = <neml2.schema.HitSchema object>¶
- initialize(*, u, g, dyn_shape=())[source]¶
Set the state and per-variable layout from typed
SparseVectorinputs.uandgcarry their ownAxisLayout, which pins each variable’ssub_batch_shape. The system trusts these layouts directly – no separatesub_batch_ndimdict is needed because the wrappers + layout already encode the same information consistently.Each
SparseVector’svaluesmay pass typed wrappers (preferred per the wrapper-discipline rule) or rawtorch.Tensor(auto-wrapped with the input_spec’s type for caller convenience at construction sites that haven’t migrated yet – these wrap-on-entry, not wrap-on-exit, so no metadata is lost).Call sites that have raw dicts + a
sub_batch_ndimcount dict should funnel throughto_sparse()to construct the typedSparseVectorpair.- Parameters:
u (SparseVector)
g (SparseVector)
- Return type:
None
- set_g(g)[source]¶
- Parameters:
g (AssembledVector | SparseVector)
- Return type:
None
- set_u(u)[source]¶
- Parameters:
u (AssembledVector | SparseVector)
- Return type:
None
- set_u_from_group_raws(u_raws)[source]¶
Commit per-group raw unknown tensors (the solver boundary).
Inverse of
_vector_to_per_group_raws(self.u()): each per-group raw is re-typed viawrap_group_raw()using the unknown layout’s structure, then committed throughset_u(). Used by the C++-backed Newton solver to write the converged iterate back into the system state.
- to(*args, **kwargs)[source]¶
Move the underlying
Modeland any populated state to a new device / dtype.Matches torch’s
nn.Module.tosignature and convention: forwards*args/**kwargstoself.model.to(...)(coveringto(device='cuda'),to(dtype=torch.float32),to('cuda', non_blocking=True)etc.) and additionally walksself._state– the per-variable typed wrappers populated byinitialize()– moving each one throughTensorWrapper.to(). Returnsselfso call chains likesystem = neml2.load_nonlinear_system(...).to('cuda')work the same way they do fornn.Module.ModelNonlinearSystemis intentionally not annn.Module(it composes one rather than being one), so torch’sModule.tosemantics don’t reach the system automatically – this method is the bridge.- Return type:
- to_sparse(u, g, sub_batch_ndim=None)[source]¶
Convert typed
(u_dict, g_dict, sub_batch_ndim)into(u_sv, g_sv)SparseVectorpair ready to pass toinitialize().Use this helper at internal call sites that already build per-variable typed-wrapper dicts + a per-variable
sub_batch_ndimcount dict (the classicImplicitUpdate/ pyzag adapter shape). Per-variablesub_batch_shapesare derived from each wrapper’s trailing batch dims so the resultingSparseVectorlayouts encode the same sub-batch structure that the wrappers carry.Per CLAUDE.md rule 1, raw
torch.Tensorinputs are rejected – wrap at the boundary first. For user-facing surfaces with no sub-batch (the typical test / notebook shape), constructSparseVector(system.ulayout, {name: typed_wrapper, ...})directly instead – no helper needed.- Parameters:
u (Mapping[str, TensorWrapper])
g (Mapping[str, TensorWrapper])
- Return type:
- class neml2.es.NewtonStep(system, linear_solver)[source]¶
Bases:
_SystemModuleExportable Newton step-direction graph.
Contract:
(*u_groups, *g_groups) -> (*du_groups, *b_groups)wheredu_groupsare the per-unknown-group step directions (inulayout.groupsorder) andb_groupsare the per-residual-groupb = -r(u)at the current iterate (inblayout.groupsorder). The C++ runtime appliesu_groups[i] = u_groups[i] + alpha * du_groups[i]per-group for line-search trials via cheapRHSevaluations.- Parameters:
system (ModelNonlinearSystem)
- forward(*args)[source]¶
Define the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Moduleinstance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
- class neml2.es.NonlinearSystem[source]¶
Bases:
LinearSystemNonlinear system with C++-matching Newton sign convention.
- class neml2.es.RHS(system)[source]¶
Bases:
_SystemModuleExportable residual graph.
Contract:
(*u_groups, *g_groups) -> (*b_groups)– per-group raw tensors.b_group = -r_groupfor each residual group; the C++ runtime computes a per-batch convergence norm by reducing each group tensor over its trailing sub_batch + base axes and summing across groups (no per-variable narrow on the hot path).- Parameters:
system (ModelNonlinearSystem)
- forward(*args)[source]¶
Define the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Moduleinstance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
- class neml2.es.SparseMatrix(row_layout, col_layout, cells)[source]¶
Bases:
objectPer-(row_var, col_var) cell map; the typed dual of
AssembledMatrix.cells[row_var][col_var]is the assembled per-cellTensorblock thatAssembledMatrix.tensors[i][j]holds – K has already been folded into trailing base via_tangent_block_to_trailing_k(). Construction validates that the outer keys cover every row variable inrow_layout; missing inner(row_var, col_var)entries are allowed and become zero blocks at assembly time (per-block sparsity is normal in chain-rule derivatives).- Parameters:
row_layout (AxisLayout)
col_layout (AxisLayout)
- assemble()[source]¶
Walk row x col groups and pack into an
AssembledMatrix.Delegates to
AssembledMatrix.select_blocks(), which handles per-block sparsity (missing entries -> zero blocks).- Return type:
- col_layout: AxisLayout¶
- row_layout: AxisLayout¶
- class neml2.es.SparseVector(layout, values)[source]¶
Bases:
objectPer-variable typed-wrapper vector; the typed dual of
AssembledVector.valuesmaps each variable name listed inlayout.vars()to its typed value (aTensorWrappersubclass instance). Construction validates that every layout variable is covered.Per CLAUDE.md rule 1:
valuesis strictly typed – rawtorch.Tensoris rejected. External boundaries that have raw tensors (the pyzag adapter, AOTI tracer fixtures, user code in notebooks / tests) wrap with the appropriateTensorWrappersubclass at the construction site, not by handing raw tensors to an internal neml2 helper.- Parameters:
layout (AxisLayout)
values (Mapping[str, TensorWrapper])
- items()[source]¶
- Return type:
ItemsView[str, TensorWrapper]
- layout: AxisLayout¶
- to(*args, **kwargs)[source]¶
Move every value to a new device / dtype; returns a new SparseVector.
- Return type:
- values: Mapping[str, TensorWrapper]¶
- neml2.es.norm(v)[source]¶
Batched Euclidean norm over all assembled vector groups.
- Parameters:
v (AssembledVector)
- Return type:
- neml2.es.norm_sq(v)[source]¶
Batched squared Euclidean norm over all assembled vector groups.
- Parameters:
v (AssembledVector)
- Return type: