Skip to the content.

Algorithms & AlgorithmSpec

This page is the conceptual companion to the field-level reference in schemas.md → AlgorithmSpec. It explains how qpubench names algorithms, where their hyperparameters live, and how the same algorithm run through different libraries stays comparable in one store.

Status. AlgorithmFamily is still provisional and being refined — see the ToDo in the schema reference. Treat the member set as subject to change.


The core split: identity vs. hyperparameters

qpubench deliberately separates which algorithm you ran from how it was configured:

Why not one big config struct? Because each library’s knobs are genuinely different, and a shared grab-bag would either lie about what a given adapter honours or collapse under optional fields. Keeping the config next to its consumer mirrors the same split used for backends and error mitigation.


AlgorithmFamily — the comparison key

family is what makes “the same algorithm, different engine” a first-class comparison. Set it, and records from different implementing adapters converge on one label you can group by in the store.

A family names a broad strategy, deliberately coarser than a specific ansatz or optimizer. Fixed-ansatz VQE is a single family, VQE, whether the ansatz is UCC-type (QForte’s UCCNVQE) or hardware-efficient, and whether the parameters are fit by BFGS or by ExcitationSolve’s Fourier-series sweep — those are choices under VQE, carried in VQAConfig.ansatz / .optimizer and the run-config, not families of their own. (The same Fourier-series optimizer is likewise a choice under ADAPT_VQE when it fits an adaptively-grown ansatz.) Keeping the granularity at the strategy level is what lets a UCC-VQE run and a hardware-efficient-VQE run land on one comparable label.

Comparability across engines only pays off once two or more adapters accept the same shared config for a family. Today that is true for exactly one family:

AlgorithmFamily Implementing adapters Shared config
ADAPT_VQE evangelistalab_qforte, ibm_qiskit_adapt_vqe, microsoft_qdk_adapt_vqe AdaptVQERunConfig
VQE evangelistalab_qforte (UCCNVQE — a UCC ansatz choice); the dlr_excitation_solve Fourier-series optimizer also runs under this family VQERunConfig (per-adapter extras still in QForteAlgorithmConfig / ExcitationSolveConfig)
UCC_PQE / SPQE evangelistalab_qforte only QForteAlgorithmConfig
QAOA none yet (runs as a plain optimization loop) QAOARunConfig
TN_QC_OPT mqsdk_cebule only TNQCOptInput
GA_CIRCUIT_SEARCH mqsdk_xenakis only GAConfig / GenomeConfig
QPE none (schema/metadata only) microsoft_qdk.QPEConfig

The single-implementation families still carry a family tag — not because a comparison is possible today, but so a second implementation has a name to converge on instead of inventing its own ad-hoc label. The same goes for a family’s shared config: VQE and QAOA each own one (VQERunConfig, QAOARunConfig) so an implementation has a contract to accept, even though neither is yet accepted by two adapters the way AdaptVQERunConfig is. QAOA has no AlgorithmAdapter at all today — you run the fixed cost/mixer ansatz through any backend in a plain optimization loop (see Variational quantum algorithms).


VQAConfig & RunConfig

VQAConfig is the single config that describes a variational run (VQE, ADAPT-VQE, or QAOA). A natural question is why isn’t the ADAPT-VQE configuration just an argument on VQAConfig? Because the two live on different objects, at different layers, and answer different questions:

The *RunConfig objects

Each variational family owns one package-agnostic runtime contract, keyed by AlgorithmFamily and set on the matching ExecutionOptions field. All three live in execution:

Config Set on AlgorithmFamily Implementations today Key fields
VQERunConfig ExecutionOptions.vqe_run_config VQE 1 — evangelistalab_qforte (UCCNVQE); dlr_excitation_solve is an optimizer choice under the same family ansatz, layers, optimizer, max_iterations, energy_threshold, initialization, initial_parameters, init_scale, use_analytic_gradient
AdaptVQERunConfig ExecutionOptions.adapt_vqe_run_config ADAPT_VQE 3 — evangelistalab_qforte, ibm_qiskit_adapt_vqe, microsoft_qdk_adapt_vqe, all accepting it unchanged pool_type, optimizer, gradient_threshold, energy_threshold, max_macro_iterations, max_micro_iterations, use_analytic_gradient
QAOARunConfig ExecutionOptions.qaoa_run_config QAOA 0 — no adapter yet; a plain optimization loop reads it (see Running QAOA) reps (p), mixer, optimizer, max_iterations, initialization, alpha_cvar

The three are independent fields, not alternatives: nothing stops a caller populating several, and only the one algorithm_spec.family names is read.

VQERunConfig is the fixed-ansatz case — the ansatz is chosen up front and never grows, so there is no operator pool and no gradient-driven macro loop. That is the whole difference from AdaptVQERunConfig, and why layers (a depth) replaces max_macro_iterations (a growth budget). It carries no seed: ExecutionOptions.seed already governs the random draw when initialization="random".

Not to be confused with GSOpt. bestquark_gsopt also has VQE-ish configs, but they are records of how one benchmark run was parameterised, not contracts — and GSOpt is a method-agnostic harness whose VQE lane sits alongside TN, DMRG, AFQMC and Gibbs lanes, so each lane has its own GSOpt*RunConfig. See GSOpt.

Why it is not folded into VQAConfig:

  1. Layer separation. VQAConfig is record metadata with no execution effect; AdaptVQERunConfig drives execution. Merging them would put an execution-driving struct inside an object defined to have none.
  2. Package-agnostic reuse. AdaptVQERunConfig is one shared contract that three different ADAPT-VQE adapters (evangelistalab_qforte, ibm_qiskit_adapt_vqe, microsoft_qdk_adapt_vqe) all accept unchanged — the “switch the adapter, keep the config” story. It belongs on ExecutionOptions where every adapter reads it, not on a per-record metadata object. Keeping each algorithm family’s real knobs in their own config (keyed by AlgorithmFamily) also stops VQAConfig ballooning into a grab-bag of every algorithm’s parameters — the same reason QAOA’s knobs went into QAOARunConfig rather than onto VQAConfig.

So VQAConfig names the experiment and a family’s run-config configures the run, and every variational benchmark uses both: an ADAPT-VQE run pairs VQAConfig with ExecutionOptions.adapt_vqe_run_config, a QAOA run with .qaoa_run_config, a fixed-ansatz VQE run with .vqe_run_config. Each energy evaluation is an ordinary CircuitSpec run through a BackendAdapter; the runner derives VQAResult.final_eigenvalue from the measured expectation values.

CircuitFormat has no VQE member — VQE is an algorithm, not a way of serializing a circuit. See the CircuitFormat note.


Adding a new algorithm

  1. Pick or propose an AlgorithmFamily. Reuse an existing member if your algorithm is the same family as an existing one (so records compare); add a member only for a genuinely new family (and update the ToDo discussion).
  2. Put hyperparameters in a config next to your adapter, not in AlgorithmSpec. If your family already has a shared config (e.g. AdaptVQERunConfig), accept that so you inherit the “switch the adapter, keep the config” story.
  3. Set AlgorithmSpec(name=..., family=...) on ExecutionOptions.
  4. Return a VQAResult (algorithm adapters) or let the runner derive one from expectation values (circuit-path runs).

See Backends & adapters for the adapter protocols and how to test an adapter with the SDK mocked out.