-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding noise transformation to the noise package * Unified interface for dict/list/string approximating operator input * Implementation of approximate_noise_model * Now generates noise approximation using unitaries if possible * Now when possible outputs error circuits instead of Kraus/Unitary * Now represent all named operators using circuits
- Loading branch information
1 parent
e497c6d
commit 6909f6b
Showing
7 changed files
with
670 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright 2019, IBM. | ||
# | ||
# This source code is licensed under the Apache License, Version 2.0 found in | ||
# the LICENSE.txt file in the root directory of this source tree. | ||
|
||
"""Noise utils for Qiskit Aer. | ||
""" | ||
from .noise_transformation import NoiseTransformer | ||
from .noise_transformation import approximate_quantum_error | ||
from .noise_transformation import approximate_noise_model |
455 changes: 455 additions & 0 deletions
455
qiskit/providers/aer/noise/utils/noise_transformation.py
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ cmake | |
scikit-build | ||
cython | ||
asv | ||
cvxopt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -712,4 +712,4 @@ def test_to_channel_circuit(self): | |
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
# Copyright 2019, IBM. | ||
# | ||
# This source code is licensed under the Apache License, Version 2.0 found in | ||
# the LICENSE.txt file in the root directory of this source tree. | ||
""" | ||
NoiseTransformer class tests | ||
""" | ||
|
||
import unittest | ||
import numpy | ||
from qiskit.providers.aer.noise.errors.errorutils import standard_gate_unitary | ||
from qiskit.providers.aer.noise import NoiseModel | ||
from qiskit.providers.aer.noise.utils import NoiseTransformer | ||
from qiskit.providers.aer.noise.utils import approximate_quantum_error | ||
from qiskit.providers.aer.noise.utils import approximate_noise_model | ||
from qiskit.providers.aer.noise.errors.standard_errors import amplitude_damping_error | ||
from qiskit.providers.aer.noise.errors.standard_errors import reset_error | ||
from qiskit.providers.aer.noise.errors.standard_errors import pauli_error | ||
|
||
try: | ||
import cvxopt | ||
has_cvxopt = True | ||
except ImportError: | ||
has_cvxopt = False | ||
|
||
@unittest.skipUnless(has_cvxopt, "Needs cvxopt to test") | ||
class TestNoiseTransformer(unittest.TestCase): | ||
def setUp(self): | ||
self.ops = {'X': standard_gate_unitary('x'), | ||
'Y': standard_gate_unitary('y'), | ||
'Z': standard_gate_unitary('z'), | ||
'H': standard_gate_unitary('h'), | ||
'S': standard_gate_unitary('s') | ||
} | ||
self.n = NoiseTransformer() | ||
|
||
|
||
def assertNoiseModelsAlmostEqual(self, lhs, rhs, places = 3): | ||
self.assertNoiseDictsAlmostEqual(lhs._nonlocal_quantum_errors, rhs._nonlocal_quantum_errors, places=places) | ||
self.assertNoiseDictsAlmostEqual(lhs._local_quantum_errors, rhs._local_quantum_errors, places=places) | ||
self.assertNoiseDictsAlmostEqual(lhs._default_quantum_errors, rhs._default_quantum_errors, places=places) | ||
self.assertNoiseDictsAlmostEqual(lhs._local_readout_errors, rhs._local_readout_errors, places=places) | ||
if lhs._default_readout_error is not None: | ||
self.assertTrue(rhs._default_readout_error is not None) | ||
self.assertErrorsAlmostEqual(lhs._default_readout_error, rhs._default_readout_error, places=places) | ||
else: | ||
self.assertTrue(rhs._default_readout_error is None) | ||
|
||
def assertNoiseDictsAlmostEqual(self, lhs, rhs, places=3): | ||
keys = set(lhs.keys()).union(set(rhs.keys())) | ||
for key in keys: | ||
self.assertTrue(key in lhs.keys(), msg="Key {} is missing from lhs".format(key)) | ||
self.assertTrue(key in rhs.keys(), msg="Key {} is missing from rhs".format(key)) | ||
for (lhs_error, rhs_error) in zip (lhs[key], rhs[key]): | ||
self.assertErrorsAlmostEqual(lhs_error, rhs_error, places=places) | ||
|
||
def assertErrorsAlmostEqual(self, lhs, rhs, places = 3): | ||
self.assertMatricesAlmostEqual(lhs.to_channel()._data, rhs.to_channel()._data, places) | ||
|
||
def assertDictAlmostEqual(self, lhs, rhs, places = None): | ||
keys = set(lhs.keys()).union(set(rhs.keys())) | ||
for key in keys: | ||
self.assertAlmostEqual(lhs.get(key), rhs.get(key), msg = "Not almost equal for key {}: {} !~ {}".format(key, lhs.get(key), rhs.get(key)), places = places) | ||
|
||
def assertListAlmostEqual(self, lhs, rhs, places = None): | ||
self.assertEqual(len(lhs), len(rhs), msg = "List lengths differ: {} != {}".format(len(lhs), len(rhs))) | ||
for i in range(len(lhs)): | ||
if isinstance(lhs[i], numpy.ndarray) and isinstance(rhs[i], numpy.ndarray): | ||
self.assertMatricesAlmostEqual(lhs[i], rhs[i], places = places) | ||
else: | ||
self.assertAlmostEqual(lhs[i], rhs[i], places = places) | ||
|
||
def assertMatricesAlmostEqual(self, lhs, rhs, places = None): | ||
self.assertEqual(lhs.shape, rhs.shape, "Marix shapes differ: {} vs {}".format(lhs, rhs)) | ||
n, m = lhs.shape | ||
for x in range(n): | ||
for y in range(m): | ||
self.assertAlmostEqual(lhs[x,y], rhs[x,y], places = places, msg="Matrices {} and {} differ on ({}, {})".format(lhs, rhs, x, y)) | ||
|
||
|
||
def test_transformation_by_pauli(self): | ||
n = NoiseTransformer() | ||
#polarization in the XY plane; we represent via Kraus operators | ||
X = self.ops['X'] | ||
Y = self.ops['Y'] | ||
Z = self.ops['Z'] | ||
p = 0.22 | ||
theta = numpy.pi / 5 | ||
E0 = numpy.sqrt(1 - p) * numpy.array(numpy.eye(2)) | ||
E1 = numpy.sqrt(p) * (numpy.cos(theta) * X + numpy.sin(theta) * Y) | ||
results = approximate_quantum_error((E0, E1), operator_dict={"X": X, "Y": Y, "Z": Z}) | ||
expected_results = pauli_error([('X', p*numpy.cos(theta)*numpy.cos(theta)), | ||
('Y', p*numpy.sin(theta)*numpy.sin(theta)), | ||
('Z', 0), | ||
('I', 1-p)]) | ||
self.assertErrorsAlmostEqual(expected_results, results) | ||
|
||
|
||
#now try again without fidelity; should be the same | ||
n.use_honesty_constraint = False | ||
results = approximate_quantum_error((E0, E1), operator_dict={"X": X, "Y": Y, "Z": Z}) | ||
self.assertErrorsAlmostEqual(expected_results, results) | ||
|
||
def test_reset(self): | ||
# approximating amplitude damping using relaxation operators | ||
gamma = 0.23 | ||
error = amplitude_damping_error(gamma) | ||
p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2 | ||
q = 0 | ||
expected_results = reset_error(p,q) | ||
results = approximate_quantum_error(error, operator_string="reset") | ||
self.assertErrorsAlmostEqual(results, expected_results) | ||
|
||
def test_transform(self): | ||
X = self.ops['X'] | ||
Y = self.ops['Y'] | ||
Z = self.ops['Z'] | ||
p = 0.34 | ||
theta = numpy.pi / 7 | ||
E0 = numpy.sqrt(1 - p) * numpy.array(numpy.eye(2)) | ||
E1 = numpy.sqrt(p) * (numpy.cos(theta) * X + numpy.sin(theta) * Y) | ||
|
||
results_dict = approximate_quantum_error((E0, E1), operator_dict={"X": X, "Y": Y, "Z": Z}) | ||
results_string = approximate_quantum_error((E0, E1), operator_string='pauli') | ||
results_list = approximate_quantum_error((E0, E1), operator_list=[X, Y, Z]) | ||
results_tuple = approximate_quantum_error((E0, E1), operator_list=(X, Y, Z)) | ||
|
||
self.assertErrorsAlmostEqual(results_dict, results_string) | ||
self.assertErrorsAlmostEqual(results_string, results_list) | ||
self.assertErrorsAlmostEqual(results_list, results_tuple) | ||
|
||
def test_fidelity(self): | ||
n = NoiseTransformer() | ||
expected_fidelity = {'X': 0, 'Y': 0, 'Z': 0, 'H': 0, 'S': 2} | ||
for key in expected_fidelity: | ||
self.assertAlmostEqual(expected_fidelity[key], n.fidelity([self.ops[key]]), msg = "Wrong fidelity for {}".format(key)) | ||
|
||
def test_approx_noise_model(self): | ||
noise_model = NoiseModel() | ||
gamma = 0.23 | ||
p = 0.4 | ||
q = 0.33 | ||
ad_error = amplitude_damping_error(gamma) | ||
r_error = reset_error(p,q) #should be approximated as-is | ||
noise_model.add_all_qubit_quantum_error(ad_error, 'iden x y s') | ||
noise_model.add_all_qubit_quantum_error(r_error, 'iden z h') | ||
|
||
result = approximate_noise_model(noise_model, operator_string="reset") | ||
|
||
expected_result = NoiseModel() | ||
gamma_p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2 | ||
gamma_q = 0 | ||
ad_error_approx = reset_error(gamma_p, gamma_q) | ||
expected_result.add_all_qubit_quantum_error(ad_error_approx, 'iden x y s') | ||
expected_result.add_all_qubit_quantum_error(r_error, 'iden z h') | ||
|
||
self.assertNoiseModelsAlmostEqual(expected_result, result) | ||
|
||
def test_clifford(self): | ||
x_p = 0.17 | ||
y_p = 0.13 | ||
z_p = 0.34 | ||
error = pauli_error([('X', x_p), ('Y', y_p), ('Z', z_p), ('I', 1 - (x_p + y_p + z_p))]) | ||
results = approximate_quantum_error(error, operator_string="clifford") | ||
self.assertErrorsAlmostEqual(error, results) | ||
|
||
def test_approx_names(self): | ||
gamma = 0.23 | ||
error = amplitude_damping_error(gamma) | ||
results_1 = approximate_quantum_error(error, operator_string="pauli") | ||
results_2 = approximate_quantum_error(error, operator_string="Pauli") | ||
self.assertErrorsAlmostEqual(results_1, results_2) | ||
|
||
def test_errors(self): | ||
gamma = 0.23 | ||
error = amplitude_damping_error(gamma) | ||
# kraus error is legit, transform_channel_operators are not | ||
with self.assertRaisesRegex(TypeError, "takes 1 positional argument but 2 were given"): | ||
approximate_quantum_error(error, 7) | ||
with self.assertRaisesRegex(RuntimeError, "No information about noise type seven"): | ||
approximate_quantum_error(error, operator_string="seven") | ||
|
||
#let's pretend cvxopt does not exist; the script should raise ImportError with proper message | ||
import unittest.mock | ||
import sys | ||
with unittest.mock.patch.dict(sys.modules, {'cvxopt': None}): | ||
with self.assertRaisesRegex(ImportError, "The CVXOPT library is required to use this module"): | ||
approximate_quantum_error(error, operator_string="reset") | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |