1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/// Implementations of various graph algorithms that can be run on a graph.
///
/// To run an algorithm simply import the module and call the function with the graph as the argument
///
use crate::graph_view::PyGraphView;
use std::collections::HashMap;

use crate::utils;
use crate::utils::{extract_input_vertex, InputVertexBox};
use pyo3::prelude::*;
use raphtory::algorithms::connected_components;
use raphtory::algorithms::degree::{
    average_degree as average_degree_rs, max_in_degree as max_in_degree_rs,
    max_out_degree as max_out_degree_rs, min_in_degree as min_in_degree_rs,
    min_out_degree as min_out_degree_rs,
};
use raphtory::algorithms::directed_graph_density::directed_graph_density as directed_graph_density_rs;
use raphtory::algorithms::generic_taint::generic_taint as generic_taint_rs;
use raphtory::algorithms::local_clustering_coefficient::local_clustering_coefficient as local_clustering_coefficient_rs;
use raphtory::algorithms::local_triangle_count::local_triangle_count as local_triangle_count_rs;
use raphtory::algorithms::motifs::three_node_local::global_temporal_three_node_motif as global_temporal_three_node_motif_rs;
use raphtory::algorithms::motifs::three_node_local::global_temporal_three_node_motif_from_local as global_temporal_three_node_motif_from_local_rs;
use raphtory::algorithms::motifs::three_node_local::temporal_three_node_motif as temporal_three_node_motif_rs;
use raphtory::algorithms::pagerank::unweighted_page_rank;
use raphtory::algorithms::reciprocity::{
    all_local_reciprocity as all_local_reciprocity_rs, global_reciprocity as global_reciprocity_rs,
};

/// Local triangle count - calculates the number of triangles (a cycle of length 3) for a node.
/// It measures the local clustering of a graph.
///
/// This is useful for understanding the level of connectivity and the likelihood of information
/// or influence spreading through a network.
///
/// For example, in a social network, the local triangle count of a user's profile can reveal the
/// number of mutual friends they have and the level of interconnectivity between those friends.
/// A high local triangle count for a user indicates that they are part of a tightly-knit group
/// of people, which can be useful for targeted advertising or identifying key influencers
/// within a network.
///
/// Local triangle count can also be used in other domains such as biology, where it can be used
/// to analyze protein interaction networks, or in transportation networks, where it can be used
/// to identify critical junctions or potential traffic bottlenecks.
///
#[pyfunction]
pub fn local_triangle_count(g: &PyGraphView, v: &PyAny) -> PyResult<Option<usize>> {
    let v = utils::extract_vertex_ref(v)?;
    Ok(local_triangle_count_rs(&g.graph, v))
}

#[pyfunction]
pub fn weakly_connected_components(
    g: &PyGraphView,
    iter_count: usize,
) -> PyResult<HashMap<String, u64>> {
    Ok(connected_components::weakly_connected_components(
        &g.graph, iter_count, None,
    ))
}

#[pyfunction]
pub fn pagerank(
    g: &PyGraphView,
    iter_count: usize,
    max_diff: Option<f64>,
) -> PyResult<HashMap<String, f64>> {
    Ok(unweighted_page_rank(&g.graph, iter_count, None, max_diff))
}

#[pyfunction]
pub fn generic_taint(
    g: &PyGraphView,
    iter_count: usize,
    start_time: i64,
    infected_nodes: Vec<&PyAny>,
    stop_nodes: Vec<&PyAny>,
) -> Result<HashMap<String, Vec<(i64, String)>>, PyErr> {
    let infected_nodes: PyResult<Vec<InputVertexBox>> = infected_nodes
        .into_iter()
        .map(|v| extract_input_vertex(v))
        .collect();
    let stop_nodes: PyResult<Vec<InputVertexBox>> = stop_nodes
        .into_iter()
        .map(|v| extract_input_vertex(v))
        .collect();

    Ok(generic_taint_rs(
        &g.graph,
        None,
        iter_count,
        start_time,
        infected_nodes?,
        stop_nodes?,
    ))
}

/// Local Clustering coefficient - measures the degree to which nodes in a graph tend to cluster together.
///
/// It is calculated by dividing the number of triangles (sets of three nodes that are all
/// connected to each other) in the graph by the total number of possible triangles.
/// The resulting value is a number between 0 and 1 that represents the density of
/// clustering in the graph.
///
/// A high clustering coefficient indicates that nodes tend to be
/// connected to nodes that are themselves connected to each other, while a low clustering
/// coefficient indicates that nodes tend to be connected to nodes that are not connected
/// to each other.
///
/// In a social network of a particular community, we can compute the clustering
/// coefficient of each node to get an idea of how strongly connected and cohesive
/// that node's neighborhood is.
///
/// A high clustering coefficient for a node in a social network indicates that the
/// node's neighbors tend to be strongly connected with each other, forming a tightly-knit
/// group or community. In contrast, a low clustering coefficient for a node indicates that
/// its neighbors are relatively less connected with each other, suggesting a more fragmented
/// or diverse community.
#[pyfunction]
pub fn local_clustering_coefficient(g: &PyGraphView, v: &PyAny) -> PyResult<Option<f32>> {
    let v = utils::extract_vertex_ref(v)?;
    Ok(local_clustering_coefficient_rs(&g.graph, v))
}

/// Graph density - measures how dense or sparse a graph is.
///
/// It is defined as the ratio of the number of edges in the graph to the total number of possible
/// edges. A dense graph has a high edge-to-vertex ratio, while a sparse graph has a low
/// edge-to-vertex ratio.
///
/// For example in social network analysis, a dense graph may indicate a highly interconnected
/// community, while a sparse graph may indicate more isolated individuals.
#[pyfunction]
pub fn directed_graph_density(g: &PyGraphView) -> f32 {
    directed_graph_density_rs(&g.graph)
}

/// The average degree of all vertices in the graph.
#[pyfunction]
pub fn average_degree(g: &PyGraphView) -> f64 {
    average_degree_rs(&g.graph)
}

/// The maximum out degree of any vertex in the graph.
#[pyfunction]
pub fn max_out_degree(g: &PyGraphView) -> usize {
    max_out_degree_rs(&g.graph)
}

/// The maximum in degree of any vertex in the graph.
#[pyfunction]
pub fn max_in_degree(g: &PyGraphView) -> usize {
    max_in_degree_rs(&g.graph)
}

/// The minimum out degree of any vertex in the graph.
#[pyfunction]
pub fn min_out_degree(g: &PyGraphView) -> usize {
    min_out_degree_rs(&g.graph)
}

/// The minimum in degree of any vertex in the graph.
#[pyfunction]
pub fn min_in_degree(g: &PyGraphView) -> usize {
    min_in_degree_rs(&g.graph)
}

/// Reciprocity - measure of the symmetry of relationships in a graph, the global reciprocity of
/// the entire graph.
/// This calculates the number of reciprocal connections (edges that go in both directions) in a
/// graph and normalizes it by the total number of edges.
///
/// In a social network context, reciprocity measures the likelihood that if person A is linked
/// to person B, then person B is linked to person A. This algorithm can be used to determine the
/// level of symmetry or balance in a social network. It can also reveal the power dynamics in a
/// group or community. For example, if one person has many connections that are not reciprocated,
/// it could indicate that this person has more power or influence in the network than others.
///
/// In a business context, reciprocity can be used to study customer behavior. For instance, in a
/// transactional network, if a customer tends to make a purchase from a seller and then the seller
/// makes a purchase from the same customer, it can indicate a strong reciprocal relationship
/// between them. On the other hand, if the seller does not make a purchase from the same customer,
/// it could imply a less reciprocal or more one-sided relationship.
#[pyfunction]
pub fn global_reciprocity(g: &PyGraphView) -> f64 {
    global_reciprocity_rs(&g.graph, None)
}

/// Reciprocity - measure of the symmetry of relationships in a graph.
/// the reciprocity of every vertex in the graph as a tuple of vector id and the reciprocity
/// This calculates the number of reciprocal connections (edges that go in both directions) in a
/// graph and normalizes it by the total number of edges.
///
/// In a social network context, reciprocity measures the likelihood that if person A is linked
/// to person B, then person B is linked to person A. This algorithm can be used to determine the
/// level of symmetry or balance in a social network. It can also reveal the power dynamics in a
/// group or community. For example, if one person has many connections that are not reciprocated,
/// it could indicate that this person has more power or influence in the network than others.
///
/// In a business context, reciprocity can be used to study customer behavior. For instance, in a
/// transactional network, if a customer tends to make a purchase from a seller and then the seller
/// makes a purchase from the same customer, it can indicate a strong reciprocal relationship
/// between them. On the other hand, if the seller does not make a purchase from the same customer,
/// it could imply a less reciprocal or more one-sided relationship.
///
#[pyfunction]
pub fn all_local_reciprocity(g: &PyGraphView) -> HashMap<String, f64> {
    all_local_reciprocity_rs(&g.graph, None)
}

/// Computes the number of both open and closed triplets within a graph
///
/// An open triplet, is one where a node has two neighbors, but no edge between them.
/// A closed triplet is one where a node has two neighbors, and an edge between them.
#[pyfunction]
pub fn triplet_count(g: &PyGraphView) -> usize {
    raphtory::algorithms::triplet_count::triplet_count(&g.graph, None)
}

/// Computes the global clustering coefficient of a graph. The global clustering coefficient is
/// defined as the number of triangles in the graph divided by the number of triplets in the graph.
#[pyfunction]
pub fn global_clustering_coefficient(g: &PyGraphView) -> f64 {
    raphtory::algorithms::clustering_coefficient::clustering_coefficient(&g.graph)
}

#[pyfunction]
pub fn temporal_three_node_motif(g: &PyGraphView, delta: i64) -> HashMap<String, Vec<usize>> {
    temporal_three_node_motif_rs(&g.graph, None, delta)
}

#[pyfunction]
pub fn global_temporal_three_node_motif(g: &PyGraphView, delta: i64) -> Vec<usize> {
    global_temporal_three_node_motif_rs(&g.graph, None, delta)
}

#[pyfunction]
pub fn global_temporal_three_node_motif_from_local(
    counts: HashMap<String, Vec<usize>>,
) -> Vec<usize> {
    global_temporal_three_node_motif_from_local_rs(counts)
}