Backend Providers: Qiskit, Braket and Cirq
Carcará’s ansätze are products of exponentials of anti-Hermitian generators,
The backend_provider argument chooses which quantum SDK constructs those
circuits — and, with execute_circuits=True, which SDK runs them.
from carcara.algorithms import ADAPTVQE
ADAPTVQE(basis="FAO", backend_provider="qiskit") # default
ADAPTVQE(basis="FAO", backend_provider="braket") # amazon-braket-sdk
ADAPTVQE(basis="FAO", backend_provider="cirq") # cirq
provider |
package |
executes on |
|---|---|---|
|
|
|
|
|
local simulator or the AWS Braket service |
|
|
|
Why the three agree exactly
Each generator is a qubit PauliSum whose terms are \(A = \sum_j i\,c_j P_j\) with
real \(c_j\) and mutually commuting Pauli strings \(P_j\) — a property of the
fermionic and qubit excitation generators. The exponential therefore factorizes
exactly, with no Trotter error:
and each factor is the textbook Pauli-rotation circuit: a basis change to the \(Z\) axis, a CNOT ladder accumulating the parity onto one qubit, an \(R_z(-2\theta c_j)\), then the ladder and basis change undone.
Because the decomposition is exact, all three providers emit the same unitary
from the same gate set (X, H, S, S†, CNOT, R_z) and must agree with
the internal NumPy state-vector backend to machine precision. That equivalence is
asserted in the test suite and demonstrated end-to-end on LiH:
provider E (Ha) err vs FCI ops cnots depth time
(matrix) -6.88824276 1.34e-07 8 208 273 0.3s
qiskit -6.88824283 6.27e-08 8 208 273 4.1s
braket -6.88824279 1.09e-07 8 208 359 43.9s
cirq -6.88824281 8.17e-08 8 208 358 8.9s
:::{note}
Endianness. Carcará puts qubit 0 in the most significant position of the
amplitude index (the leftmost Kronecker factor), matching Braket and Cirq.
Qiskit is little-endian, so the Qiskit provider lays Carcará qubit k on Qiskit
wire n-1-k. Gate counts are unaffected — relabeling is an isomorphism.
:::
Building versus executing
execute_circuits decides whether circuits are only built (for gate-count
profiling) or actually run to prepare each state:
ADAPTVQE(backend_provider="qiskit") # execute_circuits=False
ADAPTVQE(backend_provider="braket") # execute_circuits=True
ADAPTVQE(backend_provider="qiskit", execute_circuits=True) # opt in
ADAPTVQE(backend_provider="cirq", execute_circuits=False) # opt out
It defaults to True for "braket" and "cirq" — naming them is a request to
use them — and False for "qiskit", which keeps the fast NumPy state-vector
numerics unless execution is asked for explicitly. Circuit execution is orders
of magnitude slower (one simulator invocation per energy evaluation) and gives
the same answer, so it is a verification and hardware path, not a performance
one.
Circuit profiling always uses the named SDK. Counts differ between providers because only Qiskit re-optimizes during transpilation; the unitary does not.
Using an ansatz directly
The providers are also usable below the driver level:
from carcara.backends.providers import build_provider
from carcara.circuits import AdaptAnsatz
from carcara.circuits.pools import build_pool
pool = build_pool("qeb", 2, (1, 1))
provider = build_provider("cirq")
ansatz = AdaptAnsatz(4, pool.occupied_orbitals, "jordan_wigner",
provider=provider)
for op in pool.operators()[:3]:
ansatz.append(op)
psi = ansatz.state([0.31, -0.72, 0.45]) # executed as a real circuit
circuit = provider.build(4, ansatz.reference_qubits(),
ansatz.pauli_generators, [0.31, -0.72, 0.45])
print(circuit)
UCCSD accepts provider= too, but a circuit realizes
the Trotter product form, so trotter=True is required:
from carcara.circuits import UCCSD
UCCSD(2, (1, 1), trotter=True, provider=build_provider("braket"))
The VQE driver does this automatically when circuit execution is on.
:::{note}
A circuit can only be initialized in a computational basis state, so provider
execution accepts Slater-determinant references only — which is what the
Hartree-Fock reference and the SSVQE reference determinants are. A superposition
reference raises a clear ValueError.
:::
Availability
Naming a provider never fails at import time; the SDK is imported on first use.
from carcara.backends.providers import BACKEND_PROVIDERS, provider_available
BACKEND_PROVIDERS # ('qiskit', 'braket', 'cirq')
provider_available("braket") # True if amazon-braket-sdk is importable
Aliases are accepted: "ibm" → qiskit, "aws" / "amazon-braket" →
braket, "google" → cirq.
Next
Running on the AWS Braket service, including real QPUs, needs the shot-based measurement path — see Running on Amazon Braket (and real QPUs).