Skip to content

Commit 1236ee1

Browse files
committed
added dataclasses and comments, started minor restructuring
1 parent e1f58e1 commit 1236ee1

File tree

1 file changed

+68
-18
lines changed

1 file changed

+68
-18
lines changed

zero_noise_extrapolation_cnot.py

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from numpy import asarray, ndarray, shape, zeros, empty, average
1111

1212
import random, os, pickle, sys, errno
13+
from dataclasses import dataclass
1314

1415
abs_path = os.path.dirname(__file__)
1516
sys.path.append(abs_path)
@@ -41,6 +42,27 @@
4142
"""
4243

4344

45+
# Dataclasses:
46+
47+
@dataclass(frozen=True)
48+
class NoiseAmplifiedResult:
49+
amplification_factor: int
50+
exp_val: float
51+
circuit: QuantumCircuit
52+
depth: int
53+
shots: int
54+
55+
56+
@dataclass(frozen=True)
57+
class ZeroNoiseExtrapolationResult:
58+
result: float
59+
circuit: QuantumCircuit
60+
all_exp_vals: ndarray
61+
noise_amplified_exp_vals: ndarray
62+
depths: ndarray
63+
64+
65+
# Zero-noise extrapolation class:
4466
class ZeroNoiseExtrapolation:
4567

4668
def __init__(self, qc: QuantumCircuit, exp_val_func: Callable, backend=None, exp_val_filter=None,
@@ -62,7 +84,7 @@ def __init__(self, qc: QuantumCircuit, exp_val_func: Callable, backend=None, exp
6284
quantum circuit.
6385
The function should take two arguments: a list of qiskit.result.result.ExperimentResult objects as its first
6486
argument, and a possible filter as its second.
65-
The function should return: An array
87+
The function should return: An array of computed expectation values
6688
6789
backend : A valid qiskit backend, IBMQ device or simulator
6890
A qiskit backend, either an IBMQ quantum backend or a simulator backend, for circuit executions.
@@ -162,8 +184,9 @@ def __init__(self, qc: QuantumCircuit, exp_val_func: Callable, backend=None, exp
162184

163185
# Will store expectation values for each individual circuit execution
164186
self.all_exp_vals = zeros(0)
165-
# Will store expectation values for each noise amplified circuit, averaged over all sub-executions
187+
# Will store expectation values and variances from the execution of each noise amplified circuit
166188
self.noise_amplified_exp_vals = zeros(0)
189+
self.noise_amplified_variances = zeros(0)
167190

168191
self.result = None
169192

@@ -172,8 +195,8 @@ def __init__(self, qc: QuantumCircuit, exp_val_func: Callable, backend=None, exp
172195
def partition_shots(self, tot_shots: int) -> (int, int):
173196
"""
174197
IBMQ devices limits circuit executions to a max of 8192 shots per experiment. To perform more than 8192 shots,
175-
the experiment has to be repeated. Therefore, if shots > 8192, we partition the execution into several repeats
176-
of less than 8192 shots each.
198+
the experiment has to be partitioned into a set of circuit executions, each with less than 8192 shots.
199+
Therefore, if shots > 8192, we partition the execution into several repeats of less than 8192 shots each.
177200
178201
Parameters
179202
----------
@@ -304,7 +327,7 @@ def noise_amplify_and_pauli_twirl_cnots(self, qc: QuantumCircuit, amp_factor: in
304327
# The 'Unroller' transpiler pass 'unrolls' (decomposes) the circuit gates to be expressed in terms of the
305328
# physical gate set [u1,u2,u3,cx]
306329

307-
# The cz, cy (controlled-Z and -Y) gates can be constructed from a single cx-gate and sinlge-qubit gates.
330+
# The cz, cy (controlled-Z and -Y) gates can be constructed from a single cx-gate and single-qubit gates.
308331
# For backends with native gate sets consisting of some set of single-qubit gates and either the cx, cz or cy,
309332
# unrolling the circuit to the ["u3", "cx"] basis, amplifying the cx-gates, then unrolling back to the native
310333
# gate set and doing a single-qubit optimization transpiler pass, is thus still general.
@@ -369,7 +392,7 @@ def transpile_circuit(self, qc: QuantumCircuit, custom_pass_manager: PassManager
369392
The transpiled quantum circuit.
370393
"""
371394

372-
if custom_pass_manager == None:
395+
if custom_pass_manager is None:
373396
pass_manager_config = PassManagerConfig(basis_gates=["id", "u1", "u2", "u3", "cx"],
374397
backend_properties=self.backend.properties())
375398
if not self.is_simulator:
@@ -393,6 +416,9 @@ def transpile_circuit(self, qc: QuantumCircuit, custom_pass_manager: PassManager
393416

394417
def execute_circuit(self, qc: QuantumCircuit, shots=None) -> Result:
395418
"""
419+
Execute a single experiment consisting of the execution of a quantum circuit over a specified number of shots.
420+
One experiment may need to be partitioned into a set of several identical circuit executions. This is due to the
421+
IBMQ quantum devices limiting circuit executions to a maximum of 8192 shots per.
396422
397423
Parameters
398424
----------
@@ -429,28 +455,51 @@ def execute_circuit(self, qc: QuantumCircuit, shots=None) -> Result:
429455

430456
return circuit_measurement_results
431457

432-
def compute_exp_val(self, result: Result) -> (float, ndarray):
458+
def compute_exp_val(self, result: Result) -> (float, float, ndarray):
433459
"""
434-
Compute the expectation value for
460+
Compute the expectation value and variance for a set of circuit executions. We assume that all separate circuit
461+
execution was run with the same number of shots.
435462
436463
Parameters
437464
----------
438465
result : qiskit.result.result.Result
439466
A qiskit Result object containing all measurement results from a set of quantum circuit executions.
467+
440468
Returns
441469
-------
442-
averaged_experiment_exp_vals, experiment_exp_vals : Tuple[float, numpy.ndarray]
443-
The final experiment expectation value, averaged over all circuit sub-executions, and a numpy array
444-
containing the expectation values for each circuit sub-execution.
470+
averaged_experiment_exp_vals, averaged_experiment_variances experiment_exp_vals : Tuple[float, numpy.ndarray]
471+
The final estimated experiment expectation value and variance, averaged over all circuit sub-executions,
472+
and a numpy array containing the expectation values for each circuit sub-execution.
445473
"""
446474

447475
experiment_results = result.results
448476

449-
experiment_exp_vals = self.exp_val_func(experiment_results, self.exp_val_filter)
477+
experiment_exp_vals, experiment_variances = self.exp_val_func(experiment_results, self.exp_val_filter)
478+
479+
return average(experiment_exp_vals), average(experiment_variances), asarray(experiment_exp_vals)
450480

451-
return average(experiment_exp_vals), asarray(experiment_exp_vals)
481+
def compute_noise_amplified_exp_val(self):
482+
return
483+
484+
def sample_error(self):
485+
return
452486

453487
def extrapolate(self, n_amp_factors: int = None, noise_amplified_exp_vals: ndarray = None) -> float:
488+
"""
489+
Perform the extrapolation step of the zero-noise extrapolation.
490+
491+
Parameters
492+
----------
493+
n_amp_factors: int
494+
The number of amplification factors used.
495+
noise_amplified_exp_vals:
496+
The noise amplified expectation value from which to extrapolate to the zero-noise case
497+
498+
Returns
499+
-------
500+
result: float
501+
The resulting mitigated expectation value.
502+
"""
454503
if noise_amplified_exp_vals is None:
455504
noise_amplified_exp_vals = self.noise_amplified_exp_vals
456505
if n_amp_factors is None:
@@ -488,6 +537,7 @@ def mitigate(self, verbose: bool = False) -> float:
488537
n_amp_factors = shape(self.noise_amplification_factors)[0]
489538

490539
self.noise_amplified_exp_vals = zeros((n_amp_factors,))
540+
self.noise_amplified_variances = zeros((n_amp_factors,))
491541
self.all_exp_vals = zeros((n_amp_factors, self.repeats))
492542

493543
if verbose:
@@ -541,8 +591,8 @@ def mitigate(self, verbose: bool = False) -> float:
541591
if verbose:
542592
print("Results successfully written to disk.")
543593

544-
self.noise_amplified_exp_vals[i], self.all_exp_vals[i, :] = self.compute_exp_val(
545-
circuit_measurement_results)
594+
self.noise_amplified_exp_vals[i], self.noise_amplified_variances[i], self.all_exp_vals[i, :] \
595+
= self.compute_exp_val(circuit_measurement_results)
546596

547597
self.measurement_results.append(circuit_measurement_results)
548598

@@ -827,20 +877,20 @@ def noise_amplify_cnots(qc: QuantumCircuit, amp_factor: int):
827877
# unrolling the circuit to the ["u3", "cx"] basis, amplifying the cx-gates, then unrolling back to the native
828878
# gate set and doing a single-qubit optimization transpiler pass, is thus still general.
829879

830-
unroller_ugatesandcx = Unroller(["u1", "u2", "u3", "cx"])
880+
unroller_ugatesandcx = Unroller(["u", "cx"])
831881
pm = PassManager(unroller_ugatesandcx)
832882

833883
unrolled_qc = pm.run(qc)
834884

835885
circuit_qasm = unrolled_qc.qasm()
836886
new_circuit_qasm_str = ""
837887

838-
qreg_name = find_qreg_name(circuit_qasm)
888+
# qreg_name = find_qreg_name(circuit_qasm)
839889

840890
for i, line in enumerate(circuit_qasm.splitlines()):
841891
if line[0:2] == "cx":
842892
for j in range(amp_factor):
843-
new_circuit_qasm_str += pauli_twirl_cnot_gate(qreg_name, line)
893+
new_circuit_qasm_str += line + "\n"
844894
else:
845895
new_circuit_qasm_str += line + "\n"
846896

0 commit comments

Comments
 (0)