Using Quantum Computers
Goal
Set up and run a hybrid quantum/classical workflow where selected parts of the computation (e.g., sampling) run on an actual quantum hardware, while the rest of the computation runs on classical infrastructure.
When to Use Real Hardware
Running on real quantum hardware is the main goal of using Kvantify Qrunch. However, real devices are noisy, and have usage costs. We recommend:
Using a simulator for development and debugging;
Switching to real hardware only for final benchmarking and validation;
Keeping estimators on a simulator unless you specifically need to measure them on hardware.
As hardware improves and costs reduce, more parts of the workflow can be run on real devices.
Semi-Remote Execution
Kvantify Qrunch supports semi-remote execution by simply selecting a braket backend device. See Choose a Backend for details on how to select a quantum backend device.
The backend can be used with the sampler and/or the estimator. The classical part of the calculation (namely the parameter optimization) will then run locally, while the sampling and/or estimator part will run on the selected quantum device.
from pathlib import Path
import qrunch as qc
from braket.devices import Devices
def my_script() -> dict[str, list[float] | list[int]]:
# Read the xyz file, and construct a molecule object.
molecular_configuration = qc.build_molecular_configuration(
molecule="some_xyz_file.xyz",
basis_set="sto3g",
)
ground_state_problem_builder = (
qc.problem_builder_creator()
.ground_state()
.standard()
.create()
)
problem = ground_state_problem_builder.build_restricted(molecular_configuration)
# Real Quantum Hardware for sampling
quantum_computer_sampler = (
qc.sampler_creator()
.backend()
.choose_backend()
.amazon_braket(device=Devices.IQM.Garnet) # Use IQM Garnet for sampling. This is enough to run on real hardware.
.create()
)
# Simulator for expectation values (keeps cost/time down)
estimator = (
qc.estimator_creator()
.excitation_gate() # Use excitation gate estimator
.create()
)
gate_selector = (
qc.gate_selector_creator()
.fast()
.with_sampler(quantum_computer_sampler) # use IQM Garnet for sampling
.with_shots(shots=1000) # finite-shots sampling keeps cost down.
.create()
)
fast_vqe_calculator = (
qc.calculator_creator()
.vqe()
.iterative()
.standard()
.with_gate_selector(gate_selector)
.with_estimator(estimator)
.with_estimator_shots(shots=None) # Unlimited shots on simulator
.create()
)
result = fast_vqe_calculator.calculate(problem)
expectation_values = result.total_energy_per_macro_iteration_with_initial_energy_and_final_energy
energies = [exp.value for exp in expectation_values]
errors = [exp.error for exp in expectation_values]
print(" Energies: ", energies)
print(" Errors: ", errors)
if __name__ == "__main__":
my_script()
The most important part of the code above is:
.amazon_braket(device=Devices.IQM.Garnet) — this tells Kvantify Qrunch to use the IQM Garnet quantum computer for sampling.
This single line is enough to run sampling on real hardware.
Remote Execution
Alternatively, you can run the entire workflow as a hybrid job on AWS Braket, thus performing fully remote execution. Below is a minimal hybrid job setup that uses IQM Garnet for sampling and a simulator for expectation value estimation.
Running this example requires that you have uploaded the files to S3. Log on to https://console.aws.amazon.com, go to S3 and upload files.
You also need to input your cloudsmith token in the code below. This token is used when downloading Qrunch from the cloudsmith index. It is the part right after *dl.cloudsmith.io/* in the URL you use to download Qrunch, see Getting Started.
1# ruff: noqa: S106, D100, S108
2from pathlib import Path
3
4from boto3 import client # type: ignore
5from boto3.s3.transfer import S3Transfer
6from braket.devices import Devices
7
8import qrunch as qc
9
10# Setup S3 connection for retrieving data.
11# S3 stands for Amazon Simple Storage Service, a cloud storage service provided by AWS.
12s3_data_bucket = "amazon-braket-us-east-1-ID"
13s3_client = client("s3", "us-east-1")
14transfer = S3Transfer(s3_client)
15
16# Create a local file for xyz.
17local_tmp_xyz_file = Path("/tmp/tmpfile.xyz")
18local_tmp_xyz_file.touch()
19
20quantum_computer = Devices.IQM.Garnet
21
22
23def my_script() -> dict[str, list[float]]:
24 """Script to be run as a hybrid job on AWS Braket."""
25 # Download data from S3 to a temporary local file
26 transfer.download_file(s3_data_bucket, "data/acrolein_water.xyz", str(local_tmp_xyz_file))
27
28 # Read the xyz file, and construct a molecule object.
29 molecular_configuration = qc.build_molecular_configuration(
30 molecule=local_tmp_xyz_file,
31 basis_set="sto3g",
32 )
33 ground_state_problem_builder = qc.problem_builder_creator().ground_state().standard().create()
34 problem = ground_state_problem_builder.build_restricted(molecular_configuration)
35
36 # Real Quantum Hardware for sampling
37 quantum_computer_sampler = (
38 qc.sampler_creator()
39 .backend()
40 .choose_backend()
41 .amazon_braket(device=quantum_computer) # Use IQM Garnet for sampling.
42 .create()
43 )
44
45 # Simulator for expectation values (keeps cost/time down)
46 estimator = (
47 qc.estimator_creator()
48 .excitation_gate() # Use excitation gate estimator
49 .create()
50 )
51
52 gate_selector = (
53 qc.gate_selector_creator()
54 .fast()
55 .with_sampler(quantum_computer_sampler) # use IQM Garnet for sampling
56 .with_shots(shots=1000) # finite-shots sampling keeps cost down.
57 .create()
58 )
59
60 fast_vqe_calculator = (
61 qc.calculator_creator()
62 .vqe()
63 .iterative()
64 .standard()
65 .with_gate_selector(gate_selector)
66 .with_estimator(estimator)
67 .with_estimator_shots(shots=None) # Unlimited shots on simulator
68 .create()
69 )
70
71 result = fast_vqe_calculator.calculate(problem)
72
73 expectation_values = result.total_energy_per_macro_iteration_with_initial_energy_and_final_energy
74 energies = [exp.value for exp in expectation_values]
75 errors = [exp.error for exp in expectation_values]
76
77 output: dict[str, list[float]] = {
78 "energies": energies,
79 "errors": errors,
80 }
81 return output
82
83
84if __name__ == "__main__":
85 # ===== Start ===== "Remote simulation" =====
86 AWS_REGION = "us-east-1"
87 docker_image = qc.docker_image_creator(
88 region_name=AWS_REGION,
89 image_name="hybrid_job_runner",
90 cloudsmith_token="{example_token}", # The token used when downloading the QDK from the cloudsmith repository.
91 ).create()
92
93 hybrid_runner = qc.HybridJobRunner(
94 result_folder=Path.home() / "result/folder",
95 device=quantum_computer,
96 repository_name="qrunch",
97 docker_image=docker_image,
98 region=AWS_REGION,
99 )
100 output_dict = hybrid_runner.run_hybrid_job(my_script)
101 # ===== End ===== "Remote simulation" =====
`my_script` contains your calculation logic;
The HybridJobRunner packages this logic and submits it to AWS Braket as a hybrid job;
The Kvantify Qrunch backend detects which components should run on the quantum computer;
In the above example: - Sampling runs on IQM Garnet; - Estimation runs locally on a simulator;
Results are stored in the specified result/folder.
Best Practices
Separate hardware and simulation tasks: keep only the necessary quantum tasks on hardware to minimize queue time and cost;
Limit number of shots each shot on hardware takes time and increases cost; use just enough for desired statistical precision;
Test locally first: use the same backend but with a simulator device to verify that your script works before running on hardware;
Plan for queue delays: some devices may have long queues; monitor job status in AWS Braket console.
See Also
Choose a Backend — Selecting a quantum backend device
Using a Noisy Simulator — Testing algorithms under realistic noise before moving to hardware
AWS Braket Hybrid Jobs documentation: https://docs.aws.amazon.com/braket/latest/developerguide/braket-hybrid.html