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.
- structboost/__init__.py +94 -0
- structboost/_annotation.py +266 -0
- structboost/_boosting.py +552 -0
- structboost/_decoder.py +109 -0
- structboost/_encoder.py +88 -0
- structboost/_explorer.py +842 -0
- structboost/_io.py +302 -0
- structboost/_model.py +3483 -0
- structboost/_persistence.py +326 -0
- structboost/_plotting.py +301 -0
- structboost/_simulation.py +867 -0
- structboost/_stability.py +412 -0
- structboost/_types.py +393 -0
- structboost/_utils.py +509 -0
- structboost/py.typed +0 -0
- structboost-0.1.0.dist-info/METADATA +219 -0
- structboost-0.1.0.dist-info/RECORD +19 -0
- structboost-0.1.0.dist-info/WHEEL +4 -0
- structboost-0.1.0.dist-info/licenses/LICENSE +21 -0
structboost/_encoder.py
ADDED
|
@@ -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)
|