Slicing principles

Split a contraction by enumerating internal-index values to limit intermediate result sizes within each slice.

On this page

Splitting contraction into subtasks

After selecting internal indices, ArcTN enumerates every combination of their values. Each combination fixes the corresponding input axes, performs a contraction along the same SSA path, and contributes to an elementwise sum of all results. Slicing is an exact mathematical decomposition: it performs no low-rank truncation and omits no value combinations. With floating-point arithmetic, sliced summation and unsliced contraction may add values in different orders, so ordinary rounding differences are possible; bitwise equality is not promised.

How slicing reduces intermediate tensors

Consider a three-tensor contraction:

Yace=b,dAabcBbcdCde.Y_{ace}=\sum_{b,d}A_{abc}B_{bcd}C_{de}.

Let the dimensions of a,b,c,d,ea,b,c,d,e be 2, 3, 4, 5, and 6. Contracting B,CB,C first produces ZbceZ_{bce} without slicing. Fixing b=jb=j, each slice first computes Zce(j)=dBjcdCdeZ^{(j)}_{ce}=\sum_d B_{jcd}C_{de} and then contracts it with AajcA_{ajc}.

Fix internal index b to split the contraction into three slicesThe unsliced intermediate Z has 72 elements and is shown only for size comparison. Fix b to 0, 1, and 2 in inputs A and B. Each slice first computes a 24-element Z, then contracts it with the corresponding A to produce a 48-element partial output Y. Summing the three partial outputs recovers the original result.Unsliced intermediate Zbce · 72 elementsFix b in the inputsb = 0Z(0)ce · 24 elementsY(0)ace · 48 elementsb = 1Z(1)ce · 24 elementsY(1)ace · 48 elementsb = 2Z(2)ce · 24 elementsY(2)ace · 48 elementswith A(j) contract
Yace=Yace(0)+Yace(1)+Yace(2).Y_{ace}=Y^{(0)}_{ace}+Y^{(1)}_{ace}+Y^{(2)}_{ace}.

In the diagram, each slice has a 24-element intermediate and a 48-element partial output, so its largest tensor contains 48 elements. The complete 72-element ZZ is shown only for comparison; sliced execution does not allocate it first. Fixed preserves the original contraction order, potentially at the cost of repeated computation and result accumulation. See the Slicing example.

Slice count

If the sliced indices have dimensions d1,,dkd_1,\ldots,d_k, the number of slices is

Nslice=j=1kdj.N_{\mathrm{slice}}=\prod_{j=1}^{k}d_j.

SliceResult.log2_n_slices stores the base-2 logarithm of the slice count. Index dimensions need not be 2.

Intermediate-size limit

target_size controls intermediate tensor sizes after slicing. It is a positive integer measured in elements. Every binary contraction result within each slice must satisfy this limit; for a single-tensor network, the final output after unary processing is checked. It does not limit every temporary tensor created by unary preprocessing, nor is it a process-memory or GPU-memory cap.

What target_size constrains What target_size does not constrain
Largest binary contraction result per slice; final output for a single-tensor network Process RSS or GPU memory
Binary contraction results computed for the specified path and sliced indices Sum of elements in all simultaneously retained tensors
Logical element count Bytes after accounting for dtype
An individual slice Total resident memory of multiple concurrent slices
Result tensor C\lvert C\rvert Per-step A+B+C\lvert A\rvert+\lvert B\rvert+\lvert C\rvert or backend temporary workspace

log2_max_contraction_size reports the base-2 logarithm of the largest logical input-plus-output element count in a single step, counting original leaf tensors by their logical size after unary summation, trace, or diagonal operations. log2_peak_size reports the base-2 logarithm of peak live elements in the path model. Neither is another name for target_size.

A single-tensor network has an empty SSA path, but may still need traces, summation, or output-axis reordering. ArcTN checks the final output size after these unary operations. An excessively small target_size fails even when the path contains no binary contraction steps.

Choosing sliced indices for a fixed path

find_slices_to_size keeps the path fixed, temporarily sets selected index dimensions to 1, and replays the path. If intermediate results still exceed the target, it first selects the index appearing in the most oversized results. Ties are broken by larger dimension, then by smaller ID.

Along the same path, the results of steps 3, 5, and 7 exceed the size limit. Consider only candidate indices x, y, and z:

Step 3 result
{ x, y }
Step 5 result
{ x, z }
Step 7 result
{ x, y, z }

x appears in 3 oversized results, while y and z each appear in 2, so x is selected first. When evaluating each slice, set x's effective dimension to 1 and recompute sizes along the original path. Continue selecting indices if the limit is still exceeded.

  1. Sliced indices must exist in both the network and size_dict, with positive dimensions.
  2. Slicing output indices is currently unsupported; the final output shape remains unchanged.
  3. The slice list must not contain duplicate indices.
  4. An index repeated within one input tensor cannot be sliced; such indices involve trace or diagonal operations.
  5. If no valid index can further reduce result sizes, the search reports that no feasible scheme was found.

If the final output already has more elements than target_size, slicing only internal indices cannot satisfy the limit.

rust
use arctn::{find_slices_to_size, slice_result_fits_target_size};

let target_size = 1usize << 24;
let slices = find_slices_to_size(&net, &path, target_size)
    .ok_or("target_size is unreachable for this fixed path")?;
assert!(slice_result_fits_target_size(
    &net, &path, &slices, target_size
)?);

This is greedy selection on a fixed path, with no guarantee of the fewest sliced indices or lowest total FLOPs. Successful schemes pass the target check; search failure does not prove that every possible scheme is infeasible.

Reading SliceResult

Field Meaning
legs IDs of the internal indices actually selected
log2_n_slices Base-2 logarithm of the total number of value combinations
per_slice PathStats computed with sliced-index dimensions set to 1
log10_flops_total Base-10 logarithm of total FLOPs, computed as per-slice FLOPs times slice count

The largest intermediate is measured per slice and is not multiplied by the slice count. Total FLOPs and read/write volume must be multiplied by it. Evaluate a scheme using its path, sliced indices, slice count, largest per-slice result, total FLOPs, and actual execution time.

Save the path and SliceResult together. Slicing methods that allow path changes also return the corresponding final path. After changing a path, recheck that the slicing scheme satisfies the size limit.