Closed nathanieltornow closed 2 days ago
To underline the issue with qcut_processing_fn_sample
, here is another example, looking at how accurately the function reconstructs "perfect" samples:
import pennylane as qml
from functools import partial
from pennylane import numpy as np
from qiskit.quantum_info import hellinger_fidelity
shots = 10000
dev = qml.device("default.qubit", wires=2, shots=shots)
def samples_to_counts(samples):
# Convert samples to a probability distribution
distr = {}
for sample in samples:
sample = tuple(sample)
if sample not in distr:
distr[sample] = 0
distr[sample] += 1
return distr
@partial(qml.cut_circuit_mc)
@qml.qnode(dev)
def circuit(x):
qml.RX(0.89, wires=0)
qml.RY(0.5, wires=1)
qml.RX(1.3, wires=2)
qml.CNOT(wires=[0, 1])
qml.WireCut(wires=1)
qml.CNOT(wires=[1, 2])
qml.RX(x, wires=0)
qml.RY(0.7, wires=1)
qml.RX(2.3, wires=2)
return qml.sample(wires=[0, 1, 2])
x = 0.3
c1 = samples_to_counts(circuit(x))
dev2 = qml.device("default.qubit", wires=3, shots=shots)
@qml.qnode(dev2)
def circuit_base(x):
qml.RX(0.89, wires=0)
qml.RY(0.5, wires=1)
qml.RX(1.3, wires=2)
qml.CNOT(wires=[0, 1])
qml.WireCut(wires=1)
qml.CNOT(wires=[1, 2])
qml.RX(x, wires=0)
qml.RY(0.7, wires=1)
qml.RX(2.3, wires=2)
return qml.sample(wires=[0, 1, 2])
c2 = samples_to_counts(circuit_base(x))
print(hellinger_fidelity(c1, c2))
0.7794567323985822
This indicates that the sampling method is not completely off but still far away from a good (expected) fidelity of >0.99
Hi @nathanieltornow, thanks for this question.
You're right, qcut_processing_fn_sample
will not provide samples following the probability distribution of the original uncut circuit. Instead, it simply outputs samples that are stitched together from samples of the cut-up circuit fragments. The hope is that these samples might be some approximation to the distribution of the uncut circuit, but I don't recall any results that show this.
Instead, qcut_processing_fn_mc
uses the approach described in Appendix IV of Peng et al. that combines the terminal and mid-circuit samples together to produce a faithful estimate of the expectation value from the uncut circuit. This is the postprocessing function we'd recommend to use, but we decided to provide both.
Note that both of these postprocessing functions can be used as part of cut_circuit_mc
, so we recommend passing a classical_processing_fn
here.
Expected behavior
Hi! I have a question regarding the two postprocessing functions of qcut:
Is
qcut_processing_fn_mc
with a postprocessing functionfn
the same as runningqcut_processing_fn_sample
and runningnp.mean([fn(s) for s in samples])
on the resulting samples? From the current documentation, their relationship is not entirely clear.Further, the
qcut_processing_fn_sample
function seems to ignore the output of outgoing edges in the subcircuit. Is, therefore,qcut_processing_fn_sample
producing "correct" samples?Actual behavior
I expect that the following code should output the same expectation value for the
ZZZ
observable for the circuit, post-processed with the two mentioned postprocessing methods:However, the output is
indicating that the sampling method fails to achieve the same (correct) result. (the correct result is
-0.2981
) Can we, therefore, say thatqcut_processing_fn_sample
is correct in reconstructing samples of the original circuit?Additional information
No response
Source code
No response
Tracebacks
No response
System information
Existing GitHub issues