forked from Qiskit/qiskit-aer
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
563f88a
commit 47f14c7
Showing
2 changed files
with
313 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2018-2024. | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
""" | ||
PauliError class tests | ||
""" | ||
from test.terra.common import QiskitAerTestCase | ||
|
||
import unittest | ||
from itertools import combinations | ||
|
||
import ddt | ||
import numpy as np | ||
|
||
import qiskit.quantum_info as qi | ||
from qiskit_aer.noise import PauliError, QuantumError | ||
from qiskit_aer.noise.noiseerror import NoiseError | ||
|
||
|
||
@ddt.ddt | ||
class TestPauliError(QiskitAerTestCase): | ||
"""Testing QuantumError class""" | ||
|
||
@ddt.data(str, qi.Pauli, qi.PauliList) | ||
def test_init_with_paulis(self, cls): | ||
"""Test construction with different inputs.""" | ||
paulis = ["I", "X"] | ||
if cls == qi.Pauli: | ||
paulis = [qi.Pauli(i) for i in paulis] | ||
elif cls == qi.PauliList: | ||
paulis = qi.PauliList(paulis) | ||
perr = PauliError(paulis, np.ones(len(paulis)) / len(paulis)) | ||
self.assertEqual(perr.size, 2) | ||
|
||
def test_invalid_inputs(self): | ||
"""Test inputs with different lengths raises""" | ||
with self.assertRaises(NoiseError): | ||
PauliError(["X", "I"], [1]) | ||
with self.assertRaises(NoiseError): | ||
PauliError(["X", "I"], np.eye(2)) | ||
with self.assertRaises(NoiseError): | ||
PauliError(["X"], 1) | ||
|
||
def test_quasi_dist_probabilities(self): | ||
"""Test initalizing with qasui-dist probabilities.""" | ||
perr = PauliError(["I", "X"], [1.1, -0.1]) | ||
self.assertTrue(perr.is_tp()) | ||
self.assertFalse(perr.is_cp()) | ||
self.assertFalse(perr.is_cptp()) | ||
with self.assertRaises(NoiseError): | ||
perr.to_quantum_error() | ||
|
||
def test_non_normalized_probabilities(self): | ||
"""Test initalizing with qasui-dist probabilities.""" | ||
perr = PauliError(["I", "X"], [1.1, 0.1]) | ||
self.assertFalse(perr.is_tp()) | ||
self.assertTrue(perr.is_cp()) | ||
self.assertFalse(perr.is_cptp()) | ||
with self.assertRaises(NoiseError): | ||
perr.to_quantum_error() | ||
|
||
def test_attributes(self): | ||
"""Test basic attributes""" | ||
rng = np.random.default_rng(555) | ||
paulis = qi.random_pauli_list(3, 8, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
perr = PauliError(paulis, probs) | ||
self.assertEqual(perr.size, len(paulis)) | ||
self.assertEqual(perr.paulis, paulis) | ||
np.testing.assert_allclose(perr.probabilities, probs) | ||
|
||
@ddt.data(1, 10, 100, 1000) | ||
def test_ideal_single_iden(self, num_qubits): | ||
"""Test ideal gates are identified correctly.""" | ||
self.assertTrue(PauliError([num_qubits * "I"], [1.0]).ideal()) | ||
self.assertFalse(PauliError([num_qubits * "I"], [0.9]).ideal()) # non-TP | ||
self.assertFalse(PauliError([num_qubits * "I"], [-1]).ideal()) # non-CP | ||
|
||
@ddt.data(1, 10, 100, 1000) | ||
def test_ideal_single_zero_probs(self, num_qubits): | ||
"""Test ideal gates are identified correctly.""" | ||
paulis = [num_qubits * s for s in ["I", "X", "Y", "Z"]] | ||
probs = [1, 0, 0, 0] | ||
self.assertTrue(PauliError(paulis, probs).ideal()) | ||
|
||
@ddt.data(1, 10, 100, 1000) | ||
def test_ideal_single_multi_iden(self, num_qubits): | ||
"""Test ideal gates are identified correctly.""" | ||
self.assertTrue(PauliError(4 * [num_qubits * "I"], 4 * [0.25]).ideal()) | ||
|
||
def test_to_quantumchannel(self): | ||
"""Test conversion into quantum channel.""" | ||
rng = np.random.default_rng(1234) | ||
paulis = qi.random_pauli_list(3, 8, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
target = sum((prob * qi.SuperOp(op) for op, prob in zip(paulis, probs))) | ||
perr = PauliError(paulis, probs) | ||
self.assertEqual(perr.to_quantumchannel(), target) | ||
|
||
def test_to_quantum_error(self): | ||
"""Test conversion into quantum error.""" | ||
rng = np.random.default_rng(4444) | ||
paulis = qi.random_pauli_list(4, 5, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
target = QuantumError(zip(paulis, probs)) | ||
perr = PauliError(paulis, probs) | ||
self.assertEqual(perr.to_quantum_error(), target) | ||
|
||
def test_dict_round_trip(self): | ||
"""Test to_dict and from_dict round trip.""" | ||
rng = np.random.default_rng(55) | ||
paulis = qi.random_pauli_list(5, 6, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
target = PauliError(paulis, probs) | ||
value = PauliError.from_dict(target.to_dict()) | ||
self.assertDictAlmostEqual(value, target) | ||
|
||
def test_quantum_error_dict_from_dict_equiv(self): | ||
"""Test PauliError.to_dict -> QuantumError.from_dict.""" | ||
rng = np.random.default_rng(33) | ||
paulis = qi.random_pauli_list(4, 6, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
target = QuantumError(zip(paulis, probs)) | ||
perr = PauliError(paulis, probs) | ||
value = QuantumError.from_dict(perr.to_dict()) | ||
self.assertEqual(value, target) | ||
|
||
def test_quantum_error_dict_to_dict_equiv(self): | ||
"""Test QuantumError.to_dict -> PauliError.from_dict.""" | ||
rng = np.random.default_rng(33) | ||
paulis = qi.random_pauli_list(4, 6, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
target = PauliError(paulis, probs) | ||
qerr = QuantumError(zip(paulis, probs)) | ||
value = PauliError.from_dict(qerr.to_dict()) | ||
self.assertEqual(value, target) | ||
|
||
def test_simplify_zero(self): | ||
"""Test simplify removes zeros""" | ||
paulis = ["XI", "IX", "YI", "IY"] | ||
p1 = 1e-1 | ||
p2 = 1e-5 | ||
p3 = 1e-9 | ||
probs = [1 - p1 - p2 - p3, p1, p2, p3] | ||
perr = PauliError(paulis, probs) | ||
|
||
value1 = perr.simplify() | ||
target1 = PauliError(paulis[:3], probs[:3]) | ||
self.assertEqual(value1, target1) | ||
|
||
value2 = perr.simplify(atol=1e-4) | ||
target2 = PauliError(paulis[:2], probs[:2]) | ||
self.assertEqual(value2, target2) | ||
|
||
value3 = perr.simplify(atol=0) | ||
target3 = perr | ||
self.assertEqual(value3, target3) | ||
|
||
def test_simplify_duplicates(self): | ||
"""Test simplify combines duplicate terms""" | ||
paulis = ["XX", "ZZ"] | ||
probs = [0.8, 0.2] | ||
target = PauliError(paulis, probs) | ||
value = PauliError(4 * paulis, np.array(4 * probs) / 4).simplify() | ||
self.assertEqual(value, target) | ||
|
||
def test_equal_diff_order(self): | ||
rng = np.random.default_rng(33) | ||
paulis = qi.random_pauli_list(5, 10, phase=False, seed=rng) | ||
probs = rng.random(len(paulis)) | ||
probs /= sum(probs) | ||
perr1 = PauliError(paulis, probs) | ||
perr2 = PauliError(paulis[::-1], probs[::-1]) | ||
self.assertEqual(perr1, perr2) | ||
|
||
def test_not_equal_type(self): | ||
perr = PauliError(["II", "XX"], [0.9, 0.1]) | ||
qerr = perr.to_quantum_error() | ||
chan = perr.to_quantumchannel() | ||
self.assertNotEqual(perr, qerr) | ||
self.assertNotEqual(perr, chan) | ||
|
||
def test_not_equal_shape(self): | ||
perr1 = PauliError(["II", "XX"], [0.9, 0.1]) | ||
perr2 = PauliError(["II", "XX", "ZZ"], [0.9, 0.05, 0.05]) | ||
self.assertNotEqual(perr1, perr2) | ||
|
||
@ddt.data(1, 2, 3, 4) | ||
def test_compose(self, qarg_qubits): | ||
"""Test compose two quantum errors.""" | ||
rng = np.random.default_rng(33) | ||
paulis1 = qi.random_pauli_list(4, 3, phase=False, seed=rng) | ||
probs1 = rng.random(len(paulis1)) | ||
probs1 /= sum(probs1) | ||
perr1 = PauliError(paulis1, probs1) | ||
paulis2 = qi.random_pauli_list(qarg_qubits, 3, phase=False, seed=rng) | ||
probs2 = rng.random(len(paulis2)) | ||
probs2 /= sum(probs2) | ||
perr2 = PauliError(paulis2, probs2) | ||
target = perr1.to_quantumchannel().compose(perr2.to_quantumchannel(), range(qarg_qubits)) | ||
value = (perr1.compose(perr2, range(qarg_qubits))).to_quantumchannel() | ||
self.assertEqual(value, target) | ||
|
||
@ddt.idata(list(combinations(range(4), 1)) + list(combinations(range(4), 2))) | ||
def test_compose_subsystem(self, qargs): | ||
"""Test compose with 1 and 2-qubit subsystem permutations""" | ||
rng = np.random.default_rng(123) | ||
paulis1 = qi.random_pauli_list(4, 3, phase=False, seed=rng) | ||
probs1 = rng.random(len(paulis1)) | ||
probs1 /= sum(probs1) | ||
perr1 = PauliError(paulis1, probs1) | ||
paulis2 = qi.random_pauli_list(len(qargs), 3, phase=False, seed=rng) | ||
probs2 = rng.random(len(paulis2)) | ||
probs2 /= sum(probs2) | ||
perr2 = PauliError(paulis2, probs2) | ||
target = perr1.to_quantumchannel().compose(perr2.to_quantumchannel(), qargs) | ||
value = (perr1.compose(perr2, qargs)).to_quantumchannel() | ||
self.assertEqual(value, target) | ||
|
||
@ddt.data(1, 10, 100) | ||
def test_dot_equals_compose(self, num_qubits): | ||
"""Test dot is equal to compose.""" | ||
rng = np.random.default_rng(999) | ||
|
||
for _ in range(20): | ||
paulis1 = qi.random_pauli_list(num_qubits, 3, phase=False, seed=rng) | ||
coeffs1 = rng.random(len(paulis1)) | ||
perr1 = PauliError(paulis1, coeffs1) | ||
paulis2 = qi.random_pauli_list(num_qubits, 5, phase=False, seed=rng) | ||
coeffs2 = rng.random(len(paulis2)) | ||
perr2 = PauliError(paulis2, coeffs2) | ||
self.assertEqual(perr1.dot(perr2), perr1.compose(perr2)) | ||
|
||
def test_tensor(self): | ||
"""Test tensor two quantum errors.""" | ||
rng = np.random.default_rng(99) | ||
paulis1 = qi.random_pauli_list(2, 4, phase=False, seed=rng) | ||
probs1 = rng.random(len(paulis1)) | ||
probs1 /= sum(probs1) | ||
perr1 = PauliError(paulis1, probs1) | ||
paulis2 = qi.random_pauli_list(2, 3, phase=False, seed=rng) | ||
probs2 = rng.random(len(paulis2)) | ||
probs2 /= sum(probs2) | ||
perr2 = PauliError(paulis2, probs2) | ||
value = perr1.tensor(perr2).to_quantumchannel() | ||
target = perr1.to_quantumchannel().tensor(perr2.to_quantumchannel()) | ||
self.assertEqual(value, target) | ||
|
||
def test_expand(self): | ||
"""Test tensor two quantum errors.""" | ||
rng = np.random.default_rng(99) | ||
paulis1 = qi.random_pauli_list(2, 4, phase=False, seed=rng) | ||
probs1 = rng.random(len(paulis1)) | ||
probs1 /= sum(probs1) | ||
perr1 = PauliError(paulis1, probs1) | ||
paulis2 = qi.random_pauli_list(2, 3, phase=False, seed=rng) | ||
probs2 = rng.random(len(paulis2)) | ||
probs2 /= sum(probs2) | ||
perr2 = PauliError(paulis2, probs2) | ||
value = perr1.expand(perr2).to_quantumchannel() | ||
target = perr1.to_quantumchannel().expand(perr2.to_quantumchannel()) | ||
self.assertEqual(value, target) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |