bixverse_methods/methods/graph_diffusions.rs
1//! Implementation of the tied diffusion method from Paull, et al.,
2//! Bioinformatics, 2013
3
4#![allow(dead_code)]
5
6use rayon::prelude::*;
7use std::sync::Arc;
8
9use crate::graph::page_rank::*;
10use crate::prelude::BixverseFloat;
11
12///////////
13// Enums //
14///////////
15
16/// Enum for the TiedSumType types
17#[derive(Clone, Debug, Default)]
18pub enum TiedSumType {
19 /// Minimum between the two diffusion vectors
20 #[default]
21 Min,
22 /// Maximum between the two diffusion vectors
23 Max,
24 /// Average between the two diffusion vectors
25 Avg,
26}
27
28/// Parsing the tied summarisation types
29///
30/// ### Params
31///
32/// * `s` - The string that defines the tied summarisation type
33///
34/// ### Results
35///
36/// The `TiedSumType` defining the tied summarisation type
37pub fn parse_tied_sum(s: &str) -> Option<TiedSumType> {
38 match s.to_lowercase().as_str() {
39 "max" => Some(TiedSumType::Max),
40 "min" => Some(TiedSumType::Min),
41 "mean" => Some(TiedSumType::Avg),
42 _ => None,
43 }
44}
45
46////////////////////
47// Main functions //
48////////////////////
49
50/// Calculates the tied diffusion between two graphs
51///
52/// Function is designed to handle permutations of the personalisation vectors
53/// and leverages parallel processing for efficiency.
54///
55/// ### Params
56///
57/// * `node_names` - The names of the nodes in the graph
58/// * `from` - The source nodes of the edges in the graph
59/// * `to` - The destination nodes of the edges in the graph
60/// * `weights` - The weights of the edges in the graph
61/// * `summarisation_fun` - The function used to summarise the personalisation
62/// vectors (i.e., the tied diffusion)
63/// * `personalise_vecs_1` - The personalisation vectors for the first graph
64/// * `personalise_vecs_2` - The personalisation vectors for the second graph
65/// * `undirected` - Whether the graph is undirected
66/// * `damping_factor` - PageRank damping factor, i.e. the probability of
67/// continuing the walk
68/// * `max_iter` - Maximum number of power iterations
69/// * `tol` - Convergence threshold on the L1 change between iterations
70///
71/// ### Returns
72///
73/// * `Vec<Vec<T>>` - The tied diffusion between the two graphs for all of the
74/// supplied personalisation vectors
75#[allow(clippy::too_many_arguments)]
76pub fn tied_diffusion_parallel<T>(
77 node_names: Vec<String>,
78 from: Vec<String>,
79 to: Vec<String>,
80 weights: Option<&[T]>,
81 summarisation_fun: String,
82 personalise_vecs_1: &[Vec<T>],
83 personalise_vecs_2: &[Vec<T>],
84 undirected: bool,
85 damping_factor: T,
86 max_iter: usize,
87 tol: T,
88) -> Vec<Vec<T>>
89where
90 T: BixverseFloat + std::iter::Sum,
91{
92 let summarisation_type: TiedSumType = parse_tied_sum(&summarisation_fun).unwrap();
93 let pagerank_graph_1 = Arc::new(PageRankGraph::from_strings(
94 &node_names,
95 &from,
96 &to,
97 weights,
98 undirected,
99 ));
100
101 // For tied diffusion in the directed case, the directionality is reversed
102 let pagerank_graph_2 = Arc::new(if !undirected {
103 PageRankGraph::from_strings(&node_names, &to, &from, weights, undirected)
104 } else {
105 PageRankGraph::from_strings(&node_names, &from, &to, weights, undirected)
106 });
107
108 let half = T::from_f64(0.5).unwrap();
109
110 let tied_res: Vec<Vec<T>> = personalise_vecs_1
111 .into_par_iter()
112 .zip(personalise_vecs_2.into_par_iter())
113 .map_init(
114 || (PageRankWorkingMemory::new(), PageRankWorkingMemory::new()),
115 |(working_mem1, working_mem2), (diff1, diff2)| {
116 let pr1 = personalised_page_rank_optimised(
117 &pagerank_graph_1,
118 damping_factor,
119 diff1,
120 max_iter,
121 tol,
122 working_mem1,
123 );
124 let pr2 = personalised_page_rank_optimised(
125 &pagerank_graph_2,
126 damping_factor,
127 diff2,
128 max_iter,
129 tol,
130 working_mem2,
131 );
132
133 pr1.iter()
134 .zip(pr2.iter())
135 .map(|(&v1, &v2)| match summarisation_type {
136 TiedSumType::Max => v1.max(v2),
137 TiedSumType::Min => v1.min(v2),
138 TiedSumType::Avg => (v1 + v2) * half, // multiplication is faster
139 })
140 .collect()
141 },
142 )
143 .collect();
144
145 tied_res
146}