Source code for qiboml.models.encoding

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional

import numpy as np
from qibo import Circuit, gates
from qibo.config import raise_error

from qiboml import ndarray


[docs]@dataclass class QuantumEncoding(ABC): """ Abstract Encoder class. Args: nqubits (int): total number of qubits. qubits (tuple[int], optional): set of qubits it acts on, by default ``range(nqubits)``. """ nqubits: int qubits: Optional[tuple[int]] = None _circuit: Circuit = None def __post_init__( self, ): """Ancillary post initialization for the dataclass object.""" self.qubits = ( tuple(range(self.nqubits)) if self.qubits is None else tuple(self.qubits) ) self._circuit = Circuit(self.nqubits)
[docs] @abstractmethod def __call__(self, x: ndarray) -> Circuit: """Abstract call method.""" pass
@property def circuit( self, ) -> Circuit: """Internal initialized circuit.""" return self._circuit.copy() @property def differentiable(self) -> bool: """Whether the encoder is differentiable. If ``True`` the gradient w.r.t. the inputs is calculated, otherwise it is automatically set to zero. """ return True def __hash__(self) -> int: return hash(self.qubits)
[docs]class PhaseEncoding(QuantumEncoding): def __post_init__( self, ): """Ancillary post initialization: builds the internal circuit with the rotation gates.""" super().__post_init__() for q in self.qubits: self._circuit.add(gates.RY(q, theta=0.0, trainable=False))
[docs] def _set_phases(self, x: ndarray): """Helper method to set the phases of the rotations of the internal circuit. Args: x (ndarray): the input rotation angles. """ for gate, phase in zip(self._circuit.parametrized_gates, x.ravel()): gate.parameters = phase
[docs] def __call__(self, x: ndarray) -> Circuit: """Construct the circuit encoding the ``x`` data in the rotation angles of some ``RY`` gates. Args: x (ndarray): the input real data to encode in rotation angles. Returns: (Circuit): the constructed ``qibo.Circuit``. """ self._set_phases(x) return self.circuit
[docs]class BinaryEncoding(QuantumEncoding):
[docs] def __call__(self, x: ndarray) -> Circuit: r"""Construct the circuit encoding the ``x`` binary data in some ``RX`` rotation gates with angles either :math:`\pi` (for ones) or 0 (for zeros). Args: x (ndarray): the input binary data. Returns: (Circuit): the constructed ``qibo.Circuit``. """ if x.shape[-1] != len(self.qubits): raise_error( RuntimeError, f"Invalid input dimension {x.shape[-1]}, but the allocated qubits are {self.qubits}.", ) circuit = self.circuit for qubit, bit in zip(self.qubits, x.ravel()): circuit.add(gates.RX(qubit, theta=bit * np.pi, trainable=False)) return circuit
@property def differentiable(self) -> bool: return False