qiskit.circuit.instruction のソースコード

# This code is part of Qiskit.
# (C) Copyright IBM 2017.
# 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.

A generic quantum instruction.

Instructions can be implementable on hardware (u, cx, etc.) or in simulation
(snapshot, noise, etc.).

Instructions can be unitary (a.k.a Gate) or non-unitary.

Instructions are identified by the following:

    name: A string to identify the type of instruction.
          Used to request a specific instruction on the backend, or in visualizing circuits.

    num_qubits, num_clbits: dimensions of the instruction.

    params: List of parameters to specialize a specific instruction instance.

Instructions do not have any context about where they are in a circuit (which qubits/clbits).
The circuit itself keeps this context.

import copy
from itertools import zip_longest
from typing import List

import numpy

from qiskit.circuit.exceptions import CircuitError
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
from qiskit.qobj.qasm_qobj import QasmQobjInstruction
from qiskit.circuit.parameter import ParameterExpression
from qiskit.circuit.operation import Operation
from qiskit.utils.deprecation import deprecate_func
from .tools import pi_check


[ドキュメント]class Instruction(Operation): """Generic quantum instruction.""" # Class attribute to treat like barrier for transpiler, unroller, drawer # NOTE: Using this attribute may change in the future (See issue # 5811) _directive = False def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt", label=None): """Create a new instruction. Args: name (str): instruction name num_qubits (int): instruction's qubit width num_clbits (int): instruction's clbit width params (list[int|float|complex|str|ndarray|list|ParameterExpression]): list of parameters duration (int or float): instruction's duration. it must be integer if ``unit`` is 'dt' unit (str): time unit of duration label (str or None): An optional label for identifying the instruction. Raises: CircuitError: when the register is not in the correct format. TypeError: when the optional label is provided, but it is not a string. """ if not isinstance(num_qubits, int) or not isinstance(num_clbits, int): raise CircuitError("num_qubits and num_clbits must be integer.") if num_qubits < 0 or num_clbits < 0: raise CircuitError( "bad instruction dimensions: %d qubits, %d clbits." % num_qubits, num_clbits ) self._name = name self._num_qubits = num_qubits self._num_clbits = num_clbits self._params = [] # a list of gate params stored # Custom instruction label # NOTE: The conditional statement checking if the `_label` attribute is # already set is a temporary work around that can be removed after # the next stable qiskit-aer release if not hasattr(self, "_label"): if label is not None and not isinstance(label, str): raise TypeError("label expects a string or None") self._label = label # tuple (ClassicalRegister, int), tuple (Clbit, bool) or tuple (Clbit, int) # when the instruction has a conditional ("if") self.condition = None # list of instructions (and their contexts) that this instruction is composed of # empty definition means opaque or fundamental instruction self._definition = None self._duration = duration self._unit = unit self.params = params # must be at last (other properties may be required for validation) def __eq__(self, other): """Two instructions are the same if they have the same name, same dimensions, and same params. Args: other (instruction): other instruction Returns: bool: are self and other equal. """ if ( type(self) is not type(other) or self.name != other.name or self.num_qubits != other.num_qubits or self.num_clbits != other.num_clbits or self.definition != other.definition ): return False for self_param, other_param in zip_longest(self.params, other.params): try: if self_param == other_param: continue except ValueError: pass try: self_asarray = numpy.asarray(self_param) other_asarray = numpy.asarray(other_param) if numpy.shape(self_asarray) == numpy.shape(other_asarray) and numpy.allclose( self_param, other_param, atol=_CUTOFF_PRECISION, rtol=0 ): continue except (ValueError, TypeError): pass try: if numpy.isclose( float(self_param), float(other_param), atol=_CUTOFF_PRECISION, rtol=0 ): continue except TypeError: pass return False return True def __repr__(self) -> str: """Generates a representation of the Intruction object instance Returns: str: A representation of the Instruction instance with the name, number of qubits, classical bits and params( if any ) """ return "Instruction(name='{}', num_qubits={}, num_clbits={}, params={})".format( self.name, self.num_qubits, self.num_clbits, self.params )
[ドキュメント] def soft_compare(self, other: "Instruction") -> bool: """ Soft comparison between gates. Their names, number of qubits, and classical bit numbers must match. The number of parameters must match. Each parameter is compared. If one is a ParameterExpression then it is not taken into account. Args: other (instruction): other instruction. Returns: bool: are self and other equal up to parameter expressions. """ if ( self.name != other.name or other.num_qubits != other.num_qubits or other.num_clbits != other.num_clbits or len(self.params) != len(other.params) ): return False for self_param, other_param in zip_longest(self.params, other.params): if isinstance(self_param, ParameterExpression) or isinstance( other_param, ParameterExpression ): continue if isinstance(self_param, numpy.ndarray) and isinstance(other_param, numpy.ndarray): if numpy.shape(self_param) == numpy.shape(other_param) and numpy.allclose( self_param, other_param, atol=_CUTOFF_PRECISION ): continue else: try: if numpy.isclose(self_param, other_param, atol=_CUTOFF_PRECISION): continue except TypeError: pass return False return True
def _define(self): """Populates self.definition with a decomposition of this gate.""" pass @property def params(self): """return instruction params.""" return self._params @params.setter def params(self, parameters): self._params = [] for single_param in parameters: if isinstance(single_param, ParameterExpression): self._params.append(single_param) else: self._params.append(self.validate_parameter(single_param))
[ドキュメント] def validate_parameter(self, parameter): """Instruction parameters has no validation or normalization.""" return parameter
[ドキュメント] def is_parameterized(self): """Return True .IFF. instruction is parameterized else False""" return any( isinstance(param, ParameterExpression) and param.parameters for param in self.params )
@property def definition(self): """Return definition in terms of other basic gates.""" if self._definition is None: self._define() return self._definition @definition.setter def definition(self, array): """Set gate representation""" self._definition = array @property def decompositions(self): """Get the decompositions of the instruction from the SessionEquivalenceLibrary.""" # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel return sel.get_entry(self) @decompositions.setter def decompositions(self, decompositions): """Set the decompositions of the instruction from the SessionEquivalenceLibrary.""" # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel sel.set_entry(self, decompositions)
[ドキュメント] def add_decomposition(self, decomposition): """Add a decomposition of the instruction to the SessionEquivalenceLibrary.""" # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel sel.add_equivalence(self, decomposition)
@property def duration(self): """Get the duration.""" return self._duration @duration.setter def duration(self, duration): """Set the duration.""" self._duration = duration @property def unit(self): """Get the time unit of duration.""" return self._unit @unit.setter def unit(self, unit): """Set the time unit of duration.""" self._unit = unit
[ドキュメント] def assemble(self): """Assemble a QasmQobjInstruction""" instruction = QasmQobjInstruction(name=self.name) # Evaluate parameters if self.params: params = [x.evalf(x) if hasattr(x, "evalf") else x for x in self.params] instruction.params = params # Add placeholder for qarg and carg params if self.num_qubits: instruction.qubits = list(range(self.num_qubits)) if self.num_clbits: instruction.memory = list(range(self.num_clbits)) # Add label if defined if self.label: instruction.label = self.label # Add condition parameters for assembler. This is needed to convert # to a qobj conditional instruction at assemble time and after # conversion will be deleted by the assembler. if self.condition: instruction._condition = self.condition return instruction
@property def label(self) -> str: """Return instruction label""" return self._label @label.setter def label(self, name: str): """Set instruction label to name Args: name (str or None): label to assign instruction Raises: TypeError: name is not string or None. """ if isinstance(name, (str, type(None))): self._label = name else: raise TypeError("label expects a string or None")
[ドキュメント] def reverse_ops(self): """For a composite instruction, reverse the order of sub-instructions. This is done by recursively reversing all sub-instructions. It does not invert any gate. Returns: qiskit.circuit.Instruction: a new instruction with sub-instructions reversed. """ if not self._definition: return self.copy() reverse_inst = self.copy(name=self.name + "_reverse") reversed_definition = self._definition.copy_empty_like() for inst in reversed(self._definition): reversed_definition.append(inst.operation.reverse_ops(), inst.qubits, inst.clbits) reverse_inst.definition = reversed_definition return reverse_inst
[ドキュメント] def inverse(self): """Invert this instruction. If the instruction is composite (i.e. has a definition), then its definition will be recursively inverted. Special instructions inheriting from Instruction can implement their own inverse (e.g. T and Tdg, Barrier, etc.) Returns: qiskit.circuit.Instruction: a fresh instruction for the inverse Raises: CircuitError: if the instruction is not composite and an inverse has not been implemented for it. """ if self.definition is None: raise CircuitError("inverse() not implemented for %s." % self.name) from qiskit.circuit import Gate # pylint: disable=cyclic-import if self.name.endswith("_dg"): name = self.name[:-3] else: name = self.name + "_dg" if self.num_clbits: inverse_gate = Instruction( name=name, num_qubits=self.num_qubits, num_clbits=self.num_clbits, params=self.params.copy(), ) else: inverse_gate = Gate(name=name, num_qubits=self.num_qubits, params=self.params.copy()) inverse_definition = self._definition.copy_empty_like() inverse_definition.global_phase = -inverse_definition.global_phase for inst in reversed(self._definition): inverse_definition._append(inst.operation.inverse(), inst.qubits, inst.clbits) inverse_gate.definition = inverse_definition return inverse_gate
[ドキュメント] def c_if(self, classical, val): """Set a classical equality condition on this instruction between the register or cbit ``classical`` and value ``val``. .. note:: This is a setter method, not an additive one. Calling this multiple times will silently override any previously set condition; it does not stack. """ if not isinstance(classical, (ClassicalRegister, Clbit)): raise CircuitError("c_if must be used with a classical register or classical bit") if val < 0: raise CircuitError("condition value should be non-negative") if isinstance(classical, Clbit): # Casting the conditional value as Boolean when # the classical condition is on a classical bit. val = bool(val) self.condition = (classical, val) return self
[ドキュメント] def copy(self, name=None): """ Copy of the instruction. Args: name (str): name to be given to the copied circuit, if ``None`` then the name stays the same. Returns: qiskit.circuit.Instruction: a copy of the current instruction, with the name updated if it was provided """ cpy = self.__deepcopy__() if name: cpy.name = name return cpy
def __deepcopy__(self, _memo=None): cpy = copy.copy(self) cpy._params = copy.copy(self._params) if self._definition: cpy._definition = copy.deepcopy(self._definition, _memo) return cpy def _qasmif(self, string): """Print an if statement if needed.""" from qiskit.qasm2 import QASM2ExportError # pylint: disable=cyclic-import if self.condition is None: return string if not isinstance(self.condition[0], ClassicalRegister): raise QASM2ExportError( "OpenQASM 2 can only condition on registers, but got '{self.condition[0]}'" ) return "if(%s==%d) " % (self.condition[0].name, self.condition[1]) + string
[ドキュメント] @deprecate_func( additional_msg=( "Correct exporting to OpenQASM 2 is the responsibility of a larger exporter; it cannot " "safely be done on an object-by-object basis without context. No replacement will be " "provided, because the premise is wrong." ), since="0.25.0", ) def qasm(self): """Return a default OpenQASM string for the instruction. Derived instructions may override this to print in a different format (e.g. ``measure q[0] -> c[0];``). """ name_param = self.name if self.params: name_param = "{}({})".format( name_param, ",".join([pi_check(i, output="qasm", eps=1e-12) for i in self.params]), ) return self._qasmif(name_param)
[ドキュメント] def broadcast_arguments(self, qargs, cargs): """ Validation of the arguments. Args: qargs (List): List of quantum bit arguments. cargs (List): List of classical bit arguments. Yields: Tuple(List, List): A tuple with single arguments. Raises: CircuitError: If the input is not valid. For example, the number of arguments does not match the gate expectation. """ if len(qargs) != self.num_qubits: raise CircuitError( f"The amount of qubit arguments {len(qargs)} does not match" f" the instruction expectation ({self.num_qubits})." ) if len(cargs) != self.num_clbits: raise CircuitError( f"The amount of clbit arguments {len(cargs)} does not match" f" the instruction expectation ({self.num_clbits})." ) # [[q[0], q[1]], [c[0], c[1]]] -> [q[0], c[0]], [q[1], c[1]] flat_qargs = [qarg for sublist in qargs for qarg in sublist] flat_cargs = [carg for sublist in cargs for carg in sublist] yield flat_qargs, flat_cargs
def _return_repeat(self, exponent): return Instruction( name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, num_clbits=self.num_clbits, params=self.params, )
[ドキュメント] def repeat(self, n): """Creates an instruction with `gate` repeated `n` amount of times. Args: n (int): Number of times to repeat the instruction Returns: qiskit.circuit.Instruction: Containing the definition. Raises: CircuitError: If n < 1. """ if int(n) != n or n < 1: raise CircuitError("Repeat can only be called with strictly positive integer.") n = int(n) instruction = self._return_repeat(n) qargs = [] if self.num_qubits == 0 else QuantumRegister(self.num_qubits, "q") cargs = [] if self.num_clbits == 0 else ClassicalRegister(self.num_clbits, "c") if instruction.definition is None: # pylint: disable=cyclic-import from qiskit.circuit import QuantumCircuit, CircuitInstruction qc = QuantumCircuit() if qargs: qc.add_register(qargs) if cargs: qc.add_register(cargs) circuit_instruction = CircuitInstruction(self, qargs, cargs) for _ in [None] * n: qc._append(circuit_instruction) instruction.definition = qc return instruction
@property def condition_bits(self) -> List[Clbit]: """Get Clbits in condition.""" if self.condition is None: return [] if isinstance(self.condition[0], Clbit): return [self.condition[0]] else: # ClassicalRegister return list(self.condition[0]) @property def name(self): """Return the name.""" return self._name @name.setter def name(self, name): """Set the name.""" self._name = name @property def num_qubits(self): """Return the number of qubits.""" return self._num_qubits @num_qubits.setter def num_qubits(self, num_qubits): """Set num_qubits.""" self._num_qubits = num_qubits @property def num_clbits(self): """Return the number of clbits.""" return self._num_clbits @num_clbits.setter def num_clbits(self, num_clbits): """Set num_clbits.""" self._num_clbits = num_clbits