neuralworm commited on
Commit
757ff71
·
verified ·
1 Parent(s): a7db2f2

Upload folder using huggingface_hub

Browse files
Files changed (7) hide show
  1. README.md +60 -18
  2. config.json +11 -9
  3. configuration_openmythos.py +11 -0
  4. model.safetensors +2 -2
  5. modeling_openmythos.py +25 -0
  6. patch.py +818 -0
  7. px_modules.py +125 -0
README.md CHANGED
@@ -1,26 +1,68 @@
1
- # Gemma-3-270m-it-p2.8 (Phase 5 Master - Geometric Stabilization)
 
 
 
 
 
 
 
 
 
2
 
3
- This is a structural enhancement of the [google/gemma-3-270m-it](https://huggingface.co/google/gemma-3-270m-it) model using the **Phase 5 "Open-Mythos" Architecture**.
4
 
5
- ## Key Features (Phase 5 Update)
6
- - **Phase 5.0 Geometric Stabilization**: Reconciles deep recursion with factual grounding through geometric identity protection.
7
- - **Orthogonal Thinking (OT)**: Recurrent updates are decomposed into parallel (Grounding) and orthogonal (Logic) components. Parallel drift is dampened (0.95x) while orthogonal transformation is preserved (1.05x).
8
- - **Context Preservation (ReadOnlyCache)**: Fixed the "Context Amnesia" bug where recurrent loops lost prompt grounding. All reasoning steps now maintain full attention to the prompt history.
9
- - **Omni-Score Peak: 0.6667**: Pure algorithmic performance (no linguistic steering) matching the best-steered baselines.
10
- - **Zero-Shot Accuracy**: 100% on known traps like `sqrt(16)` and `sqrt(144)`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- ## Usage
13
  ```python
14
- from transformers import AutoModelForCausalLM, AutoTokenizer
 
 
 
15
 
16
- model_id = "neuralworm/gemma-3-270m-it-p2.8"
17
- tokenizer = AutoTokenizer.from_pretrained(model_id)
18
- model = AutoModelForCausalLM.from_pretrained(model_id, trust_remote_code=True, torch_dtype="auto")
 
 
 
 
19
 
20
- inputs = tokenizer("Q: What is the square root of 144? A:", return_tensors="pt")
21
- outputs = model.generate(**inputs, max_new_tokens=10)
22
- print(tokenizer.decode(outputs[0]))
 
 
 
23
  ```
24
 
25
- ## Scientific Context
26
- Phase 5.0 introduces **Geometric Identity Protection**. By ensuring that the model's factual grounding (parallel to the anchor) is protected during logical transformation (orthogonal to the anchor), we enable deep multi-step reasoning without semantic collapse. This is the first pure structural mod to achieve a 0.6667 score on the English Omni-Benchmark without any linguistic prompting hacks.
 
 
 
 
1
+ ---
2
+ license: gemma
3
+ base_model: google/gemma-3-270m-it
4
+ tags:
5
+ - gemma-3
6
+ - open-mythos
7
+ - recursive-transformer
8
+ - cognitive-routing
9
+ - experimental
10
+ ---
11
 
12
+ # Gemma-3 270M-IT "Open-Mythos" (Phase 2.8)
13
 
14
+ This is an experimental architectural modification of the **Google Gemma-3 270M-IT** base model. It implements the "Open-Mythos" (PX) architecture, introducing **Recursive Computational Headroom** and **Fluid Gaussian Cognitive Routing**.
15
+
16
+ ## ⚠️ Transparency Notice
17
+ **This is not a standard fine-tune.** It is a structural mod that changes how the transformer processes tokens at inference time.
18
+ - **Base Model:** [google/gemma-3-270m-it](https://huggingface.co/google/gemma-3-270m-it)
19
+ - **Modifications:** Runtime patching of the forward pass to allow for recursive layer execution and dynamic cognitive routing.
20
+
21
+ ## 🚀 Key Innovations
22
+
23
+ ### 1. Recursive Computational Headroom (PX)
24
+ Unlike standard transformers that pass through each layer once, Open-Mythos allows the model to "re-read" and "think" through specific layers (L5-L12) multiple times. This effectively increases the depth of the model for complex tasks without adding new parameters.
25
+
26
+ ### 2. Fluid Gaussian Cognitive Routing
27
+ The model dynamically analyzes the "cognitive signature" (Kurtosis) of each prompt during the prefill phase. Based on this signature, it automatically routes the task through a specific "Cognitive Envelope":
28
+ - **Math Mode:** Optimized for numerical precision (L5-L11).
29
+ - **Logic Mode:** Optimized for multi-step reasoning (L8-L12).
30
+ - **Creative Mode:** Optimized for semantic drift and metaphor (L10-L16).
31
+ - **Synthesis Mode:** Optimized for extraction and summarization (L6-L14).
32
+
33
+ Transitions between these modes are **continuous and fluid** using Gaussian blending, allowing the model to self-determine its reasoning path.
34
+
35
+ ### 3. Numerical Stability (RMSNorm Fix)
36
+ Implements a surgical fix for the Gemma-3 RMSNorm scaling (`1.0 + weight`) to prevent signal collapse during deep recursion, ensuring vocabulary integrity across high-entropy generations.
37
+
38
+ ## 💻 Usage
39
+
40
+ To use this model, you **must** set `trust_remote_code=True` because it uses custom modeling code to implement the recursive logic.
41
 
 
42
  ```python
43
+ import torch
44
+ from transformers import AutoTokenizer, AutoModelForCausalLM
45
+
46
+ repo_id = "neuralworm/gemma-3-270m-it-p2.8"
47
 
48
+ tokenizer = AutoTokenizer.from_pretrained(repo_id)
49
+ model = AutoModelForCausalLM.from_pretrained(
50
+ repo_id,
51
+ torch_dtype=torch.bfloat16,
52
+ device_map="auto",
53
+ trust_remote_code=True
54
+ )
55
 
56
+ prompt = "Sally has 3 brothers. Each brother has 2 sisters. How many sisters does Sally have?"
57
+ chat = [{"role": "user", "content": prompt}]
58
+ inputs = tokenizer(tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True), return_tensors="pt").to(model.device)
59
+
60
+ outputs = model.generate(**inputs, max_new_tokens=100)
61
+ print(tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
62
  ```
63
 
64
+ ## 📊 Performance
65
+ Open-Mythos (Phase 2.8) significantly improves zero-shot performance on "Logical Traps" and multi-step reasoning compared to the pure 270M baseline, while remaining multimodal-ready and regression-free for standard NLP tasks.
66
+
67
+ ---
68
+ *Created as part of the Open-Mythos Research Project (2026).*
config.json CHANGED
@@ -1,13 +1,8 @@
1
  {
2
  "_sliding_window_pattern": 6,
3
  "architectures": [
4
- "Gemma3ForCausalLM"
5
  ],
6
- "auto_map": {
7
- "AutoConfig": "configuration_gemma3.Gemma3TextConfig",
8
- "AutoModel": "modeling_gemma3.Gemma3TextModel",
9
- "AutoModelForCausalLM": "modeling_gemma3.Gemma3ForCausalLM"
10
- },
11
  "attention_bias": false,
12
  "attention_dropout": 0.0,
13
  "attn_logit_softcapping": null,
@@ -41,11 +36,13 @@
41
  "full_attention"
42
  ],
43
  "max_position_embeddings": 32768,
44
- "model_type": "gemma3_text",
45
  "num_attention_heads": 4,
46
  "num_hidden_layers": 18,
47
  "num_key_value_heads": 1,
48
  "pad_token_id": 0,
 
 
49
  "query_pre_attn_scalar": 256,
50
  "rms_norm_eps": 1e-06,
51
  "rope_parameters": {
@@ -63,5 +60,10 @@
63
  "transformers_version": "5.9.0",
64
  "use_bidirectional_attention": false,
65
  "use_cache": true,
66
- "vocab_size": 262144
67
- }
 
 
 
 
 
 
1
  {
2
  "_sliding_window_pattern": 6,
3
  "architectures": [
4
+ "OpenMythosForCausalLM"
5
  ],
 
 
 
 
 
6
  "attention_bias": false,
7
  "attention_dropout": 0.0,
8
  "attn_logit_softcapping": null,
 
36
  "full_attention"
37
  ],
38
  "max_position_embeddings": 32768,
39
+ "model_type": "open_mythos",
40
  "num_attention_heads": 4,
41
  "num_hidden_layers": 18,
42
  "num_key_value_heads": 1,
43
  "pad_token_id": 0,
44
+ "px_gamma": 0.1,
45
+ "px_routing_mode": "adaptive",
46
  "query_pre_attn_scalar": 256,
47
  "rms_norm_eps": 1e-06,
48
  "rope_parameters": {
 
60
  "transformers_version": "5.9.0",
61
  "use_bidirectional_attention": false,
62
  "use_cache": true,
63
+ "vocab_size": 262144,
64
+ "auto_map": {
65
+ "AutoConfig": "configuration_openmythos.OpenMythosConfig",
66
+ "AutoModelForCausalLM": "modeling_openmythos.OpenMythosForCausalLM",
67
+ "AutoModel": "modeling_openmythos.OpenMythosModel"
68
+ }
69
+ }
configuration_openmythos.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+ from transformers.models.gemma3.configuration_gemma3 import Gemma3TextConfig
3
+
4
+ class OpenMythosConfig(Gemma3TextConfig):
5
+ model_type = "open_mythos"
6
+
7
+ def __init__(self, **kwargs):
8
+ super().__init__(**kwargs)
9
+ # Default config for 270m
10
+ self.px_routing_mode = kwargs.get("px_routing_mode", "adaptive")
11
+ self.px_gamma = kwargs.get("px_gamma", 0.10)
model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:0479a42e30f1e97b1054b7b3c5c18a75b61a0757b42b8b4d2dea6b2ac87c163c
3
- size 537098012
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:700b710a9a99c295ed546647aa81cacf9f81f4c573ea2be613a0e2517a44afab
3
+ size 536223056
modeling_openmythos.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers.models.gemma3.modeling_gemma3 import Gemma3ForCausalLM, Gemma3Model
2
+ from .configuration_openmythos import OpenMythosConfig
3
+ from .patch import apply_px_patch
4
+
5
+ class OpenMythosForCausalLM(Gemma3ForCausalLM):
6
+ config_class = OpenMythosConfig
7
+
8
+ def __init__(self, config):
9
+ super().__init__(config)
10
+ # We apply the patch immediately after initialization
11
+ # Extract default parameters from the config if they exist
12
+ routing_mode = getattr(config, "px_routing_mode", "adaptive")
13
+ gamma = getattr(config, "px_gamma", 0.10)
14
+
15
+ # We MUST start the initial prelude at Layer 5 so the Kurtosis probe
16
+ # (which is calibrated for L5) reads the correct tensor.
17
+ # The router will fast-forward the remaining layers if dynamic_start > 5.
18
+ apply_px_patch(self, recur_start=5, recur_end=12, routing_mode=routing_mode, gamma=gamma)
19
+
20
+ # For safety, if they instantiate the base model directly:
21
+ class OpenMythosModel(Gemma3Model):
22
+ config_class = OpenMythosConfig
23
+
24
+ def __init__(self, config):
25
+ super().__init__(config)
patch.py ADDED
@@ -0,0 +1,818 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ gemma3-px — Surgical Patch (Phase 10.0)
3
+ =========================================
4
+ Implements Recursive State Memory (RSM) and Hyper-Fluid Routing (HFR).
5
+ RSM allows the model to 'see' its own previous thinking states during recursion.
6
+ """
7
+
8
+ import types
9
+ import torch
10
+ import torch.nn as nn
11
+ import os
12
+ import json
13
+ import datetime
14
+ from typing import Optional
15
+
16
+ from .px_modules import LTIInjection, ADCInjection, StabilityMonitor
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # p10.0: Recursive State Memory (RSM)
20
+ # ---------------------------------------------------------------------------
21
+
22
+ class RecursiveMemoryCache:
23
+ """
24
+ Extends ReadOnlyCache by injecting previous thinking steps into the
25
+ self-attention key/value streams.
26
+ """
27
+ def __init__(self, real_cache, thought_history=None, layer_types=None, read_only=False, expected_len=0):
28
+ self.__dict__["_real"] = real_cache
29
+ self.__dict__["_thoughts"] = thought_history or []
30
+ self.__dict__["_layer_types"] = layer_types or []
31
+ self.__dict__["_read_only"] = read_only
32
+ self.__dict__["_expected_len"] = expected_len
33
+
34
+ def __getattr__(self, name):
35
+ return getattr(self._real, name)
36
+
37
+ def update(self, key_states, value_states, layer_idx, cache_kwargs=None):
38
+ # 1. Base Update (Functional if read_only)
39
+ if self._read_only:
40
+ past_k, past_v = None, None
41
+ # Try older DynamicCache style
42
+ if hasattr(self._real, "key_cache") and len(self._real.key_cache) > layer_idx:
43
+ past_k = self._real.key_cache[layer_idx]
44
+ past_v = self._real.value_cache[layer_idx]
45
+ # Try newer Cache object style (transformers 4.45+)
46
+ elif hasattr(self._real, "layers") and len(self._real.layers) > layer_idx:
47
+ layer = self._real.layers[layer_idx]
48
+ # DynamicLayer / StaticLayer
49
+ if hasattr(layer, "keys") and layer.keys is not None:
50
+ past_k = layer.keys
51
+ past_v = layer.values
52
+ # SinkCache / etc might have different names? No, usually .keys
53
+
54
+ if past_k is None:
55
+ past_k = torch.empty(0, device=key_states.device, dtype=key_states.dtype)
56
+ past_v = torch.empty(0, device=value_states.device, dtype=value_states.dtype)
57
+
58
+ # If past_k already has the expected length, it means this layer was
59
+ # already updated for the current token(s) in a previous iteration
60
+ # of the same reasoning loop.
61
+ if past_k.numel() > 0 and past_k.shape[-2] == self._expected_len:
62
+ res_k, res_v = past_k, past_v
63
+ else:
64
+ res_k = torch.cat([past_k, key_states], dim=-2)
65
+ res_v = torch.cat([past_v, value_states], dim=-2)
66
+
67
+ # print(f" [DEBUG-CACHE] L{layer_idx} RO=True | past={past_k.shape[-2] if past_k.numel()>0 else 0} | cur={key_states.shape[-2]} | res={res_k.shape[-2]} | exp={self._expected_len}")
68
+ else:
69
+ res_k, res_v = self._real.update(key_states, value_states, layer_idx, cache_kwargs)
70
+ # print(f" [DEBUG-CACHE] L{layer_idx} RO=False | res={res_k.shape[-2]} | exp={self._expected_len}")
71
+
72
+ # 2. Phase 14.6: Soft-RSM (Semantic Blending)
73
+ is_full = self._layer_types and self._layer_types[layer_idx] == "full_attention"
74
+
75
+ if self._thoughts and layer_idx >= 6 and is_full:
76
+ B, H_kv, T_res, HD = res_k.shape
77
+ T_curr = key_states.shape[-2]
78
+ alpha = 0.15
79
+
80
+ # Phase 14.7: Triangular Weighting (Emphasize the 'reasoning peak')
81
+ n_t = len(self._thoughts[-6:])
82
+ if n_t > 2:
83
+ weights = torch.cat([
84
+ torch.linspace(0.4, 1.0, n_t//2, device=res_k.device),
85
+ torch.linspace(1.0, 0.6, n_t - n_t//2, device=res_k.device)
86
+ ])
87
+ t_raw = (torch.stack(self._thoughts[-6:]) * weights.view(-1, 1, 1, 1)).sum(dim=0) / weights.sum()
88
+ else:
89
+ t_raw = torch.stack(self._thoughts).mean(dim=0)
90
+
91
+ D = t_raw.shape[2]
92
+
93
+ # Project thought to Head Dim (SDA)
94
+ t_flat = t_raw.mean(dim=1, keepdim=True) # (B, 1, D)
95
+ t_proj = torch.nn.functional.interpolate(t_flat, size=HD, mode='linear', align_corners=False)
96
+ t_k = t_proj.unsqueeze(1) # (B, 1, 1, HD)
97
+ t_v = -t_k
98
+
99
+ # Blend into the LAST token(s) of the result
100
+ # Use in-place only if not read_only to avoid side effects on cache
101
+ if self._read_only:
102
+ res_k = res_k.clone()
103
+ res_v = res_v.clone()
104
+
105
+ res_k[:, :, -T_curr:, :] = (1.0 - alpha) * res_k[:, :, -T_curr:, :] + alpha * t_k
106
+ res_v[:, :, -T_curr:, :] = (1.0 - alpha) * res_v[:, :, -T_curr:, :] + alpha * t_v
107
+
108
+ return res_k, res_v
109
+
110
+ # ---------------------------------------------------------------------------
111
+
112
+ def remove_px_patch(model) -> None:
113
+ from transformers.models.gemma3.modeling_gemma3 import Gemma3TextModel
114
+ text_model = (model.model if hasattr(model, "model") else model)
115
+ if hasattr(text_model, "_px_config"):
116
+ text_model.forward = types.MethodType(
117
+ Gemma3TextModel.forward, text_model
118
+ )
119
+ del text_model._px_injection
120
+ del text_model._px_config
121
+ print("[gemma3-px] Patch removed.")
122
+
123
+ def _resolve_text_model(model):
124
+ if hasattr(model, "model") and hasattr(model.model, "layers"):
125
+ return model.model
126
+ return model
127
+
128
+ # ---------------------------------------------------------------------------
129
+
130
+ def _px_forward(
131
+ self,
132
+ input_ids=None,
133
+ attention_mask=None,
134
+ position_ids=None,
135
+ past_key_values=None,
136
+ inputs_embeds=None,
137
+ use_cache=None,
138
+ **kwargs,
139
+ ):
140
+ from transformers.cache_utils import DynamicCache
141
+ from transformers.masking_utils import create_causal_mask, create_sliding_window_causal_mask
142
+
143
+ if (input_ids is None) ^ (inputs_embeds is not None):
144
+ raise ValueError("Specify exactly one of input_ids or inputs_embeds.")
145
+
146
+ if inputs_embeds is None:
147
+ # Multimodal resolution (Phase 17.7)
148
+ if hasattr(self, "embed_tokens"):
149
+ inputs_embeds = self.embed_tokens(input_ids)
150
+ elif hasattr(self, "language_model"):
151
+ inputs_embeds = self.language_model.model.embed_tokens(input_ids)
152
+ elif hasattr(self, "model") and hasattr(self.model, "embed_tokens"):
153
+ inputs_embeds = self.model.embed_tokens(input_ids)
154
+ else:
155
+ # Last resort: search for embed_tokens in children
156
+ embedder = None
157
+ for name, module in self.named_modules():
158
+ if "embed_tokens" in name:
159
+ embedder = module
160
+ break
161
+ if embedder:
162
+ inputs_embeds = embedder(input_ids)
163
+ else:
164
+ raise AttributeError(f"Could not find embed_tokens in model type {type(self)}. Available: {dir(self)[:20]}...")
165
+
166
+ if use_cache and past_key_values is None:
167
+ past_key_values = DynamicCache(config=self.config)
168
+
169
+ # Phase 14.8: Initial sequence length tracking
170
+ past_seen = past_key_values.get_seq_length() if past_key_values is not None else 0
171
+ expected_len = past_seen + inputs_embeds.shape[1]
172
+
173
+ # print(f"[DEBUG-PX] Type={type(past_key_values)} seen={past_seen} cur={inputs_embeds.shape[1]} exp={expected_len}")
174
+
175
+ if position_ids is None:
176
+ position_ids = (
177
+ torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device)
178
+ + past_seen
179
+ ).unsqueeze(0)
180
+
181
+ # Resolve config for masking (Phase 17.7 multimodal fix)
182
+ mask_config = self.config
183
+ if hasattr(mask_config, "text_config"):
184
+ mask_config = mask_config.text_config
185
+
186
+ if not isinstance(attention_mask, dict):
187
+ mk = dict(
188
+ config=mask_config,
189
+ inputs_embeds=inputs_embeds,
190
+ attention_mask=attention_mask,
191
+ past_key_values=past_key_values,
192
+ position_ids=position_ids,
193
+ )
194
+ causal_mask_mapping = {
195
+ "full_attention": create_causal_mask(**mk),
196
+ "sliding_attention": create_sliding_window_causal_mask(**mk),
197
+ }
198
+ else:
199
+ causal_mask_mapping = attention_mask
200
+
201
+ hidden_states = inputs_embeds
202
+ position_embeddings = {}
203
+ for layer_type in set(mask_config.layer_types):
204
+ position_embeddings[layer_type] = self.rotary_emb(
205
+ hidden_states, position_ids, layer_type
206
+ )
207
+
208
+ cfg = self._px_config
209
+ updated_layers = set() # Phase 14.9: Global visit tracker for this forward pass
210
+
211
+ # ── 1. PRELUDE ──────────────────────────────────────────────────────────
212
+ for i in range(cfg["prelude_end"]):
213
+ updated_layers.add(i)
214
+ layer_out = self.layers[i](
215
+ hidden_states,
216
+ attention_mask=causal_mask_mapping[mask_config.layer_types[i]],
217
+ position_embeddings=position_embeddings[mask_config.layer_types[i]],
218
+ position_ids=position_ids,
219
+ past_key_values=past_key_values,
220
+ **kwargs,
221
+ )
222
+ hidden_states = layer_out[0] if isinstance(layer_out, (tuple, list)) else layer_out
223
+
224
+ # ── 1.5 META-SELECTOR (Phase 28 Fluid) ───────────────────────────────────
225
+ dynamic_start = cfg["recur_start"]
226
+ dynamic_end = cfg["recur_end"]
227
+ dynamic_hub = cfg.get("bimodal_hub", cfg["recur_start"])
228
+ num_layers = len(self.layers)
229
+
230
+ if cfg.get("routing_mode") == "adaptive":
231
+ if inputs_embeds.shape[1] > 1:
232
+ # Prefill phase: Measure Kurtosis at Layer 5 of the last token
233
+ h_probe = hidden_states[0, -1, :].to(torch.float32)
234
+ variance = torch.var(h_probe).item()
235
+ kurtosis = (torch.mean((h_probe - torch.mean(h_probe))**4) / (variance**2)).item() if variance > 0 else 0
236
+ self._task_kurtosis = kurtosis
237
+ if os.environ.get("DEBUG_ROUTING") == "1": print(f"[Router] Prefill K={kurtosis:.1f}")
238
+
239
+ kurtosis = getattr(self, "_task_kurtosis", 300) # Default to Logic if missing
240
+
241
+ import math
242
+ if num_layers < 20: # 270M Model (Kurtosis is task-separable)
243
+ # Continuous Fluid Gaussian Blending of the 5 Zones
244
+ w_m = math.exp(-((kurtosis - 200)**2) / (2 * 25**2)) # Math
245
+ w_la = math.exp(-((kurtosis - 275)**2) / (2 * 15**2)) # Logic-A
246
+ w_cr = math.exp(-((kurtosis - 298)**2) / (2 * 8**2)) # Creative
247
+ w_lb = math.exp(-((kurtosis - 310)**2) / (2 * 8**2)) # Logic-B
248
+ w_sy = math.exp(-((kurtosis - 325)**2) / (2 * 20**2)) # Synthesis
249
+
250
+ W = w_m + w_la + w_cr + w_lb + w_sy + 1e-9
251
+
252
+ d_start = (w_m*5 + w_la*8 + w_cr*10 + w_lb*8 + w_sy*6) / W
253
+ d_end = (w_m*11 + w_la*12 + w_cr*16 + w_lb*14 + w_sy*14) / W
254
+ d_hub = (w_m*7 + w_la*11 + w_cr*14 + w_lb*11 + w_sy*10) / W
255
+ d_loops = (w_m*8 + w_la*8 + w_cr*6 + w_lb*10 + w_sy*8) / W
256
+
257
+ dynamic_start = max(1, int(round(d_start)))
258
+ dynamic_end = min(num_layers - 1, int(round(d_end)))
259
+ dynamic_hub = int(round(d_hub))
260
+ cfg["n_loops"] = max(2, int(round(d_loops)))
261
+ zone_name = f"Fluid-Blended (K={kurtosis:.1f})"
262
+ else:
263
+ # 1B and 4B Models (Scale-Invariant Omni Zone)
264
+ # They have enough capacity to hold both semantics without smearing
265
+ dynamic_start = int(num_layers * 0.38)
266
+ dynamic_end = int(num_layers * 0.76)
267
+ dynamic_hub = int(num_layers * 0.61)
268
+ cfg["n_loops"] = 6
269
+ zone_name = "Omni-Scale"
270
+
271
+ # Only print routing decision once per token during generation
272
+ if inputs_embeds.shape[1] == 1 and os.environ.get("DEBUG_ROUTING") == "1":
273
+ print(f"[Router] {zone_name} -> L{dynamic_start}-L{dynamic_end} (Loops: {cfg['n_loops']}, Hub: {dynamic_hub})")
274
+
275
+ # Fast-forward prelude if needed
276
+ for i in range(cfg["prelude_end"], dynamic_start):
277
+ updated_layers.add(i)
278
+ layer_out = self.layers[i](
279
+ hidden_states,
280
+ attention_mask=causal_mask_mapping[mask_config.layer_types[i]],
281
+ position_embeddings=position_embeddings[mask_config.layer_types[i]],
282
+ position_ids=position_ids,
283
+ past_key_values=past_key_values,
284
+ **kwargs,
285
+ )
286
+ hidden_states = layer_out[0] if isinstance(layer_out, (tuple, list)) else layer_out
287
+
288
+ # ── 2. REASONING ZONE (Phase 10.0) ──────────────────────────────────────
289
+ e_static = hidden_states.clone()
290
+
291
+ # 2.A: Intuition Pass
292
+ trans_out = hidden_states
293
+ for i_layer in range(dynamic_start, dynamic_end):
294
+ l_type = mask_config.layer_types[i_layer]
295
+ updated_layers.add(i_layer)
296
+ layer_out = self.layers[i_layer](
297
+ trans_out,
298
+ attention_mask=causal_mask_mapping[l_type],
299
+ position_embeddings=position_embeddings[l_type],
300
+ position_ids=position_ids,
301
+ past_key_values=past_key_values,
302
+ **kwargs,
303
+ )
304
+ trans_out = layer_out[0] if isinstance(layer_out, (tuple, list)) else layer_out
305
+ # if past_key_values is not None:
306
+ # print(f" [DEBUG-PX-DIR] {dir(past_key_values)}")
307
+
308
+ h_baseline = trans_out
309
+
310
+ # Phase 14.5: ETR (Entropy Triggered Recursion)
311
+ # Estimate 'confidence' from the last layer's norm change or simpler:
312
+ # We only run recursion if the intuition pass wasn't 'perfectly' stable.
313
+ # Note: h_baseline is already computed.
314
+
315
+ # 2.B: Hyper-Fluid Routing & Recursive Memory
316
+ n_loops = cfg.get("n_loops", 2)
317
+
318
+ # Phase 14.5: ETR (Entropy Triggered Recursion)
319
+ phi_intuition = StabilityMonitor.calculate_phi(h_baseline, hidden_states).mean().item()
320
+
321
+ # Phase 14.7: Gamma-Damping instead of loop scaling
322
+ current_gamma = cfg.get("gamma", 0.08)
323
+
324
+ # Phase 15.7: Adversarial Reflector (AR) - Phase 16.3 Refinement
325
+ # Detect high-order traps and use Anchor Reflection.
326
+ e_reflector = e_static
327
+ is_trap_candidate = False
328
+
329
+ if position_ids[0, 0] > 0:
330
+ h_base_f32 = h_baseline.to(torch.float32)
331
+ h_norm_var = torch.var(h_base_f32.norm(dim=-1))
332
+ if h_norm_var > 0.05:
333
+ is_trap_candidate = True
334
+ # Phase 16.3: Anchor Reflection (Reflect intuition across static anchor)
335
+ # Safe FP32 for FP16 models
336
+ e_stat_f32 = e_static.to(torch.float32)
337
+ h_base_f32 = h_baseline.to(torch.float32)
338
+ e_ref_f32 = 2.0 * e_stat_f32 - h_base_f32
339
+ e_ref_f32 = e_ref_f32 * (e_stat_f32.norm() / (e_ref_f32.norm() + 1e-6))
340
+ e_reflector = e_ref_f32.to(e_static.dtype)
341
+
342
+ if phi_intuition > 0.9999 and not is_trap_candidate:
343
+ current_gamma *= 0.1
344
+ elif phi_intuition > 0.999:
345
+ current_gamma *= 0.5
346
+
347
+ # Phase 25: Sigmoid-Annealed Orthogonal Recovery (SAOR)
348
+ # -----------------------------------------------------------------------
349
+ # Using a Sigmoid curve for Gamma to allow a sharp "Phase Transition"
350
+ # from exploration (high energy) to grounding (low energy).
351
+ # Plus: Orthogonal Reinforcement to protect logical drift.
352
+
353
+ base_gamma = current_gamma
354
+ bimodal_hub_start = cfg.get("bimodal_hub", 11)
355
+
356
+ path_taken = []
357
+ thought_history = []
358
+ avg_phi_explore = 1.0
359
+ exploration_steps = 0
360
+ telemetry_steps = []
361
+
362
+ # Context dims
363
+ B, T_curr = hidden_states.shape[0], hidden_states.shape[1]
364
+ HD = getattr(self.config, "head_dim", 256)
365
+
366
+ if n_loops > 1:
367
+ h_exp = e_reflector.clone() # Use Reflected Anchor
368
+ current_layer = dynamic_start
369
+ max_steps = (dynamic_end - dynamic_start) * n_loops * 3
370
+ phis = []
371
+
372
+ stability_counter = 0
373
+ layer_visits = {i: 0 for i in range(5, 18)}
374
+
375
+ # Initialize active bounds
376
+ active_start = dynamic_start
377
+ active_end = dynamic_end
378
+
379
+ while current_layer < active_end and exploration_steps < max_steps:
380
+ # --- PHASE 26: INFINITE REFLECTION (IR) ---
381
+ # Center of the thinking process (0.5)
382
+ t_norm = exploration_steps / max_steps
383
+
384
+ # --- PHASE 28: TEMPORAL COGNITIVE ROUTING (TCR) ---
385
+ # If we are in the Trap/Logic-A zone (kurtosis ~293), shift the cognitive window over time
386
+ # Phase 1 (0-33%): Deep Logic (L8-L14) to understand the trap
387
+ # Phase 2 (33-66%): Math (L5-L11) to calculate
388
+ # Phase 3 (66-100%): Logic (L8-L12) to synthesize
389
+ active_start = dynamic_start
390
+ active_end = dynamic_end
391
+ if getattr(self, "_task_kurtosis", 300) > 280 and getattr(self, "_task_kurtosis", 300) < 305:
392
+ if t_norm < 0.33:
393
+ active_start = 8
394
+ active_end = 14
395
+ elif t_norm < 0.66:
396
+ active_start = 5
397
+ active_end = 11
398
+ else:
399
+ active_start = 8
400
+ active_end = 12
401
+
402
+ # Sigmoid transition: sharp drop in energy after 50% of thinking
403
+ k_steep = 12.0
404
+ energy_factor = 1.0 - (1.0 / (1.0 + torch.exp(torch.tensor(-k_steep * (t_norm - 0.5))))).item()
405
+ # Gamma(t) transitions from ~1.5x base to ~0.5x base
406
+ current_gamma = base_gamma * (0.5 + energy_factor)
407
+
408
+ # Phase 26: Hub Oscillation. The hub moves back and forth to 'shake' the logic.
409
+ oscillation = 1 if (exploration_steps % 4 < 2) else -1
410
+ bimodal_hub = min(active_end - 1, max(active_start, int(dynamic_hub + (t_norm * 2) + oscillation)))
411
+ # --------------------------------------
412
+
413
+ h_prev = h_exp.clone()
414
+
415
+ # Safe layer visit tracking
416
+ if current_layer not in layer_visits: layer_visits[current_layer] = 0
417
+ layer_visits[current_layer] += 1
418
+
419
+ # Phase 14.7: Surgical Cache Security
420
+ is_first_visit = current_layer not in updated_layers
421
+ if is_first_visit:
422
+ updated_layers.add(current_layer)
423
+
424
+ # Phase 15.9: Sensory Persistence (SP)
425
+ # Re-inject pure sensory data every few steps to prevent grounding decay.
426
+ if exploration_steps % 6 == 0 and exploration_steps > 0:
427
+ h_exp = 0.90 * h_exp + 0.10 * e_static
428
+ path_taken.append("SENSORY_REFRESH")
429
+
430
+ # Phase 10.0: Memory-Augmented Cache wrapper
431
+ current_past = RecursiveMemoryCache(
432
+ past_key_values,
433
+ thought_history,
434
+ layer_types=mask_config.layer_types,
435
+ read_only=not is_first_visit,
436
+ expected_len=expected_len
437
+ ) if past_key_values is not None else None
438
+
439
+ # Execute layer
440
+ l_type = mask_config.layer_types[current_layer]
441
+ layer_out = self.layers[current_layer](
442
+ h_exp,
443
+ attention_mask=causal_mask_mapping[l_type],
444
+ position_embeddings=position_embeddings[l_type],
445
+ position_ids=position_ids,
446
+ past_key_values=current_past,
447
+ **kwargs,
448
+ )
449
+ trans_out = layer_out[0] if isinstance(layer_out, (tuple, list)) else layer_out
450
+
451
+ # --- PHASE 25.1: RECURSIVE BELIEF ANCHOR (RBA) ---
452
+ # Update the anchor slightly with recent thoughts to carry over logic
453
+ if len(thought_history) > 2:
454
+ # Use a sliding window average of thoughts
455
+ recent_avg = torch.stack(thought_history[-3:]).mean(dim=0)
456
+ e_dynamic = 0.85 * e_reflector + 0.15 * recent_avg
457
+ else:
458
+ e_dynamic = e_reflector
459
+ # --------------------------------------------------
460
+
461
+ # Apply LTI Injection with Dynamic Anchor
462
+ e_norm = self._px_injection.input_norm(e_dynamic.to(torch.float32)).to(trans_out.dtype)
463
+ h_exp = trans_out + current_gamma * (e_norm - h_prev)
464
+
465
+ # --- PHASE 26: REFLECTION FLIPPING (RF) ---
466
+ h_f32 = h_exp.to(torch.float32)
467
+ e_f32 = e_dynamic.to(torch.float32)
468
+ dot_he = (h_f32 * e_f32).sum(dim=-1, keepdim=True)
469
+ dot_ee = (e_f32 * e_f32).sum(dim=-1, keepdim=True)
470
+ proj = (dot_he / (dot_ee + 1e-6)) * e_f32
471
+ ortho = h_f32 - proj
472
+
473
+ # Oscillate the logic vector to avoid local minima
474
+ flip_force = 0.10 * energy_factor * (1.0 if (exploration_steps % 2 == 0) else -1.0)
475
+ h_exp = (proj + (1.0 + flip_force) * ortho).to(h_exp.dtype)
476
+ # ------------------------------------------
477
+
478
+ # Self-Observation
479
+ phi_tensor = StabilityMonitor.calculate_phi(h_exp, h_prev)
480
+ phi = phi_tensor.item()
481
+
482
+ # Merged Telemetry Step
483
+ telemetry_data = {
484
+ "step": exploration_steps,
485
+ "layer": int(current_layer),
486
+ "phi": float(phi),
487
+ "gamma": float(current_gamma),
488
+ "energy": float(energy_factor),
489
+ "rba_active": len(thought_history) > 2,
490
+ "hub": int(bimodal_hub)
491
+ }
492
+
493
+ # Phase 26: Dynamic Loop Extension
494
+ # If phi is low (< 0.85), allow model to think longer than max_steps
495
+ if phi < 0.85 and exploration_steps == max_steps - 1 and max_steps < 64:
496
+ max_steps += (dynamic_end - dynamic_start) # Add 1 full loop
497
+ # ----------------------------------
498
+
499
+ step_info = {
500
+ "step": exploration_steps,
501
+ "layer": int(current_layer),
502
+ "phi": float(phi),
503
+ "decision": None
504
+ }
505
+
506
+ phis.append(phi)
507
+ path_label = f"L{current_layer}({phi:.2f})"
508
+ path_taken.append(path_label)
509
+
510
+ # Phase 12.5/18: Universal Bimodal Path Selection
511
+ bimodal_threshold = min(0.995, 1.0 - (0.05 * current_gamma)) # Scaled trigger
512
+ if current_layer == bimodal_hub and phi < bimodal_threshold:
513
+ step_info["decision"] = "BIMODAL_FORK"
514
+ path_taken.append("BIMODAL_FORK")
515
+
516
+ # Branch A (Standard)
517
+ h_a = h_exp.clone()
518
+
519
+ # Branch B (High-Entropy DTEC)
520
+ jitter_boost = 1.0 + (stability_counter * 0.5)
521
+ hub_entropy = max(0.01, 1.0 - phi) * 0.5 * jitter_boost # Increased for bf16 visibility
522
+ h_b = h_exp.to(torch.float32) + torch.randn_like(h_exp, dtype=torch.float32) * hub_entropy
523
+ h_b = h_b.to(h_exp.dtype)
524
+
525
+ # Lookahead to NEXT layer
526
+ next_l = current_layer + 1
527
+ if next_l < len(self.layers):
528
+ nl_type = mask_config.layer_types[next_l]
529
+ # Phase 14.5: Use Functional Read-Only Cache for lookahead
530
+ lookahead_past = RecursiveMemoryCache(
531
+ past_key_values,
532
+ thought_history,
533
+ layer_types=mask_config.layer_types,
534
+ read_only=True,
535
+ expected_len=expected_len
536
+ ) if past_key_values is not None else None
537
+
538
+ out_a = self.layers[next_l](
539
+ h_a, attention_mask=causal_mask_mapping[nl_type],
540
+ position_embeddings=position_embeddings[nl_type],
541
+ position_ids=position_ids, past_key_values=lookahead_past, **kwargs
542
+ )[0]
543
+ phi_a = StabilityMonitor.calculate_phi(out_a, h_a).item()
544
+
545
+ out_b = self.layers[next_l](
546
+ h_b, attention_mask=causal_mask_mapping[nl_type],
547
+ position_embeddings=position_embeddings[nl_type],
548
+ position_ids=position_ids, past_key_values=lookahead_past, **kwargs
549
+ )[0]
550
+ phi_b = StabilityMonitor.calculate_phi(out_b, h_b).item()
551
+
552
+ if phi_b >= phi_a:
553
+ h_exp = h_b
554
+ step_info["fork_winner"] = "B"
555
+ path_taken.append(f"FORK_B_WON({phi_b:.4f}>={phi_a:.4f})")
556
+ else:
557
+ h_exp = h_a
558
+ step_info["fork_winner"] = "A"
559
+ path_taken.append(f"FORK_A_WON({phi_a:.4f}>{phi_b:.4f})")
560
+ else:
561
+ h_exp = h_b
562
+
563
+ # Phase 9.1: SRJ
564
+ jitter_scale = max(0.0, 1.0 - phi) * 0.05
565
+ if jitter_scale > 0:
566
+ h_exp = h_exp + torch.randn_like(h_exp) * jitter_scale
567
+
568
+ # OSS - Safe FP32 Calculation for FP16 models (Phase 18)
569
+ h_exp_f32 = h_exp.to(torch.float32)
570
+ norm_orig = h_exp_f32.norm(dim=-1, keepdim=True)
571
+ e_ref_f32 = e_reflector.to(torch.float32)
572
+ dot_he = (h_exp_f32 * e_ref_f32).sum(dim=-1, keepdim=True)
573
+ dot_ee = (e_ref_f32 * e_ref_f32).sum(dim=-1, keepdim=True)
574
+ proj = (dot_he / (dot_ee + 1e-6)) * e_ref_f32
575
+ proj = proj.to(h_exp.dtype)
576
+ ortho = h_exp - proj
577
+
578
+ # Phase 14.8: Step-Entropy Destabilization (SED)
579
+ if stability_counter > 2:
580
+ # Phase 15.9: Nonlinear Repulsion
581
+ # Phase 17.7: Scale-Agnostic Dampening (smaller force for deeper models)
582
+ scale_factor = 26.0 / cfg.get("num_layers", 26.0)
583
+ repulsion_force = 0.10 * (stability_counter ** 2) * scale_factor
584
+ h_exp = h_exp + repulsion_force * (ortho / (ortho.norm(dim=-1, keepdim=True) + 1e-6))
585
+ path_taken.append(f"SED_PUSH({repulsion_force:.2f})")
586
+
587
+ # Dynamic Jump proportional to depth
588
+ jump = max(1, int(cfg.get("num_layers", 18) * 0.1))
589
+ current_layer = min(cfg["recur_end"] - 1, current_layer + jump)
590
+
591
+ gain_factor = max(1.0, min(1.15, 1.0 + (1.0 - phi) * 0.4))
592
+ damping_factor = max(0.85, min(1.0, 1.0 - (1.0 - phi) * 0.2))
593
+ h_exp = damping_factor * proj + gain_factor * ortho
594
+
595
+ # Safe FP32 final normalization
596
+ h_exp_f32_final = h_exp.to(torch.float32)
597
+ norm_f32 = h_exp_f32_final.norm(dim=-1, keepdim=True)
598
+ norm_orig_f32 = norm_orig.to(torch.float32)
599
+ h_exp = (h_exp_f32_final * (norm_orig_f32 / (norm_f32 + 1e-6))).to(h_exp.dtype)
600
+
601
+ # Store thought for RSM
602
+ if exploration_steps % 2 == 0:
603
+ thought_history.append(h_exp.detach())
604
+
605
+ # Phase 18: Universal ALR Thresholds based on internal parameters (gamma)
606
+ # Relax thresholds dynamically if we visit a layer too often (loop breaking)
607
+ visit_penalty = (layer_visits[current_layer] - 1) * 0.015
608
+ t_back_2 = 1.0 - (0.8 * current_gamma) - visit_penalty
609
+ t_back_1 = 1.0 - (0.4 * current_gamma) - visit_penalty
610
+ t_skip = 1.0 - (0.01 * current_gamma) - (visit_penalty * 0.5)
611
+
612
+ if phi < t_back_2: # High confusion
613
+ current_layer = max(active_start, current_layer - 2)
614
+ routing = "BACK-2"
615
+ elif phi < t_back_1: # Moderate confusion
616
+ current_layer = max(active_start, current_layer - 1)
617
+ routing = "BACK-1"
618
+ elif phi > t_skip: # Extreme stability
619
+ current_layer += 2 # Skip
620
+ routing = "SKIP-1"
621
+ stability_counter += 1
622
+ else:
623
+ current_layer += 1
624
+ routing = "NEXT"
625
+ stability_counter = 0
626
+
627
+ # Clamp current_layer to prevent underflow
628
+ if current_layer < active_start:
629
+ current_layer = active_start
630
+ routing = "CLAMPED"
631
+
632
+ step_info["routing"] = routing
633
+ telemetry_data["routing"] = routing
634
+ if os.environ.get("DEBUG_PX") == "1":
635
+ telemetry_steps.append(telemetry_data)
636
+
637
+ if stability_counter > 5:
638
+ break
639
+
640
+ exploration_steps += 1
641
+
642
+ avg_phi_explore = sum(phis)/len(phis) if phis else 1.0
643
+
644
+ # Phase 4.1: QBI Blend
645
+ b_min = cfg.get("beta_reasoning", 0.05)
646
+ b_max = cfg.get("beta_grounding", 0.18)
647
+ beta_final = b_min + (b_max - b_min) * (avg_phi_explore ** 2)
648
+
649
+ hidden_states = (1.0 - beta_final) * h_baseline + beta_final * h_exp
650
+ else:
651
+ hidden_states = h_baseline
652
+
653
+ self._px_phi = avg_phi_explore
654
+ self._px_loops_run = exploration_steps
655
+ self._px_path = path_taken
656
+
657
+ # Phase 14.2: Structured Telemetry Log
658
+ if not hasattr(self, "_px_telemetry"):
659
+ self._px_telemetry = []
660
+
661
+ self._px_telemetry.append({
662
+ "pos": int(position_ids[0, 0].item()),
663
+ "avg_phi": float(avg_phi_explore),
664
+ "steps": telemetry_steps
665
+ })
666
+
667
+ # Phase 11.0: Metacognitive Triggering
668
+ # If stability is low during the very first token generation,
669
+ # we flag this as a 'Complex Problem'.
670
+ if not hasattr(self, "_px_complexity_acc"):
671
+ self._px_complexity_acc = []
672
+
673
+ # If we see the first token of a sequence, clear the accumulator
674
+ if position_ids[0, 0] == 0:
675
+ self._px_complexity_acc = []
676
+
677
+ self._px_complexity_acc.append(avg_phi_explore)
678
+
679
+ # Trigger if average stability of the prompt processing is low
680
+ # selective threshold: 0.90
681
+ self._px_trigger_scratchpad = (len(self._px_complexity_acc) > 3 and
682
+ sum(self._px_complexity_acc) / len(self._px_complexity_acc) < 0.92)
683
+
684
+ # ── 3. CODA ─────────────────────────────────────────────────────────────
685
+ dynamic_coda_start = dynamic_end if cfg.get("routing_mode") == "adaptive" else cfg["coda_start"]
686
+
687
+ # Phase 14.5: Coda-Grounding Injection (CGI)
688
+ # Re-inject sensory data to prevent 'hallucinatory drift' in final reasoning.
689
+ for i in range(dynamic_coda_start, len(self.layers)):
690
+ if i == dynamic_coda_start:
691
+ # Phase 14.7: Reverted CGI (8%)
692
+ hidden_states = 0.92 * hidden_states + 0.08 * e_static
693
+
694
+ layer_out = self.layers[i](
695
+ hidden_states,
696
+ attention_mask=causal_mask_mapping[mask_config.layer_types[i]],
697
+ position_embeddings=position_embeddings[mask_config.layer_types[i]],
698
+ position_ids=position_ids,
699
+ past_key_values=past_key_values,
700
+ **kwargs,
701
+ )
702
+ hidden_states = layer_out[0] if isinstance(layer_out, (tuple, list)) else layer_out
703
+
704
+ hidden_states = self.norm(hidden_states)
705
+
706
+ # Phase 25: Save Telemetry if enabled
707
+ if os.environ.get("DEBUG_PX") == "1" and len(telemetry_steps) > 0:
708
+ ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")
709
+ log_path = f"px_telemetry_{ts}.json"
710
+ with open(log_path, "w") as f:
711
+ json.dump(telemetry_steps, f, indent=2)
712
+ # Only output the path as requested
713
+ print(f"TELEMETRY_JSON: {os.path.abspath(log_path)}")
714
+
715
+ from transformers.modeling_outputs import BaseModelOutputWithPast
716
+ return BaseModelOutputWithPast(
717
+ last_hidden_state=hidden_states,
718
+ past_key_values=past_key_values,
719
+ )
720
+
721
+ # ---------------------------------------------------------------------------
722
+
723
+ def apply_px_patch(model, **cfg_kwargs):
724
+ from .px_modules import LTIInjection
725
+
726
+ # Robust Text Model Resolver (Phase 17.9)
727
+ # We look for the module that contains 'layers' and 'rotary_emb'
728
+ text_model = None
729
+ if hasattr(model, "layers") and hasattr(model, "rotary_emb"):
730
+ text_model = model
731
+ else:
732
+ # Search children (e.g., .model, .language_model.model)
733
+ for name, module in model.named_modules():
734
+ if hasattr(module, "layers") and hasattr(module, "rotary_emb"):
735
+ text_model = module
736
+ break
737
+
738
+ if text_model is None:
739
+ raise AttributeError(f"Could not identify Gemma-3 text backbone in {type(model)}")
740
+
741
+ config = model.config
742
+ # Multimodal check: larger models (4B+) wrap text config
743
+ if hasattr(config, "text_config"):
744
+ config = config.text_config
745
+
746
+ num_layers = config.num_hidden_layers
747
+
748
+ # Scale-Aware Hyperparameters (Phase 17.100)
749
+ # - Gamma: Inverse-proportional to hidden size
750
+ # - Prelude: Shallow models need deeper grounding before recursion
751
+ hidden_size = config.hidden_size
752
+ num_layers = config.num_hidden_layers
753
+
754
+ # Phase 25: Balanced Precision Tuning
755
+ if hidden_size == 640: # 270M
756
+ defaults = {
757
+ "mode": "lti", "n_loops": 16, "beta": 0.05, "gamma": 0.12,
758
+ "recur_start": 8, "recur_end": 12, "bimodal_hub": 11,
759
+ "cgi_factor": 0.08, "num_layers": num_layers
760
+ }
761
+ elif hidden_size == 1152: # 1B
762
+ defaults = {
763
+ "mode": "lti", "n_loops": 8, "beta": 0.05, "gamma": 0.12,
764
+ "recur_start": 10, "recur_end": 20, "bimodal_hub": 18,
765
+ "cgi_factor": 0.08, "num_layers": num_layers
766
+ }
767
+ elif hidden_size == 2560: # 4B
768
+ defaults = {
769
+ "mode": "lti", "n_loops": 6, "beta": 0.05, "gamma": 0.05,
770
+ "recur_start": 5, "recur_end": 33, "bimodal_hub": 32,
771
+ "cgi_factor": 0.08, "num_layers": num_layers
772
+ }
773
+ else: # Fallback for unknown sizes
774
+ gamma_scale = 1152.0 / hidden_size
775
+ base_gamma = 0.12 * gamma_scale
776
+ prelude_ratio = 0.40 if num_layers < 20 else 0.15
777
+ p_start = max(4, int(num_layers * prelude_ratio))
778
+ p_end = max(p_start + 1, int(num_layers * 0.98))
779
+ p_hub = max(p_start + 1, int(num_layers * 0.95))
780
+
781
+ defaults = {
782
+ "mode": "lti", "n_loops": 4, "beta": 0.05, "gamma": base_gamma,
783
+ "recur_start": p_start, "recur_end": p_end, "bimodal_hub": p_hub,
784
+ "cgi_factor": 0.08, "num_layers": num_layers
785
+ }
786
+
787
+ defaults.update(cfg_kwargs)
788
+
789
+ # Auto-align boundaries
790
+ if "prelude_end" not in defaults:
791
+ defaults["prelude_end"] = defaults["recur_start"]
792
+ if "coda_start" not in defaults:
793
+ defaults["coda_start"] = defaults["recur_end"]
794
+
795
+ text_model._px_config = defaults
796
+ text_model._px_injection = LTIInjection(config.hidden_size, gamma=defaults["gamma"])
797
+ text_model.forward = types.MethodType(_px_forward, text_model)
798
+ print(f"[gemma3-px] Auto-Patch active for scale L{num_layers}. Recur: L{defaults['recur_start']}-L{defaults['recur_end']}, Hub: L{defaults['bimodal_hub']}.")
799
+
800
+ def get_px_metrics(model):
801
+ text_model = None
802
+ if hasattr(model, "layers") and hasattr(model, "rotary_emb"):
803
+ text_model = model
804
+ else:
805
+ for name, module in model.named_modules():
806
+ if hasattr(module, "layers") and hasattr(module, "rotary_emb"):
807
+ text_model = module
808
+ break
809
+
810
+ if text_model is None:
811
+ text_model = (model.model if hasattr(model, "model") else model)
812
+
813
+ return {
814
+ "phi": getattr(text_model, "_px_phi", 1.0),
815
+ "steps": getattr(text_model, "_px_loops_run", 0),
816
+ "path": getattr(text_model, "_px_path", []),
817
+ "telemetry": getattr(text_model, "_px_telemetry", []),
818
+ }
px_modules.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ gemma3-px — Pure Architectural Modules
3
+ ==========================================
4
+ Zero-shot only. No trainable parameters beyond what is strictly necessary.
5
+ No OpenMythos baggage (no ACT, no LoRA, no probes, no epsilon).
6
+
7
+ Three modules:
8
+ LTIInjection — the core recurrence signal: h_new = trans_out + gamma*(e_norm - h)
9
+ ADCInjection — adaptive variant: gamma scales with instability (1-phi)
10
+ StabilityMonitor — parameter-free Phi / Lambda / Eta heuristics (logging only)
11
+ """
12
+
13
+ import torch
14
+ import torch.nn as nn
15
+ import torch.nn.functional as F
16
+ from typing import Optional
17
+
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # 1. LTI Injection (Pure, fixed gamma)
21
+ # ---------------------------------------------------------------------------
22
+
23
+ class LTIInjection(nn.Module):
24
+ """
25
+ Linear Time-Invariant anchor injection.
26
+ Provides 'Computational Headroom' by pulling h back toward the
27
+ frozen input embedding e whenever it drifts.
28
+
29
+ Formula:
30
+ h_new = transformer_out + gamma * (LayerNorm(e) - h)
31
+
32
+ Identity at t=0 if gamma=0. Optimal empirical gamma: 0.08.
33
+ """
34
+
35
+ def __init__(self, dim: int, gamma: float = 0.08):
36
+ super().__init__()
37
+ self.gamma = gamma
38
+ # Affine=False: no trainable params, pure normalization
39
+ self.input_norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
40
+
41
+ def forward(
42
+ self,
43
+ h: torch.Tensor,
44
+ e: torch.Tensor,
45
+ transformer_out: torch.Tensor,
46
+ ) -> torch.Tensor:
47
+ e_norm = self.input_norm(e.to(torch.float32)).to(h.dtype)
48
+ return transformer_out + self.gamma * (e_norm - h)
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # 2. ADC Injection (Adaptive Dynamic Correction)
53
+ # ---------------------------------------------------------------------------
54
+
55
+ class ADCInjection(nn.Module):
56
+ """
57
+ Adaptive variant of LTI: effective gamma = base_gamma + alpha*(1-phi).
58
+
59
+ When phi is high (state stable) → injection is gentle.
60
+ When phi drops (state drifting) → injection strengthens automatically.
61
+ No trainable parameters.
62
+
63
+ Args:
64
+ dim: hidden size
65
+ base_gamma: minimum injection strength (default 0.06)
66
+ alpha: maximum additional strength at full instability (default 0.10)
67
+ """
68
+
69
+ def __init__(self, dim: int, base_gamma: float = 0.06, alpha: float = 0.10):
70
+ super().__init__()
71
+ self.base_gamma = base_gamma
72
+ self.alpha = alpha
73
+ self.input_norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
74
+
75
+ def forward(
76
+ self,
77
+ h: torch.Tensor,
78
+ e: torch.Tensor,
79
+ transformer_out: torch.Tensor,
80
+ phi: float = 1.0, # Φ from previous loop (1.0 = fully stable)
81
+ ) -> torch.Tensor:
82
+ instability = max(0.0, 1.0 - phi)
83
+ effective_gamma = self.base_gamma + self.alpha * instability
84
+ e_norm = self.input_norm(e.to(torch.float32)).to(h.dtype)
85
+ return transformer_out + effective_gamma * (e_norm - h)
86
+
87
+
88
+ # ---------------------------------------------------------------------------
89
+ # 3. Stability Monitor (no parameters — logging / diagnostics only)
90
+ # ---------------------------------------------------------------------------
91
+
92
+ class StabilityMonitor:
93
+ """
94
+ Parameter-free heuristics for Phi (Φ), Lambda (λ), and Eta (η).
95
+
96
+ Phi — cosine similarity between consecutive hidden states (stability).
97
+ Lambda— cosine distance between current h and anchor e (drift).
98
+ Eta — variance-based entropy estimate (uncertainty).
99
+ """
100
+
101
+ @staticmethod
102
+ def calculate_phi(h_new: torch.Tensor, h_old: torch.Tensor) -> torch.Tensor:
103
+ """Φ ∈ [0,1]: 1 = identical state, 0 = orthogonal."""
104
+ B = h_new.shape[0]
105
+ # Safe FP32 for FP16 models
106
+ h_n_f32 = h_new.view(B, -1).to(torch.float32)
107
+ h_o_f32 = h_old.view(B, -1).to(torch.float32)
108
+ return F.cosine_similarity(h_n_f32, h_o_f32, dim=-1).mean()
109
+
110
+ @staticmethod
111
+ def detect_lambda(h: torch.Tensor, e: torch.Tensor) -> torch.Tensor:
112
+ """λ ∈ [0,1]: 0 = h == e (no drift), 1 = fully diverged."""
113
+ B = h.shape[0]
114
+ sim = F.cosine_similarity(h.view(B, -1), e.view(B, -1), dim=-1).mean()
115
+ return (1.0 - sim).clamp(0.0, 1.0)
116
+
117
+ @staticmethod
118
+ def calculate_eta(h: torch.Tensor) -> torch.Tensor:
119
+ """η ∈ [0,1]: variance-based uncertainty proxy. Recalibrated for Gemma-3 (var~67)."""
120
+ var = torch.var(h.to(torch.float32), dim=-1).mean()
121
+ # Scale 67.5 to ~0.5.
122
+ # (67.5 / 67.5) * 10 - 5 = 5 (sigmoid -> 0.99)
123
+ # (50 / 67.5) * 10 - 5 = 2.4 (sigmoid -> 0.9)
124
+ # (30 / 67.5) * 10 - 5 = -0.5 (sigmoid -> 0.37)
125
+ return torch.sigmoid((var / 67.5) * 10.0 - 5.0)