modewise_operations/
functional_connectivity.rs1use modewise_connectivity::Measure;
2use modewise_fc::{Source, functional_connectivity};
3use modewise_pipeline::{
4 Artifact, ArtifactKind, Inputs, Operation, OperationDescriptor, Outputs, Port, resolve_params,
5};
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use crate::input;
11
12#[derive(Serialize, Deserialize, JsonSchema, Clone, Copy)]
13#[serde(rename_all = "lowercase")]
14enum Kind {
15 Pearson,
16 Plv,
17}
18
19#[derive(Serialize, Deserialize, JsonSchema)]
20#[serde(deny_unknown_fields)]
21struct Params {
22 measure: Kind,
24 #[serde(default)]
26 fisher_z: bool,
27}
28
29impl Params {
30 fn measure(&self) -> Result<Measure, String> {
31 match (self.measure, self.fisher_z) {
32 (Kind::Pearson, fisher_z) => Ok(Measure::Pearson { fisher_z }),
33 (Kind::Plv, false) => Ok(Measure::PhaseLockingValue),
34 (Kind::Plv, true) => Err("fisher_z applies to pearson only".into()),
35 }
36 }
37}
38
39pub struct FunctionalConnectivityOperation;
41
42impl Operation for FunctionalConnectivityOperation {
43 fn summary(&self) -> &'static str {
44 "Functional connectivity per component (and per epoch): Pearson correlation or phase locking value (docs/connectivity.md)."
45 }
46
47 fn params_schema(&self) -> serde_json::Value {
48 modewise_pipeline::schema_of::<Params>()
49 }
50
51 fn descriptor(&self) -> OperationDescriptor {
52 OperationDescriptor {
53 name: "functional_connectivity",
54 version: env!("CARGO_PKG_VERSION"),
55 inputs: &[Port {
56 name: "data",
57 kinds: &[
58 ArtifactKind::Series,
59 ArtifactKind::Decomposition,
60 ArtifactKind::Epochs,
61 ],
62 }],
63 outputs: &[Port {
64 name: "matrices",
65 kinds: &[ArtifactKind::FunctionalConnectivity],
66 }],
67 }
68 }
69
70 fn resolve(&self, params: Value) -> Result<Value, String> {
71 let (typed, resolved) = resolve_params::<Params>(params)?;
72 typed.measure()?;
73 Ok(resolved)
74 }
75
76 fn run(&self, params: &Value, inputs: &Inputs) -> Result<Outputs, String> {
77 let measure = serde_json::from_value::<Params>(params.clone())
78 .map_err(|e| e.to_string())?
79 .measure()?;
80 let source = match input(inputs, "data")? {
81 Artifact::Series(s) => Source::Series(s),
82 Artifact::Decomposition(d) => Source::Decomposition(d),
83 Artifact::Epochs(e) => Source::Epochs(e),
84 other => return Err(format!("input \"data\" cannot be a {}", other.kind())),
85 };
86 let matrices = functional_connectivity(source, measure).map_err(|e| e.to_string())?;
87 Ok(Outputs::from([(
88 "matrices".into(),
89 Artifact::FunctionalConnectivity(matrices),
90 )]))
91 }
92}