-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add transpiler passes for adding instruction-dependent noises (#1391)
Co-authored-by: Christopher J. Wood <cjwood@us.ibm.com>
- Loading branch information
1 parent
53038b1
commit 130ad57
Showing
13 changed files
with
652 additions
and
119 deletions.
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
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
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,18 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2021. | ||
# | ||
# 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. | ||
|
||
""" | ||
Passes for adding noises. | ||
""" | ||
|
||
from .local_noise_pass import LocalNoisePass | ||
from .relaxation_noise_pass import RelaxationNoisePass |
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,133 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2021. | ||
# | ||
# 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. | ||
""" | ||
Local noise addition pass. | ||
""" | ||
from typing import Optional, Union, Sequence, Callable, Iterable | ||
|
||
from qiskit.circuit import Instruction | ||
from qiskit.dagcircuit import DAGCircuit | ||
from qiskit.transpiler import TransformationPass | ||
from qiskit.transpiler.exceptions import TranspilerError | ||
from ..errors import QuantumError, ReadoutError | ||
|
||
InstructionLike = Union[Instruction, QuantumError] | ||
|
||
|
||
class LocalNoisePass(TransformationPass): | ||
"""Transpiler pass to insert noise into a circuit. | ||
The noise in this pass is defined by a noise function or callable with signature | ||
.. code:: python | ||
def func( | ||
inst: Instruction, | ||
qubits: Optional[List[int]] = None | ||
) -> InstructionLike: | ||
For every instance of one of the reference instructions in a circuit the | ||
supplied function is called on that instruction and the returned noise | ||
is added to the circuit. This noise can depend on properties of the | ||
instruction it is called on (for example parameters or duration) to | ||
allow inserting parameterized noise models. | ||
Several methods for adding the constructed errors to circuits are supported | ||
and can be set by using the ``method`` kwarg. The supported methods are | ||
* ``"append"``: add the return of the callable after the instruction. | ||
* ``"prepend"``: add the return of the callable before the instruction. | ||
* ``"replace"``: replace the instruction with the return of the callable. | ||
If the return is None, the instruction will be removed. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
func: Callable[[Instruction, Sequence[int]], InstructionLike], | ||
op_types: Optional[Union[type, Iterable[type]]] = None, | ||
method: str = 'append' | ||
): | ||
"""Initialize noise pass. | ||
Args: | ||
func: noise function `func(inst, qubits) -> InstructionLike`. | ||
op_types: Optional, single or list of instruction types to apply the | ||
noise function to. If None the noise function will be | ||
applied to all instructions in the circuit. | ||
method: method for inserting noise. Allow methods are | ||
'append', 'prepend', 'replace'. | ||
Raises: | ||
TranspilerError: if an invalid option is specified. | ||
""" | ||
if method not in {"append", "prepend", "replace"}: | ||
raise TranspilerError( | ||
f'Invalid method: {method}, it must be "append", "prepend" or "replace"' | ||
) | ||
if isinstance(op_types, type): | ||
op_types = (op_types,) | ||
super().__init__() | ||
self._func = func | ||
self._ops = tuple(op_types) if op_types else tuple() | ||
self._method = method | ||
if not all(isinstance(op, type) for op in self._ops): | ||
raise TranspilerError( | ||
f"Invalid ops: '{op_types}', expecting single or list of operation types (or None)" | ||
) | ||
|
||
def run(self, dag: DAGCircuit) -> DAGCircuit: | ||
"""Run the LocalNoisePass pass on `dag`. | ||
Args: | ||
dag: DAG to be changed. | ||
Returns: | ||
A changed DAG. | ||
Raises: | ||
TranspilerError: if generated operation is not valid. | ||
""" | ||
qubit_indices = {qubit: idx for idx, qubit in enumerate(dag.qubits)} | ||
for node in dag.topological_op_nodes(): | ||
if self._ops and not isinstance(node.op, self._ops): | ||
continue | ||
|
||
qubits = [qubit_indices[q] for q in node.qargs] | ||
new_op = self._func(node.op, qubits) | ||
if new_op is None: | ||
if self._method == "replace": | ||
dag.remove_op_node(node) | ||
continue | ||
if isinstance(new_op, ReadoutError): | ||
raise TranspilerError("Insertions of ReadoutError is not yet supported.") | ||
if not isinstance(new_op, Instruction): | ||
try: | ||
new_op = new_op.to_instruction() | ||
except AttributeError as att_err: | ||
raise TranspilerError( | ||
"Function must return an object implementing 'to_instruction' method." | ||
) from att_err | ||
if new_op.num_qubits != len(node.qargs): | ||
raise TranspilerError( | ||
f"Number of qubits of generated op {new_op.num_qubits} != " | ||
f"{len(node.qargs)} that of a reference op {node.name}" | ||
) | ||
|
||
new_dag = DAGCircuit() | ||
new_dag.add_qubits(node.qargs) | ||
new_dag.add_clbits(node.cargs) | ||
if self._method == "append": | ||
new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs) | ||
new_dag.apply_operation_back(new_op, qargs=node.qargs) | ||
if self._method == "prepend": | ||
new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs) | ||
|
||
dag.substitute_node_with_dag(node, new_dag) | ||
|
||
return dag |
Oops, something went wrong.