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.transpiler.passmanager import PassManager
|
||||
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_2q_blocks import Collect2qBlocks
|
||||
|
||||
|
@ -110,19 +111,24 @@ class ConsolidateBlocks(TransformationPass):
|
|||
if isinstance(nd, DAGOpNode) and getattr(nd.op, "condition", None):
|
||||
block_cargs |= set(getattr(nd.op, "condition", None)[0])
|
||||
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)
|
||||
for nd in block:
|
||||
if nd.op.name == basis_gate_name:
|
||||
basis_count += 1
|
||||
if self._check_not_in_basis(nd.op.name, nd.qargs, global_index_map):
|
||||
outside_basis = True
|
||||
qc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs])
|
||||
unitary = UnitaryGate(Operator(qc))
|
||||
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])
|
||||
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.
|
||||
if ( # pylint: disable=too-many-boolean-expressions
|
||||
|
|
|
@ -32,3 +32,4 @@ from .minimum_point import MinimumPoint
|
|||
|
||||
# Utility functions
|
||||
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