Source code for qailab.circuit.base
  1"""ABC structure for circuit building blocks"""
  2from abc import ABC, abstractmethod
  3from typing import Literal
  4from collections.abc import Sequence
  5
  6from qiskit import QuantumCircuit
  7from qiskit.circuit import ParameterVector, Parameter, QuantumRegister
  8from qiskit.circuit.gate import Gate
  9from qiskit.circuit.quantumcircuit import QubitSpecifier
 10
 11
[docs]
 12class CircuitBlock(ABC):
 13    """
 14    Base class for any circuit building block
 15
 16    Attributes:
 17        name (str): Block (and block circuit) name.
 18    """
 19
 20    def __init__(self, name: str = 'unknown') -> None:
 21        self.name = name
 22
[docs]
 23    def to_gate(self, num_qubits: int) -> Gate:
 24        """
 25        Get a gate form of of this block.
 26
 27        Args:
 28            num_qubits (int): Desired width.
 29
 30        Returns:
 31            Gate: Block defined circuit as a single gate.
 32        """
 33        qc = self._build_circuit(num_qubits)
 34        return qc.to_gate(label=self.name) 
 35
[docs]
 36    def add_to_circuit(self, circuit: QuantumCircuit, qargs: Sequence[QubitSpecifier] | None = None) -> None:
 37        """
 38        Add this block to a circuit (number of qubits must match)
 39
 40        Args:
 41            circuit (QuantumCircuit): The circuit that will receive this block (in place).
 42            qargs (list[QubitSpecifier] | None, optional): Which qubits to apply this circuit to. If None apply to all. Defaults to None.
 43        """
 44        if qargs is None:
 45            qargs = list(range(circuit.num_qubits))
 46        else:
 47            qargs = list(qargs)
 48
 49        gate = self.to_gate(len(qargs))
 50
 51        if gate.num_qubits > len(qargs):
 52            num_qb_before = circuit.num_qubits
 53            circuit.add_register(QuantumRegister(gate.num_qubits - len(qargs)))
 54            qargs += list(range(num_qb_before, circuit.num_qubits))
 55
 56        circuit.append(gate, qargs) 
 57
 58    @abstractmethod
 59    def _build_circuit(self, num_qubits: int) -> QuantumCircuit:
 60        pass 
 61
 62
[docs]
 63class ParameterizedBlock(CircuitBlock, ABC):
 64    """
 65    Blocks generating parametrized circuits
 66    """
 67
 68    def __init__(self, name: str = 'unknown') -> None:
 69        self._parameters: Sequence[Parameter] | None = None
 70        super().__init__(name)
 71
 72    @property
 73    def parameters(self) -> Sequence[Parameter]:
 74        """Get this block's parameter vector"""
 75        if self._parameters is None:
 76            raise ValueError("No parameters, the block was not added to any circuit.")
 77        return self._parameters 
 78
 79
[docs]
 80class EntanglingBlock(CircuitBlock, ABC):
 81    """Blocks entangling qubits together""" 
 82
 83
[docs]
 84class EncodingBlock(ParameterizedBlock, CircuitBlock, ABC):
 85    """
 86    Blocks encoding some parameter vector (trainable or not)
 87
 88    Attributes:
 89        block_type (Literal['input', 'weight']): Whether this block encodes weights or inputs.
 90    """
 91
 92    def __init__(self, name: str = 'unknown', block_type: Literal['input', 'weight'] = 'input') -> None:
 93        self.block_type = block_type
 94        super().__init__(name)
 95
 96    def _create_parameters(self, size: int) -> Sequence[Parameter]:
 97        parameter_vector = ParameterVector(f"{self.block_type}_{self.__class__.__name__}_Params_{hex(id(super()))}", size)
 98        return parameter_vector.params 
 99
100
[docs]
101class NonGateBlock(CircuitBlock, ABC):
102    """Blocks that cannot be converted to gates, e.g. measurement."""
103
[docs]
104    def to_gate(self, num_qubits: int) -> Gate:
105        raise ValueError("This block cannot be converted to a gate.") 
106
[docs]
107    def add_to_circuit(self, circuit: QuantumCircuit, qargs: Sequence[QubitSpecifier] | None = None) -> None:
108        if qargs is None:
109            qargs = list(range(circuit.num_qubits))
110        else:
111            qargs = list(qargs)
112
113        c = self._build_circuit(len(qargs))
114
115        circuit.compose(c, qargs, inplace=True)