Optimizing paths with Heavy
Call Heavy, read contraction paths, objective values, and timings, and save the resulting plan.
On this page
First prepare the example network structure, dimensions, and numerical arrays from Tensor-network input in Quick start.
Running Heavy and keeping its report
After installing the full wheel containing Light / Heavy, reuse the network in Quick start, or supply your own inputs, output, and size_dict.
Save the network definition and the call below as heavy_example.py, and set the thread count when launching it:
RAYON_NUM_THREADS=24 python heavy_example.py
Obtaining the result
arctn_schedule optimizes the contraction order without executing numerical arrays. Its report is a Python dictionary containing the path, metrics, and optional slicing information.
from arctn import arctn_schedule
report = arctn_schedule(
inputs, output, size_dict,
preset="heavy", seed=17,
use_ssa=True,
)
path = report["path"]
print(report["log10_flops"])
print(report["log2_read_write"])
print(report["planner_objective_score_log2"])
Heavy uses a larger search budget to seek better paths. It is still heuristic: it returns the best completed candidate from the current run, with no guarantee of local or global optimality or of outperforming Light every time.
Reading the result
The default objective is . When comparing searches on the same network with the same weights, read planner_objective_score_log2; FLOPs alone do not capture this weighted objective.
Path and objective fields
| Field | Meaning |
|---|---|
path, path_format |
Final path and encoding; defaults to linear-v1, while the example uses ssa-v1 |
planner_objective |
Optimization objective name |
flops_weight, read_write_weight |
Objective weights for this call |
planner_objective_score_log2 |
Base-2 logarithm of the final objective, including all slices when sliced |
log10_flops, log2_read_write |
Work and read/write volume of the final path on the unsliced network, in base-10 and base-2 logarithms respectively |
log2_max_size |
Base-2 logarithm of the largest binary contraction result element count on the unsliced path |
log2_max_contraction_size |
Base-2 logarithm of the largest sum of logical input and result elements in one unsliced-path step |
log2_total_size, log2_peak_size |
Base-2 logarithms of total writes and peak live elements on the unsliced path |
wall_s |
Search time in seconds recorded by the Light / Heavy compiled library; excludes subsequent array execution |
Integers in path are tensor IDs interpreted according to path_format; integers in sliced_legs are internal index IDs. See Contraction trees and paths for the two path formats.
Path metrics are computed from network structure and dimensions, not measured execution time or process memory. See Path metrics and optimization objective for the formulas.
Slicing fields
The example does not set target_size. To limit per-slice intermediates, configure slicing as in Controlling intermediates, then read these fields:
| Field | Meaning |
|---|---|
sliced_legs |
Internal integer IDs of sliced indices |
log2_n_slices |
Base-2 logarithm of the slice count |
sliced_log10_flops_total |
Base-10 logarithm of total FLOPs across all slices |
sliced_log2_max_size |
Base-2 logarithm of the largest binary contraction result element count per slice |
planner_log2_read_write |
Base-2 logarithm of total read/write volume across all slices |
target_size |
Requested maximum result element count per slice |
memory_constraint_metric |
Definition of the size constraint |
max_intermediate_log2_elements_per_slice |
Base-2 logarithm of the final largest result element count per slice |
sliced_legs lists which indices are sliced; its length is not the slice count. One index of dimension 4, for example, produces 4 slices and log2_n_slices equal to 2.
Without target_size, sliced_legs is empty, log2_n_slices is 0, some sliced_* fields are absent, and max_intermediate_log2_elements_per_slice is None. Even with target_size, slicing may not be needed; bool(report["sliced_legs"]) indicates whether any indices were actually selected.
Check the size constraint using the per-slice size, not the unsliced log2_max_size. Compare total work with sliced_log10_flops_total and planner_log2_read_write; planner_objective_score_log2 already includes the objective across all slices using the current weights.
Setting threads and reading timings
The example calls Heavy once. RAYON_NUM_THREADS=24 gives 3 independent search starts sharing 24 worker threads; no outer seed loop is needed. seed=17 sets the base seed, and the library derives the others. This setting does not reserve 24 physical cores. See Recommended thread configurations for 8, 16, and 32 threads.
Read the search time:
print("Search time (seconds):", report["wall_s"])
Internal candidate lists, stage records, and independent-start reports are not public return fields. To time the entire Python call, use time.perf_counter() around it; that covers a different interval from the search time recorded by the compiled library.
Saving the contraction plan
Given report, use ArcTNExecutionPlan.from_schedule to save the network, SSA path, and slicing information without searching again:
from arctn import ArcTNExecutionPlan
plan = ArcTNExecutionPlan.from_schedule(
report, inputs=inputs, output=output, size_dict=size_dict,
)
plan.save("plan.json")
Subsequent execution and reuse
See Saving and reusing paths for loading files, executing contractions, and updating arrays. To pass paths to other tools, see Path representations and persistence.