POVM Sampler Pub¶
This tutorial shows you how to input multiple circuits and parameter values to the POVMSampler.run
method via primitive unified blocs (PUBs).
[1]:
%load_ext autoreload
%autoreload 2
Interface of the POVMSampler.run
method¶
The interface follows the style of the Qiskit V2 Primitives, which relies on PUBs. Each PUB is a tuple that contains a circuit and the data broadcasted to it. This greatly simplifies your ability to send complex data to a circuit.
the run()
method takes an array of PUBs. Each PUB is in the format
(<single circuit>, <optional one or more parameter values>, <optional shots>, <optional POVM implementation>)
First example¶
Let us look at a first simple example.
Initialize QuantumCircuit and POVMSampler¶
The first step is to create the qiskit.circuit.QuantumCircuits
from which you want to obtain the probability distribution with the povm_toolbox.sampler.POVMSampler
.
[2]:
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
qc1 = QuantumCircuit(2)
qc1.h(0)
qc1.cx(0, 1)
qc1.draw("mpl", style="iqp")
[2]:
Initialize measurement procedure¶
Next, create a povm_toolbox.library.POVMImplementation
instance.
[3]:
from povm_toolbox.library import ClassicalShadows
shots1 = 128
measurement1 = ClassicalShadows(num_qubits=2, seed=43)
measurement1.measurement_circuit.draw("mpl")
[3]:
Create the PUB¶
Finally, create the pub-like object, which is simply a tuple (<single circuit>, <optional one or more parameter values>, <optional shots>, <optional POVM implementation>)
[4]:
pub1 = (qc1, None, shots1, measurement1)
Second example¶
Let us look at an example where the full PUB is not specified.
Initialize Quantum Circuits¶
The first step is to create the qiskit.circuit.QuantumCircuits
from which you want to obtain the probability distribution with the povm_toolbox.sampler.POVMSampler
.
[5]:
qc2 = QuantumCircuit(2)
qc2.x(0)
qc2.h(0)
qc2.cx(0, 1)
qc2.draw("mpl", style="iqp")
[5]:
[6]:
qc3 = QuantumCircuit(2)
qc3.h(0)
qc3.x(1)
qc3.cx(0, 1)
qc3.draw("mpl", style="iqp")
[6]:
[7]:
qc4 = QuantumCircuit(2)
qc4.x(0)
qc4.x(1)
qc4.h(0)
qc4.cx(0, 1)
qc4.draw("mpl", style="iqp")
[7]:
Create the PUBs¶
Finally, create the pub-like objects, which is simply a tuple (<single circuit>, <optional one or more parameter values>, <optional shots>, <optional POVM implementation>)
. Here we will use the fact that the last three items are optional because : - The circuits are not parametrized so there is no need to specify parameter values. - We assume a default number of shot will be supplied with the POVMSampler.run
method. - We assume a default POVM implementation will be supplied with the
POVMSampler.run
method.
[8]:
# All of the following syntax are equivalent:
pub2 = (qc2, None) # or (qc2, None, None) or (qc2, None, None, None)
pub3 = (qc3,)
pub4 = qc4
Set a default measurement procedure¶
We specify a povm_toolbox.library.POVMImplementation
instance that will be the default one if no measurement is specified by a PUB.
[9]:
shots_default = 2048
measurement_default = ClassicalShadows(num_qubits=2, seed=78)
Third example¶
Let’s look at an example with a parametrized circuit.
Initialize QuantumCircuit and POVMSampler¶
The first step is to create the qiskit.circuit.QuantumCircuits
from which you want to obtain the probability distribution with the povm_toolbox.sampler.POVMSampler
.
[10]:
import numpy as np
qc5 = QuantumCircuit(3)
theta = Parameter("theta")
qc5.ry(theta, 0)
qc5.cx(0, 1)
qc5.cx(1, 2)
qc5.draw("mpl", style="iqp")
[10]:
Set the parameter values to bound to the QuantumCircuit
¶
In this example the quantum circuit has a single parameter (theta
). We can set different values for theta
.
[11]:
# Set 4 different values for theta
parameter_values = np.array([0.0, 0.4, 0.5 * np.pi, np.pi])
Initialize measurement procedure¶
Next, create a povm_toolbox.library.POVMImplementation
instance.
[12]:
shots5 = 4096
measurement5 = ClassicalShadows(num_qubits=3, seed=62)
measurement5.measurement_circuit.draw("mpl")
[12]:
Create the PUB¶
Finally, create the pub-like object, which is simply a tuple (<single circuit>, <optional one or more parameter values>, <optional shots>, <optional POVM implementation>)
[13]:
pub5 = (qc5, parameter_values, shots5, measurement5)
Initialize the POVMSampler
¶
[14]:
from povm_toolbox.sampler import POVMSampler
from qiskit.primitives import StatevectorSampler as Sampler
sampler = Sampler(seed=91)
povm_sampler = POVMSampler(sampler=sampler)
Run and get results¶
Now that you have defined your povm_sampler
, run it by calling the povm_toolbox.sampler.POVMSampler.run
method, which returns an instance of povm_toolbox.sampler.POVMSamplerJob
. You can get the results from the job (as a qiskit.primitives.PrimitiveResult
object) with the povm_toolbox.sampler.POVMSamplerJob.result
method.
[15]:
job = povm_sampler.run(
[pub1, pub2, pub3, pub4, pub5], shots=shots_default, povm=measurement_default
)
result = job.result()
print(result)
PrimitiveResult([POVMPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(), num_shots=128, num_bits=2>)), metadata=RPMMetadata(povm_implementation=ClassicalShadows(num_qubits=2), composed_circuit=<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x00000176C8F10B50>, pvm_keys=np.ndarray<128,2>)), POVMPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(), num_shots=2048, num_bits=2>)), metadata=RPMMetadata(povm_implementation=ClassicalShadows(num_qubits=2), composed_circuit=<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x00000176C8F108B0>, pvm_keys=np.ndarray<2048,2>)), POVMPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(), num_shots=2048, num_bits=2>)), metadata=RPMMetadata(povm_implementation=ClassicalShadows(num_qubits=2), composed_circuit=<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x00000176C8F118A0>, pvm_keys=np.ndarray<2048,2>)), POVMPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(), num_shots=2048, num_bits=2>)), metadata=RPMMetadata(povm_implementation=ClassicalShadows(num_qubits=2), composed_circuit=<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x00000176C8F11E10>, pvm_keys=np.ndarray<2048,2>)), POVMPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(4,), num_shots=4096, num_bits=3>)), metadata=RPMMetadata(povm_implementation=ClassicalShadows(num_qubits=3), composed_circuit=<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x00000176C8F10A60>, pvm_keys=np.ndarray<4,4096,3>))], metadata={'raw_results': PrimitiveResult([SamplerPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(128,), num_shots=1, num_bits=2>), shape=(128,)), metadata={'shots': 1}), SamplerPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(2048,), num_shots=1, num_bits=2>), shape=(2048,)), metadata={'shots': 1}), SamplerPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(2048,), num_shots=1, num_bits=2>), shape=(2048,)), metadata={'shots': 1}), SamplerPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(2048,), num_shots=1, num_bits=2>), shape=(2048,)), metadata={'shots': 1}), SamplerPubResult(data=DataBin(povm_measurement_creg=BitArray(<shape=(4, 4096), num_shots=1, num_bits=3>), shape=(4, 4096)), metadata={'shots': 1})], metadata={})})
[16]:
for pub_result in result:
print(pub_result.get_counts())
[Counter({(5, 0): 19, (3, 4): 19, (5, 2): 15, (3, 0): 15, (2, 2): 14, (0, 0): 14, (1, 2): 14, (5, 4): 10, (1, 4): 8})]
[Counter({(5, 2): 243, (3, 2): 240, (4, 4): 233, (1, 4): 232, (3, 0): 227, (0, 0): 224, (5, 0): 223, (1, 2): 214, (3, 4): 212})]
[Counter({(5, 0): 246, (5, 2): 245, (4, 4): 232, (1, 4): 232, (1, 0): 231, (1, 2): 220, (2, 2): 219, (3, 4): 215, (3, 0): 208})]
[Counter({(3, 2): 244, (1, 0): 242, (5, 2): 241, (3, 0): 240, (5, 4): 237, (1, 4): 213, (3, 4): 213, (5, 0): 210, (1, 2): 208})]
[Counter({(4, 5, 4): 179, (2, 5, 2): 170, (4, 5, 2): 168, (0, 5, 4): 164, (5, 4, 0): 163, (3, 2, 0): 161, (5, 2, 0): 158, (0, 3, 2): 158, (0, 4, 0): 157, (0, 3, 4): 155, (2, 3, 2): 155, (0, 0, 2): 155, (0, 5, 2): 152, (2, 5, 4): 150, (4, 3, 2): 149, (0, 0, 4): 149, (3, 0, 2): 149, (4, 0, 0): 146, (0, 2, 0): 146, (2, 3, 4): 146, (4, 3, 4): 143, (5, 0, 4): 140, (0, 0, 0): 139, (3, 0, 4): 137, (5, 0, 2): 137, (3, 4, 0): 135, (2, 0, 0): 135})
Counter({(4, 0, 0): 179, (2, 3, 2): 167, (2, 0, 0): 164, (4, 3, 4): 164, (2, 3, 4): 164, (3, 0, 2): 163, (2, 5, 2): 162, (0, 0, 4): 160, (5, 0, 2): 159, (4, 5, 2): 157, (0, 5, 2): 156, (4, 5, 4): 155, (0, 3, 2): 155, (3, 0, 4): 154, (0, 0, 2): 150, (5, 2, 0): 149, (0, 5, 4): 148, (4, 3, 2): 145, (3, 2, 0): 144, (0, 0, 0): 143, (0, 2, 0): 141, (3, 4, 0): 140, (2, 5, 4): 139, (5, 4, 0): 137, (0, 4, 0): 135, (0, 3, 4): 133, (5, 0, 4): 133})
Counter({(1, 1, 2): 182, (4, 5, 4): 174, (4, 3, 2): 169, (2, 5, 2): 163, (0, 5, 4): 163, (0, 3, 4): 163, (0, 3, 2): 162, (4, 1, 4): 159, (3, 0, 0): 157, (0, 3, 0): 154, (2, 3, 4): 154, (2, 3, 0): 153, (1, 1, 4): 153, (2, 5, 0): 151, (4, 3, 0): 150, (0, 5, 0): 149, (2, 1, 2): 149, (2, 1, 4): 147, (4, 3, 4): 144, (0, 5, 2): 144, (4, 5, 2): 143, (4, 1, 2): 142, (4, 5, 0): 140, (2, 5, 4): 136, (0, 0, 0): 132, (5, 0, 0): 132, (3, 3, 2): 131})
Counter({(3, 1, 4): 180, (4, 3, 2): 179, (1, 5, 4): 170, (2, 5, 4): 168, (2, 3, 4): 167, (4, 1, 1): 166, (1, 5, 2): 165, (5, 1, 4): 161, (2, 1, 1): 160, (1, 3, 4): 158, (1, 1, 4): 151, (5, 4, 1): 149, (3, 1, 2): 148, (3, 2, 1): 147, (1, 1, 2): 146, (4, 5, 4): 144, (4, 5, 2): 144, (1, 3, 2): 143, (1, 2, 1): 143, (1, 1, 1): 143, (1, 4, 1): 143, (3, 4, 1): 143, (5, 2, 1): 141, (2, 3, 2): 138, (5, 1, 2): 136, (2, 5, 2): 133, (4, 3, 4): 130})]
[17]:
from povm_toolbox.post_processor import POVMPostProcessor
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])
post_processor1 = POVMPostProcessor(result[0])
exp_value, std = post_processor1.get_expectation_value(observable=observable)
print(exp_value)
for pub_result in result[1:4]:
post_processor = POVMPostProcessor(pub_result)
exp_value, std = post_processor.get_expectation_value(observable=observable)
print(exp_value)
3.671874999999999
-0.09423828125000021
-0.07226562499999953
-0.09423828124999951
[18]:
post_processor5 = POVMPostProcessor(result[4])
observable = SparsePauliOp(
["IIZ", "IXX", "YIY", "ZZI", "XXX", "XYZ"], coeffs=[1, 1, -1, 1, 0.5, -2]
)
exp_value, std = post_processor5.get_expectation_value(observable=observable)
print(exp_value)
[ 2.00793457 2.0892334 2.35656738 -3.83862305]
[19]:
exp_value, std = post_processor5.get_expectation_value(observable=observable, loc=(1))
print(exp_value, "\u00b1", std)
2.089233398437499 ± 0.20321114673076907