Constant tensors#
Use this page when a node must emit a tensor that is not derived from any traced input. For module buffer state versus constants, see State capture and feedback.
Static outputs#
Sometimes a node needs to output a constant tensor that is not derived
from any input. The static_outputs parameter on output_tensors()
handles this case:
import torch
import leapp
from leapp import annotate
leapp.start(name="static_example")
input_tensor = torch.tensor([1.0, 2.0, 3.0])
traced_input = annotate.input_tensors('my_node',
{'input': input_tensor})
# Computed output -- derived from the traced input.
computed_output = traced_input + 1.0
# Static output -- a constant, NOT derived from any input.
static_tensor = torch.tensor([4.0, 5.0, 6.0])
annotate.output_tensors(
'my_node',
{'computed': computed_output},
static_outputs={'static': static_tensor},
export_with="jit",
)
leapp.stop()
leapp.compile_graph()
The exported model returns both outputs: computed (input-dependent) and
static (always [4, 5, 6]).
static_outputs follows the same top-level naming contract as
output_tensors():
pass a dict of named raw tensors for plain static outputs, or
pass
TensorSemantics(...)/ a list ofTensorSemantics(...)if the static outputs should carry semantic metadata in the exported YAML.
Warning
Static outputs must be raw
torch.Tensorvalues. Using aTracedTensorwill raise an error.Bare top-level tensors are not accepted. Pass a dict of named raw tensors or
TensorSemantics(...)/ a list ofTensorSemantics(...).Static outputs are merged with the regular outputs in the compiled model — downstream nodes can consume them like any other output.
Preserving traced state across nonstandard copies#
Between nodes, LEAPP wires pipeline.data_flow from the producing node and
output port that a finished output value carries.
Copies that keep the values, shape and dtype of a finished output carry that state automatically, so the next node connects without any extra call:
Torch:
clone(),detach(),contiguous(),cpu(),cuda(), a device-onlyto(), and a full overwrite of a preallocated buffer withbuffer[:] = out,buffer[...] = out, orbuffer.copy_(out).NumPy:
np.copy(),.copy(), andnp.asanyarray().Warp: a full-range
wp.copy().Conversions between backends that keep shape and dtype, such as
torch.as_tensor()and.numpy().
Anything that changes the values — arithmetic, slicing, a reshape, a dtype cast, or a partial write — deliberately yields a value with no output port. The next node reports it as a dangling input rather than inventing an edge to data it never received.
Two cases still need an explicit call, because the destination cannot be upgraded in place or the values were produced some other way:
a preallocated raw
np.ndarraydestination, andany copy performed outside the operations listed above.
In both cases the data moves but the state does not, so the next node looks disconnected.
mirror_leapp_tags() copies the traced state from the
source output to the destination value after verifying that their values match.
Use it only for copies between finished nodes. Inside a traced node,
full-slice assignment and copy_() from a traced tensor are handled
automatically.
import torch
import leapp
from leapp import annotate
@annotate.method(export_with="jit")
def upstream(x: torch.Tensor):
return x + 1.0
@annotate.method(export_with="jit")
def downstream(x: torch.Tensor):
return x * 2.0
leapp.start(name="state_copy_example")
out = upstream(torch.tensor([1.0, 2.0, 3.0]))
buffer = torch.zeros_like(out)
buffer[:] = out
annotate.mirror_leapp_tags(out, buffer)
result = downstream(buffer)
leapp.stop()
leapp.compile_graph()
Torch and Warp destinations are upgraded in place, so the return value can be
ignored. A raw np.ndarray cannot be upgraded in place, so NumPy callers must
assign the return value instead:
buffer = annotate.mirror_leapp_tags(out, buffer)
Warning
mirror_leapp_tags requires source and target values to match exactly. It
is for preserving graph wiring after equivalent copies, not for marking a
newly computed tensor as if it came directly from another tensor.