Source code for mimiqcircuits.instruction

#
# Copyright © 2022-2023 University of Strasbourg. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from mimiqcircuits.operations.operation import Operation
import mimiqcircuits as mc
import copy
import numpy as np


def _allunique(lst):
    seen = set()
    for element in lst:
        if element in seen:
            return False
        seen.add(element)
    return True


[docs] class Instruction: """Initializes an instruction of a quantum circuit. Args: operation (Operation): The operation applied by the instruction. qubits (tuple of int): The qubits to apply the quantum operation to. bits (tuple of int): The classical bits to apply the quantum operation to. Raises: TypeError: If operation is not a subclass of Gate or qubits is not a tuple. ValueError: If qubits contains less than 1 or more than 2 elements. Examples: >>> from mimiqcircuits import * >>> Instruction(GateX(),(0,),()) X @ q[0] >>> Instruction(Barrier(4),(0,1,2,3),()) Barrier @ q[0,1,2,3] """ _operation = None _qubits = None _bits = None def __init__(self, operation, qubits=None, bits=None): if qubits is None: qubits = tuple() if bits is None: bits = tuple() if not isinstance(qubits, tuple): raise TypeError( f"Target qubits should be given in a tuple of integers. Given {qubits} of type {type(qubits)}." ) if not isinstance(bits, tuple): raise TypeError( f"Target bits should be given in a tuple of integers. Given {bits} of type {type(bits)}." ) if not isinstance(operation, Operation): raise TypeError( f"Operation must be a subclass of Operation. Given {operation} of type f{type(operation)}" ) if not _allunique(qubits): raise ValueError("Duplicated qubit target in instruction") if not _allunique(bits): raise ValueError("Duplicated classical bit target in instruction") for qi in qubits: if qi < 0: raise ValueError("Qubit target index cannot be negative") for bi in bits: if bi < 0: raise ValueError("Bit target index cannot be negative") if len(qubits) != operation.num_qubits: raise ValueError( f"Wrong number of target qubits for operation {operation} wanted {operation.num_qubits}, given {len(qubits)}" ) if len(bits) != operation.num_bits: raise ValueError( f"Wrong number of target bits for operation {operation} wanted {operation.num_bits}, given {len(bits)}" ) self._operation = operation self._qubits = qubits self._bits = bits @property def operation(self): return self._operation @operation.setter def operation(self, _): raise AttributeError("operation is a read-only attribute") @property def qubits(self): return self._qubits @qubits.setter def qubits(self, _): raise AttributeError("qubits is a read-only attribute") @property def bits(self): return self._bits @bits.setter def bits(self, _): raise AttributeError("bits is a read-only attribute") def __repr__(self): return str(self) def get_operation(self): return self._operation
[docs] def get_qubits(self): return self._qubits
[docs] def get_bits(self): return self._bits
[docs] def get_operation(self): return self.operation
[docs] def asciiwidth(self): return self.operation.asciiwidth(self._qubits, self._bits)
def __eq__(self, other): if not isinstance(other, Instruction): return False return ( (self.operation == other.operation) and (self.qubits == other.qubits) and (self.bits == other.bits) )
[docs] def inverse(self): return Instruction(self.operation.inverse(), self.qubits, self.bits)
[docs] def copy(self): return copy.copy(self)
[docs] def deepcopy(self): return copy.deepcopy(self)
def _decompose(self, circ): return self.operation._decompose(circ, self.qubits, self.bits)
[docs] def decompose(self): return self._decompose(mc.Circuit())
[docs] def evaluate(self, d): return Instruction(self.operation.evaluate(d), self.qubits, self.bits)
def __str__(self): compact = False op = str(self.operation) nq = len(self.qubits) nb = len(self.bits) space = "" if compact else " " targets = "" if nq != 0 or nb != 0: targets = f"{space}@{space}" if nq != 0: q_partition = _partition( self.get_qubits(), np.cumsum(self.operation.qregsizes) ) q_targets = f",{space}".join( f"q{_string_with_square(_find_unit_range(x),',')}" for x in q_partition ) targets += f",".join(q_targets.split(",")) if nb != 0: c_partition = _partition( self.get_bits(), np.cumsum(self.operation.cregsizes) ) c_targets = f", {space}".join( f", c{_string_with_square(x, ',')}" for x in c_partition ) targets += c_targets return f"{op}{targets}"
def _partition(arr, indices): vec = list(arr) partitions = [vec[: indices[0]]] for i in range(1, len(indices)): partitions.append(vec[indices[i - 1] : indices[i]]) return partitions def _string_with_square(arr, sep): return ( "[" + sep.join( map(lambda e: sep.join(map(str, e)) if isinstance(e, list) else str(e), arr) ) + "]" ) def _find_unit_range(arr): if len(arr) < 2: return arr narr = [] rangestart = arr[0] rangestop = arr[0] for v in arr[1:]: if v == rangestop + 1: rangestop = v elif rangestart == rangestop: narr.append(rangestart) rangestart = v rangestop = v else: narr.append(list(range(rangestart, rangestop + 1))) rangestart = v rangestop = v if rangestart == rangestop: narr.append(rangestart) else: narr.append(list(range(rangestart, rangestop + 1))) return narr __all__ = ["Instruction"]