multigedi.tools.multigedi¶
- multigedi.tools.multigedi(mdata, modalities, sample_key, *, K=10, seed=None, mode='Bsphere', max_iterations=30, track_interval=1, num_threads=0, verbose=1, use_gpu=False, gpu_device=0, gpu_output_dir=None, batch_size=-1, key_added='multigedi', copy=False)[source]¶
Run multi-modal GEDI (MultiGEDI) integration.
Trains a hub-and-spoke MultiGEDI model across multiple data modalities, learning shared cell loadings (joint B) and modality-specific metagenes.
- Parameters:
mdata (
MuData) – MuData object. Each key inmodalitiesmust be a valid modality name accessible asmdata[name].modalities (
dict[str,dict[str,Any]]) –Dict mapping modality name → configuration dict. Each config may have: -
obs_type:"M"(default),"M_list", or"X"-orthoZ: bool (defaultTrue) -fixed_si: scalar fixed cell-effect value, matching R’sfixed_sioptionlayer: layer name to use instead of.X(for"M"/"X")layers: 2-tuple of layer names for"M_list"(first may beNoneto use.X)Z_shrinkage,A_shrinkage,Qi_shrinkage,Rk_shrinkage,oi_shrinkage,o_shrinkage, andsi_shrinkage: finite positive scalar regularization strengths (all default to 1, matching R)
sample_key (
str) – Column inmdata[name].obscontaining sample/batch labels. Whole sample blocks may be absent from a modality. For every sample present in more than one modality, those modalities must have the sameobs_namesin the same within-sample order. The function validates this identity contract before fitting and raises rather than silently coupling different cells.K (
int, default:10) – Number of latent factors.seed (
int|None, default:None) – Random seed for reproducible per-modality rSVD initialization.mode (
Literal['Bsphere','Bl2'], default:'Bsphere') – B normalization:"Bsphere"(recommended) or"Bl2".max_iterations (
int, default:30) – Number of optimization iterations.track_interval (
int, default:1) – Record sigma² and dZ every this many iterations.num_threads (
int, default:0) – OpenMP thread count (0 = auto).verbose (
int, default:1) – Verbosity level (0 = silent, 1 = normal).use_gpu (
bool, default:False) – WhenTrue, run optimization on GPU via the CUDA backend. Requires a GPU-enabled build containing the_multigedi_gpu_pyextension and a visible CUDA device. Supports 1..N modalities of any combination ofobs_type(“M”, “M_list”, “X”), up to the backend’s 16-modality cap.gpu_device (
int, default:0) – CUDA device index (default 0).gpu_output_dir (
str|None, default:None) – Reserved (no effect in v1.1; the in-memory pipeline does not write a binary directory). Kept in the signature for backward compatibility with notebooks that still pass it.batch_size (
int, default:-1) – GPUsolve_Zcell-batch size (Tier-1 BCD batching). Only consulted whenuse_gpu=True; ignored on the CPU path.-1(default) selects the single-shotsolve_Zbelow the automatic cell ceiling. A positive value tiles the transient J×N residual scratch into cell-batches. When left at-1and the total cell count exceeds ~500K, the runner auto-selects a batch size, emits a warning, and records that effective value in the result. Passing0or any value< -1is rejected. Seedocs/architecture/bcd_batching.md.key_added (
str, default:'multigedi') – Key under which results are stored inmdata.unsand used in each modality’sobsmcoordinate name.copy (
bool, default:False) – Unused (reserved for API symmetry). Always returnsNone.
- Return type:
- Returns:
None. Results are stored on the MuData container per scverseconvention for multi-modal joint analyses- ``mdata.uns[key_added]`— single source` oftruth–params— training configuration, including backend/device and requested/effective GPU batch sizesmodel—joint, plus per-modalityZ,D,Bi,o,sigma2,tracking.joint.global_cell_metadatacontains row-alignedsampleandcell_idlists for joining the shared embedding without relying on row position alone.svd—jointd/u/vfrom joint SVD
- ``mdata[name].obsm[f``”X_{key_added}_pca”:py:class:`]— joint PCA coordinates` – (equal toV * d) sliced to that modality’s local cells, sosc.pp.neighbors(mdata[name], use_rep="X_multigedi_pca")works even when modalities cover different sample subsets.For backwards compatibility a copyofmdata.uns[key_added]isalso writtentomdata[first_modality].uns[key_added]with aDeprecationWarning. The first-modality location will be removedin a future release; new code should read from ``mdata.uns`.`
Examples
>>> import multigedi as gd >>> import mudata as md >>> mdata = md.read_h5mu("data.h5mu") >>> gd.tl.multigedi( ... mdata, ... modalities={ ... "gene": {"obs_type": "M", "orthoZ": True}, ... "splicing": {"obs_type": "M_list", "orthoZ": False, ... "layers": (None, "M2")}, ... }, ... sample_key="sample", ... K=20, ... max_iterations=30, ... ) >>> mdata["gene"].obsm["X_multigedi_pca"] # joint PCA rows for gene cells
Notes
Reproducibility & relationship to R `multigedi`. The BCD optimizer underlying
tl.multigediimplements the R reference model. Given the same exportediter_0state, local CPU/R comparisons agree to approximately1e-12. However an independenttl.multigedirun does NOT reproduce R’s exact iter_N factorization — the rSVD initializer draws its random projection matrix from numpy’s RNG, while R draws from R’s Mersenne-Twister. Different initial projections route BCD into different deep local minima of the non-convex objective. Both minima have nearly identical sigma², but the loadingsZ, the cell embeddingX_multigedi_pca, and the pairwise cell distances differ across backends. Within a single backend, identicalmdataand sameMultiGEDIModel(seed=...)produce identical output. Pick one backend per dataset; do not mix R-trained and Python-trained outputs downstream. For exact agreement with a specific R reference run to the documented tolerance, load R’s exportediter_0viaMultiGEDIModel.add_modality(..., init_state=ref).``batch_size`` is part of run identity. On the GPU path, the
batch_sizeargument becomes part of the run’s identity: a run is only reproducible against another run that used the same effectivebatch_size.batch_size=-1(the default) uses the single-shotsolve_Zbelow the automatic ceiling. A positivebatch_sizetiles thesolve_Zresidual reduction; re-associating that sum in IEEE-754 f64 shifts the trailing ULPs, so two runs with differentbatch_sizevalues land ≤1e-11 apart — the same way two different seeds would. This drift is expected and joins the existingmultigedinumerical-divergence family; it must not be “fixed”. Do not mix outputs produced with differentbatch_sizevalues downstream. When the GPU runner auto-selects abatch_size(cell count above the ~500K single-GPUsolve_Zceiling), it emits a warning and stores the chosen value asparams["effective_batch_size"].