Usage
Backends
QddProvider exposes QDD as Qiskit BackendV2 objects. The default backend
is qasm_simulator, which returns shot counts or exact probabilities. The
statevector_simulator backend returns statevectors.
from qdd import QddProvider
provider = QddProvider()
backend = provider.get_backend("qasm_simulator")
statevector_backend = provider.get_backend("statevector_simulator")
If the backend name is omitted, QddProvider().get_backend() returns
qasm_simulator.
Run a Qiskit Circuit
Use the backend with normal Qiskit QuantumCircuit objects. Transpiling for the
QDD backend is recommended when the circuit may contain gates outside QDD’s
native basis.
from qiskit import QuantumCircuit, transpile
from qdd import QddProvider
backend = QddProvider().get_backend("qasm_simulator")
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure_all()
compiled = transpile(circuit, backend, seed_transpiler=1234)
job = backend.run(
compiled,
shots=2048,
memory=True,
seed_simulator=1234,
)
result = job.result()
print(result.get_counts())
print(result.get_memory()[:5])
Set shots=None to compute exact probabilities instead of sampling:
probability_circuit = circuit.remove_final_measurements(inplace=False)
compiled = transpile(probability_circuit, backend, seed_transpiler=1234)
result = backend.run(compiled, shots=None).result()
print(result.data(0)["probabilities"])
Runtime options can be passed to run or stored on the backend:
backend.set_options(shots=4096, seed_simulator=1234)
result = backend.run(compiled).result()
result = backend.run(compiled, shots=1024, seed_simulator=5678).result()
Common runtime options are shots, memory, seed_simulator, use_mpi,
show_progress, and show_progress_frequency.
Statevector Simulation
Use statevector_simulator for final statevectors. The circuit should not end
with measurements when you want the unmeasured quantum state.
from qiskit import QuantumCircuit, transpile
from qdd import QddProvider
backend = QddProvider().get_backend("statevector_simulator")
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
compiled = transpile(circuit, backend, seed_transpiler=1234)
statevector = backend.run(compiled).result().get_statevector()
print(statevector)
QDD Sampler
QDD also provides a Sampler implementation for Qiskit primitive-style inputs.
Each pub is a tuple of (circuit, parameter_values).
from qiskit import QuantumCircuit
from qdd.qdd_sampler import Sampler
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
sampler = Sampler()
job = sampler.run(pubs=[(circuit, [])], shots=4096)
result = job.result()[0]
print(result.data.quasi_dist.binary_probabilities())
print(result.metadata["shots"])
Use is_exact=True to request exact probabilities with shots=None:
exact_job = sampler.run(pubs=[(circuit, [])], is_exact=True)
exact_result = exact_job.result()[0]
print(exact_result.data.quasi_dist.binary_probabilities())
Pass backend options, transpile options, or skip Qiskit transpilation in the constructor. For example, this sets QDD backend shots and asks Qiskit transpilation to use optimization level 1:
sampler = Sampler(
backend_options={"shots": 4096},
transpile_options={"optimization_level": 1},
)
QDD Estimator
The Estimator computes expectation values for observables represented by Qiskit primitive inputs.
from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp
from qdd.qdd_estimator import Estimator
ansatz = RealAmplitudes(num_qubits=2, reps=1)
observable = SparsePauliOp.from_list([("ZZ", 1.0), ("XI", 0.5)])
theta = [0.1, 0.2, 0.3, 0.4]
estimator = Estimator(default_precision=0.0)
job = estimator.run([(ansatz, [observable], theta)])
result = job.result()[0]
print(result.data.evs)
default_precision=0.0 requests exact probabilities. Positive precision values
are translated into a shot count. You can also override precision on each
run call:
job = estimator.run([(ansatz, [observable], theta)], precision=0.01)
result = job.result()[0]
print(result.data.evs)
print(result.data.stds)
Backend options are accepted by both primitives. With the qdd-mpi package,
pass {"use_mpi": True} and launch the Python process with mpirun; see
MPI Support for a complete MPI example.