diffbio 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.
Files changed (202) hide show
  1. diffbio/__init__.py +39 -0
  2. diffbio/configs.py +75 -0
  3. diffbio/constants.py +204 -0
  4. diffbio/core/__init__.py +127 -0
  5. diffbio/core/base_operators.py +612 -0
  6. diffbio/core/data_types.py +260 -0
  7. diffbio/core/gnn_components.py +629 -0
  8. diffbio/core/graph_utils.py +149 -0
  9. diffbio/core/neural_components.py +270 -0
  10. diffbio/core/optimal_transport.py +133 -0
  11. diffbio/core/soft_ops/__init__.py +216 -0
  12. diffbio/core/soft_ops/_projections_permutahedron.py +1864 -0
  13. diffbio/core/soft_ops/_projections_simplex.py +240 -0
  14. diffbio/core/soft_ops/_projections_transport.py +508 -0
  15. diffbio/core/soft_ops/_sorting_network.py +204 -0
  16. diffbio/core/soft_ops/_types.py +15 -0
  17. diffbio/core/soft_ops/_utils.py +342 -0
  18. diffbio/core/soft_ops/autograd_safe.py +120 -0
  19. diffbio/core/soft_ops/comparison.py +235 -0
  20. diffbio/core/soft_ops/elementwise.py +309 -0
  21. diffbio/core/soft_ops/logical.py +146 -0
  22. diffbio/core/soft_ops/quantile.py +376 -0
  23. diffbio/core/soft_ops/selection.py +236 -0
  24. diffbio/core/soft_ops/sorting.py +926 -0
  25. diffbio/core/soft_ops/straight_through.py +261 -0
  26. diffbio/core/uncertainty.py +279 -0
  27. diffbio/evaluation/__init__.py +42 -0
  28. diffbio/evaluation/adapters.py +409 -0
  29. diffbio/evaluation/graders.py +223 -0
  30. diffbio/evaluation/problem.py +157 -0
  31. diffbio/evaluation/runner.py +277 -0
  32. diffbio/losses/__init__.py +59 -0
  33. diffbio/losses/alignment_losses.py +222 -0
  34. diffbio/losses/biological_regularization.py +288 -0
  35. diffbio/losses/metric_losses.py +139 -0
  36. diffbio/losses/singlecell_losses.py +387 -0
  37. diffbio/losses/statistical_losses.py +345 -0
  38. diffbio/operators/__init__.py +60 -0
  39. diffbio/operators/_count_vae.py +197 -0
  40. diffbio/operators/_loss_balancing.py +65 -0
  41. diffbio/operators/_masked_gene_transformer.py +118 -0
  42. diffbio/operators/_transformer_validation.py +50 -0
  43. diffbio/operators/alignment/__init__.py +51 -0
  44. diffbio/operators/alignment/profile_hmm.py +350 -0
  45. diffbio/operators/alignment/scoring.py +127 -0
  46. diffbio/operators/alignment/smith_waterman.py +261 -0
  47. diffbio/operators/alignment/soft_msa.py +419 -0
  48. diffbio/operators/assembly/__init__.py +27 -0
  49. diffbio/operators/assembly/gnn_assembly.py +252 -0
  50. diffbio/operators/assembly/metagenomic_binning.py +296 -0
  51. diffbio/operators/crispr/__init__.py +17 -0
  52. diffbio/operators/crispr/guide_scoring.py +269 -0
  53. diffbio/operators/drug_discovery/__init__.py +133 -0
  54. diffbio/operators/drug_discovery/_graph_utils.py +142 -0
  55. diffbio/operators/drug_discovery/admet_predictor.py +285 -0
  56. diffbio/operators/drug_discovery/attentive_fp.py +411 -0
  57. diffbio/operators/drug_discovery/dti.py +261 -0
  58. diffbio/operators/drug_discovery/fingerprint.py +490 -0
  59. diffbio/operators/drug_discovery/maccs_keys.py +267 -0
  60. diffbio/operators/drug_discovery/message_passing.py +200 -0
  61. diffbio/operators/drug_discovery/primitives.py +242 -0
  62. diffbio/operators/drug_discovery/property_predictor.py +163 -0
  63. diffbio/operators/drug_discovery/similarity.py +193 -0
  64. diffbio/operators/epigenomics/__init__.py +35 -0
  65. diffbio/operators/epigenomics/chromatin_state.py +491 -0
  66. diffbio/operators/epigenomics/contextual.py +288 -0
  67. diffbio/operators/epigenomics/fno_peak_calling.py +153 -0
  68. diffbio/operators/epigenomics/peak_calling.py +555 -0
  69. diffbio/operators/foundation_models/__init__.py +119 -0
  70. diffbio/operators/foundation_models/adapters.py +114 -0
  71. diffbio/operators/foundation_models/contracts.py +245 -0
  72. diffbio/operators/foundation_models/embedding_probe.py +83 -0
  73. diffbio/operators/foundation_models/experimental.py +128 -0
  74. diffbio/operators/foundation_models/foundation_model.py +332 -0
  75. diffbio/operators/foundation_models/frozen.py +59 -0
  76. diffbio/operators/foundation_models/precomputed.py +270 -0
  77. diffbio/operators/foundation_models/transformer_encoder.py +564 -0
  78. diffbio/operators/mapping/__init__.py +17 -0
  79. diffbio/operators/mapping/neural_mapper.py +493 -0
  80. diffbio/operators/metabolomics/__init__.py +39 -0
  81. diffbio/operators/metabolomics/spectral_similarity.py +315 -0
  82. diffbio/operators/molecular_dynamics/__init__.py +51 -0
  83. diffbio/operators/molecular_dynamics/force_field.py +265 -0
  84. diffbio/operators/molecular_dynamics/integrator.py +304 -0
  85. diffbio/operators/molecular_dynamics/primitives.py +115 -0
  86. diffbio/operators/multiomics/__init__.py +38 -0
  87. diffbio/operators/multiomics/hic_contact.py +377 -0
  88. diffbio/operators/multiomics/multiomics_vae.py +325 -0
  89. diffbio/operators/multiomics/spatial_deconvolution.py +316 -0
  90. diffbio/operators/multiomics/spatial_gene_detection.py +493 -0
  91. diffbio/operators/normalization/__init__.py +42 -0
  92. diffbio/operators/normalization/embedding.py +222 -0
  93. diffbio/operators/normalization/phate.py +400 -0
  94. diffbio/operators/normalization/umap.py +261 -0
  95. diffbio/operators/normalization/vae_normalizer.py +258 -0
  96. diffbio/operators/population/__init__.py +17 -0
  97. diffbio/operators/population/ancestry_estimation.py +274 -0
  98. diffbio/operators/preprocessing/__init__.py +76 -0
  99. diffbio/operators/preprocessing/adapter_removal.py +311 -0
  100. diffbio/operators/preprocessing/duplicate_filter.py +317 -0
  101. diffbio/operators/preprocessing/error_correction.py +287 -0
  102. diffbio/operators/protein/__init__.py +31 -0
  103. diffbio/operators/protein/secondary_structure.py +509 -0
  104. diffbio/operators/quality_filter.py +128 -0
  105. diffbio/operators/rna_structure/__init__.py +35 -0
  106. diffbio/operators/rna_structure/rna_folding.py +509 -0
  107. diffbio/operators/rnaseq/__init__.py +23 -0
  108. diffbio/operators/rnaseq/motif_discovery.py +251 -0
  109. diffbio/operators/rnaseq/splicing_psi.py +216 -0
  110. diffbio/operators/singlecell/__init__.py +193 -0
  111. diffbio/operators/singlecell/ambient_removal.py +333 -0
  112. diffbio/operators/singlecell/archetypes.py +191 -0
  113. diffbio/operators/singlecell/batch_correction.py +288 -0
  114. diffbio/operators/singlecell/cell_annotation.py +519 -0
  115. diffbio/operators/singlecell/communication.py +704 -0
  116. diffbio/operators/singlecell/differential_distribution.py +243 -0
  117. diffbio/operators/singlecell/doublet_detection.py +657 -0
  118. diffbio/operators/singlecell/downsampling.py +166 -0
  119. diffbio/operators/singlecell/enhanced_batch_correction.py +519 -0
  120. diffbio/operators/singlecell/grn_inference.py +336 -0
  121. diffbio/operators/singlecell/imputation.py +429 -0
  122. diffbio/operators/singlecell/knockdown_filter.py +176 -0
  123. diffbio/operators/singlecell/ot_trajectory.py +277 -0
  124. diffbio/operators/singlecell/simulation.py +444 -0
  125. diffbio/operators/singlecell/sindy_grn.py +247 -0
  126. diffbio/operators/singlecell/soft_clustering.py +211 -0
  127. diffbio/operators/singlecell/spatial_domains.py +677 -0
  128. diffbio/operators/singlecell/switch_de.py +184 -0
  129. diffbio/operators/singlecell/trajectory.py +447 -0
  130. diffbio/operators/singlecell/velocity.py +361 -0
  131. diffbio/operators/statistical/__init__.py +35 -0
  132. diffbio/operators/statistical/em_quantification.py +260 -0
  133. diffbio/operators/statistical/hmm.py +234 -0
  134. diffbio/operators/statistical/nb_glm.py +272 -0
  135. diffbio/operators/variant/__init__.py +64 -0
  136. diffbio/operators/variant/classifier.py +333 -0
  137. diffbio/operators/variant/cnn_classifier.py +255 -0
  138. diffbio/operators/variant/cnv_segmentation.py +678 -0
  139. diffbio/operators/variant/deepvariant_pileup.py +426 -0
  140. diffbio/operators/variant/pileup.py +240 -0
  141. diffbio/operators/variant/quality_recalibration.py +274 -0
  142. diffbio/pipelines/__init__.py +65 -0
  143. diffbio/pipelines/differential_expression.py +279 -0
  144. diffbio/pipelines/enhanced_variant_calling.py +326 -0
  145. diffbio/pipelines/perturbation.py +407 -0
  146. diffbio/pipelines/preprocessing.py +267 -0
  147. diffbio/pipelines/single_cell.py +366 -0
  148. diffbio/pipelines/variant_calling.py +490 -0
  149. diffbio/samplers/__init__.py +9 -0
  150. diffbio/samplers/perturbation_sampler.py +142 -0
  151. diffbio/sequences/__init__.py +34 -0
  152. diffbio/sequences/dna.py +239 -0
  153. diffbio/sources/__init__.py +149 -0
  154. diffbio/sources/_anndata_shared.py +89 -0
  155. diffbio/sources/_batch_iteration.py +37 -0
  156. diffbio/sources/_benchmark_source.py +152 -0
  157. diffbio/sources/_indexed_batch_source.py +38 -0
  158. diffbio/sources/_utils.py +45 -0
  159. diffbio/sources/anndata_interop.py +387 -0
  160. diffbio/sources/anndata_source.py +361 -0
  161. diffbio/sources/archive_ii.py +174 -0
  162. diffbio/sources/balifam.py +207 -0
  163. diffbio/sources/bam.py +265 -0
  164. diffbio/sources/bengrn_ground_truth.py +306 -0
  165. diffbio/sources/contextual_epigenomics.py +242 -0
  166. diffbio/sources/dti.py +359 -0
  167. diffbio/sources/embeddings.py +203 -0
  168. diffbio/sources/encode_peaks.py +223 -0
  169. diffbio/sources/fasta.py +226 -0
  170. diffbio/sources/immune_human.py +172 -0
  171. diffbio/sources/indexed_embeddings.py +128 -0
  172. diffbio/sources/indexed_view.py +191 -0
  173. diffbio/sources/molnet.py +493 -0
  174. diffbio/sources/multiomics.py +279 -0
  175. diffbio/sources/pancreas.py +108 -0
  176. diffbio/sources/perturbation/__init__.py +69 -0
  177. diffbio/sources/perturbation/_types.py +51 -0
  178. diffbio/sources/perturbation/_utils.py +125 -0
  179. diffbio/sources/perturbation/concat_source.py +115 -0
  180. diffbio/sources/perturbation/control_mapping.py +215 -0
  181. diffbio/sources/perturbation/experiment_config.py +261 -0
  182. diffbio/sources/perturbation/h5_metadata_cache.py +218 -0
  183. diffbio/sources/perturbation/output_space.py +52 -0
  184. diffbio/sources/perturbation/perturbation_source.py +513 -0
  185. diffbio/sources/seqfish.py +145 -0
  186. diffbio/sources/sequence_foundation.py +68 -0
  187. diffbio/sources/singlecell_foundation.py +68 -0
  188. diffbio/splitters/__init__.py +63 -0
  189. diffbio/splitters/base.py +251 -0
  190. diffbio/splitters/molecular.py +330 -0
  191. diffbio/splitters/perturbation.py +199 -0
  192. diffbio/splitters/random.py +217 -0
  193. diffbio/splitters/sequence.py +201 -0
  194. diffbio/utils/__init__.py +55 -0
  195. diffbio/utils/dependency_runtime.py +115 -0
  196. diffbio/utils/nn_utils.py +157 -0
  197. diffbio/utils/quality.py +45 -0
  198. diffbio/utils/training.py +585 -0
  199. diffbio-0.1.0.dist-info/METADATA +480 -0
  200. diffbio-0.1.0.dist-info/RECORD +202 -0
  201. diffbio-0.1.0.dist-info/WHEEL +4 -0
  202. diffbio-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,277 @@
1
+ """Optimal-transport trajectory inference between two single-cell timepoints.
2
+
3
+ This module implements Waddington-OT-style trajectory inference using
4
+ entropy-regularised optimal transport. Given gene-expression matrices at
5
+ two timepoints, the operator:
6
+
7
+ 1. Computes a squared-Euclidean expression cost matrix between all cell
8
+ pairs across the two timepoints.
9
+ 2. Solves for the transport plan via the shared ``SinkhornLayer`` from
10
+ ``diffbio.core.optimal_transport`` (DRY).
11
+ 3. Estimates per-cell growth rates from the transport-plan row sums.
12
+ 4. Interpolates an intermediate cell distribution at a configurable time
13
+ between the two observations.
14
+
15
+ All operations are fully differentiable through JAX, enabling gradient-based
16
+ optimisation of upstream embeddings or transport parameters.
17
+
18
+ References:
19
+ Schiebinger et al., "Optimal-Transport Analysis of Single-Cell Gene
20
+ Expression Identifies Developmental Trajectories in Reprogramming",
21
+ Cell 2019.
22
+ """
23
+
24
+ import logging
25
+ from dataclasses import dataclass
26
+ from typing import Any
27
+
28
+ import jax.numpy as jnp
29
+ from datarax.core.config import OperatorConfig
30
+ from datarax.core.operator import OperatorModule
31
+ from flax import nnx
32
+ from jaxtyping import Array, Float, PyTree
33
+
34
+ from diffbio.constants import EPSILON
35
+ from diffbio.core.optimal_transport import SinkhornLayer
36
+ from diffbio.utils.nn_utils import ensure_rngs
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+ __all__ = [
41
+ "OTTrajectoryConfig",
42
+ "DifferentiableOTTrajectory",
43
+ ]
44
+
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Configuration
48
+ # ---------------------------------------------------------------------------
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class OTTrajectoryConfig(OperatorConfig):
53
+ """Configuration for OT-based trajectory inference.
54
+
55
+ Attributes:
56
+ n_genes: Number of input genes per cell.
57
+ sinkhorn_epsilon: Entropy regularisation strength for the Sinkhorn
58
+ solver. Larger values produce smoother transport plans.
59
+ sinkhorn_iters: Number of Sinkhorn iterations.
60
+ growth_rate_regularization: Scaling factor applied to raw row-sums
61
+ before normalisation. Higher values amplify growth-rate variation.
62
+ interpolation_time: Fraction in (0, 1) at which to compute the
63
+ interpolated cell distribution between t1 and t2.
64
+ """
65
+
66
+ n_genes: int = 200
67
+ sinkhorn_epsilon: float = 0.1
68
+ sinkhorn_iters: int = 100
69
+ growth_rate_regularization: float = 1.0
70
+ interpolation_time: float = 0.5
71
+
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # Operator
75
+ # ---------------------------------------------------------------------------
76
+
77
+
78
+ class DifferentiableOTTrajectory(OperatorModule):
79
+ """Waddington-OT-style differentiable trajectory inference.
80
+
81
+ Computes an optimal-transport plan between cell populations at two
82
+ timepoints, estimates per-cell growth (proliferation) rates, and
83
+ interpolates an intermediate cell distribution.
84
+
85
+ Algorithm:
86
+ 1. Build the squared-Euclidean cost matrix ``C[i,j] = ||x_i - y_j||^2``
87
+ between cells at t1 and t2.
88
+ 2. Compute the entropy-regularised transport plan via ``SinkhornLayer``.
89
+ 3. Derive growth rates from the transport plan: cells that transport to
90
+ more targets in t2 have higher proliferation. Normalise so that
91
+ ``mean(growth_rates) == 1``.
92
+ 4. Interpolate an intermediate distribution at time *s*:
93
+ ``x_interp = (1-s) * x_t1 + s * (T @ x_t2) / T.sum(axis=1)``
94
+
95
+ Args:
96
+ config: OTTrajectoryConfig with operator parameters.
97
+ rngs: Flax NNX random number generators.
98
+ name: Optional operator name.
99
+
100
+ Example:
101
+ >>> config = OTTrajectoryConfig(n_genes=100, sinkhorn_iters=50)
102
+ >>> op = DifferentiableOTTrajectory(config)
103
+ >>> data = {
104
+ ... "counts_t1": jnp.ones((20, 100)),
105
+ ... "counts_t2": jnp.ones((25, 100)),
106
+ ... }
107
+ >>> result, state, meta = op.apply(data, {}, None)
108
+ >>> result["transport_plan"].shape
109
+ (20, 25)
110
+ """
111
+
112
+ def __init__(
113
+ self,
114
+ config: OTTrajectoryConfig,
115
+ *,
116
+ rngs: nnx.Rngs | None = None,
117
+ name: str | None = None,
118
+ ) -> None:
119
+ """Initialize the OT trajectory operator.
120
+
121
+ Args:
122
+ config: OT trajectory configuration.
123
+ rngs: Random number generators (for API consistency).
124
+ name: Optional operator name.
125
+ """
126
+ super().__init__(config, rngs=rngs, name=name)
127
+
128
+ rngs = ensure_rngs(rngs)
129
+
130
+ self.sinkhorn = SinkhornLayer(
131
+ epsilon=config.sinkhorn_epsilon,
132
+ num_iters=config.sinkhorn_iters,
133
+ rngs=rngs,
134
+ )
135
+
136
+ # -- Internal helpers ---------------------------------------------------
137
+
138
+ def _compute_expression_cost(
139
+ self,
140
+ counts_t1: Float[Array, "n1 g"],
141
+ counts_t2: Float[Array, "n2 g"],
142
+ ) -> Float[Array, "n1 n2"]:
143
+ """Compute the squared-Euclidean expression cost matrix.
144
+
145
+ Uses the expansion ``||a - b||^2 = ||a||^2 + ||b||^2 - 2 a . b``
146
+ for efficiency.
147
+
148
+ Args:
149
+ counts_t1: Expression matrix at timepoint 1.
150
+ counts_t2: Expression matrix at timepoint 2.
151
+
152
+ Returns:
153
+ Cost matrix of shape ``(n1, n2)``.
154
+ """
155
+ sq1 = jnp.sum(counts_t1**2, axis=-1, keepdims=True) # (n1, 1)
156
+ sq2 = jnp.sum(counts_t2**2, axis=-1) # (n2,)
157
+ dot = jnp.dot(counts_t1, counts_t2.T) # (n1, n2)
158
+ cost = sq1 + sq2 - 2.0 * dot
159
+ return jnp.maximum(cost, 0.0)
160
+
161
+ def _estimate_growth_rates(
162
+ self,
163
+ transport_plan: Float[Array, "n1 n2"],
164
+ ) -> Float[Array, "n1"]:
165
+ """Estimate per-cell growth rates from the transport plan.
166
+
167
+ Cells whose row in the transport plan sums to a larger value are
168
+ inferred to be proliferating (they contribute mass to more cells
169
+ in the next timepoint). The rates are normalised so that
170
+ ``mean(growth_rates) == 1``.
171
+
172
+ Args:
173
+ transport_plan: Optimal transport plan ``(n1, n2)``.
174
+
175
+ Returns:
176
+ Normalised growth rates ``(n1,)``.
177
+ """
178
+ raw_rates = jnp.sum(transport_plan, axis=1)
179
+ mean_rate = jnp.mean(raw_rates) + EPSILON
180
+ return raw_rates / mean_rate
181
+
182
+ def _interpolate_trajectory(
183
+ self,
184
+ counts_t1: Float[Array, "n1 g"],
185
+ counts_t2: Float[Array, "n2 g"],
186
+ transport_plan: Float[Array, "n1 n2"],
187
+ interpolation_time: float,
188
+ ) -> Float[Array, "n1 g"]:
189
+ """Interpolate cell states at an intermediate timepoint.
190
+
191
+ For each cell *i* in t1, the transported expression is the weighted
192
+ average of t2 cells according to the transport plan. The interpolated
193
+ state is a convex combination of the original t1 expression and the
194
+ transported expression.
195
+
196
+ Args:
197
+ counts_t1: Expression at timepoint 1.
198
+ counts_t2: Expression at timepoint 2.
199
+ transport_plan: Optimal transport plan.
200
+ interpolation_time: Fraction *s* in (0, 1).
201
+
202
+ Returns:
203
+ Interpolated expression matrix ``(n1, g)``.
204
+ """
205
+ # Row-normalise the transport plan so each row sums to 1
206
+ row_sums = jnp.sum(transport_plan, axis=1, keepdims=True) + EPSILON
207
+ plan_normalised = transport_plan / row_sums
208
+
209
+ # Transported expression: weighted average of t2 cells
210
+ transported = plan_normalised @ counts_t2 # (n1, g)
211
+
212
+ # Convex interpolation
213
+ s = interpolation_time
214
+ return (1.0 - s) * counts_t1 + s * transported
215
+
216
+ # -- Public apply -------------------------------------------------------
217
+
218
+ def apply(
219
+ self,
220
+ data: PyTree,
221
+ state: PyTree,
222
+ metadata: dict[str, Any] | None,
223
+ random_params: Any = None,
224
+ stats: dict[str, Any] | None = None,
225
+ ) -> tuple[PyTree, PyTree, dict[str, Any] | None]:
226
+ """Apply OT-based trajectory inference to two-timepoint expression data.
227
+
228
+ Args:
229
+ data: Dictionary containing:
230
+ - ``"counts_t1"``: Expression matrix at timepoint 1 ``(n1, g)``
231
+ - ``"counts_t2"``: Expression matrix at timepoint 2 ``(n2, g)``
232
+ state: Element state (passed through unchanged).
233
+ metadata: Element metadata (passed through unchanged).
234
+ random_params: Not used (non-stochastic operator).
235
+ stats: Not used.
236
+
237
+ Returns:
238
+ Tuple of (transformed_data, state, metadata):
239
+ - transformed_data contains all original keys plus:
240
+
241
+ - ``"transport_plan"``: OT plan ``(n1, n2)``
242
+ - ``"growth_rates"``: Per-cell growth rates ``(n1,)``
243
+ - ``"interpolated_counts"``: Interpolated expression
244
+ at the configured midpoint ``(n1, g)``
245
+ - state is passed through unchanged
246
+ - metadata is passed through unchanged
247
+ """
248
+ counts_t1: Float[Array, "n1 g"] = data["counts_t1"]
249
+ counts_t2: Float[Array, "n2 g"] = data["counts_t2"]
250
+
251
+ n1 = counts_t1.shape[0]
252
+ n2 = counts_t2.shape[0]
253
+
254
+ # Step 1: Expression cost matrix
255
+ cost = self._compute_expression_cost(counts_t1, counts_t2)
256
+
257
+ # Step 2: Solve OT via Sinkhorn with uniform marginals
258
+ a = jnp.ones(n1) / n1
259
+ b = jnp.ones(n2) / n2
260
+ transport_plan = self.sinkhorn(cost, a, b)
261
+
262
+ # Step 3: Growth rates from row sums
263
+ growth_rates = self._estimate_growth_rates(transport_plan)
264
+
265
+ # Step 4: Interpolated expression at configured time
266
+ interpolated = self._interpolate_trajectory(
267
+ counts_t1, counts_t2, transport_plan, self.config.interpolation_time
268
+ )
269
+
270
+ transformed_data = {
271
+ **data,
272
+ "transport_plan": transport_plan,
273
+ "growth_rates": growth_rates,
274
+ "interpolated_counts": interpolated,
275
+ }
276
+
277
+ return transformed_data, state, metadata