Numerical contraction

Evaluate arrays along a contraction path using native Rust CPU execution or another array library, returning axes in output order.

On this page

From path to numerical result

A contraction path determines the order of computation; numerical execution reads input arrays and computes intermediate tensors and the final result step by step.

Use Entry point
Plan and execute, optionally with slicing arctn_contract
Execute a saved path and slicing set ArcTNExecutionPlan.execute
Execute a supplied SSA path in Rust contract_network
Execute a supplied SSA path and slicing set in Rust contract_network_sliced

Python defaults to backend="native", the ArcTN Rust CPU executor. You may also select execution backends such as NumPy or CuPy. In Quimb, pass an ArcTNOptimizer to optimize= and let Quimb/Cotengra execute the contraction; see the Quimb example.

Native pairwise contraction steps

Native execution first checks that array count, axis dimensions, and stored element counts match the network, then validates that the path produces the final result from those inputs. For example, an input of shape (2, 3) must contain six elements and cannot simply be substituted with axes ordered as (3, 2). See input and path checks for examples.

After validation, each step arranges input axes according to which indices must be retained or summed, groups like axes into matrix dimensions, and performs matrix multiplication (GEMM). A shared index must be retained if it still occurs in a later tensor or the final output; it is summed only when no longer needed.

C[b,i,j] = Σk A[b,i,k] B[b,k,j]

A[b,i,k]batch × m × k
B[b,k,j]batch × k × n
C[b,i,j]batch × m × n
b
Shared by both tensors; retained as batch axes
k
Shared by both tensors; summed out in this step
i, j
Free axes from A and B, respectively

Reordering and merging axes converts the operation to batched GEMM. The result is then restored to the output tensor's axis order.

Example of converting a binary contraction to batched matrix multiplication: b is retained, k is summed out, and i and j form the free axes of the result. Multiple axes of the same type can be reordered and merged into one matrix dimension.

Indices shared by both inputs and retained after contraction become batch axes; shared indices to be summed become matrix-multiplication contraction axes. Retained indices found on only one side form the row and column axes respectively. The final result is permuted into the order specified by output.

Inputs, dtype, and results

Rust type Python data type
f32 float32
f64 float64
Complex32 complex64
Complex64 complex128

For every execution, array count and shapes must match the network, and all inputs must use the same supported data type. The Python native backend prepares C-contiguous NumPy arrays; output specifies result-axis order.

Execution uses dense pairwise contraction, without bond-dimension truncation or low-rank approximation. The metric called FLOPs in PathStats counts scalar multiplications from the network structure and path. It is not directly equivalent to CPU instruction count, memory traffic, or actual runtime.

Even paths with identical PathStats can have different runtimes because of axis ordering, transpositions, and GEMM shapes. Measure backend performance separately with the same path, inputs, dtype, threads, and timing boundaries.

Direct execution

Rust: execute an existing path without searching again

rust
use arctn::contract_network;

let result = contract_network(&net, tensors, &ssa_path)?;

Python: plan and execute

python
from arctn import arctn_contract

result, info = arctn_contract(
    inputs, output, size_dict, arrays,
    preset="heavy", backend="native",
    return_info=True,
)

info records the actual backend and execution time. If only array values change while the structure and path remain fixed, compile once and execute repeatedly. To persist a path and slicing set across processes, use arctn_plan; see saving and reusing plans.