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.
AlgorithmFamilyis 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:
AlgorithmSpec(inexecution) carries only identity — a library-specificname(e.g."ADAPTVQE","UCCNVQE"), an optional package-agnosticfamily(AlgorithmFamily), and anextra_paramsescape hatch. It holds no tuning knobs.- Hyperparameters live in a family- or library-specific config next to
the code that consumes them —
AdaptVQERunConfig(the package-agnostic ADAPT-VQE contract),dlr_excitation_solve.ExcitationSolveConfig,mqsdk_xenakis.GAConfig,mqsdk_cebule.TNQCOptInput,microsoft_qdk.QPEConfig,evangelistalab_qforte.QForteAlgorithmConfig, …
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:
-
VQAConfig(onBenchmarkRecord.vqa, inrecord) is experiment metadata — it labels what you ran (problem_type,molecule,basis,algorithm,ansatz,optimizername) so records are filterable and comparable in the store. By contract it hasextra="forbid"and changes nothing about execution. A QAOA run names itself here withproblem_type="optimization"andalgorithm="QAOA"; its actual knobs (p, mixer, optimizer) live inQAOARunConfig, not as newVQAConfigfields — same layering as ADAPT-VQE below.from qpubench.schemas import VQAConfig vqa = VQAConfig( problem_type="chemistry", molecule="H2", basis="sto-3g", algorithm="VQE", # or "ADAPTVQE", "QAOA", ... ansatz="UCCSD", optimizer="COBYLA", ) -
AdaptVQERunConfig(onExecutionOptions.adapt_vqe_run_config, inexecution) is a runtime hyperparameter contract — the knobs an adapter actually reads to drive the adaptive loop (operator pool, gradient/energy thresholds, macro/micro iteration caps). It sits onExecutionOptions, next toZNEConfig/TranspilerConfig, because that is the “how to execute” layer.
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_gsoptalso 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 ownGSOpt*RunConfig. See GSOpt.
Why it is not folded into VQAConfig:
- Layer separation.
VQAConfigis record metadata with no execution effect;AdaptVQERunConfigdrives execution. Merging them would put an execution-driving struct inside an object defined to have none. - Package-agnostic reuse.
AdaptVQERunConfigis 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 onExecutionOptionswhere every adapter reads it, not on a per-record metadata object. Keeping each algorithm family’s real knobs in their own config (keyed byAlgorithmFamily) also stopsVQAConfigballooning into a grab-bag of every algorithm’s parameters — the same reason QAOA’s knobs went intoQAOARunConfigrather than ontoVQAConfig.
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.
CircuitFormathas noVQEmember — VQE is an algorithm, not a way of serializing a circuit. See theCircuitFormatnote.
Adding a new algorithm
- 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). - 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. - Set
AlgorithmSpec(name=..., family=...)onExecutionOptions. - 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.