// Bioconductor Banksy-inspired spatial clustering workflow.
///|
fn banksy_demo_config() -> @src.BanksyComputeConfig {
@src.BanksyComputeConfig::create(
max_harmonic=1,
k_geom=[4, 4],
spatial_mode=@src.banksy_knn_median(),
seed=7,
) catch {
BanksyError(message) => abort("invalid BANKSY configuration: " + message)
}
}
///|
fn banksy_demo_cluster_config() -> @src.BanksyClusterConfig {
@src.BanksyClusterConfig::create(
n_clusters=2,
n_starts=8,
max_iterations=100,
tolerance=1.0e-8,
seed=3,
) catch {
BanksyError(message) =>
abort("invalid BANKSY cluster configuration: " + message)
}
}
///|
fn main {
println("=== Bioconductor Banksy Demo ===")
let (expression, coordinates, gene_names, spot_names) = @src.banksy_example_data()
let config = banksy_demo_config()
let model = @src.banksy_compute_with_config(
expression,
coordinates,
config,
gene_names~,
spot_names~,
) catch {
BanksyError(message) =>
abort("BANKSY feature computation failed: " + message)
}
println("\n1. Compute spatial neighborhood harmonics")
println(" " + model.summary())
println(
" first spot left_marker: expression=" +
model.expression[0][0].to_string() +
", H0=" +
model.harmonics[0][0][0].to_string() +
", H1=" +
model.harmonics[1][0][0].to_string(),
)
let mut weight_sum = 0.0
for neighbor in model.neighbors[0][0] {
weight_sum = weight_sum + neighbor.weight
}
println(
" H0 neighbors=" +
model.neighbors[0][0].length().to_string() +
", normalized weight sum=" +
weight_sum.to_string(),
)
println("\n2. Compare cell-typing and spatial-domain lambda values")
let typing = @src.banksy_run(
model,
lambda=0.2,
n_components=3,
cluster_config=banksy_demo_cluster_config(),
max_harmonic=1,
scale=true,
smooth=true,
smoothing_k=3,
smoothing_threshold=0.5,
smoothing_iterations=10,
) catch {
BanksyError(message) => abort("BANKSY cell-typing run failed: " + message)
}
let domains = @src.banksy_run(
model,
lambda=0.8,
n_components=3,
cluster_config=banksy_demo_cluster_config(),
max_harmonic=1,
scale=true,
smooth=true,
smoothing_k=3,
smoothing_threshold=0.5,
smoothing_iterations=10,
) catch {
BanksyError(message) => abort("BANKSY domain run failed: " + message)
}
println(" lambda=0.2: " + typing.summary())
println(" lambda=0.8: " + domains.summary())
println(
" PCA dimensions=" +
typing.pca.n_spots().to_string() +
"x" +
typing.pca.n_components.to_string() +
", cluster sizes=" +
repr(typing.clustering.cluster_sizes),
)
let ari = @src.banksy_adjusted_rand_index(
typing.final_labels(),
domains.final_labels(),
) catch {
BanksyError(message) => abort("BANKSY ARI failed: " + message)
}
println(" adjusted Rand index between lambda runs=" + ari.to_string())
println("\n3. Sweep spatial weights")
let sweep = @src.banksy_parameter_sweep(
model,
[0.0, 0.2, 0.5, 0.8],
n_components=3,
cluster_config=banksy_demo_cluster_config(),
max_harmonic=1,
scale=true,
) catch {
BanksyError(message) => abort("BANKSY parameter sweep failed: " + message)
}
for run in sweep {
println(
" lambda=" +
run.lambda.to_string() +
", silhouette=" +
run.clustering.silhouette.to_string() +
", spatial agreement=" +
run.spatial_agreement.to_string(),
)
}
println("\n4. Enrich an immutable SpatialExperiment")
let experiment = @src.SpatialExperiment::new()
ignore(@src.se_add_assay(experiment, "logcounts", expression))
for gene_name in gene_names {
ignore(@src.se_add_row(experiment, Map([("gene_name", gene_name)])))
}
for spot in 0..
abort("BANKSY SpatialExperiment integration failed: " + message)
}
println(
" assays include H0=" +
integrated.experiment.assay.contains("H0").to_string() +
", H1=" +
integrated.experiment.assay.contains("H1").to_string(),
)
println(
" first cluster=" +
integrated.experiment.col_data[0]["banksy_cluster"] +
", input remains unchanged=" +
(!experiment.assay.contains("H0")).to_string(),
)
println("=== Demo Complete ===")
}