Fix a corner case of `SparsePauliOp.apply_layout` (#12375)

* fix a corner case of `SparsePauliOp.apply_layout`

* Add zero-qubit tests of Pauli.apply_layout

* use combine and apply isort

* Update releasenotes/notes/fix-sparse-pauli-op-apply-layout-zero-43b9e70f0d1536a6.yaml

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
This commit is contained in:
Takashi Imamichi 2024-05-14 20:09:01 +04:00 committed by GitHub
parent fe69594699
commit cea93a0516
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 58 additions and 25 deletions

View File

@ -1165,6 +1165,8 @@ class SparsePauliOp(LinearOp):
raise QiskitError("Provided layout contains indices outside the number of qubits.")
if len(set(layout)) != len(layout):
raise QiskitError("Provided layout contains duplicate indices.")
if self.num_qubits == 0:
return type(self)(["I" * n_qubits] * self.size, self.coeffs)
new_op = type(self)("I" * n_qubits)
return new_op.compose(self, qargs=layout)

View File

@ -0,0 +1,10 @@
fixes:
- |
Fixed :meth:`.SparsePauliOp.apply_layout` to work correctly with zero-qubit operators.
For example, if you previously created a 0 qubit and applied a layout like::
op = SparsePauliOp("")
op.apply_layout(None, 3)
this would have previously raised an error. Now this will correctly return an operator of the form:
``SparsePauliOp(['III'], coeffs=[1.+0.j])``

View File

@ -14,42 +14,41 @@
"""Tests for Pauli operator class."""
import itertools as it
import re
import unittest
import itertools as it
from functools import lru_cache
from test import QiskitTestCase, combine
import numpy as np
from ddt import ddt, data, unpack
from ddt import data, ddt, unpack
from qiskit import QuantumCircuit
from qiskit.circuit import Qubit
from qiskit.exceptions import QiskitError
from qiskit.circuit.library import (
CXGate,
CYGate,
CZGate,
ECRGate,
EfficientSU2,
HGate,
IGate,
SdgGate,
SGate,
SwapGate,
XGate,
YGate,
ZGate,
HGate,
SGate,
SdgGate,
CXGate,
CZGate,
CYGate,
SwapGate,
ECRGate,
EfficientSU2,
)
from qiskit.circuit.library.generalized_gates import PauliGate
from qiskit.compiler.transpiler import transpile
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.exceptions import QiskitError
from qiskit.primitives import BackendEstimator
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.quantum_info.operators import Operator, Pauli, SparsePauliOp
from qiskit.quantum_info.random import random_clifford, random_pauli
from qiskit.quantum_info.operators import Pauli, Operator, SparsePauliOp
from qiskit.utils import optionals
from test import QiskitTestCase # pylint: disable=wrong-import-order
LABEL_REGEX = re.compile(r"(?P<coeff>[+-]?1?[ij]?)(?P<pauli>[IXYZ]*)")
PHASE_MAP = {"": 0, "-i": 1, "-": 2, "i": 3}
@ -618,6 +617,13 @@ class TestPauli(QiskitTestCase):
with self.assertRaises(QiskitError):
op.apply_layout(layout=[0, 0], num_qubits=3)
@combine(phase=["", "-i", "-", "i"], layout=[None, []])
def test_apply_layout_zero_qubit(self, phase, layout):
"""Test apply_layout with a zero-qubit operator"""
op = Pauli(phase)
res = op.apply_layout(layout=layout, num_qubits=5)
self.assertEqual(Pauli(phase + "IIIII"), res)
if __name__ == "__main__":
unittest.main()

View File

@ -14,23 +14,22 @@
import itertools as it
import unittest
from test import QiskitTestCase, combine
import numpy as np
import scipy.sparse
import rustworkx as rx
import scipy.sparse
from ddt import ddt
from qiskit import QiskitError
from qiskit.circuit import ParameterExpression, Parameter, ParameterVector
from qiskit.circuit.parametertable import ParameterView
from qiskit.quantum_info.operators import Operator, Pauli, PauliList, SparsePauliOp
from qiskit.circuit import Parameter, ParameterExpression, ParameterVector
from qiskit.circuit.library import EfficientSU2
from qiskit.circuit.parametertable import ParameterView
from qiskit.compiler.transpiler import transpile
from qiskit.primitives import BackendEstimator
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.compiler.transpiler import transpile
from qiskit.quantum_info.operators import Operator, Pauli, PauliList, SparsePauliOp
from qiskit.utils import optionals
from test import QiskitTestCase # pylint: disable=wrong-import-order
from test import combine # pylint: disable=wrong-import-order
def pauli_mat(label):
@ -1191,6 +1190,22 @@ class TestSparsePauliOpMethods(QiskitTestCase):
with self.assertRaises(QiskitError):
op.apply_layout(layout=[0, 0], num_qubits=3)
@combine(layout=[None, []])
def test_apply_layout_zero_qubit(self, layout):
"""Test apply_layout with a zero-qubit operator"""
with self.subTest("default"):
op = SparsePauliOp("")
res = op.apply_layout(layout=layout, num_qubits=5)
self.assertEqual(SparsePauliOp("IIIII"), res)
with self.subTest("coeff"):
op = SparsePauliOp("", 2)
res = op.apply_layout(layout=layout, num_qubits=5)
self.assertEqual(SparsePauliOp("IIIII", 2), res)
with self.subTest("multiple ops"):
op = SparsePauliOp.from_list([("", 1), ("", 2)])
res = op.apply_layout(layout=layout, num_qubits=5)
self.assertEqual(SparsePauliOp.from_list([("IIIII", 1), ("IIIII", 2)]), res)
if __name__ == "__main__":
unittest.main()