Source code for 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 warnings
import copy
from itertools import zip_longest

import numpy

from qiskit.circuit.exceptions import CircuitError
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit.classicalregister import ClassicalRegister
from qiskit.qobj.qasm_qobj import QasmQobjInstruction
from qiskit.circuit.parameter import ParameterExpression
from .tools import pi_check


[docs]class Instruction: """Generic quantum instruction."""
[docs] def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit='dt'): """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 Raises: CircuitError: when the register is not in the correct format. """ 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 # tuple (ClassicalRegister, 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.params = params self._duration = duration self._unit = unit
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: if numpy.shape(self_param) == numpy.shape(other_param) \ and numpy.allclose(self_param, other_param, atol=_CUTOFF_PRECISION): continue except TypeError: pass try: if numpy.isclose(float(self_param), float(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))
[docs] def validate_parameter(self, parameter): """Instruction parameters has no validation or normalization.""" return parameter
[docs] 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)
[docs] 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
[docs] 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 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
[docs] def mirror(self): """DEPRECATED: use instruction.reverse_ops(). Return: qiskit.circuit.Instruction: a new instruction with sub-instructions reversed. """ warnings.warn('instruction.mirror() is deprecated. Use circuit.reverse_ops()' 'to reverse the order of gates.', DeprecationWarning) return self.reverse_ops()
[docs] 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') reverse_inst.definition._data = [(inst.reverse_ops(), qargs, cargs) for inst, qargs, cargs in reversed(self._definition)] return reverse_inst
[docs] 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 QuantumCircuit, Gate # pylint: disable=cyclic-import if self.num_clbits: inverse_gate = Instruction(name=self.name + '_dg', num_qubits=self.num_qubits, num_clbits=self.num_clbits, params=self.params.copy()) else: inverse_gate = Gate(name=self.name + '_dg', num_qubits=self.num_qubits, params=self.params.copy()) inverse_gate.definition = QuantumCircuit(*self.definition.qregs, *self.definition.cregs) inverse_gate.definition._data = [(inst.inverse(), qargs, cargs) for inst, qargs, cargs in reversed(self._definition)] return inverse_gate
[docs] def c_if(self, classical, val): """Add classical condition on register classical and value val.""" if not isinstance(classical, ClassicalRegister): raise CircuitError("c_if must be used with a classical register") if val < 0: raise CircuitError("condition value should be non-negative") self.condition = (classical, val) return self
[docs] 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.""" if self.condition is None: return string return "if(%s==%d) " % (self.condition[0].name, self.condition[1]) + string
[docs] 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 = "%s(%s)" % (name_param, ",".join( [pi_check(i, ndigits=8, output='qasm') for i in self.params])) return self._qasmif(name_param)
[docs] 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( 'The amount of qubit arguments does not match the instruction expectation.') # [[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="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, num_clbits=self.num_clbits, params=self.params)
[docs] 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 import QuantumCircuit qc = QuantumCircuit() if qargs: qc.add_register(qargs) if cargs: qc.add_register(cargs) qc.data = [(self, qargs[:], cargs[:])] * n instruction.definition = qc return instruction

© Copyright 2020, Qiskit Development Team. Last updated on 2021/02/18.

Built with Sphinx using a theme provided by Read the Docs.