neml2.aoti¶
Python interface to the thin C++ neml2::aoti::Model runtime.
This module exposes Model – a wrapper around the bare C++ class
that loads AOTI-exported NEML2 model artifacts (.pt2 + _meta.json)
produced by neml2-compile.
The runtime exposes forward, jvp, jacobian plus the
parameter-derivative pair param_jacobian / param_vjp (d(output)/
d(parameter) for promoted parameters) – all keyed by the structural
input/output names recorded in the metadata. A named_parameters() dict
exposes parameters that were explicitly promoted via neml2-compile
--parameter NAME at compile time; mutating those tensors in place is
reflected on the next call. The derivative graphs are emitted only for the
(output, input) / (output, parameter) pairs requested with
neml2-compile --derivative OUT:IN (jvp / jacobian /
param_jacobian / param_vjp raise for pairs that were not compiled in).
Example usage:
from neml2.aoti import Model
m = Model("aoti/my_model/my_model_meta.json")
outputs = m.forward({"strain": strain_tensor})
outputs, J = m.jacobian({"strain": strain_tensor})
# If `--parameter E` was passed at compile time:
m.named_parameters()["E"].fill_(210000.0)
- class neml2.aoti.AOTIModel(meta_path)[source]¶
Bases:
ModuleHIT-loadable wrapper around
neml2.aoti.Model.Constructed from a HIT
[Models]block with anartifact_pathoption pointing at the per-device artifact folder produced byneml2-compile(the folder holding one<device>/subfolder per compiled device). The subfolder for the currenttorch.get_default_device()is loaded – soneml2-run --device cuda(which sets the default device) pickscuda/. Eager and single-device: no dispatch happens here.Plays the native-Model role:
input_specandoutput_specare populated from the metadata’svar_typefields;__call__takesTensorWrapperpositional args ininput_specorder, unwraps them to raw tensors, runs the AOTIforwardgraph, and wraps each output back in its declared type. Promoted parameters (if any) are not part ofinput_spec; they are reachable throughnamed_parameters()(a mutable dict) andset_parameter(). The full sensitivity surface –jvp(),jacobian(),param_jacobian(),param_vjp()– is forwarded to the binding so the py-aoti route matches the others (CLAUDE.md “six evaluation routes” parity).- Parameters:
meta_path (str | Path)
- SECTION = 'Models'¶
Inherits from
nn.Modulerather thanneml2.model.Modelso the boundtorch::inductor::AOTIModelPackageLoaderruntime drives evaluation; the explicit class attribute keeps it in the [Models] section of the syntax catalog despite not subclassing Model.
- forward(*args, v=None, v2=None, vh=None)[source]¶
Drive the AOTI graph.
Accepts
input_specpositional args asTensorWrapperinstances (mirroring the nativeComposedModelboundary). Returns a tuple of typed wrappers inoutput_specorder – always a tuple, even for single-output models, so consumers can iterate uniformly.v/v2/vh(the native chain-rule hooks) are accepted only for signature compatibility with other native models; passing them is rejected because the AOTI graph’s JVP path is structurally different (it’s a separate_jvp.pt2graph, exposed via thejvp()/jacobian()methods rather than the typed chain-rule protocol). Drivers that don’t need sensitivities – e.g.TransientDriver,TransientRegression– work as-is.- Parameters:
args (TensorWrapper)
- classmethod from_hit(node, factory)[source]¶
- Parameters:
node (nmhit.Node)
factory (_NativeInputFile)
- Return type:
- hit = <neml2.schema.HitSchema object>¶
- jacobian(inputs, param_overrides=None)[source]¶
Outputs + the per-(output, input) Jacobian blocks from the compiled
jacobiangraph.inputsis a{variable_name: tensor}dict. Returns(outputs, J)whereJ[out][in]is the blockd(out)/d(in), shaped(batch, *out_base, *in_base). Likejvp(), needs an artifact compiled with-d.
- jvp(inputs, tangents, param_overrides=None)[source]¶
Outputs + the directional derivative
J @ vfrom the compiledjvpgraph.inputs/tangentsare{variable_name: tensor}dicts (same keys asinput_spec);tangentscarries the seed direction per input. Returns(outputs, jvp)keyed by output name – the same raw-tensor surface the eager runtime exposes. Requires the artifact to carry the derivative graph (compiled withneml2-compile ... -d OUT:IN); otherwise the underlying binding raises.
- named_parameters()[source]¶
The promoted parameters as a mutable
{qualified_name: tensor}dict.Overrides
nn.Module.named_parameters– whose(name, Parameter)iterator would be empty here, since the shim registers nonn.Parameter``s of its own (the calibratable values live in the compiled binding). This returns the binding's promoted-parameter surface, the same dict the eager runtime and the C++ routes expose. Entries may be mutated in place (``m.named_parameters()["elasticity.E"].fill_(...)) or replaced viaset_parameter(); the change is seen on the next call. Only parameters promoted at compile time (neml2-compile ... -p NAME) appear – with none promoted the dict is empty.
- param_jacobian(inputs, param_overrides=None)[source]¶
Outputs + the per-(output, parameter) Jacobian blocks
d(out)/d(param).The reverse-mode parameter-derivative counterpart to
jacobian()(mirrorsneml2.models.model.Model.param_jacobian()on the other routes):inputsis a{variable_name: tensor}dict and the result is(outputs, P)withP[out][param]the blockd(out)/d(param), keyed by qualified promoted-parameter name (seenamed_parameters()). Needs an artifact with the parameter promoted (neml2-compile ... -p NAME) and its derivative graph (-d).
- param_vjp(inputs, cotangents, param_overrides=None)[source]¶
Reverse-mode parameter VJP –
cotangentcontracted withd(out)/d(param).The reverse-mode counterpart to
param_jacobian()(mirrorsneml2.models.model.Model.param_vjp()):inputsis a{variable_name: tensor}dict,cotangentsis keyed by output name, and the result is keyed by qualified promoted-parameter name. Needs a-p/-dartifact.
- set_parameter(name, value)[source]¶
Replace a promoted parameter’s value (forwarded to the binding).
nameis a qualified promoted-parameter name (a key ofnamed_parameters());valuemust match the artifact’s pinned device/dtype. The new value is used on the next forward / derivative call.
- exception neml2.aoti.ConvergenceError¶
Bases:
RuntimeError
- class neml2.aoti.Model¶
Bases:
pybind11_objectThin C++ runtime for an AOTI-exported NEML2 model.
Construct from the path to the metadata JSON produced by
neml2-compile; the loader resolves the per-segment.pt2files relative to that path.The artifact is device- and dtype-pinned at export time; there is no runtime
to(). To target a different device, re-runneml2-compile.Parameters that were explicitly promoted via
--parameter NAMEat compile time are reachable throughnamed_parameters()and may be mutated in-place (e.g.model.named_parameters()['E'].fill_(210000.0)). Everything else is baked into the graph as a constant.- property device¶
Device the artifact was compiled for (immutable).
- property dtype¶
Floating-point dtype the artifact was compiled for (immutable).
- forward(self: neml2.aoti._aoti.Model, inputs: collections.abc.Mapping[str, torch.Tensor], param_overrides: collections.abc.Mapping[str, torch.Tensor] = {}) dict¶
Evaluate the model.
inputsis keyed by the names returned byinput_names; missing keys raise an error. Returns one tensor per name inoutput_names, preserving declaration order.param_overrides(default empty) replaces a promoted parameter’s value for this call only, without mutatingnamed_parameters()– a hook for multi-device dispatch.
- property input_base_shapes¶
Per-input base shape (Scalar -> [], SR2 -> [6], R2 -> [3, 3]). Inputs must be passed at their canonical (*B, *base_shape) shape.
- property input_names¶
Master input names in graph-call order.
- jacobian(self: neml2.aoti._aoti.Model, inputs: collections.abc.Mapping[str, torch.Tensor], param_overrides: collections.abc.Mapping[str, torch.Tensor] = {}) tuple[dict[str, torch.Tensor], dict[str, dict[str, torch.Tensor]]]¶
Evaluate + full Jacobian as unflattened variable-pair blocks.
Returns a 2-tuple
(outputs, J)whereJis a nesteddict[str, dict[str, Tensor]]:J[out_name][in_name]is the block(*B, *out_base_shape, *in_base_shape)(e.g. SR2->SR2 -> (B, 6, 6); Scalar->SR2 -> (*B, 6)) over the **structural* inputs (promoted-parameter inputs are not exposed in J).
- jvp(self: neml2.aoti._aoti.Model, inputs: collections.abc.Mapping[str, torch.Tensor], tangents: collections.abc.Mapping[str, torch.Tensor], param_overrides: collections.abc.Mapping[str, torch.Tensor] = {}) tuple[dict[str, torch.Tensor], dict[str, torch.Tensor]]¶
Evaluate + JVP.
inputsandtangentsare keyed byinput_namesand shaped(*B, *base_shape); a missing tangent key defaults to zero. Returns a 2-tuple(outputs, jvp_outputs)– bothdict[str, Tensor]keyed byoutput_names;jvp_outputs[name]is the directional derivative at the output’s natural(*B, *out_base_shape).
- named_parameters(self: neml2.aoti._aoti.Model) dict[str, torch.Tensor]¶
Return the mutable map of runtime-flexible (promoted) parameters.
The dict’s tensor values share storage with the C++-side parameter slots; in-place mutation (
model.named_parameters()['E'].fill_(...)) is reflected on the nextforward/jvp/jacobiancall. Reassigning a dict entry viamodel.named_parameters()['E'] = new_tensorupdates the Python dict only, not the C++ slot – useset_parameterfor that.Empty when the model was compiled with no
--parameterflags.
- property output_base_shapes¶
Per-output base shape (Scalar -> [], SR2 -> [6], R2 -> [3, 3]).
- property output_names¶
Master output names in graph-call order.
- param_jacobian(self: neml2.aoti._aoti.Model, inputs: collections.abc.Mapping[str, torch.Tensor], param_overrides: collections.abc.Mapping[str, torch.Tensor] = {}) tuple[dict[str, torch.Tensor], dict[str, dict[str, torch.Tensor]]]¶
Evaluate + parameter Jacobian as unflattened variable-pair blocks (schema v7).
Returns a 2-tuple
(outputs, P)wherePis a nesteddict[str, dict[str, Tensor]]:P[out_name][param_qname]is the block(*B, *out_base_shape, *param_base_shape)(e.g. stress w.r.t. a Scalar E -> (*B, 6)). The keys are promoted parameters (seenamed_parameters()), not structural inputs. Requires the artifact was compiled with-d OUT:PARAMover a promoted parameter; otherwise raises.
- param_vjp(self: neml2.aoti._aoti.Model, inputs: collections.abc.Mapping[str, torch.Tensor], cotangents: collections.abc.Mapping[str, torch.Tensor], param_overrides: collections.abc.Mapping[str, torch.Tensor] = {}) dict[str, torch.Tensor]¶
Parameter VJP / adjoint (schema v7):
dL/d(param)for the lossL = sum_o <cotangent_o, out_o>.cotangentsis adict[str, Tensor]keyed by output name, each at the output’s(*B, *out_base_shape)shape. Returnsdict[str, Tensor]keyed by parameter qualified name – the cheaper form for many-parameter inverse optimization. Same compile requirement asparam_jacobian.
- property parameter_base_shapes¶
Per-promoted-parameter natural base shape, keyed by qualified name (Scalar -> [], SR2 -> [6]). The keys are the promoted parameters (same keys as named_parameters()); empty when nothing was promoted. The parameter analogue of input_base_shapes / output_base_shapes, and the unified parameter surface shared with neml2.eager._EagerModel.
- set_parameter(self: neml2.aoti._aoti.Model, name: str, value: torch.Tensor) None¶
Replace a promoted parameter’s tensor (the C++-side slot is updated).
- set_solver_config(self: neml2.aoti._aoti.Model, atol: SupportsFloat, rtol: SupportsFloat, miters: SupportsInt, ls_type: str, ls_max_iters: SupportsInt, ls_cutback: SupportsFloat, ls_c: SupportsFloat) None¶
Configure the implicit-segment Newton solve (from the stub’s [Solvers] block). Schema v4+ no longer bakes these into the artifact.