mirror of https://github.com/Qiskit/qiskit.git
Optimize `ConsolidateBlocks` pass (#10365)
* Initial: Introducing speedup by calculating matrix * Lint: Reformat using Black * Fix: Added `basis_count` and `outside_basis` * Fix: Use `Operator` if `to_matrix` is unavailable. * Fix: Default to Operator when necessary - Will default to previous method when blocks have more than 2 qubits. - This is a temporary measure. * Lint: Removed Cyclic import * Docs: Added release note. * Docs: Added reference to the issue. * Fix: Move `block_to_matrix` to ~.passes.utils * Lint: Fixed cyclical import and order * CI: Removed type checking * Add: Exceptions on `_block_to_matrix` * Docs: Fix release note. * Fix: Change import path for block_to_matrix * Update qiskit/transpiler/passes/utils/block_to_matrix.py --------- Co-authored-by: ewinston <ewinston@us.ibm.com>
This commit is contained in:
parent
ac29776243
commit
2b225d3713
|
@ -26,6 +26,7 @@ from qiskit.transpiler.basepasses import TransformationPass
|
||||||
from qiskit.circuit.controlflow import ControlFlowOp
|
from qiskit.circuit.controlflow import ControlFlowOp
|
||||||
from qiskit.transpiler.passmanager import PassManager
|
from qiskit.transpiler.passmanager import PassManager
|
||||||
from qiskit.transpiler.passes.synthesis import unitary_synthesis
|
from qiskit.transpiler.passes.synthesis import unitary_synthesis
|
||||||
|
from qiskit.transpiler.passes.utils import _block_to_matrix
|
||||||
from .collect_1q_runs import Collect1qRuns
|
from .collect_1q_runs import Collect1qRuns
|
||||||
from .collect_2q_blocks import Collect2qBlocks
|
from .collect_2q_blocks import Collect2qBlocks
|
||||||
|
|
||||||
|
@ -110,19 +111,24 @@ class ConsolidateBlocks(TransformationPass):
|
||||||
if isinstance(nd, DAGOpNode) and getattr(nd.op, "condition", None):
|
if isinstance(nd, DAGOpNode) and getattr(nd.op, "condition", None):
|
||||||
block_cargs |= set(getattr(nd.op, "condition", None)[0])
|
block_cargs |= set(getattr(nd.op, "condition", None)[0])
|
||||||
all_block_gates.add(nd)
|
all_block_gates.add(nd)
|
||||||
q = QuantumRegister(len(block_qargs))
|
|
||||||
qc = QuantumCircuit(q)
|
|
||||||
if block_cargs:
|
|
||||||
c = ClassicalRegister(len(block_cargs))
|
|
||||||
qc.add_register(c)
|
|
||||||
block_index_map = self._block_qargs_to_indices(block_qargs, global_index_map)
|
block_index_map = self._block_qargs_to_indices(block_qargs, global_index_map)
|
||||||
for nd in block:
|
for nd in block:
|
||||||
if nd.op.name == basis_gate_name:
|
if nd.op.name == basis_gate_name:
|
||||||
basis_count += 1
|
basis_count += 1
|
||||||
if self._check_not_in_basis(nd.op.name, nd.qargs, global_index_map):
|
if self._check_not_in_basis(nd.op.name, nd.qargs, global_index_map):
|
||||||
outside_basis = True
|
outside_basis = True
|
||||||
|
if len(block_qargs) > 2:
|
||||||
|
q = QuantumRegister(len(block_qargs))
|
||||||
|
qc = QuantumCircuit(q)
|
||||||
|
if block_cargs:
|
||||||
|
c = ClassicalRegister(len(block_cargs))
|
||||||
|
qc.add_register(c)
|
||||||
|
for nd in block:
|
||||||
qc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs])
|
qc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs])
|
||||||
unitary = UnitaryGate(Operator(qc))
|
unitary = UnitaryGate(Operator(qc))
|
||||||
|
else:
|
||||||
|
matrix = _block_to_matrix(block, block_index_map)
|
||||||
|
unitary = UnitaryGate(matrix)
|
||||||
|
|
||||||
max_2q_depth = 20 # If depth > 20, there will be 1q gates to consolidate.
|
max_2q_depth = 20 # If depth > 20, there will be 1q gates to consolidate.
|
||||||
if ( # pylint: disable=too-many-boolean-expressions
|
if ( # pylint: disable=too-many-boolean-expressions
|
||||||
|
|
|
@ -32,3 +32,4 @@ from .minimum_point import MinimumPoint
|
||||||
|
|
||||||
# Utility functions
|
# Utility functions
|
||||||
from . import control_flow
|
from . import control_flow
|
||||||
|
from .block_to_matrix import _block_to_matrix
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# This code is part of Qiskit.
|
||||||
|
#
|
||||||
|
# (C) Copyright IBM 2017, 2018.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Converts any block of 2 qubit gates into a matrix."""
|
||||||
|
|
||||||
|
from numpy import identity, kron
|
||||||
|
from qiskit.circuit.library import SwapGate
|
||||||
|
from qiskit.quantum_info import Operator
|
||||||
|
from qiskit.exceptions import QiskitError
|
||||||
|
|
||||||
|
|
||||||
|
SWAP_GATE = SwapGate()
|
||||||
|
SWAP_MATRIX = SWAP_GATE.to_matrix()
|
||||||
|
IDENTITY = identity(2, dtype=complex)
|
||||||
|
|
||||||
|
|
||||||
|
def _block_to_matrix(block, block_index_map):
|
||||||
|
"""
|
||||||
|
The function converts any sequence of operations between two qubits into a matrix
|
||||||
|
that can be utilized to create a gate or a unitary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block (List(DAGOpNode)): A block of operations on two qubits.
|
||||||
|
block_index_map (dict(Qubit, int)): The mapping of the qubit indices in the main circuit.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
NDArray: Matrix representation of the block of operations.
|
||||||
|
"""
|
||||||
|
block_index_length = len(block_index_map)
|
||||||
|
if block_index_length != 2:
|
||||||
|
raise QiskitError(
|
||||||
|
"This function can only operate with blocks of 2 qubits."
|
||||||
|
+ f"This block had {block_index_length}"
|
||||||
|
)
|
||||||
|
matrix = identity(2**block_index_length, dtype=complex)
|
||||||
|
for node in block:
|
||||||
|
try:
|
||||||
|
current = node.op.to_matrix()
|
||||||
|
except QiskitError:
|
||||||
|
current = Operator(node.op).data
|
||||||
|
q_list = [block_index_map[qubit] for qubit in node.qargs]
|
||||||
|
if len(q_list) > 2:
|
||||||
|
raise QiskitError(
|
||||||
|
f"The operation {node.op.name} in this block has "
|
||||||
|
+ f"{len(q_list)} qubits, only 2 max allowed."
|
||||||
|
)
|
||||||
|
basis_change = False
|
||||||
|
if len(q_list) < block_index_length:
|
||||||
|
if q_list[0] == 1:
|
||||||
|
current = kron(current, IDENTITY)
|
||||||
|
else:
|
||||||
|
current = kron(IDENTITY, current)
|
||||||
|
else:
|
||||||
|
if q_list[0] > q_list[1]:
|
||||||
|
if node.op != SWAP_GATE:
|
||||||
|
basis_change = True
|
||||||
|
if basis_change:
|
||||||
|
matrix = (SWAP_MATRIX @ current) @ (SWAP_MATRIX @ matrix)
|
||||||
|
else:
|
||||||
|
matrix = current @ matrix
|
||||||
|
return matrix
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added utility function :func:`qiskit.transpiler.passes.utils._block_to_matrix` that can
|
||||||
|
generate a matrix based on a block of operations between two qubits. This function can
|
||||||
|
be used in transpiler passes that work on some decomposed circuits such as :class:`.ConsolidateBlocks`.
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Reduced overhead of the :class:`.ConsolidateBlocks` pass by performing matrix operations
|
||||||
|
on all two-qubit blocks using :func:`qiskit.transpiler.passes.utils._block_to_matrix`
|
||||||
|
instead of creating an instance of :class:`.QuantumCircuit` and passing it to an :class:`.Operator`.
|
||||||
|
The speedup will only be applicable when consolidating two-qubit blocks. Anything higher
|
||||||
|
than that will still be handled by the :class:`.Operator` class.
|
||||||
|
Check `#8779 <https://github.com/Qiskit/qiskit-terra/issues/8779>`__ for details.
|
Loading…
Reference in New Issue