Running FAST-VQE on Quantum Computers
This example demonstrates how to run FAST-VQE with custom estimators and samplers — including Amazon Braket’s local simulator interfaces, and real quantum hardware - specifically the Rigetti Cepheus1108Q device.
We will also estimate the total shot counts and the approximate execution cost before running the on the Rigetti Cepheus1108Q device.
Full Script
The script below shows the complete workflow; each section is explained afterward.
1"""Run FAST-VQE with non-default estimators and samplers."""
2# ruff: noqa # ruff: ignore[noqa-comments]
3
4from pathlib import Path
5
6import matplotlib.pyplot as plt
7import numpy as np
8from braket.devices import Devices
9
10import qrunch as qc
11
12
13def plot_energies(
14 fast_convergence: list[tuple[list[float], list[float], str]],
15 *,
16 outdir: Path = Path("dist"),
17 show: bool = False,
18) -> None:
19 """
20 Plot VQE energy convergence and log-scale error vs reference energy.
21
22 Args:
23 fast_convergence: Sequence of energies, errors and label.
24 outdir: Output directory where plots are written. Defaults to "dist".
25 show: Whether to display the figures interactively after saving.
26 """
27 # Create figure and axis
28 fig, ax = plt.subplots(figsize=(8, 5))
29
30 # Ensure output directory exists.
31 outdir.mkdir(parents=True, exist_ok=True)
32
33 for fast_energies, energy_errors, label in fast_convergence:
34 energies = np.array(fast_energies)
35 errors = np.array(energy_errors)
36
37 # Prepare x-axis as 1-based iteration indices for readability.
38 iterations = np.arange(1, len(energies) + 1)
39
40 # Plot error bars
41 ax.errorbar(
42 iterations,
43 energies,
44 yerr=errors,
45 fmt="o",
46 capsize=5,
47 elinewidth=1.2,
48 markeredgewidth=1.2,
49 label=label,
50 )
51
52 # Label axes and title
53 ax.set_xlabel("Iteration")
54 ax.set_ylabel("Total Energy [Hartree]")
55 ax.set_title("FAST-VQE Convergence with Error Bars due to shot Noise, and emulated noise")
56
57 # Grid and legend
58 ax.grid(True, linestyle="--", alpha=0.5)
59 ax.legend()
60
61 # Tight layout and save plots
62 fig.tight_layout()
63
64 convergence_plot = outdir / "vqe_convergence.png"
65 fig.savefig(convergence_plot, dpi=200, bbox_inches="tight")
66 if show:
67 plt.show()
68 else:
69 # Close figures to free memory when running in batch contexts.
70 plt.close(fig)
71
72
73
74
75def main(
76 *,
77 do_local_braket: bool = True,
78 do_noisy_sampler: bool = True,
79 do_real_hardware: bool = False,
80) -> tuple[float, float]:
81 """
82 Run FAST-VQE on LiH with different estimators and samplers.
83
84 Args:
85 do_local_braket: Whether to run with the local Braket backend simulator.
86 do_noisy_sampler: Whether to run with a noisy sampler emulating Cepheus1108Q.
87 do_real_hardware: Whether to run with real quantum hardware (Cepheus1108Q).
88
89 """
90
91 # Build Lithium Hydride (LiH) molecular configuration.
92 molecular_configuration = qc.build_molecular_configuration(
93 molecule=[
94 ("H", 0.0, 0.0, 0.0),
95 ("Li", 1.5474, 0.0, 0.0),
96 ],
97 basis_set="sto3g",
98 )
99 # Build ground state problem.
100 problem_builder = qc.problem_builder_creator().ground_state().standard().create()
101 ground_state_problem = problem_builder.build_unrestricted(molecular_configuration)
102
103 # Excitation gate estimator (Kvantifys proprietary chemistry tailored state vector simulator)
104 excitation_gate_estimator = (
105 qc
106 .estimator_creator()
107 .excitation_gate() # Narrow the estimator type to an excitation gate estimator
108 .with_parallel_setting("parallel") # Configure parallelization behavior: "serial" or "parallel"
109 .with_spin_particle_conservation() # Configure to conserve alpha and beta electrons separately
110 .choose_estimator_error_mitigator() # Start sub-choice: Choose estimator error mitigator
111 .symmetry_adapted() # Perform the sub-choice selection - Here we pick the symmetry adapted error mitigator
112 .create(with_shot_counter=True) # Create the estimator instance
113 )
114
115 # Excitation gate sampler (Kvantifys proprietary chemistry tailored state vector simulator)
116 excitation_gate_sampler = (
117 qc
118 .sampler_creator()
119 .excitation_gate() # Narrow the sampler type to an excitation gate sampler
120 .with_parallel_setting("parallel") # Configure parallelization behavior: "serial" or "parallel"
121 .with_spin_particle_conservation() # Configure to conserve alpha and beta electrons separately
122 .create(with_shot_counter=True) # Create the sampler instance
123 )
124
125 # A FAST gate selector using the excitation gate sampler.
126 excitation_gate_gate_selector = (
127 qc
128 .gate_selector_creator()
129 .fast() # Narrow the gate selector type to a FAST gate selector
130 .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling
131 .with_sampler(excitation_gate_sampler) # Configure to use the excitation gate sampler
132 .create() # Create the gate selector instance
133 )
134
135 # Build the user-configured VQE instance with the excitation_gate_estimator
136 # and gate selector that uses the excitation_gate_sampler
137 excitation_gate_fast_vqe_calculator = (
138 qc
139 .calculator_creator() # Start creating a VQE instance
140 .vqe() # Narrow to Variational quantum eigensolver (VQE)
141 .iterative() # Narrow to the iterative VQE
142 .standard() # Narrow to the standard FAST-VQE
143 .with_options( # Configure to use the user-defined iterative VQE options
144 options=qc.options.IterativeVqeOptions(max_iterations=10)
145 )
146 .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer
147 .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer
148 .with_estimator(excitation_gate_estimator) # Configure to use the excitation gate estimator
149 .with_estimator_shots(100_000) # Configure to use 100,000 shots in the estimator
150 .with_gate_selector(excitation_gate_gate_selector) # Configure to use the gate selector
151 .create() # Create the calculator instance
152 )
153 excitation_gate_result = excitation_gate_fast_vqe_calculator.calculate(ground_state_problem)
154
155 print("result from excitation gate estimator and sampler:")
156 print(excitation_gate_result)
157
158 print("Total number of shots used in estimator", excitation_gate_estimator.total_shots)
159 estimated_estimator_cost = excitation_gate_estimator.total_braket_price(device=Devices.Rigetti.Cepheus1108Q)
160 print("Estimated cost in dollars for Estimator on Devices.Rigetti.Cepheus1108Q: ", estimated_estimator_cost)
161
162 print("Total number of shots used in sampler", excitation_gate_sampler.total_shots)
163 estimated_sampler_cost = excitation_gate_sampler.total_braket_price(device=Devices.Rigetti.Cepheus1108Q)
164 print("Estimated cost in dollars for Sampler on Devices.Rigetti.Cepheus1108Q: ", estimated_sampler_cost)
165
166 local_braket_result = None
167 if do_local_braket:
168 # An Amazon Local Braket backend estimator
169 local_braket_estimator = (
170 qc
171 .estimator_creator()
172 .backend() # Narrow the estimator type to a backend estimator
173 .choose_backend() # Start sub-choice: Choose backend
174 .local_amazon_braket() # Perform the sub-choice selection - Here we pick the local Braket state vector simulator backend
175 .create(with_shot_counter=True) # Create the estimator instance
176 )
177
178 # An Amazon Local Braket backend sampler
179 local_braket_sampler = (
180 qc
181 .sampler_creator()
182 .backend() # Narrow the sampler type to a backend sampler
183 .choose_backend() # Start sub-choice: Choose backend
184 .local_amazon_braket() # Perform the sub-choice selection - Here we pick the local Braket simulator backend
185 .create(with_shot_counter=True) # Create the estimator instance
186 )
187
188 # A FAST gate selector using the local braket sampler.
189 local_braket_gate_selector = (
190 qc
191 .gate_selector_creator()
192 .fast() # Narrow the gate selector type to a FAST gate selector
193 .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling
194 .with_sampler(local_braket_sampler) # Configure to use the local braket sampler
195 .create() # Create the gate selector instance
196 )
197
198 # Create the calculator instance using the local_braket_estimator
199 # and gate selector that uses the local_braket_sampler
200 local_braket_fast_vqe_calculator = (
201 qc
202 .calculator_creator() # Start creating a calculator instance
203 .vqe() # Narrow: pick Variational quantum eigensolver (VQE)
204 .iterative() # Narrow to the iterative VQE
205 .standard() # Narrow to the standard FAST-VQE
206 .with_options( # Configure to use the user-defined iterative VQE options
207 options=qc.options.IterativeVqeOptions(max_iterations=10)
208 )
209 .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer
210 .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer
211 .with_estimator(local_braket_estimator) # Configure to use the local braket estimator
212 .with_total_estimator_shots(100_000) # Configure to use a 100,000 shots in the estimator
213 .with_gate_selector(local_braket_gate_selector) # Configure to use the gate selector
214 .create() # Create the calculator instance
215 )
216 local_braket_result = local_braket_fast_vqe_calculator.calculate(ground_state_problem)
217
218 print("Total number of shots used in estimator", local_braket_estimator.total_shots)
219 estimated_estimator_cost = local_braket_estimator.total_braket_price(device=Devices.Rigetti.Cepheus1108Q)
220 print("Estimated cost in dollars for Estimator on Devices.Rigetti.Cepheus1108Q: ", estimated_estimator_cost)
221
222 print("Total number of shots used in sampler", local_braket_sampler.total_shots)
223 estimated_sampler_cost = local_braket_sampler.total_braket_price(device=Devices.Rigetti.Cepheus1108Q)
224 print("Estimated cost in dollars for Sampler on Devices.Rigetti.Cepheus1108Q: ", estimated_sampler_cost)
225
226 print("result from local braket estimator and sampler:")
227 print(local_braket_result)
228
229 noisy_result = None
230 if do_noisy_sampler:
231
232 # Create a real Cepheus1108Q Quantum Processor hardware backend.
233 backend = qc.backend_creator().amazon_braket(device=Devices.Rigetti.Cepheus1108Q).create()
234
235 # Get device data from the backend - from the Cepheus1108Q Quantum Processor
236 cepheus1108q_device_data = backend.get_device_data()
237
238 # An Amazon Local Braket backend simulator sampler
239 # that emulate the noise from the Cepheus1108Q Quantum Processor
240 # using the density matrix simulator.
241 noisy_sampler = (
242 qc
243 .sampler_creator()
244 .backend() # Narrow the estimator type to a backend estimator
245 .choose_backend() # Start sub-choice: Choose backend
246 .local_amazon_braket(
247 device_to_simulate=cepheus1108q_device_data
248 ) # Perform the sub-choice selection - Here the local braket emulating Cepheus1108Q
249 .choose_sampler_error_mitigator() # Start sub-choice: Choose sampler error mitigator
250 .hamming_weight_post_selection() # Configure the error mitigator to use hamming weight post selection.
251 .create() # Create the estimator instance
252 )
253
254 # A FAST gate selector using the noisy sampler that emulate an Cepheus1108Q Quantum Processor.
255 noisy_gate_selector = (
256 qc
257 .gate_selector_creator()
258 .fast() # Narrow the gate selector type to a FAST gate selector
259 .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling
260 .with_sampler(noisy_sampler) # Configure to use the local braket sampler
261 .create() # Create the gate selector instance
262 )
263
264 # Build the user-configured VQE calculator instance with the excitation gate estimator
265 # and gate selector that uses the noisy sampler that emulate an Cepheus1108Q Quantum Processor.
266 noisy_fast_vqe_calculator = (
267 qc
268 .calculator_creator() # Start creating a calculator instance
269 .vqe() # Narrow: pick Variational quantum eigensolver (VQE)
270 .iterative() # Narrow to the iterative VQE
271 .standard() # Narrow to the standard FAST-VQE
272 .with_options( # Configure to use the user-defined iterative VQE options
273 options=qc.options.IterativeVqeOptions(max_iterations=10)
274 )
275 .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer
276 .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer
277 .with_estimator(excitation_gate_estimator) # Configure to use the excitation gate estimator
278 .with_estimator_shots(100_000) # Configure to use 100,000 shots in the estimator
279 .with_gate_selector(noisy_gate_selector) # Configure to use the gate selector
280 .create() # Create the calculator instance
281 )
282 noisy_result = noisy_fast_vqe_calculator.calculate(ground_state_problem)
283 print("result from the local braket estimator and noisy sampler:")
284 print(noisy_result)
285
286 real_hardware_braket_result = None
287 if do_real_hardware:
288 # A real hardware sampler using the Amazon Braket backend
289 # dispatching to an Cepheus1108Q Quantum Processor
290 real_hardware_braket_sampler = (
291 qc
292 .sampler_creator()
293 .backend() # Narrow the estimator type to a backend estimator
294 .choose_backend() # Start sub-choice: Choose backend
295 .amazon_braket(
296 device=Devices.Rigetti.Cepheus1108Q
297 ) # Perform the sub-choice selection - Here we pick the Rigetti Cepheus1108Q
298 .choose_sampler_error_mitigator() # Start sub-choice: Choose sampler error mitigator
299 .hamming_weight_post_selection() # Configure the error mitigator to use hamming weight post selection.
300 .create() # Create the estimator instance
301 )
302
303 # A FAST gate selector using the Cepheus1108Q Quantum Processor for the sampler.
304 real_hardware_braket_gate_selector = (
305 qc
306 .gate_selector_creator()
307 .fast() # Narrow the gate selector type to a FAST gate selector
308 .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling
309 .with_sampler(real_hardware_braket_sampler) # Configure to use the local braket sampler
310 .create() # Create the gate selector instance
311 )
312
313 # Build the user-configured VQE calculator instance with the excitation gate estimator
314 # and gate selector that uses the Cepheus1108Q Quantum Processor for the sampler.
315 real_hardware_braket_fast_vqe_calculator = (
316 qc
317 .calculator_creator() # Start creating a calculator instance
318 .vqe() # Narrow: pick Variational quantum eigensolver (VQE)
319 .iterative() # Narrow to the iterative VQE
320 .standard() # Narrow to the standard FAST-VQE
321 .with_options( # Configure to use the user-defined iterative VQE options
322 options=qc.options.IterativeVqeOptions(max_iterations=10)
323 )
324 .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer
325 .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer
326 .with_estimator(excitation_gate_estimator) # Configure to use the excitation gate estimator
327 .with_estimator_shots(100_000) # Configure to use 100,000 shots in the estimator
328 .with_gate_selector(real_hardware_braket_gate_selector) # Configure to use the gate selector
329 .create() # Create the calculator instance
330 )
331 real_hardware_braket_result = real_hardware_braket_fast_vqe_calculator.calculate(ground_state_problem)
332
333 print("result from the excitation gate estimator and real quantum hardware sampler:")
334 print(real_hardware_braket_result)
335
336 fast_convergence: list[tuple[list[float], list[float], str]] = []
337
338 labels = ["Excitation Gate Simulator"]
339 results = [excitation_gate_result]
340 if do_local_braket and local_braket_result is not None:
341 labels.append("Local Braket Simulator")
342 results.append(local_braket_result)
343
344 if do_noisy_sampler and noisy_result is not None:
345 labels.append("Noisy Simulator")
346 results.append(noisy_result)
347
348 if do_real_hardware and real_hardware_braket_result is not None:
349 labels.append("Real Hardware")
350 results.append(real_hardware_braket_result)
351
352 for result, label in zip(results, labels, strict=True):
353 fast_energies = result.total_energy_per_macro_iteration_with_initial_energy_and_final_energy.values
354 energy_errors = result.total_energy_per_macro_iteration_with_initial_energy_and_final_energy.errors
355 fast_convergence.append((fast_energies, energy_errors, label))
356
357 plot_energies(
358 fast_convergence=fast_convergence,
359 show=True,
360 )
361
362 return excitation_gate_result.total_energy.value, excitation_gate_result.total_energy.error
363
364
365if __name__ == "__main__":
366 main()
Explanation
Imports
from pathlib import Path import matplotlib.pyplot as plt import numpy as np from braket.devices import Devices import qrunch as qc
We import the public Kvantify Qrunch API via
import qrunch as qc(stable user interface) and, since we will query device pricing and eventually run on the device, we also importDevicesfrombraket.devices. In addition, we usematplotlib,numpy, andPath(frompathlib).The plotting method
def plot_energies( fast_convergence: list[tuple[list[float], list[float], str]], *, outdir: Path = Path("dist"), show: bool = False, ) -> None: """ Plot VQE energy convergence and log-scale error vs reference energy. Args: fast_convergence: Sequence of energies, errors and label. outdir: Output directory where plots are written. Defaults to "dist". show: Whether to display the figures interactively after saving. """ # Create figure and axis fig, ax = plt.subplots(figsize=(8, 5)) # Ensure output directory exists. outdir.mkdir(parents=True, exist_ok=True) for fast_energies, energy_errors, label in fast_convergence: energies = np.array(fast_energies) errors = np.array(energy_errors) # Prepare x-axis as 1-based iteration indices for readability. iterations = np.arange(1, len(energies) + 1) # Plot error bars ax.errorbar( iterations, energies, yerr=errors, fmt="o", capsize=5, elinewidth=1.2, markeredgewidth=1.2, label=label, ) # Label axes and title ax.set_xlabel("Iteration") ax.set_ylabel("Total Energy [Hartree]") ax.set_title("FAST-VQE Convergence with Error Bars due to shot Noise, and emulated noise") # Grid and legend ax.grid(True, linestyle="--", alpha=0.5) ax.legend() # Tight layout and save plots fig.tight_layout() convergence_plot = outdir / "vqe_convergence.png" fig.savefig(convergence_plot, dpi=200, bbox_inches="tight") if show: plt.show() else: # Close figures to free memory when running in batch contexts. plt.close(fig)
This code plots the convergence of the VQE energy with the error as error bars.
The code generates a PNG file under
dist/asvqe_convergence.pngBuild the LiH ground-state problem
# Build Lithium Hydride (LiH) molecular configuration. molecular_configuration = qc.build_molecular_configuration( molecule=[ ("H", 0.0, 0.0, 0.0), ("Li", 1.5474, 0.0, 0.0), ], basis_set="sto3g", ) # Build ground state problem. problem_builder = qc.problem_builder_creator().ground_state().standard().create() ground_state_problem = problem_builder.build_unrestricted(molecular_configuration)
We construct a lithium hydride (LiH) molecule using the minimal
STO-3Gbasis.Create an excitation-gate estimator
# Excitation gate estimator (Kvantifys proprietary chemistry tailored state vector simulator) excitation_gate_estimator = ( qc .estimator_creator() .excitation_gate() # Narrow the estimator type to an excitation gate estimator .with_parallel_setting("parallel") # Configure parallelization behavior: "serial" or "parallel" .with_spin_particle_conservation() # Configure to conserve alpha and beta electrons separately .choose_estimator_error_mitigator() # Start sub-choice: Choose estimator error mitigator .symmetry_adapted() # Perform the sub-choice selection - Here we pick the symmetry adapted error mitigator .create(with_shot_counter=True) # Create the estimator instance )
The excitation-gate estimator is Kvantify’s proprietary chemistry-aware state-vector simulator. It supports parallel execution and can enforce separate conservation of α and β electrons. We additionally attach a symmetry-adapted error mitigator and we enable shot counting for later cost analysis. This is the default estimator, but we here define it explicitly for clarity.
The estimator is the object that can evaluate expectation values of quantum circuits. In this case the estimator is used to evaluate the expectation value of the Hamiltonian (energy) of the quantum state prepared by the VQE iterative ansatz.
Create an excitation-gate sampler
# Excitation gate sampler (Kvantifys proprietary chemistry tailored state vector simulator) excitation_gate_sampler = ( qc .sampler_creator() .excitation_gate() # Narrow the sampler type to an excitation gate sampler .with_parallel_setting("parallel") # Configure parallelization behavior: "serial" or "parallel" .with_spin_particle_conservation() # Configure to conserve alpha and beta electrons separately .create(with_shot_counter=True) # Create the sampler instance )
Like the estimator, it runs in parallel mode, conserves spin, and maintains an internal shot counter.
The sampler is the object that provide a set of bitstrings representing measurement outcomes, and associated counts. In this case, this means sampling the quantum state prepared by the VQE iterative ansatz, where each bitstring represents a Slater determinant (or occupation number vector).
Build the FAST gate selector
# A FAST gate selector using the excitation gate sampler. excitation_gate_gate_selector = ( qc .gate_selector_creator() .fast() # Narrow the gate selector type to a FAST gate selector .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling .with_sampler(excitation_gate_sampler) # Configure to use the excitation gate sampler .create() # Create the gate selector instance )
A FAST gate selector controls which excitation gates are added during the iterative VQE procedure. Here it is configured to use the excitation-gate sampler and 10000 shots per iteration. Here we use the default FAST gate selector that uses the Heuristic Gradient metric to select gates. Note that the FAST gate selector only require a sampler, not an estimator. See https://arxiv.org/abs/2303.07417 for details.
Assemble and run the excitation-gate-based FAST-VQE calculator
# Build the user-configured VQE instance with the excitation_gate_estimator # and gate selector that uses the excitation_gate_sampler excitation_gate_fast_vqe_calculator = ( qc .calculator_creator() # Start creating a VQE instance .vqe() # Narrow to Variational quantum eigensolver (VQE) .iterative() # Narrow to the iterative VQE .standard() # Narrow to the standard FAST-VQE .with_options( # Configure to use the user-defined iterative VQE options options=qc.options.IterativeVqeOptions(max_iterations=10) ) .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer .with_estimator(excitation_gate_estimator) # Configure to use the excitation gate estimator .with_estimator_shots(100_000) # Configure to use 100,000 shots in the estimator .with_gate_selector(excitation_gate_gate_selector) # Configure to use the gate selector .create() # Create the calculator instance ) excitation_gate_result = excitation_gate_fast_vqe_calculator.calculate(ground_state_problem)
We combine the estimator, and gate selector into a user-configured iterative VQE calculator. It uses up to 10 iterations, a “quick default” greedy minimizer, and 100000 estimator shots per energy evaluation. The last line performs the ground state energy calculation.
Estimate total shots and cost
print("Total number of shots used in estimator", excitation_gate_estimator.total_shots) estimated_estimator_cost = excitation_gate_estimator.total_braket_price(device=Devices.Rigetti.Cepheus1108Q) print("Estimated cost in dollars for Estimator on Devices.Rigetti.Cepheus1108Q: ", estimated_estimator_cost) print("Total number of shots used in sampler", excitation_gate_sampler.total_shots) estimated_sampler_cost = excitation_gate_sampler.total_braket_price(device=Devices.Rigetti.Cepheus1108Q) print("Estimated cost in dollars for Sampler on Devices.Rigetti.Cepheus1108Q: ", estimated_sampler_cost)
Both estimator and sampler expose their total shot counts and we can estimate cloud-hardware pricing via
.total_braket_price(device=...). This provides a convenient cost estimate for experiments on, for example, Rigetti Cepheus1108Q.Create a local Braket backend estimator
# An Amazon Local Braket backend estimator local_braket_estimator = ( qc .estimator_creator() .backend() # Narrow the estimator type to a backend estimator .choose_backend() # Start sub-choice: Choose backend .local_amazon_braket() # Perform the sub-choice selection - Here we pick the local Braket state vector simulator backend .create(with_shot_counter=True) # Create the estimator instance ) # An Amazon Local Braket backend sampler local_braket_sampler = ( qc .sampler_creator() .backend() # Narrow the sampler type to a backend sampler .choose_backend() # Start sub-choice: Choose backend .local_amazon_braket() # Perform the sub-choice selection - Here we pick the local Braket simulator backend .create(with_shot_counter=True) # Create the estimator instance )
Here we construct an estimator and sampler that uses a local Braket simulator backend. The prefix``local_`` indicates that the calculation runs locally on the user’s machine. We enable shot counting for later cost analysis.
Build and run a FAST-VQE with the local Braket backends
# A FAST gate selector using the local braket sampler. local_braket_gate_selector = ( qc .gate_selector_creator() .fast() # Narrow the gate selector type to a FAST gate selector .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling .with_sampler(local_braket_sampler) # Configure to use the local braket sampler .create() # Create the gate selector instance ) # Create the calculator instance using the local_braket_estimator # and gate selector that uses the local_braket_sampler local_braket_fast_vqe_calculator = ( qc .calculator_creator() # Start creating a calculator instance .vqe() # Narrow: pick Variational quantum eigensolver (VQE) .iterative() # Narrow to the iterative VQE .standard() # Narrow to the standard FAST-VQE .with_options( # Configure to use the user-defined iterative VQE options options=qc.options.IterativeVqeOptions(max_iterations=10) ) .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer .with_estimator(local_braket_estimator) # Configure to use the local braket estimator .with_total_estimator_shots(100_000) # Configure to use a 100,000 shots in the estimator .with_gate_selector(local_braket_gate_selector) # Configure to use the gate selector .create() # Create the calculator instance ) local_braket_result = local_braket_fast_vqe_calculator.calculate(ground_state_problem)Similar to the excitation-gate-based VQE, we build a gate selector that uses the local Braket sampler. Then we assemble an iterative VQE, configured to use the local Braket estimator, and create the FAST-VQE calculator.
Finally, we run the FAST-VQE calculator again, this time with the local Braket estimator and sampler.
Evaluate cost for the local Braket run
print("Total number of shots used in estimator", local_braket_estimator.total_shots) estimated_estimator_cost = local_braket_estimator.total_braket_price(device=Devices.Rigetti.Cepheus1108Q) print("Estimated cost in dollars for Estimator on Devices.Rigetti.Cepheus1108Q: ", estimated_estimator_cost) print("Total number of shots used in sampler", local_braket_sampler.total_shots) estimated_sampler_cost = local_braket_sampler.total_braket_price(device=Devices.Rigetti.Cepheus1108Q) print("Estimated cost in dollars for Sampler on Devices.Rigetti.Cepheus1108Q: ", estimated_sampler_cost)As before, we print shot statistics and estimated dollar costs. The cost estimates should be close to the cost estimated using the excitation-gate estimator and sampler. Any differences arise from the error that arises from the limited shot counts.
Create a noisy sampler
# Create a real Cepheus1108Q Quantum Processor hardware backend. backend = qc.backend_creator().amazon_braket(device=Devices.Rigetti.Cepheus1108Q).create() # Get device data from the backend - from the Cepheus1108Q Quantum Processor cepheus1108q_device_data = backend.get_device_data() # An Amazon Local Braket backend simulator sampler # that emulate the noise from the Cepheus1108Q Quantum Processor # using the density matrix simulator. noisy_sampler = ( qc .sampler_creator() .backend() # Narrow the estimator type to a backend estimator .choose_backend() # Start sub-choice: Choose backend .local_amazon_braket( device_to_simulate=cepheus1108q_device_data ) # Perform the sub-choice selection - Here the local braket emulating Cepheus1108Q .choose_sampler_error_mitigator() # Start sub-choice: Choose sampler error mitigator .hamming_weight_post_selection() # Configure the error mitigator to use hamming weight post selection. .create() # Create the estimator instance )Next, we create a noisy sampler that uses the Rigetti Cepheus1108Q device as a noise model. You can think of this as a simulator that emulates the noise characteristics of the real device, using a density matrix simulator.
The cost for running sampling on real hardware is typically much smaller than running the estimator on real hardware, as shown by the cost estimates.
To create the noisy sampler, we first create a backend object, and then query the backend object for the device data, which is used to emulate the behaviour of that device.
Note that this time we choose the
hamming_weight_post_selectionas the sampler error mitigator.This error mitigator removes all states with an incorrect Hamming weight, i.e., an incorrect number of 1’s in the bitstrings, which means an incorrect number of alpha and beta electrons in the occupation number vector (or Slater determinant).
This is required as the noisy sampler will produce bitstrings that do not conserve the number of alpha and beta electrons, due to noise.
Build and run a FAST-VQE with the noisy sampler
# A FAST gate selector using the noisy sampler that emulate an Cepheus1108Q Quantum Processor. noisy_gate_selector = ( qc .gate_selector_creator() .fast() # Narrow the gate selector type to a FAST gate selector .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling .with_sampler(noisy_sampler) # Configure to use the local braket sampler .create() # Create the gate selector instance ) # Build the user-configured VQE calculator instance with the excitation gate estimator # and gate selector that uses the noisy sampler that emulate an Cepheus1108Q Quantum Processor. noisy_fast_vqe_calculator = ( qc .calculator_creator() # Start creating a calculator instance .vqe() # Narrow: pick Variational quantum eigensolver (VQE) .iterative() # Narrow to the iterative VQE .standard() # Narrow to the standard FAST-VQE .with_options( # Configure to use the user-defined iterative VQE options options=qc.options.IterativeVqeOptions(max_iterations=10) ) .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer .with_estimator(excitation_gate_estimator) # Configure to use the excitation gate estimator .with_estimator_shots(100_000) # Configure to use 100,000 shots in the estimator .with_gate_selector(noisy_gate_selector) # Configure to use the gate selector .create() # Create the calculator instance ) noisy_result = noisy_fast_vqe_calculator.calculate(ground_state_problem) print("result from the local braket estimator and noisy sampler:") print(noisy_result)Similar to the earlier, we build a gate selector that uses the noisy sampler. Then we assemble an iterative VQE and create the FAST-VQE calculator. Finally we run FAST-VQE calculator again, this time with the noisy sampler and local Braket state vector estimator.
This yields a slightly higher energy, due to the noise in the sampler.
Create a real hardware sampler
Finally, now that we have an estimate of the cost and we have emulated the behaviour of the real hardware, we can create a sampler that runs on real quantum hardware.
# A real hardware sampler using the Amazon Braket backend # dispatching to an Cepheus1108Q Quantum Processor real_hardware_braket_sampler = ( qc .sampler_creator() .backend() # Narrow the estimator type to a backend estimator .choose_backend() # Start sub-choice: Choose backend .amazon_braket( device=Devices.Rigetti.Cepheus1108Q ) # Perform the sub-choice selection - Here we pick the Rigetti Cepheus1108Q .choose_sampler_error_mitigator() # Start sub-choice: Choose sampler error mitigator .hamming_weight_post_selection() # Configure the error mitigator to use hamming weight post selection. .create() # Create the estimator instance )Here we create a sampler that runs on the Rigetti Cepheus1108Q device. We choose
.hamming_weight_post_selection()as the sampler error mitigator for the sampler.Choosing another device is as simple as changing the device name.
See Choose a Backend for more options on using different backends, different quantum computer providers, etc.
Build and run a FAST-VQE with the real hardware sampler
# A FAST gate selector using the Cepheus1108Q Quantum Processor for the sampler. real_hardware_braket_gate_selector = ( qc .gate_selector_creator() .fast() # Narrow the gate selector type to a FAST gate selector .with_shots(10_000) # Configure to use 10,000 shots when estimating sampling .with_sampler(real_hardware_braket_sampler) # Configure to use the local braket sampler .create() # Create the gate selector instance ) # Build the user-configured VQE calculator instance with the excitation gate estimator # and gate selector that uses the Cepheus1108Q Quantum Processor for the sampler. real_hardware_braket_fast_vqe_calculator = ( qc .calculator_creator() # Start creating a calculator instance .vqe() # Narrow: pick Variational quantum eigensolver (VQE) .iterative() # Narrow to the iterative VQE .standard() # Narrow to the standard FAST-VQE .with_options( # Configure to use the user-defined iterative VQE options options=qc.options.IterativeVqeOptions(max_iterations=10) ) .choose_minimizer() # Start sub-choice: Choose the gate parameter minimizer .quick_default() # Perform the sub-choice selection - Here we pick the greedy and quick minimizer .with_estimator(excitation_gate_estimator) # Configure to use the excitation gate estimator .with_estimator_shots(100_000) # Configure to use 100,000 shots in the estimator .with_gate_selector(real_hardware_braket_gate_selector) # Configure to use the gate selector .create() # Create the calculator instance ) real_hardware_braket_result = real_hardware_braket_fast_vqe_calculator.calculate(ground_state_problem)As before, we build a VQE instance, a gate selector, and the FAST-VQE calculator, that now uses the real hardware sampler to select the gate.
Finally, we run FAST-VQE calculator.
Make the plot
fast_convergence: list[tuple[list[float], list[float], str]] = [] labels = ["Excitation Gate Simulator"] results = [excitation_gate_result] if do_local_braket and local_braket_result is not None: labels.append("Local Braket Simulator") results.append(local_braket_result) if do_noisy_sampler and noisy_result is not None: labels.append("Noisy Simulator") results.append(noisy_result) if do_real_hardware and real_hardware_braket_result is not None: labels.append("Real Hardware") results.append(real_hardware_braket_result) for result, label in zip(results, labels, strict=True): fast_energies = result.total_energy_per_macro_iteration_with_initial_energy_and_final_energy.values energy_errors = result.total_energy_per_macro_iteration_with_initial_energy_and_final_energy.errors fast_convergence.append((fast_energies, energy_errors, label)) plot_energies( fast_convergence=fast_convergence, show=True, )We plot the VQE convergence with the error bars by calling the plotting function defined above.
Running the Example
After saving the script as run_estimators_and_samplers.py, execute:
$ python run_estimators_and_samplers.py
You will see the cost estimates printed to the console:
$ python run_estimators_and_samplers.py
Total number of shots used in estimator 470600000
Estimated cost in dollars for Estimator on Devices.Rigetti.Cepheus1108Q: 424951.7999999651
Total number of shots used in sampler 100000
Estimated cost in dollars for Sampler on Devices.Rigetti.Cepheus1108Q: 92.99999999999999
The estimated cost is from 14 October 2025, so they are most likely out of date.
You will see the VQE convergence plot saved as dist/vqe_convergence.png: