Describe the bug
Umbrella issue: non-default config options and importable nn blocks that crash or are silently broken. None of these fire with default configs, which is why the test suite is green — but each is reachable through documented options or public imports.
Config-gated, in shipped architectures:
- TabM
norm=... crashes at construction (deeptab/architectures/tabm.py:102-104): get_normalization_layer(config) returns an instance (deeptab/nn/normalization.py:28-38), then self.norm_f(self.hparams.layer_sizes[0]) calls its forward with an int.
- TabM
batch_norm=True applies BatchNorm1d(layer_sizes[0]) to the 3-D (B, ensemble, F) output of LinearBatchEnsembleLayer (tabm.py:99-100) → shape error (or wrong-axis normalization if ensemble_size == layer_sizes[0]). Similar latent mismatch for use_glu=True (halved feature dim vs next layer's in_features; also in architectures/mlp.py/tangos.py).
norm_first=True is silently ignored: CustomTransformerEncoderLayer.forward hardcodes post-norm ordering (deeptab/nn/blocks/transformer.py:61-74). FTTransformer additionally applies the same LayerNorm instance twice (as the encoder's final norm and again after pooling, architectures/ft_transformer.py:73-113).
- Custom
BatchNorm crashes on 3-D input in training mode (deeptab/nn/blocks/common.py:220-233, verified: RuntimeError: output with shape [16] doesn't match the broadcast shape [6, 16]); InstanceNorm/GroupNorm require 4-D input — none usable inside sequence-shaped residual blocks despite being offered via norm=.
shuffle_embeddings=True breaks checkpoint reload (architectures/mambular.py:83, mambattention.py:102): self.perm = torch.randperm(...) is not a registered buffer, so a reloaded model draws a new permutation and predicts garbage with no error.
- Trompt's
init_rec is uninitialized memory (architectures/experimental/trompt.py:63): nn.Parameter(torch.empty(P, d_model)) with no nn.init — can start as huge values/NaNs; unreproducible under any seed.
- Single-class
y silently builds an MSE-regression TaskModel (num_classes=1 falls into the regression loss branch) and predict then does classes_[1] on a length-1 array → IndexError. A clear "need at least 2 classes" error at fit would be right.
- NODE
tree_dim > 1: ODSTE's response einsum sums over the response channel (nn/blocks/node.py:685) and architectures/node.py:86-87 sizes the head ignoring tree_dim (crash if ≠ 1).
Importable-but-broken blocks (no shipped architecture uses them, but they're public):
9. RotaryTransformerEncoderLayer creates fresh random QKV/out projections on every forward (transformer.py:684, 701) — untrained, unreproducible eval, while the parent's self_attn weights sit unused in the optimizer.
10. MultiHeadAttentionBatchEnsemble.process_projection scrambles sequence/ensemble axes for non-ensembled projections (common.py:1392-1398): view(N*E, S, D) on an (N, S, E, D) tensor + view(N, E, S, D).permute(...) reinterprets row order (verified numerically, max abs deviation 2.40 from the correct projection). Default batch_ensemble_projections=["query"] sends key/value/out through this path.
11. PositionalInvariance sub-modules crash on construction/forward (common.py:343-367): in-place assignment on a grad-leaf Parameter, shape-incompatible broadcasts unless seq_len == d_model, and the "conv" variant changes sequence length.
12. mLSTM/sLSTM blocks (common.py:1578-1620, 1743-1780, reachable via TabulaRNN(model_type="mLSTM"/"sLSTM")): the recurrent state is re-zeroed every call (stored shape (1,1,H) never matches B), and batch-global reductions (torch.max(nt * q), mean over batch) make each sample's output depend on the other rows in its inference batch.
13. AutoInt: kv_compression builds parameters that forward never uses (silent no-op, dead parameters), and the interaction layer lacks the reference implementation's ReLU (architectures/autoint.py:84-174).
14. BaseModel.encode() / pool_sequence("cls"): encode's no-grad branch has a stray embeddings = self.encoder(x) line (core/base_model.py:269-271) — AttributeError for Mamba models (no encoder attribute), double compute for the rest; CLS pooling always takes token 0 while EmbeddingLayer supports cls_position=1 (append at end) — silently pools an ordinary feature token.
To Reproduce
Each item lists its file/line; the config-gated ones reproduce by constructing the model with the named option, e.g.:
from deeptab.configs.models import TabMConfig
from deeptab.models import TabMRegressor
TabMRegressor(model_config=TabMConfig(norm="LayerNorm")).build_model(X, y) # item 1 crash
Expected behavior
Options exposed in configs should either work or be rejected at construction; public blocks should be functional or moved out of the public namespace. A parametrized smoke test sweeping the config-gated options (norm, batch_norm, use_glu, norm_first, shuffle_embeddings + save/load, tree_dim) would pin all of these.
Screenshots
n/a
Desktop (please complete the following information):
- OS: macOS (Darwin 25.5.0, arm64)
- Python version: 3.11.15
- deeptab Version: 2.0.0 (main @ 4e6a359)
Additional context
Items 4, 10, 11 verified by execution; the rest by close reading during a full-package review. Happy to split any of these out into standalone issues if they're being picked up separately.
Describe the bug
Umbrella issue: non-default config options and importable nn blocks that crash or are silently broken. None of these fire with default configs, which is why the test suite is green — but each is reachable through documented options or public imports.
Config-gated, in shipped architectures:
norm=...crashes at construction (deeptab/architectures/tabm.py:102-104):get_normalization_layer(config)returns an instance (deeptab/nn/normalization.py:28-38), thenself.norm_f(self.hparams.layer_sizes[0])calls itsforwardwith an int.batch_norm=TrueappliesBatchNorm1d(layer_sizes[0])to the 3-D(B, ensemble, F)output ofLinearBatchEnsembleLayer(tabm.py:99-100) → shape error (or wrong-axis normalization ifensemble_size == layer_sizes[0]). Similar latent mismatch foruse_glu=True(halved feature dim vs next layer'sin_features; also inarchitectures/mlp.py/tangos.py).norm_first=Trueis silently ignored:CustomTransformerEncoderLayer.forwardhardcodes post-norm ordering (deeptab/nn/blocks/transformer.py:61-74). FTTransformer additionally applies the same LayerNorm instance twice (as the encoder's finalnormand again after pooling,architectures/ft_transformer.py:73-113).BatchNormcrashes on 3-D input in training mode (deeptab/nn/blocks/common.py:220-233, verified:RuntimeError: output with shape [16] doesn't match the broadcast shape [6, 16]);InstanceNorm/GroupNormrequire 4-D input — none usable inside sequence-shaped residual blocks despite being offered vianorm=.shuffle_embeddings=Truebreaks checkpoint reload (architectures/mambular.py:83,mambattention.py:102):self.perm = torch.randperm(...)is not a registered buffer, so a reloaded model draws a new permutation and predicts garbage with no error.init_recis uninitialized memory (architectures/experimental/trompt.py:63):nn.Parameter(torch.empty(P, d_model))with nonn.init— can start as huge values/NaNs; unreproducible under any seed.ysilently builds an MSE-regression TaskModel (num_classes=1falls into the regression loss branch) andpredictthen doesclasses_[1]on a length-1 array →IndexError. A clear "need at least 2 classes" error at fit would be right.tree_dim > 1:ODSTE's response einsum sums over the response channel (nn/blocks/node.py:685) andarchitectures/node.py:86-87sizes the head ignoringtree_dim(crash if ≠ 1).Importable-but-broken blocks (no shipped architecture uses them, but they're public):
9.
RotaryTransformerEncoderLayercreates fresh random QKV/out projections on every forward (transformer.py:684, 701) — untrained, unreproducible eval, while the parent'sself_attnweights sit unused in the optimizer.10.
MultiHeadAttentionBatchEnsemble.process_projectionscrambles sequence/ensemble axes for non-ensembled projections (common.py:1392-1398):view(N*E, S, D)on an(N, S, E, D)tensor +view(N, E, S, D).permute(...)reinterprets row order (verified numerically, max abs deviation 2.40 from the correct projection). Defaultbatch_ensemble_projections=["query"]sends key/value/out through this path.11.
PositionalInvariancesub-modules crash on construction/forward (common.py:343-367): in-place assignment on a grad-leaf Parameter, shape-incompatible broadcasts unlessseq_len == d_model, and the"conv"variant changes sequence length.12. mLSTM/sLSTM blocks (
common.py:1578-1620, 1743-1780, reachable viaTabulaRNN(model_type="mLSTM"/"sLSTM")): the recurrent state is re-zeroed every call (stored shape(1,1,H)never matchesB), and batch-global reductions (torch.max(nt * q), mean over batch) make each sample's output depend on the other rows in its inference batch.13. AutoInt:
kv_compressionbuilds parameters thatforwardnever uses (silent no-op, dead parameters), and the interaction layer lacks the reference implementation's ReLU (architectures/autoint.py:84-174).14.
BaseModel.encode()/pool_sequence("cls"):encode's no-grad branch has a strayembeddings = self.encoder(x)line (core/base_model.py:269-271) —AttributeErrorfor Mamba models (noencoderattribute), double compute for the rest; CLS pooling always takes token 0 whileEmbeddingLayersupportscls_position=1(append at end) — silently pools an ordinary feature token.To Reproduce
Each item lists its file/line; the config-gated ones reproduce by constructing the model with the named option, e.g.:
Expected behavior
Options exposed in configs should either work or be rejected at construction; public blocks should be functional or moved out of the public namespace. A parametrized smoke test sweeping the config-gated options (norm, batch_norm, use_glu, norm_first, shuffle_embeddings + save/load, tree_dim) would pin all of these.
Screenshots
n/a
Desktop (please complete the following information):
Additional context
Items 4, 10, 11 verified by execution; the rest by close reading during a full-package review. Happy to split any of these out into standalone issues if they're being picked up separately.