Generated from the full canonical file for this source snapshot. Line numbers match the library source.
Source SHA256: 65736264d79aae216fb492212fa878ee0b93d0026aab6b1cc1ac8e972463630c
1"""Independent-batch squared-expected-signal optimisation in a fixed chart.23The acceptance band uses an estimated standard error, not a distribution-free4confidence bound. Sampling uncertainty can force batch growth or exhaustion;5it never turns an unresolved trial into an accepted decrease. General nonlinear6losses need different estimators and are outside this interface's contract.7"""89from __future__ import annotations1011import math12from collections.abc import Callable13from dataclasses import dataclass, replace14from functools import partial15from typing import Literal, Protocol, TypeVar, cast1617from dpt.contracts import ContractError, NumericalError, TrialDomainError, finite_scalar, integer18from dpt.registration import Vector19from dpt.statistics import mean_standard_error20from dpt.stochastic_models import (21Matrix,22QuadraticProposal,23quadratic_proposal,24quadratic_reduction,25validate_curvature,26)27from dpt.transport.rng import HistoryBatch282930class IndependentSquaredOracle(Protocol):31"""Original histories are IID within each caller-specified batch.3233Source sampling must be keyed by the provided history identity too. The two34independent batches supply distinct mean and derivative estimates. Replaying35an original history to obtain its derivative does not create a new sample.36"""3738def gradient_replicate(39self, parameters: Vector, mean_batch: HistoryBatch, derivative_batch: HistoryBatch40) -> Vector:41"""Return (estimated mean - observed) times an independent mean derivative."""42...4344def change_replicate(45self, before: Vector, after: Vector, first: HistoryBatch, second: HistoryBatch46) -> float:47"""Return an unbiased squared-expected-signal loss difference.4849Each pose uses the product of independent residual-mean estimates.50Reuse the specified streams between poses for common random numbers;51batches first and second must stay independent within either pose.52"""53...545556class QuadraticSquaredOracle(IndependentSquaredOracle, Protocol):57def model_replicate(58self, parameters: Vector, mean_batch: HistoryBatch, derivative_batch: HistoryBatch59) -> tuple[Vector, Matrix]:60"""Return independent-product gradient and PSD estimated-Jacobian metric."""61...626364@dataclass(frozen=True, slots=True)65class StochasticPolicy:66iterations: int = 10067replicates: int = 868initial_batch: int = 102469maximum_batch: int = 1_048_57670unique_history_budget: int = 100_000_00071initial_radius: float = 0.172minimum_radius: float = 1e-773maximum_radius: float = 1.074acceptance_fraction: float = 0.175standard_error_multiplier: float = 2.076gradient_tolerance: float = 1e-577relative_gradient_uncertainty: float = 0.578proposal: Literal["linear", "quadratic"] = "linear"79numerical_gradient_allowance: float = 0.080final_validation_batch: int | None = None81damping_relative: float = 1e-1282rank_tolerance: float = 1e-1083radius_growth_agreement: float = 0.758485def __post_init__(self) -> None:86for name in ("iterations", "initial_batch", "maximum_batch", "unique_history_budget"):87integer(getattr(self, name), name, minimum=1, maximum=2**63 - 1)88integer(self.replicates, "replicates", minimum=2)89if self.initial_batch > self.maximum_batch or self.maximum_batch >= 2**31:90raise ContractError("batch sizes must be ordered positive signed-32-bit counts")91for name in (92"initial_radius",93"minimum_radius",94"maximum_radius",95"acceptance_fraction",96"standard_error_multiplier",97"gradient_tolerance",98"relative_gradient_uncertainty",99):100if finite_scalar(getattr(self, name), name, minimum=0.0) == 0:101raise ContractError(f"{name} must be positive")102if not self.minimum_radius <= self.initial_radius <= self.maximum_radius:103raise ContractError("trust radii must satisfy minimum <= initial <= maximum")104if not 0 < self.acceptance_fraction < 1:105raise ContractError("acceptance fraction must lie in (0,1)")106if self.proposal not in ("linear", "quadratic"):107raise ContractError("proposal must be linear or quadratic")108finite_scalar(self.numerical_gradient_allowance, "numerical_gradient_allowance", minimum=0)109finite_scalar(self.damping_relative, "damping_relative", minimum=0)110finite_scalar(self.rank_tolerance, "rank_tolerance", minimum=0)111finite_scalar(self.radius_growth_agreement, "radius_growth_agreement", minimum=0)112if not 0 < self.rank_tolerance < 1 or not 0 < self.radius_growth_agreement <= 1:113raise ContractError("rank tolerance and radius agreement must lie in (0,1] (rank < 1)")114if self.final_validation_batch is not None:115integer(self.final_validation_batch, "final_validation_batch", minimum=1)116if self.final_validation_batch > self.maximum_batch:117raise ContractError("final validation batch exceeds prepared batch capacity")118119120@dataclass(frozen=True, slots=True)121class StochasticStep:122iteration: int123accepted: bool124mean_change: float125change_standard_error: float126predicted_decrease: float127radius: float128batch_size: int129first_history: int130histories_used: int131132133@dataclass(frozen=True, slots=True)134class GradientAttempt:135"""Host statistics for one complete set of independent gradient replicates."""136137iteration: int138parameters: Vector139batch_size: int140first_history: int141histories_used: int142gradient: Vector143standard_error: Vector144gradient_norm: float145standard_error_norm: float146decision: Literal["zero_sample", "gradient_band", "resolved", "relative_uncertainty"]147model_id: int | None = None148pool: str = "proposal"149band_method: str = "heuristic_euclidean_marginal_se"150numerical_allowance: float = 0.0151replicate_gradients: tuple[Vector, ...] = ()152153154@dataclass(frozen=True, slots=True)155class WorkReservation:156"""Random identities reserved, distinct from completed oracle calls/work.157158A failed call may have executed partial CUDA work. Only the operator's work159counters can report that; completed_replicates does not infer replay costs.160No identity from a failed reservation is reused.161"""162163operation: str164iteration: int165first_history: int166required_histories: int167reserved_histories: int168checkpoint_histories: int169pairs: tuple[tuple[HistoryBatch, HistoryBatch], ...]170status: str171attempted_replicates: int = 0172completed_replicates: int = 0173174175@dataclass(frozen=True, slots=True)176class LocalModel:177model_id: int178parameters: Vector179gradient: Vector180curvature: Matrix181first_history: int182histories_used: int183batch_size: int184185186@dataclass(frozen=True, slots=True)187class AcceptanceAttempt:188iteration: int189incumbent: Vector190candidate: Vector191model_id: int | None192step: Vector193predicted_decrease: float194radius: float195batch_size: int196first_history: int197histories_used: int198required_histories: int199replicate_changes: tuple[float, ...]200mean_change: float | None201standard_error: float | None202band_method: str203look_index: int204threshold: float205outcome: str206agreement: float | None207boundary: bool208proposal_diagnostics: QuadraticProposal | None = None209210211@dataclass(frozen=True, slots=True)212class StochasticRecoveryResult:213parameters: Vector214reason: Literal[215"gradient_band", "sampling_unresolved", "history_budget", "radius_limit", "iteration_budget"216]217unique_histories: int218steps: tuple[StochasticStep, ...]219domain_rejections: tuple[DomainRejection, ...] = ()220gradient_attempts: tuple[GradientAttempt, ...] = ()221termination_detail: str = ""222diagnostic_version: int = 2223acceptance_attempts: tuple[AcceptanceAttempt, ...] = ()224reservations: tuple[WorkReservation, ...] = ()225local_models: tuple[LocalModel, ...] = ()226final_validation_attempts: tuple[GradientAttempt, ...] = ()227sampling_classification: str = "stochastic"228proposal_policy: str = "linear"229uncertainty_policy: str = "heuristic_euclidean_marginal_se"230numerical_gradient_allowance: float = 0.0231final_validation_batch: int | None = None232final_validation_trigger: str | None = None233234235@dataclass(frozen=True, slots=True)236class DomainRejection:237iteration: int238radius: float239message: str240241242_Replicate = TypeVar("_Replicate")243244245def _gradient_attempt(246replicates: tuple[Vector, ...],247parameters: Vector,248selected: StochasticPolicy,249deterministic: bool,250*,251iteration: int,252batch_size: int,253first_history: int,254histories_used: int,255model_id: int | None,256pool: str = "proposal",257) -> GradientAttempt:258if any(len(value) != len(parameters) for value in replicates):259raise ContractError("gradient replicate dimension differs from the active chart")260statistics = tuple(261mean_standard_error(tuple(row[j] for row in replicates)) for j in range(len(parameters))262)263gradient = tuple(mean for mean, _ in statistics)264norm = math.hypot(*gradient)265empirical_error = math.hypot(*(error for _, error in statistics))266if not math.isfinite(norm) or not math.isfinite(empirical_error):267raise NumericalError("gradient or uncertainty norm exceeds the finite chart range")268if deterministic and empirical_error > selected.numerical_gradient_allowance:269raise NumericalError("verified deterministic oracle exceeds its numerical allowance")270error_norm = 0.0 if deterministic else empirical_error271allowance = selected.numerical_gradient_allowance if deterministic else 0.0272decision: Literal["zero_sample", "gradient_band", "resolved", "relative_uncertainty"]273if not deterministic and norm == 0.0 and error_norm == 0.0:274decision = "zero_sample"275elif (276norm + selected.standard_error_multiplier * error_norm + allowance277<= selected.gradient_tolerance278):279decision = "gradient_band"280elif norm > 0 and error_norm + allowance <= selected.relative_gradient_uncertainty * norm:281decision = "resolved"282else:283decision = "relative_uncertainty"284return GradientAttempt(285iteration,286parameters,287batch_size,288first_history,289histories_used,290gradient,291(0.0,) * len(parameters) if deterministic else tuple(error for _, error in statistics),292norm,293error_norm,294decision,295model_id,296pool,297"deterministic_numerical_allowance" if deterministic else "heuristic_euclidean_marginal_se",298allowance,299replicates,300)301302303# region book:stochastic-independent-acceptance304def recover_expected_signal(305oracle: IndependentSquaredOracle,306initial: Vector,307*,308seed: int,309policy: StochasticPolicy | None = None,310) -> StochasticRecoveryResult:311"""Fresh proposal/acceptance pools with an optional held-out final checkpoint.312313Quadratic proposals cache a PSD metric at the unchanged incumbent; rejected314radii use fresh acceptance identities. Final sample size is fixed here before315observing any samples. A failed final checkpoint terminates unresolved and316never feeds a new proposal. ``linear`` with no explicit final batch retains317the original trajectory for ablation, including its heuristic stopping rule.318319The explicit ``deterministic_sampling`` oracle flag must be established from320immutable physical/source properties. Empirical variance never establishes321that classification. Its numerical allowance requires independent validation322by the caller. Stochastic norm bands are heuristic, not simultaneous-vector323or repeated-look confidence bounds. Unique identities are not replay costs.324"""325selected: StochasticPolicy = policy or StochasticPolicy()326quadratic = selected.proposal == "quadratic"327integer(seed, "seed", maximum=2**64 - 1)328parameters: Vector = tuple(finite_scalar(value, "initial parameter") for value in initial)329if not parameters:330raise ContractError("a stochastic inverse problem needs active parameters")331if quadratic and len(parameters) > 16:332raise ContractError("dense stochastic models support at most 16 active parameters")333if quadratic and not callable(getattr(oracle, "model_replicate", None)):334raise ContractError("quadratic proposals require an oracle model_replicate operation")335classification = getattr(oracle, "deterministic_sampling", False)336if type(classification) is not bool:337raise ContractError("deterministic_sampling must be an explicit boolean contract")338deterministic = classification339final_size = selected.final_validation_batch340if quadratic and final_size is None:341final_size = selected.initial_batch342final_cost = 0 if final_size is None else 2 * final_size * selected.replicates343first_history = 0344radius = selected.initial_radius345batch_size = selected.initial_batch346damping_relative = selected.damping_relative347rank_tolerance = selected.rank_tolerance348steps: list[StochasticStep] = []349domain_rejections: list[DomainRejection] = []350gradient_attempts: list[GradientAttempt] = []351acceptance_attempts: list[AcceptanceAttempt] = []352reservations: list[WorkReservation] = []353local_models: list[LocalModel] = []354validation_attempts: list[GradientAttempt] = []355validation_trigger: str | None = None356cached: list[LocalModel] = []357band_method = (358"deterministic_numerical_allowance" if deterministic else "heuristic_euclidean_marginal_se"359)360361def reserve(size: int, operation: str, iteration: int, holdback: int = 0) -> int | None:362nonlocal first_history363required = 2 * size * selected.replicates364start = first_history365if first_history + required + holdback > selected.unique_history_budget:366reservations.append(367WorkReservation(368operation, iteration, start, required, 0, holdback, (), "budget_denied"369)370)371return None372pairs: list[tuple[HistoryBatch, HistoryBatch]] = []373for _ in range(selected.replicates):374left = HistoryBatch(seed, first_history, size, f"inverse-source-{first_history}")375first_history += size376right = HistoryBatch(seed, first_history, size, f"inverse-source-{first_history}")377first_history += size378pairs.append((left, right))379reservations.append(380WorkReservation(381operation, iteration, start, required, required, holdback, tuple(pairs), "reserved"382)383)384return len(reservations) - 1385386def execute(387index: int, operation: Callable[[HistoryBatch, HistoryBatch], _Replicate]388) -> tuple[_Replicate, ...]:389outputs: list[_Replicate] = []390for left, right in reservations[index].pairs:391reservations[index] = replace(392reservations[index], attempted_replicates=len(outputs) + 1393)394try:395outputs.append(operation(left, right))396except Exception as error:397reservations[index] = replace(398reservations[index],399status="domain_error"400if isinstance(error, TrialDomainError)401else "oracle_error",402completed_replicates=len(outputs),403)404raise405reservations[index] = replace(reservations[index], completed_replicates=len(outputs))406reservations[index] = replace(reservations[index], status="complete")407return tuple(outputs)408409def result(410reason: Literal[411"gradient_band",412"sampling_unresolved",413"history_budget",414"radius_limit",415"iteration_budget",416],417detail: str = "",418) -> StochasticRecoveryResult:419return StochasticRecoveryResult(420parameters,421reason,422first_history,423tuple(steps),424tuple(domain_rejections),425tuple(gradient_attempts),426detail or reason,427acceptance_attempts=tuple(acceptance_attempts),428reservations=tuple(reservations),429local_models=tuple(local_models),430final_validation_attempts=tuple(validation_attempts),431sampling_classification="verified_deterministic_expectation"432if deterministic433else "stochastic",434proposal_policy=selected.proposal,435uncertainty_policy=band_method,436numerical_gradient_allowance=selected.numerical_gradient_allowance,437final_validation_batch=final_size,438final_validation_trigger=validation_trigger,439)440441def record_exception(error: Exception) -> None:442# Preserve the normal numerical exception contract, with a serialisable443# snapshot for callers recording a failed run. Partial device work is444# deliberately unknown here; the production operator records its cost.445error.__dict__["recovery_diagnostics"] = result("sampling_unresolved", "oracle_exception")446error.add_note("Recovery reservation/attempt snapshot is in recovery_diagnostics.")447448def log_acceptance(449base: AcceptanceAttempt,450changes: list[float],451outcome: str,452change: float | None = None,453error: float | None = None,454) -> None:455ratio = None if change is None else -change / base.predicted_decrease456acceptance_attempts.append(457replace(458base,459outcome=outcome,460replicate_changes=tuple(changes),461mean_change=change,462standard_error=error,463agreement=ratio if ratio is None or math.isfinite(ratio) else None,464)465)466467def finish_stationarity(468iteration: int, trigger: str = "proposal_gradient_band"469) -> StochasticRecoveryResult:470nonlocal validation_trigger471if final_size is None:472return result("gradient_band")473validation_trigger = trigger474index = reserve(final_size, "final_validation", iteration)475if index is None:476return result("history_budget", "final_validation_reservation_failed")477try:478values = execute(index, partial(oracle.gradient_replicate, parameters))479attempt = _gradient_attempt(480values,481parameters,482selected,483deterministic,484iteration=iteration,485batch_size=final_size,486first_history=reservations[index].first_history,487histories_used=reservations[index].reserved_histories,488model_id=None,489pool="final_validation",490)491except Exception as error:492record_exception(error)493raise494validation_attempts.append(attempt)495if attempt.decision == "gradient_band":496return result("gradient_band", "held_out_gradient_band")497return result("sampling_unresolved", "final_validation_failed")498499def finish_normal(500reason: Literal["sampling_unresolved", "radius_limit"], detail: str, iteration: int501) -> StochasticRecoveryResult:502# A development decision can be unresolved even when the fixed incumbent503# is stationary. Spend its already reserved, independent checkpoint once.504# Final failure terminates; none of these samples select another point.505if final_size is not None and first_history + final_cost <= selected.unique_history_budget:506return finish_stationarity(iteration, detail or reason)507return result(reason, detail)508509for iteration in range(selected.iterations):510if not cached:511# Fresh-batch growth is deliberately retained: there is no pooling512# across adaptive epochs or different parameter values.513while True:514checkpoint = final_cost515if final_size is not None:516checkpoint += 2 * batch_size * selected.replicates517index = reserve(518batch_size,519"model" if quadratic else "gradient",520iteration,521checkpoint,522)523if index is None:524if final_size is not None and any(value.accepted for value in steps):525return finish_stationarity(526iteration, "proposal_checkpoint_reservation_failed"527)528return result(529"history_budget",530"proposal_checkpoint_reservation_failed"531if checkpoint532else "history_budget",533)534model_id = len(local_models) if quadratic else None535try:536curvature: Matrix = ()537if quadratic:538model_oracle = cast(QuadraticSquaredOracle, oracle)539models = execute(index, partial(model_oracle.model_replicate, parameters))540replicates = tuple(value[0] for value in models)541size = len(parameters)542if any(543len(value[1]) != size or any(len(row) != size for row in value[1])544for value in models545):546raise ContractError(547"model curvature dimension differs from the active chart"548)549curvature = tuple(550tuple(551mean_standard_error(tuple(value[1][i][j] for value in models))[0]552for j in range(size)553)554for i in range(size)555)556validate_curvature(curvature, size)557else:558replicates = execute(index, partial(oracle.gradient_replicate, parameters))559attempt = _gradient_attempt(560replicates,561parameters,562selected,563deterministic,564iteration=iteration,565batch_size=batch_size,566first_history=reservations[index].first_history,567histories_used=reservations[index].reserved_histories,568model_id=model_id,569)570except Exception as error:571record_exception(error)572raise573gradient_attempts.append(attempt)574if model_id is not None:575local_models.append(576LocalModel(577model_id,578parameters,579attempt.gradient,580curvature,581attempt.first_history,582attempt.histories_used,583batch_size,584)585)586if attempt.decision == "zero_sample":587if batch_size == selected.maximum_batch:588return finish_normal(589"sampling_unresolved",590"zero_gradient_and_variance_at_maximum_batch",591iteration,592)593batch_size = min(2 * batch_size, selected.maximum_batch)594continue595if attempt.decision == "gradient_band":596return finish_stationarity(iteration)597if attempt.decision == "resolved":598gradient, norm = attempt.gradient, attempt.gradient_norm599if model_id is not None:600cached[:] = [local_models[-1]]601break602if batch_size == selected.maximum_batch:603return finish_normal(604"sampling_unresolved", "gradient_uncertainty_at_maximum_batch", iteration605)606batch_size = min(2 * batch_size, selected.maximum_batch)607else:608gradient, norm = cached[0].gradient, math.hypot(*cached[0].gradient)609proposal: QuadraticProposal | None = None610active_curvature: Matrix = ()611if quadratic:612assert cached613active_curvature = cached[0].curvature614proposal = quadratic_proposal(615gradient,616active_curvature,617radius,618damping_relative=damping_relative,619rank_tolerance=rank_tolerance,620)621step, predicted, boundary = (622proposal.step,623proposal.predicted_decrease,624proposal.boundary,625)626else:627step = tuple(-radius * (value / norm) for value in gradient)628predicted, boundary = radius * norm, True629candidate: Vector = tuple(a + b for a, b in zip(parameters, step, strict=True))630if not math.isfinite(predicted) or predicted <= 0 or not all(map(math.isfinite, candidate)):631raise NumericalError("stochastic proposal exceeds the finite chart range")632if candidate == parameters:633return finish_normal(634"sampling_unresolved", "proposal_below_chart_resolution", iteration635)636# Addition in a large finite chart can round the requested displacement.637# Log the realised move and assess its actual quadratic prediction. The638# legacy linear prediction/decisions remain unchanged for the ablation.639step = tuple(b - a for a, b in zip(parameters, candidate, strict=True))640if proposal is not None:641actual_norm = math.hypot(*step)642if actual_norm > radius * (1 + 1e-12):643return finish_normal(644"sampling_unresolved", "chart_rounding_exceeds_trust_radius", iteration645)646predicted = quadratic_reduction(gradient, active_curvature, step)647boundary = proposal.boundary and actual_norm >= radius * (1 - 1e-12)648proposal = replace(proposal, step=step, predicted_decrease=predicted, boundary=boundary)649look = 0650while True:651look += 1652start = first_history653index = reserve(batch_size, "acceptance", iteration, final_cost)654changes: list[float] = []655threshold = -selected.acceptance_fraction * predicted656657base = AcceptanceAttempt(658iteration,659parameters,660candidate,661None if not cached else cached[0].model_id,662step,663predicted,664radius,665batch_size,666start,667first_history - start,6682 * batch_size * selected.replicates,669(),670None,671None,672band_method,673look,674threshold,675"reserved",676None,677boundary,678proposal,679)680681if index is None:682log_acceptance(base, changes, "budget_denied")683return result(684"history_budget",685"acceptance_checkpoint_reservation_failed" if final_cost else "history_budget",686)687688def change_operation(689a: HistoryBatch,690b: HistoryBatch,691*,692before: Vector = parameters,693after: Vector = candidate,694outputs: list[float] = changes,695) -> float:696value = oracle.change_replicate(before, after, a, b)697if not math.isfinite(value):698raise NumericalError("objective-change replicate is nonfinite")699outputs.append(value)700return value701702try:703execute(index, change_operation)704change, standard_error = mean_standard_error(changes)705band = selected.standard_error_multiplier * standard_error706if not math.isfinite(band):707raise NumericalError("objective-change uncertainty band exceeds finite range")708except TrialDomainError as error:709log_acceptance(base, changes, "domain_error")710domain_rejections.append(DomainRejection(iteration, radius, str(error)))711radius *= 0.5712if radius < selected.minimum_radius:713return finish_normal("radius_limit", "radius_limit", iteration)714break715except Exception as error:716log_acceptance(base, changes, "oracle_error")717record_exception(error)718raise719if change + band < threshold:720log_acceptance(base, changes, "accepted", change, standard_error)721steps.append(722StochasticStep(723iteration,724True,725change,726standard_error,727predicted,728radius,729batch_size,730start,731first_history - start,732)733)734parameters = candidate735cached.clear()736if not quadratic or (737boundary and -change / predicted >= selected.radius_growth_agreement738):739radius = min(2 * radius, selected.maximum_radius)740break741if change - band >= threshold:742log_acceptance(base, changes, "rejected", change, standard_error)743steps.append(744StochasticStep(745iteration,746False,747change,748standard_error,749predicted,750radius,751batch_size,752start,753first_history - start,754)755)756radius *= 0.5757if radius < selected.minimum_radius:758return finish_normal("radius_limit", "radius_limit", iteration)759break760log_acceptance(base, changes, "ambiguous", change, standard_error)761if batch_size == selected.maximum_batch:762return finish_normal(763"sampling_unresolved", "acceptance_uncertainty_at_maximum_batch", iteration764)765batch_size = min(2 * batch_size, selected.maximum_batch)766if final_size is not None:767return finish_stationarity(selected.iterations, "iteration_budget")768return result("iteration_budget")769770771# endregion book:stochastic-independent-acceptance772