Struct accept_any_index¶
Defined in File eval.hpp
Struct Documentation¶
-
struct accept_any_index¶
Builds a custom evaluator (see CacheManager::custom_evaluator_type) that evaluates a subtree in batches over a contracted index, to bound the peak memory of intermediates that carry that index.
For each node it is consulted on, the returned evaluator chooses a batch mode
Kfrom the modes the optimizer annotated at that node (EvalExpr::node_slice_mask; see thepick_sliceablelambda); it declines if the node carries no accepted annotation. Annotations are authoritative — there is no heuristic fallback, so a node the peak-constrained optimizer did not ask to batch is never batched. It asks the backend to partitionKinto contiguous, whole-tile element-range batches of at mosttarget_batch_size(K)elements each — the target is an upper bound, not a goal (Result::mode_batches). Mode selection is sliceability-aware: it takes the first accepted mode that actually partitions into more than one batch in the current (possibly already-outer-sliced) context, so a mode already sliced by an outer re-entry is skipped and the node advances to its next annotated mode; if no candidate is sliceable it declines (leaving small / unselected / already-fully-sliced indices to the standard scheme).A node the optimizer priced with several annotated modes is sliced on each of them, and annotated modes may also sit at different nodes of one tree; either way the batching nests. The per-batch scratch cache carries a reinstalled copy of this evaluator, so when the standard-scheme replay of an outer batch reaches an annotated node — an inner one, or the same node with a still-unsliced mode — the evaluator fires again and slices the next mode within the outer batch —
for outer-batch: for inner-batch: replay. The reinstalled evaluator closes over the outer-sliced leaf evaluator, so inner slicing composes on top of the outer slice; nesting is exact by the samesum_K = sum_{batches} sum_{K in batch}identity applied per mode, and a captured depth counter backstops runaway re-entry.Otherwise it replays the build of every compatible persistent final in the same batch passes: the group is the trigger node plus every key of
cachethat is registered persistent, not yet alive, and batches over an mode with the identical realized partition. Per batch, each group member is evaluated by the standard scheme — with every leaf carrying the member’s batch mode sliced to the batch’s element range — on a shared registered scratch cache (see detail::make_batched_scratch), so sub-intermediates repeated within a member (canonically-equal siblings) or shared between members are evaluated once per batch, exactly as the real cache would share them; the per-member partials are summed across batches. This is exact becausesum_K = sum_{batches} sum_{K in batch}, and never materializes the whole batch-mode extent of any intermediate at once. Completed members are stored intocache(canonical-phase convention); the trigger’s result is returned for evaluate() to cache as usual. Members nested inside other members evaluate in earlier passes and are then seeded (slice-free w.r.t. the outer batch mode) or re-derived sliced in the outer pass. Considering a group candidate costs one leaf evaluation (the mode_batches probe); with an unregistered (empty) cache the group is just the trigger.Why a group of trees rather than the trigger alone: sub-intermediates are shared between separately-intercepted finals, and a scratch scoped to one final cannot see the other consumers. Concretely, in DF-based PNO-CCSD the half-transformed DF factor gC = g.C (g the 3-index DF factor carrying the aux index K, C the PNO coefficients) feeds both canonically-equal gCC children of the particle-particle-ladder intermediate W = gCC.gCC and the triply-transformed final gCCC. Unbatched, the real cache builds gC once and serves all three uses (its keys are canonical, max_life = 3). Batching each final in isolation rebuilds gC n_batches times per final — the shared scratch of a single pass dedups W’s two gCC children within each batch, but cross-final sharing with gCCC is restored only by streaming both finals over the same batch partition in the same passes, which brings gC back to one evaluation per batch (work parity with the unbatched path, at sliced rather than full intermediate peak memory).
- Param leaf_evaluator:
the leaf evaluator (captured).
- Param target_batch_size:
per-index function
std::function<std::size_t(Index const&)>returning the per-batch slice size (in elements) for a given (batch-mode) index — an upper BOUND, not a goal. Backend-neutral: a tiled backend rounds batch boundaries to tile boundaries (down to a tile multiple), so realized batches are uneven and each covers at most this many elements, except the one-tile floor (a lone tile larger than the target).- Param accept:
predicate selecting which contracted indices may be batched (e.g. only those in the auxiliary/RI IndexSpace). Defaults to any.
- Param make_scope_guard:
factory, called with the batch count, returning an RAII object held for the duration of the batched partial contractions; a backend may use it to relax block-sparse screening (scaled by the batch count) so per-batch screening does not drop small contributions that are significant once summed over the full batch mode. Defaults to a no-op (make_no_scope_guard). Nested levels: when annotated modes sit at different nodes of one tree (see the nesting note above the class), the re-entrant inner evaluator is built with this same factory (unchanged, along with
accept,is_volatile,persistent_onlyandtarget_batch_size— see the reinstall below), so the inner level constructs its own guard frommake_scope_guard(inner_batches). The outer guard is held for the outer level’s entire batch loop, which includes the per-batch evaluate() calls that re-enter and construct the inner guard — so both guards are alive simultaneously while the innermost contractions run. A backend factory that relaxes screening scaled by its own level’s batch count therefore composes multiplicatively across nesting depth: net relaxation = product of batch counts over all nesting levels (outer_batches * inner_batches * …), matching the invariant that a contribution significant over the full product of batch modes must not be screened away in any individual (outer-cell, inner-cell, …) combination. No extra bookkeeping is needed to achieve this — it falls out of RAII scoping plus unchanged threading of the factory through re-entry; see “nested scope guards compose multiplicatively” in test_eval_ta.cpp for a structural proof (dense TensorD has no real screening to relax, so it spies on guard construction/destruction instead).- Param is_volatile:
predicate flagging a volatile leaf node (e.g. an amplitude tensor); the evaluator declines to batch any node whose subtree contains such a leaf, so only persistent (build-once) subtrees are streamed. Defaults to never_volatile (no persistence gate). Same classification as the eval cache’s volatility predicate. Kept last so the prior 4-argument form (…, accept, make_scope_guard) still compiles unchanged.