initium-deeplearning 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- initium/LNN_controller/cfc_backbone_parallel.py +265 -0
- initium/LNN_controller/cfc_controller.py +325 -0
- initium/LNN_controller/chained_controller.py +83 -0
- initium/LNN_controller/hybrid_controller.py +90 -0
- initium/MoE/README.md +1 -0
- initium/MoE/moe_layer.py +552 -0
- initium/__init__.py +1 -0
- initium/config/controller_config.py +86 -0
- initium/config/train_config.py +64 -0
- initium/core_training.py +2182 -0
- initium/data/__init__.py +0 -0
- initium/data/audio/__init__.py +0 -0
- initium/data/audio/audio_dataset.py +96 -0
- initium/data/base_dataset.py +75 -0
- initium/data/common/__init__.py +0 -0
- initium/data/common/chain_task.py +542 -0
- initium/data/common/digit_codec.py +85 -0
- initium/data/common/graph_io.py +123 -0
- initium/data/common/real_data.py +747 -0
- initium/data/dataset_registry.py +59 -0
- initium/data/graph_traversal/__init__.py +0 -0
- initium/data/graph_traversal/graph_traversal.py +746 -0
- initium/data/multimodal/__init__.py +0 -0
- initium/data/multimodal/multimodal_dataset.py +401 -0
- initium/data/text/__init__.py +0 -0
- initium/data/text/text_dataset.py +79 -0
- initium/data/video/__init__.py +0 -0
- initium/data/video/video_dataset.py +85 -0
- initium/inference/__init__.py +1 -0
- initium/inference/cache/__init__.py +1 -0
- initium/inference/cache/base_cache.py +112 -0
- initium/inference/cache/cache_config.py +21 -0
- initium/inference/cache/cache_registry.py +99 -0
- initium/inference/cache/cached_engine.py +264 -0
- initium/inference/cache/keys.py +77 -0
- initium/inference/cache/prefix_cache.py +59 -0
- initium/inference/cache/result_cache.py +57 -0
- initium/inference/cache/state_utils.py +74 -0
- initium/inference/cache/stores.py +167 -0
- initium/inference/capabilities.py +86 -0
- initium/inference/checkpoint_io.py +63 -0
- initium/inference/engine.py +88 -0
- initium/inference/inference_config.py +22 -0
- initium/inference/metrics.py +103 -0
- initium/inference/model_loader.py +270 -0
- initium/inference/run_inference.py +526 -0
- initium/inference/run_logging.py +173 -0
- initium/inference/tasks/__init__.py +1 -0
- initium/inference/tasks/audio_task.py +114 -0
- initium/inference/tasks/base_task.py +60 -0
- initium/inference/tasks/graph_traversal_task.py +235 -0
- initium/inference/tasks/multimodal_task.py +74 -0
- initium/inference/tasks/task_registry.py +68 -0
- initium/inference/tasks/text_task.py +114 -0
- initium/inference/tasks/video_task.py +114 -0
- initium/mamba_controller/mamba2_controller.py +433 -0
- initium/mamba_controller/mamba3_controller.py +242 -0
- initium/mamba_controller/mamba_backbone_parallel.py +291 -0
- initium/mamba_controller/mamba_controller.py +983 -0
- initium/mamba_controller/split_graph_dnc.py +623 -0
- initium/memory_manipulation/dynamic_memory_resize.py +157 -0
- initium/memory_manipulation/dynamic_n_controller.py +152 -0
- initium/memory_manipulation/link_matrix_ablation.py +130 -0
- initium/memory_manipulation/nvrtc_compat.py +143 -0
- initium/memory_manipulation/stochastic_write_head_v2.py +545 -0
- initium/setup_wheels.py +175 -0
- initium_deeplearning-0.1.0.dist-info/METADATA +253 -0
- initium_deeplearning-0.1.0.dist-info/RECORD +71 -0
- initium_deeplearning-0.1.0.dist-info/WHEEL +4 -0
- initium_deeplearning-0.1.0.dist-info/entry_points.txt +3 -0
- initium_deeplearning-0.1.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
"""
|
|
2
|
+
file: split_graph_dnc.py
|
|
3
|
+
|
|
4
|
+
Alternate Phase 3, Step 2, Option 5 -- "A genuine, higher-payoff Idea
|
|
5
|
+
candidate: split the compute graph, don't try to parallelize the addressing
|
|
6
|
+
itself" (see Experiment-Roadmap.md, Idea 5 and its failure-mode table).
|
|
7
|
+
|
|
8
|
+
DISABLED BY DEFAULT AT EVERY LEVEL (module has no global toggle of its own
|
|
9
|
+
-- it's simply not constructed unless the training script's
|
|
10
|
+
SPLIT_GRAPH_ENABLED / --split-graph is explicitly set). The roadmap
|
|
11
|
+
explicitly flags this mechanism as having NO corpus-established failure
|
|
12
|
+
mode yet ("Honesty check first... there is no corpus-established failure
|
|
13
|
+
mode for this option yet") and requiring its own isolated ablation before
|
|
14
|
+
being trusted -- treat every run of this module as an open experiment, not
|
|
15
|
+
a validated component.
|
|
16
|
+
|
|
17
|
+
--- Design (mirrors the roadmap's own description of Idea 5) -------------
|
|
18
|
+
Two halves, matching "(a) the Mamba backbone's own token-level state
|
|
19
|
+
updates, which have no dependency on M_{t-1} and can run through the
|
|
20
|
+
standard parallel scan" and "(b) a genuinely sequential, but much smaller
|
|
21
|
+
and cheaper, memory read/write/addressing step layered on top, consuming
|
|
22
|
+
the backbone's parallel output as an input stream":
|
|
23
|
+
|
|
24
|
+
(a) self.backbone (MambaBackboneParallel, see mamba_backbone_parallel.py)
|
|
25
|
+
processes the RAW input sequence X -- never concatenated with a read
|
|
26
|
+
vector -- in ONE call, using mamba_ssm's own whole-sequence
|
|
27
|
+
forward(). This is what makes it parallel: it has no dependency on
|
|
28
|
+
any memory state, by construction, so there is nothing forcing a
|
|
29
|
+
Python loop.
|
|
30
|
+
|
|
31
|
+
(b) A per-timestep loop that: (i) combines this timestep's backbone
|
|
32
|
+
output h_t with the PREVIOUS timestep's read vector read_{t-1} via a
|
|
33
|
+
small Linear ("combiner") to produce the interface vector xi_t fed
|
|
34
|
+
into Memory, then (ii) calls the existing dnc.memory.Memory exactly
|
|
35
|
+
the same way every other controller in this project already does.
|
|
36
|
+
This is the "read now, decide next hop immediately" loop the
|
|
37
|
+
roadmap's own diagnosis of DEER's and chunking's failures both
|
|
38
|
+
center on -- it is preserved exactly, per-step, sequential, never
|
|
39
|
+
frozen for a window. The ONLY thing removed from the sequential
|
|
40
|
+
critical path is the backbone's own internal computation; Memory
|
|
41
|
+
itself was already the sequential part in every prior architecture
|
|
42
|
+
this project has built, so this doesn't add a new bottleneck, it
|
|
43
|
+
just stops making the (much more expensive) backbone share it.
|
|
44
|
+
|
|
45
|
+
Requirement from the roadmap's own methodological note on this option: "do
|
|
46
|
+
not layer them all at once... Concept 16 / Suspected Pattern SP-10". This
|
|
47
|
+
module therefore deliberately does NOT wire in Option 4's MoE (moe_enabled
|
|
48
|
+
raises if set) -- Option 5 must be run and ablated on its own first.
|
|
49
|
+
|
|
50
|
+
--- Built-in ablation for the roadmap's own required verification --------
|
|
51
|
+
"Verify [the design] preserves the exact 'read now, decide next hop
|
|
52
|
+
immediately' dependency, not just an approximation of it... treat as an
|
|
53
|
+
open risk requiring its own isolated ablation before adoption." The
|
|
54
|
+
combiner can be disabled entirely (combine_reads=False), which makes
|
|
55
|
+
xi_t = h_t for every t -- i.e. addressing becomes a pure function of the
|
|
56
|
+
backbone output and NEVER sees any read vector at all. Comparing
|
|
57
|
+
combine_reads=True vs. False on the SAME architecture is the cheapest
|
|
58
|
+
possible direct test of whether that dependency actually matters here,
|
|
59
|
+
rather than assuming it does because the roadmap says so.
|
|
60
|
+
|
|
61
|
+
--- Functional-usage check (LB-9 / Concept 6) is inherited, not special-
|
|
62
|
+
cased here. This module implements the SAME `pass_through_memory` kwarg
|
|
63
|
+
contract every other controller in this project's forward() already
|
|
64
|
+
honors, so the training script's existing standing verification step
|
|
65
|
+
(evaluate_traversal(..., ablate_memory=True), wired into
|
|
66
|
+
Alter_PHASE3_mamba.py's run() at every EVAL_EVERY cadence, logged to
|
|
67
|
+
run_{run_id}_memory_dependency.csv) applies to this controller with ZERO
|
|
68
|
+
additional code -- see the "elif pass_through_memory is False" branch in
|
|
69
|
+
forward() below.
|
|
70
|
+
|
|
71
|
+
--- API-compatibility notes (why this is written the way it is) ----------
|
|
72
|
+
This does NOT subclass dnc.DNC or MambaDNC -- the whole point of Option 5
|
|
73
|
+
is that the forward pass's control flow is fundamentally different (one
|
|
74
|
+
parallel backbone call, THEN a sequential loop, instead of one interleaved
|
|
75
|
+
step per timestep), so there's no meaningful DNC.forward() to defer to.
|
|
76
|
+
Instead this file reproduces, by hand, every piece of the surrounding
|
|
77
|
+
project's expected surface area that other files depend on:
|
|
78
|
+
- self.memories: a plain python list (NOT nn.ModuleList) holding one
|
|
79
|
+
dnc.memory.Memory, matching MambaDNC's exact convention, because
|
|
80
|
+
dynamic_memory_resize.resize_memory() does `model.memories[layer] =
|
|
81
|
+
new_memory` and link_matrix_ablation.patch_link_matrix() iterates
|
|
82
|
+
`model.memories` -- both assume this exact shape regardless of
|
|
83
|
+
controller class.
|
|
84
|
+
- setattr(self, "rnn_layer_memory_shared", self.memories[0]): the ONLY
|
|
85
|
+
reason the Memory instance is also a genuine, autograd-registered
|
|
86
|
+
submodule (needed for install_stochastic_write_heads()'s
|
|
87
|
+
named_modules() walk in stochastic_write_head_v2.py, and for
|
|
88
|
+
resize_memory()'s "find the attribute name pointing at old_memory"
|
|
89
|
+
swap in dynamic_memory_resize.py). Same convention MambaDNC uses.
|
|
90
|
+
- forward(input, hx=(chx, mhx, last_read), reset_experience,
|
|
91
|
+
pass_through_memory) -> (output, (chx, mhx, last_read)), with output
|
|
92
|
+
shaped (T, B, input_size) -- matching the (T, B, C) convention the
|
|
93
|
+
rest of this project's training/eval code already expects from
|
|
94
|
+
rnn(...)'s output (see the .transpose(0,1) calls immediately after
|
|
95
|
+
every rnn(...) call in Alter_PHASE3_mamba.py), and mhx wrapped as a
|
|
96
|
+
single-entry list (`mhx = [mem_state]`) so Dynamic-N's existing
|
|
97
|
+
`mem_hidden[0] if isinstance(mem_hidden, list) else mem_hidden` read
|
|
98
|
+
of hidden[1]["usage_vector"] keeps working unmodified.
|
|
99
|
+
|
|
100
|
+
ASSUMPTION FLAGGED FOR VERIFICATION: this file calls
|
|
101
|
+
`self.memories[0](xi_t, mem_state)` expecting it to return
|
|
102
|
+
`(read_vectors, updated_mem_state)`, inferred from this project's existing
|
|
103
|
+
usage of dnc.memory.Memory (`.reset()`, `write_vector_transform` swapped
|
|
104
|
+
inside install_stochastic_write_heads()) rather than from having the
|
|
105
|
+
literal pytorch-dnc source in hand. Run one batch through this module and
|
|
106
|
+
confirm read_vectors.shape == (B, read_heads*cell_size) before trusting a
|
|
107
|
+
full run -- if the installed library's Memory.forward returns the pair in
|
|
108
|
+
the opposite order, this is the one line to fix.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
from __future__ import annotations
|
|
112
|
+
|
|
113
|
+
from typing import Any
|
|
114
|
+
|
|
115
|
+
import torch
|
|
116
|
+
import torch.nn as nn
|
|
117
|
+
from dnc.memory import Memory
|
|
118
|
+
|
|
119
|
+
from initium.LNN_controller.cfc_backbone_parallel import (
|
|
120
|
+
build_parallel_backbone,
|
|
121
|
+
collect_backbone_moe_layers,
|
|
122
|
+
)
|
|
123
|
+
from initium.LNN_controller.cfc_controller import CfCControllerWrapper
|
|
124
|
+
from initium.LNN_controller.hybrid_controller import ( # v15: hybrid combiner
|
|
125
|
+
build_hybrid_controller,
|
|
126
|
+
is_hybrid_rnn_type,
|
|
127
|
+
)
|
|
128
|
+
from initium.mamba_controller.mamba2_controller import Mamba2ControllerWrapper
|
|
129
|
+
from initium.mamba_controller.mamba3_controller import Mamba3ControllerWrapper
|
|
130
|
+
from initium.mamba_controller.mamba_controller import MambaControllerWrapper
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class SplitGraphDNC(nn.Module):
|
|
134
|
+
"""Alternate Phase 3, Step 2, Option 5 controller+memory assembly.
|
|
135
|
+
See module docstring for the full design rationale and the explicit
|
|
136
|
+
"not validated yet" framing.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
def __init__(
|
|
140
|
+
self,
|
|
141
|
+
input_size: int,
|
|
142
|
+
hidden_size: int,
|
|
143
|
+
nr_cells: int = 256,
|
|
144
|
+
cell_size: int = 64,
|
|
145
|
+
read_heads: int = 4,
|
|
146
|
+
num_backbone_blocks: int = 2,
|
|
147
|
+
mamba_variant: str = "mamba1",
|
|
148
|
+
mamba_d_state: int = 16,
|
|
149
|
+
mamba_d_conv: int = 4,
|
|
150
|
+
mamba_expand: int = 2,
|
|
151
|
+
cfc_kwargs: dict
|
|
152
|
+
| None = None, # v14: CfC hyperparameters for variants containing "cfc" (None -> defaults)
|
|
153
|
+
mamba_headdim: int = 64, # mamba2-only
|
|
154
|
+
combine_reads: bool = True, # built-in ablation switch, see module docstring
|
|
155
|
+
# v11: combiner mechanism for the sequential addressing step. This is
|
|
156
|
+
# ORTHOGONAL to the backbone (mamba_variant above, still parallel,
|
|
157
|
+
# unchanged) -- it only changes how xi_t is produced from (h_t,
|
|
158
|
+
# read_{t-1}) inside the per-timestep loop.
|
|
159
|
+
# "linear" (default): the original plain nn.Linear combiner --
|
|
160
|
+
# stateless, one matmul per timestep. This is the config that
|
|
161
|
+
# produced the "marvelous efficiency" mamba1-backbone result;
|
|
162
|
+
# leaving this as default means every existing call site is
|
|
163
|
+
# byte-for-byte unaffected.
|
|
164
|
+
# "controller": xi_t is instead produced by a real interleaved
|
|
165
|
+
# controller cell (MambaControllerWrapper for
|
|
166
|
+
# combiner_variant="mamba1", Mamba2ControllerWrapper for
|
|
167
|
+
# "mamba2") carrying its own (conv_state, ssm_state) across
|
|
168
|
+
# the whole episode -- i.e. the sequential step gets genuine
|
|
169
|
+
# recurrent SSM capacity instead of a stateless projection,
|
|
170
|
+
# while the backbone stays exactly as parallel as before.
|
|
171
|
+
combiner_mode: str = "linear",
|
|
172
|
+
combiner_variant: str = "mamba1", # "mamba1" | "mamba2", only used when combiner_mode="controller"
|
|
173
|
+
combiner_num_blocks: int = 1, # kept small on purpose -- see module docstring's
|
|
174
|
+
# "much smaller and cheaper" framing of this step
|
|
175
|
+
combiner_d_state: int | None = None, # None -> variant's own paper default (16 / 64)
|
|
176
|
+
combiner_d_conv: int = 4,
|
|
177
|
+
combiner_expand: int = 2,
|
|
178
|
+
combiner_headdim: int = 64, # mamba2 combiner_variant only
|
|
179
|
+
combiner_ngroups: int = 1, # mamba2 combiner_variant only
|
|
180
|
+
independent_linears: bool = True,
|
|
181
|
+
device: torch.device | None = None,
|
|
182
|
+
moe_enabled: bool = False,
|
|
183
|
+
moe_num_experts: int = 8,
|
|
184
|
+
moe_expert_dim: int | None = None,
|
|
185
|
+
moe_top_k: int = 1,
|
|
186
|
+
moe_capacity_factor: float = 1.5,
|
|
187
|
+
moe_load_balance_alpha: float = 0.01,
|
|
188
|
+
# When combiner_variant == "cfc" (standalone, not part of a hybrid
|
|
189
|
+
# combiner chain) and moe_enabled, route the combiner's two natural
|
|
190
|
+
# inputs -- backbone output h_t and the previous read vector --
|
|
191
|
+
# through per-source specialized experts (see
|
|
192
|
+
# cfc_controller.CfCControllerWrapper's moe_source_dims) instead of
|
|
193
|
+
# a single pre-concatenated MoE pass. True is the recommended
|
|
194
|
+
# default per this project's own CfC-property requirement; set
|
|
195
|
+
# False to fall back to plain per-block MoE on the pre-concatenated
|
|
196
|
+
# input, for direct A/B comparison.
|
|
197
|
+
moe_cfc_multi_source: bool = True,
|
|
198
|
+
):
|
|
199
|
+
super().__init__()
|
|
200
|
+
if not independent_linears:
|
|
201
|
+
raise ValueError(
|
|
202
|
+
"SplitGraphDNC requires independent_linears=True, same "
|
|
203
|
+
"requirement as install_stochastic_write_heads() (Phase 1) "
|
|
204
|
+
"-- it needs Memory to expose a standalone "
|
|
205
|
+
"write_vector_transform Linear to swap out."
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
self.input_size = input_size
|
|
209
|
+
self.hidden_size = hidden_size
|
|
210
|
+
self.output_size = hidden_size
|
|
211
|
+
self.nr_cells = nr_cells
|
|
212
|
+
self.cell_size = cell_size
|
|
213
|
+
self.read_heads = read_heads
|
|
214
|
+
self.w = cell_size
|
|
215
|
+
self.r = read_heads
|
|
216
|
+
self.read_vectors_size = read_heads * cell_size
|
|
217
|
+
self.nn_output_size = hidden_size + self.read_vectors_size
|
|
218
|
+
self.device = device
|
|
219
|
+
self.mamba_variant = mamba_variant
|
|
220
|
+
self.combine_reads = combine_reads
|
|
221
|
+
|
|
222
|
+
# Mamba bookkeeping, for checkpoint self-description -- same
|
|
223
|
+
# rationale/convention as MambaDNC (mamba_controller.py).
|
|
224
|
+
self.mamba_d_state = mamba_d_state
|
|
225
|
+
self.mamba_d_conv = mamba_d_conv
|
|
226
|
+
self.mamba_expand = mamba_expand
|
|
227
|
+
self.mamba_headdim = mamba_headdim
|
|
228
|
+
|
|
229
|
+
self.backbone = build_parallel_backbone(
|
|
230
|
+
in_dim=input_size,
|
|
231
|
+
d_model=hidden_size,
|
|
232
|
+
num_blocks=num_backbone_blocks,
|
|
233
|
+
variant=mamba_variant,
|
|
234
|
+
d_state=mamba_d_state,
|
|
235
|
+
d_conv=mamba_d_conv,
|
|
236
|
+
expand=mamba_expand,
|
|
237
|
+
headdim=mamba_headdim,
|
|
238
|
+
cfc_kwargs=cfc_kwargs,
|
|
239
|
+
moe_enabled=moe_enabled,
|
|
240
|
+
moe_num_experts=moe_num_experts,
|
|
241
|
+
moe_expert_dim=moe_expert_dim,
|
|
242
|
+
moe_top_k=moe_top_k,
|
|
243
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
244
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
245
|
+
device=device,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if combiner_mode not in ("linear", "controller"):
|
|
249
|
+
raise ValueError(
|
|
250
|
+
f"SplitGraphDNC: unknown combiner_mode {combiner_mode!r}, "
|
|
251
|
+
"expected 'linear' or 'controller'."
|
|
252
|
+
)
|
|
253
|
+
self.combiner_mode = combiner_mode
|
|
254
|
+
self.combiner_variant = combiner_variant
|
|
255
|
+
self.combiner = None
|
|
256
|
+
# These wrappers share the runtime interface but have unrelated
|
|
257
|
+
# concrete classes (and the hybrid wrapper is composed dynamically).
|
|
258
|
+
# Keep the heterogeneous dispatch boundary dynamic; type their
|
|
259
|
+
# component classes independently at construction sites.
|
|
260
|
+
self.combiner_wrapper: Any = None
|
|
261
|
+
|
|
262
|
+
if combiner_mode == "linear":
|
|
263
|
+
if combine_reads:
|
|
264
|
+
self.combiner = nn.Linear(
|
|
265
|
+
hidden_size + self.read_vectors_size, hidden_size, device=device
|
|
266
|
+
)
|
|
267
|
+
# Zero-init: at step 0 of training, xi_t == h_t exactly (the
|
|
268
|
+
# split model starts by ignoring the previous read vector
|
|
269
|
+
# entirely and has to learn to use it). Same "start equal to
|
|
270
|
+
# the simpler/prior baseline" philosophy already used
|
|
271
|
+
# elsewhere in this project (StochasticWriteHead's zero-init
|
|
272
|
+
# logvar head, mu_transform copied from the original Linear --
|
|
273
|
+
# stochastic_write_head_v2.py).
|
|
274
|
+
nn.init.zeros_(self.combiner.weight)
|
|
275
|
+
nn.init.zeros_(self.combiner.bias)
|
|
276
|
+
else: # combiner_mode == "controller"
|
|
277
|
+
# v11: real interleaved cell as the combiner. combine_reads is
|
|
278
|
+
# implicitly True here -- a controller combiner is read-
|
|
279
|
+
# dependent by construction (its whole input is [h_t, read_{t-1}]
|
|
280
|
+
# every step), so there is no meaningful "ablated" variant of
|
|
281
|
+
# this mode the way the Linear combiner has combine_reads=False.
|
|
282
|
+
# Use combiner_mode="linear", combine_reads=False for that check
|
|
283
|
+
# instead (unchanged, still available).
|
|
284
|
+
if combiner_variant == "mamba2":
|
|
285
|
+
print(
|
|
286
|
+
"[SplitGraphDNC] WARNING: combiner_mode='controller' with "
|
|
287
|
+
"combiner_variant='mamba2' constructs a genuine per-timestep "
|
|
288
|
+
"interleaved Mamba-2 cell for the addressing step. This is "
|
|
289
|
+
"known to be inefficient (same reason standalone "
|
|
290
|
+
"--controller mamba2 runs aren't being pursued) and is wired "
|
|
291
|
+
"here only for completeness/future comparison -- "
|
|
292
|
+
"combiner_variant='mamba1' is the configuration expected to "
|
|
293
|
+
"actually be used."
|
|
294
|
+
)
|
|
295
|
+
_d_state = (
|
|
296
|
+
combiner_d_state
|
|
297
|
+
if combiner_d_state is not None
|
|
298
|
+
else (64 if combiner_variant in ("mamba2", "mamba3") else 16)
|
|
299
|
+
)
|
|
300
|
+
combiner_in_dim = hidden_size + self.read_vectors_size
|
|
301
|
+
if combiner_variant == "mamba2":
|
|
302
|
+
self.combiner_wrapper = Mamba2ControllerWrapper(
|
|
303
|
+
in_dim=combiner_in_dim,
|
|
304
|
+
d_model=hidden_size,
|
|
305
|
+
num_blocks=combiner_num_blocks,
|
|
306
|
+
d_state=_d_state,
|
|
307
|
+
d_conv=combiner_d_conv,
|
|
308
|
+
expand=combiner_expand,
|
|
309
|
+
headdim=combiner_headdim,
|
|
310
|
+
ngroups=combiner_ngroups,
|
|
311
|
+
moe_enabled=moe_enabled,
|
|
312
|
+
moe_num_experts=moe_num_experts,
|
|
313
|
+
moe_expert_dim=moe_expert_dim,
|
|
314
|
+
moe_top_k=moe_top_k,
|
|
315
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
316
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
317
|
+
device=device,
|
|
318
|
+
)
|
|
319
|
+
elif combiner_variant == "mamba3":
|
|
320
|
+
self.combiner_wrapper = Mamba3ControllerWrapper(
|
|
321
|
+
in_dim=combiner_in_dim,
|
|
322
|
+
d_model=hidden_size,
|
|
323
|
+
num_blocks=combiner_num_blocks,
|
|
324
|
+
d_state=_d_state,
|
|
325
|
+
expand=combiner_expand,
|
|
326
|
+
headdim=combiner_headdim,
|
|
327
|
+
moe_enabled=moe_enabled,
|
|
328
|
+
moe_num_experts=moe_num_experts,
|
|
329
|
+
moe_expert_dim=moe_expert_dim,
|
|
330
|
+
moe_top_k=moe_top_k,
|
|
331
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
332
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
333
|
+
device=device,
|
|
334
|
+
)
|
|
335
|
+
elif is_hybrid_rnn_type(
|
|
336
|
+
combiner_variant
|
|
337
|
+
): # v15: e.g. "mamba+cfc" (hybrid_controller kind names: mamba, not mamba1)
|
|
338
|
+
self.combiner_wrapper = build_hybrid_controller(
|
|
339
|
+
combiner_variant.lower(),
|
|
340
|
+
in_dim=combiner_in_dim,
|
|
341
|
+
d_model=hidden_size,
|
|
342
|
+
blocks_per_kind={
|
|
343
|
+
k: combiner_num_blocks for k in ("mamba", "mamba2", "mamba3", "cfc")
|
|
344
|
+
},
|
|
345
|
+
kwargs_per_kind={
|
|
346
|
+
"mamba": dict(
|
|
347
|
+
d_state=16,
|
|
348
|
+
d_conv=combiner_d_conv,
|
|
349
|
+
expand=combiner_expand,
|
|
350
|
+
moe_enabled=moe_enabled,
|
|
351
|
+
moe_num_experts=moe_num_experts,
|
|
352
|
+
moe_expert_dim=moe_expert_dim,
|
|
353
|
+
moe_top_k=moe_top_k,
|
|
354
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
355
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
356
|
+
),
|
|
357
|
+
"mamba2": dict(
|
|
358
|
+
d_state=64,
|
|
359
|
+
d_conv=combiner_d_conv,
|
|
360
|
+
expand=combiner_expand,
|
|
361
|
+
headdim=combiner_headdim,
|
|
362
|
+
ngroups=combiner_ngroups,
|
|
363
|
+
moe_enabled=moe_enabled,
|
|
364
|
+
moe_num_experts=moe_num_experts,
|
|
365
|
+
moe_expert_dim=moe_expert_dim,
|
|
366
|
+
moe_top_k=moe_top_k,
|
|
367
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
368
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
369
|
+
),
|
|
370
|
+
"mamba3": dict(
|
|
371
|
+
d_state=64,
|
|
372
|
+
expand=combiner_expand,
|
|
373
|
+
headdim=combiner_headdim,
|
|
374
|
+
moe_enabled=moe_enabled,
|
|
375
|
+
moe_num_experts=moe_num_experts,
|
|
376
|
+
moe_expert_dim=moe_expert_dim,
|
|
377
|
+
moe_top_k=moe_top_k,
|
|
378
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
379
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
380
|
+
),
|
|
381
|
+
"cfc": dict(cfc_kwargs or {}),
|
|
382
|
+
},
|
|
383
|
+
device=device,
|
|
384
|
+
)
|
|
385
|
+
elif combiner_variant == "cfc":
|
|
386
|
+
self.combiner_wrapper = CfCControllerWrapper(
|
|
387
|
+
in_dim=combiner_in_dim,
|
|
388
|
+
d_model=hidden_size,
|
|
389
|
+
num_blocks=combiner_num_blocks,
|
|
390
|
+
moe_enabled=moe_enabled,
|
|
391
|
+
moe_num_experts=moe_num_experts,
|
|
392
|
+
moe_expert_dim=moe_expert_dim,
|
|
393
|
+
moe_top_k=moe_top_k,
|
|
394
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
395
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
396
|
+
# Preserve CfC's "distinct input sources" property: the
|
|
397
|
+
# combiner's two natural sources are backbone output
|
|
398
|
+
# (hidden_size) and the previous read vector
|
|
399
|
+
# (read_vectors_size), routed by a shared source-aware
|
|
400
|
+
# Top-K expert bank rather than a single flattened
|
|
401
|
+
# concat -- see moe_layer.MultiSourceMoEBlock.
|
|
402
|
+
moe_source_dims=(
|
|
403
|
+
[hidden_size, self.read_vectors_size]
|
|
404
|
+
if (moe_enabled and moe_cfc_multi_source)
|
|
405
|
+
else None
|
|
406
|
+
),
|
|
407
|
+
device=device,
|
|
408
|
+
)
|
|
409
|
+
elif combiner_variant == "mamba1":
|
|
410
|
+
self.combiner_wrapper = MambaControllerWrapper(
|
|
411
|
+
in_dim=combiner_in_dim,
|
|
412
|
+
d_model=hidden_size,
|
|
413
|
+
num_blocks=combiner_num_blocks,
|
|
414
|
+
d_state=_d_state,
|
|
415
|
+
d_conv=combiner_d_conv,
|
|
416
|
+
expand=combiner_expand,
|
|
417
|
+
moe_enabled=moe_enabled,
|
|
418
|
+
moe_num_experts=moe_num_experts,
|
|
419
|
+
moe_expert_dim=moe_expert_dim,
|
|
420
|
+
moe_top_k=moe_top_k,
|
|
421
|
+
moe_capacity_factor=moe_capacity_factor,
|
|
422
|
+
moe_load_balance_alpha=moe_load_balance_alpha,
|
|
423
|
+
device=device,
|
|
424
|
+
)
|
|
425
|
+
else:
|
|
426
|
+
raise ValueError(
|
|
427
|
+
f"SplitGraphDNC: unknown combiner_variant {combiner_variant!r}, "
|
|
428
|
+
"expected 'mamba1' or 'mamba2'."
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
_ad = (
|
|
432
|
+
self.combiner_wrapper.in_adapter
|
|
433
|
+
) # read-vector columns start at 0, like the zero-init linear combiner
|
|
434
|
+
if isinstance(_ad, nn.Linear):
|
|
435
|
+
with torch.no_grad():
|
|
436
|
+
_ad.weight[:, hidden_size:].zero_()
|
|
437
|
+
|
|
438
|
+
self.memories = []
|
|
439
|
+
self.memories.append(
|
|
440
|
+
Memory(
|
|
441
|
+
input_size=self.output_size,
|
|
442
|
+
nr_cells=self.nr_cells,
|
|
443
|
+
cell_size=self.w,
|
|
444
|
+
read_heads=self.r,
|
|
445
|
+
device=self.device,
|
|
446
|
+
independent_linears=independent_linears,
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
# See module docstring's API-compatibility section for why this
|
|
450
|
+
# setattr (not just the list append above) is required.
|
|
451
|
+
self.rnn_layer_memory_shared = self.memories[0]
|
|
452
|
+
|
|
453
|
+
self.output = nn.Linear(self.nn_output_size, self.input_size, device=device)
|
|
454
|
+
torch.nn.init.kaiming_uniform_(self.output.weight)
|
|
455
|
+
|
|
456
|
+
# Option 4 (MoE) interoperability: kept OFF and deliberately NOT
|
|
457
|
+
# wired into the backbone blocks here (unlike mamba_controller.py's
|
|
458
|
+
# MambaControllerWrapper) -- Concept 16/SP-10 (non-additive
|
|
459
|
+
# combination) is why Option 5 is being built and ablated on its
|
|
460
|
+
# own first. moe_layers stays an empty list so
|
|
461
|
+
# pop_total_moe_aux_loss(...) (moe_layer.py) is still safe to call
|
|
462
|
+
# unconditionally from the training script regardless of which
|
|
463
|
+
# controller is active this run.
|
|
464
|
+
self.moe_enabled = moe_enabled
|
|
465
|
+
self.moe_cfc_multi_source = moe_cfc_multi_source
|
|
466
|
+
# Flat list every pop_total_moe_aux_loss() call sums over: the
|
|
467
|
+
# backbone's own per-block MoE sublayers, PLUS the combiner's (a
|
|
468
|
+
# single MultiSourceMoEBlock for a standalone cfc combiner, a
|
|
469
|
+
# ChainedControllerWrapper's own aggregated moe_blocks for a hybrid
|
|
470
|
+
# combiner, or a per-block list for any other controller combiner).
|
|
471
|
+
self.moe_layers = collect_backbone_moe_layers(self.backbone)
|
|
472
|
+
if self.combiner_wrapper is not None and getattr(
|
|
473
|
+
self.combiner_wrapper, "moe_enabled", False
|
|
474
|
+
):
|
|
475
|
+
assert self.combiner_wrapper.moe_blocks is not None
|
|
476
|
+
self.moe_layers.extend(list(self.combiner_wrapper.moe_blocks))
|
|
477
|
+
|
|
478
|
+
if self.device is not None and getattr(self.device, "type", None) == "cuda":
|
|
479
|
+
self.to(self.device)
|
|
480
|
+
|
|
481
|
+
def forward(
|
|
482
|
+
self,
|
|
483
|
+
input: torch.Tensor,
|
|
484
|
+
hx=(None, None, None),
|
|
485
|
+
reset_experience: bool = False,
|
|
486
|
+
pass_through_memory: bool = True,
|
|
487
|
+
combiner_skip_stages=None,
|
|
488
|
+
start_step: int = 0, # v20: resume the sequential loop mid-sequence (inference caching)
|
|
489
|
+
):
|
|
490
|
+
"""
|
|
491
|
+
input: (B, T, input_size) -- batch-first, the whole padded episode
|
|
492
|
+
at once (this project always calls rnn(input_seq, ...) with the
|
|
493
|
+
full sequence already assembled -- see Alter_PHASE3_mamba.py's
|
|
494
|
+
training loop and evaluate_traversal()).
|
|
495
|
+
hx: (chx, mhx, last_read). chx is unused by this controller (the
|
|
496
|
+
combiner has no recurrent state of its own -- it's a plain
|
|
497
|
+
per-timestep Linear, not a cell) and is passed through
|
|
498
|
+
untouched. mhx follows dnc.DNC's own convention: None on a
|
|
499
|
+
fresh call, or a single-entry list `[mem_state_dict]` on a
|
|
500
|
+
resumed/continuing call.
|
|
501
|
+
pass_through_memory: when False, skips Memory read AND write for
|
|
502
|
+
EVERY timestep and substitutes zero read-vectors -- this is
|
|
503
|
+
the functional-usage ablation check
|
|
504
|
+
(evaluate_traversal(..., ablate_memory=True)) already wired
|
|
505
|
+
into this project's training loop; see module docstring.
|
|
506
|
+
|
|
507
|
+
Returns (output, (chx, mhx, last_read)) with output shaped
|
|
508
|
+
(T, B, input_size) -- see module docstring for why this shape,
|
|
509
|
+
not (B, T, input_size).
|
|
510
|
+
"""
|
|
511
|
+
chx, mhx, last_read = hx
|
|
512
|
+
B, T, _ = input.shape
|
|
513
|
+
if start_step:
|
|
514
|
+
# Backbone still runs over the FULL input (cheap, parallel, stateless across
|
|
515
|
+
# calls); only the sequential memory loop is skipped for t < start_step.
|
|
516
|
+
# hx must then carry the state as of step `start_step`.
|
|
517
|
+
if not (0 < start_step < T):
|
|
518
|
+
raise ValueError(f"start_step={start_step} must be in (0, T={T})")
|
|
519
|
+
if mhx is None:
|
|
520
|
+
raise ValueError("start_step>0 requires a resumed hx=(chx, mhx, last_read)")
|
|
521
|
+
device = input.device
|
|
522
|
+
|
|
523
|
+
# ---- (a) PARALLEL BACKBONE ------------------------------------
|
|
524
|
+
# Single call, whole sequence, zero dependency on memory state.
|
|
525
|
+
# This is the entire "removed from the sequential critical path"
|
|
526
|
+
# half of Option 5.
|
|
527
|
+
H = self.backbone(input) # (B, T, hidden_size)
|
|
528
|
+
|
|
529
|
+
# ---- memory hidden-state init (byte-identical convention to
|
|
530
|
+
# dnc.DNC._init_hidden / MambaDNC._init_hidden's memory branch) ---
|
|
531
|
+
if mhx is None:
|
|
532
|
+
mhx = [self.memories[0].reset(B, erase=reset_experience)]
|
|
533
|
+
else:
|
|
534
|
+
if len(mhx) == 0 or mhx[0] is None:
|
|
535
|
+
mhx = [self.memories[0].reset(B, erase=reset_experience)]
|
|
536
|
+
else:
|
|
537
|
+
mhx = [self.memories[0].reset(B, mhx[0], erase=reset_experience)]
|
|
538
|
+
mem_state = mhx[0]
|
|
539
|
+
|
|
540
|
+
if last_read is None:
|
|
541
|
+
read_vec = torch.zeros(B, self.w * self.r, device=device, dtype=H.dtype)
|
|
542
|
+
else:
|
|
543
|
+
read_vec = last_read
|
|
544
|
+
|
|
545
|
+
# ---- (b) SEQUENTIAL ADDRESSING STEP ----------------------------
|
|
546
|
+
# The only sequential part left: a cheap per-timestep combine
|
|
547
|
+
# (h_t, read_{t-1}) -> xi_t, followed by the existing
|
|
548
|
+
# Memory.forward() call (already-sequential, already-cheap
|
|
549
|
+
# relative to a full backbone block -- this project never made
|
|
550
|
+
# Memory itself parallel, it only ever removed the BACKBONE from
|
|
551
|
+
# the sequential critical path). Preserves the "read now, decide
|
|
552
|
+
# next hop immediately" loop exactly, per-step.
|
|
553
|
+
# v11: local per-episode state for the controller combiner, if
|
|
554
|
+
# active -- analogous to mem_state above, NOT threaded through chx
|
|
555
|
+
# (chx stays "unused, pass through untouched" per this class's
|
|
556
|
+
# existing convention, since every call site in this project
|
|
557
|
+
# constructs fresh episodes with reset_experience=True). If chx was
|
|
558
|
+
# given (e.g. a future caller that does want continuity), reuse it
|
|
559
|
+
# as the starting state instead of a fresh zero-init.
|
|
560
|
+
combiner_hx = None
|
|
561
|
+
if self.combiner_mode == "controller":
|
|
562
|
+
assert self.combiner_wrapper is not None
|
|
563
|
+
combiner_hx = (
|
|
564
|
+
chx
|
|
565
|
+
if chx is not None
|
|
566
|
+
else self.combiner_wrapper.init_state(B, device=device, dtype=H.dtype)
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
outputs = []
|
|
570
|
+
for t in range(start_step, T):
|
|
571
|
+
h_t = H[:, t, :] # (B, hidden_size)
|
|
572
|
+
|
|
573
|
+
if self.combiner_mode == "controller":
|
|
574
|
+
assert self.combiner_wrapper is not None
|
|
575
|
+
_cfc_multi_source = (
|
|
576
|
+
self.combiner_variant == "cfc"
|
|
577
|
+
and getattr(self.combiner_wrapper, "moe_source_dims", None) is not None
|
|
578
|
+
)
|
|
579
|
+
if _cfc_multi_source:
|
|
580
|
+
xi_out, combiner_hx = self.combiner_wrapper.forward_multi_source(
|
|
581
|
+
[h_t, read_vec], combiner_hx
|
|
582
|
+
)
|
|
583
|
+
else:
|
|
584
|
+
combiner_in = torch.cat([h_t, read_vec], dim=-1).unsqueeze(
|
|
585
|
+
1
|
|
586
|
+
) # (B, 1, hidden+read)
|
|
587
|
+
if combiner_skip_stages is not None:
|
|
588
|
+
xi_out, combiner_hx = self.combiner_wrapper(
|
|
589
|
+
combiner_in, combiner_hx, skip_stages=combiner_skip_stages
|
|
590
|
+
)
|
|
591
|
+
else:
|
|
592
|
+
xi_out, combiner_hx = self.combiner_wrapper(combiner_in, combiner_hx)
|
|
593
|
+
xi_t = h_t + xi_out.squeeze(1)
|
|
594
|
+
elif self.combine_reads:
|
|
595
|
+
assert self.combiner is not None
|
|
596
|
+
xi_t = h_t + self.combiner(torch.cat([h_t, read_vec], dim=-1))
|
|
597
|
+
else:
|
|
598
|
+
# Built-in ablation (see module docstring): addressing
|
|
599
|
+
# never sees any read vector at all.
|
|
600
|
+
xi_t = h_t
|
|
601
|
+
|
|
602
|
+
if pass_through_memory:
|
|
603
|
+
read_vec, mem_state = self.memories[0](xi_t, mem_state)
|
|
604
|
+
read_vec = read_vec.reshape(
|
|
605
|
+
B, -1
|
|
606
|
+
) # dnc.memory.Memory returns (B, r, w) unflattened
|
|
607
|
+
else:
|
|
608
|
+
# LB-9/Concept 6 functional-usage check: skip memory read
|
|
609
|
+
# AND write for this timestep, matching every other
|
|
610
|
+
# controller's ablate_memory contract in this project.
|
|
611
|
+
read_vec = torch.zeros(B, self.w * self.r, device=device, dtype=H.dtype)
|
|
612
|
+
|
|
613
|
+
step_out = self.output(torch.cat([xi_t, read_vec], dim=-1)) # (B, input_size)
|
|
614
|
+
outputs.append(step_out)
|
|
615
|
+
|
|
616
|
+
if self.combiner_mode == "controller":
|
|
617
|
+
chx = combiner_hx # v11: expose the controller combiner's final state, same
|
|
618
|
+
# convention as mem_state -> mhx just above
|
|
619
|
+
|
|
620
|
+
mhx = [mem_state]
|
|
621
|
+
output = torch.stack(outputs, dim=0) # (T, B, input_size)
|
|
622
|
+
|
|
623
|
+
return output, (chx, mhx, read_vec)
|