-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
'Peephole' optimization - or: collecting and optimizing two-qubit blo…
…cks - before routing (#12727) (#12881) * init * up * up * Update builtin_plugins.py * Update builtin_plugins.py * reno * Update builtin_plugins.py * Update builtin_plugins.py * Update peephole-before-routing-c3d184b740bb7a8b.yaml * neko check * check neko * Update builtin_plugins.py * test neko * Update builtin_plugins.py * Update builtin_plugins.py * Update builtin_plugins.py * lint * tests and format * remove FakeTorino test * Update peephole-before-routing-c3d184b740bb7a8b.yaml * Apply suggestions from code review Co-authored-by: Matthew Treinish <mtreinish@kortar.org> * comments from code review * fix precision * up * up * update * up * . * cyclic import * cycl import * cyl import * . * circular import * . * lint * Include new pass in docs * Fix Split2QUnitaries dag manipulation This commit fixes the dag handling to do the 1q unitary insertion. Previously the dag manipulation was being done manually using the insert_node_on_in_edges() rustworkx method. However as the original node had 2 incoming edges for each qubit this caused the dag after running the pass to become corrupted. Each of the new 1q unitary nodes would end up with 2 incident edges and they would be in a sequence. This would result in later passes not being able to correctly understand the state of the circuit correctly. This was causing the unit tests to fail. This commit fixes this by just using `substitute_node_with_dag()` to handle the node substition, while doing it manually to avoid the overhead of checking is probably possible, the case where a unitary is the product of two 1q gates is not very common so optimizing it isn't super critical. * Update releasenotes/notes/peephole-before-routing-c3d184b740bb7a8b.yaml * stricter check for doing split2q * Update qiskit/transpiler/preset_passmanagers/builtin_plugins.py Co-authored-by: Matthew Treinish <mtreinish@kortar.org> * code review * Update qiskit/transpiler/passes/optimization/split_2q_unitaries.py Co-authored-by: Matthew Treinish <mtreinish@kortar.org> * new tests * typo * lint * lint --------- Co-authored-by: Matthew Treinish <mtreinish@kortar.org> (cherry picked from commit 1214d51) Co-authored-by: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com>
- Loading branch information
1 parent
9120b8d
commit f4cb741
Showing
8 changed files
with
436 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
qiskit/transpiler/passes/optimization/split_2q_unitaries.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2017, 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. | ||
"""Splits each two-qubit gate in the `dag` into two single-qubit gates, if possible without error.""" | ||
from typing import Optional | ||
|
||
from qiskit.transpiler.basepasses import TransformationPass | ||
from qiskit.circuit.quantumcircuitdata import CircuitInstruction | ||
from qiskit.dagcircuit.dagcircuit import DAGCircuit, DAGOpNode | ||
from qiskit.circuit.library.generalized_gates import UnitaryGate | ||
from qiskit.synthesis.two_qubit.two_qubit_decompose import TwoQubitWeylDecomposition | ||
|
||
|
||
class Split2QUnitaries(TransformationPass): | ||
"""Attempt to splits two-qubit gates in a :class:`.DAGCircuit` into two single-qubit gates | ||
This pass will analyze all the two qubit gates in the circuit and analyze the gate's unitary | ||
matrix to determine if the gate is actually a product of 2 single qubit gates. In these | ||
cases the 2q gate can be simplified into two single qubit gates and this pass will | ||
perform this optimization and will replace the two qubit gate with two single qubit | ||
:class:`.UnitaryGate`. | ||
""" | ||
|
||
def __init__(self, fidelity: Optional[float] = 1.0 - 1e-16): | ||
"""Split2QUnitaries initializer. | ||
Args: | ||
fidelity (float): Allowed tolerance for splitting two-qubit unitaries and gate decompositions | ||
""" | ||
super().__init__() | ||
self.requested_fidelity = fidelity | ||
|
||
def run(self, dag: DAGCircuit): | ||
"""Run the Split2QUnitaries pass on `dag`.""" | ||
for node in dag.topological_op_nodes(): | ||
# skip operations without two-qubits and for which we can not determine a potential 1q split | ||
if ( | ||
len(node.cargs) > 0 | ||
or len(node.qargs) != 2 | ||
or node.matrix is None | ||
or node.is_parameterized() | ||
): | ||
continue | ||
|
||
decomp = TwoQubitWeylDecomposition(node.op, fidelity=self.requested_fidelity) | ||
if ( | ||
decomp._inner_decomposition.specialization | ||
== TwoQubitWeylDecomposition._specializations.IdEquiv | ||
): | ||
new_dag = DAGCircuit() | ||
new_dag.add_qubits(node.qargs) | ||
|
||
ur = decomp.K1r | ||
ur_node = DAGOpNode.from_instruction( | ||
CircuitInstruction(UnitaryGate(ur), qubits=(node.qargs[0],)), dag=new_dag | ||
) | ||
|
||
ul = decomp.K1l | ||
ul_node = DAGOpNode.from_instruction( | ||
CircuitInstruction(UnitaryGate(ul), qubits=(node.qargs[1],)), dag=new_dag | ||
) | ||
new_dag._apply_op_node_back(ur_node) | ||
new_dag._apply_op_node_back(ul_node) | ||
new_dag.global_phase = decomp.global_phase | ||
dag.substitute_node_with_dag(node, new_dag) | ||
elif ( | ||
decomp._inner_decomposition.specialization | ||
== TwoQubitWeylDecomposition._specializations.SWAPEquiv | ||
): | ||
# TODO maybe also look into swap-gate-like gates? Things to consider: | ||
# * As the qubit mapping may change, we'll always need to build a new dag in this pass | ||
# * There may not be many swap-gate-like gates in an arbitrary input circuit | ||
# * Removing swap gates from a user-routed input circuit here is unexpected | ||
pass | ||
return dag |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
releasenotes/notes/peephole-before-routing-c3d184b740bb7a8b.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
--- | ||
features_transpiler: | ||
- | | ||
Added a new pass :class:`.Split2QUnitaries` that iterates over all two-qubit gates or unitaries in a | ||
circuit and replaces them with two single-qubit unitaries, if possible without introducing errors, i.e. | ||
the two-qubit gate/unitary is actually a (kronecker) product of single-qubit unitaries. | ||
- | | ||
The passes :class:`.Collect2qBlocks`, :class:`.ConsolidateBlocks` and :class:`.Split2QUnitaries` have been | ||
added to the ``init`` stage of the preset pass managers with optimization level 2 and optimization level 3. | ||
The modification of the `init` stage should allow for a more efficient routing for quantum circuits that either: | ||
* contain two-qubit unitaries/gates that are actually a product of single-qubit gates | ||
* contain multiple two-qubit gates in a continuous block of two-qubit gates. | ||
In the former case, the routing of the two-qubit gate can simply be skipped as no real interaction | ||
between a pair of qubits occurs. In the latter case, the lookahead space of routing algorithms is not | ||
'polluted' by superfluous two-qubit gates, i.e. for routing it is sufficient to only consider one single | ||
two-qubit gate per continuous block of two-qubit gates. These passes are not run if the pass | ||
managers target a :class:`.Target` that has a discrete basis gate set, i.e. all basis gates have are not | ||
parameterized. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.