-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
[WIP] Adding FinalPermutation attribute to DAGCircuit and QuantumCircuit #12534
base: main
Are you sure you want to change the base?
Changes from all commits
f5e4e4b
179280a
adf022f
0feef95
84dda8e
00477ee
cc29b11
ef62bbd
0faee11
c04b224
28c4dcf
cb43e9e
af7c8c0
9e2d16f
4b959d5
71225c3
2795d5e
6350ef1
33e2bc7
23eae47
7d622d3
2b70a70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# 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. | ||
|
||
"""Reasoning about the implicit permutation of output qubits.""" | ||
|
||
from qiskit.circuit.exceptions import CircuitError | ||
|
||
|
||
class FinalPermutation: | ||
r""" | ||
Reasons about the implicit permutation of output qubits. | ||
|
||
The notation use here is the same as for the class :class:`~PermutationGate`: | ||
the permutation is stored as a list describing which qubits occupy the | ||
positions 0, 1, 2, etc. after applying the permutation. As an example, | ||
the permutation ``[2, 4, 3, 0, 1]`` means that the qubit ``2`` goes to | ||
position ``0``, qubit ``4`` goes to the position ``1``, and so on. | ||
In particular, a circuit with an implicit permutation :math:`\sigma` | ||
can be replaced by a :class:`~PermutationGate` with the same permutation | ||
pattern :math:`\sigma`. | ||
""" | ||
|
||
def __init__(self, permutation=None): | ||
"""Initializer.""" | ||
if permutation is None: | ||
permutation = [] | ||
self.permutation = permutation | ||
|
||
def add_qubit(self): | ||
"""Extends the permutation when a new qubit is added to a DAGCircuit or to | ||
a QuantumCircuit.""" | ||
self.permutation.append(len(self.permutation)) | ||
|
||
def num_qubits(self) -> int: | ||
"""Returns the length of the permutation (i.e. the total number of qubits).""" | ||
return len(self.permutation) | ||
|
||
def is_identity(self) -> bool: | ||
"""Returns whether the permutation is the identity permutation.""" | ||
return all(from_index == to_index for from_index, to_index in enumerate(self.permutation)) | ||
|
||
def compose_with_permutation(self, permutation, front) -> "FinalPermutation": | ||
"""Composes FinalPermution with a permutation.""" | ||
# pylint: disable=cyclic-import | ||
from qiskit.synthesis.permutation.permutation_utils import _compose_permutations | ||
|
||
if front: | ||
composed_permutation = _compose_permutations(self.permutation, permutation) | ||
else: | ||
composed_permutation = _compose_permutations(permutation, self.permutation) | ||
return FinalPermutation(composed_permutation) | ||
|
||
def __repr__(self): | ||
return str(self.permutation) | ||
|
||
def copy(self) -> "FinalPermutation": | ||
"""Creates a copy of the FinalPermutation object.""" | ||
return FinalPermutation(self.permutation.copy()) | ||
|
||
def push_using_mapping(self, forward_map, num_target_qubits=None) -> "FinalPermutation": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And for a better name for this function. |
||
r""" | ||
Applies a layout mapping (or more generally any mapping) to a | ||
permutation. | ||
|
||
More precisely, given a permutation :math:`\sigma: V \rightarrow V`, | ||
and a map :math:`\tau: V \rightarrow P`, returns a permutation | ||
:math:`\tilde{\sigma}: P\rightarrow P`, where | ||
:math:`\tilde{\sigma}` maps :math:`\tau(a)` to :math:`\tau(b)` | ||
whenever :math:`\sigma` maps :math:`a` to :math:`b`, and | ||
:math:`\tilde{\sigma}` is identity on the remaining elements. | ||
""" | ||
|
||
if num_target_qubits is None: | ||
num_target_qubits = len(forward_map) | ||
|
||
target_permutation = list(range(num_target_qubits)) | ||
|
||
if isinstance(forward_map, list): | ||
for inp, out in enumerate(forward_map): | ||
target_permutation[out] = forward_map[self.permutation[inp]] | ||
elif isinstance(forward_map, dict): | ||
for inp, out in forward_map.items(): | ||
target_permutation[out] = forward_map[self.permutation[inp]] | ||
else: | ||
raise CircuitError("The map should be given either as a list or as a dict.") | ||
|
||
return FinalPermutation(target_permutation) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -363,6 +363,7 @@ def from_circuit( | |
ignore_set_layout: bool = False, | ||
layout: Layout | None = None, | ||
final_layout: Layout | None = None, | ||
original_qubit_indices: dict | None = None, | ||
) -> Operator: | ||
"""Create a new Operator object from a :class:`.QuantumCircuit` | ||
|
||
|
@@ -372,7 +373,7 @@ def from_circuit( | |
you control how the :class:`.Operator` is created so it can be adjusted | ||
for a particular use case. | ||
|
||
By default this constructor method will permute the qubits based on a | ||
By default, this constructor method will permute the qubits based on a | ||
configured initial layout (i.e. after it was transpiled). It also | ||
provides an option to manually provide a :class:`.Layout` object | ||
directly. | ||
|
@@ -391,6 +392,8 @@ def from_circuit( | |
final_layout (Layout): If specified this kwarg can be used to represent the | ||
output permutation caused by swap insertions during the routing stage | ||
of the transpiler. | ||
original_qubit_indices (dict): The mapping from qubits to positional indices | ||
for the ``layout`` argument. | ||
Comment on lines
+395
to
+396
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This argument allows creating a callback function that checks the equivalence of the current operator and the original operator after each transpiler pass, something along the following:
I have found this very useful to catch problems, however this is still not fully correct: the |
||
Returns: | ||
Operator: An operator representing the input circuit | ||
""" | ||
|
@@ -401,9 +404,13 @@ def from_circuit( | |
else: | ||
from qiskit.transpiler.layout import TranspileLayout # pylint: disable=cyclic-import | ||
|
||
if original_qubit_indices is not None: | ||
input_qubit_mapping = original_qubit_indices | ||
else: | ||
input_qubit_mapping = {qubit: index for index, qubit in enumerate(circuit.qubits)} | ||
layout = TranspileLayout( | ||
initial_layout=layout, | ||
input_qubit_mapping={qubit: index for index, qubit in enumerate(circuit.qubits)}, | ||
input_qubit_mapping=input_qubit_mapping, | ||
) | ||
|
||
initial_layout = layout.initial_layout if layout is not None else None | ||
|
@@ -439,6 +446,61 @@ def from_circuit( | |
|
||
return op | ||
|
||
@classmethod | ||
def _from_circuit_new( | ||
cls, | ||
circuit: QuantumCircuit, | ||
ignore_set_layout: bool = False, | ||
layout: Layout | None = None, | ||
original_qubit_indices: dict | None = None, | ||
) -> Operator: | ||
""" | ||
Implements the same functionality as ``from_circuit`` but obtains the final | ||
permutation from the circuit's attribute ``_final_permutation`` rather than | ||
from the property set. | ||
""" | ||
if layout is None: | ||
if not ignore_set_layout: | ||
layout = getattr(circuit, "_layout", None) | ||
else: | ||
from qiskit.transpiler.layout import TranspileLayout # pylint: disable=cyclic-import | ||
|
||
if original_qubit_indices is not None: | ||
input_qubit_mapping = original_qubit_indices | ||
else: | ||
input_qubit_mapping = {qubit: index for index, qubit in enumerate(circuit.qubits)} | ||
|
||
layout = TranspileLayout( | ||
initial_layout=layout, | ||
input_qubit_mapping=input_qubit_mapping, | ||
) | ||
|
||
initial_layout = layout.initial_layout if layout is not None else None | ||
|
||
from qiskit.synthesis.permutation.permutation_utils import _inverse_pattern | ||
|
||
if initial_layout is not None: | ||
input_qubits = [None] * len(layout.input_qubit_mapping) | ||
for q, p in layout.input_qubit_mapping.items(): | ||
input_qubits[p] = q | ||
|
||
initial_permutation = initial_layout.to_permutation(input_qubits) | ||
initial_permutation_inverse = _inverse_pattern(initial_permutation) | ||
|
||
final_permutation = circuit._final_permutation.permutation | ||
|
||
op = Operator(circuit) | ||
|
||
if initial_layout: | ||
op = op.apply_permutation(initial_permutation, True) | ||
|
||
op = op.apply_permutation(final_permutation, False) | ||
|
||
if initial_layout: | ||
op = op.apply_permutation(initial_permutation_inverse, False) | ||
|
||
return op | ||
|
||
def is_unitary(self, atol=None, rtol=None): | ||
"""Return True if operator is a unitary matrix.""" | ||
if atol is None: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,3 +71,13 @@ def _decompose_cycles(cycles): | |
for i in range(m // 2): | ||
swap_list.append((cycle[i - 1], cycle[m - 2 - i])) | ||
return swap_list | ||
|
||
|
||
def _compose_permutations(*perms): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note that this file was moved to rust in #12327 |
||
"""Compose multiple permutations, with the permutations applied in the | ||
order they appear in the list. | ||
""" | ||
out = range(len(perms[0])) | ||
for perm in perms: | ||
out = [perm[i] for i in out] | ||
return out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am looking for a better name for this function.