---
title: "Numerical contraction"
description: "Evaluate arrays along a contraction path using native Rust CPU execution or another array library, returning axes in output order."
eyebrow: "Numerical execution"
---

## From path to numerical result {#planning-vs-execution}

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](/docs/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](/docs/tutorial-quimb).

## Native pairwise contraction steps {#native-step}

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](/docs/validation#layers) 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.

```diagram
execution-step
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 {#data-contract}

| 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 {#usage}

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](/docs/compiled-contraction). To persist a path and slicing set across processes, use `arctn_plan`; see [saving and reusing plans](/docs/execution-plan).
