Skip to content

Providers

Where a calculation runs

A variational algorithm is built once, as a product of exponentials of anti-Hermitian generators. Which SDK turns that into a circuit, and which machine executes it, are two keyword arguments — and because the decomposition is exact, the three SDKs return the same state vector to machine precision.

The three SDKs

Qiskit

backend_provider="qiskit"

IBM's SDK, the default, and the route to IBM Quantum. Qiskit is little-endian, so Mandacaru qubit k is laid on wire n−1−k; the unitary is unaffected.

package
qiskit
executes on
qiskit.quantum_info.Statevector, or an Estimator primitive with shots
execution
off — circuits are built for profiling, the state evaluated internally unless execute_circuits=True

Amazon Braket

backend_provider="braket"

The route to IonQ, IQM and Rigetti. Locally it returns the exact state vector; through AWS the energy is measured from shots over qubit-wise-commuting Pauli groups.

package
amazon-braket-sdk
executes on
braket.devices.LocalSimulator, or the AWS service by ARN
execution
on — naming Braket is a request to run circuits on it

Cirq

backend_provider="cirq"

Google's SDK. Big-endian like Mandacaru itself, so the circuit reads exactly as the generators are written.

package
cirq
executes on
cirq.Simulator
execution
on — naming Cirq is a request to run circuits on it

One Hamiltonian, three SDKs

The same ADAPT-VQE on LiH, executed as real circuits on each SDK

from ase import Atoms from mandacaru import Mandacaru atoms = Atoms("LiH", positions=[[0, 0, 0], [0, 0, 1.595]], cell=[15.0] * 3) atoms.center() # Build the qubit Hamiltonian once and cache it. Every provider below # replays it, so any spread between them is the SDK, not the integrals. atoms.calc = Mandacaru(method="adapt-vqe", basis="FAO", pool="qeb", h=0.25, save_hamiltonian="lih.parquet") atoms.get_potential_energy() for sdk in ("qiskit", "braket", "cirq"): calc = Mandacaru(method="adapt-vqe", pool="qeb", load_hamiltonian="lih.parquet", backend_provider=sdk, # which SDK builds the circuits execute_circuits=True, # ... and runs them trace=False) result = calc.run() # no geometry needed print(f"{sdk:7s} {result.optimal_energy:.6f} eV")
  • Every generator's terms commute, so exp(θA) factorizes exactly into Pauli rotations — basis change, CNOT ladder, Rz(−2θc), uncompute — emitted once as a shared gate stream (X, H, S, S†, CNOT, Rz) and translated per SDK. No Trotter error.
  • execute_circuits=True makes state preparation run a real circuit on that SDK's local simulator instead of the internal NumPy state vector. It defaults to True for braket and cirq, False for qiskit.
ADAPT-VQE on LiH from one cached Hamiltonian, run on each provider
Provider Energy (eV) Error vs. FCI CNOTs Depth Time
internal (matrix) −187.438634 3.65×10⁻⁶ 208 273 0.3 s
Qiskit −187.438636 1.71×10⁻⁶ 208 273 4.1 s
Amazon Braket −187.438635 2.97×10⁻⁶ 208 359 43.9 s
Cirq −187.438636 2.22×10⁻⁶ 208 358 8.9 s

All four grow the same eight-operator ansatz and recover the exact ground state of the cached Hamiltonian; the depth differs only because each SDK schedules one gate stream its own way. Braket's local simulator is the slow one — that is the SDK, not the algorithm.

Real quantum hardware

Ten devices are registered by name, and any Braket ARN, ibm_* or fake_* backend is accepted verbatim. Every QPU needs shots — with shots > 0 the energy is measured from bit strings over qubit-wise-commuting Pauli groups instead of read off amplitudes.

Registered devices
Kind device= Notes
Simulators AER_simulator · braket-local · braket-sv1 · braket-dm1 · braket-tn1 Exact state vector, or shot-based with shots > 0
IBM Quantum ibm-quantum · ibm_<name> · fake_<name> Qiskit Runtime; ibm-quantum picks the least busy operational QPU, fake_* rehearses the path locally
Braket QPUs braket-ionq-aria · braket-ionq-forte · braket-iqm-garnet · braket-rigetti-ankaa IonQ, IQM and Rigetti through AWS; any device ARN also works
The hardware workflow: grow and optimize the ansatz on the local state vector, then send the converged state to the QPU as a single measurement job. your machine — state vector the QPU — one job Grow the ansatz pool screening Optimize θ exact, local Measure ⟨H⟩ shots, QWC groups |ψ(θ*)⟩ One job per geometry. The optimization loop never touches hardware.

Optimize locally, measure once

from ase import Atoms from mandacaru import Mandacaru from mandacaru.backends.providers import QiskitProvider atoms = Atoms("H2", positions=[[0, 0, 0], [0, 0, 0.74]], cell=[8.0] * 3) # physical_qubits pins the layout: rank pairs by readout and CZ error # first -- a qubit whose readout drifted since calibration ruins the # energy while its recorded error still looks fine. atoms.calc = Mandacaru(method="adapt-vqe", basis="FAO", pool="fermionic", mapping="parity_reduced", # H2: 4 qubits -> 2 measurement_provider=QiskitProvider( device="ibm_fez", shots=4096, physical_qubits=[22, 23], estimator_options={"resilience_level": 2})) energy = atoms.get_potential_energy() # measured on the QPU, in eV

Amazon Braket is the same shape: device="braket-ionq-aria", shots=4096. A dry run may name a QPU without shots, so a register can be costed against a device before an account exists.

Energy against hydrogen–hydrogen distance for H2 on two IBM quantum processors, with error bars, against the exact local state vector. The ibm_fez points track the exact curve closely and lie above it; the ibm_kingston points drift away beyond 1.2 angstrom.
H₂ on IBM Quantum: 2 qubits, 4096 shots, resilience level 2. Every ibm_fez point sits above the exact curve, as a variational measurement must, and within 0.44 eV (0.016 Ha) of it — while the same script on ibm_kingston strays up to 1.75 eV.
Energy against lithium–hydrogen distance for LiH. The exact local curve is smooth and flat near −210 eV; the hardware points scatter between −196 and −188 eV and do not resemble the curve.
The same workflow on LiH — 12 qubits, readout mitigation only — lands 0.48 to 0.78 Ha high and does not trace the curve. A few tens of two-qubit gates is today's ceiling.

What doesn't work yet

Error mitigation is the provider's, not Mandacaru's

There is no mitigation module: the Qiskit Runtime Estimator's resilience levels — readout mitigation, twirling, zero-noise extrapolation — are used as they come. Writing one is a roadmap item, not a shipped feature.

Gradient screening on hardware is still classical

Every variational energy can be measured natively, but ADAPT-VQE's operator-gradient screening still runs on the classical state vector behind it. Fixed-ansatz VQE is the fully hardware-native method today.

The grid is not translation-invariant, and the forces say so

Translating a molecule rigidly across a frozen grid changes its energy — the egg-box effect, tens of meV at a coarse spacing and falling with it. The analytic force differentiates that faithfully rather than hiding it, so converge the spacing before reading a small force as chemistry.

Checked against

  • H₂ and LiH ground states against exact diagonalization of their own Hamiltonians
  • Analytic forces against central differences of the same energy on the same grid
  • A resumed run reproducing the uninterrupted one after a simulated crash
  • 53 tests, including backend-provider equivalence and both hardware paths

Keep reading

Twelve families of orbitals generated at run time, the frozen core, and the integral engine underneath.

ADAPT-VQE, VQE, excited states, phase estimation and periodic systems — one driver under all of them.