CoolFace
Modelpublic

Premchan369/Q-TensorFormer

sourceHugging Faceapache-2.0updated 9d agoView on Hugging Face
2likes185downloads
router.py145 linesDownload Raw Back to src
1"""2Quantum Router: Selective Quantum Activation.3 4Only "hard" tokens pass through the quantum circuit.5Decision mechanism: learned linear gate + straight-through estimator.6 7v3 improvements:8  - Sparsity target: ensures target fraction of tokens skip quantum9  - Straight-through gradient for gradient-based learning10  - Sparsity statistics tracking11  - Fallback embedding for bypassed tokens12"""13 14import torch15import torch.nn as nn16import torch.nn.functional as F17 18 19class QuantumRouter(nn.Module):20    """21    Selective quantum activation gate.22 23    Given a batch of token embeddings, computes a per-token24    probability of routing through quantum. Uses straight-through25    estimator: forward pass uses hard binary decisions, backward26    uses soft sigmoid gradient.27 28    Parameters29    ----------30    d_model : int31        Input feature dimension.32    q_input_dim : int33        Dimension expected by quantum circuit (typically n_qubits).34    target_sparsity : float35        Target fraction of tokens that SKIP quantum (0.7 = 70% skip).36    temperature : float37        Softmax temperature for gate decisions (lower = harder).38    """39 40    def __init__(self, d_model: int, q_input_dim: int = 4,41                 target_sparsity: float = 0.7, temperature: float = 1.0):42        super().__init__()43        self.d_model = d_model44        self.q_input_dim = q_input_dim45        self.target_sparsity = target_sparsity46        self.temperature = temperature47 48        # Projection for gate decision49        self.gate_proj = nn.Sequential(50            nn.LayerNorm(d_model),51            nn.Linear(d_model, d_model // 4),52            nn.GELU(),53            nn.Linear(d_model // 4, 1),54        )55 56        # Projection to quantum input dimension57        self.q_proj = nn.Linear(d_model, q_input_dim)58 59        # Statistics60        self.register_buffer("total_tokens", torch.tensor(0, dtype=torch.long))61        self.register_buffer("quantum_tokens", torch.tensor(0, dtype=torch.long))62        self.register_buffer("_ema_sparsity", torch.tensor(target_sparsity))63 64    def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:65        """66        Route tokens selectively through quantum.67 68        Args:69            x: (*batch, seq_len, d_model)70 71        Returns:72            quantum_out: (*batch, seq_len, d_model) — quantum-processed tokens73            mask: (*batch, seq_len) — which tokens went through quantum (bool)74        """75        *batch_dims, seq_len, d_model = x.shape76 77        # Gate decision78        gate_logits = self.gate_proj(x).squeeze(-1)  # (*, seq_len)79        soft_mask = torch.sigmoid(gate_logits / self.temperature)80 81        # Straight-through: hard forward, soft backward82        hard_mask = (soft_mask > 0.5).float()83        mask = hard_mask.detach() + soft_mask - soft_mask.detach()84 85        # Project selected tokens to quantum dimension86        q_input = self.q_proj(x)  # (*, seq_len, q_input_dim)87 88        # TODO: actual quantum circuit call goes here89        # For now: project back to d_model with learned linear layer90        quantum_out = F.gelu(q_input)91        if not hasattr(self, '_q_out_proj'):92            self._q_out_proj = nn.Linear(self.q_input_dim, d_model).to(x.device)93        quantum_out = self._q_out_proj(quantum_out)94 95        # Gate output96        mask_expanded = mask.unsqueeze(-1)  # (*, seq_len, 1)97        output = mask_expanded * quantum_out98 99        # Update statistics100        with torch.no_grad():101            n_tokens = seq_len * max(1, math_prod(batch_dims))102            n_quantum = int(mask_expanded.sum().item())103            self.total_tokens += n_tokens104            self.quantum_tokens += n_quantum105            actual_rate = n_quantum / max(n_tokens, 1)106            self._ema_sparsity.mul_(0.99).add_(107                (1 - actual_rate), alpha=0.01108            )109 110        return output, mask.detach().bool()111 112    @property113    def sparsity(self) -> float:114        """Fraction of tokens that SKIP the quantum circuit."""115        return self._ema_sparsity.item()116 117    @property118    def usage_percent(self) -> float:119        """Fraction of tokens that use the quantum circuit."""120        return 1.0 - self.sparsity121 122    def reset_stats(self):123        self.total_tokens.zero_()124        self.quantum_tokens.zero_()125        self._ema_sparsity.fill_(self.target_sparsity)126 127    def reset_state(self):128        """Full reset for clean evaluation runs."""129        self.reset_stats()130        for m in self.modules():131            if hasattr(m, "reset_parameters"):132                m.reset_parameters()133 134    def extra_repr(self) -> str:135        return (f"d_model={self.d_model}, q_dim={self.q_input_dim}, "136                f"target_sparsity={self.target_sparsity:.1%}")137 138 139def math_prod(iterable):140    """Safe product of iterable."""141    result = 1142    for x in iterable:143        result *= x144    return result145