Source code for qibo.gates.special

from qibo.backends import _check_backend
from qibo.gates.abstract import SpecialGate
from qibo.gates.measurements import M


[docs]class CallbackGate(SpecialGate): """Calculates a :class:`qibo.callbacks.Callback` at a specific point in the circuit. This gate performs the callback calulation without affecting the state vector. Args: callback (:class:`qibo.callbacks.Callback`): Callback object to calculate. """ def __init__(self, callback: "Callback"): super().__init__() self.name = callback.__class__.__name__ self.draw_label = "".join([c for c in self.name if c.isupper()]) self.callback = callback self.init_args = [callback] def apply(self, backend, state, nqubits): self.callback.nqubits = nqubits self.callback.apply(backend, state) return state def apply_density_matrix(self, backend, state, nqubits): self.callback.nqubits = nqubits self.callback.apply_density_matrix(backend, state) return state
[docs]class FusedGate(SpecialGate): """Collection of gates that will be fused and applied as single gate during simulation. This gate is constructed automatically by :meth:`qibo.models.circuit.Circuit.fuse` and should not be used by user. """ def __init__(self, *q): super().__init__() self.name = "Fused Gate" self.draw_label = "[]" self.target_qubits = tuple(sorted(q)) self.init_args = list(q) self.qubit_set = set(q) self.gates = [] self.marked = False self.fused = False self.left_neighbors = {} self.right_neighbors = {} @classmethod def from_gate(cls, gate): fgate = cls(*gate.qubits) fgate.append(gate) if isinstance(gate, (M, SpecialGate)): # special gates do not participate in fusion fgate.marked = True return fgate def prepend(self, gate): self.qubit_set = self.qubit_set | set(gate.qubits) self.init_args = sorted(self.qubit_set) self.target_qubits = tuple(self.init_args) if isinstance(gate, self.__class__): self.gates = gate.gates + self.gates else: self.gates = [gate] + self.gates def append(self, gate): self.qubit_set = self.qubit_set | set(gate.qubits) self.init_args = sorted(self.qubit_set) self.target_qubits = tuple(self.init_args) if isinstance(gate, self.__class__): self.gates.extend(gate.gates) else: self.gates.append(gate) def _dagger(self): dagger = self.__class__(*self.init_args) for gate in self.gates[::-1]: dagger.append(gate.dagger()) return dagger
[docs] def can_fuse(self, gate, max_qubits): """Check if two gates can be fused.""" if gate is None: return False if self.marked or gate.marked: # gates are already fused return False if len(self.qubit_set | gate.qubit_set) > max_qubits: # combined qubits are more than ``max_qubits`` return False return True
[docs] def matrix(self, backend=None): """Returns matrix representation of special gate. Args: backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses :class:`qibo.backends.GlobalBackend`. Defaults to ``None``. Returns: ndarray: Matrix representation of special gate. """ backend = _check_backend(backend) return backend.matrix_fused(self)
[docs] def fuse(self, gate): """Fuses two gates.""" left_gates = set(self.right_neighbors.values()) - {gate} right_gates = set(gate.left_neighbors.values()) - {self} if len(left_gates) > 0 and len(right_gates) > 0: # abort if there are blocking gates between the two gates # not in the shared qubits return qubits = self.qubit_set & gate.qubit_set # the gate with most neighbors different than the two gates to # fuse will be the parent if len(left_gates) > len(right_gates): parent, child = self, gate between_gates = {parent.right_neighbors.get(q) for q in qubits} if between_gates == {child}: child.marked = True parent.append(child) for q in qubits: neighbor = child.right_neighbors.get(q) if neighbor is not None: parent.right_neighbors[q] = neighbor neighbor.left_neighbors[q] = parent else: parent.right_neighbors.pop(q) else: parent, child = gate, self between_gates = {parent.left_neighbors.get(q) for q in qubits} if between_gates == {child}: child.marked = True parent.prepend(child) for q in qubits: neighbor = child.left_neighbors.get(q) if neighbor is not None: parent.left_neighbors[q] = neighbor neighbor.right_neighbors[q] = parent if child.marked: # update the neighbors graph for q in child.qubit_set - qubits: neighbor = child.right_neighbors.get(q) if neighbor is not None: parent.right_neighbors[q] = neighbor neighbor.left_neighbors[q] = parent neighbor = child.left_neighbors.get(q) if neighbor is not None: parent.left_neighbors[q] = neighbor neighbor.right_neighbors[q] = parent
def apply_clifford(self, backend, state, nqubits): for gate in self.gates: state = gate.apply_clifford(backend, state, nqubits) return state