Skip to content

Commit

Permalink
Fix a corner case of SparsePauliOp.apply_layout (#12375)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
t-imamichi and mtreinish authored May 14, 2024
1 parent fe69594 commit cea93a0
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 25 deletions.
2 changes: 2 additions & 0 deletions qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,8 @@ def apply_layout(
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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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])``
40 changes: 23 additions & 17 deletions test/python/quantum_info/operators/symplectic/test_pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
IGate,
XGate,
YGate,
ZGate,
HGate,
SGate,
SdgGate,
CXGate,
CZGate,
CYGate,
SwapGate,
CZGate,
ECRGate,
EfficientSU2,
HGate,
IGate,
SdgGate,
SGate,
SwapGate,
XGate,
YGate,
ZGate,
)
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}

Expand Down Expand Up @@ -618,6 +617,13 @@ def test_apply_layout_duplicate_indices(self):
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()
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -1191,6 +1190,22 @@ def test_apply_layout_duplicate_indices(self):
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()

0 comments on commit cea93a0

Please sign in to comment.