CoolFace
Modelpublic

Premchan369/Q-TensorFormer

sourceHugging Faceapache-2.0updated 9d agoView on Hugging Face
2likes185downloads
quantum_layers.py203 linesDownload Raw Back to src
1"""2Quantum Feature Encoding Layers.3 4PennyLane-based quantum circuits wrapped as PyTorch nn.Module layers.5 6Components:7  - QuantumAngleEmbedding: Classical data → rotation angles on qubits8  - QuantumAmplitudeEmbedding: Encodes data as quantum amplitudes9  - EntanglementMonitor: Estimates entanglement via attention patterns10  - ClassicalQuantumFallback: MLP-based fallback when PennyLane unavailable11"""12 13import torch14import torch.nn as nn15import torch.nn.functional as F16import math17from typing import Optional, Tuple, List18 19try:20    import pennylane as qml21    HAS_PENNYLANE = True22except ImportError:23    HAS_PENNYLANE = False24 25 26class QuantumAngleEmbedding(nn.Module):27    """28    Encodes classical features into quantum states via angle encoding.29 30    Circuit: RX(input) → [RY(θ) → CNOT ladder] × n_layers → ⟨Z_i⟩31 32    Parameters33    ----------34    n_qubits : int35        Number of qubits (4-8 for NISQ compatibility).36    n_layers : int37        Number of variational circuit layers.38    n_outputs : int or None39        Number of expectation values to measure. Default: n_qubits.40    diff_method : str41        Differentiation method. 'backprop' for batched inputs,42        'parameter-shift' for hardware compatibility.43    """44 45    def __init__(self, n_qubits: int = 4, n_layers: int = 2,46                 n_outputs: int = None, diff_method: str = "backprop"):47        super().__init__()48        if not HAS_PENNYLANE:49            raise ImportError(50                "PennyLane is required for quantum layers. "51                "Install with: pip install pennylane"52            )53 54        self.n_qubits = n_qubits55        self.n_layers = n_layers56        self.n_outputs = n_outputs or n_qubits57 58        dev = qml.device("default.qubit", wires=n_qubits)59 60        @qml.qnode(dev, interface="torch", diff_method=diff_method)61        def circuit(inputs, weights):62            # Angle encoding63            for i in range(n_qubits):64                qml.RX(inputs[..., i], wires=i)65 66            # Variational layers with entanglement67            for layer in range(n_layers):68                for i in range(n_qubits):69                    qml.RY(weights[layer, i], wires=i)70                # Nearest-neighbor CNOT ladder71                for i in range(n_qubits - 1):72                    qml.CNOT(wires=[i, i + 1])73                # Cyclic entanglement for >2 qubits74                if n_qubits > 2:75                    qml.CNOT(wires=[n_qubits - 1, 0])76 77            # Measure PauliZ expectation values78            return [qml.expval(qml.PauliZ(i)) for i in range(self.n_outputs)]79 80        weight_shapes = {"weights": (n_layers, n_qubits)}81        self.qlayer = qml.qnn.TorchLayer(circuit, weight_shapes)82 83    def forward(self, x: torch.Tensor) -> torch.Tensor:84        """85        Args:86            x: (*batch, n_qubits) — classical inputs mapped to rotation angles87        Returns:88            (*batch, n_outputs) — PauliZ expectation values in [-1, 1]89        """90        return self.qlayer(x)91 92 93class EntanglementMonitor(nn.Module):94    """95    Estimates entanglement entropy from attention patterns.96 97    Uses attention distribution entropy as a classical proxy98    for quantum entanglement entropy. Avoids expensive quantum99    state tomography during training.100 101    Parameters102    ----------103    n_qubits : int104        Number of qubits in the simulated quantum system.105    subsystem_a : list of ints or None106        Qubit indices for subsystem A (bipartition).107    """108 109    def __init__(self, n_qubits: int = 4,110                 subsystem_a: Optional[List[int]] = None):111        super().__init__()112        self.n_qubits = n_qubits113        if subsystem_a is None:114            subsystem_a = list(range(n_qubits // 2))115        self.subsystem_a = subsystem_a116 117    def forward(self, attention_weights: torch.Tensor) -> torch.Tensor:118        """119        Estimate entanglement from attention distributions.120 121        Args:122            attention_weights: (batch, heads, seq_len, seq_len)123                Softmax-normalized attention weights.124 125        Returns:126            (batch, heads) — estimated entanglement entropy per head127        """128        eps = 1e-8129        entropy = -torch.sum(130            attention_weights * torch.log(attention_weights + eps),131            dim=-1132        )  # (batch, heads, seq_len)133        return entropy.mean(dim=-1)  # (batch, heads)134 135 136class ClassicalQuantumFallback(nn.Module):137    """138    Classical MLP fallback when PennyLane is unavailable.139 140    Uses sinusoidal activations to mimic quantum rotation gate behavior.141    """142 143    def __init__(self, n_qubits: int = 4, n_layers: int = 2,144                 n_outputs: int = None):145        super().__init__()146        n_outputs = n_outputs or n_qubits147        layers = []148        in_dim = n_qubits149        for _ in range(n_layers):150            layers.extend([151                nn.Linear(in_dim, n_qubits * 2),152                nn.SiLU(),  # Smooth activation like quantum gates153            ])154            in_dim = n_qubits * 2155        layers.append(nn.Linear(in_dim, n_outputs))156        layers.append(nn.Tanh())  # Bound output to [-1, 1] like expectation values157        self.net = nn.Sequential(*layers)158 159    def forward(self, x: torch.Tensor) -> torch.Tensor:160        return self.net(x)161 162 163def create_quantum_embedding(input_dim: int, n_qubits: int = 4,164                              n_layers: int = 2, output_dim: int = None,165                              embedding_type: str = "angle") -> nn.Module:166    """167    Factory for quantum embedding layers.168 169    Args:170        input_dim: Input feature dimension.171        n_qubits: Number of qubits.172        n_layers: Circuit depth.173        output_dim: Output dimension.174        embedding_type: 'angle' or 'amplitude'.175 176    Returns:177        Quantum embedding nn.Module (or classical fallback if no PennyLane).178    """179    output_dim = output_dim or n_qubits180 181    if not HAS_PENNYLANE:182        print("[WARN] PennyLane not installed. Using classical fallback.")183        return nn.Sequential(184            nn.Linear(input_dim, n_qubits),185            ClassicalQuantumFallback(n_qubits, n_layers, output_dim),186            nn.Linear(output_dim, output_dim),187        )188 189    if embedding_type == "angle":190        return nn.Sequential(191            nn.Linear(input_dim, n_qubits),192            QuantumAngleEmbedding(n_qubits, n_layers, output_dim),193        )194    elif embedding_type == "amplitude":195        return nn.Sequential(196            nn.Linear(input_dim, 2 ** n_qubits),197            nn.Softmax(dim=-1),198            # Amplitude embedding would go here199            nn.Linear(2 ** n_qubits, output_dim),200        )201    else:202        raise ValueError(f"Unknown embedding type: {embedding_type}")203