Using Qaptiva Backends with Qiskit 2.0 via qat.qiskit¶
qat.qiskit is a module allowing you to use qaptiva backends with qiskit 2.0 API.
It is possible to run Samplers and Estimators function to obtain a result given in a qiskit data structure called Primitive.
Before going into details about these 2 classes, let's understand first how to connect to the backend.
Connection to the backend¶
Before doing the Estimator or Sampler function to have a result, we need to have a connection to a QPU to run these functions.
We will use QaptivaService, a class that will instanciate a connexion to the Qaptiva Access server to gain access to the QPU.
Note: Like
QLMaaSConnection,QaptivaServicetakes the same arguments to instanciate itseft.
from qat.qiskit import QaptivaService
# Connection to Qaptiva Access
service = QaptivaService()
# List of all the backend available
backends = service.backends()
print([backend.name for backend in backends])
# Connexion to a Backend
backend = service.backend("LinAlg")
['qat.qpus:AnalogQPU', 'qat.qpus:SQAQPU', 'qat.qpus:Bdd', 'qat.qpus:CLinalg', 'qat.qpus:CNoisy', 'qat.qpus:RemoteQPU', 'qat.qpus:DLinAlg', 'qat.qpus:DNoisy', 'qat.qpus:Feynman', 'qat.qpus:ClassicalQPU', 'qat.qpus:LinAlg', 'qat.qpus:LinAlgLegacy', 'qat.qpus:MPSLegacy', 'qat.qpus:MPS', 'qat.qpus:MPO', 'qat.qpus:MPSTraj', 'qat.qpus:DMPSTraj', 'qat.qpus:NoisyQProc', 'qat.qpus:NoisyLinAlg', 'qat.qpus:UploadedQPU', 'qat.qpus:QPEG', 'qat.qpus:QutipQPU', 'qat.qpus:SPD', 'qat.qpus:NoisySPD', 'qat.qpus:Stabs']
Estimator class¶
Now that we are connected to the QPU, we can begin to test our circuit.
Here is an example of a circuit.
from qiskit.circuit import Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit import QuantumCircuit
observable = SparsePauliOp.from_list(
[("II", 2), ("XX", -2), ("YY", 3), ("ZZ", -3)]
)
theta = Parameter('θ')
phi = Parameter('φ')
quantum_circuit = QuantumCircuit(2)
quantum_circuit.rx(theta, 0)
quantum_circuit.ry(phi, 1)
param_dict = {theta: 1.57, phi: 3.14}
qiskit_pub = (quantum_circuit, observable, param_dict)
After constructing the quantum circuit, we next define the Estimator class. This class is responsible for executing expectation value computations and takes the following arguments:
backend: the backend on which the sampling jobs will be executed.default_precision(optional): the default numerical precision used for the estimation.
Once the class is instantiated, we can invoke its main method, run. This method expects as input a qiskit.primitives object. Conceptually, a qiskit.primitives object can be viewed as a collection of unified blocks, where each block contains three elements:
- a quantum circuit.
- one or more
observables. - a corresponding set of values (e.g., coefficients) associated with these observables.
from qat.qiskit import Estimator
# Use of the backend setup before
estimator = Estimator(backend)
job = estimator.run([qiskit_pub])
print(job.status())
print(job.result())
Submitted a new batch: Job1
JobStatus.RUNNING
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(3,), dtype=object>), stds=np.ndarray(<shape=(3,), dtype=float64>)))], metadata={'single_job': 'False'})
Sampler class¶
The Sampler class follows a similar design philosophy to the Estimator class, but it is dedicated to sampling measurement outcomes from quantum circuits rather than computing expectation values. It provides direct access to the raw measurement statistics produced by executing circuits on a given backend.
The Sampler class is initialized with the following arguments.
backend: the backend on which the sampling jobs will be executed.default_shots: the default number of measurement shots to use when none is explicitly specified.
from qat.qiskit import Sampler
qc = QuantumCircuit(2)
# Create Bell state
qc.h(0)
qc.cx(0, 1)
# Measurements (required for Sampler)
qc.measure_all()
sampler_pub = qc
# Use of the backend setup before
sample = Sampler(backend, default_shots=2048)
job = sample.run([sampler_pub])
print(job.status())
# Error due to the waiting for merge
print(job.result()[0].data.items())
Submitted a new batch: Job2
JobStatus.RUNNING
dict_items([('register_0', array(['00', '00', '11', ..., '11', '00', '00'],
shape=(2048,), dtype='<U2'))])