---
title: "Execution backends"
description: "Choose Rust CPU, NumPy, or a GPU array backend, and understand sliced execution and timing."
eyebrow: "Numerical execution"
---

## Choosing a backend {#dispatch}

`arctn_contract` and `ArcTNExecutionPlan.execute` default to `backend="native"`, the Rust CPU executor. Specify the appropriate backend for NumPy or a GPU array library. ArcTN neither switches automatically based on hardware nor falls back to another backend after a failure.

The backend determines which library performs each array operation. For example, the same path can run through the Rust CPU implementation or NumPy; existing CuPy device arrays can run through CuPy. Backend selection does not change the chosen contraction path.

| backend | Execution mechanism | Array location |
| --- | --- | --- |
| `native` | Rust `matrixmultiply` CPU kernels | Inputs are prepared as C-contiguous NumPy arrays before being passed to Rust |
| `numpy` | opt_einsum calls NumPy | CPU |
| `cupy`, `torch`, `jax`, etc. | opt_einsum calls the specified array library | Retains the device of the input device arrays; prepare inputs according to the selected library |

## Executing slices {#unsliced-and-sliced}

For unsliced execution, the native backend follows the supplied SSA path; external backends prepare and evaluate a fixed opt_einsum contraction expression.

Sliced execution uses the same slicing set and per-slice path, but the two backend types schedule work differently:

- Native backend: Rust groups slices through Rayon, then combines results in a fixed group order.
- External backend: Python sequentially enumerates slicing-index value combinations, evaluates the same expression for each slice, and adds results using the selected array library. Intermediate tensors are not reused between slices.

## Execution through Quimb / Cotengra {#cotengra}

`arctn_tree` returns a Cotengra `ContractionTree` containing the path and slicing indices; it does not contract arrays itself. When the tree is subsequently contracted, Cotengra/autoray selects array operations and executes slices.

For an existing Quimb `TensorNetwork`, pass `optimize=ArcTNOptimizer(...)`. Quimb obtains the tree through `ArcTNOptimizer.search()`; see the [Quimb frontend example](/docs/tutorial-quimb). This execution route differs from selecting a backend directly in `arctn_contract`.

## Synchronization, timing, and dependencies {#timing}

In an `arctn_contract` report, native and NumPy execution are treated as synchronous: numerical computation is complete when the call returns. `execution_wall_s` includes expression preparation and the backend call, but excludes input preparation; `execution_total_host_wall_s` also includes `execution_input_prepare_wall_s`.

GPU libraries may only submit work during the call, with device computation finishing later. ArcTN does not insert device synchronization, so other external backends report `execution_wall_s` as `None` instead of treating the shorter host-call duration as full computation time. Unsliced `plan.execute(return_info=True)` returns timing fields from the compiled object.

| Field | Scope |
| --- | --- |
| execution\_setup\_wall\_s | Expression compilation and preparation of input-array slicing indices |
| execution\_dispatch\_wall\_s | From the start of the backend call to its return on the host |
| execution\_wall\_s | Setup plus dispatch for native/NumPy; excludes input preparation in arctn\_contract |
| execution\_timing\_scope | Indicates synchronous host timing or unverified external-backend completion |

```python
from arctn import arctn_contract

value, info = arctn_contract(
    inputs, output, size_dict, arrays,
    backend="cupy", return_info=True,
)
# For an asynchronous device backend, use synchronization or event timing from the corresponding library
```

Install external-execution dependencies with `arctn[execution]`. Install CuPy, PyTorch, JAX, and their CUDA/runtime versions for your target hardware yourself; ArcTN does not select compatible distributions of these libraries.
