Skip to content

Commit

Permalink
Noise transform (#162)
Browse files Browse the repository at this point in the history
* 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
gadial authored and chriseclectic committed Apr 22, 2019
1 parent e497c6d commit 6909f6b
Show file tree
Hide file tree
Showing 7 changed files with 670 additions and 2 deletions.
9 changes: 9 additions & 0 deletions qiskit/providers/aer/noise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,17 @@
Amplitude damping error
Phase damping error
Combined phase and amplitude damping error
Noise Utilities
--------------
The `noise.utils` module contains utilities for noise models and errors including:
'approximate_quantum_error' for approximating a general quantum error via
a provided set of errors (e.g. approximating amplitude damping via reset errors)
'approximate_noise_model' for approximating all the errors in a nose model using
the same provided set of errors
"""

from .noise_model import NoiseModel
from . import errors
from . import device
from . import utils
2 changes: 1 addition & 1 deletion qiskit/providers/aer/noise/errors/errorutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,4 +622,4 @@ def kraus2instructions(kraus_ops, standard_gates, atol=ATOL_DEFAULT):
]
instructions.append(make_kraus_instruction(non_unitaries, qubits))
probabilities.append(prob_kraus)
return zip(instructions, probabilities)
return zip(instructions, probabilities)
12 changes: 12 additions & 0 deletions qiskit/providers/aer/noise/utils/__init__.py
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 qiskit/providers/aer/noise/utils/noise_transformation.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ cmake
scikit-build
cython
asv
cvxopt
2 changes: 1 addition & 1 deletion test/terra/noise/test_quantum_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,4 +712,4 @@ def test_to_channel_circuit(self):


if __name__ == '__main__':
unittest.main()
unittest.main()
191 changes: 191 additions & 0 deletions test/terra/test_noise_transformation.py
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()

0 comments on commit 6909f6b

Please sign in to comment.