Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
Loading