structboost 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.
@@ -0,0 +1,88 @@
1
+ """BAE Encoder module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import torch
8
+ from torch import nn
9
+
10
+ if TYPE_CHECKING:
11
+ from ._types import BAEConfig
12
+
13
+
14
+ class SplitSoftmax(nn.Module):
15
+ """Split-softmax transformation for compositional latent codes.
16
+
17
+ Interleaves each latent dimension z_i with its negation −z_i, then
18
+ applies softmax over the 2d-dimensional vector to produce a
19
+ compositional representation h ∈ Δ^{2d−1}.
20
+
21
+ σ_split(z_1, ..., z_d) = σ((z_1, −z_1, ..., z_d, −z_d))
22
+
23
+ Reference: Supplementary Data of Brunn et al. (2025),
24
+ https://academic.oup.com/bioinformatics/article/5/1/vbaf230/8262953
25
+ """
26
+
27
+ def forward(self, z: torch.Tensor) -> torch.Tensor:
28
+ """Map z ∈ ℝ^d to h ∈ Δ^{2d−1} via interleaved-negation softmax."""
29
+ s = torch.stack([z, -z], dim=-1).reshape(z.shape[0], -1) # (n, 2d)
30
+ return torch.softmax(s, dim=-1)
31
+
32
+
33
+ class BAEEncoder(nn.Module):
34
+ """Linear encoder for Boosting Autoencoder.
35
+
36
+ Single linear projection from gene expression to latent space.
37
+ Weights are optimized via componentwise boosting (not gradient descent).
38
+
39
+ The encoder maps only gene expression to latent space: z = W @ x.
40
+ Obs covariates never modify z. Depending on ``batch_integration_mode`` the
41
+ encoded covariate enters allboost as a mandatory regressor, or the decoder as
42
+ a conditioning input, or both, but never this layer. That is what keeps the
43
+ encoder deployable on data carrying no covariate labels.
44
+
45
+ Parameters
46
+ ----------
47
+ n_input
48
+ Number of input features (genes).
49
+ config
50
+ BAE configuration object.
51
+ """
52
+
53
+ def __init__(self, n_input: int, config: BAEConfig) -> None:
54
+ super().__init__()
55
+ self.n_input = n_input
56
+ self.config = config
57
+ # Single linear layer: W ∈ ℝ^{n_genes × latent_dim}, no bias
58
+ self.linear = nn.Linear(n_input, config.latent_dim, bias=False)
59
+ self.reset_weights()
60
+
61
+ def reset_weights(self) -> None:
62
+ """Reset encoder weights to zero."""
63
+ nn.init.zeros_(self.linear.weight)
64
+
65
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
66
+ """Encode input to latent representation.
67
+
68
+ Parameters
69
+ ----------
70
+ x
71
+ Input tensor of shape (n_cells, n_genes).
72
+
73
+ Returns
74
+ -------
75
+ Latent representation of shape (n_cells, latent_dim).
76
+ """
77
+ return self.linear(x)
78
+
79
+ def set_weights(self, W: torch.Tensor) -> None:
80
+ """Set encoder weights directly (from boosting output).
81
+
82
+ Parameters
83
+ ----------
84
+ W
85
+ Weight tensor of shape (latent_dim, n_genes).
86
+ """
87
+ with torch.no_grad():
88
+ self.linear.weight.copy_(W)