Parallel tempering / replica exchange¶
Track: exact-K binary selection. Method ID: parallel-tempering.
parallel_tempering_binary_baseline implements replica-exchange Metropolis
search for the original binary mean–variance objective, without a cardinality
penalty. The method follows
Hukushima and Nemoto’s exchange Monte Carlo procedure,
adapted to exact-K portfolio swaps and a fixed temperature ladder. NumPy is the
only dependency. Replicas execute serially; the algorithm does not require
threads, multiprocessing, or quantum execution.
Local moves and exchanges¶
Each temperature slot holds a feasible portfolio and its cached energy, gradient, selected indices, and unselected indices. Every iteration proposes one uniform selected/unselected exchange in each slot. For objective change \(\Delta=f(x')-f(x)\) and temperature \(T\), accept with probability \(\min(1,\exp(-\Delta/T))\). The symmetric proposal preserves exactly K assets. Quadratic swap deltas use the maintained gradient; accepted portfolios are rescored using the original objective before considering them as incumbents.
Every exchange_interval iterations, attempt exchanges between disjoint adjacent
temperature slots. Alternate edges (0,1), (2,3), ... and (1,2), (3,4), ....
With two replicas, attempt their single edge every exchange round.
For neighboring temperatures \(T_i<T_j\), accept an exchange with probability
This is the ratio of swapped to current product-Boltzmann weights. Exchange the complete states and caches while keeping temperatures fixed. All acceptance tests use log ratios and exponential random draws, avoiding exponentiation of large energies. Exchanges rearrange already evaluated states; they do not create new objective evaluations. The retained best portfolio spans every temperature and restart, including initial states and states later left behind.
Configuration and diagnostics¶
Defaults are seed=0, restarts=16, max_iterations=1000, n_replicas=8, and
exchange_interval=10. An iteration means one proposal per replica, so a
completed nontrivial run makes restarts * max_iterations * n_replicas local
proposals. It is not equivalent to the same iteration count of single-chain
simulated annealing. Use end-to-end budgets for performance comparisons.
The default ladder is geometric from 0.01*S to S, where S is the largest
absolute swap cost from the top-return selection. A flat starting neighborhood
uses S=1; nonzero scales have a numerical floor of 100 * finfo(float).tiny.
Scale construction evaluates K*(n-K) swap costs and uses no reference optimum.
The ladder remains fixed across all restarts; it does not implement adaptive
temperature tuning. Poor ladder choices can prevent useful exchanges.
Pass temperatures to supply exactly n_replicas strictly increasing, positive,
finite values. At least two replicas are required. All count parameters must be
positive integers. Across all replicas and restarts, initialization uses top
returns, then diagonal costs, then seeded random exact-K subsets. restarts
counts fresh groups of replica states sharing the fixed ladder.
The result has status iteration_limit, or trivial for K=0/K=n. Neither a
finite search nor a high acceptance rate certifies optimality or equilibrium.
lower_bound and mip_gap remain absent. Nonfinite evaluated swap costs or
portfolio energies raise an error.
ClassicalResult.diagnostics records the ladder and controls, completed restarts
and iterations, local proposals and accepts by temperature, exchange attempts
and accepts by adjacent edge, acceptance rates, original-objective evaluations,
and swap evaluations used to choose the default scale. Rates are None before
an attempt. The message includes the same diagnostics as JSON for comparison
notes; validation results expose solver_diagnostics directly.
An optional candidate_callback(candidate, details) receives independent copies
only on strict incumbent improvement. Its details include the source, restart,
iteration, temperature slot, and a copied diagnostic snapshot. Within an
iteration the snapshot can contain proposals from a partially completed sweep.
Callbacks do not alter the solver’s random stream or retained state.
The budget API and CLI return final solver_diagnostics when completion metadata
arrives within the deadline. On timeout or failure that field remains empty;
delivered incumbents retain their partial diagnostic snapshots. The budget
includes startup, initialization, scale selection, search, callbacks, and
independent candidate checks. Budget and validation CSVs encode structured
diagnostics as JSON. Archived study outputs are unchanged.
Examples¶
Use the shared inputs.
from vqe_portfolio.baselines import parallel_tempering_binary_baseline
result = parallel_tempering_binary_baseline(
mu, Sigma, lam=4.0, k=2, seed=0,
restarts=2, n_replicas=4, temperatures=[0.01, 0.05, 0.2, 1.0],
)
print(result.candidate, result.objective, result.diagnostics)
vqe-portfolio budget --method parallel-tempering \
--mu "0.08,0.12,0.10" \
--sigma "0.04,0.01,0;0.01,0.09,0.01;0,0.01,0.06" \
--lam 4 --k 2 --seed 0 --budget-seconds 10
python scripts/generate_comparison_results.py --methods parallel-tempering,anneal \
--asset-counts 4,8 --k 2 --seeds 0,1,2 --local-restarts 2 \
--output results/tempering_summary.csv --trials-output results/tempering_trials.csv
The small-instance validation and fixed-deadline runners accept the same method ID. Comparison and validation use every configured solver seed. The budget interface uses solver defaults; the ordinary Python API exposes ladder controls.