Optimize dual frame¶
This document shows you how to optimize the post-processing of the POVM samples to improve expectation value estimations.
[1]:
%load_ext autoreload
%autoreload 2
Getting the POVM samples¶
Define quantum circuit¶
[2]:
from qiskit.circuit.random import random_circuit
qc = random_circuit(5, 3, measure=False, seed=875)
qc.draw("mpl", style="iqp")
[2]:
![../_images/how_tos_dual_optimizer_4_0.png](../_images/how_tos_dual_optimizer_4_0.png)
Define measurement procedure¶
[3]:
import numpy as np
from numpy.random import default_rng
from povm_toolbox.library import RandomizedProjectiveMeasurements
rng = default_rng(96568)
num_qubits = qc.num_qubits
bias = np.array([0.5, 0.25, 0.25])
angles = np.array(
[
[2.01757238, -1.85001671, 2.52155716, 0.45636669, 1.17175533, -0.48263278],
[0.0, 0.0, 1.57079633, -2.35619449, 1.57079633, -0.78539816],
[1.94493547, -2.39620342, 0.3760775, -2.28966468, 1.53443501, 2.33046898],
[0.0, 0.0, 1.57079633, 0.6, 1.57079633, 2.17079633],
[0.0, 0.0, 1.57079633, 0.0, 1.57079633, 1.57079633],
]
)
measurement = RandomizedProjectiveMeasurements(num_qubits, bias=bias, angles=angles, seed_rng=rng)
measurement.definition().draw_bloch(
title=f"{num_qubits}-qubit Randomized Projective Measurements", colorbar=True
)
[3]:
![../_images/how_tos_dual_optimizer_6_0.png](../_images/how_tos_dual_optimizer_6_0.png)
Run the job¶
Initialize Sampler
and POVMSampler
. Then run the job.
[4]:
from povm_toolbox.sampler import POVMSampler
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler(seed=rng)
povm_sampler = POVMSampler(sampler=sampler)
job = povm_sampler.run([qc], shots=4096, povm=measurement)
pub_result = job.result()[0]
Define observable¶
[5]:
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(
["XIIII", "IIYII", num_qubits * "Y", num_qubits * "Z"], coeffs=[1.3, 1.2, -1, 1.4]
)
Choosing the dual frame¶
Given a POVM \(\{M_k\}_k\), a dual frame \(\{D_k\}_k\) can be obtained through
where the parametrized frame super-operator \(\mathcal{F}_{\alpha}\) is defined as
for parameters \(\{\alpha_k\}_k \subset \mathbb{R}\).
Canonical dual frame¶
The default case implemented in the POVMPostProcessor
is to use the canonical dual frame, which is defined by parameters
[6]:
from povm_toolbox.post_processor import POVMPostProcessor
from qiskit.quantum_info import Statevector
exact_expectation_value = np.real_if_close(Statevector(qc).expectation_value(observable))
print(f"Exact value: {exact_expectation_value}")
post_processor = POVMPostProcessor(pub_result)
exp_value_canonical, std_canonical = post_processor.get_expectation_value(observable)
print(f"Estimated value: {exp_value_canonical}")
print(f"\nEstimated standard deviation of the estimator: {std_canonical}")
Exact value: 0.9786732290044553
Estimated value: 0.7885807349016064
Estimated standard deviation of the estimator: 0.4044506732174399
State-marginal dual frame¶
Here we use the state-marginal dual frame, which requires the knowledge of the sampled state \(\rho\). More specifically, we compute the outcome probabilities
where the POVM is assumed to have a product structure. We then marginalize these outcome probabilities to ensure that the dual frame will also have a product structure. That is, we set the parameters of the dual frame to
where \(p^{(i)}_{k_i}\) is the (marginal) probability of obtaining outcome \(k_i\) on qubit \(i\).
The caveat of this dual frame is that we do not have access to the state \(\rho\) in general. However, it does work for testing and/or teaching purposes.
[7]:
from povm_toolbox.post_processor import dual_from_marginal_probabilities
post_processor.dual = dual_from_marginal_probabilities(
povm=post_processor.povm, state=Statevector(qc)
)
exp_value_marginal, std_marginal = post_processor.get_expectation_value(observable)
print(f"Estimated value: {exp_value_marginal}")
print(f"\nEstimated standard deviation of the estimator: {std_marginal}")
Estimated value: 0.9970974145287157
Estimated standard deviation of the estimator: 0.3041460424516805
Empirical frequencies dual frame¶
We now present a dual frame that uses the same idea as for the state-marginal dual frame, but without requiring the knowledge of the state \(\rho\) a priori. More precisely, we replace the marginal outcome probabilities by the empirical outcome frequencies:
where \(f^{(i)}_{k_i} = \# k_i / S\) is the number of time we sampled outcome \(k_i\) on qubit \(i\) divided by the total number of shots \(S\).
To add stability of the estimator (and avoid parameters set to 0 if an outcome was not sampled), we add a bias term to the frequencies as
where \(\rho_{\mathrm{bias}}\) is an ansatz state (by default set to \(\frac{1}{d}\mathbb{I}\)) and \(S_{\mathrm{bias}}\) is the strength of the bias (by default set to the number of outcomes of the local POVM).
We have two edge cases: * In the limit \(S \to 0\), we recover the canonical dual frame if \(\rho_{\mathrm{bias}}=\frac{1}{d}\mathbb{I}\). * In the limit \(S \to \infty\), we recover the marginal-state dual frame (independently of \(\rho_{\mathrm{bias}}\)).
[8]:
from povm_toolbox.post_processor import dual_from_empirical_frequencies
post_processor.dual = dual_from_empirical_frequencies(povm_post_processor=post_processor)
exp_value_freq, std_freq = post_processor.get_expectation_value(observable)
print(f"Estimated value: {exp_value_freq}")
print(f"\nEstimated standard deviation of the estimator: {std_freq}")
Estimated value: 1.0142327046854116
Estimated standard deviation of the estimator: 0.30334996039156614
Summary¶
We finally compare the estimates obtained with the different dual frames.
[10]:
print(f"Exact value: {exact_expectation_value}\n")
print("Dual frame Estimated value Estimated std Actual error")
print("------------------------------------------------------------------------")
print(
f"canonical {exp_value_canonical:>29.6f} {std_canonical:>15.6f} {abs(exp_value_canonical - exact_expectation_value):>14.6f}"
)
print(
f"marginal probabilities {exp_value_marginal:>16.6f} {std_marginal:>15.6f} {abs(exp_value_marginal - exact_expectation_value):>14.6f}"
)
print(
f"empirical frequencies {exp_value_freq:>17.6f} {std_freq:>15.6f} {abs(exp_value_freq - exact_expectation_value):>14.6f}"
)
Exact value: 0.9786732290044553
Dual frame Estimated value Estimated std Actual error
------------------------------------------------------------------------
canonical 0.788581 0.404451 0.190092
marginal probabilities 0.997097 0.304146 0.018424
empirical frequencies 1.014233 0.303350 0.035559