Source code for pyfracval.experimental.candidate_policies

"""Archived CCA candidate-pair ordering policies.

``leaf_soft`` (leaf-pairs-first ordering) and ``leaf_score``/``leaf_hybrid``
(heuristic-scored ordering) were benchmarked against the unranked
(shuffled) baseline ordering at N=512 in the hard regime (see
``docs/source/experiments.md``): both land on the same success rate as
baseline. *How* you search for a contact pair doesn't matter much when the
real question is *whether* a valid contact pair exists at all at the
enforced ``gamma_pc``.

Kept reachable via ``cca_candidate_policy`` for anyone who wants to try a
different scoring heuristic later. Leaf classification and scoring
themselves (``_candidate_leaf_class``/``_candidate_score`` in
``cca/candidates.py``) stay in production code since the per-attempt
telemetry they feed is diagnostic instrumentation used regardless of
policy, not part of what was benchmarked here.
"""

from __future__ import annotations

from typing import Callable

import numpy as np

from ..config import OrchestratorAlgorithmConfig

LeafClassFn = Callable[[bool, bool], str]
ScoreFn = Callable[..., float]


[docs] def reorder_candidates_by_policy( candidate_policy: str, candidate_indices: np.ndarray, leaf_mask_1: np.ndarray, leaf_mask_2: np.ndarray, coords1: np.ndarray, radii1: np.ndarray, cm1: np.ndarray, coords2: np.ndarray, radii2: np.ndarray, cm2: np.ndarray, gamma_pc: float, algorithm_config: OrchestratorAlgorithmConfig, candidate_leaf_class_fn: LeafClassFn, candidate_score_fn: ScoreFn, ) -> np.ndarray: """Reorder shuffled candidate pairs according to an archived policy.""" ll: list[np.ndarray] = [] ln: list[np.ndarray] = [] nn: list[np.ndarray] = [] for pair in candidate_indices: i = int(pair[0]) j = int(pair[1]) cls = candidate_leaf_class_fn(bool(leaf_mask_1[i]), bool(leaf_mask_2[j])) if cls == "LL": ll.append(pair) elif cls == "LN": ln.append(pair) else: nn.append(pair) if candidate_policy == "leaf_soft": return np.array(ll + ln + nn, dtype=int) topk = int(algorithm_config.cca_score_topk_per_class) def _score_and_sort(pairs: list[np.ndarray], cls: str) -> list[np.ndarray]: if not pairs: return [] n_score = len(pairs) if topk <= 0 else min(topk, len(pairs)) scored: list[tuple[float, np.ndarray]] = [] for pair in pairs[:n_score]: i = int(pair[0]) j = int(pair[1]) score = candidate_score_fn( coords1, radii1, cm1, i, coords2, radii2, cm2, j, float(gamma_pc), cls, ) scored.append((score, pair)) scored.sort(key=lambda x: x[0], reverse=True) scored_pairs = [p for _, p in scored] return scored_pairs + pairs[n_score:] # Both leaf_score (global score order) and leaf_hybrid (score within # leaf-priority class) end up doing the same per-class scoring pass - # the two policies were probed head-to-head and found indistinguishable # (docs/source/experiments.md), so there's no separate "global" merge # left to preserve. merged = ( _score_and_sort(ll, "LL") + _score_and_sort(ln, "LN") + _score_and_sort(nn, "NN") ) return np.array(merged, dtype=int)