qiskit/test/python/synthesis/test_stabilizer_circuit.py

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()