Files
rustytorch/demos/cellatlas-shared/src/lib.rs
T
osobhandClaude Opus 4.6 02d382d5f6 style: apply rustfmt across all crates and demos
Consistent formatting pass: line wrapping, import sorting, trailing
whitespace removal, let-chain indentation, merged derive attributes,
and unsafe block reformatting.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-12 07:01:58 -07:00

639 lines
18 KiB
Rust

//! Shared IPC types for CellAtlas single-cell transcriptomics demo.
//!
//! This crate provides data structures for communication between
//! the Tauri frontend and Rust backend for single-cell analysis.
use serde::{Deserialize, Serialize};
// ============================================================================
// Cell Data Types
// ============================================================================
/// A single cell with gene expression data.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Cell {
/// Unique cell identifier
pub id: String,
/// Cell barcode (from sequencing)
pub barcode: Option<String>,
/// Gene expression values (sparse representation)
pub expression: SparseExpression,
/// Assigned cell type
pub cell_type: Option<CellType>,
/// Cell state annotations
pub state: Option<CellState>,
/// Quality metrics
pub qc_metrics: QualityMetrics,
/// Spatial coordinates (if spatial transcriptomics)
pub spatial_coords: Option<SpatialCoords>,
/// Cluster assignment
pub cluster_id: Option<usize>,
/// UMAP/t-SNE embedding coordinates
pub embedding: Option<Embedding2D>,
}
/// Sparse gene expression representation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SparseExpression {
/// Gene indices (into a gene name list)
pub gene_indices: Vec<usize>,
/// Expression values (counts or normalized)
pub values: Vec<f32>,
/// Total number of genes in reference
pub num_genes: usize,
}
impl SparseExpression {
/// Get expression value for a gene index.
pub fn get(&self, gene_idx: usize) -> f32 {
self.gene_indices
.iter()
.position(|&idx| idx == gene_idx)
.map_or(0.0, |pos| self.values[pos])
}
/// Get the number of expressed genes.
pub fn num_expressed(&self) -> usize {
self.values.iter().filter(|&&v| v > 0.0).count()
}
/// Get total UMI counts.
pub fn total_counts(&self) -> f32 {
self.values.iter().sum()
}
}
/// 2D embedding coordinates (UMAP, t-SNE, etc.).
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Embedding2D {
pub x: f32,
pub y: f32,
}
/// Spatial coordinates for spatial transcriptomics.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct SpatialCoords {
/// X coordinate in tissue space
pub x: f32,
/// Y coordinate in tissue space
pub y: f32,
/// Z coordinate (for 3D spatial data)
pub z: Option<f32>,
/// Tissue section/slice ID
pub section_id: Option<u32>,
}
// ============================================================================
// Cell Type Annotation
// ============================================================================
/// Cell type annotation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CellType {
/// Primary cell type name
pub name: String,
/// Confidence score (0-1)
pub confidence: f32,
/// Hierarchical lineage (e.g., ["Immune", "T cell", "CD8+ T cell"])
pub lineage: Vec<String>,
/// Marker genes used for annotation
pub markers: Vec<String>,
}
/// Cell state annotation (activation, cycling, etc.).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CellState {
/// State name (e.g., "Activated", "Resting", "Cycling")
pub name: String,
/// State score (0-1)
pub score: f32,
/// Cell cycle phase (if applicable)
pub cell_cycle_phase: Option<CellCyclePhase>,
}
/// Cell cycle phase.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum CellCyclePhase {
G1,
S,
G2,
M,
G0,
}
/// Quality control metrics for a cell.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityMetrics {
/// Number of genes detected
pub n_genes: usize,
/// Total UMI counts
pub total_counts: f32,
/// Percentage of mitochondrial reads
pub pct_mito: f32,
/// Percentage of ribosomal reads
pub pct_ribo: f32,
/// Doublet score (0-1, higher = more likely doublet)
pub doublet_score: Option<f32>,
}
// ============================================================================
// Dataset Types
// ============================================================================
/// Single-cell dataset.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SingleCellDataset {
/// Dataset name
pub name: String,
/// Dataset description
pub description: Option<String>,
/// List of gene names
pub gene_names: Vec<String>,
/// Number of cells
pub n_cells: usize,
/// Number of genes
pub n_genes: usize,
/// Data modality
pub modality: DataModality,
/// Organism
pub organism: Organism,
/// Tissue/sample source
pub tissue: Option<String>,
}
/// Data modality.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DataModality {
/// Single-cell RNA sequencing
ScRnaSeq,
/// Single-nucleus RNA sequencing
SnRnaSeq,
/// Spatial transcriptomics (Visium, etc.)
SpatialTranscriptomics,
/// CITE-seq (RNA + protein)
CiteSeq,
/// Single-cell ATAC-seq
ScAtacSeq,
/// Multi-modal (multiple modalities)
MultiModal,
}
/// Organism.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Organism {
Human,
Mouse,
Rat,
Zebrafish,
Drosophila,
Other,
}
// ============================================================================
// Analysis Request/Response Types
// ============================================================================
/// Request to analyze a single-cell dataset.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisRequest {
/// Dataset to analyze
pub dataset_id: String,
/// Analysis configuration
pub config: AnalysisConfig,
}
/// Analysis configuration.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AnalysisConfig {
/// Preprocessing options
pub preprocessing: PreprocessingConfig,
/// Dimensionality reduction
pub dim_reduction: DimReductionConfig,
/// Clustering options
pub clustering: ClusteringConfig,
/// Cell type annotation options
pub annotation: AnnotationConfig,
}
/// Preprocessing configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreprocessingConfig {
/// Minimum genes per cell
pub min_genes: usize,
/// Minimum cells per gene
pub min_cells: usize,
/// Maximum mitochondrial percentage
pub max_mito_pct: f32,
/// Normalization method
pub normalization: NormalizationMethod,
/// Number of highly variable genes
pub n_hvg: usize,
}
impl Default for PreprocessingConfig {
fn default() -> Self {
Self {
min_genes: 200,
min_cells: 3,
max_mito_pct: 20.0,
normalization: NormalizationMethod::LogNormalize,
n_hvg: 2000,
}
}
}
/// Normalization method.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NormalizationMethod {
/// Log normalization (Seurat/Scanpy default)
LogNormalize,
/// SCTransform (variance stabilizing)
ScTransform,
/// Counts per million
Cpm,
/// No normalization (use raw counts)
None,
}
/// Dimensionality reduction configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DimReductionConfig {
/// Number of PCA components
pub n_pcs: usize,
/// Method for 2D embedding
pub embedding_method: EmbeddingMethod,
/// Number of neighbors for UMAP/t-SNE
pub n_neighbors: usize,
/// Minimum distance for UMAP
pub min_dist: f32,
}
impl Default for DimReductionConfig {
fn default() -> Self {
Self {
n_pcs: 50,
embedding_method: EmbeddingMethod::Umap,
n_neighbors: 15,
min_dist: 0.1,
}
}
}
/// Embedding method for visualization.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EmbeddingMethod {
Umap,
Tsne,
Pca,
ForceDirected,
}
/// Clustering configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusteringConfig {
/// Clustering algorithm
pub algorithm: ClusteringAlgorithm,
/// Resolution parameter (for Leiden/Louvain)
pub resolution: f32,
/// Number of clusters (for k-means)
pub n_clusters: Option<usize>,
}
impl Default for ClusteringConfig {
fn default() -> Self {
Self {
algorithm: ClusteringAlgorithm::Leiden,
resolution: 1.0,
n_clusters: None,
}
}
}
/// Clustering algorithm.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ClusteringAlgorithm {
Leiden,
Louvain,
Kmeans,
Hierarchical,
}
/// Cell type annotation configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnnotationConfig {
/// Annotation method
pub method: AnnotationMethod,
/// Reference dataset for transfer learning
pub reference: Option<String>,
/// Marker gene sets
pub marker_genes: Option<Vec<MarkerGeneSet>>,
}
impl Default for AnnotationConfig {
fn default() -> Self {
Self {
method: AnnotationMethod::MarkerBased,
reference: None,
marker_genes: None,
}
}
}
/// Annotation method.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AnnotationMethod {
/// Manual marker-based annotation
MarkerBased,
/// Transfer learning from reference
TransferLearning,
/// Foundation model (CellAtlas transformer)
FoundationModel,
}
/// Marker gene set for a cell type.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarkerGeneSet {
/// Cell type name
pub cell_type: String,
/// Positive markers (must be expressed)
pub positive_markers: Vec<String>,
/// Negative markers (must not be expressed)
pub negative_markers: Vec<String>,
}
// ============================================================================
// Analysis Results
// ============================================================================
/// Analysis results.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisResult {
/// Cluster assignments
pub clusters: Vec<ClusterInfo>,
/// Cell type annotations
pub cell_types: Vec<CellTypeAnnotation>,
/// Differential expression results
pub de_results: Option<Vec<DifferentialExpression>>,
/// Spatial niche analysis (if applicable)
pub spatial_niches: Option<Vec<SpatialNiche>>,
/// Quality summary
pub qc_summary: QcSummary,
}
/// Cluster information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterInfo {
/// Cluster ID
pub id: usize,
/// Number of cells
pub n_cells: usize,
/// Top marker genes
pub markers: Vec<String>,
/// Centroid in embedding space
pub centroid: Option<Embedding2D>,
}
/// Cell type annotation result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CellTypeAnnotation {
/// Cell type name
pub name: String,
/// Number of cells
pub n_cells: usize,
/// Percentage of total
pub percentage: f32,
/// Average confidence
pub avg_confidence: f32,
/// Clusters with this cell type
pub clusters: Vec<usize>,
}
/// Differential expression result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DifferentialExpression {
/// Gene name
pub gene: String,
/// Log2 fold change
pub log2fc: f32,
/// P-value
pub pvalue: f32,
/// Adjusted p-value (FDR)
pub padj: f32,
/// Percentage expressed in group 1
pub pct1: f32,
/// Percentage expressed in group 2
pub pct2: f32,
}
/// Spatial niche information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpatialNiche {
/// Niche ID
pub id: usize,
/// Niche name/description
pub name: String,
/// Cell types in this niche
pub cell_types: Vec<String>,
/// Cell type proportions
pub proportions: Vec<f32>,
/// Spatial center
pub center: SpatialCoords,
}
/// QC summary statistics.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QcSummary {
/// Total cells before filtering
pub n_cells_before: usize,
/// Total cells after filtering
pub n_cells_after: usize,
/// Total genes before filtering
pub n_genes_before: usize,
/// Total genes after filtering
pub n_genes_after: usize,
/// Median genes per cell
pub median_genes: f32,
/// Median counts per cell
pub median_counts: f32,
}
// ============================================================================
// Sample Data
// ============================================================================
/// Sample dataset for demo.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SampleDataset {
/// Dataset name
pub name: String,
/// Description
pub description: String,
/// Number of cells
pub n_cells: usize,
/// Modality
pub modality: DataModality,
/// Tissue type
pub tissue: String,
/// Organism
pub organism: Organism,
}
/// Get available sample datasets.
pub fn get_sample_datasets() -> Vec<SampleDataset> {
vec![
SampleDataset {
name: "PBMC 3k".to_string(),
description: "3,000 peripheral blood mononuclear cells from 10x Genomics".to_string(),
n_cells: 2700,
modality: DataModality::ScRnaSeq,
tissue: "Blood".to_string(),
organism: Organism::Human,
},
SampleDataset {
name: "Mouse Brain Spatial".to_string(),
description: "Spatial transcriptomics of mouse brain cortex".to_string(),
n_cells: 3500,
modality: DataModality::SpatialTranscriptomics,
tissue: "Brain".to_string(),
organism: Organism::Mouse,
},
SampleDataset {
name: "Tumor Microenvironment".to_string(),
description: "Single-cell analysis of tumor-infiltrating immune cells".to_string(),
n_cells: 8000,
modality: DataModality::ScRnaSeq,
tissue: "Tumor".to_string(),
organism: Organism::Human,
},
SampleDataset {
name: "Developing Heart".to_string(),
description: "Multi-nuclei RNA-seq of developing human heart".to_string(),
n_cells: 5000,
modality: DataModality::SnRnaSeq,
tissue: "Heart".to_string(),
organism: Organism::Human,
},
]
}
/// Common marker genes for major cell types.
pub fn get_common_markers() -> Vec<MarkerGeneSet> {
vec![
MarkerGeneSet {
cell_type: "T cell".to_string(),
positive_markers: vec!["CD3D".to_string(), "CD3E".to_string(), "CD3G".to_string()],
negative_markers: vec!["CD19".to_string(), "CD14".to_string()],
},
MarkerGeneSet {
cell_type: "B cell".to_string(),
positive_markers: vec!["CD19".to_string(), "MS4A1".to_string(), "CD79A".to_string()],
negative_markers: vec!["CD3D".to_string(), "CD14".to_string()],
},
MarkerGeneSet {
cell_type: "Monocyte".to_string(),
positive_markers: vec!["CD14".to_string(), "LYZ".to_string(), "CST3".to_string()],
negative_markers: vec!["CD3D".to_string(), "CD19".to_string()],
},
MarkerGeneSet {
cell_type: "NK cell".to_string(),
positive_markers: vec!["GNLY".to_string(), "NKG7".to_string(), "KLRD1".to_string()],
negative_markers: vec!["CD3D".to_string(), "CD19".to_string()],
},
MarkerGeneSet {
cell_type: "Dendritic cell".to_string(),
positive_markers: vec!["FCER1A".to_string(), "CD1C".to_string()],
negative_markers: vec!["CD3D".to_string(), "CD14".to_string()],
},
]
}
// ============================================================================
// Tests
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sparse_expression() {
let expr = SparseExpression {
gene_indices: vec![0, 5, 10],
values: vec![1.0, 2.5, 0.5],
num_genes: 100,
};
assert_eq!(expr.get(0), 1.0);
assert_eq!(expr.get(5), 2.5);
assert_eq!(expr.get(1), 0.0); // Not expressed
assert_eq!(expr.num_expressed(), 3);
assert!((expr.total_counts() - 4.0).abs() < 0.001);
}
#[test]
fn test_analysis_config_default() {
let config = AnalysisConfig::default();
assert_eq!(config.preprocessing.min_genes, 200);
assert_eq!(config.dim_reduction.n_pcs, 50);
assert_eq!(config.clustering.algorithm, ClusteringAlgorithm::Leiden);
}
#[test]
fn test_sample_datasets() {
let datasets = get_sample_datasets();
assert_eq!(datasets.len(), 4);
assert!(datasets.iter().any(|d| d.name == "PBMC 3k"));
}
#[test]
fn test_common_markers() {
let markers = get_common_markers();
assert!(!markers.is_empty());
let t_cell = markers.iter().find(|m| m.cell_type == "T cell").unwrap();
assert!(t_cell.positive_markers.contains(&"CD3D".to_string()));
}
#[test]
fn test_modality_serialization() {
let modality = DataModality::SpatialTranscriptomics;
let json = serde_json::to_string(&modality).unwrap();
assert_eq!(json, "\"spatial_transcriptomics\"");
}
#[test]
fn test_cell_cycle_phase() {
let phase = CellCyclePhase::S;
let json = serde_json::to_string(&phase).unwrap();
assert_eq!(json, "\"S\"");
}
#[test]
fn test_embedding_method() {
let method = EmbeddingMethod::Umap;
let json = serde_json::to_string(&method).unwrap();
assert_eq!(json, "\"umap\"");
}
#[test]
fn test_quality_metrics() {
let qc = QualityMetrics {
n_genes: 2000,
total_counts: 5000.0,
pct_mito: 5.0,
pct_ribo: 10.0,
doublet_score: Some(0.1),
};
assert_eq!(qc.n_genes, 2000);
assert!(qc.doublet_score.is_some());
}
}