Skip to content

Commit f065e2b

Browse files
committed
finished notebook, added properties and fixed some bugs
1 parent 7300791 commit f065e2b

File tree

2 files changed

+121
-57
lines changed

2 files changed

+121
-57
lines changed

3qubit Swap-test.ipynb

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,11 @@
146146
{
147147
"cell_type": "markdown",
148148
"metadata": {},
149-
"source": []
149+
"source": [
150+
"We specify the expectation value function that computes the estimated expectation value from a set of quantum circuit experiment results.\n",
151+
"\n",
152+
"The repeating CNOTs implementation requires the expectation value function to return both the expectation value and the variances. The random Pauli-sampling implementation requires only the expectation value. To reuse code, we specify a filter to be passed to the function that specifies the method used."
153+
]
150154
},
151155
{
152156
"cell_type": "code",
@@ -378,7 +382,7 @@
378382
],
379383
"source": [
380384
"x = qem.noise_amplification_factors\n",
381-
"y = qem.get_noise_amplified_exp_vals()\n",
385+
"y = qem.noise_amplified_exp_vals\n",
382386
"\n",
383387
"plt.xlabel(r\"$r$, noise amplification factor\")\n",
384388
"plt.ylabel(r\"$E_r$, expectation value\")\n",
@@ -396,7 +400,11 @@
396400
{
397401
"cell_type": "markdown",
398402
"metadata": {},
399-
"source": []
403+
"source": [
404+
"We plot the mitigated expectation value, as a function of the number of noise amplification factors used, and we can see a noticable improvement.\n",
405+
"\n",
406+
"From the plot above we observe that the noise amplified expectation values are being slightly over-estimated for the last amplification factors for the repeating CNOTs-method. Correspondingly, we observe that the mitigated expectation value flattens off and fails to improve when including these amplification factors."
407+
]
400408
},
401409
{
402410
"cell_type": "code",
@@ -418,7 +426,7 @@
418426
],
419427
"source": [
420428
"n_amp_factors = qem.n_amp_factors\n",
421-
"noise_amplified_exp_vals = qem.get_noise_amplified_exp_vals()\n",
429+
"noise_amplified_exp_vals = qem.noise_amplified_exp_vals\n",
422430
"noise_amplification_factors = qem.noise_amplification_factors\n",
423431
"\n",
424432
"mitigated_exp_vals = np.zeros(n_amp_factors)\n",
@@ -436,16 +444,11 @@
436444
"plt.show()"
437445
]
438446
},
439-
{
440-
"cell_type": "markdown",
441-
"metadata": {},
442-
"source": []
443-
},
444447
{
445448
"cell_type": "markdown",
446449
"metadata": {},
447450
"source": [
448-
"Zero noise extrapolation, mitigating noise on CNOT-gates, on the SWAP-test circuit. Now on the mock backend FakeVigo, which emulates IBMQ's Vigo quantum device.\n",
451+
"Zero noise extrapolation, mitigating noise on CNOT-gates, on the SWAP-test circuit. Now on the mock backend FakeAthens that emulates the IBMQ Athens quantum device. This mock backend has the same configurations as the real device, and an noise model that aims to approximate the physical device as closely as possible.\n",
449452
"\n",
450453
"Noise amplification factors = [1,3,5,7,9]"
451454
]
@@ -544,7 +547,11 @@
544547
{
545548
"cell_type": "markdown",
546549
"metadata": {},
547-
"source": []
550+
"source": [
551+
"For comparisons we run also the zero-noise extrapolation implementation with noise amplification by random Pauli gate-sampling.\n",
552+
"\n",
553+
"While the repeating CNOT-method requires odd noise amplificaiton factors (1, 3, 5, ..., 2n-1), the random Pauli-method seems to work best with powers of two (1, 2, 4, ..., 2^(n-1))."
554+
]
548555
},
549556
{
550557
"cell_type": "code",
@@ -596,6 +603,15 @@
596603
"print(R[-1])"
597604
]
598605
},
606+
{
607+
"cell_type": "markdown",
608+
"metadata": {},
609+
"source": [
610+
"We plot the mitigated expectation values as a function of the number of amplification factors used for both methods. We observe from the below plot that the repeating CNOTs-method converges a lot closer to the ideal expectation value than the random Pauli-method.\n",
611+
"\n",
612+
"The random Pauli-method relies on approximating the quantum noise as a two-qubit depolarizing noise model. Optionally, doing a pauli-twirling before applying this assumption. The repeating CNOTs-method have no such assumptions about the character of the quantum noise. Thus, we might expected the latter to work better for general noise models, and exp"
613+
]
614+
},
599615
{
600616
"cell_type": "code",
601617
"execution_count": 13,
@@ -605,7 +621,6 @@
605621
"name": "stdout",
606622
"output_type": "stream",
607623
"text": [
608-
"(2048, 1) (2048,) (2048,)\n",
609624
"CNOT repetition, mitigated exp vals: [0.29174495 0.38494003 0.43351895 0.46148582 0.4778664 0.48694545\n",
610625
" 0.49105048]\n",
611626
"Random pauli gates, mitigated exp vals: [0.29193199 0.40144479 0.44075068 0.45646464 0.4634236 0.46669605\n",
@@ -628,7 +643,7 @@
628643
"source": [
629644
"# Process results from ZNE with noise amplification by CNOT repetition:\n",
630645
"n_amp_factors = qem.n_amp_factors\n",
631-
"noise_amplified_exp_vals = qem.get_noise_amplified_exp_vals()\n",
646+
"noise_amplified_exp_vals = qem.noise_amplified_exp_vals\n",
632647
"noise_amplification_factors = qem.noise_amplification_factors\n",
633648
"\n",
634649
"mitigated_exp_vals = np.zeros(n_amp_factors)\n",
@@ -672,14 +687,9 @@
672687
{
673688
"cell_type": "markdown",
674689
"metadata": {},
675-
"source": []
676-
},
677-
{
678-
"cell_type": "code",
679-
"execution_count": null,
680-
"metadata": {},
681-
"outputs": [],
682-
"source": []
690+
"source": [
691+
"Qiskit version:"
692+
]
683693
},
684694
{
685695
"cell_type": "code",
@@ -705,13 +715,6 @@
705715
"source": [
706716
"qiskit.__qiskit_version__"
707717
]
708-
},
709-
{
710-
"cell_type": "code",
711-
"execution_count": null,
712-
"metadata": {},
713-
"outputs": [],
714-
"source": []
715718
}
716719
],
717720
"metadata": {

zero_noise_extrapolation_cnot.py

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
sys.path.append(abs_path)
1818
sys.path.append(os.path.dirname(abs_path))
1919

20-
from zero_noise_extrapolation import Richardson_extrapolate
21-
2220
from typing import Callable, Union
2321

2422
"""
@@ -63,21 +61,26 @@ class ZeroNoiseExtrapolationResult:
6361
gamma_coefficients: ndarray
6462
exp_val: float
6563

66-
def get_bare_exp_val(self) -> float:
64+
@property
65+
def bare_exp_val(self) -> float:
6766
return self.noise_amplified_results[0].exp_val
6867

69-
def get_noise_amplified_exp_vals(self) -> ndarray:
68+
@property
69+
def noise_amplified_exp_vals(self) -> ndarray:
7070
return asarray([result.exp_val for result in self.noise_amplified_results])
7171

72-
def get_noise_amplified_variances(self) -> ndarray:
72+
@property
73+
def noise_amplified_variances(self) -> ndarray:
7374
return asarray([result.variance for result in self.noise_amplified_results])
7475

75-
def get_noise_amplified_circuit_depths(self) -> ndarray:
76-
return asarray([result.qc.depth() for result in self.noise_amplified_results])
77-
78-
def get_noise_amplified_circuit_shots(self) -> ndarray:
76+
@property
77+
def shots(self) -> ndarray:
7978
return asarray([result.shots for result in self.noise_amplified_results])
8079

80+
@property
81+
def depths(self) -> ndarray:
82+
return asarray([result.qc.depth() for result in self.noise_amplified_results])
83+
8184

8285
# Zero-noise extrapolation class:
8386
class ZeroNoiseExtrapolation:
@@ -206,8 +209,6 @@ def __init__(self, qc: QuantumCircuit, exp_val_func: Callable, backend=None, exp
206209

207210
self.result = None
208211

209-
self.mitigated_exp_val = None
210-
211212
@staticmethod
212213
def partition_shots(tot_shots: int) -> (int, int):
213214
"""
@@ -234,14 +235,79 @@ def partition_shots(tot_shots: int) -> (int, int):
234235
repeats = (tot_shots // 8192) + 1
235236
return int(tot_shots / repeats), repeats
236237

237-
# Useful set and get functions:
238+
# We use the @property decorator for certain useful data that we might want to retrieve that are stored within the
239+
# noise amplified results
240+
241+
@property
242+
def bare_exp_val(self) -> float:
243+
return self.noise_amplified_results[0].exp_val
238244

239-
def get_noise_amplified_exp_vals(self) -> ndarray:
245+
@property
246+
def noise_amplified_exp_vals(self) -> ndarray:
240247
return asarray([result.exp_val for result in self.noise_amplified_results])
241248

242-
# Error mitigation help functions:
249+
@property
250+
def noise_amplified_variances(self) -> ndarray:
251+
return asarray([result.variance for result in self.noise_amplified_results])
252+
253+
@property
254+
def depths(self) -> ndarray:
255+
return asarray([result.qc.depth() for result in self.noise_amplified_results])
256+
257+
@property
258+
def mitigated_exp_val(self) -> float:
259+
if self.result is None:
260+
return None
261+
return self.result.exp_val
262+
263+
# Functions for computing the Richardson extrapolation
264+
265+
def extrapolate(self, noise_amplified_exp_vals: ndarray):
266+
"""
267+
The Richardson extrapolation reads
268+
269+
E^* = \sum_i gamma_i * E(\lambda_i),
270+
271+
where the gamma_i-coefficients are computed as a function of the amplification factors by solving a system
272+
of linear equations, this is done in self.compute_extrapolation_coefficients(), and E(\lambda_i) is the
273+
i-th noise amplified expectation value, corresponding to the amplification factor \lambda_i.
274+
275+
Ref: https://doi.org/10.1098/rsta.1911.0009
276+
277+
Parameters
278+
----------
279+
noise_amplified_exp_vals: numpy.ndarray
280+
The noise amplified expectation value, in order corresponding to amplification factors 1, 3, 5, ... , 2n-1.
281+
282+
Returns
283+
-------
284+
mitigated_exp_val: float
285+
The mitigated expectation value, which is the exp val extrapolated to the zero-noise case.
286+
"""
287+
if self.n_amp_factors == 1:
288+
return noise_amplified_exp_vals[0]
289+
if not shape(noise_amplified_exp_vals)[0] == shape(self.gamma_coefficients)[0]:
290+
raise Exception("Shape mismatch between noise_amplified_exp_vals and gamma_coefficients." +
291+
" length={:}".format(shape(noise_amplified_exp_vals)[0]) +
292+
" does not match length={:}".format(shape(self.gamma_coefficients)[0]))
293+
return dot(transpose(noise_amplified_exp_vals), self.gamma_coefficients)[0]
243294

244295
def compute_extrapolation_coefficients(self, n_amp_factors: int = None) -> ndarray:
296+
"""
297+
Compute the gamma_i-coefficients used in the Richardson extrapolation. We assume the specific noise
298+
amplification factors to be 1, 3, 5, ..., 2n-1, where n=n_amp_factors is the number of noise amplification
299+
factors.
300+
301+
Parameters
302+
----------
303+
n_amp_factors: int
304+
Number of amplification factors.
305+
306+
Returns
307+
----------
308+
gamma_coefficients: numpy.ndarray
309+
The set of coefficients to be used in the Richardson extrapolation.
310+
"""
245311
if n_amp_factors is None:
246312
n_amp_factors = self.n_amp_factors
247313
if n_amp_factors == 1:
@@ -251,7 +317,7 @@ def compute_extrapolation_coefficients(self, n_amp_factors: int = None) -> ndarr
251317

252318
A, b = zeros((n_amp_factors, n_amp_factors)), zeros((n_amp_factors, 1))
253319

254-
A[0,:], b[0] = 1, 1
320+
A[0, :], b[0] = 1, 1
255321

256322
for k in range(1, n_amp_factors):
257323
A[k, :] = amplification_factors**k
@@ -260,14 +326,7 @@ def compute_extrapolation_coefficients(self, n_amp_factors: int = None) -> ndarr
260326

261327
return gamma_coefficients
262328

263-
def extrapolate(self, noise_amplified_exp_vals: ndarray):
264-
if self.n_amp_factors == 1:
265-
return noise_amplified_exp_vals[0]
266-
if not shape(noise_amplified_exp_vals)[0] == shape(self.gamma_coefficients)[0]:
267-
raise Exception("Shape mismatch between noise_amplified_exp_vals and gamma_coefficients." +
268-
" length={:}".format(shape(noise_amplified_exp_vals)[0]) +
269-
" does not match length={:}".format(shape(self.gamma_coefficients)[0]))
270-
return dot(transpose(noise_amplified_exp_vals), self.gamma_coefficients)[0]
329+
# Functions involved in saving and loading results from disk
271330

272331
def set_experiment_name(self, experiment_name):
273332
"""
@@ -352,6 +411,8 @@ def write_to_file(self, filename: str, data):
352411
pickle.dump(data, file)
353412
file.close()
354413

414+
# Functions for processing and executing the quantum circuits
415+
355416
def noise_amplify_and_pauli_twirl_cnots(self, qc: QuantumCircuit, amp_factor: int,
356417
pauli_twirl: bool) -> QuantumCircuit:
357418
"""
@@ -505,6 +566,8 @@ def execute_circuit(self, qc: QuantumCircuit, shots=None) -> Result:
505566

506567
return circuit_measurement_results
507568

569+
# Functions involved in computing the noise amplified and mitigated expectation values and related measures.
570+
508571
def compute_exp_val(self, result: Result) -> (float, float, ndarray):
509572
"""
510573
Compute the expectation value and variance for a set of circuit executions. We assume that all separate circuit
@@ -695,9 +758,7 @@ def mitigate(self, verbose: bool = False) -> float:
695758
"variance = {:.8f}, ".format(noise_amplified_result.variance) +
696759
"total shots executed = {:}.".format(noise_amplified_result.shots))
697760

698-
mitigated_exp_val = self.extrapolate(self.get_noise_amplified_exp_vals())
699-
700-
self.mitigated_exp_val = mitigated_exp_val
761+
mitigated_exp_val = self.extrapolate(self.noise_amplified_exp_vals)
701762

702763
self.result = ZeroNoiseExtrapolationResult(qc=self.qc,
703764
noise_amplified_results=self.noise_amplified_results,
@@ -708,9 +769,9 @@ def mitigate(self, verbose: bool = False) -> float:
708769

709770
if verbose:
710771
print("-----\nERROR MITIGATION DONE\n" +
711-
"Bare circuit expectation value: {:.8f}\n".format(self.result.get_bare_exp_val()) +
712-
"Noise amplified expectation values: {:}\n".format(self.result.get_noise_amplified_exp_vals()) +
713-
"Circuit depths: {:}\n".format(self.result.get_noise_amplified_circuit_depths()) +
772+
"Bare circuit expectation value: {:.8f}\n".format(self.result.bare_exp_val) +
773+
"Noise amplified expectation values: {:}\n".format(self.result.noise_amplified_exp_vals) +
774+
"Circuit depths: {:}\n".format(self.result.depths) +
714775
"-----\n" +
715776
"Mitigated expectation value: {:.8f}\n".format(self.result.exp_val))
716777

0 commit comments

Comments
 (0)