autoencodix 0.2.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.

Potentially problematic release.


This version of autoencodix might be problematic. Click here for more details.

Files changed (91) hide show
  1. autoencodix/__init__.py +27 -0
  2. autoencodix/base/.ipynb_checkpoints/_base_pipeline-checkpoint.py +1274 -0
  3. autoencodix/base/.ipynb_checkpoints/_base_trainer-checkpoint.py +247 -0
  4. autoencodix/base/__init__.py +21 -0
  5. autoencodix/base/_base_autoencoder.py +138 -0
  6. autoencodix/base/_base_dataset.py +103 -0
  7. autoencodix/base/_base_evaluator.py +34 -0
  8. autoencodix/base/_base_loss.py +311 -0
  9. autoencodix/base/_base_pipeline.py +1355 -0
  10. autoencodix/base/_base_predictor.py +6 -0
  11. autoencodix/base/_base_preprocessor.py +1033 -0
  12. autoencodix/base/_base_trainer.py +259 -0
  13. autoencodix/base/_base_visualizer.py +578 -0
  14. autoencodix/base/sampleconfig.yaml +132 -0
  15. autoencodix/configs/__init__.py +21 -0
  16. autoencodix/configs/default_config.py +652 -0
  17. autoencodix/configs/disentanglix_config.py +15 -0
  18. autoencodix/configs/maskix_config.py +39 -0
  19. autoencodix/configs/ontix_config.py +35 -0
  20. autoencodix/configs/stackix_config.py +33 -0
  21. autoencodix/configs/vanillix_config.py +18 -0
  22. autoencodix/configs/varix_config.py +15 -0
  23. autoencodix/configs/xmodalix_config.py +43 -0
  24. autoencodix/disentanglix.py +72 -0
  25. autoencodix/evaluate/.ipynb_checkpoints/evaluate-checkpoint.py +532 -0
  26. autoencodix/evaluate/__init__.py +4 -0
  27. autoencodix/evaluate/_general_evaluator.py +730 -0
  28. autoencodix/evaluate/_xmodalix_evaluator.py +376 -0
  29. autoencodix/imagix.py +76 -0
  30. autoencodix/losses/disentanglix_loss.py +215 -0
  31. autoencodix/losses/maskix_loss.py +94 -0
  32. autoencodix/losses/vanillix_loss.py +28 -0
  33. autoencodix/losses/varix_loss.py +102 -0
  34. autoencodix/losses/xmodal_loss.py +304 -0
  35. autoencodix/maskix.py +108 -0
  36. autoencodix/modeling/.ipynb_checkpoints/__init__-checkpoint.py +16 -0
  37. autoencodix/modeling/.ipynb_checkpoints/_captum_forward-checkpoint.py +16 -0
  38. autoencodix/modeling/__init__.py +20 -0
  39. autoencodix/modeling/_captum_forward.py +20 -0
  40. autoencodix/modeling/_classifier.py +45 -0
  41. autoencodix/modeling/_imagevae_architecture.py +296 -0
  42. autoencodix/modeling/_imgfast_architecture.py +288 -0
  43. autoencodix/modeling/_layer_factory.py +71 -0
  44. autoencodix/modeling/_maskix_architecture.py +190 -0
  45. autoencodix/modeling/_ontix_architecture.py +305 -0
  46. autoencodix/modeling/_vanillix_architecture.py +136 -0
  47. autoencodix/modeling/_varix_architecture.py +192 -0
  48. autoencodix/notebooks/__init__.py +0 -0
  49. autoencodix/ontix.py +136 -0
  50. autoencodix/py.typed +0 -0
  51. autoencodix/stackix.py +122 -0
  52. autoencodix/trainers/__init__.py +6 -0
  53. autoencodix/trainers/_general_trainer.py +525 -0
  54. autoencodix/trainers/_maskix_trainer.py +116 -0
  55. autoencodix/trainers/_ontix_trainer.py +77 -0
  56. autoencodix/trainers/_stackix_orchestrator.py +522 -0
  57. autoencodix/trainers/_stackix_trainer.py +209 -0
  58. autoencodix/trainers/_xmodal_trainer.py +1006 -0
  59. autoencodix/utils/.ipynb_checkpoints/_explainer-checkpoint.py +118 -0
  60. autoencodix/utils/.ipynb_checkpoints/_model_output-checkpoint.py +30 -0
  61. autoencodix/utils/.ipynb_checkpoints/_utils-checkpoint.py +626 -0
  62. autoencodix/utils/.ipynb_checkpoints/adata_converter-checkpoint.py +99 -0
  63. autoencodix/utils/.ipynb_checkpoints/feature_importance-checkpoint.py +823 -0
  64. autoencodix/utils/__init__.py +22 -0
  65. autoencodix/utils/_annealer.py +193 -0
  66. autoencodix/utils/_bulkreader.py +232 -0
  67. autoencodix/utils/_explainer.py +246 -0
  68. autoencodix/utils/_imgreader.py +404 -0
  69. autoencodix/utils/_internals.py +23 -0
  70. autoencodix/utils/_llm_explainer.py +318 -0
  71. autoencodix/utils/_losses.py +20 -0
  72. autoencodix/utils/_model_output.py +30 -0
  73. autoencodix/utils/_result.py +393 -0
  74. autoencodix/utils/_screader.py +76 -0
  75. autoencodix/utils/_traindynamics.py +187 -0
  76. autoencodix/utils/_utils.py +690 -0
  77. autoencodix/utils/adata_converter.py +111 -0
  78. autoencodix/utils/example_data.py +398 -0
  79. autoencodix/utils/feature_importance.py +829 -0
  80. autoencodix/utils/prompts.py +21 -0
  81. autoencodix/vanillix.py +141 -0
  82. autoencodix/varix.py +80 -0
  83. autoencodix/visualize/__init__.py +5 -0
  84. autoencodix/visualize/_general_visualizer.py +913 -0
  85. autoencodix/visualize/_imagix_visualizer.py +111 -0
  86. autoencodix/visualize/_xmodal_visualizer.py +841 -0
  87. autoencodix/visualize/visualize.py +1214 -0
  88. autoencodix/xmodalix.py +318 -0
  89. autoencodix-0.2.0.dist-info/METADATA +143 -0
  90. autoencodix-0.2.0.dist-info/RECORD +91 -0
  91. autoencodix-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,311 @@
1
+ from abc import abstractmethod, ABC
2
+ import itertools
3
+ from autoencodix.configs.default_config import DefaultConfig
4
+ import torch
5
+ from torch import nn
6
+ from autoencodix.utils._annealer import AnnealingScheduler
7
+
8
+ from typing import Optional, Any
9
+
10
+
11
+ class BaseLoss(nn.Module, ABC):
12
+ """Provides common loss computation functionality for autoencoders.
13
+
14
+ Implements standard loss calculations including reconstruction loss,
15
+ KL divergence, and Maximum Mean Discrepancy (MMD), while requiring
16
+ subclasses to implement the specific forward method.
17
+
18
+ Attributes:
19
+ config: Configuration parameters for the loss function.
20
+ recon_loss: Module for computing reconstruction loss (MSE or BCE).
21
+ reduction_fn: Function to apply reduction (mean or sum).
22
+ compute_kernel: Function to compute kernel for MMD loss.
23
+ annealing_scheduler: Helper for loss calculation with annealing.
24
+ """
25
+
26
+ def __init__(self, config: DefaultConfig, annealing_scheduler=None):
27
+ """Initializes the loss module with the specified configuration.
28
+
29
+ Args:
30
+ config: Configuration parameters for the loss function.
31
+ annealing_scheduler: Helper class for loss calculation with annealing.
32
+
33
+ Raises:
34
+ NotImplementedError: If unsupported loss reduction or reconstruction
35
+ loss type is specified.
36
+ """
37
+ super().__init__()
38
+ self.annealing_scheduler = annealing_scheduler or AnnealingScheduler()
39
+ self.config = config
40
+ self.recon_loss: nn.Module
41
+
42
+ if self.config.loss_reduction == "mean":
43
+ self.reduction_fn = torch.mean
44
+ elif self.config.loss_reduction == "sum":
45
+ self.reduction_fn = torch.sum
46
+ else:
47
+ raise NotImplementedError(
48
+ f"Invalid loss reduction type: {self.config.loss_reduction}. "
49
+ f"Only 'mean' and 'sum' are supported."
50
+ )
51
+
52
+ if self.config.reconstruction_loss == "mse":
53
+ self.recon_loss = nn.MSELoss(reduction=config.loss_reduction)
54
+ elif self.config.reconstruction_loss == "bce":
55
+ self.recon_loss = nn.BCEWithLogitsLoss(reduction=config.loss_reduction)
56
+ else:
57
+ raise NotImplementedError(
58
+ f"Invalid reconstruction loss type: {self.config.reconstruction_loss}. "
59
+ f"Only 'mse' and 'bce' are supported. Please check the value of "
60
+ f"'config.reconstruction_loss' for typos or unsupported types."
61
+ )
62
+
63
+ self.compute_kernel = self._mmd_kernel
64
+
65
+ def _mmd_kernel(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
66
+ """Computes Gaussian kernel for Maximum Mean Discrepancy calculation.
67
+
68
+ Calculates the kernel matrix between two sets of samples, using a
69
+ Gaussian kernel with normalization by feature dimension.
70
+
71
+ Args:
72
+ x: First set of input samples.
73
+ y: Second set of input samples.
74
+
75
+ Returns:
76
+ Kernel matrix of shape (x.shape[0], y.shape[0]).
77
+ """
78
+ x_size = x.size(0)
79
+ y_size = y.size(0)
80
+ dim = x.size(1)
81
+
82
+ x = x.unsqueeze(1)
83
+ y = y.unsqueeze(0)
84
+ tiled_x = x.expand(x_size, y_size, dim)
85
+ tiled_y = y.expand(x_size, y_size, dim)
86
+
87
+ kernel_input = (tiled_x - tiled_y).pow(2).mean(2) / float(dim)
88
+ return torch.exp(-kernel_input)
89
+
90
+ def compute_mmd_loss(
91
+ self, z: torch.Tensor, true_samples: torch.Tensor
92
+ ) -> torch.Tensor:
93
+ """Computes Maximum Mean Discrepancy loss.
94
+
95
+ Args:
96
+ z: Samples from the encoded distribution.
97
+ true_samples: Samples from the prior distribution.
98
+
99
+ Returns:
100
+ The MMD loss value.
101
+
102
+ Raises:
103
+ NotImplementedError: If unsupported loss reduction type is specified.
104
+ """
105
+ true_samples_kernel = self.compute_kernel(x=true_samples, y=true_samples)
106
+ z_device = z.device
107
+ true_samples = true_samples.to(z_device)
108
+ z_kernel = self.compute_kernel(z, z)
109
+ ztr_kernel = self.compute_kernel(x=true_samples, y=z)
110
+
111
+ if self.config.loss_reduction == "mean":
112
+ return true_samples_kernel.mean() + z_kernel.mean() - 2 * ztr_kernel.mean()
113
+ elif self.config.loss_reduction == "sum":
114
+ return true_samples_kernel.sum() + z_kernel.sum() - 2 * ztr_kernel.sum()
115
+ else:
116
+ raise NotImplementedError(
117
+ f"Invalid loss reduction type: {self.config.loss_reduction}. "
118
+ f"Only 'mean' and 'sum' are supported."
119
+ )
120
+
121
+ def compute_kl_loss(self, mu: torch.Tensor, logvar: torch.Tensor) -> torch.Tensor:
122
+ """Computes KL divergence loss between N(mu, logvar) and N(0, 1).
123
+
124
+ Args:
125
+ mu: Mean tensor.
126
+ logvar: Log variance tensor.
127
+
128
+ Returns:
129
+ The KL divergence loss value.
130
+
131
+ Raises:
132
+ ValueError: If mu and logvar do not have the same shape.
133
+ """
134
+ if mu.shape != logvar.shape:
135
+ raise ValueError(
136
+ f"Shape mismatch: mu has shape {mu.shape}, but logvar has shape {logvar.shape}."
137
+ )
138
+ return -0.5 * self.reduction_fn(1 + logvar - mu.pow(2) - logvar.exp())
139
+
140
+ def compute_variational_loss(
141
+ self,
142
+ mu: Optional[torch.Tensor],
143
+ logvar: Optional[torch.Tensor],
144
+ z: Optional[torch.Tensor] = None,
145
+ true_samples: Optional[torch.Tensor] = None,
146
+ ) -> torch.Tensor:
147
+ """Computes either KL or MMD loss based on configuration.
148
+
149
+ Args:
150
+ mu: Mean tensor for variational loss.
151
+ logvar: Log variance tensor for variational loss.
152
+ z: Encoded samples for MMD loss.
153
+ true_samples: Prior samples for MMD loss.
154
+
155
+ Returns:
156
+ The computed variational loss.
157
+
158
+ Raises:
159
+ ValueError: If required parameters are missing or if mu and logvar have shape mismatch.
160
+ NotImplementedError: If unsupported VAE loss type is specified.
161
+ """
162
+
163
+ if self.config.default_vae_loss == "kl":
164
+ if mu is None:
165
+ raise ValueError("mu must be provided for VAE loss")
166
+ if logvar is None:
167
+ raise ValueError("logvar must be provided for VAE loss")
168
+ if mu.shape != logvar.shape:
169
+ raise ValueError(
170
+ f"Shape mismatch: mu has shape {mu.shape}, but logvar has shape {logvar.shape}"
171
+ )
172
+
173
+ return self.compute_kl_loss(mu=mu, logvar=logvar)
174
+
175
+ elif self.config.default_vae_loss == "mmd":
176
+ if z is None:
177
+ raise ValueError("z must be provided for MMD loss")
178
+ if true_samples is None:
179
+ raise ValueError("true_samples must be provided for MMD loss")
180
+ return self.compute_mmd_loss(z=z, true_samples=true_samples)
181
+ else:
182
+ raise NotImplementedError(
183
+ f"VAE loss type {self.config.default_vae_loss} is not implemented. "
184
+ f"Only 'kl' and 'mmd' are supported."
185
+ )
186
+
187
+ def compute_paired_loss(
188
+ self,
189
+ latentspaces: dict[str, torch.Tensor],
190
+ sample_ids: dict[str, list],
191
+ ) -> torch.Tensor:
192
+ """
193
+ Calculates the paired distance loss across all pairs of modalities in a batch.
194
+
195
+ Args:
196
+ latentspaces: A dictionary mapping modality names to their latent space tensors.
197
+ e.g., {'RNA': tensor_rna, 'ATAC': tensor_atac}
198
+ sample_ids: A dictionary mapping modality names to their list of sample IDs.
199
+
200
+ Returns:
201
+ A single scalar tensor representing the total paired loss.
202
+ """
203
+
204
+ loss_helper = []
205
+ modality_names = list(latentspaces.keys())
206
+
207
+ # 1. Iterate through all unique pairs of modalities
208
+ for mod_a, mod_b in itertools.combinations(modality_names, 2):
209
+ ids_a = sample_ids[mod_a]
210
+ ids_b = sample_ids[mod_b]
211
+
212
+ # 2. Find the intersection of sample IDs
213
+ common_ids = set(ids_a) & set(ids_b)
214
+
215
+ if not common_ids:
216
+ print("no common ids")
217
+ continue
218
+
219
+ # 3. Create a mapping from sample ID to index for efficient lookup
220
+ id_to_idx_a = {sample_id: i for i, sample_id in enumerate(ids_a)}
221
+ id_to_idx_b = {sample_id: i for i, sample_id in enumerate(ids_b)}
222
+
223
+ # Get the corresponding indices for the common samples
224
+ indices_a = [id_to_idx_a[common_id] for common_id in common_ids]
225
+ indices_b = [id_to_idx_b[common_id] for common_id in common_ids]
226
+
227
+ # 4. Select the latent vectors for the paired samples
228
+ paired_latents_a = latentspaces[mod_a][indices_a]
229
+ paired_latents_b = latentspaces[mod_b][indices_b]
230
+
231
+ # 5. Calculate the distance between the aligned latent vectors
232
+ # L1 distance, averaged over latent dimensions and then over samples
233
+ distance = torch.abs(paired_latents_a - paired_latents_b).mean(dim=1)
234
+ pair_loss = self.reduction_fn(distance)
235
+ loss_helper.append(pair_loss)
236
+ if not loss_helper:
237
+ return torch.tensor(0.0)
238
+ return torch.stack(loss_helper).mean()
239
+
240
+ @staticmethod
241
+ def _compute_log_gauss_dense(
242
+ z: torch.Tensor, mu: torch.Tensor, logvar: torch.Tensor
243
+ ) -> torch.Tensor:
244
+ """Computes the log probability of a Gaussian distribution.
245
+
246
+ Args:
247
+ z: Latent variable tensor.
248
+ mu: Mean tensor.
249
+ logvar: Log variance tensor.
250
+
251
+ Returns:
252
+ Log probability of the Gaussian distribution.
253
+ """
254
+ return -0.5 * (
255
+ torch.log(torch.tensor([2 * torch.pi]).to(z.device))
256
+ + logvar
257
+ + (z - mu) ** 2 * torch.exp(-logvar)
258
+ )
259
+
260
+ @staticmethod
261
+ def _compute_log_import_weight_mat(batch_size: int, n_samples: int) -> torch.Tensor:
262
+ """Computes the log import weight matrix for disentangled loss.
263
+ Similar to: https://github.com/rtqichen/beta-tcvae
264
+ Args:
265
+ batch_size: Number of samples in the batch.
266
+ n_samples: Total number of samples in the dataset.
267
+
268
+ Returns:
269
+ Log import weight matrix of shape (batch_size, n_samples).
270
+ """
271
+
272
+ N = n_samples
273
+ M = batch_size - 1
274
+ strat_weight = (N - M) / (N * M)
275
+ W = torch.Tensor(batch_size, batch_size).fill_(1 / M)
276
+ W.view(-1)[:: M + 1] = 1 / N
277
+ W.view(-1)[1 :: M + 1] = strat_weight
278
+ W[M - 1, 0] = strat_weight
279
+ return W.log()
280
+
281
+ @abstractmethod
282
+ def forward(
283
+ self,
284
+ *args,
285
+ **kwargs,
286
+ ) -> Any:
287
+ """Calculates the loss for the autoencoder.
288
+
289
+ This method must be implemented by subclasses to define the specific
290
+ loss computation logic for the autoencoder. The implementation should
291
+ compute the total loss as well as any individual loss components
292
+ (e.g., reconstruction loss, KL divergence, etc.) based on the model's
293
+ output and the provided targets.
294
+
295
+ Args:
296
+ *kwargs depending on the loss type and pipeline
297
+
298
+
299
+ Returns:
300
+ - The total loss value as a scalar tensor.
301
+ - A dictionary of individual loss components, where the keys are
302
+ descriptive strings (e.g., "reconstruction_loss", "kl_loss") and
303
+ the values are the corresponding loss tensors.
304
+ - Implementation in subclasses is flexible, so for new loss classes this can differ.
305
+
306
+ Note:
307
+ Subclasses must implement this method to define the specific loss
308
+ computation logic for their use case.
309
+ """
310
+ # TODO maybe standardize the return types more i.e. request a scalar and a dict
311
+ pass