mirror of https://github.com/Qiskit/qiskit.git
184 lines
7.5 KiB
Python
184 lines
7.5 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 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.
|
|
"""Tests for synth_circuit_from_stabilizers function."""
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from collections.abc import Collection
|
|
from ddt import ddt
|
|
import numpy as np
|
|
|
|
from qiskit.synthesis import synth_circuit_from_stabilizers
|
|
from qiskit.exceptions import QiskitError
|
|
from qiskit.quantum_info import Pauli, StabilizerState, random_clifford
|
|
from qiskit.quantum_info.operators import Clifford
|
|
from test import combine # pylint: disable=wrong-import-order
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
@ddt
|
|
class TestStabilizerCircuits(QiskitTestCase):
|
|
"""Tests for synth_circuit_from_stabilizers function."""
|
|
|
|
def verify_stabilizers(self, stabilizers: Collection[str], **kwargs) -> None:
|
|
"""
|
|
Verify that the circuit generated from stabilizers is correct.
|
|
|
|
Args:
|
|
stabilizers (Collection[str]): list of stabilizer strings
|
|
kwargs: keyword arguments for synth_circuit_from_stabilizer_list
|
|
"""
|
|
circuit = synth_circuit_from_stabilizers(stabilizers, **kwargs)
|
|
clifford = Clifford(circuit)
|
|
state = StabilizerState(circuit)
|
|
for stabilizer in stabilizers:
|
|
composed = clifford.compose(Pauli(stabilizer).to_instruction())
|
|
composed_state = StabilizerState(composed)
|
|
# Test that the stabilizer is a stabilizer of the state by applying it to state
|
|
self.assertTrue(state.equiv(composed_state))
|
|
|
|
def test_stabilizer_to_circuit_simple(self):
|
|
"""Simple test case"""
|
|
stabilizers = {"+ZXX", "+XYX", "+ZYY"}
|
|
self.verify_stabilizers(stabilizers)
|
|
|
|
def test_stabilizer_to_circuit_with_sign(self):
|
|
"""Simple test case with signs stabilizer"""
|
|
stabilizers = {"ZXX", "-XYX", "+ZYY"}
|
|
self.verify_stabilizers(stabilizers)
|
|
|
|
def test_stabilizer_to_circuit_larger(self):
|
|
"""Larger test case"""
|
|
stabilizer_list = [
|
|
"YXIZZIXY",
|
|
"ZIXYYXIZ",
|
|
"IZYXXYZI",
|
|
"XIIZIIXZ",
|
|
"IIXZXIIZ",
|
|
"IZXIXZII",
|
|
"YYYYIIII",
|
|
"XXIIXXII",
|
|
]
|
|
self.verify_stabilizers(stabilizer_list)
|
|
|
|
def test_stabilizer_to_circuit_underconstrained(self):
|
|
"""Underconstrained test case"""
|
|
stabilizer_list = ["ZXX", "ZYY"]
|
|
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
"Stabilizers are underconstrained and allow_underconstrained is False. Add "
|
|
"allow_underconstrained=True to the function call if you want to allow "
|
|
"underconstrained stabilizers.",
|
|
cm.exception.message,
|
|
)
|
|
self.verify_stabilizers(stabilizer_list, allow_underconstrained=True)
|
|
|
|
def test_stabilizer_to_circuit_redundant(self):
|
|
"""Redundant test case"""
|
|
stabilizer_list = ["ZZXX", "XXXX", "XXZZ", "ZZZZ"]
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
f"Stabilizer 3 ({stabilizer_list[3]}) is a product of the others "
|
|
"and allow_redundant is False. Add allow_redundant=True "
|
|
"to the function call if you want to allow redundant stabilizers.",
|
|
cm.exception.message,
|
|
)
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list, allow_redundant=True)
|
|
self.assertEqual(
|
|
"Stabilizers are underconstrained and allow_underconstrained is False. Add "
|
|
"allow_underconstrained=True to the function call if you want to allow "
|
|
"underconstrained stabilizers.",
|
|
cm.exception.message,
|
|
)
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list, allow_underconstrained=True)
|
|
self.assertEqual(
|
|
f"Stabilizer 3 ({stabilizer_list[3]}) is a product of the others "
|
|
"and allow_redundant is False. Add allow_redundant=True "
|
|
"to the function call if you want to allow redundant stabilizers.",
|
|
cm.exception.message,
|
|
)
|
|
self.verify_stabilizers(stabilizer_list, allow_redundant=True, allow_underconstrained=True)
|
|
|
|
def test_stabilizer_to_circuit_redundant_swap(self):
|
|
"""Redundant test case that requires to swap qubits during Gaussian elimination
|
|
(i.e., some pivot is off-diagonal)"""
|
|
stabilizer_list = ["ZZXX", "ZZZZ", "XXXX", "YYYY", "XZXZ"]
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
f"Stabilizer 3 ({stabilizer_list[3]}) is a product of the others "
|
|
"and allow_redundant is False. Add allow_redundant=True "
|
|
"to the function call if you want to allow redundant stabilizers.",
|
|
cm.exception.message,
|
|
)
|
|
self.verify_stabilizers(stabilizer_list, allow_redundant=True)
|
|
|
|
def test_stabilizer_to_circuit_non_commuting(self):
|
|
"""Non-commuting stabilizers"""
|
|
stabilizer_list = ["ZXX", "XYX", "YYY"]
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
"Some stabilizers do not commute.",
|
|
cm.exception.message,
|
|
)
|
|
|
|
def test_stabilizer_to_circuit_contradicting(self):
|
|
"""Contradicting stabilizers"""
|
|
stabilizer_list = ["ZXX", "-ZXX"]
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
f"Stabilizer 1 ({stabilizer_list[1]}) contradicts some of the previous stabilizers.",
|
|
cm.exception.message,
|
|
)
|
|
|
|
def test_invalid_stabilizers(self):
|
|
"""Invalid stabilizers"""
|
|
stabilizer_list = ["iZXX", "-ZXX"]
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
"Some stabilizers have an invalid phase",
|
|
cm.exception.message,
|
|
)
|
|
stabilizer_list = ["-ZIX", "iZXQ"]
|
|
with self.assertRaises(QiskitError) as cm:
|
|
self.verify_stabilizers(stabilizer_list)
|
|
self.assertEqual(
|
|
f'Pauli string label "{stabilizer_list[1]}" is not valid.',
|
|
cm.exception.message,
|
|
)
|
|
|
|
@combine(num_qubits=[4, 5, 6, 10, 15])
|
|
def test_regenerate_clifford(self, num_qubits):
|
|
"""Create a circuit from Clifford-generated list of stabilizers and verify that the
|
|
circuit output is equivalent to the original state."""
|
|
rng = np.random.default_rng(1234)
|
|
samples = 10
|
|
for _ in range(samples):
|
|
clifford = random_clifford(num_qubits, seed=rng)
|
|
state = StabilizerState(clifford)
|
|
|
|
stabilizer_list = clifford.to_labels(mode="S")
|
|
state_syn = StabilizerState.from_stabilizer_list(stabilizer_list)
|
|
self.assertTrue(state.equiv(StabilizerState(state_syn)))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|