---
title: "Compilation and reuse"
description: "Prepare a fixed network and contraction path once, then execute them on multiple sets of numerical arrays."
eyebrow: "Numerical execution"
---

## Compile once, execute repeatedly {#compile-once}

When network structure, dimensions, and contraction path stay fixed and only array values change, compile once and execute on multiple array sets. This avoids both replanning and repeatedly preparing the same contraction expression.

Path search decides which two tensors to contract next. Compilation determines the axis permutations and matrix multiplications for each step; execution applies those steps to actual arrays. Reusing a compiled object saves preparation work, but every new array set still requires the corresponding numerical computation.

Compilation here prepares contraction steps for execution; it does not regenerate machine code for each input. The compiled object does not store input values.

## What the native backend prepares {#native-plan}

Rust `CompiledContraction` stores axes for unary processing, input-axis permutations for each step, batched-matrix-multiplication shapes, and the final output-axis order. Subsequent `execute` calls use this information directly.

Every execution still validates input count, shapes, and `DenseTensor` layout.

```pseudocode
steps = prepare axis orders, matrix shapes, and output order from network and path
For each input array set arrays:
    Check whether the arrays match the network
    result = execute numerical contraction using steps
```

## Reusing an object in Python {#python-usage}

Assume `ssa_path` is available and `arrays0` and `arrays1` contain different numerical values. Both sets follow `inputs` order and retain the same shape and axis meanings for each array:

```python
from arctn import ArcTNCompiledContraction

compiled = ArcTNCompiledContraction.compile(
    inputs, output, size_dict,
    ssa_path=ssa_path,
    backend="numpy",
)
y0 = compiled.execute(arrays0)
y1 = compiled.execute(arrays1)
print(compiled.stats())
```

`y0` and `y1` are the contraction results for the two array sets; the second `execute` reuses the same `compiled` object. For example, gate parameters can change in a fixed quantum-circuit structure while reusing the compiled contraction, provided the gate-tensor connectivity and dimensions remain unchanged.

| backend | Object retained by compile | execute |
| --- | --- | --- |
| native | Rust CompiledContraction | Calls the Rust CPU executor |
| Installed opt\_einsum array backend, such as numpy, cupy, torch, or jax | Fixed opt\_einsum contract\_expression | Evaluates the expression with the selected backend |

The default is `backend="native"`. Other backends must be selected explicitly; `"auto"` is not accepted. Install external backends and their dependencies separately.

`ssa_path=` requires SSA format, not an opt\_einsum linear path. SSA uses fixed tensor IDs; a linear path uses positions in the current tensor list, which change after each contraction.

## Reuse requirements {#lifecycle}

`ArcTNCompiledContraction` is used within the current process. Recompile after changing the network structure, dimensions, or SSA path.

Persist the network, contraction path, and slicing information, not the compiled object. Save and load these through `ArcTNExecutionPlan`; for an unsliced plan, call `plan.compile()` once and reuse its result. Repeated unsliced `plan.execute()` calls also avoid replanning, but create a new compiled object on every call.

The Python compilation interface does not support a nonempty slicing set. Execute a saved sliced plan with `ArcTNExecutionPlan.execute()`, or pass it to Cotengra through `to_tree()`.

Rust also provides `execute_stripped`: after each matrix multiplication, if the maximum magnitude is positive and finite, it normalizes by that magnitude and accumulates a separate base-10 scaling exponent. This prevents numerical scales from becoming too large or too small, without truncation or approximation.
