Calculate the Ground State Energy Using the BEAST-VQE
Goal
Run a BEAST-VQE calculation for a prepared ground-state problem and obtain the ground-state energy.
Prerequisites
A ground-state problem (see Construct a Ground State Energy Problem)
Steps
Build the default BEAST-VQE calculator
The
calculator_creator()is a fluent builder (see Understanding and Using Kvantify Qrunch’s Fluent Builder Pattern) that configures and returns aGroundStateProblemCalculator. By default,.vqe().iterative().beast()builds a BEAST-VQE calculator with conservative defaults to avoid lengthy runs on real hardware.import qrunch as qc beast_vqe_calculator = ( qc.calculator_creator() .vqe() .iterative() .beast() .create() )
Use the default BEAST-VQE calculator to calculate the ground state energy
You can now calculate the result of an existing
ground_state_problem:result = beast_vqe_calculator.calculate(ground_state_problem)
The result is a
GroundStateProblemCalculatorResultobject that contains:Electronic energy of the active electrons
Electronic energy of all electrons
Total molecular energy
Electronic energy per macro iteration (list of values for each VQE iteration)
Customize the VQE settings
The most common setting to adjust is
max_iterationsinIterativeVqeOptions. The default is 5 iterations, which is is often insufficient for convergence but it is intentionally low to avoid long, costly runs on quantum hardware.import qrunch as qc adaptive_vqe_options = qc.options.IterativeVqeOptions( max_iterations=100, ) beast_vqe_calculator = ( qc.calculator_creator() .vqe() .iterative() .beast() .with_options(options=adaptive_vqe_options) .create() )
See the API reference for
IterativeVqeOptionsfor the full list of options.You can configure additional options before calling
.create():Set the estimator:
.with_estimator(my_estimator)
Setting an estimator to be used for parameter optimization. See Create an Estimator for more details.
Define number of measurement shots:
.with_estimator_shots(1000)
Setting the number of shots to use in the estimator when doing parameter optimization. Setting to
Noneuses an exact simulator.Choose classical minimizer:
.choose_minimizer() # .<pick-a-minimizer>(...)
You can choose which classical minimizer you want to use to optimize the parameters. See Choose a Minimizer for available options.
Choose reminimizer:
.choose_reminimizer() # .<pick-a-minimizer>(...)
You can choose a classical minimizer if you want to re-optimize the parameters at the end of a VQE run. See Choose a Minimizer for available options.
Set stopping criteria:
.choose_stopping_criterion() # .<pick-a-stopping-criterion>(...)
You can choose the stopping criterion for the iterative VQE. See Choose a Stopping Criterion for available options.
Select a custom gate selector:
.with_gate_selector(my_gate_selector)
You can provide a custom gate selector to control which excitation gate is added at each iteration. See Create a FAST Gate Selector for how to build a
gate_selector.Configure data persistence:
.choose_data_persister_manager() # .<pick-a-persister>(...)
You can choose how and where to save/load intermediate data during the VQE run. See Choose a Data Persister Manager for available options.
Configure analytical BEAST-VQE:
.with_analytical_beast_basic_vqe(active=True)
Choose to use the analytical basic VQE inside each adaptive iteration.
Instead of having the minimizer call the estimator directly, the estimator is first called to generate an analytical expression for the energy as a function of the gate parameter. This expression is then passed to the minimizer, requiring no more measurements.
Note
This feature only works for BEAST-VQE with last parameter optimization - the default. If you have chosen a different minimizer, you may need to deactivate this feature.
OO-BEAST-VQE (Orbital-Optimized BEAST-VQE)
OO-BEAST-VQE augments BEAST-VQE with orbital optimization, improving accuracy for some systems. During the adaptive loop an intermittent orbital optimizer keeps the orbital rotations close to the global minimum as gates are added. After the loop, an optional final orbital optimizer (potentially with basin-hopping) polishes the result.
import qrunch as qc adaptive_vqe_options = qc.options.IterativeVqeOptions( max_iterations=100, ) oo_beast_vqe_calculator = ( qc.calculator_creator() .vqe() .iterative_with_orbital_optimization() .beast() .with_options(options=adaptive_vqe_options) .create() )
The OO-BEAST-VQE has all the same configuration options as BEAST-VQE, and in addition you can also:
Define the intermittent orbital optimizer (runs during the adaptive loop):
.with_orbital_optimizer(intermittent_optimizer)
This optimizer runs at selected iterations during the adaptive VQE to keep orbital rotations small. Use loose convergence criteria here, as the gate parameters will shift on the next iteration. See Create an Orbital Optimizer for how to construct an optimizer.
Note if you just want to select which estimator the orbital optimizer uses, you can use the
with_orbital_optimizer_estimator.Control the estimator of the intermittent orbital optimization:
.with_orbital_optimizer_estimator(estimator)
This is a simple way to specify the estimator to used for the orbital optimizer.
Control the estimator shots of the intermittent orbital optimization:
.with_orbital_optimizer_estimator_shots(1000)
This is a simple way to specify the number of estimator shots to used for the orbital optimizer.
Control when intermittent optimization runs:
.with_intermittent_orbital_optimizer_options( qc.options.IntermittentOrbitalOptimizerAlgorithmOptions( every_nth_iteration=1, # consider OO every iteration gradient_threshold=1e-2, # below this: single Newton step instead of full OO skip_gradient_threshold=1e-16, # below this: skip OO entirely orbital_change_threshold=1e-3, # above this orbital change: force full OO force_full_after_n_non_full_steps=10, # force full OO after 10 consecutive non-full steps gate_addition_threshold=1e-3, # if gate addition changes energy above this: full OO ) )
The
every_nth_iterationdetermine how often the intermittent orbital optimizer runs. Setting it to 1 considers orbital optimization at every iteration (Recommended), while setting it to a larger number reduces the frequency.The intermittent optimizer uses a three-tier strategy based on the orbital gradient norm:
Above
gradient_threshold→ a full converged orbital optimization is performed.Between
skip_gradient_thresholdandgradient_threshold→ a single Newton step is taken. This prevents slow drift as new gates are added.Below
skip_gradient_threshold→ the OO step is skipped entirely.
Additionally, a full optimization is triggered when:
The orbital change from the previous step exceeds
orbital_change_threshold.The energy change due to a gate addition exceeds
gate_addition_threshold.
A forced full optimization after
force_full_after_n_non_full_stepsconsecutive non-full decisions acts as a safety net. Set to0to disable.See
IntermittentOrbitalOptimizerAlgorithmOptionsfor the full list of options.Alternatively, use the preset-based selection via the builder:
.choose_intermittent_orbital_optimizer_options() .quick() # or .balanced(), .accurate()
Or use the class methods directly:
.with_intermittent_orbital_optimizer_options( qc.options.IntermittentOrbitalOptimizerAlgorithmOptions.quick() )
Available presets:
quick()— Always takes a single Newton step; never runs full OO.balanced()— Single Newton step each iteration; full OO after 10 consecutive non-full steps or when a gate addition causes a large energy change.accurate()— Full OO at nearly every iteration.
See Create an Orbital Optimizer for details on each preset.
Define the final orbital optimizer (runs once after the adaptive loop):
.with_final_orbital_optimizer(final_optimizer)
This optimizer runs a single polishing step after the adaptive loop completes. It is recommended to use tight convergence and enable basin-hopping to especially when the intermittent optimizer was not used (setting
every_nth_iterationlarger than the maximum number of iterations andforce_full_after_n_non_full_stepsto0).import qrunch as qc final_optimizer = ( qc.orbital_optimizer_creator() .newton() .choose_gradient_calculator() .local_gradient( estimator=qc.estimator_creator().excitation_gate().create() ) .with_shots(None) .with_options(qc.options.NewtonMinimizerOptions( relative_error_tolerance=1e-6, )) .with_basin_hopping_options(qc.options.OrbitalOptimizerBasinHoppingOptions( active=True, )) .create() )
See Create an Orbital Optimizer for full details on basin-hopping and the distinction between intermittent and final optimizers.
Choose a reminimizer for final gate-parameter re-optimization:
.choose_reminimizer() # .<pick-a-minimizer>(...)
If set, all gate parameters are re-optimized (with orbital rotations locked) before the final orbital optimization step.
Note
choose_orbital_optimizer_stopping_criterion() is deprecated.
Use .with_intermittent_orbital_optimizer_options(...) instead to control
when the intermittent orbital optimizer runs.
Optimal Performance
To optimize performance and cost when running on real quantum hardware, consider the following:
import qrunch as qc adaptive_vqe_options = qc.options.IterativeVqeOptions( max_iterations=100, ) beast_vqe_calculator = ( qc.calculator_creator() .vqe() .iterative() .beast() .with_options(options=adaptive_vqe_options) .with_analytical_beast_basic_vqe(active=True) .choose_minimizer() .last_variable_fft() .create() )
This configuration uses the FFT minimizer for the optimization of the parameter of each gate, and it uses a specialized analytical BEAST-VQE that reduces the number of quantum measurements needed. As with all greedy algorithms, there is a trade-off between performance and accuracy, so validate the results carefully.
It is possible to improve the accuracy by using an efficient reminimizer.
Verify the Result
Check that the
result.total_energyis correct.For simulator runs, repeated calculations should produce the same
.valuewith.errorclose to zero unless the simulator has a finite shot count.On hardware,
.errorwill reflect shot noise from finite measurements, and other noise sources, such as decoherence.An error like
Exception: Invalid single excitation gate. You might want to use mixed spin?indicate that you have mixed a calculator that assume Bosonic encoding with a VQE instance that uses Fermionic encoding.