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

Fixed Issue 11026: SparsePauliOp.apply_layout should do nothing if given None #11041

Merged
merged 7 commits into from
Nov 16, 2023
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
16 changes: 12 additions & 4 deletions qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -1109,24 +1109,30 @@ def assign_parameters(
return None if inplace else bound

def apply_layout(
self, layout: TranspileLayout | List[int], num_qubits: int | None = None
self, layout: TranspileLayout | List[int] | None, num_qubits: int | None = None
) -> SparsePauliOp:
"""Apply a transpiler layout to this :class:`~.SparsePauliOp`

Args:
layout: Either a :class:`~.TranspileLayout` or a list of integers.
layout: Either a :class:`~.TranspileLayout`, a list of integers or None.
If both layout and num_qubits are none, a copy of the operator is
returned.
num_qubits: The number of qubits to expand the operator to. If not
provided then if ``layout`` is a :class:`~.TranspileLayout` the
number of the transpiler output circuit qubits will be used by
default. If ``layout`` is a list of integers the permutation
specified will be applied without any expansion.
specified will be applied without any expansion. If layout is
None, the operator will be expanded to the given number of qubits.


Returns:
A new :class:`.SparsePauliOp` with the provided layout applied
"""
from qiskit.transpiler.layout import TranspileLayout

if layout is None and num_qubits is None:
return self.copy()

n_qubits = self.num_qubits
if isinstance(layout, TranspileLayout):
n_qubits = len(layout._output_qubit_list)
Expand All @@ -1138,8 +1144,10 @@ def apply_layout(
f"applied to a {n_qubits} qubit operator"
)
n_qubits = num_qubits
if any(x >= n_qubits for x in layout):
if layout is not None and any(x >= n_qubits for x in layout):
raise QiskitError("Provided layout contains indicies outside the number of qubits.")
if layout is None:
layout = list(range(self.num_qubits))
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,6 @@
---
features:
- |
The function :meth:`~.SparsePauliOp.apply_layout` from :class:`.SparsePauliOp` now allows for the
layout argument to also be None. That is, the method can now also be used for circuits where no transpilation/routing
took place (for example when transpiling for a simulator).
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,25 @@ def test_apply_layout_layout_list_and_num_qubits(self):
res = op.apply_layout([4, 0], 5)
self.assertEqual(SparsePauliOp.from_list([("IIIIY", 2), ("IIIIX", 1)]), res)

def test_apply_layout_null_layout_no_num_qubits(self):
"""Test apply_layout with a null layout"""
op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
res = op.apply_layout(layout=None)
self.assertEqual(op, res)

def test_apply_layout_null_layout_and_num_qubits(self):
"""Test apply_layout with a null layout a num_qubits provided"""
op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
res = op.apply_layout(layout=None, num_qubits=5)
# this should expand the operator
self.assertEqual(SparsePauliOp.from_list([("IIIII", 1), ("IIIIZ", 2), ("IIIXI", 3)]), res)

def test_apply_layout_null_layout_invalid_num_qubits(self):
"""Test apply_layout with a null layout and num_qubits smaller than capable"""
op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
with self.assertRaises(QiskitError):
op.apply_layout(layout=None, num_qubits=1)


if __name__ == "__main__":
unittest.main()