Download src/hamiltonzero/evaluation/runner.py from Twobombs/HamiltonZero: direct link, hf CLI and curl.
- Browser
- Download file 16.7 kB
-
https://huggingface.co/Twobombs/HamiltonZero/resolve/main/src/hamiltonzero/evaluation/runner.py
- Command line
-
hf download hf://Twobombs/HamiltonZero/src/hamiltonzero/evaluation/runner.py
-
curl -L -o runner.py https://huggingface.co/Twobombs/HamiltonZero/resolve/main/src/hamiltonzero/evaluation/runner.py
16.7 kB
| # Copyright (c) 2026 Simulacra Research Inc. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| from __future__ import annotations | |
| import time | |
| from dataclasses import replace | |
| from typing import Any, Callable | |
| import jax | |
| import jax.numpy as jnp | |
| import numpy as np | |
| from hamiltonzero.config import EvalConfig | |
| from .backend import EvalBackend, MCMCPopulation | |
| from .statistics import EnergyWindow, P01_TWO_SIDED, select_winner_per_physical | |
| from .types import ContestCandidate, ContestResult, EvalMetric, EvalResult | |
| def _permute_q_prefix(q, permutations): | |
| index = jnp.broadcast_to(permutations[:, None, None, :, None], q.shape) | |
| return jnp.take_along_axis(q, index, axis=3) | |
| def _collapse_to_winner(q_virtual, permutations, winner_idx, *, P, K): | |
| winner_idx = jnp.asarray(winner_idx, dtype=jnp.int32) | |
| inverse = jnp.argsort(permutations, axis=-1) | |
| q_canonical = _permute_q_prefix(q_virtual, inverse) | |
| batch_per_candidate = q_canonical.shape[1] | |
| tail = q_canonical.shape[2:] | |
| q_physical = q_canonical.reshape((P, K, batch_per_candidate) + tail).reshape( | |
| (P, K * batch_per_candidate) + tail | |
| ) | |
| permutations_pk = permutations.reshape((P, K, -1)) | |
| route = jnp.take_along_axis( | |
| permutations_pk, | |
| winner_idx[:, None, None], | |
| axis=1, | |
| )[:, 0] | |
| return _permute_q_prefix(q_physical, route), route | |
| def _gather_winner_ladder(values, winner_idx, *, P, K): | |
| winner_idx = jnp.asarray(winner_idx, dtype=jnp.int32) | |
| values_pk = values.reshape((P, K) + values.shape[1:]) | |
| index = winner_idx[(slice(None), None) + (None,) * (values_pk.ndim - 2)] | |
| return jnp.take_along_axis(values_pk, index, axis=1)[:, 0] | |
| def _as_batched_route(permutation): | |
| value = jnp.asarray(permutation, dtype=jnp.int32) | |
| if value.ndim == 1: | |
| value = value[None, :] | |
| if value.ndim != 2 or value.shape[0] != 1: | |
| raise ValueError("single-system evaluation requires a route with shape [1, N]") | |
| return value | |
| def _compose_walker_route(old_inverse, route): | |
| return jnp.take_along_axis( | |
| jnp.asarray(old_inverse, dtype=jnp.int32), | |
| route, | |
| axis=-1, | |
| ) | |
| def _adapt(backend: EvalBackend, state: Any, config: EvalConfig): | |
| return backend.adapt_mcmc(state, config.mcmc) | |
| def _burn_in( | |
| backend: EvalBackend, | |
| state: Any, | |
| model: Any, | |
| context: Any, | |
| config: EvalConfig, | |
| *, | |
| iterations: int, | |
| replica_steps: int, | |
| ): | |
| for _ in range(int(iterations)): | |
| state = backend.step_mcmc( | |
| state, | |
| model, | |
| context, | |
| replica_steps=int(replica_steps), | |
| walker_chunk_size=int(config.mcmc.walker_chunk_size), | |
| ) | |
| backend.block_until_ready(backend.cold_walkers(state)) | |
| state = _adapt(backend, state, config) | |
| return state | |
| def _measure( | |
| backend: EvalBackend, | |
| state: Any, | |
| model: Any, | |
| context: Any, | |
| config: EvalConfig, | |
| *, | |
| started: float, | |
| metric_sink: Callable[[EvalMetric], None] | None, | |
| ): | |
| window = EnergyWindow( | |
| config.measurements, | |
| systems=1, | |
| batch_size=config.mcmc.batch_size, | |
| ) | |
| for step in range(config.measurements): | |
| step_started = time.perf_counter() | |
| state = backend.step_mcmc( | |
| state, | |
| model, | |
| context, | |
| replica_steps=int(config.mcmc.steps), | |
| walker_chunk_size=int(config.mcmc.walker_chunk_size), | |
| ) | |
| q_cold = backend.cold_walkers(state) | |
| total, exchange, _casimir, field = backend.custom_lap_energy( | |
| model, | |
| context, | |
| q_cold, | |
| config.energy, | |
| ) | |
| backend.block_until_ready(total) | |
| window.push(total, exchange, field) | |
| state = _adapt(backend, state, config) | |
| if metric_sink is not None: | |
| energy = np.asarray(total).real | |
| metric_sink( | |
| EvalMetric( | |
| step=step, | |
| energy=float(np.mean(energy)), | |
| energy_std=float(np.std(energy)), | |
| step_walltime=float(time.perf_counter() - step_started), | |
| walltime=float(time.perf_counter() - started), | |
| ) | |
| ) | |
| return state, window | |
| def _ordinary( | |
| backend: EvalBackend, | |
| model: Any, | |
| context: Any, | |
| canonical, | |
| mcmc_key, | |
| config: EvalConfig, | |
| ): | |
| state = backend.initialize_mcmc(mcmc_key, model, context, config.mcmc) | |
| candidates = backend.beam_candidates( | |
| model, | |
| canonical.context, | |
| beam_width=int(config.contest_beam_width), | |
| top_k=1, | |
| temperature=float(config.route_temperature), | |
| ) | |
| permutations = jnp.asarray(candidates.permutations, dtype=jnp.int32) | |
| if permutations.ndim != 3 or permutations.shape[:2] != (1, 1): | |
| raise ValueError("ordinary eval router must return shape [1, 1, N]") | |
| route = permutations[:, 0] | |
| walker_route = _compose_walker_route(canonical.old_inverse, route) | |
| routed_context = backend.route_context( | |
| canonical.context, | |
| route, | |
| compact_custom_lap=False, | |
| ) | |
| state = backend.route_mcmc(state, walker_route) | |
| wavefunction = backend.compile_single(model, routed_context) | |
| backend.block_until_ready(wavefunction) | |
| logp = float(np.asarray(candidates.log_probabilities)[0, 0]) | |
| return wavefunction, routed_context, state, route, logp, None | |
| def _compiled_finetune_ordinary( | |
| backend: EvalBackend, | |
| model: Any, | |
| context: Any, | |
| canonical, | |
| embedded_route, | |
| mcmc_key, | |
| config: EvalConfig, | |
| ): | |
| state = backend.initialize_mcmc(mcmc_key, model, context, config.mcmc) | |
| route = _as_batched_route(embedded_route) | |
| if route.shape[-1] != canonical.context.mask.shape[-1]: | |
| raise ValueError( | |
| "compiled fine-tune route width does not match the evaluation system" | |
| ) | |
| walker_route = _compose_walker_route(canonical.old_inverse, route) | |
| routed_context = backend.route_context( | |
| canonical.context, | |
| route, | |
| compact_custom_lap=False, | |
| ) | |
| state = backend.route_mcmc(state, walker_route) | |
| wavefunction = backend.compile_embedded(model) | |
| backend.block_until_ready(wavefunction) | |
| return wavefunction, routed_context, state, route, None, None | |
| def _contest( | |
| backend: EvalBackend, | |
| model: Any, | |
| canonical, | |
| root_key, | |
| config: EvalConfig, | |
| ): | |
| K = int(config.contest_candidates) | |
| batch_per_candidate = int(config.mcmc.batch_size) // K | |
| candidates = backend.beam_candidates( | |
| model, | |
| canonical.context, | |
| beam_width=int(config.contest_beam_width), | |
| top_k=K, | |
| temperature=float(config.route_temperature), | |
| ) | |
| beam_permutations = jnp.asarray(candidates.permutations, dtype=jnp.int32) | |
| if beam_permutations.ndim != 3 or beam_permutations.shape[:2] != (1, K): | |
| raise ValueError(f"contest router must return shape [1, {K}, N]") | |
| n_sites = int(beam_permutations.shape[-1]) | |
| permutations = beam_permutations.reshape((K, n_sites)) | |
| virtual_context = backend.virtual_context(canonical.context, permutations) | |
| race_mcmc = replace(config.mcmc, batch_size=batch_per_candidate) | |
| state = backend.initialize_mcmc( | |
| jax.random.fold_in(root_key, 7411), | |
| model, | |
| virtual_context, | |
| race_mcmc, | |
| ) | |
| wavefunctions = backend.compile_candidates( | |
| model, | |
| canonical.context, | |
| permutations, | |
| ) | |
| backend.block_until_ready(wavefunctions) | |
| for _ in range(int(config.contest_preburn)): | |
| state = backend.step_mcmc( | |
| state, | |
| wavefunctions, | |
| virtual_context, | |
| replica_steps=int(config.mcmc.burn_in_replica_steps), | |
| walker_chunk_size=int(config.mcmc.walker_chunk_size), | |
| ) | |
| backend.block_until_ready(backend.cold_walkers(state)) | |
| state = backend.adapt_mcmc(state, race_mcmc) | |
| race_window = EnergyWindow( | |
| config.contest_measurements, | |
| systems=K, | |
| batch_size=batch_per_candidate, | |
| ) | |
| for _ in range(int(config.contest_measurements)): | |
| state = backend.step_mcmc( | |
| state, | |
| wavefunctions, | |
| virtual_context, | |
| replica_steps=int(config.mcmc.steps), | |
| walker_chunk_size=int(config.mcmc.walker_chunk_size), | |
| ) | |
| state = backend.adapt_mcmc(state, race_mcmc) | |
| q_cold = backend.cold_walkers(state) | |
| total, exchange, _casimir, field = backend.custom_lap_energy( | |
| wavefunctions, | |
| virtual_context, | |
| q_cold, | |
| config.energy, | |
| ) | |
| backend.block_until_ready(total) | |
| race_window.push(total, exchange, field) | |
| energies = np.asarray( | |
| [[race_window.tail_mean("total", candidate) for candidate in range(K)]] | |
| ) | |
| tailstd = np.asarray( | |
| [[race_window.tail_std("total", candidate) for candidate in range(K)]] | |
| ) | |
| beam_logp = np.asarray(candidates.log_probabilities, dtype=float) | |
| winners, ties, reasons, _bands, standard_errors = select_winner_per_physical( | |
| energies, | |
| tailstd, | |
| beam_logp, | |
| batch_per_candidate, | |
| z=P01_TWO_SIDED, | |
| ucb_z=float(config.contest_se_multiplier), | |
| ) | |
| winner = int(winners[0]) | |
| wavefunction = backend.select_candidate(wavefunctions, winner) | |
| population = backend.mcmc_population(state) | |
| q_final, route = _collapse_to_winner( | |
| population.q, | |
| permutations, | |
| winners, | |
| P=1, | |
| K=K, | |
| ) | |
| sigma = _gather_winner_ladder(population.sigma, winners, P=1, K=K) | |
| beta = _gather_winner_ladder(population.beta, winners, P=1, K=K) | |
| routed_context = backend.route_context( | |
| canonical.context, | |
| route, | |
| compact_custom_lap=False, | |
| ) | |
| final_state = backend.initialize_mcmc( | |
| jax.random.fold_in(root_key, 7919), | |
| wavefunction, | |
| routed_context, | |
| config.mcmc, | |
| ) | |
| final_state = backend.replace_mcmc_population( | |
| final_state, | |
| MCMCPopulation(q=q_final, sigma=sigma, beta=beta), | |
| ) | |
| backend.block_until_ready(backend.cold_walkers(final_state)) | |
| contest_candidates = tuple( | |
| ContestCandidate( | |
| index=index, | |
| route_log_probability=float(beam_logp[0, index]), | |
| energy=float(energies[0, index]), | |
| standard_error=float(standard_errors[0, index]), | |
| walker_tail_std=float(tailstd[0, index]), | |
| in_tie_set=bool(ties[0, index]), | |
| ) | |
| for index in range(K) | |
| ) | |
| contest = ContestResult( | |
| winner=winner, | |
| reason=reasons[0], | |
| candidates=contest_candidates, | |
| ) | |
| backend.release_context(virtual_context) | |
| return ( | |
| wavefunction, | |
| routed_context, | |
| final_state, | |
| route, | |
| float(beam_logp[0, winner]), | |
| contest, | |
| ) | |
| def _large_n( | |
| backend: EvalBackend, | |
| model: Any, | |
| context: Any, | |
| canonical, | |
| mcmc_key, | |
| config: EvalConfig, | |
| ): | |
| state = backend.initialize_mcmc(mcmc_key, model, context, config.mcmc) | |
| compiled = backend.compile_large_n( | |
| model, | |
| canonical.context, | |
| sequence_shards=int(config.large_n_sequence_shards), | |
| pair_tile_size=int(config.large_n_pair_tile_size), | |
| temperature=float(config.route_temperature), | |
| ) | |
| route = _as_batched_route(compiled.permutation) | |
| walker_route = _compose_walker_route(canonical.old_inverse, route) | |
| routed_context = backend.route_context( | |
| canonical.context, | |
| route, | |
| compact_custom_lap=True, | |
| ) | |
| state = backend.route_mcmc(state, walker_route) | |
| backend.block_until_ready(compiled.wavefunction) | |
| logp = float(np.asarray(compiled.log_probability)) | |
| return compiled.wavefunction, routed_context, state, route, logp, None | |
| def _validate(config: EvalConfig) -> None: | |
| if config.contest and config.large_n: | |
| raise ValueError("contest and large_n are mutually exclusive") | |
| if int(config.measurements) < 1: | |
| raise ValueError("measurements must be positive") | |
| if int(config.mcmc.batch_size) < 1: | |
| raise ValueError("MCMC batch size must be positive") | |
| if int(config.mcmc.replicas) < 2: | |
| raise ValueError("MCMC requires at least two replicas") | |
| if int(config.mcmc.steps) < 1: | |
| raise ValueError("MCMC replica steps must be positive") | |
| if int(config.mcmc.burn_in_replica_steps) < 1: | |
| raise ValueError("burn-in replica steps must be positive") | |
| if int(config.mcmc.walker_chunk_size) < 1: | |
| raise ValueError("walker chunk size must be positive") | |
| if config.contest: | |
| K = int(config.contest_candidates) | |
| W = int(config.contest_beam_width) | |
| if K < 2 or W < K: | |
| raise ValueError("contest requires beam_width >= candidates >= 2") | |
| if int(config.mcmc.batch_size) % K: | |
| raise ValueError("MCMC batch size must be divisible by candidates") | |
| if int(config.mcmc.batch_size) // K < 32: | |
| raise ValueError("contest requires at least 32 walkers per candidate") | |
| if int(config.contest_preburn) < 0: | |
| raise ValueError("contest preburn must be non-negative") | |
| if int(config.contest_measurements) < 1: | |
| raise ValueError("contest measurements must be positive") | |
| if int(config.large_n_sequence_shards) < 0: | |
| raise ValueError("large-N sequence shards must be non-negative") | |
| if int(config.large_n_pair_tile_size) < 1: | |
| raise ValueError("large-N pair tile size must be positive") | |
| def evaluate( | |
| config: EvalConfig, | |
| backend: EvalBackend, | |
| *, | |
| metric_sink: Callable[[EvalMetric], None] | None = None, | |
| ) -> EvalResult: | |
| _validate(config) | |
| started = time.perf_counter() | |
| root_key = jax.random.PRNGKey(int(config.seed)) | |
| model_key, mcmc_key = jax.random.split(root_key) | |
| context = backend.load_system(config.system, config.energy) | |
| model = backend.load_model( | |
| config.checkpoint, | |
| config.model, | |
| model_key, | |
| context, | |
| contextualizer_attention=config.contextualizer_attention, | |
| ) | |
| canonical = backend.canonicalize_context(context) | |
| embedded_route = backend.embedded_route(model) | |
| if embedded_route is not None and (config.contest or config.large_n): | |
| raise ValueError( | |
| "compiled fine-tune checkpoints support ordinary eval only; " | |
| "contest and large_n require a router checkpoint" | |
| ) | |
| if embedded_route is not None: | |
| prepared = _compiled_finetune_ordinary( | |
| backend, | |
| model, | |
| context, | |
| canonical, | |
| embedded_route, | |
| mcmc_key, | |
| config, | |
| ) | |
| path = "ordinary" | |
| elif config.contest: | |
| prepared = _contest( | |
| backend, | |
| model, | |
| canonical, | |
| root_key, | |
| config, | |
| ) | |
| path = "contest" | |
| elif config.large_n: | |
| prepared = _large_n( | |
| backend, | |
| model, | |
| context, | |
| canonical, | |
| mcmc_key, | |
| config, | |
| ) | |
| path = "large_n" | |
| else: | |
| prepared = _ordinary( | |
| backend, | |
| model, | |
| context, | |
| canonical, | |
| mcmc_key, | |
| config, | |
| ) | |
| path = "ordinary" | |
| wavefunction, routed_context, state, route, route_logp, contest = prepared | |
| del model, context, canonical, embedded_route, prepared | |
| wavefunction, routed_context, state = backend.prepare_singular( | |
| wavefunction, | |
| routed_context, | |
| state, | |
| ) | |
| state = _burn_in( | |
| backend, | |
| state, | |
| wavefunction, | |
| routed_context, | |
| config, | |
| iterations=int(config.mcmc.burn_in), | |
| replica_steps=int(config.mcmc.burn_in_replica_steps), | |
| ) | |
| _state, window = _measure( | |
| backend, | |
| state, | |
| wavefunction, | |
| routed_context, | |
| config, | |
| started=started, | |
| metric_sink=metric_sink, | |
| ) | |
| route_host = np.asarray(route, dtype=np.int32) | |
| if route_host.shape[0] != 1: | |
| raise ValueError("single-system eval produced more than one route") | |
| return EvalResult( | |
| path=path, | |
| route=tuple(int(value) for value in route_host[0]), | |
| route_log_probability=(None if route_logp is None else float(route_logp)), | |
| measurements=int(window.count), | |
| walltime_seconds=float(time.perf_counter() - started), | |
| energy=window.metrics("total"), | |
| channels={ | |
| "exchange": window.metrics("exchange"), | |
| "field": window.metrics("field"), | |
| }, | |
| contest=contest, | |
| ) | |