FEAST-py 0.1.7__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.
- FEAST/FEAST_core/APIs.py +711 -0
- FEAST/FEAST_core/__init__.py +6 -0
- FEAST/FEAST_core/parameter_cloud.py +492 -0
- FEAST/FEAST_core/simulator.py +973 -0
- FEAST/__init__.py +43 -0
- FEAST/alignment/__init__.py +25 -0
- FEAST/alignment/alignment_simulator.py +806 -0
- FEAST/alignment/spatial_align_alter.py +415 -0
- FEAST/deconvolution/__init__.py +32 -0
- FEAST/deconvolution/deconvolution_simulator.py +403 -0
- FEAST/deconvolution/generate_deconvolution.py +190 -0
- FEAST/interpolation/__init__.py +28 -0
- FEAST/interpolation/coordinate_generation.py +310 -0
- FEAST/interpolation/count_generation.py +287 -0
- FEAST/interpolation/interpolation_pipeline.py +306 -0
- FEAST/interpolation/parameter_interpolation.py +369 -0
- FEAST/modeling/Beta_mixture_model.py +414 -0
- FEAST/modeling/StudentT_mixture_model.py +234 -0
- FEAST/modeling/__init__.py +0 -0
- FEAST/modeling/marginal_alteration.py +439 -0
- feast_py-0.1.7.dist-info/METADATA +228 -0
- feast_py-0.1.7.dist-info/RECORD +25 -0
- feast_py-0.1.7.dist-info/WHEEL +5 -0
- feast_py-0.1.7.dist-info/licenses/LICENSE +19 -0
- feast_py-0.1.7.dist-info/top_level.txt +1 -0
FEAST/FEAST_core/APIs.py
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
import anndata as ad
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Union, List, Dict, Optional, Any
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
# Import core simulation functions
|
|
7
|
+
from .simulator import simulate_single_slice, SpatialSimulator
|
|
8
|
+
# from .parameter_cloud_interpolation import (
|
|
9
|
+
# batch_interpolate_parameter_clouds,
|
|
10
|
+
# interpolate_slice_with_statistical_modeling,
|
|
11
|
+
# interpolate_parameter_clouds_with_continuous_ot
|
|
12
|
+
# )
|
|
13
|
+
|
|
14
|
+
# from .continuous_ot_interpolation import (
|
|
15
|
+
# interpolate_with_continuous_ot,
|
|
16
|
+
# interpolate_with_trajectory_ot,
|
|
17
|
+
# validate_interpolation_quality
|
|
18
|
+
# )
|
|
19
|
+
|
|
20
|
+
# Import specialized simulators
|
|
21
|
+
from ..alignment.alignment_simulator import (
|
|
22
|
+
AlignmentSimulator,
|
|
23
|
+
simulate_alignment_rotation,
|
|
24
|
+
simulate_alignment_warp,
|
|
25
|
+
generate_alignment_benchmark_suite
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from ..deconvolution.deconvolution_simulator import (
|
|
29
|
+
DeconvolutionSimulator,
|
|
30
|
+
simulate_deconvolution_from_single_cells,
|
|
31
|
+
create_deconvolution_benchmark_suite
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from ..deconvolution.generate_deconvolution import (
|
|
35
|
+
create_deconvolution_benchmark_data
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
from ..deconvolution.deconvolution_simulator import (
|
|
39
|
+
DeconvolutionSimulator,
|
|
40
|
+
simulate_deconvolution_from_single_cells,
|
|
41
|
+
create_deconvolution_benchmark_suite
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FEAST:
|
|
46
|
+
"""
|
|
47
|
+
Unified FEAST API for all spatial transcriptomics simulation tasks.
|
|
48
|
+
|
|
49
|
+
This class provides a single entry point for:
|
|
50
|
+
- Single slice simulation
|
|
51
|
+
- Alignment simulation
|
|
52
|
+
- Deconvolution simulation
|
|
53
|
+
- Multi-slice reconstruction
|
|
54
|
+
|
|
55
|
+
The simulator automatically detects whether you're working with single or
|
|
56
|
+
multiple slices and provides appropriate methods.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, adata: Union[ad.AnnData, List[ad.AnnData]], verbose: bool = True):
|
|
60
|
+
"""
|
|
61
|
+
Initialize FEAST with spatial transcriptomics data.
|
|
62
|
+
|
|
63
|
+
Parameters:
|
|
64
|
+
-----------
|
|
65
|
+
adata : AnnData or List[AnnData]
|
|
66
|
+
Single AnnData object for single slice operations, or list of
|
|
67
|
+
AnnData objects for multi-slice reconstruction
|
|
68
|
+
verbose : bool
|
|
69
|
+
Whether to print progress messages
|
|
70
|
+
"""
|
|
71
|
+
self.verbose = verbose
|
|
72
|
+
self.is_multi_slice = isinstance(adata, list)
|
|
73
|
+
|
|
74
|
+
if self.is_multi_slice:
|
|
75
|
+
self.adata_list = adata
|
|
76
|
+
self.n_slices = len(adata)
|
|
77
|
+
if self.verbose:
|
|
78
|
+
print(f"✓ FEAST initialized with {self.n_slices} slices")
|
|
79
|
+
for i, slice_data in enumerate(adata):
|
|
80
|
+
print(f" Slice {i}: {slice_data.shape}")
|
|
81
|
+
else:
|
|
82
|
+
self.adata = adata
|
|
83
|
+
if self.verbose:
|
|
84
|
+
print(f"✓ FEAST initialized with single slice: {adata.shape}")
|
|
85
|
+
|
|
86
|
+
# Initialize specialized simulators (lazy loading)
|
|
87
|
+
self._core_simulator = None
|
|
88
|
+
self._alignment_simulator = None
|
|
89
|
+
self._deconvolution_simulator = None
|
|
90
|
+
self._deconvolution_simulator = None
|
|
91
|
+
|
|
92
|
+
def _get_core_simulator(self):
|
|
93
|
+
"""Lazy initialization of core simulator."""
|
|
94
|
+
if self._core_simulator is None:
|
|
95
|
+
if self.is_multi_slice:
|
|
96
|
+
warnings.warn("Core simulator requires single slice. Using first slice.")
|
|
97
|
+
self._core_simulator = SpatialSimulator(self.adata_list[0])
|
|
98
|
+
else:
|
|
99
|
+
self._core_simulator = SpatialSimulator(self.adata)
|
|
100
|
+
return self._core_simulator
|
|
101
|
+
|
|
102
|
+
def _get_alignment_simulator(self):
|
|
103
|
+
"""Lazy initialization of alignment simulator."""
|
|
104
|
+
if self._alignment_simulator is None:
|
|
105
|
+
if self.is_multi_slice:
|
|
106
|
+
warnings.warn("Alignment simulator requires single slice. Using first slice.")
|
|
107
|
+
data_to_use = self.adata_list[0]
|
|
108
|
+
else:
|
|
109
|
+
data_to_use = self.adata
|
|
110
|
+
self._alignment_simulator = AlignmentSimulator(data_to_use, verbose=self.verbose)
|
|
111
|
+
return self._alignment_simulator
|
|
112
|
+
|
|
113
|
+
def _get_deconvolution_simulator(self):
|
|
114
|
+
"""Lazy initialization of deconvolution simulator."""
|
|
115
|
+
if self._deconvolution_simulator is None:
|
|
116
|
+
self._deconvolution_simulator = DeconvolutionSimulator(verbose=self.verbose)
|
|
117
|
+
return self._deconvolution_simulator
|
|
118
|
+
|
|
119
|
+
def _get_deconvolution_simulator(self):
|
|
120
|
+
"""Lazy initialization of deconvolution simulator."""
|
|
121
|
+
if self._deconvolution_simulator is None:
|
|
122
|
+
self._deconvolution_simulator = DeconvolutionSimulator(verbose=self.verbose)
|
|
123
|
+
return self._deconvolution_simulator
|
|
124
|
+
|
|
125
|
+
# ===============================
|
|
126
|
+
# SINGLE SLICE SIMULATION METHODS
|
|
127
|
+
# ===============================
|
|
128
|
+
|
|
129
|
+
def simulate_single_slice(self,
|
|
130
|
+
sigma: float = 1.0,
|
|
131
|
+
follower_sigma_factor: float = 0.1,
|
|
132
|
+
visualize_fits: bool = False,
|
|
133
|
+
num_simulation_cores: int = 12,
|
|
134
|
+
verbose: Optional[bool] = None,
|
|
135
|
+
clip_overshoot_factor: float = 0.1,
|
|
136
|
+
use_real_stats_directly: bool = False,
|
|
137
|
+
annotation_key: Optional[str] = None,
|
|
138
|
+
use_heuristic_search: bool = False,
|
|
139
|
+
min_accepted_error: float = 0.5,
|
|
140
|
+
assignment_weights: Optional[Dict] = None,
|
|
141
|
+
screening_pool_size: int = 100,
|
|
142
|
+
top_n_to_fully_evaluate: int = 10,
|
|
143
|
+
n_jobs: int = -1,
|
|
144
|
+
alteration_config: Optional[Any] = None,
|
|
145
|
+
boundary_multiplier: float = 1.1,
|
|
146
|
+
**kwargs) -> ad.AnnData:
|
|
147
|
+
"""
|
|
148
|
+
Generate a single simulated spatial transcriptomics slice.
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
-----------
|
|
152
|
+
sigma : float, default=1.0
|
|
153
|
+
Spatial smoothness parameter for Gene-Spatial Relevance Based Assignment (G-SRBA).
|
|
154
|
+
- sigma=0: Perfect pattern preservation (zero spatial change)
|
|
155
|
+
- sigma=0.5-1.5: "Gentle" mode, introduces subtle, local variations
|
|
156
|
+
- sigma>2.0: "Exploratory" mode, introduces more significant changes
|
|
157
|
+
follower_sigma_factor : float, default=0.1
|
|
158
|
+
Factor for follower gene spatial smoothness (legacy parameter, now uses correlation-guided interpolation)
|
|
159
|
+
visualize_fits : bool, default=False
|
|
160
|
+
Whether to show fitting visualization plots
|
|
161
|
+
num_simulation_cores : int, default=12
|
|
162
|
+
Number of cores for parallel processing during simulation
|
|
163
|
+
verbose : bool, optional
|
|
164
|
+
Override default verbosity setting
|
|
165
|
+
clip_overshoot_factor : float, default=0.1
|
|
166
|
+
Factor for clipping expression overshoot during simulation
|
|
167
|
+
use_real_stats_directly : bool, default=False
|
|
168
|
+
Whether to use real statistics directly instead of parameter cloud fitting
|
|
169
|
+
annotation_key : str, optional
|
|
170
|
+
Key in adata.obs for annotation-based simulation
|
|
171
|
+
use_heuristic_search : bool, default=False
|
|
172
|
+
Whether to use heuristic search for parameter assignment
|
|
173
|
+
min_accepted_error : float, default=0.5
|
|
174
|
+
Minimum accepted error threshold for heuristic search
|
|
175
|
+
assignment_weights : Dict, optional
|
|
176
|
+
Weights for parameter assignment {'mean': 3.0, 'variance': 1.0, 'zero_prop': 1.0}
|
|
177
|
+
screening_pool_size : int, default=100
|
|
178
|
+
Size of screening pool for heuristic search
|
|
179
|
+
top_n_to_fully_evaluate : int, default=10
|
|
180
|
+
Number of top candidates to fully evaluate in heuristic search
|
|
181
|
+
n_jobs : int, default=-1
|
|
182
|
+
Number of parallel jobs (-1 uses all available cores)
|
|
183
|
+
alteration_config : AlterationConfig, optional
|
|
184
|
+
Configuration for marginal distribution alterations
|
|
185
|
+
boundary_multiplier : float, default=1.1
|
|
186
|
+
Multiplier for maximum count boundary constraint (1.1 = 110% of reference max)
|
|
187
|
+
**kwargs
|
|
188
|
+
Additional parameters passed to simulate_single_slice
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
--------
|
|
192
|
+
AnnData
|
|
193
|
+
Simulated spatial transcriptomics data
|
|
194
|
+
"""
|
|
195
|
+
if self.is_multi_slice:
|
|
196
|
+
warnings.warn("Single slice simulation with multi-slice input. Using first slice.")
|
|
197
|
+
adata_to_use = self.adata_list[0]
|
|
198
|
+
else:
|
|
199
|
+
adata_to_use = self.adata
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if verbose is None:
|
|
203
|
+
verbose = self.verbose
|
|
204
|
+
|
|
205
|
+
return simulate_single_slice(
|
|
206
|
+
adata_to_use,
|
|
207
|
+
sigma=sigma,
|
|
208
|
+
follower_sigma_factor=follower_sigma_factor,
|
|
209
|
+
visualize_fits=visualize_fits,
|
|
210
|
+
num_simulation_cores=num_simulation_cores,
|
|
211
|
+
verbose=verbose,
|
|
212
|
+
clip_overshoot_factor=clip_overshoot_factor,
|
|
213
|
+
use_real_stats_directly=use_real_stats_directly,
|
|
214
|
+
annotation_key=annotation_key,
|
|
215
|
+
use_heuristic_search=use_heuristic_search,
|
|
216
|
+
min_accepted_error=min_accepted_error,
|
|
217
|
+
assignment_weights=assignment_weights,
|
|
218
|
+
screening_pool_size=screening_pool_size,
|
|
219
|
+
top_n_to_fully_evaluate=top_n_to_fully_evaluate,
|
|
220
|
+
n_jobs=n_jobs,
|
|
221
|
+
alteration_config=alteration_config,
|
|
222
|
+
boundary_multiplier=boundary_multiplier,
|
|
223
|
+
**kwargs
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# ===============================
|
|
227
|
+
# ALIGNMENT SIMULATION METHODS
|
|
228
|
+
# ===============================
|
|
229
|
+
|
|
230
|
+
def simulate_alignment(self,
|
|
231
|
+
transformation_type: str = 'rotation',
|
|
232
|
+
rotation_angle: float = 0,
|
|
233
|
+
warp_strength: float = 0,
|
|
234
|
+
data_type: str = 'imaging',
|
|
235
|
+
filter_edge_spots: bool = True,
|
|
236
|
+
edge_margin_ratio: float = 0.03,
|
|
237
|
+
fit_params: Optional[Dict] = None,
|
|
238
|
+
expression_params: Optional[Dict] = None,
|
|
239
|
+
sigma: float = 0,
|
|
240
|
+
follower_sigma_factor: float = 0,
|
|
241
|
+
visualize_fits: bool = False,
|
|
242
|
+
num_simulation_cores: int = 12,
|
|
243
|
+
clip_overshoot_factor: float = 0.1,
|
|
244
|
+
use_real_stats_directly: bool = False,
|
|
245
|
+
annotation_key: Optional[str] = None,
|
|
246
|
+
use_heuristic_search: bool = False,
|
|
247
|
+
min_accepted_error: float = 0.5,
|
|
248
|
+
assignment_weights: Optional[Dict] = None,
|
|
249
|
+
screening_pool_size: int = 100,
|
|
250
|
+
top_n_to_fully_evaluate: int = 10,
|
|
251
|
+
n_jobs: int = -1,
|
|
252
|
+
alteration_config: Optional[Any] = None,
|
|
253
|
+
boundary_multiplier: float = 1.1,
|
|
254
|
+
verbose: Optional[bool] = None,
|
|
255
|
+
**kwargs) -> tuple:
|
|
256
|
+
"""
|
|
257
|
+
Generate alignment simulation data with spatial transformations.
|
|
258
|
+
|
|
259
|
+
Parameters:
|
|
260
|
+
-----------
|
|
261
|
+
transformation_type : str, default='rotation'
|
|
262
|
+
Type of transformation ('rotation', 'warp', 'cut_move')
|
|
263
|
+
rotation_angle : float, default=45.0
|
|
264
|
+
Rotation angle in degrees (for rotation transformation)
|
|
265
|
+
warp_strength : float, default=0.3
|
|
266
|
+
Warping strength (for TPS warping)
|
|
267
|
+
data_type : str, default='imaging'
|
|
268
|
+
Data type for transformation ('imaging', 'sequencing')
|
|
269
|
+
fit_params : Dict, optional
|
|
270
|
+
Parameters for parameter fitting (merged into simulation parameters)
|
|
271
|
+
expression_params : Dict, optional
|
|
272
|
+
Parameters for expression generation (merged into simulation parameters)
|
|
273
|
+
|
|
274
|
+
# Single slice simulation parameters (same as simulate_single_slice):
|
|
275
|
+
sigma : float, default=1.0
|
|
276
|
+
Spatial smoothness parameter for G-SRBA
|
|
277
|
+
follower_sigma_factor : float, default=0.1
|
|
278
|
+
Factor for follower gene spatial smoothness
|
|
279
|
+
visualize_fits : bool, default=False
|
|
280
|
+
Whether to show fitting visualization plots
|
|
281
|
+
num_simulation_cores : int, default=12
|
|
282
|
+
Number of cores for parallel processing
|
|
283
|
+
clip_overshoot_factor : float, default=0.1
|
|
284
|
+
Factor for clipping expression overshoot
|
|
285
|
+
use_real_stats_directly : bool, default=False
|
|
286
|
+
Whether to use real statistics directly instead of parameter cloud fitting
|
|
287
|
+
annotation_key : str, optional
|
|
288
|
+
Key in adata.obs for annotation-based simulation
|
|
289
|
+
use_heuristic_search : bool, default=False
|
|
290
|
+
Whether to use heuristic search for parameter assignment
|
|
291
|
+
min_accepted_error : float, default=0.5
|
|
292
|
+
Minimum accepted error threshold for heuristic search
|
|
293
|
+
assignment_weights : Dict, optional
|
|
294
|
+
Weights for parameter assignment {'mean': 3.0, 'variance': 1.0, 'zero_prop': 1.0}
|
|
295
|
+
screening_pool_size : int, default=100
|
|
296
|
+
Size of screening pool for heuristic search
|
|
297
|
+
top_n_to_fully_evaluate : int, default=10
|
|
298
|
+
Number of top candidates to fully evaluate
|
|
299
|
+
n_jobs : int, default=-1
|
|
300
|
+
Number of parallel jobs (-1 uses all available cores)
|
|
301
|
+
alteration_config : AlterationConfig, optional
|
|
302
|
+
Configuration for marginal distribution alterations
|
|
303
|
+
boundary_multiplier : float, default=1.1
|
|
304
|
+
Multiplier for maximum count boundary constraint
|
|
305
|
+
verbose : bool, optional
|
|
306
|
+
Override default verbosity setting
|
|
307
|
+
**kwargs
|
|
308
|
+
Additional transformation parameters
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
--------
|
|
312
|
+
tuple
|
|
313
|
+
(original_data, transformed_data) as AnnData objects
|
|
314
|
+
"""
|
|
315
|
+
if self.is_multi_slice:
|
|
316
|
+
warnings.warn("Alignment simulation with multi-slice input. Using first slice.")
|
|
317
|
+
adata_to_use = self.adata_list[0]
|
|
318
|
+
else:
|
|
319
|
+
adata_to_use = self.adata
|
|
320
|
+
|
|
321
|
+
if verbose is None:
|
|
322
|
+
verbose = self.verbose
|
|
323
|
+
|
|
324
|
+
# Merge all simulation parameters
|
|
325
|
+
simulation_params = {
|
|
326
|
+
'sigma': sigma,
|
|
327
|
+
'follower_sigma_factor': follower_sigma_factor,
|
|
328
|
+
'visualize_fits': visualize_fits,
|
|
329
|
+
'num_simulation_cores': num_simulation_cores,
|
|
330
|
+
'clip_overshoot_factor': clip_overshoot_factor,
|
|
331
|
+
'use_real_stats_directly': use_real_stats_directly,
|
|
332
|
+
'annotation_key': annotation_key,
|
|
333
|
+
'use_heuristic_search': use_heuristic_search,
|
|
334
|
+
'min_accepted_error': min_accepted_error,
|
|
335
|
+
'assignment_weights': assignment_weights,
|
|
336
|
+
'screening_pool_size': screening_pool_size,
|
|
337
|
+
'top_n_to_fully_evaluate': top_n_to_fully_evaluate,
|
|
338
|
+
'n_jobs': n_jobs,
|
|
339
|
+
'alteration_config': alteration_config,
|
|
340
|
+
'boundary_multiplier': boundary_multiplier,
|
|
341
|
+
'verbose': verbose,
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
# Override with fit_params and expression_params if provided
|
|
345
|
+
if fit_params:
|
|
346
|
+
simulation_params.update(fit_params)
|
|
347
|
+
if expression_params:
|
|
348
|
+
simulation_params.update(expression_params)
|
|
349
|
+
|
|
350
|
+
# Additional kwargs
|
|
351
|
+
simulation_params.update(kwargs)
|
|
352
|
+
|
|
353
|
+
if transformation_type == 'rotation':
|
|
354
|
+
return simulate_alignment_rotation(
|
|
355
|
+
adata_to_use,
|
|
356
|
+
rotation_angle=rotation_angle,
|
|
357
|
+
data_type=data_type,
|
|
358
|
+
filter_edge_spots=filter_edge_spots,
|
|
359
|
+
edge_margin_ratio=edge_margin_ratio,
|
|
360
|
+
fit_params=simulation_params,
|
|
361
|
+
expression_params={}, # Already merged above
|
|
362
|
+
**kwargs
|
|
363
|
+
)
|
|
364
|
+
elif transformation_type == 'warp':
|
|
365
|
+
return simulate_alignment_warp(
|
|
366
|
+
adata_to_use,
|
|
367
|
+
distort_level=warp_strength,
|
|
368
|
+
filter_edge_spots=filter_edge_spots,
|
|
369
|
+
edge_margin_ratio=edge_margin_ratio,
|
|
370
|
+
fit_params=simulation_params,
|
|
371
|
+
expression_params={}, # Already merged above
|
|
372
|
+
**kwargs
|
|
373
|
+
)
|
|
374
|
+
else:
|
|
375
|
+
raise ValueError(f"Unsupported transformation type: {transformation_type}")
|
|
376
|
+
|
|
377
|
+
def simulate_alignment_benchmark(self,
|
|
378
|
+
transformations: Optional[List[str]] = None,
|
|
379
|
+
parameters: Optional[Dict] = None,
|
|
380
|
+
data_types: Optional[List[str]] = None) -> Dict[str, tuple]:
|
|
381
|
+
"""
|
|
382
|
+
Generate comprehensive alignment benchmark suite.
|
|
383
|
+
|
|
384
|
+
Parameters:
|
|
385
|
+
-----------
|
|
386
|
+
transformations : List[str], optional
|
|
387
|
+
List of transformations to test
|
|
388
|
+
parameters : Dict, optional
|
|
389
|
+
Parameters for each transformation type
|
|
390
|
+
data_types : List[str], optional
|
|
391
|
+
Data types to test
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
--------
|
|
395
|
+
Dict[str, tuple]
|
|
396
|
+
Dictionary mapping scenario names to (original, transformed) data pairs
|
|
397
|
+
"""
|
|
398
|
+
if self.is_multi_slice:
|
|
399
|
+
warnings.warn("Alignment benchmark with multi-slice input. Using first slice.")
|
|
400
|
+
adata_to_use = self.adata_list[0]
|
|
401
|
+
else:
|
|
402
|
+
adata_to_use = self.adata
|
|
403
|
+
|
|
404
|
+
return generate_alignment_benchmark_suite(
|
|
405
|
+
adata_to_use,
|
|
406
|
+
transformations=transformations,
|
|
407
|
+
parameters=parameters,
|
|
408
|
+
data_types=data_types
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def simulate_deconvolution(self,
|
|
413
|
+
cell_type_key: str,
|
|
414
|
+
downsampling_factor: float = 0.25,
|
|
415
|
+
cells_per_spot: int = 50,
|
|
416
|
+
aggregation_method: str = 'sum',
|
|
417
|
+
fractional_rounding: str = 'probabilistic',
|
|
418
|
+
use_ot_assignment: bool = True,
|
|
419
|
+
verbose: Optional[bool] = None) -> ad.AnnData:
|
|
420
|
+
"""
|
|
421
|
+
Generate deconvolution simulation data using vine copula + spot marginals approach.
|
|
422
|
+
|
|
423
|
+
This method implements the sophisticated simulation approach:
|
|
424
|
+
1. Preserves vine copula relationships from real single cell data
|
|
425
|
+
2. Derives marginal distributions from aggregated spots (cells combined into spots)
|
|
426
|
+
3. Uses probabilistic fractional rounding (0.5 → 50% chance of 1, 50% chance of 0)
|
|
427
|
+
4. Applies optimal transport for gene assignment following current simulator methodology
|
|
428
|
+
5. Uses downsampling_factor to determine realistic number of spots to simulate
|
|
429
|
+
|
|
430
|
+
Parameters:
|
|
431
|
+
-----------
|
|
432
|
+
cell_type_key : str
|
|
433
|
+
Key in adata.obs containing cell type annotations (assumes single-cell input)
|
|
434
|
+
downsampling_factor : float, default=0.25
|
|
435
|
+
Factor to determine number of spots relative to single cells (0.25 = 4x fewer spots than cells)
|
|
436
|
+
cells_per_spot : int, default=50
|
|
437
|
+
Number of cells to aggregate per spot for marginal distribution fitting
|
|
438
|
+
aggregation_method : str, default='sum'
|
|
439
|
+
Method to aggregate cells into spots ('sum', 'mean')
|
|
440
|
+
fractional_rounding : str, default='probabilistic'
|
|
441
|
+
How to handle fractional counts:
|
|
442
|
+
- 'probabilistic': 0.5 → 50% chance of 1, 50% chance of 0
|
|
443
|
+
- 'round': standard rounding
|
|
444
|
+
- 'floor': round down
|
|
445
|
+
use_ot_assignment : bool, default=True
|
|
446
|
+
Whether to use optimal transport for gene assignment (following current simulator)
|
|
447
|
+
verbose : bool, optional
|
|
448
|
+
Override default verbosity setting
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
--------
|
|
452
|
+
AnnData
|
|
453
|
+
Simulated ST data with realistic spot-level expression and ground truth cell type proportions
|
|
454
|
+
"""
|
|
455
|
+
if self.is_multi_slice:
|
|
456
|
+
warnings.warn("Deconvolution simulation with multi-slice input. Using first slice.")
|
|
457
|
+
adata_to_use = self.adata_list[0]
|
|
458
|
+
else:
|
|
459
|
+
adata_to_use = self.adata
|
|
460
|
+
|
|
461
|
+
if verbose is None:
|
|
462
|
+
verbose = self.verbose
|
|
463
|
+
|
|
464
|
+
# Calculate number of spots based on downsampling factor
|
|
465
|
+
n_cells = adata_to_use.shape[0]
|
|
466
|
+
n_spots = int(n_cells * downsampling_factor)
|
|
467
|
+
|
|
468
|
+
if verbose:
|
|
469
|
+
print(f"Deconvolution simulation: {n_cells} cells → {n_spots} spots (factor: {downsampling_factor})")
|
|
470
|
+
|
|
471
|
+
return simulate_deconvolution_from_single_cells(
|
|
472
|
+
adata_to_use,
|
|
473
|
+
cell_type_key=cell_type_key,
|
|
474
|
+
n_spots=n_spots,
|
|
475
|
+
cells_per_spot=cells_per_spot,
|
|
476
|
+
aggregation_method=aggregation_method,
|
|
477
|
+
fractional_rounding=fractional_rounding,
|
|
478
|
+
verbose=verbose
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
def create_deconvolution_benchmark(self,
|
|
482
|
+
cell_type_key: str,
|
|
483
|
+
downsampling_factor: float = 0.25,
|
|
484
|
+
grid_type: str = 'hexagonal',
|
|
485
|
+
alpha: float = 0.01,
|
|
486
|
+
verbose: Optional[bool] = None) -> ad.AnnData:
|
|
487
|
+
"""
|
|
488
|
+
Create deconvolution benchmark data using fast spatial downsampling.
|
|
489
|
+
|
|
490
|
+
This method creates lower-resolution spatial transcriptomics data with ground truth
|
|
491
|
+
cell type proportions by aggregating spots from existing high-resolution ST data.
|
|
492
|
+
This is fast but just rearranges existing data rather than simulating new data.
|
|
493
|
+
|
|
494
|
+
Parameters:
|
|
495
|
+
-----------
|
|
496
|
+
cell_type_key : str
|
|
497
|
+
Key in adata.obs containing cell type annotations
|
|
498
|
+
downsampling_factor : float, default=0.25
|
|
499
|
+
Factor to reduce spatial resolution (0.25 = 4x fewer spots, 0.1 = 10x fewer spots)
|
|
500
|
+
grid_type : str, default='hexagonal'
|
|
501
|
+
Type of grid for downsampled spots ('hexagonal', 'square', 'kmeans')
|
|
502
|
+
alpha : float, default=0.01
|
|
503
|
+
Alpha shape parameter for tissue boundary detection
|
|
504
|
+
verbose : bool, optional
|
|
505
|
+
Override default verbosity setting
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
--------
|
|
509
|
+
AnnData
|
|
510
|
+
Downsampled ST data with ground truth cell type proportions in .obsm['cell_type_proportions']
|
|
511
|
+
"""
|
|
512
|
+
if self.is_multi_slice:
|
|
513
|
+
warnings.warn("Deconvolution benchmark with multi-slice input. Using first slice.")
|
|
514
|
+
adata_to_use = self.adata_list[0]
|
|
515
|
+
else:
|
|
516
|
+
adata_to_use = self.adata
|
|
517
|
+
|
|
518
|
+
if verbose is None:
|
|
519
|
+
verbose = self.verbose
|
|
520
|
+
|
|
521
|
+
return create_deconvolution_benchmark_data(
|
|
522
|
+
adata_to_use,
|
|
523
|
+
downsampling_factor=downsampling_factor,
|
|
524
|
+
grid_type=grid_type,
|
|
525
|
+
cell_type_key=cell_type_key,
|
|
526
|
+
alpha=alpha
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
def simulate_deconvolution_benchmark(self,
|
|
530
|
+
cell_type_key: Optional[str] = None,
|
|
531
|
+
downsampling_factors: List[float] = [0.1, 0.25, 0.5],
|
|
532
|
+
grid_types: List[str] = ['hexagonal', 'square', 'kmeans'],
|
|
533
|
+
alpha: float = 0.01,
|
|
534
|
+
verbose: Optional[bool] = None) -> Dict[str, ad.AnnData]:
|
|
535
|
+
"""
|
|
536
|
+
Generate comprehensive deconvolution benchmark suite.
|
|
537
|
+
|
|
538
|
+
Creates multiple spatial resolution scenarios for testing deconvolution algorithms.
|
|
539
|
+
|
|
540
|
+
Parameters:
|
|
541
|
+
-----------
|
|
542
|
+
cell_type_key : str, optional
|
|
543
|
+
Key for cell type annotations (for ground truth)
|
|
544
|
+
downsampling_factors : List[float]
|
|
545
|
+
Different resolution reduction factors to test
|
|
546
|
+
grid_types : List[str]
|
|
547
|
+
Different spatial grid types to test
|
|
548
|
+
alpha : float
|
|
549
|
+
Alpha shape parameter for tissue boundary detection
|
|
550
|
+
verbose : bool, optional
|
|
551
|
+
Override default verbosity setting
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
--------
|
|
555
|
+
Dict[str, AnnData]
|
|
556
|
+
Dictionary mapping scenario names to benchmark datasets
|
|
557
|
+
"""
|
|
558
|
+
if self.is_multi_slice:
|
|
559
|
+
warnings.warn("Deconvolution benchmark with multi-slice input. Using first slice.")
|
|
560
|
+
adata_to_use = self.adata_list[0]
|
|
561
|
+
else:
|
|
562
|
+
adata_to_use = self.adata
|
|
563
|
+
|
|
564
|
+
if verbose is None:
|
|
565
|
+
verbose = self.verbose
|
|
566
|
+
|
|
567
|
+
return create_deconvolution_benchmark_suite(
|
|
568
|
+
adata_to_use,
|
|
569
|
+
cell_type_key=cell_type_key,
|
|
570
|
+
downsampling_factors=downsampling_factors,
|
|
571
|
+
grid_types=grid_types,
|
|
572
|
+
alpha=alpha,
|
|
573
|
+
verbose=verbose
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
# ===============================
|
|
577
|
+
# DECONVOLUTION SIMULATION METHODS
|
|
578
|
+
# ===============================
|
|
579
|
+
|
|
580
|
+
def simulate_deconvolution(self,
|
|
581
|
+
downsampling_factor: float = 0.25,
|
|
582
|
+
grid_type: str = 'hexagonal',
|
|
583
|
+
cell_type_key: Optional[str] = None,
|
|
584
|
+
alpha: float = 0.01,
|
|
585
|
+
# Single slice simulation parameters
|
|
586
|
+
sigma: float = 1.0,
|
|
587
|
+
visualize_fits: bool = False,
|
|
588
|
+
use_heuristic_search: bool = False,
|
|
589
|
+
alteration_config: Optional[Any] = None,
|
|
590
|
+
boundary_multiplier: float = 1.1,
|
|
591
|
+
verbose: Optional[bool] = None,
|
|
592
|
+
**simulation_kwargs) -> ad.AnnData:
|
|
593
|
+
"""
|
|
594
|
+
Generate deconvolution simulation using two-stage approach:
|
|
595
|
+
1. High-quality single slice simulation
|
|
596
|
+
2. Spatial aggregation to create deconvolution data
|
|
597
|
+
|
|
598
|
+
This method combines the robust single slice simulation with spatial
|
|
599
|
+
downsampling to create realistic deconvolution benchmark data.
|
|
600
|
+
|
|
601
|
+
Parameters:
|
|
602
|
+
-----------
|
|
603
|
+
downsampling_factor : float, default=0.25
|
|
604
|
+
Factor to reduce spatial resolution (0.25 = 4x fewer spots)
|
|
605
|
+
grid_type : str, default='hexagonal'
|
|
606
|
+
Type of spatial grid ('hexagonal', 'square', 'kmeans')
|
|
607
|
+
cell_type_key : str, optional
|
|
608
|
+
Key in adata.obs for cell type annotations (ground truth)
|
|
609
|
+
alpha : float, default=0.01
|
|
610
|
+
Alpha parameter for tissue boundary detection
|
|
611
|
+
|
|
612
|
+
Single Slice Simulation Parameters:
|
|
613
|
+
----------------------------------
|
|
614
|
+
sigma : float, default=1.0
|
|
615
|
+
Spatial smoothness parameter for G-SRBA algorithm
|
|
616
|
+
visualize_fits : bool, default=False
|
|
617
|
+
Whether to show parameter fitting visualizations
|
|
618
|
+
use_heuristic_search : bool, default=False
|
|
619
|
+
Whether to use heuristic parameter assignment
|
|
620
|
+
alteration_config : AlterationConfig, optional
|
|
621
|
+
Configuration for marginal distribution alterations
|
|
622
|
+
boundary_multiplier : float, default=1.1
|
|
623
|
+
Multiplier for maximum count boundary constraint
|
|
624
|
+
verbose : bool, optional
|
|
625
|
+
Override default verbosity setting
|
|
626
|
+
**simulation_kwargs
|
|
627
|
+
Additional parameters passed to simulate_single_slice
|
|
628
|
+
|
|
629
|
+
Returns:
|
|
630
|
+
--------
|
|
631
|
+
AnnData
|
|
632
|
+
Deconvolution simulation data with spatial aggregation
|
|
633
|
+
"""
|
|
634
|
+
if self.is_multi_slice:
|
|
635
|
+
raise ValueError("Deconvolution simulation requires single slice input data")
|
|
636
|
+
|
|
637
|
+
if verbose is None:
|
|
638
|
+
verbose = self.verbose
|
|
639
|
+
|
|
640
|
+
simulator = self._get_deconvolution_simulator()
|
|
641
|
+
|
|
642
|
+
return simulator.simulate_deconvolution_data(
|
|
643
|
+
reference_adata=self.reference_adata,
|
|
644
|
+
downsampling_factor=downsampling_factor,
|
|
645
|
+
grid_type=grid_type,
|
|
646
|
+
alpha=alpha,
|
|
647
|
+
cell_type_key=cell_type_key,
|
|
648
|
+
sigma=sigma,
|
|
649
|
+
visualize_fits=visualize_fits,
|
|
650
|
+
use_heuristic_search=use_heuristic_search,
|
|
651
|
+
alteration_config=alteration_config,
|
|
652
|
+
boundary_multiplier=boundary_multiplier,
|
|
653
|
+
verbose=verbose,
|
|
654
|
+
**simulation_kwargs
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
def simulate_deconvolution_benchmark(self,
|
|
658
|
+
cell_type_key: Optional[str] = None,
|
|
659
|
+
downsampling_factors: List[float] = [0.1, 0.25, 0.5],
|
|
660
|
+
grid_types: List[str] = ['hexagonal', 'square'],
|
|
661
|
+
sigma_values: List[float] = [0.5, 1.0, 1.5],
|
|
662
|
+
alpha: float = 0.01,
|
|
663
|
+
verbose: Optional[bool] = None,
|
|
664
|
+
**simulation_kwargs) -> Dict[str, ad.AnnData]:
|
|
665
|
+
"""
|
|
666
|
+
Create comprehensive deconvolution benchmark suite.
|
|
667
|
+
|
|
668
|
+
Generates multiple deconvolution scenarios by combining different
|
|
669
|
+
spatial aggregation parameters with single slice simulation parameters.
|
|
670
|
+
|
|
671
|
+
Parameters:
|
|
672
|
+
-----------
|
|
673
|
+
cell_type_key : str, optional
|
|
674
|
+
Key for cell type annotations in reference data
|
|
675
|
+
downsampling_factors : List[float]
|
|
676
|
+
Different resolution reduction factors to test
|
|
677
|
+
grid_types : List[str]
|
|
678
|
+
Different spatial grid types to test
|
|
679
|
+
sigma_values : List[float]
|
|
680
|
+
Different spatial smoothness values for single slice simulation
|
|
681
|
+
alpha : float, default=0.01
|
|
682
|
+
Alpha parameter for tissue boundary detection
|
|
683
|
+
verbose : bool, optional
|
|
684
|
+
Override default verbosity setting
|
|
685
|
+
**simulation_kwargs
|
|
686
|
+
Additional parameters for single slice simulation
|
|
687
|
+
|
|
688
|
+
Returns:
|
|
689
|
+
--------
|
|
690
|
+
Dict[str, AnnData]
|
|
691
|
+
Dictionary of benchmark datasets with descriptive keys
|
|
692
|
+
"""
|
|
693
|
+
if self.is_multi_slice:
|
|
694
|
+
raise ValueError("Deconvolution benchmark requires single slice input data")
|
|
695
|
+
|
|
696
|
+
if verbose is None:
|
|
697
|
+
verbose = self.verbose
|
|
698
|
+
|
|
699
|
+
simulator = self._get_deconvolution_simulator()
|
|
700
|
+
|
|
701
|
+
return simulator.create_deconvolution_benchmark_suite(
|
|
702
|
+
reference_adata=self.reference_adata,
|
|
703
|
+
downsampling_factors=downsampling_factors,
|
|
704
|
+
grid_types=grid_types,
|
|
705
|
+
cell_type_key=cell_type_key,
|
|
706
|
+
sigma_values=sigma_values,
|
|
707
|
+
alpha=alpha,
|
|
708
|
+
**simulation_kwargs
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
|