---
title: "Computing and summing slices"
description: "Enumerating slice values, executing in parallel, and combining results, including the effect of slicing on total work and memory."
eyebrow: "Slicing"
---

## Compute each slice, then add the results {#one-slice}

Array computation starts only after the path and sliced indices are determined. Each combination of index values defines a subtask: extract the corresponding input data and follow the planned contraction path to produce a slice result. After all slices finish, add their results elementwise to obtain the contraction of the original network.

For example, slicing an internal index of dimension 2 produces two results, $R_0$ and $R_1$, with final result $R=R_0+R_1$. This executes the slicing scheme; it does not search for another path.

```diagram
slicing-execution
Value combinations for two sliced indices. Consecutive IDs form a group; slices are accumulated sequentially within each group, groups run in parallel, and results are combined in group-ID order.
```

```rust
use arctn::contract_network_sliced;

let output = contract_network_sliced(
    &net, &tensors, &ssa_path, &sliced_legs
)?;
```

## Parallel computation and result summation {#native-chunks}

The native executor divides consecutively numbered slices into chunks. Each chunk accumulates in slice-ID order, and Rayon processes different chunks in parallel. Partial sums are then merged in chunk-ID order. Chunking depends only on the slice count and output element count, not the thread count.

| Item | Behavior |
| --- | --- |
| Maximum chunk count | At most 256, further limited by the partial-output element budget |
| Changing thread count | Changes when chunks finish, but not their grouping or final merge order |
| No sliced indices | Calls `contract_network` directly |
| Concurrent memory | Each simultaneously executing slice has its own input copies and intermediates |

The thread-count-independent merge order stabilizes single-machine floating-point results. It does not imply that MPI Allreduce is bitwise identical across different rank counts.

## Total work and memory {#cost-and-memory}

For $N$ slices, the binary contraction work across all slices is

$$
\begin{aligned}
F_{\mathrm{total}}&=NF_{\mathrm{slice}},\\
\log_{10}F_{\mathrm{total}}&=\log_{10}F_{\mathrm{slice}}+\log_{10}N.
\end{aligned}
$$

Slicing may reduce the largest per-slice result while increasing total FLOPs through repeated computation. The model excludes extra additions in the final cross-slice summation.

| Usually decreases with more slicing | Usually increases with more slicing |
| --- | --- |
| Largest per-slice intermediate | Number of slices in a complete execution |
| Path-model memory per subtask | Total FLOPs and input reads |

`target_size` limits only per-slice binary contraction results; for single-tensor networks it checks the final output. Original inputs, unary preprocessing, transpose temporaries, backend workspace, partial sums, and other concurrently executing slices all increase actual RSS. Execution parallelism must also be considered when controlling total memory.

## Three execution routes {#execution-routes}

| Route | Slice scheduling | Per-slice contraction | Result accumulation |
| --- | --- | --- | --- |
| Rust / Python native | Fixed chunks in Rust, parallelized with Rayon | ArcTN Rust CPU executor | Accumulation in ID order both within and between chunks |
| Python external backend | Serial enumeration in a fixed order in Python | Reuses one `opt_einsum.contract_expression` | Addition on the selected array backend |
| tnmpi | Each process executes a different contiguous slice range | ArcTN Rust CPU executor; `--dtype` supports four real / complex types; see [MPI execution](/docs/mpi#run) | MPI Allreduce |

All three routes use the same mathematical decomposition, but scheduling, synchronization, and floating-point reduction order differ. State the execution route when reporting performance.
