File size: 7,579 Bytes
67ded87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
"""
Unified Transformer Block: Information-Value Driven Hybrid Block.

Integrates:
  - MultiHeadAttention with optional QKSAM and Adaptive KV Cache
  - TokenInformationState vector construction (8D)
  - InformationValueAllocator for dynamic rank, depth, and attention path
  - Nested TTFeedForward with instantaneous rank switching
  - Layer-skipping depth management
"""

import torch
import torch.nn as nn
from typing import Optional, Dict, Tuple
from .attention import MultiHeadAttention
from .tensor_layers import TTFeedForward
from .information_state import TokenInformationState
from .resource_allocator import InformationValueAllocator, AllocationBudget
from .kv_cache import AdaptiveKVCache
from .quantum_backend import QuantumBackend, BackendType


class HybridBlock(nn.Module):
    """
    A single Q-TensorFormer block with closed-loop resource allocation.
    """

    def __init__(
        self,
        d_model: int = 128,
        n_heads: int = 4,
        n_kv_heads: Optional[int] = None,
        ff_multiplier: int = 4,
        tt_rank: int = 8,
        tt_min_rank: int = 2,
        use_quantum: bool = True,
        n_qubits: int = 4,
        backend_type: str = "classical_surrogate",
        dropout: float = 0.1,
        max_seq_len: int = 128,
        hysteresis_tau: float = 0.15,
        default_preset: str = "balanced",
        enable_early_exit: bool = False,
        early_exit_threshold: float = 0.20,
        vocab_size: Optional[int] = None,
    ):
        super().__init__()
        self.d_model = d_model
        self.use_quantum = use_quantum
        self.max_seq_len = max_seq_len
        self.tt_max_rank = tt_rank
        self.n_heads = n_heads
        self.n_kv_heads = n_kv_heads if n_kv_heads is not None else n_heads
        self.enable_early_exit = enable_early_exit
        self.early_exit_threshold = early_exit_threshold

        if enable_early_exit and vocab_size is not None:
            self.early_exit_head = nn.Linear(d_model, vocab_size, bias=False)
        else:
            self.early_exit_head = None

        # Attention sublayer (supports GQA / MQA)
        self.attention = MultiHeadAttention(
            d_model=d_model,
            n_heads=n_heads,
            n_kv_heads=self.n_kv_heads,
            dropout=dropout,
            max_seq_len=max_seq_len,
            use_quantum_kernel=False,
            n_qubits=n_qubits,
            backend_type=backend_type,
        )

        # Layer norms
        self.ln1 = nn.LayerNorm(d_model)
        self.ln2 = nn.LayerNorm(d_model)

        # Token Information State Engine
        self.info_engine = TokenInformationState(d_model=d_model, n_heads=n_heads)

        # Information-Value Resource Allocator
        self.allocator = InformationValueAllocator(
            info_dim=8,
            hidden_dim=32,
            hysteresis_tau=hysteresis_tau,
            default_preset=default_preset,
        )

        # Nested Tensor-Train FFN
        self.tt_ffn = TTFeedForward(
            hidden_dim=d_model,
            ff_multiplier=ff_multiplier,
            rank=tt_rank,
        )

        # Quantum Feature Transformer (for hard tokens routed to quantum feature map)
        self.quantum_backend = QuantumBackend(
            backend_type=backend_type,
            n_qubits=n_qubits,
            n_layers=2,
            d_model=d_model,
        )

        self.dropout = nn.Dropout(dropout)

    def forward(
        self,
        x: torch.Tensor,
        mask: Optional[torch.Tensor] = None,
        kv_cache: Optional[AdaptiveKVCache] = None,
        budget: Optional[AllocationBudget] = None,
        preset: Optional[str] = None,
        force_classical: bool = False,
    ) -> Tuple[torch.Tensor, Dict]:
        """
        Closed-loop block forward pass.

        Flow:
          1. Construct initial information state z_t
          2. Resource Allocator chooses rank, attention mode, depth, KV precision
          3. Attention with chosen mode & KV cache
          4. FFN with dynamically sliced TT cores
          5. Selective quantum enrichment for hard tokens
          6. Residuals and layer outputs
        """
        B, T, D = x.shape
        stats = {}

        # 1. Probe information state
        z_t = self.info_engine(hidden_states=x)

        # 2. Allocate resources based on marginal utility
        decisions, diagnostics = self.allocator(
            z_t,
            budget=budget,
            preset=preset,
            force_classical=force_classical or (not self.use_quantum),
        )

        chosen_rank = decisions["rank"]
        depth_mode = decisions["depth_mode"]
        is_quantum_token = decisions["is_quantum_token"]  # (B, T)
        kv_precision = decisions["kv_precision"]

        stats.update(diagnostics)

        # Handle depth skipping
        if depth_mode == "skip":
            # Zero computation: skip layer via identity residual
            return x, stats

        # Dynamic KV Cache precision adjustment
        if kv_cache is not None:
            kv_cache.set_precision(kv_precision)

        # 3. Attention Sublayer
        normed_x = self.ln1(x)
        # Determine whether any token in sequence requests quantum kernel
        has_quantum_attn = (decisions["attn_mode_idx"] == 2).any().item()

        attn_out, attn_weights = self.attention(
            normed_x,
            mask=mask,
            kv_cache=kv_cache,
            use_quantum=has_quantum_attn and not force_classical,
            return_attn_weights=True,
        )

        x = x + self.dropout(attn_out)

        # Early exit check based on epistemic uncertainty
        if self.enable_early_exit and self.early_exit_head is not None:
            mean_uncertainty = z_t[..., 2].mean().item()
            if mean_uncertainty < self.early_exit_threshold:
                stats["early_exit_triggered"] = True
                stats["early_exit_logits"] = self.early_exit_head(self.ln2(x))
                return x, stats

        # If partial depth mode: skip FFN
        if depth_mode == "partial":
            return x, stats

        # 4. FFN Sublayer with Nested TT rank slicing
        self.tt_ffn.set_rank(chosen_rank)
        normed_ffn_in = self.ln2(x)
        token_ranks = decisions.get("token_ranks", None)
        ffn_out = self.tt_ffn(normed_ffn_in, token_ranks=token_ranks)

        # 5. Quantum feature enhancement on selected hard tokens
        if is_quantum_token.any() and not force_classical:
            q_out, q_meta = self.quantum_backend(normed_ffn_in)
            mask_expanded = is_quantum_token.unsqueeze(-1).float()  # (B, T, 1)
            ffn_out = ffn_out + mask_expanded * q_out

        x = x + self.dropout(ffn_out)

        # Update memory traffic stats
        ffn_traffic = self.tt_ffn.get_memory_traffic()
        stats["memory_traffic_bytes"] = ffn_traffic["total_bytes"]

        return x, stats

    def set_rank(self, rank: int):
        """Manual rank override."""
        self.tt_ffn.set_rank(rank)

    def reset_scheduler(self):
        """Reset allocator stability state."""
        self.allocator.reset_stability_counters()

    @property
    def total_params(self) -> int:
        return sum(p.numel() for p in self.parameters())

    def flops_estimate(self, batch_size: int = 1, seq_len: int = 32) -> Dict[str, int]:
        attn_flops = self.attention.flops(batch_size, seq_len)["total"]
        ffn_flops = self.tt_ffn.flops(batch_size, seq_len)
        return {
            "attention": attn_flops,
            "tt_ffn": ffn_flops,
            "total": attn_flops + ffn_flops,
        }