///|
/// openCyto automated flow-cytometry gating demo.
///
/// Demonstrates template-driven gating on synthetic flow cytometry data
/// using mindensity and flowClust gating methods.
fn main {
println("=== openCyto Gating Pipeline Demo ===\n")
// Create synthetic flow cytometry data
// Channels: FSC-A, SSC-A, FITC-A, PE-A
let channels = ["FSC-A", "SSC-A", "FITC-A", "PE-A"]
let sample_names = ["sample_01", "sample_02"]
// Generate events for 2 samples
let all_data : Array[Array[Array[Double]]] = Array(capacity=0)
for _s in 0..<2 {
let events : Array[Array[Double]] = Array(capacity=0)
// 500 events: mix of negative and positive populations
for i in 0..<500 {
let fsc = 100.0 + i.to_double() * 0.3
let ssc = 80.0 + i.to_double() * 0.2
// First 250 events: FITC-negative (low FITC), PE-negative
// Last 250 events: FITC-positive (high FITC), PE-positive
let fitc = if i < 250 {
10.0 + i.to_double() % 50.0 * 0.3
} else {
200.0 + i.to_double() % 50.0 * 0.5
}
let pe = if i < 250 {
5.0 + i.to_double() % 30.0 * 0.2
} else {
150.0 + i.to_double() % 30.0 * 0.4
}
events.push([fsc, ssc, fitc, pe])
}
all_data.push(events)
}
let flowset = @bio.OcFlowSet::new(
sample_names~,
channel_names=channels,
data=all_data,
)
println("FlowSet: " + flowset.data.length().to_string() + " samples")
println("Channels: " + channels.length().to_string())
println("Events per sample: " + flowset.data[0].length().to_string())
println("")
// Define gating template
let args1 : Map[String, Double] = Map([], capacity=4)
let args2 : Map[String, Double] = Map([], capacity=4)
args2["K"] = 2.0
let template : Array[@bio.OcGatingRule] = Array(capacity=0)
template.push(
@bio.OcGatingRule::new(
parent="root",
child="cells",
method_="mindensity",
dims=["FSC-A"],
args=args1,
),
)
template.push(
@bio.OcGatingRule::new(
parent="cells",
child="fitc_pos",
method_="mindensity",
dims=["FITC-A"],
args=args1,
),
)
template.push(
@bio.OcGatingRule::new(
parent="fitc_pos",
child="double_pos",
method_="flowClust",
dims=["FITC-A", "PE-A"],
args=args2,
),
)
println("Gating Template:")
let sorted_template = @bio.oc_topo_sort(template)
for rule in sorted_template {
let mut dims_str = rule.dims[0]
if rule.dims.length() > 1 {
dims_str = rule.dims[0] + ", " + rule.dims[1]
}
println(
" " +
rule.parent +
" -> " +
rule.child +
" (" +
rule.method_ +
" on " +
dims_str +
")",
)
}
println("")
// Run gating pipeline
let results = @bio.oc_gate_flow_set(flowset, template)
// Display results
println("Gating Results:")
for r in results {
let pct = if r.parent_events > 0 {
r.child_events.to_double() / r.parent_events.to_double() * 100.0
} else {
0.0
}
println(
" [" +
r.sample +
"] " +
r.population +
": " +
r.child_events.to_string() +
"/" +
r.parent_events.to_string() +
" events (" +
pct.to_string() +
"%)",
)
}
println("")
// Population statistics
println("Population Statistics:")
let stats = @bio.oc_population_stats(results, 500)
for s in stats {
println(
" " +
s.population +
" (" +
s.sample +
"): count=" +
s.count.to_string() +
", %parent=" +
s.percent_of_parent.to_string() +
", %total=" +
s.percent_of_total.to_string(),
)
}
println("")
// Demonstrate individual gating methods
println("--- Individual Gating Method Demos ---\n")
// mindensity on FITC channel
let fitc_vals = @bio.oc_extract_channel(flowset.data[0], 2)
let mindensity_gate = @bio.oc_mindensity(
fitc_vals,
"FITC-A",
Map([], capacity=4),
)
println(
"mindensity gate on FITC-A: [" +
mindensity_gate.min1.to_string() +
", " +
mindensity_gate.max1.to_string() +
"]",
)
// tailgate on PE channel
let pe_vals = @bio.oc_extract_channel(flowset.data[0], 3)
let tail_args : Map[String, Double] = Map([], capacity=4)
tail_args["quantile"] = 0.95
let tail_gate = @bio.oc_tailgate(pe_vals, "PE-A", tail_args)
println(
"tailgate gate on PE-A (q=0.95): [" +
tail_gate.min1.to_string() +
", " +
tail_gate.max1.to_string() +
"]",
)
// flowClust 1D on FITC
let fc_gate = @bio.oc_flow_clust(
fitc_vals,
[],
"FITC-A",
None,
Map([], capacity=4),
2,
)
println(
"flowClust 1D gate on FITC-A (K=2): [" +
fc_gate.min1.to_string() +
", " +
fc_gate.max1.to_string() +
"]",
)
// KDE demonstration
let h = @bio.oc_bandwidth_silverman(fitc_vals)
let (gx, gy) = @bio.oc_kde_grid(fitc_vals, 10, h)
println("\nKDE density (10 grid points, bandwidth=" + h.to_string() + "):")
for i in 0..