Rate this Page
★ ★ ★ ★ ★

Source code for helion.autotuner.surrogate_pattern_search

from __future__ import annotations

import copy
import hashlib
import json
import math
import operator
import random
from typing import TYPE_CHECKING
from typing import cast

from .. import exc
from .base_search import BenchmarkResult
from .base_search import PopulationBasedSearch
from .base_search import PopulationMember
from .base_search import check_population_consistency
from .base_search import performance
from .benchmark_provider import MultiShapeBenchmarkProvider
from .benchmark_provider import _compile_config_failure_source_hash
from .benchmark_provider import _MultiShapeAutotuneArgs
from .benchmark_provider import _unset_fn
from .effort_profile import PATTERN_SEARCH_DEFAULTS
from .effort_profile import FlashStructuralSearchConfig
from .pattern_search import InitialPopulationStrategy
from .pattern_search import PatternSearch
from .search_space_logger import canonical_config_id
from helion._dist_utils import sync_seed

# 17: a pipeline lane whose conditional candidate search finds no unmeasured
# config counts as exhausted instead of leaving the qualification incomplete.
CUTE_FLASH_LANE_POLICY_VERSION = 17
# Raw per-family product up to which the structural qualification enumerates
# the effective (normalized) CuTe-flash search space (0.4 s for 1024 at the
# d256 pair cell); larger spaces stay sampled.
FLASH_EXACT_SPACE_RAW_FLOOR = 1024
_FLASH_TERMINAL_REFINEMENT_SCHEMA_VERSION = 2
_FLASH_TERMINAL_REFINEMENT_POLICY_VERSION = 2
_FLASH_TERMINAL_COORDINATE_POLICY = "same_leaf_full_surface_normalized_coordinate_v2"
_FLASH_TERMINAL_REFINEMENT_TARGET_MS = 200.0
_FLASH_TERMINAL_CONFIRMATION_TARGET_MS = 5000.0
_FLASH_TERMINAL_MEASUREMENT_POLICY = "mirrored_rotating_batched_wall_v2"

if TYPE_CHECKING:
    from collections.abc import Callable
    from collections.abc import Iterator
    from collections.abc import Mapping
    from collections.abc import Sequence

    from .._compiler.cute.autotune.config_generation_ext import (
        CuteConfigGenerationExtension,
    )
    from .._compiler.cute.cute_flash import FlashStructuralLeaf
    from ..autotuner.effort_profile import AutotuneEffortProfile
    from ..runtime.config import Config
    from ..runtime.settings import Settings
    from .base_search import _AutotunableKernel
    from .benchmarking import MirroredBenchmarkTrace
    from .config_generation import ConfigGeneration
    from .config_generation import CoordinateNeighborProjection
    from .config_generation import FlatConfig

try:
    import numpy as np
    from sklearn.ensemble import RandomForestClassifier

    HAS_ML_DEPS = True
except ImportError as e:
    HAS_ML_DEPS = False
    _IMPORT_ERROR = e


[docs] def flash_terminal_measurement_is_valid( record: Mapping[str, object], *, allow_projection_rejected: bool = False ) -> bool: """Validate one normalized terminal structural-qualification measurement.""" status = record.get("status") attempt_perf = record.get("attempt_perf") selection_perf = record.get("selection_perf") succeeded = status in {"ok", "deduplicated"} finite_perfs = bool( not isinstance(attempt_perf, bool) and isinstance(attempt_perf, (int, float)) and math.isfinite(attempt_perf) and attempt_perf > 0 and not isinstance(selection_perf, bool) and isinstance(selection_perf, (int, float)) and math.isfinite(selection_perf) and selection_perf > 0 ) if succeeded: return finite_perfs if status in {"error", "timeout", "peer_compilation_fail"}: return attempt_perf is None and selection_perf is None return bool( allow_projection_rejected and status == "projection_rejected" and record.get("config_id") is None and record.get("projected_config_id") is None and attempt_perf is None and selection_perf is None )
[docs] def flash_terminal_refinement_result_is_valid(record: Mapping[str, object]) -> bool: """Validate a candidate result recorded by terminal coordinate refinement.""" if flash_terminal_measurement_is_valid(record): return True return bool( record.get("status") in {"accuracy_error", "source_rejected"} and record.get("attempt_perf") is None and record.get("selection_perf") is None )
[docs] class LFBOPatternSearch(PatternSearch): """ Batch Likelihood-Free Bayesian Optimization (LFBO) Pattern Search. This algorithm enhances PatternSearch by using a Random Forest classifier as a surrogate model to select which configurations to benchmark, reducing the number of kernel compilations and runs needed to find optimal configurations. It imposes a similarity penalty to encourage diverse config selection. Algorithm Overview: 1. Generate an initial population (random or default) and benchmark all configurations 2. Fit a Random Forest classifier to predict "good" vs "bad" configurations: - Configs with performance < quantile threshold are labeled as "good" (class 1) - Configs with performance >= quantile threshold are labeled as "bad" (class 0) - Weighted classification emphasize configs that are much better than the threshold 3. For each generation: - Generate random neighbors around the current best configurations - Score all neighbors using the classifier's predicted probability of being "good" - Penalizes points that are similar to previously selected points - Selects points to benchmark via sequential greedy optimization - Retrain the classifier on all observed data (not incremental) - Update search trajectories based on new results The weighted classification model learns to identify which configs maximize expected improvement over the current best config. Compared to fitting a surrogate to fit the config performances themselves, since this method is based on classification, it can also learn from configs that timeout or have unacceptable accuracy. References: - Song, J., et al. (2022). "A General Recipe for Likelihood-free Bayesian Optimization." Args: kernel: The kernel to be autotuned. args: The arguments to be passed to the kernel during benchmarking. initial_population: Number of random configurations in initial population. Default from PATTERN_SEARCH_DEFAULTS. Ignored when using DEFAULT strategy. copies: Number of top configurations to run pattern search from. Full CuTe-flash searches qualify every structural leaf, give every live family one measured probe generation, then continue the best evidence-ranked parent families and every compound leaf. Default from PATTERN_SEARCH_DEFAULTS. max_generations: Maximum number of search iterations per copy. Default from PATTERN_SEARCH_DEFAULTS. min_improvement_delta: Early stopping threshold. Search stops if the relative improvement abs(best/current - 1) < min_improvement_delta. Default: 0.001 (0.1% improvement threshold). frac_selected: Fraction of generated neighbors to actually benchmark, after filtering by classifier score. Range: (0, 1]. Lower values reduce benchmarking cost but may miss good configurations. Default: 0.15. num_neighbors: Number of random neighbor configurations to generate around each search point per generation. Default: 300. radius: Maximum perturbation distance in configuration space. For power-of-two parameters, this is the max change in log2 space. For other parameters, this limits how many parameters can be changed. Default: 2. quantile: Threshold for labeling configs as "good" (class 1) vs "bad" (class 0). Configs with performance below this quantile are labeled as good. Range: (0, 1). Lower values create a more selective definition of "good". Default: 0.3 (top 30% are considered good). patience: Number of generations without improvement before stopping the search copy. Default: 2. similarity_penalty: Penalty for selecting points that are similar to points already selected in the batch. Default: 1.0. initial_population_strategy: Strategy for generating the initial population. FROM_RANDOM generates initial_population random configs. FROM_BEST_AVAILABLE uses cached configs from prior runs, and fills the remainder with random configs when best_available_pad_random is True. Can be overridden by HELION_AUTOTUNER_INITIAL_POPULATION env var. """ # Keep old serialized/test-created search state usable; normal construction # replaces this with a per-instance list for CuTe flash. train_source_hashes: list[str | None] | None = None # Written by ``FlashStructuralQualification`` (absent until it ran); read # by ``_select_starting_paths`` and the lane helpers through ``getattr``. _flash_qualified_pipeline_lanes: dict[ FlashStructuralLeaf, tuple[tuple[str, object], ...] ] _flash_qualified_compound_config_ids: dict[FlashStructuralLeaf, set[str]] _flash_parent_score_config_ids: set[str]
[docs] def __init__( self, kernel: _AutotunableKernel, args: Sequence[object], *, initial_population: int = PATTERN_SEARCH_DEFAULTS.initial_population, copies: int = PATTERN_SEARCH_DEFAULTS.copies, max_generations: int = PATTERN_SEARCH_DEFAULTS.max_generations, min_improvement_delta: float = 0.001, frac_selected: float = 0.10, num_neighbors: int = 300, radius: int = 2, quantile: float = 0.1, patience: int = 1, similarity_penalty: float = 1.0, initial_population_strategy: InitialPopulationStrategy | None = None, best_available_pad_random: bool = PATTERN_SEARCH_DEFAULTS.best_available_pad_random, num_neighbors_cap: int = -1, finishing_rounds: int = 0, polish_rounds: int = 10, compile_timeout_lower_bound: float = PATTERN_SEARCH_DEFAULTS.compile_timeout_lower_bound, compile_timeout_quantile: float = PATTERN_SEARCH_DEFAULTS.compile_timeout_quantile, flash_structural_search: FlashStructuralSearchConfig | None = None, ) -> None: if not HAS_ML_DEPS: raise exc.AutotuneError( "LFBOPatternSearch requires numpy and scikit-learn." "Install them with: pip install helion[surrogate]" ) from _IMPORT_ERROR super().__init__( kernel=kernel, args=args, initial_population=initial_population, copies=copies, max_generations=max_generations, min_improvement_delta=min_improvement_delta, initial_population_strategy=initial_population_strategy, best_available_pad_random=best_available_pad_random, num_neighbors_cap=num_neighbors_cap, finishing_rounds=finishing_rounds, compile_timeout_lower_bound=compile_timeout_lower_bound, compile_timeout_quantile=compile_timeout_quantile, ) # Parallel to train_x/train_y: the live member behind each training # row (None for externally seeded rows). Used to sync labels with # rebenchmarked timings before each surrogate fit. self._train_members: list[PopulationMember | None] = [] # Number of neighbors and how many to evalaute self.num_neighbors = num_neighbors self.radius = radius self.frac_selected = frac_selected self.patience = patience self.similarity_penalty = similarity_penalty self.polish_rounds = polish_rounds self.surrogate: RandomForestClassifier | None = None # Save training data self.train_x = [] self.train_y = [] self.train_configs: list[Config] | None = ( [] if self.config_spec.cute_flash_search_enabled else None ) self.train_source_hashes: list[str | None] | None = ( [] if self.config_spec.cute_flash_search_enabled else None ) self.quantile = quantile self.flash_structural_search = flash_structural_search self._flash_family_probe_path_limit = 0 self._flash_promoted_path_limit = self.copies self._cute_flash_lane_policy_enabled = ( self.config_spec.cute_flash_search_enabled and flash_structural_search is not None ) if self._cute_flash_lane_policy_enabled: assert flash_structural_search is not None if ( flash_structural_search.terminal_coordinate_rounds > 0 and flash_structural_search.terminal_coordinate_beam_width > 0 and self.settings.autotune_budget_seconds is None and self.settings.autotune_benchmark_fn is None and not isinstance(self.args, _MultiShapeAutotuneArgs) ): self._terminal_refinement_members = {} self._flash_promoted_path_limit = ( self._flash_generation().flash_structural_starting_path_limit( minimum=max(self.copies, flash_structural_search.starting_paths), retained_families=flash_structural_search.retained_families, retained_candidates_per_leaf=( flash_structural_search.retained_candidates_per_leaf ), ) ) self._flash_family_probe_path_limit = ( self._flash_generation().flash_structural_family_probe_path_limit( flash_structural_search.retained_families, flash_structural_search.family_probe_generations, ) ) self.copies = max( self._flash_promoted_path_limit, self._flash_family_probe_path_limit, )
def _algorithm_cache_policy(self) -> dict[str, object]: policy = super()._algorithm_cache_policy() policy.update( { # 2: benchmarked-only visited set, per-copy selection floor, # incumbent retention, and rebenchmark-synced surrogate labels # became unconditional. # 3: a full-neighborhood polish descent runs after the main # loop (see polish_rounds). "lfbo_version": 3, "polish_rounds": self.polish_rounds, "num_neighbors": self.num_neighbors, "radius": self.radius, "frac_selected": self.frac_selected, "quantile": self.quantile, "patience": self.patience, "similarity_penalty": self.similarity_penalty, "flash_structural_search": ( self.flash_structural_search if self._cute_flash_lane_policy_enabled else None ), } ) if self._cute_flash_lane_policy_enabled: policy["cute_flash_lane_policy_version"] = CUTE_FLASH_LANE_POLICY_VERSION assert self.flash_structural_search is not None policy["cute_flash_terminal_coordinate_refinement"] = { "schema_version": _FLASH_TERMINAL_REFINEMENT_SCHEMA_VERSION, "policy_version": _FLASH_TERMINAL_REFINEMENT_POLICY_VERSION, "coordinate_policy": _FLASH_TERMINAL_COORDINATE_POLICY, "rounds": self.flash_structural_search.terminal_coordinate_rounds, "beam_width": ( self.flash_structural_search.terminal_coordinate_beam_width ), "radius": self.radius, "minimum_improvement_fraction": self.min_improvement_delta, "measurement_policy": _FLASH_TERMINAL_MEASUREMENT_POLICY, "round_target_ms": _FLASH_TERMINAL_REFINEMENT_TARGET_MS, "confirmation_target_ms": (_FLASH_TERMINAL_CONFIRMATION_TARGET_MS), } policy["cute_flash_starting_path_limit"] = self._flash_promoted_path_limit policy["cute_flash_family_probe_path_limit"] = ( self._flash_family_probe_path_limit ) policy["cute_flash_maximum_path_capacity"] = self.copies return policy
[docs] @classmethod def get_kwargs_from_profile( cls, profile: AutotuneEffortProfile, settings: Settings ) -> dict[str, object]: from ..runtime.settings import _env_get_int from ..runtime.settings import _get_initial_population_strategy assert profile.lfbo_pattern_search is not None strategy = _get_initial_population_strategy( profile.lfbo_pattern_search.initial_population_strategy, settings.autotune_initial_population_strategy, ) return { "initial_population": profile.lfbo_pattern_search.initial_population, "copies": profile.lfbo_pattern_search.copies, "max_generations": profile.lfbo_pattern_search.max_generations, "initial_population_strategy": strategy, "best_available_pad_random": profile.lfbo_pattern_search.best_available_pad_random, "num_neighbors_cap": _env_get_int("HELION_CAP_AUTOTUNE_NUM_NEIGHBORS", -1), "flash_structural_search": profile.flash_structural_search, **PopulationBasedSearch.get_kwargs_from_profile(profile, settings), }
[docs] def seed_training_data( self, results: Sequence[BenchmarkResult], ) -> None: """Pre-populate the surrogate's training set with externally-benchmarked configs. Useful when an outer loop (e.g. a hybrid LLM+LFBO search) has already benchmarked configs and wants the LFBO surrogate to learn from them rather than starting from scratch. Failed configs (perf=inf) are kept since the surrogate's binary classifier learns from negatives too. """ for result in results: try: flat_values = self.config_gen.flatten(result.config) encoded = self.config_gen.encode_config(flat_values) except Exception as e: self.log.debug(f"seed_training_data: skipping config: {e}") continue self._append_training_sample( encoded, result.perf, result.config, result.fn, )
def _append_training_sample( self, encoded: object, perf: float, config: Config, fn: object, member: PopulationMember | None = None, ) -> None: self.train_x.append(encoded) self.train_y.append(perf) self._train_members.append(member) if self.train_configs is None: return if self.train_source_hashes is None: self.train_source_hashes = [None] * len(self.train_configs) self.train_configs.append(config) self.train_source_hashes.append( self.config_spec.backend.generated_source_hash(fn) ) def _apply_effective_source_repairs( self, repairs: dict[Config, BenchmarkResult], current_members: Sequence[PopulationMember], ) -> None: super()._apply_effective_source_repairs(repairs, current_members) if self.train_configs is None: return if self.train_source_hashes is None: self.train_source_hashes = [None] * len(self.train_configs) for index, config in enumerate(self.train_configs): repair = repairs.get(config) if ( repair is not None and index < len(self.train_y) and not math.isfinite(self.train_y[index]) ): self.train_y[index] = repair.perf self.train_source_hashes[index] = ( self.config_spec.backend.generated_source_hash(repair.fn) ) def _invalidate_rebenchmark_training_targets( self, failed_configs: set[Config], failed_source_hashes: set[str], ) -> None: if self.train_configs is None: return if self.train_source_hashes is None: self.train_source_hashes = [None] * len(self.train_configs) for index, (config, source_hash) in enumerate( zip(self.train_configs, self.train_source_hashes, strict=True) ): if index < len(self.train_y) and ( config in failed_configs or source_hash in failed_source_hashes ): self.train_y[index] = math.inf def _sync_training_labels(self) -> None: """Refresh train_y from live members so rebenchmarked timings (which replace noisy one-shot measurements) are what the surrogate learns from. Rows without a live member keep their recorded label.""" for index, member in enumerate(self._train_members): if member is None or index >= len(self.train_y): continue if member.perfs and math.isfinite(self.train_y[index]): self.train_y[index] = member.perf def _fit_surrogate(self) -> None: self._sync_training_labels() train_x = np.array(self.train_x) train_y = np.array(self.train_y) # Compute labels based on quantile threshold finite_mask = ~np.isinf(train_y) if finite_mask.any(): # Compute quantile among finite performance values train_y_quantile = np.quantile(train_y[finite_mask], self.quantile) pos_mask: np.ndarray = train_y <= train_y_quantile train_labels: np.ndarray = 1.0 * (pos_mask) # Sample weights to emphasize configs that are much better than the threshold # Clip this difference to a small number (e.g. 1e-5) so that in the case that all perfs # are equal (and train_y_quantile - train_y = 0) we avoid dividing by zero. # Instead, we will have all sample weights = 1 for all positive points. pos_weights = np.maximum(1e-5, train_y_quantile - train_y) * train_labels normalizing_factor = np.mean(pos_weights[pos_mask]) # Normalize weights so on average they are 1.0 pos_weights = pos_weights / normalizing_factor # Weights for negative labels are 1.0 sample_weight: np.ndarray = np.where(pos_mask, pos_weights, 1.0) else: # If all targets are inf, then all labels are 0 (except the first one) train_labels: np.ndarray = np.zeros(len(train_y)) sample_weight: np.ndarray = np.ones(len(train_y)) # Ensure we have at least 2 classes for the classifier # If all labels are the same, we need to handle this case if np.all(train_labels == train_labels[0]): self.log("All labels are identical, skip training surrogate.") self.surrogate = None else: self.log( f"Fitting surrogate: {len(train_x)} points, {len(train_y)} targets" ) self.surrogate = RandomForestClassifier( criterion="log_loss", random_state=42, n_estimators=100, n_jobs=-1, ) self.surrogate.fit(train_x, train_labels, sample_weight=sample_weight) assert len(self.surrogate.classes_) == 2
[docs] def compute_leaf_similarity( self, surrogate: RandomForestClassifier, X_test: np.ndarray ) -> np.ndarray: """ Compute pairwise similarity matrix using leaf node co-occurrence. For RandomForest, two samples are similar if they land in the same leaf nodes across trees. This is the Jaccard similarity of their leaf assignments. Args: model: Fitted RandomForestClassifier X_test: Test samples (n_samples, n_features) Returns: similarity_matrix: (n_samples, n_samples) matrix where entry [i,j] is the fraction of trees where samples i and j land in the same leaf """ n_samples = X_test.shape[0] # Get leaf indices for each sample across all trees # leaf_indices shape: (n_samples, n_trees) leaf_indices = surrogate.apply(X_test) n_trees = leaf_indices.shape[1] # Compute similarity: fraction of trees where samples land in same leaf # This is equivalent to Jaccard similarity on the leaf assignments similarity_matrix = np.zeros((n_samples, n_samples)) for i in range(n_samples): # Vectorized comparison: how many trees have same leaf as sample i same_leaf: np.ndarray = ( leaf_indices == leaf_indices[i : i + 1, :] ) # (n_samples, n_trees) similarity_matrix[i, :] = same_leaf.sum(axis=1) / n_trees return similarity_matrix
def _surrogate_select( self, candidates: list[PopulationMember], n_sorted: int ) -> list[PopulationMember]: """ Select top candidates using the surrogate model with diversity-aware scoring. Uses sequential greedy selection to pick candidates that balance high predicted probability of being "good" (from the Random Forest classifier) with diversity (avoiding candidates too similar to already-selected ones). The selection process: 1. Score each candidate using the surrogate's predicted probability of class 1 ("good") 2. Compute pairwise similarity between candidates using leaf node co-occurrence 3. Greedily select candidates one at a time: - First candidate: highest probability - Subsequent candidates: highest (probability - similarity_penalty * mean_similarity) where mean_similarity is the average similarity to already-selected candidates 4. Return the top n_sorted candidates based on selection order If no surrogate model is available (e.g., all training labels were identical), candidates are scored randomly. Args: candidates: List of PopulationMember configurations to score and select from. n_sorted: Number of top candidates to return. Returns: List of the top n_sorted PopulationMember candidates, ordered by selection rank. """ if n_sorted <= 0 or not candidates: return [] # Score candidates candidate_X = np.array( [self.config_gen.encode_config(member.flat_values) for member in candidates] ) n_samples = len(candidate_X) n_selected = min(n_sorted, n_samples) # Get predicted probabilities (higher = more likely to be good) surrogate: RandomForestClassifier | None = self.surrogate if surrogate is None: # If surrogate is None, scores are random with sync_seed(process_group_name=self.kernel.env.process_group_name): scores = [random.random() for _ in range(n_samples)] candidates_sorted = sorted( zip(candidates, scores, strict=True), key=operator.itemgetter(1), )[:n_selected] candidates_sorted = [member for member, _ in candidates_sorted] else: proba = np.asarray(surrogate.predict_proba(candidate_X))[:, 1] # Track the cumulative similarity to already-selected points and update # it incrementally. This preserves the original ranking while avoiding # the dense n_samples x n_samples similarity matrix. leaf_indices = surrogate.apply(candidate_X) n_trees = leaf_indices.shape[1] similarity_sums = np.zeros(n_samples) remaining_indices = list(range(n_samples)) selected_indices: list[int] = [] for _rank in range(n_selected): if selected_indices: mean_similarities = similarity_sums[remaining_indices] / len( selected_indices ) proba_minus_similarity = ( proba[remaining_indices] - self.similarity_penalty * mean_similarities ) else: proba_minus_similarity = proba[remaining_indices] best_local_idx = int(np.argmax(proba_minus_similarity)) best_global_idx = remaining_indices.pop(best_local_idx) selected_indices.append(best_global_idx) if len(selected_indices) == n_selected: break same_leaf: np.ndarray = ( leaf_indices == leaf_indices[best_global_idx : best_global_idx + 1] ) similarity_sums += same_leaf.sum(axis=1) / n_trees candidates_sorted = [candidates[idx] for idx in selected_indices] self.log.debug( f"Scoring {len(candidate_X)} neighbors, selecting {(n_selected / len(candidate_X)) * 100:.0f}% neighbors: {len(candidates_sorted)}" ) return candidates_sorted def _autotune(self) -> Config: initial_population_name = self.initial_population_strategy.name self.log( f"Starting {self.__class__.__name__} with initial_population={initial_population_name}," f" copies={self.copies}," f" max_generations={self.max_generations}," f" similarity_penalty={self.similarity_penalty}" ) visited: set[Config] = set() self.population = [] for flat_config in self._generate_initial_population_flat(): member = self.make_unbenchmarked(flat_config) if member is not None and member.config not in visited: visited.add(member.config) self.population.append(member) initial_population = list(self.population) self.set_generation(0) self.benchmark_initial_population( self.population, random_fallback_target=self._random_fallback_population_target(), visited=visited, ) # Configs the budget never attempted are no evidence for the # structural qualification (they were dropped from the population). initial_population = [ member for member in initial_population if member.status != "skipped" ] # Compute adaptive compile timeout based on initial population compile times self.set_adaptive_compile_timeout( self.population, min_seconds=self.compile_timeout_lower_bound, quantile=self.compile_timeout_quantile, ) # again with higher accuracy self.rebenchmark_population(self.population, desc="Verifying initial results") check_population_consistency( self.population, process_group_name=self.kernel.env.process_group_name ) # Snapshot compiler-seeded members so they survive the search-loop # pruning into the final-pick verification candidate pool. self.capture_compiler_seed_members(self.population) self.population.sort(key=performance) if not any(math.isfinite(member.perf) for member in self.population): raise exc.NoConfigFound # Save to training data for member in self.population: self._append_training_sample( self.config_gen.encode_config(member.flat_values), member.perf, member.config, member.fn, member=member, ) # Fit model self._fit_surrogate() # Initial witnesses can underrate a family whose useful child settings # are non-default. Full CuTe-flash tuning qualifies every ordinary leaf # before transferring its best representatives to compound leaves and # promoting parent families. # Quick tuning keeps its historical generation budget. qualification_generations = self._run_flash_structural_qualification( visited, initial_population=initial_population, ) first_main_generation = 1 + qualification_generations phase = self._autotune_metrics.search_phase_metrics if phase is not None and phase.get("completed") is not True: if self._autotune_budget_exceeded_across_ranks(): return self._finalize() # Main search cannot replace missing qualification witnesses. Keep # the recorded failure and stop before promotion or final selection. failures = [ ( f"passes={phase.get('qualification_passes_completed')}/" f"{phase.get('qualification_passes_planned')}" ) ] for key in ( "schedule_anchor_complete", "compound_catalog_complete", "family_probe_complete", ): if phase.get(key) is False: failures.append(f"{key}=False") for key in ("leaf_results", "clc_families", "compound_transfers"): rows = phase.get(key, []) assert isinstance(rows, list) for row in rows: if row["complete"] is True: continue detail = ( f"{key}: family={row['family']}, " f"compound_packet={row.get('compound_packet')}, " f"softmax_disc={row['softmax_disc']}" ) if key == "compound_transfers": detail += ( " successful transfers=" f"{len(row['successful_transfer_config_ids'])}/" f"{row['transfer_target_count']}" ) failures.append(detail) raise exc.AutotuneError( "required CuTe flash structural qualification did not complete: " + "; ".join(failures) ) if phase is not None: # A complete qualification's lanes carry their metrics; the lanes # the conditional search exhausted (no unmeasured candidate within # reach of a measured parent) are logged once here. leaf_rows = phase.get("leaf_results", []) assert isinstance(leaf_rows, list) exhausted_lanes = [] for row in leaf_rows: lanes = row["pipeline_lanes"] assert isinstance(lanes, list) exhausted_lanes.extend( f"{row['family']}:{lane['key']}={lane['value']!r}" for lane in lanes if lane["conditional_exhausted"] ) if exhausted_lanes: self.log( "Structural qualification: no unmeasured conditional candidate " "is reachable in " f"{', '.join(exhausted_lanes)}; the lane(s) count as exhausted." ) starting_paths = self._select_starting_paths() starting_points = [member for member, _constraints in starting_paths] self.log( f"Qualified population of {len(self.population)}, " f"{len(starting_points)} retained search paths:", self.statistics, ) if not starting_points: raise exc.NoConfigFound if self._autotune_metrics.search_phase_metrics is not None: self._autotune_metrics.search_phase_metrics["retained_path_count"] = len( starting_paths ) search_copies = [] for idx, (member, constraints) in enumerate(starting_paths): required_leaf = self._flash_structural_leaf(member) if constraints else None # A retained leaf owns a conditional field set. Generating from the # global union mostly produces inactive aliases and silently spends # this path's bounded generations without exploring its children. search_copies.append( self._pruned_pattern_search_from( idx, member, visited, constraints, required_leaf=required_leaf, conditional_surface=required_leaf is not None, disable_early_stopping=self._path_exhausts_generation_budget( constraints ), ) ) for generation in self._budgeted_range( first_main_generation, self.max_generations + 1 ): prior_best = self.best new_population = {id(prior_best): prior_best} num_neighbors = 0 num_active = 0 for search_copy in search_copies: added = next(search_copy, ()) if added: assert len(added) > 1 num_active += 1 num_neighbors += len(added) - 1 for member in added: new_population[id(member)] = member if num_active == 0: self.log( f"Autotuning stop at generation {generation} because of no active search path" ) break # Log generation header before compiling/benchmarking self.log( f"Generation {generation} starting: {num_neighbors} neighbors, {num_active} active search path(s)" ) self.population = [*new_population.values()] # compile any unbenchmarked members in parallel unbenchmarked = [m for m in self.population if len(m.perfs) == 0] if unbenchmarked: self.set_generation(generation) self.benchmark_population( unbenchmarked, desc=f"Generation {generation}:" ) # higher-accuracy rebenchmark self.rebenchmark_population( self.population, desc=f"Generation {generation}: verifying top configs" ) # Log final statistics for this generation self.log(f"Generation {generation} complete:", self.statistics) # no need to retrain the model for the last generation if generation != self.max_generations: # Update training data with newly benchmarked members only for member in unbenchmarked: self._append_training_sample( self.config_gen.encode_config(member.flat_values), member.perf, member.config, member.fn, member=member, ) # Fit model self._fit_surrogate() self._polish_descent(visited) # Final verification, finishing phase, and (TPU-only) final-pick re-rank. return self._finalize() def _polish_descent(self, visited: set[Config]) -> None: """Full-neighborhood descent after the surrogate-guided main loop. Run plain pattern-search descent from the incumbent: benchmark the *entire* deterministic radius-1 neighborhood (no surrogate pruning) and move to the best neighbor until a round fails to improve, up to ``polish_rounds`` rounds. Recovers local wins the surrogate's selection fraction skipped, at a bounded extra eval cost. """ rounds = self.polish_rounds if rounds <= 0 or self.config_spec.cute_flash_search_enabled: return current = self.best for round_num in self._budgeted_range(1, rounds + 1): candidates = [current] for flat_config in PatternSearch._generate_neighbors( self, current.flat_values ): member = self.make_unbenchmarked(flat_config) if member is not None and member.config not in visited: visited.add(member.config) candidates.append(member) if len(candidates) <= 1: self.log(f"Polish round {round_num}: no unvisited neighbors") break self.set_generation(self._autotune_metrics.num_generations + 1) self.benchmark_population(candidates[1:], desc=f"Polish round {round_num}") self.rebenchmark_population( candidates, desc=f"Polish round {round_num}: verifying" ) self.population.extend(candidates[1:]) best = min(candidates, key=performance) self.log( f"Polish round {round_num}: {len(candidates) - 1} neighbors, " f"best {self.format_performance(best.perf)}" ) if self._check_early_stopping(best, current): break current = best def _flash_generation( self, generation: ConfigGeneration | None = None ) -> CuteConfigGenerationExtension: """The CuTe generation extension the flash lane policy reads (the policy runs only under the CuTe flash surface).""" return cast( "CuteConfigGenerationExtension", (self.config_gen if generation is None else generation).extension, ) @staticmethod def _flash_structural_leaf( member: PopulationMember, ) -> FlashStructuralLeaf | None: from .._compiler.cute.cute_flash import flash_structural_leaf_from_config return flash_structural_leaf_from_config(member.config.config) @staticmethod def _flash_leaf_constraints( leaf: FlashStructuralLeaf, ) -> tuple[tuple[str, object], ...]: from .._compiler.cute.cute_flash import FLASH_EXP2_PACKET_KEY from .._compiler.cute.cute_flash import FLASH_PIPELINE_FAMILY_KEY from .._compiler.cute.cute_flash import FLASH_SOFTMAX_DISC_KEY constraints: list[tuple[str, object]] = [ (FLASH_PIPELINE_FAMILY_KEY, leaf.pipeline_family), (FLASH_SOFTMAX_DISC_KEY, leaf.softmax_disc), ] if leaf.compound_exp2_packet is not None: constraints.append((FLASH_EXP2_PACKET_KEY, leaf.compound_exp2_packet)) return tuple(constraints) @staticmethod def _flash_member_rank_key(member: PopulationMember) -> tuple[float, str]: """Order equal-performing flash configs reproducibly.""" return performance(member), canonical_config_id(member.config) @staticmethod def _flash_member_succeeded(member: PopulationMember) -> bool: return ( member.status in {"ok", "deduplicated"} and bool(member.perfs) and math.isfinite(member.perfs[0]) and math.isfinite(member.perf) ) @staticmethod def _flash_member_has_retryable_failure(member: PopulationMember) -> bool: """Return whether a structural alternate may repair this failed attempt.""" return member.status in {"error", "timeout", "peer_compilation_fail"} def _flash_member_source_hash(self, member: PopulationMember) -> str | None: """Return the effective generated-source identity for one flash member.""" if member.fn is _unset_fn and member.status == "error": return _compile_config_failure_source_hash(member.config) return self.config_spec.backend.generated_source_hash(member.fn) @staticmethod def _flash_clc_combination_statuses_allowed( cells: Sequence[Mapping[str, object]], ) -> bool: """Reject correctness and policy failures hidden by marginal coverage.""" return all( flash_terminal_measurement_is_valid(cell, allow_projection_rejected=True) for cell in cells ) @staticmethod def _flash_pipeline_qualification_keys() -> tuple[str, str]: from .._compiler.cute.cute_flash import FLASH_KV_STAGE_KEY from .._compiler.cute.cute_flash import FLASH_S_STAGE_KEY # KV depth controls the long producer pipeline and is the primary axis. # S depth is still qualified when it has a live family-conditional choice. return FLASH_KV_STAGE_KEY, FLASH_S_STAGE_KEY def _flash_pipeline_lanes( self, leaf: FlashStructuralLeaf, ) -> tuple[tuple[str, object], ...]: """Return ConfigGeneration's exact normalized depth catalog for ``leaf``.""" return self._flash_generation().flash_pipeline_lane_catalog().get(leaf, ()) @staticmethod def _flash_member_matches_pipeline_lane( member: PopulationMember, lane: tuple[str, object] ) -> bool: key, value = lane return member.config.config.get(key) == value @staticmethod def _flash_pipeline_lane_metric( lane: tuple[str, object] | None, ) -> dict[str, object] | None: if lane is None: return None return {"key": lane[0], "value": lane[1]} def _flash_lane_diverse_members( self, members: Sequence[PopulationMember], lanes: Sequence[tuple[str, object]], limit: int, ) -> list[tuple[PopulationMember, tuple[str, object] | None]]: """Retain fast members while covering primary depth values first.""" remaining = sorted(members, key=self._flash_member_rank_key) if limit <= 0 or not remaining: return [] selected: list[tuple[PopulationMember, tuple[str, object] | None]] = [ (remaining.pop(0), None) ] covered = { lane for lane in lanes if self._flash_member_matches_pipeline_lane(selected[0][0], lane) } key_order = self._flash_pipeline_qualification_keys() while remaining and len(selected) < limit: def rank(member: PopulationMember) -> tuple[object, ...]: newly_covered = { lane for lane in lanes if lane not in covered and self._flash_member_matches_pipeline_lane(member, lane) } coverage_by_key = tuple( -sum(lane[0] == key for lane in newly_covered) for key in key_order ) return ( *coverage_by_key, self._flash_member_rank_key(member), ) member = min(remaining, key=rank) remaining.remove(member) newly_covered = [ lane for lane in lanes if lane not in covered and self._flash_member_matches_pipeline_lane(member, lane) ] assigned_lane = newly_covered[0] if newly_covered else None selected.append((member, assigned_lane)) covered.update( lane for lane in lanes if self._flash_member_matches_pipeline_lane(member, lane) ) return selected def _flash_pipeline_lane_witness( self, leaf: FlashStructuralLeaf, lane: tuple[str, object], ) -> PopulationMember | None: """Create the deterministic normalized candidate for a missing lane.""" config = ( self._flash_generation() .flash_pipeline_lane_witnesses() .get((leaf, lane[0], lane[1])) ) if config is None: return None global_flat = self.config_gen.flatten(config) global_flat, global_config = self.config_gen.canonicalize_flat(global_flat) assert self._flash_structural_leaf_from_config(global_config) == leaf assert global_config.config.get(lane[0]) == lane[1] return self.make_unbenchmarked(global_flat) def _flash_clc_lane_witness( self, leaf: FlashStructuralLeaf, value: int, ) -> PopulationMember | None: """Create the deterministic normalized candidate for a CLC divisor.""" config = self._flash_generation().flash_clc_lane_witnesses().get((leaf, value)) if config is None: return None global_flat = self.config_gen.flatten(config) global_flat, global_config = self.config_gen.canonicalize_flat(global_flat) assert self._flash_structural_leaf_from_config(global_config) == leaf from .._compiler.cute.cute_flash import FLASH_CLC_HEADS_PER_BATCH_KEY assert global_config.config.get(FLASH_CLC_HEADS_PER_BATCH_KEY) == value return self.make_unbenchmarked(global_flat) def _flash_config_variant( self, member: PopulationMember, overrides: Mapping[str, object], *, expected_leaf: FlashStructuralLeaf, ) -> PopulationMember | None: """Normalize a structural transfer while preserving the source fields.""" config = copy.deepcopy(member.config) config.config.update(overrides) try: global_flat = self.config_gen.flatten(config) global_flat, global_config = self.config_gen.canonicalize_flat(global_flat) except exc.InvalidConfig: return None if self._flash_structural_leaf_from_config(global_config) != expected_leaf: return None return self.make_unbenchmarked(global_flat) def _flash_pipeline_values_survive( self, source: PopulationMember, candidate: PopulationMember, ) -> bool: """Return whether normalization preserved the source pipeline depths.""" return all( key not in source.config.config or candidate.config.config.get(key) == source.config.config[key] for key in self._flash_pipeline_qualification_keys() ) def _flash_clc_depth_variant( self, depth_member: PopulationMember, value: int, *, expected_leaf: FlashStructuralLeaf, ) -> PopulationMember | None: """Apply a CLC divisor without changing the selected depth schedule.""" from .._compiler.cute.cute_flash import FLASH_CLC_HEADS_PER_BATCH_KEY candidate = self._flash_config_variant( depth_member, {FLASH_CLC_HEADS_PER_BATCH_KEY: value}, expected_leaf=expected_leaf, ) if ( candidate is None or candidate.config.config.get(FLASH_CLC_HEADS_PER_BATCH_KEY) != value or not self._flash_pipeline_values_survive(depth_member, candidate) ): return None return candidate def _flash_compound_variant( self, source: PopulationMember, packet: object, *, expected_leaf: FlashStructuralLeaf, ) -> PopulationMember | None: """Apply a compound packet only when source pipeline depths survive.""" from .._compiler.cute.cute_flash import FLASH_EXP2_PACKET_KEY candidate = self._flash_config_variant( source, {FLASH_EXP2_PACKET_KEY: packet}, expected_leaf=expected_leaf, ) if candidate is None or not self._flash_pipeline_values_survive( source, candidate ): return None return candidate @staticmethod def _flash_lane_qualification_passes( lanes: Sequence[tuple[str, object]], *, candidate_limit: int, conditional_candidates_per_lane: int, minimum_passes: int, conditional_lanes: Sequence[tuple[str, object]] | None = None, ) -> list[list[tuple[str, tuple[str, object] | None]]]: """Build dependency-ordered witness and conditional qualification passes.""" if candidate_limit <= 0: raise ValueError("candidate_limit must be positive") if conditional_candidates_per_lane < 0: raise ValueError("conditional_candidates_per_lane must be nonnegative") if not lanes: return [[("ordinary", None)] for _ in range(minimum_passes)] passes: list[list[tuple[str, tuple[str, object] | None]]] = [] witness_jobs: list[tuple[str, tuple[str, object] | None]] = [ ("witness", lane) for lane in lanes ] conditional_jobs: list[tuple[str, tuple[str, object] | None]] = [ ("conditional", lane) for _ in range(conditional_candidates_per_lane) for lane in lanes if conditional_lanes is None or lane in conditional_lanes ] for jobs in (witness_jobs, conditional_jobs): passes.extend( jobs[offset : offset + candidate_limit] for offset in range(0, len(jobs), candidate_limit) ) passes.extend([] for _ in range(max(0, minimum_passes - len(passes)))) return passes @staticmethod def _flash_lane_neighbor_limits( quotas: Sequence[tuple[tuple[str, object] | None, int]], total_neighbors: int, ) -> list[int]: """Divide one historical neighbor-generation budget across lanes.""" total_quota = sum(quota for _lane, quota in quotas) if total_quota <= 0: return [0] * len(quotas) limits: list[int] = [] cumulative = 0 for _lane, quota in quotas: start = cumulative * total_neighbors // total_quota cumulative += quota limits.append(cumulative * total_neighbors // total_quota - start) return limits def _flash_qualification_neighbor_limit(self) -> int: """Return the one effective raw-neighbor budget shared by a leaf.""" if self.num_neighbors_cap > 0: return min(self.num_neighbors, self.num_neighbors_cap) return self.num_neighbors def _flash_family_probe_paths( self, population: Sequence[PopulationMember] ) -> list[ tuple[ PopulationMember, FlashStructuralLeaf | None, tuple[tuple[str, object], ...], bool, ] ]: """Select one measured probe start per family/compound leaf and globally.""" eligible = [ member for member in population if self._flash_member_succeeded(member) and self._flash_structural_leaf(member) is not None ] if not eligible: return [] ordinary_by_family: dict[ str, list[tuple[PopulationMember, FlashStructuralLeaf]] ] = {} compound_by_leaf: dict[FlashStructuralLeaf, list[PopulationMember]] = {} probe_eligible: list[PopulationMember] = [] qualified_compound_config_ids = getattr( self, "_flash_qualified_compound_config_ids", {} ) for member in eligible: leaf = self._flash_structural_leaf(member) assert leaf is not None if leaf.compound_exp2_packet is None: probe_eligible.append(member) ordinary_by_family.setdefault(leaf.pipeline_family, []).append( (member, leaf) ) elif canonical_config_id( member.config ) in qualified_compound_config_ids.get(leaf, set()): probe_eligible.append(member) compound_by_leaf.setdefault(leaf, []).append(member) paths: list[ tuple[ PopulationMember, FlashStructuralLeaf | None, tuple[tuple[str, object], ...], bool, ] ] = [] family_starts = [ min(members, key=lambda item: self._flash_member_rank_key(item[0])) for members in ordinary_by_family.values() ] for member, leaf in sorted( family_starts, key=lambda item: ( self._flash_member_rank_key(item[0]), item[1].pipeline_family, ), ): paths.append((member, leaf, self._flash_leaf_constraints(leaf), False)) for leaf, members in sorted( compound_by_leaf.items(), key=lambda item: ( self._flash_member_rank_key( min(item[1], key=self._flash_member_rank_key) ), item[0].pipeline_family, item[0].compound_exp2_packet or "", item[0].softmax_disc, ), ): member = min(members, key=self._flash_member_rank_key) paths.append((member, leaf, self._flash_leaf_constraints(leaf), False)) if not probe_eligible: return [] global_best = min(probe_eligible, key=self._flash_member_rank_key) paths.append((global_best, None, (), True)) return paths def _run_flash_structural_qualification( self, visited: set[Config], *, initial_population: Sequence[PopulationMember] | None = None, ) -> int: """Qualify ordinary schedules, then transfer their best representatives. The phase lives in ``FlashStructuralQualification``; this seam stays a method so a search can be built with it mocked out. """ from .._compiler.cute.autotune.flash_search import FlashStructuralQualification return FlashStructuralQualification( self, visited, initial_population=initial_population ).run() def _path_exhausts_generation_budget( self, constraints: tuple[tuple[str, object], ...] ) -> bool: """Keep the full-search winner path alive through the bounded budget.""" policy = getattr(self, "flash_structural_search", None) return bool( self.config_spec.cute_flash_search_enabled and policy is not None and policy.exhaust_unrestricted_path and not constraints ) def _select_starting_paths( self, ) -> list[tuple[PopulationMember, tuple[tuple[str, object], ...]]]: """Select starts and structural values each qualification path must retain.""" if not self.config_spec.cute_flash_search_enabled: return [ (member, ()) for member in self.population[: self.copies] if math.isfinite(member.perf) ] eligible = [member for member in self.population if math.isfinite(member.perf)] selection_limit = getattr(self, "_flash_promoted_path_limit", self.copies) if selection_limit <= 1: return [(member, ()) for member in eligible[:selection_limit]] policy = getattr(self, "flash_structural_search", None) if policy is not None: path_limit = selection_limit by_leaf: dict[FlashStructuralLeaf, list[PopulationMember]] = {} qualified_compound_config_ids = getattr( self, "_flash_qualified_compound_config_ids", None ) for member in eligible: if not self._flash_member_succeeded(member): continue leaf = self._flash_structural_leaf(member) if ( leaf is not None and leaf.compound_exp2_packet is not None and qualified_compound_config_ids is not None and canonical_config_id(member.config) not in qualified_compound_config_ids.get(leaf, set()) ): continue if leaf is not None: by_leaf.setdefault(leaf, []).append(member) for members in by_leaf.values(): members.sort(key=self._flash_member_rank_key) qualified_pipeline_lanes = getattr( self, "_flash_qualified_pipeline_lanes", {} ) leaf_pipeline_lanes = { leaf: ( qualified_pipeline_lanes[leaf] if leaf in qualified_pipeline_lanes else self._flash_pipeline_lanes(leaf) ) for leaf in by_leaf } retained_by_leaf = { leaf: self._flash_lane_diverse_members( members, leaf_pipeline_lanes[leaf], policy.retained_candidates_per_leaf, ) for leaf, members in by_leaf.items() } family_queues: dict[ str, list[ tuple[ PopulationMember, FlashStructuralLeaf, tuple[str, object] | None, ] ], ] = {} family_score_entries: dict[ str, tuple[PopulationMember, FlashStructuralLeaf] ] = {} parent_score_families: set[str] = set() parent_score_config_ids = getattr( self, "_flash_parent_score_config_ids", None ) families = list( dict.fromkeys(leaf.pipeline_family for leaf in by_leaf if by_leaf[leaf]) ) for family in families: leaves = [ leaf for leaf in by_leaf if leaf.pipeline_family == family and by_leaf[leaf] ] queue: list[ tuple[ PopulationMember, FlashStructuralLeaf, tuple[str, object] | None, ] ] = [] for rank in range(policy.retained_candidates_per_leaf): layer = [ (*retained_by_leaf[leaf][rank], leaf) for leaf in leaves if rank < len(retained_by_leaf[leaf]) ] queue.extend( sorted( ((member, leaf, lane) for member, lane, leaf in layer), key=lambda item: ( performance(item[0]), item[1].compound_exp2_packet or "", item[1].softmax_disc, canonical_config_id(item[0].config), ), ) ) if queue: family_queues[family] = queue ordinary: list[tuple[PopulationMember, FlashStructuralLeaf]] = [] for leaf in leaves: if leaf.compound_exp2_packet is not None: continue score_members = [ member for member in by_leaf[leaf] if parent_score_config_ids is None or canonical_config_id(member.config) in parent_score_config_ids ] if score_members: ordinary.append((score_members[0], leaf)) if ordinary: parent_score_families.add(family) family_score_entries[family] = min( ordinary or [(queue[0][0], queue[0][1])], key=lambda item: ( performance(item[0]), canonical_config_id(item[0].config), ), ) ranked_families = sorted( parent_score_families, key=lambda family: ( performance(family_score_entries[family][0]), family, ), ) if not family_queues: phase = getattr( getattr(self, "_autotune_metrics", None), "search_phase_metrics", None, ) if phase is not None: phase["retained_families"] = [] return [(member, ()) for member in eligible[:1]] # Score every parent by its ordinary leaf. Compound packets receive # only transferred ordinary representatives and can earn a leaf path # below, but a family with more packet variants gets no # multiple-comparison advantage during parent promotion. competitive_families: list[str] = [] if policy.retained_families is None: # Full effort continues every qualified live family. A family # with a weak pipeline-only witness may still own the best # arithmetic settings, so qualification rank cannot prune it. competitive_families = ranked_families elif ranked_families: best_family_perf = performance( family_score_entries[ranked_families[0]][0] ) competitive_families = [ family for family in ranked_families if performance(family_score_entries[family][0]) <= best_family_perf * policy.retained_family_slowdown_limit ] best_leaf_entry = min( ((by_leaf[leaf][0], leaf) for leaf in by_leaf if by_leaf[leaf]), key=lambda item: ( performance(item[0]), item[1].pipeline_family, item[1].compound_exp2_packet or "", item[1].softmax_disc, canonical_config_id(item[0].config), ), ) best_member, best_leaf = best_leaf_entry best_family = best_leaf.pipeline_family alternate_leaf_order = [best_leaf] family_score_leaf = family_score_entries[best_family][1] if family_score_leaf != best_leaf: alternate_leaf_order.append(family_score_leaf) alternate_leaf_order.extend( leaf for leaf in sorted( ( leaf for leaf in by_leaf if leaf.pipeline_family == best_family and leaf not in alternate_leaf_order ), key=lambda leaf: self._flash_member_rank_key(by_leaf[leaf][0]), ) ) best_lane_alternate = next( ( (member, leaf, lane) for leaf in alternate_leaf_order for member, lane in retained_by_leaf[leaf] if member.config != best_member.config and lane is not None ), None, ) # Reserve one path for the unrestricted winner. It is appended last # so its global neighbor generation cannot consume candidates before # the constrained structural paths have selected theirs. constrained_limit = max(0, path_limit - 1) retained_parent_families = competitive_families[ : min( len(competitive_families) if policy.retained_families is None else policy.retained_families, constrained_limit, ) ] selected_leaf_paths: list[ tuple[ PopulationMember, FlashStructuralLeaf, bool, tuple[str, object] | None, ] ] = [] for family in retained_parent_families: member, leaf = family_score_entries[family] if len(selected_leaf_paths) >= constrained_limit: break selected_leaf_paths.append((member, leaf, False, None)) selected_configs = { member.config for member, _leaf, _unrestricted, _lane in selected_leaf_paths } selected_leaves = { leaf for _member, leaf, _unrestricted, _lane in selected_leaf_paths } # Retain every ordinary protocol in the promoted families before # spending a path on a compound packet. ordinary_leaf_candidates = sorted( ( (by_leaf[leaf][0], leaf) for family in retained_parent_families for leaf in by_leaf if leaf.pipeline_family == family and leaf.compound_exp2_packet is None and leaf not in selected_leaves ), key=lambda item: ( performance(item[0]), item[1].pipeline_family, item[1].softmax_disc, canonical_config_id(item[0].config), ), ) for member, leaf in ordinary_leaf_candidates: if len(selected_leaf_paths) >= constrained_limit: break if member.config in selected_configs: continue selected_leaf_paths.append((member, leaf, False, None)) selected_configs.add(member.config) selected_leaves.add(leaf) # Keep one measured alternate pipeline lane for the global winner's # family. This reservation precedes all compound continuations. if ( best_lane_alternate is not None and best_family in retained_parent_families and len(selected_leaf_paths) < constrained_limit ): alternate_member, alternate_leaf, alternate_lane = best_lane_alternate if alternate_member.config not in selected_configs: selected_leaf_paths.append( (alternate_member, alternate_leaf, False, alternate_lane) ) selected_configs.add(alternate_member.config) selected_leaves.add(alternate_leaf) # Qualification ranking is noisy enough that the measured leading # family may not own the best arithmetic basin. Give every promoted # family one available ordinary secondary before allocating paths to # compound schedules. The global-family lane alternate above counts # when it is already a secondary for its ordinary leaf. families_with_ordinary_secondary = { leaf.pipeline_family for member, leaf, _unrestricted, _lane in selected_leaf_paths if leaf.compound_exp2_packet is None and member.config != by_leaf[leaf][0].config } for family in retained_parent_families: if ( len(selected_leaf_paths) >= constrained_limit or family in families_with_ordinary_secondary ): continue secondary = next( ( (member, leaf, lane) for member, leaf, lane in family_queues[family] if leaf.compound_exp2_packet is None and member.config != by_leaf[leaf][0].config and member.config not in selected_configs ), None, ) if secondary is None: continue member, leaf, lane = secondary selected_leaf_paths.append((member, leaf, False, lane)) selected_configs.add(member.config) selected_leaves.add(leaf) families_with_ordinary_secondary.add(family) compound_leaf_candidates = sorted( ( (by_leaf[leaf][0], leaf) for leaf in by_leaf if leaf.compound_exp2_packet is not None and leaf not in selected_leaves ), key=lambda item: ( performance(item[0]), item[1].pipeline_family, item[1].compound_exp2_packet or "", item[1].softmax_disc, canonical_config_id(item[0].config), ), ) for member, leaf in compound_leaf_candidates: if len(selected_leaf_paths) >= constrained_limit: break if member.config in selected_configs: continue selected_leaf_paths.append((member, leaf, False, None)) selected_configs.add(member.config) selected_leaves.add(leaf) queue_offsets = dict.fromkeys(retained_parent_families, 0) while len(selected_leaf_paths) < constrained_limit: added = False for family in retained_parent_families: queue = family_queues[family] offset = queue_offsets[family] while ( offset < len(queue) and queue[offset][0].config in selected_configs ): offset += 1 queue_offsets[family] = offset if offset >= len(queue): continue item = queue[offset] queue_offsets[family] += 1 member, leaf, lane = item selected_leaf_paths.append((member, leaf, False, lane)) selected_configs.add(item[0].config) added = True if len(selected_leaf_paths) >= constrained_limit: break if not added: break if path_limit > 0: selected_leaf_paths.append((best_member, best_leaf, True, None)) paths = [ ( member, () if unrestricted else ( *self._flash_leaf_constraints(leaf), *((lane,) if lane is not None else ()), ), ) for member, leaf, unrestricted, lane in selected_leaf_paths ] metrics = getattr(self, "_autotune_metrics", None) phase = ( getattr(metrics, "search_phase_metrics", None) if metrics is not None else None ) if phase is not None: reported_families = list( dict.fromkeys( ( best_family, *retained_parent_families, *( leaf.pipeline_family for _member, leaf, _unrestricted, _lane in selected_leaf_paths ), ) ) ) phase["retained_families"] = [ { "family": family, "score": family_score_entries[family][0].perf, "score_compound_packet": family_score_entries[family][ 1 ].compound_exp2_packet, "score_softmax_disc": family_score_entries[family][ 1 ].softmax_disc, "parent_promoted": family in retained_parent_families, "starting_paths": [ { "family": leaf.pipeline_family, "compound_packet": leaf.compound_exp2_packet, "softmax_disc": leaf.softmax_disc, "config_id": canonical_config_id(member.config), "unrestricted": unrestricted, "pipeline_lane": self._flash_pipeline_lane_metric(lane), } for member, leaf, unrestricted, lane in selected_leaf_paths if leaf.pipeline_family == family ], } for family in reported_families ] return paths selected: list[tuple[PopulationMember, tuple[tuple[str, object], ...]]] = [] selected_configs: set[Config] = set() if eligible: selected.append((eligible[0], ())) selected_configs.add(eligible[0].config) for path in sorted( self._flash_structural_paths(), key=lambda item: self._flash_member_rank_key(item[0]), ): if len(selected) >= self.copies: break if path[0].config in selected_configs: continue selected.append(path) selected_configs.add(path[0].config) for member in eligible: if len(selected) >= self.copies: break if member.config in selected_configs: continue selected.append((member, ())) selected_configs.add(member.config) return selected def _flash_structural_paths( self, *, include_nonfinite: bool = False, ) -> list[tuple[PopulationMember, tuple[tuple[str, object], ...]]]: """Return one measured representative for each live structure. Qualification may start from a failed witness because a nearby child configuration can still be valid. Long-running paths remain finite-only. """ if not self.config_spec.cute_flash_search_enabled: return [] eligible = ( self.population if include_nonfinite else [member for member in self.population if math.isfinite(member.perf)] ) by_leaf: dict[FlashStructuralLeaf, PopulationMember] = {} for member in eligible: leaf = self._flash_structural_leaf(member) if leaf is not None and ( leaf not in by_leaf or self._flash_member_rank_key(member) < self._flash_member_rank_key(by_leaf[leaf]) ): by_leaf[leaf] = member catalog = self._flash_generation().flash_structural_leaf_catalog() return [ (by_leaf[leaf], self._flash_leaf_constraints(leaf)) for leaf in catalog if leaf in by_leaf ] def _generate_neighbors( self, base: FlatConfig, *, fixed_flat_values: Mapping[int, object] | None = None, config_gen: ConfigGeneration | None = None, num_neighbors: int | None = None, proposal_callback: Callable[[FlatConfig], None] | None = None, ) -> list[FlatConfig]: """ Generate neighboring configurations randomly within a specified radius. Strategy: 1. Sample one block size index and change it by at most radius (in log2 space) 2. Sample the num_warps index and change it by at most radius (in log2 space) 3. For at most radius remaining indices, randomly select pattern neighbors Args: base: The base configuration to generate neighbors from Returns: A list of neighboring configurations """ neighbors: list[FlatConfig] = [] config_gen = self.config_gen if config_gen is None else config_gen # Generate num_neighbors random neighbors frozen = set(config_gen.overridden_flat_indices) if fixed_flat_values: frozen.update(fixed_flat_values) eligible_block = [i for i in config_gen.block_size_indices if i not in frozen] warp_idx = config_gen.num_warps_index tune_warps = warp_idx >= 0 and warp_idx not in frozen for _ in range(self.num_neighbors if num_neighbors is None else num_neighbors): new_flat = [*base] # Copy the base configuration modified_indices = set() # 1. Sample a block size index and change it if eligible_block: block_idx = random.choice(eligible_block) modified_indices.add(block_idx) block_spec = config_gen.flat_spec[block_idx] block_neighbors = block_spec.pattern_neighbors( base[block_idx], self.radius ) if block_neighbors: new_flat[block_idx] = random.choice(block_neighbors) # 2. Sample the num_warps index and change it if tune_warps: modified_indices.add(warp_idx) warp_spec = config_gen.flat_spec[warp_idx] warp_neighbors = warp_spec.pattern_neighbors( base[warp_idx], self.radius ) if warp_neighbors: new_flat[warp_idx] = random.choice(warp_neighbors) # 3. For at most radius remaining indices, use pattern neighbors # Exclude the already-modified block size and warp indices # Collect available pattern neighbors for remaining indices remaining_pattern_neighbors = [] for index, spec in enumerate(config_gen.flat_spec): if index not in modified_indices and index not in frozen: pattern_neighbors = spec.pattern_neighbors(base[index]) if pattern_neighbors: remaining_pattern_neighbors.append((index, pattern_neighbors)) # Randomly select at most radius indices to change if remaining_pattern_neighbors: num_to_change = random.randint( 0, min(self.radius, len(remaining_pattern_neighbors)) ) if num_to_change > 0: indices_to_change = random.sample( remaining_pattern_neighbors, num_to_change ) for idx, pattern_neighbors in indices_to_change: new_flat[idx] = random.choice(pattern_neighbors) if proposal_callback is not None: proposal_callback(new_flat) # Only add if it's different from the base if new_flat != base: neighbors.append(new_flat) return self.shrink_neighbors(neighbors) def _flash_leaf_config_generation( self, leaf: FlashStructuralLeaf, lane_constraints: tuple[tuple[str, object], ...] = (), ) -> ConfigGeneration | None: if not hasattr(self.config_spec, "create_config_generation"): return None cache = getattr(self, "_flash_leaf_config_generation_cache", None) if cache is None: cache = {} self._flash_leaf_config_generation_cache = cache cache_key = (leaf, lane_constraints) if cache_key in cache: return cache[cache_key] from .._compiler.cute.cute_flash import FLASH_EXP2_PACKET_KEY from .._compiler.cute.cute_flash import FLASH_PIPELINE_FAMILY_KEY from .._compiler.cute.cute_flash import FLASH_SOFTMAX_DISC_KEY overrides = dict(self.config_gen._override_values) overrides[FLASH_PIPELINE_FAMILY_KEY] = leaf.pipeline_family overrides[FLASH_SOFTMAX_DISC_KEY] = leaf.softmax_disc if leaf.compound_exp2_packet is not None: overrides[FLASH_EXP2_PACKET_KEY] = leaf.compound_exp2_packet overrides.update(lane_constraints) config_gen = self.config_spec.create_config_generation( overrides=overrides, advanced_controls_files=self.config_gen._advanced_controls_files, process_group_name=self.config_gen.process_group_name, ) cache[cache_key] = config_gen return config_gen @staticmethod def _flash_terminal_projection_metric( projection: CoordinateNeighborProjection, *, outcome: str, ) -> dict[str, object]: return { "flat_index": projection.flat_index, "key": projection.key, "sequence_index": projection.sequence_index, "from_value": copy.deepcopy(projection.from_value), "to_value": copy.deepcopy(projection.to_value), "outcome": outcome, "config_id": ( canonical_config_id(projection.config) if projection.config is not None else None ), } def _flash_terminal_member_result( self, member: PopulationMember ) -> dict[str, object]: succeeded = self._flash_member_succeeded(member) return { "config_id": canonical_config_id(member.config), "attempt_perf": ( member.perfs[0] if member.perfs and math.isfinite(member.perfs[0]) else None ), "selection_perf": member.perf if succeeded else None, "status": member.status, "source_hash": self._flash_member_source_hash(member), } @staticmethod def _flash_terminal_trace_metric( member_ids: Sequence[str], trace: MirroredBenchmarkTrace ) -> dict[str, object]: return { "base_order": list(member_ids), "target_ms": trace.target_ms, "repeat_reference_perf_ms": trace.repeat_reference_perf_ms, "sweep_count": trace.sweep_count, "calls_per_sample": trace.calls_per_sample, "total_calls": trace.total_calls, "elapsed_ms": [list(times) for times in trace.elapsed_ms], "median_ms": [ {"config_id": config_id, "value": timing} for config_id, timing in zip( member_ids, trace.medians_ms, strict=True, ) ], }
[docs] def run_terminal_refinement(self, best: PopulationMember) -> PopulationMember: """Close the final CuTe-flash basin with a deterministic coordinate beam.""" policy = getattr(self, "flash_structural_search", None) phase = getattr( getattr(self, "_autotune_metrics", None), "search_phase_metrics", None, ) if ( not getattr(self, "_cute_flash_lane_policy_enabled", False) or policy is None or policy.terminal_coordinate_rounds <= 0 or policy.terminal_coordinate_beam_width <= 0 or not isinstance(phase, dict) or self.performance_unit != "ms" or self.settings.autotune_benchmark_fn is not None or self.settings.autotune_budget_seconds is not None or isinstance( getattr(self, "benchmark_provider", None), MultiShapeBenchmarkProvider, ) ): return best search_generation = self._autotune_metrics.num_generations initial_config_id = canonical_config_id(best.config) config_manifest: dict[str, dict[str, object]] = {} unique_candidate_ids: set[str] = set() new_candidate_ids: set[str] = set() reused_candidate_ids: set[str] = set() intra_terminal_reused_candidate_ids: set[str] = set() prior_failed_candidate_ids: set[str] = set() projection_attempt_count = 0 projection_parent_count = 0 transcript: dict[str, object] = { "schema_version": _FLASH_TERMINAL_REFINEMENT_SCHEMA_VERSION, "policy_version": _FLASH_TERMINAL_REFINEMENT_POLICY_VERSION, "lane_policy_version": CUTE_FLASH_LANE_POLICY_VERSION, "coordinate_policy": _FLASH_TERMINAL_COORDINATE_POLICY, "measurement_policy": _FLASH_TERMINAL_MEASUREMENT_POLICY, "rounds_planned": policy.terminal_coordinate_rounds, "beam_width": policy.terminal_coordinate_beam_width, "maximum_projection_parent_count": 1 + policy.terminal_coordinate_beam_width * max(policy.terminal_coordinate_rounds - 1, 0), "projection_parent_count": 0, "rounds_started": 0, "rounds_completed": 0, "completed": False, "budget_exhausted": False, "termination_reason": None, "search_generation": search_generation, "preterminal_num_configs_tested": getattr( self._autotune_metrics, "num_configs_tested", 0, ), "preterminal_registry_config_count": 0, "preterminal_registry_config_ids_hash_policy": ( "sorted_compact_json_sha256_v1" ), "preterminal_registry_config_ids_sha256": None, "radius": self.radius, "minimum_improvement_fraction": self.min_improvement_delta, "initial_incumbent_config_id": initial_config_id, "refined_config_id": initial_config_id, "final_config_id": initial_config_id, "projection_attempt_count": 0, "unique_candidate_count": 0, "new_candidate_count": 0, "reused_candidate_count": 0, "intra_terminal_reused_candidate_count": 0, "prior_failed_candidate_count": 0, "accepted_config_ids": [], "config_manifest_sha256": None, "config_manifest": config_manifest, "rounds": [], "confirmation": None, } phase["terminal_coordinate_refinement"] = transcript registry = getattr(self, "_terminal_refinement_members", None) if registry is None: registry = {} self._terminal_refinement_members = registry self._record_best_member_for_config( registry, best.config, best, replace=True, ) preterminal_registry_config_ids = sorted( canonical_config_id(config) for config in registry ) preterminal_configs = set(registry) transcript["preterminal_registry_config_count"] = len( preterminal_registry_config_ids ) transcript["preterminal_registry_config_ids_sha256"] = hashlib.sha256( json.dumps( preterminal_registry_config_ids, separators=(",", ":"), ).encode() ).hexdigest() def add_manifest_config(member_config: Config) -> None: config_id = canonical_config_id(member_config) config_manifest[config_id] = {"config": copy.deepcopy(member_config.config)} def finish_transcript(current: PopulationMember) -> PopulationMember: transcript["final_config_id"] = canonical_config_id(current.config) transcript["unique_candidate_count"] = len(unique_candidate_ids) transcript["new_candidate_count"] = len(new_candidate_ids) transcript["reused_candidate_count"] = len(reused_candidate_ids) transcript["intra_terminal_reused_candidate_count"] = len( intra_terminal_reused_candidate_ids ) transcript["prior_failed_candidate_count"] = len(prior_failed_candidate_ids) sorted_manifest = dict(sorted(config_manifest.items())) transcript["config_manifest"] = sorted_manifest transcript["config_manifest_sha256"] = hashlib.sha256( json.dumps( sorted_manifest, sort_keys=True, separators=(",", ":"), ).encode() ).hexdigest() if self._autotune_metrics.num_generations != search_generation: raise AssertionError( "terminal refinement changed LFBO generation count" ) if current.config != best.config: self.log( "Terminal coordinate refinement selected " f"{current.config} ({self.format_performance(current.perf)})" ) self._selected_member = current return current add_manifest_config(best.config) initial_leaf = self._flash_structural_leaf(best) if initial_leaf is None: transcript["termination_reason"] = "no_candidates" transcript["completed"] = True transcript["confirmation"] = { "candidate_config_ids": [initial_config_id], "measurement": None, "best_config_id": initial_config_id, "selected_config_id": initial_config_id, "accepted": False, "improvement_fraction": 0.0, "skipped_reason": "missing_structural_leaf", } return finish_transcript(best) incumbent = best beam = [best] final_beam = [best] accepted_round_winners: list[PopulationMember] = [] accepted_configs: set[Config] = set() round_metrics = transcript["rounds"] assert isinstance(round_metrics, list) termination_reason = "round_limit" for round_index in range(1, policy.terminal_coordinate_rounds + 1): transcript["rounds_started"] = round_index parent_ids = [canonical_config_id(parent.config) for parent in beam] projection_parent_count += len(parent_ids) transcript["projection_parent_count"] = projection_parent_count parent_configs = {parent.config for parent in beam} parent_projections: list[dict[str, object]] = [] candidate_member_by_config: dict[Config, PopulationMember] = {} candidate_configs: list[Config] = [] round_seen_configs: set[Config] = set() newly_unbenchmarked: list[PopulationMember] = [] round_new_ids: list[str] = [] round_reused_ids: list[str] = [] round_intra_terminal_reused_ids: list[str] = [] prior_failed_ids: list[str] = [] for parent in beam: leaf_config_gen = self._flash_leaf_config_generation(initial_leaf) if leaf_config_gen is None: termination_reason = "no_candidates" break projections = self.config_gen.canonicalize_coordinate_projections( leaf_config_gen.coordinate_neighbor_projections( leaf_config_gen.flatten(parent.config), radius=self.radius, ), base_config=parent.config, ) requests: list[dict[str, object]] = [] projection_attempt_count += len(projections) for projection in projections: outcome = projection.outcome config = projection.config if ( outcome == "candidate" and config is not None and self._flash_structural_leaf_from_config(config) != initial_leaf ): outcome = "different_leaf" elif outcome == "candidate" and config in parent_configs: outcome = "beam_alias" elif outcome == "candidate" and config in round_seen_configs: outcome = "round_candidate_alias" requests.append( self._flash_terminal_projection_metric( projection, outcome=outcome, ) ) if config is not None: add_manifest_config(config) if outcome != "candidate" or config is None: continue round_seen_configs.add(config) candidate_configs.append(config) config_id = canonical_config_id(config) unique_candidate_ids.add(config_id) existing = registry.get(config) if existing is not None: candidate_member_by_config[config] = existing if self._flash_member_succeeded(existing): if config in preterminal_configs: round_reused_ids.append(config_id) reused_candidate_ids.add(config_id) else: round_intra_terminal_reused_ids.append(config_id) intra_terminal_reused_candidate_ids.add(config_id) else: prior_failed_ids.append(config_id) prior_failed_candidate_ids.add(config_id) continue member = self.make_unbenchmarked(self.config_gen.flatten(config)) if member is None: prior_failed_ids.append(config_id) prior_failed_candidate_ids.add(config_id) continue candidate_member_by_config[config] = member newly_unbenchmarked.append(member) round_new_ids.append(config_id) new_candidate_ids.add(config_id) parent_projections.append( { "parent_config_id": canonical_config_id(parent.config), "coordinate_requests": requests, } ) if termination_reason == "no_candidates": break transcript["projection_attempt_count"] = projection_attempt_count if newly_unbenchmarked: self.benchmark_population( newly_unbenchmarked, desc=f"Terminal coordinate refinement {round_index}:", ) measured: list[PopulationMember] = [] measured_configs: set[Config] = set() for member in (*beam, *candidate_member_by_config.values()): if ( self._flash_member_succeeded(member) and member.config not in measured_configs ): measured.append(member) measured_configs.add(member.config) candidate_ids = [ canonical_config_id(config) for config in candidate_configs ] candidate_results = [ self._flash_terminal_member_result(candidate_member_by_config[config]) for config in candidate_configs if config in candidate_member_by_config ] round_metric: dict[str, object] = { "round_index": round_index, "incumbent_config_id": canonical_config_id(incumbent.config), "leaf": { "family": initial_leaf.pipeline_family, "compound_packet": initial_leaf.compound_exp2_packet, "softmax_disc": initial_leaf.softmax_disc, }, "parent_config_ids": parent_ids, "parent_projections": parent_projections, "candidate_config_ids": candidate_ids, "new_candidate_ids": round_new_ids, "reused_candidate_ids": round_reused_ids, "intra_terminal_reused_candidate_ids": ( round_intra_terminal_reused_ids ), "prior_failed_candidate_ids": prior_failed_ids, "candidate_results": candidate_results, "comparison_config_ids": [], "measurement": None, "round_best_config_id": canonical_config_id(incumbent.config), "selected_config_id": canonical_config_id(incumbent.config), "accepted": False, "improvement_fraction": 0.0, "beam_config_ids": parent_ids, } round_metrics.append(round_metric) if len(measured) < 2: transcript["rounds_completed"] = round_index final_beam = beam termination_reason = "no_candidates" break member_ids = [canonical_config_id(member.config) for member in measured] round_metric["comparison_config_ids"] = member_ids trace = self.mirrored_rebenchmark( measured, desc=f"Terminal coordinate refinement {round_index}: comparing", target_ms=_FLASH_TERMINAL_REFINEMENT_TARGET_MS, ) round_metric["measurement"] = self._flash_terminal_trace_metric( member_ids, trace, ) round_metric["candidate_results"] = [ self._flash_terminal_member_result(candidate_member_by_config[config]) for config in candidate_configs if config in candidate_member_by_config ] round_best = min(measured, key=self._flash_member_rank_key) improvement = ( 1.0 - round_best.perf / incumbent.perf if math.isfinite(round_best.perf) and math.isfinite(incumbent.perf) and incumbent.perf > 0.0 else 0.0 ) accepted = bool( round_best.config != incumbent.config and improvement >= self.min_improvement_delta ) if accepted: incumbent = round_best add_manifest_config(incumbent.config) if incumbent.config not in accepted_configs: accepted_configs.add(incumbent.config) accepted_round_winners.append(incumbent) accepted_config_ids = transcript["accepted_config_ids"] assert isinstance(accepted_config_ids, list) accepted_config_ids.append(canonical_config_id(incumbent.config)) ranked = sorted(measured, key=self._flash_member_rank_key) next_beam = [incumbent] for member in ranked: if member.config in {item.config for item in next_beam}: continue next_beam.append(member) if len(next_beam) >= policy.terminal_coordinate_beam_width: break beam = next_beam final_beam = beam round_metric["round_best_config_id"] = canonical_config_id( round_best.config ) round_metric["selected_config_id"] = canonical_config_id(incumbent.config) round_metric["accepted"] = accepted round_metric["improvement_fraction"] = improvement round_metric["beam_config_ids"] = [ canonical_config_id(member.config) for member in beam ] transcript["rounds_completed"] = round_index transcript["termination_reason"] = termination_reason transcript["refined_config_id"] = canonical_config_id(incumbent.config) confirmation_members: list[PopulationMember] = [] confirmation_configs: set[Config] = set() for member in (best, *accepted_round_winners, *final_beam): if member.config in confirmation_configs: continue confirmation_configs.add(member.config) confirmation_members.append(member) add_manifest_config(member.config) current = best if len(confirmation_members) > 1: confirmation_ids = [ canonical_config_id(member.config) for member in confirmation_members ] confirmation_trace = self.mirrored_rebenchmark( confirmation_members, desc="Terminal coordinate refinement: confirming", target_ms=_FLASH_TERMINAL_CONFIRMATION_TARGET_MS, ) confirmed_best = min( confirmation_members, key=self._flash_member_rank_key, ) confirmation_improvement = ( 1.0 - confirmed_best.perf / best.perf if math.isfinite(confirmed_best.perf) and math.isfinite(best.perf) and best.perf > 0.0 else 0.0 ) confirmation_accepted = bool( confirmed_best.config != best.config and confirmation_improvement >= self.min_improvement_delta ) if confirmation_accepted: current = confirmed_best transcript["confirmation"] = { "candidate_config_ids": confirmation_ids, "measurement": self._flash_terminal_trace_metric( confirmation_ids, confirmation_trace, ), "best_config_id": canonical_config_id(confirmed_best.config), "selected_config_id": canonical_config_id(current.config), "accepted": confirmation_accepted, "improvement_fraction": confirmation_improvement, "skipped_reason": None, } else: transcript["confirmation"] = { "candidate_config_ids": [canonical_config_id(best.config)], "measurement": None, "best_config_id": canonical_config_id(best.config), "selected_config_id": canonical_config_id(best.config), "accepted": False, "improvement_fraction": 0.0, "skipped_reason": "single_candidate", } transcript["completed"] = True return finish_transcript(current)
def _flash_conditional_parent_candidates( self, members: Sequence[PopulationMember], leaf: FlashStructuralLeaf, constraints: tuple[tuple[str, object], ...], known_configs: set[Config], neighbor_limit: int, ) -> tuple[PopulationMember | None, list[PopulationMember], dict[str, object]]: """Find one conditional child within a shared ranked-parent budget. A parent's projected neighborhood can already be measured even when another parent in the same lane has novel children. Keep the complete attempt ledger so strict qualification can distinguish that local saturation from exhaustion of the lane. """ ranked = sorted(members, key=self._flash_member_rank_key) parent_count = min(len(ranked), max(0, neighbor_limit)) allocations = [ (index + 1) * neighbor_limit // parent_count - index * neighbor_limit // parent_count for index in range(parent_count) ] attempts: list[dict[str, object]] = [] ledger: dict[str, object] = { "schema_version": 2, "neighbor_limit": neighbor_limit, "radius": self.radius, "known_config_ids": sorted(map(canonical_config_id, known_configs)), "allocations": [ {"parent_config_id": canonical_config_id(parent.config), "limit": limit} for parent, limit in zip(ranked, allocations, strict=False) ], "attempts": attempts, "consumed": 0, "selected_parent_config_id": None, "selected_child_config_id": None, } consumed_total = 0 for parent, limit in zip(ranked, allocations, strict=False): generation = self._flash_leaf_config_generation(leaf, constraints) assert generation is not None base, parent_config = generation.canonicalize_flat( generation.flatten(parent.config) ) owned_indices = self._flash_generation( generation ).flash_owned_coordinate_indices(parent_config) proposals: list[dict[str, object]] = [] candidates: list[PopulationMember] = [] proposed_configs: set[Config] = set() def record_proposal( raw: FlatConfig, kind: str, *, generation: ConfigGeneration = generation, proposals: list[dict[str, object]] = proposals, candidates: list[PopulationMember] = candidates, proposed_configs: set[Config] = proposed_configs, ) -> None: record: dict[str, object] = { "kind": kind, "raw_flat_values": copy.deepcopy(raw), "config": None, "config_id": None, "outcome": "invalid", } proposals.append(record) try: _, local = generation.canonicalize_flat(raw) flat, config = self.config_gen.canonicalize_flat( self.config_gen.flatten(local) ) except exc.InvalidConfig: return record["config"] = dict(config.config) record["config_id"] = canonical_config_id(config) if self._flash_structural_leaf_from_config(config) != leaf or any( config.config.get(key) != value for key, value in constraints ): record["outcome"] = "constraint_mismatch" elif not self._backend_config_is_viable(config): record["outcome"] = "backend_invalid" elif config in known_configs: record["outcome"] = "known" elif config in proposed_configs: record["outcome"] = "duplicate" else: record["outcome"] = "novel" proposed_configs.add(config) candidates.append(PopulationMember(_unset_fn, [], flat, config)) with sync_seed(process_group_name=self.kernel.env.process_group_name): projections = generation.coordinate_neighbor_projections( base, radius=self.radius, limit=limit, frozen_indices=owned_indices, ) for projection in projections: raw = copy.deepcopy(base) raw[projection.flat_index] = copy.deepcopy(projection.to_value) record_proposal(raw, "coordinate") remaining = limit - len(proposals) random_state = random.getstate() if remaining else None if remaining: LFBOPatternSearch._generate_neighbors( self, base, config_gen=generation, num_neighbors=remaining, proposal_callback=lambda raw: record_proposal(raw, "random"), ) assert len(proposals) == limit attempt: dict[str, object] = { "parent_config_id": canonical_config_id(parent.config), "owned_coordinate_indices": owned_indices, "allocated": limit, "consumed": len(proposals), "random_state": random_state, "proposals": proposals, "novel_config_ids": [ canonical_config_id(item.config) for item in candidates ], } attempts.append(attempt) consumed_total += len(proposals) ledger["consumed"] = consumed_total if not candidates: continue selected = self._surrogate_select([parent, *candidates], 2) child = next(item for item in selected if item.config != parent.config) ledger["selected_parent_config_id"] = canonical_config_id(parent.config) ledger["selected_child_config_id"] = canonical_config_id(child.config) return parent, [child], ledger return None, [], ledger def _generate_flash_leaf_neighbors( self, current: PopulationMember, leaf: FlashStructuralLeaf, lane_constraints: tuple[tuple[str, object], ...] = (), neighbor_limit: int | None = None, ) -> list[FlatConfig]: """Generate on the family-conditional surface, then map back globally.""" config_gen = self._flash_leaf_config_generation(leaf, lane_constraints) if config_gen is None: return self._generate_neighbors( current.flat_values, num_neighbors=neighbor_limit ) base = config_gen.flatten(current.config) child_neighbors = LFBOPatternSearch._generate_neighbors( self, base, config_gen=config_gen, num_neighbors=neighbor_limit, ) result: list[FlatConfig] = [] seen: set[Config] = set() for child_flat in child_neighbors: try: _child_flat, config = config_gen.canonicalize_flat(child_flat) if self._flash_structural_leaf_from_config(config) != leaf: continue global_flat = self.config_gen.flatten(config) global_flat, global_config = self.config_gen.canonicalize_flat( global_flat ) except exc.InvalidConfig: continue if ( global_config in seen or self._flash_structural_leaf_from_config(global_config) != leaf ): continue seen.add(global_config) result.append(global_flat) return self.shrink_neighbors(result) @staticmethod def _flash_structural_leaf_from_config( config: Config, ) -> FlashStructuralLeaf | None: from .._compiler.cute.cute_flash import flash_structural_leaf_from_config return flash_structural_leaf_from_config(config.config) def _pruned_pattern_search_from( self, copy_idx: int, current: PopulationMember, visited: set[Config], constraints: tuple[tuple[str, object], ...] = (), selected_limit: int | None = None, required_leaf: FlashStructuralLeaf | None = None, conditional_surface: bool = False, disable_early_stopping: bool = False, neighbor_limit: int | None = None, ) -> Iterator[list[PopulationMember]]: """ Run a single copy of pattern search from the given starting point. We use a generator and yield the new population at each generation so that we can run multiple copies of pattern search in parallel. Only keep self.frac_selected of the neighbors generated from the current search_copy using _surrogate_select. Args: current: The current best configuration. visited: A set of visited configurations. Returns: A generator that yields the new population at each generation. """ patience = self.patience fixed_flat_values: dict[int, object] = {} for key, _value in constraints: indices, _is_sequence = self.config_gen._key_to_flat_indices[key] for index in indices: fixed_flat_values[index] = current.flat_values[index] for _ in range(self.max_generations): candidates: list[PopulationMember] = [current] generated_configs: set[Config] = set() with sync_seed(process_group_name=self.kernel.env.process_group_name): if required_leaf is not None and conditional_surface: all_neighbors = self._generate_flash_leaf_neighbors( current, required_leaf, constraints, neighbor_limit ) elif fixed_flat_values: if neighbor_limit is None: all_neighbors = self._generate_neighbors( current.flat_values, fixed_flat_values=fixed_flat_values, ) else: all_neighbors = self._generate_neighbors( current.flat_values, fixed_flat_values=fixed_flat_values, num_neighbors=neighbor_limit, ) else: if neighbor_limit is None: all_neighbors = self._generate_neighbors(current.flat_values) else: all_neighbors = self._generate_neighbors( current.flat_values, num_neighbors=neighbor_limit ) for flat_config in all_neighbors: new_member = self.make_unbenchmarked(flat_config) if new_member is None or any( new_member.config.config.get(key) != value for key, value in constraints ): continue if ( required_leaf is not None and self._flash_structural_leaf(new_member) != required_leaf ): continue if ( new_member.config not in visited and new_member.config not in generated_configs ): candidates.append(new_member) generated_configs.add(new_member.config) # Score candidates. Only the selected (i.e. benchmarked) # candidates enter `visited`, so proposals the surrogate rejects # can be re-proposed after it learns more. The incumbent is always # retained and at least one real neighbor is selected so a copy # cannot die from selection-quota truncation. n_sorted = int(len(candidates) * self.frac_selected) if len(candidates) > 1: n_sorted = max(2, n_sorted) if selected_limit is not None: n_sorted = min(n_sorted, selected_limit) candidates = self._surrogate_select(candidates, n_sorted) selected_neighbors = [ member for member in candidates if member.config != current.config ] candidates = [current, *selected_neighbors[: max(0, n_sorted - 1)]] visited.update(member.config for member in candidates) if len(candidates) <= 1: self.log(f"Copy {copy_idx} finish because of no candidates") return # no new candidates, stop searching yield candidates # yield new population to benchmark in parallel best = min(candidates, key=performance) if not disable_early_stopping and self._check_early_stopping(best, current): if patience > 0: patience -= 1 else: self.log(f"Copy {copy_idx} finish because of no improvement") return current = best
[docs] class LFBOTreeSearch(LFBOPatternSearch): """ LFBO Tree Search: Likelihood-Free Bayesian Optimization with tree-guided neighbor generation. This algorithm uses a Random Forest classifier as a surrogate model to both select which configurations to benchmark and to guide the generation of new candidate configurations via greedy decision tree traversal. Algorithm Overview: 1. Generate an initial population (random or default) and benchmark all configurations 2. Fit a Random Forest classifier to predict "good" vs "bad" configurations: - Configs with performance < quantile threshold are labeled as "good" (class 1) - Configs with performance >= quantile threshold are labeled as "bad" (class 0) - Weighted classification emphasizes configs that are much better than the threshold 3. For the first generation, generate neighbors via random perturbation since the surrogate is not yet fitted 4. For subsequent generations, generate neighbors via greedy tree traversal: a. For each of num_neighbors trials: - Pick a random decision tree from the Random Forest - Trace the decision path for the current best config through that tree - Extract the configuration parameters used in the tree's split decisions - For each parameter on the path, greedily optimize it: * Generate pattern_neighbors within the configured radius * Score candidates using the single tree's predicted probability * Accept the best value (ties broken randomly) and incrementally update the encoded representation - Keep the result only if it differs from the base configuration b. Score candidates using the full ensemble's predicted probability with a diversity-aware similarity penalty, then select top candidates 5. Benchmark selected candidates, retrain the classifier on all observed data The tree-guided traversal focuses search on parameters the surrogate has identified as important (those used in tree splits). Using a single tree per trial (rather than the full ensemble) introduces diversity since different trees may emphasize different parameters. References: - Song, J., et al. (2022). "A General Recipe for Likelihood-free Bayesian Optimization." - Mišić, Velibor V. "Optimization of tree ensembles." Operations Research 68.5 (2020): 1605-1624. Args: kernel: The kernel to be autotuned. args: The arguments to be passed to the kernel during benchmarking. initial_population: Number of random configurations in initial population. Default from PATTERN_SEARCH_DEFAULTS. Ignored when using DEFAULT strategy. copies: Number of top configurations to run pattern search from. Full CuTe-flash searches give every ordinary structural leaf bounded qualification work and transfer top representatives to compound leaves, then retain at most ``copies`` paths across the best parent families. Default from PATTERN_SEARCH_DEFAULTS. max_generations: Maximum number of search iterations per copy. Default from PATTERN_SEARCH_DEFAULTS. min_improvement_delta: Early stopping threshold. Search stops if the relative improvement abs(best/current - 1) < min_improvement_delta. Default: 0.001 (0.1% improvement threshold). frac_selected: Fraction of generated neighbors to actually benchmark, after filtering by classifier score. Range: (0, 1]. Lower values reduce benchmarking cost but may miss good configurations. Default: 0.15. num_neighbors: Number of greedy tree traversal trials to run per generation. Each trial picks a random tree, traces its decision path, and greedily optimizes parameters along that path. Default: 100. radius: Maximum perturbation distance when generating pattern neighbors for each parameter during tree traversal. For power-of-two parameters, this is the max change in log2 space. For other parameters, this limits the neighborhood size. Default: 3. quantile: Threshold for labeling configs as "good" (class 1) vs "bad" (class 0). Configs with performance below this quantile are labeled as good. Range: (0, 1). Lower values create a more selective definition of "good". Default: 0.1 (top 10% are considered good). patience: Number of generations without improvement before stopping the search copy. Default: 1. similarity_penalty: Penalty for selecting points that are similar to points already selected in the batch. Default: 1.0. initial_population_strategy: Strategy for generating the initial population. FROM_RANDOM generates initial_population random configs. FROM_BEST_AVAILABLE uses cached configs from prior runs, and fills the remainder with random configs when best_available_pad_random is True. Can be overridden by HELION_AUTOTUNER_INITIAL_POPULATION env var. num_neighbors_cap: Maximum number of neighbors to explore per generation. -1 means no cap. Set HELION_CAP_AUTOTUNE_NUM_NEIGHBORS=N to override. """
[docs] def __init__( self, kernel: _AutotunableKernel, args: Sequence[object], *, num_neighbors: int = 200, frac_selected: float = 0.10, radius: int = 2, initial_population: int = PATTERN_SEARCH_DEFAULTS.initial_population, copies: int = PATTERN_SEARCH_DEFAULTS.copies, max_generations: int = PATTERN_SEARCH_DEFAULTS.max_generations, min_improvement_delta: float = 0.001, quantile: float = 0.1, patience: int = 1, similarity_penalty: float = 1.0, initial_population_strategy: InitialPopulationStrategy | None = None, best_available_pad_random: bool = PATTERN_SEARCH_DEFAULTS.best_available_pad_random, num_neighbors_cap: int = -1, finishing_rounds: int = 0, polish_rounds: int = 10, compile_timeout_lower_bound: float = PATTERN_SEARCH_DEFAULTS.compile_timeout_lower_bound, compile_timeout_quantile: float = PATTERN_SEARCH_DEFAULTS.compile_timeout_quantile, flash_structural_search: FlashStructuralSearchConfig | None = None, ) -> None: super().__init__( kernel=kernel, args=args, num_neighbors=num_neighbors, frac_selected=frac_selected, radius=radius, initial_population=initial_population, copies=copies, max_generations=max_generations, min_improvement_delta=min_improvement_delta, quantile=quantile, patience=patience, similarity_penalty=similarity_penalty, initial_population_strategy=initial_population_strategy, best_available_pad_random=best_available_pad_random, num_neighbors_cap=num_neighbors_cap, finishing_rounds=finishing_rounds, polish_rounds=polish_rounds, compile_timeout_lower_bound=compile_timeout_lower_bound, compile_timeout_quantile=compile_timeout_quantile, flash_structural_search=flash_structural_search, ) self._encoded_to_flat_mapping: list[tuple[int, int, int]] | None = None
def _get_encoded_to_flat_mapping(self) -> list[tuple[int, int, int]]: """Build and cache mapping from encoded feature indices to flat_spec indices.""" if self._encoded_to_flat_mapping is None: mapping: list[tuple[int, int, int]] = [] offset = 0 for flat_idx, spec in enumerate(self.config_gen.flat_spec): d = spec.dim() mapping.append((offset, offset + d, flat_idx)) offset += d self._encoded_to_flat_mapping = mapping return self._encoded_to_flat_mapping @staticmethod def _encoded_index_to_flat_index( mapping: list[tuple[int, int, int]], encoded_idx: int ) -> int: """Map an encoded feature index used in tree splits to its flat_spec index.""" for start, end, flat_idx in mapping: if start <= encoded_idx < end: return flat_idx raise ValueError(f"Encoded index {encoded_idx} out of range") def _generate_neighbors( self, base: FlatConfig, *, fixed_flat_values: Mapping[int, object] | None = None, config_gen: ConfigGeneration | None = None, num_neighbors: int | None = None, proposal_callback: Callable[[FlatConfig], None] | None = None, ) -> list[FlatConfig]: """ Generate neighbors via greedy tree traversal with incremental encoding. For each of num_neighbors trials: 1. Pick a random tree from the Random Forest surrogate 2. Get its decision path for the base config 3. Extract unique flat_spec indices from the path's split features 4. Augment with a random block_size index and the num_warps index 5. For each parameter on the path: - Generate pattern_neighbors with the configured radius - Score current value + neighbors with single tree (ties broken randomly) - Only re-encode the changed parameter's features (incremental) Returns all distinct candidates. Falls back to the parent's random neighbor generation if no surrogate is fitted. """ if config_gen is not None or proposal_callback is not None: return super()._generate_neighbors( base, fixed_flat_values=fixed_flat_values, config_gen=config_gen, num_neighbors=num_neighbors, proposal_callback=proposal_callback, ) surrogate = self.surrogate if surrogate is None or self._autotune_metrics.num_generations <= 1: return super()._generate_neighbors( base, fixed_flat_values=fixed_flat_values, num_neighbors=num_neighbors, ) config_gen = self.config_gen mapping = self._get_encoded_to_flat_mapping() n_trees = len(surrogate.estimators_) base_list = list(base) base_encoded = np.array(config_gen.encode_config(base), dtype=np.float64) frozen = set(config_gen.overridden_flat_indices) if fixed_flat_values: frozen.update(fixed_flat_values) eligible_block = [i for i in config_gen.block_size_indices if i not in frozen] warp_idx = config_gen.num_warps_index tune_warps = warp_idx >= 0 and warp_idx not in frozen all_results: list[FlatConfig] = [] for _ in range(self.num_neighbors if num_neighbors is None else num_neighbors): # 1. Pick a random tree tree_idx = random.randint(0, n_trees - 1) estimator = surrogate.estimators_[tree_idx] tree = estimator.tree_ # 2. Get decision path for base config decision_path = estimator.decision_path(base_encoded.reshape(1, -1)) path_node_indices = decision_path.indices.tolist() # type: ignore[union-attr] # 3. Extract flat_spec indices (deduplicated, order-preserving) seen: set[int] = set(frozen) path_flat_indices: list[int] = [] for node_id in path_node_indices: feat = tree.feature[node_id] # pyrefly: ignore [missing-attribute] if feat >= 0: flat_idx = self._encoded_index_to_flat_index(mapping, feat) if flat_idx not in seen: seen.add(flat_idx) path_flat_indices.append(flat_idx) # 4. Augment with block_size and num_warps indices if eligible_block: bs_idx = random.choice(eligible_block) if bs_idx not in seen: seen.add(bs_idx) path_flat_indices.append(bs_idx) if tune_warps and warp_idx not in seen: seen.add(warp_idx) path_flat_indices.append(warp_idx) # 5. Greedy traversal with incremental encoding current_flat: FlatConfig = list(base) current_encoded = base_encoded.copy() for flat_idx in path_flat_indices: spec = config_gen.flat_spec[flat_idx] current_val = current_flat[flat_idx] neighbors = spec.pattern_neighbors(current_val, self.radius) if not neighbors: continue # Build candidate encodings by patching only the changed slice candidate_vals = [current_val, *neighbors] enc_start, enc_end, _ = mapping[flat_idx] n_candidates = len(candidate_vals) candidate_encoded = np.tile(current_encoded, (n_candidates, 1)) for i, val in enumerate(candidate_vals): candidate_encoded[i, enc_start:enc_end] = spec.encode(val) # Score with single tree (ties broken randomly) probas = np.asarray(estimator.predict_proba(candidate_encoded))[:, 1] # Greedy: pick the best, with random tie-breaking max_proba = float(np.max(probas)) top_indices = [i for i, p in enumerate(probas) if p == max_proba] chosen_idx = random.choice(top_indices) current_flat[flat_idx] = candidate_vals[chosen_idx] current_encoded[enc_start:enc_end] = candidate_encoded[ chosen_idx, enc_start:enc_end ] # Only keep if different from base if current_flat != base_list: all_results.append(list(current_flat)) return self.shrink_neighbors(all_results)