///|
/// mixOmics Demo: Multi-omics integration methods.
/// Demonstrates PLS, sparse PLS (sPLS), and DIABLO algorithms
/// for integrating multiple omics data modalities.
///
/// Run: moon run examples/mix_omics_demo/main.mbt
fn main {
println("=== mixOmics Demo ===")
println("")
// === 1. Create simulated multi-omics data ===
println("--- 1. Create Simulated Multi-Omics Data ---")
// Gene expression data: 20 samples x 10 genes
let x_data : Array[Array[Double]] = [
[2.1, 3.2, 1.5, 4.0, 2.8, 3.5, 1.9, 4.2, 3.1, 2.7],
[1.8, 2.9, 1.2, 3.8, 2.5, 3.2, 1.7, 4.0, 2.9, 2.4],
[2.5, 3.6, 1.8, 4.3, 3.0, 3.8, 2.2, 4.5, 3.4, 2.9],
[1.2, 2.4, 0.8, 3.2, 2.0, 2.7, 1.3, 3.5, 2.3, 1.8],
[3.0, 4.1, 2.2, 4.8, 3.5, 4.3, 2.6, 5.0, 3.9, 3.3],
[0.8, 1.9, 0.5, 2.8, 1.5, 2.2, 0.9, 3.1, 1.8, 1.4],
[2.7, 3.8, 2.0, 4.5, 3.2, 4.0, 2.4, 4.7, 3.6, 3.1],
[1.5, 2.6, 1.0, 3.5, 2.2, 2.9, 1.5, 3.7, 2.6, 2.0],
[3.2, 4.3, 2.4, 5.0, 3.7, 4.5, 2.8, 5.2, 4.1, 3.5],
[1.0, 2.1, 0.7, 3.0, 1.7, 2.4, 1.1, 3.3, 2.0, 1.6],
[2.3, 3.4, 1.6, 4.1, 2.9, 3.6, 2.0, 4.3, 3.2, 2.8],
[1.6, 2.7, 1.1, 3.6, 2.3, 3.0, 1.6, 3.8, 2.7, 2.1],
[2.8, 3.9, 2.1, 4.6, 3.3, 4.1, 2.5, 4.8, 3.7, 3.2],
[1.3, 2.5, 0.9, 3.3, 2.1, 2.8, 1.4, 3.6, 2.4, 1.9],
[3.1, 4.2, 2.3, 4.9, 3.6, 4.4, 2.7, 5.1, 4.0, 3.4],
[0.9, 2.0, 0.6, 2.9, 1.6, 2.3, 1.0, 3.2, 1.9, 1.5],
[2.6, 3.7, 1.9, 4.4, 3.1, 3.9, 2.3, 4.6, 3.5, 3.0],
[1.4, 2.5, 1.0, 3.4, 2.2, 2.9, 1.5, 3.6, 2.5, 2.0],
[2.9, 4.0, 2.2, 4.7, 3.4, 4.2, 2.6, 4.9, 3.8, 3.3],
[1.1, 2.3, 0.8, 3.1, 1.8, 2.5, 1.2, 3.4, 2.2, 1.7],
]
// Clinical traits data: 20 samples x 3 traits
let y_data : Array[Array[Double]] = [
[5.2, 3.1, 10.5],
[4.8, 2.9, 9.8],
[5.5, 3.4, 11.2],
[3.9, 2.3, 8.5],
[6.0, 3.8, 12.0],
[3.2, 1.9, 7.2],
[5.7, 3.5, 11.5],
[4.3, 2.6, 9.0],
[6.2, 4.0, 12.5],
[3.5, 2.1, 7.8],
[5.3, 3.2, 10.8],
[4.5, 2.7, 9.2],
[5.9, 3.7, 11.8],
[3.8, 2.2, 8.2],
[6.1, 3.9, 12.2],
[3.3, 2.0, 7.5],
[5.6, 3.4, 11.3],
[4.1, 2.5, 8.8],
[6.0, 3.8, 12.0],
[3.6, 2.2, 8.0],
]
let n_samples = x_data.length()
let n_genes = 10
let n_traits = 3
println(" Gene expression (X): \{n_samples} samples x \{n_genes} genes")
println(" Clinical traits (Y): \{n_samples} samples x \{n_traits} traits")
println(
" X[0] (first 5 genes): [\{x_data[0][0]}, \{x_data[0][1]}, \{x_data[0][2]}, \{x_data[0][3]}, \{x_data[0][4]}]",
)
println(" Y[0]: [\{y_data[0][0]}, \{y_data[0][1]}, \{y_data[0][2]}]")
println("")
// === 2. Run PLS regression ===
println("--- 2. PLS Regression (2 components) ---")
let pls_opts = @bio.pls_options_full(2, "regression", true, 500, 1.0e-6)
println(
" Options: ncomp=\{pls_opts.ncomp}, mode=\{pls_opts.mode}, scale=\{pls_opts.scale}",
)
let pls_result = @bio.run_pls(x_data, y_data, pls_opts)
// Display scores
println("")
println(" X Scores (first 5 samples):")
for i = 0; i < 5; i = i + 1 {
println(
" Sample \{i}: [\{pls_result.x_scores[i][0]}, \{pls_result.x_scores[i][1]}]",
)
}
println("")
println(" Y Scores (first 5 samples):")
for i = 0; i < 5; i = i + 1 {
println(
" Sample \{i}: [\{pls_result.y_scores[i][0]}, \{pls_result.y_scores[i][1]}]",
)
}
// Display loadings
println("")
println(" X Loadings (gene weights):")
println(" Gene Comp1 Comp2")
println(" ---- ------ ------")
let gene_names = [
"GeneA", "GeneB", "GeneC", "GeneD", "GeneE", "GeneF", "GeneG", "GeneH", "GeneI",
"GeneJ",
]
for j = 0; j < n_genes; j = j + 1 {
println(
" \{gene_names[j]} \{pls_result.x_loadings[j][0]} \{pls_result.x_loadings[j][1]}",
)
}
println("")
println(" Y Loadings (trait weights):")
println(" Trait Comp1 Comp2")
println(" ---- ------ ------")
let trait_names = ["Trait1", "Trait2", "Trait3"]
for k = 0; k < n_traits; k = k + 1 {
println(
" \{trait_names[k]} \{pls_result.y_loadings[k][0]} \{pls_result.y_loadings[k][1]}",
)
}
// Explained variance
println("")
println(" Explained Variance:")
for comp = 0; comp < pls_result.ncomp; comp = comp + 1 {
let (x_var, y_var) = pls_result.explained_variance[comp]
println(
" Component \{comp + 1}: X = \{x_var} (\{x_var * 100.0}%), Y = \{y_var} (\{y_var * 100.0}%)",
)
}
println("")
// === 3. Run sparse PLS ===
println("--- 3. Sparse PLS (sPLS) with Variable Selection ---")
let spls_opts = @bio.spls_options_full(2, [5, 5], [2, 2], true, 500, 1.0e-6)
println(
" Options: ncomp=\{spls_opts.ncomp}, keep_x=\{Repr(spls_opts.keep_x)}, keep_y=\{Repr(spls_opts.keep_y)}",
)
let spls_result = @bio.run_spls(x_data, y_data, spls_opts)
// Show selected variables
println("")
println(" Selected X variables (genes):")
for comp = 0; comp < spls_result.ncomp; comp = comp + 1 {
let selected = Array::make(0, 0)
for j = 0; j < n_genes; j = j + 1 {
if spls_result.x_selected[j][comp] {
selected.push(j)
}
}
let mut names = ""
for i = 0; i < selected.length(); i = i + 1 {
let idx = selected[i]
let comma = if i < selected.length() - 1 { ", " } else { "" }
names = "\{names}\{gene_names[idx]}\{comma}"
}
println(" Component \{comp + 1}: [\{names}]")
}
println("")
println(" Selected Y variables (traits):")
for comp = 0; comp < spls_result.ncomp; comp = comp + 1 {
let selected = Array::make(0, 0)
for k = 0; k < n_traits; k = k + 1 {
if spls_result.y_selected[k][comp] {
selected.push(k)
}
}
let mut names = ""
for i = 0; i < selected.length(); i = i + 1 {
let idx = selected[i]
let comma = if i < selected.length() - 1 { ", " } else { "" }
names = "\{names}\{trait_names[idx]}\{comma}"
}
println(" Component \{comp + 1}: [\{names}]")
}
// Display sPLS loadings
println("")
println(" sPLS X Loadings (gene weights):")
println(" Gene Comp1 Comp2")
println(" ---- ------ ------")
for j = 0; j < n_genes; j = j + 1 {
println(
" \{gene_names[j]} \{spls_result.x_loadings[j][0]} \{spls_result.x_loadings[j][1]}",
)
}
// Explained variance
println("")
println(" sPLS Explained Variance:")
for comp = 0; comp < spls_result.ncomp; comp = comp + 1 {
let (x_var, y_var) = spls_result.explained_variance[comp]
println(
" Component \{comp + 1}: X = \{x_var} (\{x_var * 100.0}%), Y = \{y_var} (\{y_var * 100.0}%)",
)
}
println("")
// === 4. Run DIABLO ===
println("--- 4. DIABLO Multi-Block Integration ---")
// Create 2 blocks: genomics (5 features) + transcriptomics (5 features)
let block1 : Array[Array[Double]] = [
[1.2, 2.3, 0.8, 3.1, 2.5],
[1.0, 2.0, 0.6, 2.9, 2.2],
[1.5, 2.6, 1.0, 3.3, 2.8],
[0.8, 1.7, 0.4, 2.4, 1.9],
[1.8, 2.9, 1.2, 3.6, 3.1],
[0.5, 1.4, 0.3, 2.0, 1.5],
[1.6, 2.7, 1.1, 3.4, 2.9],
[0.9, 1.9, 0.5, 2.7, 2.1],
[2.0, 3.1, 1.4, 3.8, 3.3],
[0.6, 1.5, 0.3, 2.2, 1.7],
[1.4, 2.5, 0.9, 3.2, 2.7],
[1.1, 2.1, 0.7, 2.8, 2.3],
[1.7, 2.8, 1.2, 3.5, 3.0],
[0.7, 1.6, 0.4, 2.5, 1.8],
[1.9, 3.0, 1.3, 3.7, 3.2],
[0.5, 1.4, 0.3, 2.1, 1.6],
[1.5, 2.6, 1.0, 3.3, 2.8],
[0.8, 1.8, 0.5, 2.6, 2.0],
[1.8, 2.9, 1.2, 3.6, 3.1],
[0.6, 1.5, 0.3, 2.3, 1.7],
]
let block2 : Array[Array[Double]] = [
[3.1, 5.2, 2.8, 4.5, 3.9],
[2.9, 4.8, 2.5, 4.2, 3.6],
[3.4, 5.5, 3.1, 4.8, 4.2],
[2.3, 3.9, 1.8, 3.5, 3.0],
[3.8, 6.0, 3.5, 5.2, 4.5],
[1.9, 3.2, 1.5, 2.8, 2.4],
[3.5, 5.7, 3.2, 4.9, 4.3],
[2.6, 4.3, 2.1, 3.7, 3.2],
[4.0, 6.2, 3.7, 5.4, 4.7],
[2.1, 3.5, 1.7, 3.0, 2.6],
[3.2, 5.3, 2.9, 4.6, 4.0],
[2.7, 4.5, 2.3, 3.9, 3.3],
[3.7, 5.9, 3.4, 5.1, 4.4],
[2.2, 3.7, 1.8, 3.2, 2.7],
[3.9, 6.1, 3.6, 5.3, 4.6],
[2.0, 3.3, 1.6, 2.9, 2.5],
[3.4, 5.6, 3.1, 4.8, 4.2],
[2.5, 4.1, 2.0, 3.6, 3.1],
[3.8, 6.0, 3.5, 5.2, 4.5],
[2.2, 3.6, 1.8, 3.1, 2.6],
]
let blocks = [block1, block2]
let block_names = ["Genomics", "Transcriptomics"]
// DIABLO options with variable selection
let diablo_opts = @bio.diablo_options_full(
2,
[[3, 3], [3, 3]],
[[0.0, 1.0], [1.0, 0.0]],
true,
500,
1.0e-6,
)
println(
" Options: ncomp=\{diablo_opts.ncomp}, n_blocks=2, scale=\{diablo_opts.scale}",
)
println(" Keep variables per component:")
for comp = 0; comp < diablo_opts.ncomp; comp = comp + 1 {
let kv = diablo_opts.keep_variables[comp]
println(" Component \{comp + 1}: \{Repr(kv)}")
}
let diablo_result = @bio.run_diablo(blocks, block_names, diablo_opts)
println("")
println(" Block scores (first 5 samples):")
for b = 0; b < diablo_result.n_blocks; b = b + 1 {
println(" \{block_names[b]}:")
for i = 0; i < 5; i = i + 1 {
println(
" Sample \{i}: [\{diablo_result.block_scores[b][i][0]}, \{diablo_result.block_scores[b][i][1]}]",
)
}
}
println("")
println(" Block loadings:")
for b = 0; b < diablo_result.n_blocks; b = b + 1 {
println(" \{block_names[b]}:")
let pb = diablo_result.block_sizes[b]
let feature_names = if b == 0 {
["SNP_A", "SNP_B", "SNP_C", "SNP_D", "SNP_E"]
} else {
["Exp_A", "Exp_B", "Exp_C", "Exp_D", "Exp_E"]
}
println(" Feature Comp1 Comp2")
println(" ------ ------ ------")
for j = 0; j < pb; j = j + 1 {
println(
" \{feature_names[j]} \{diablo_result.block_loadings[b][j][0]} \{diablo_result.block_loadings[b][j][1]}",
)
}
}
println("")
println(" Variable selection per block:")
for b = 0; b < diablo_result.n_blocks; b = b + 1 {
println(" \{block_names[b]}:")
let pb = diablo_result.block_sizes[b]
for comp = 0; comp < diablo_result.ncomp; comp = comp + 1 {
let selected = Array::make(0, 0)
for j = 0; j < pb; j = j + 1 {
if diablo_result.block_selected[b][j][comp] {
selected.push(j)
}
}
let mut names = ""
for i = 0; i < selected.length(); i = i + 1 {
let idx = selected[i]
let comma = if i < selected.length() - 1 { ", " } else { "" }
let fname = if b == 0 {
["SNP_A", "SNP_B", "SNP_C", "SNP_D", "SNP_E"][idx]
} else {
["Exp_A", "Exp_B", "Exp_C", "Exp_D", "Exp_E"][idx]
}
names = "\{names}\{fname}\{comma}"
}
println(" Component \{comp + 1}: [\{names}]")
}
}
println("")
// === 5. Compare PLS vs sPLS ===
println("--- 5. Compare PLS vs sPLS ---")
println(" X Loadings comparison (first 5 genes, Component 1):")
println(" Gene PLS sPLS")
println(" ---- --- ----")
for j = 0; j < 5; j = j + 1 {
println(
" \{gene_names[j]} \{pls_result.x_loadings[j][0]} \{spls_result.x_loadings[j][0]}",
)
}
println("")
println(" Variance explained comparison:")
println(" Component PLS_X sPLS_X PLS_Y sPLS_Y")
println(" --------- ------- ------- ------- -------")
for comp = 0; comp < pls_result.ncomp; comp = comp + 1 {
let (pls_x, pls_y) = pls_result.explained_variance[comp]
let (spls_x, spls_y) = spls_result.explained_variance[comp]
println(" Comp \{comp + 1} \{pls_x} \{spls_x} \{pls_y} \{spls_y}")
}
println("")
println(" Key differences:")
println(" - PLS uses all variables, sPLS selects top-k per component")
println(" - sPLS sets unselected variable loadings to exactly 0")
println(
" - sPLS provides variable selection via x_selected/y_selected flags",
)
println(
" - PLS may explain slightly more variance but includes noise variables",
)
println("")
println("=== Demo Complete ===")
}