Skip to content

Commit

Permalink
Merge branch 'master' into fix/node_type2
Browse files Browse the repository at this point in the history
  • Loading branch information
miratepuffin authored May 28, 2024
2 parents cac690c + eae4622 commit 9afa2a9
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ flate2 = "1.0.28"
regex = "1.10.3"
genawaiter = "0.99.1"
num-traits = "0.2.18"
num-integer = "0.1"
rand_distr = "0.4.3"
rustc-hash = "1.1.0"
twox-hash = "1.6.3"
Expand Down
1 change: 1 addition & 0 deletions python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fn raphtory(py: Python<'_>, m: &PyModule) -> PyResult<()> {
single_source_shortest_path,
global_clustering_coefficient,
temporally_reachable_nodes,
temporal_bipartite_graph_projection,
local_clustering_coefficient,
weakly_connected_components,
strongly_connected_components,
Expand Down
1 change: 1 addition & 0 deletions raphtory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ bincode = { workspace = true }
chrono = { workspace = true }
itertools = { workspace = true }
num-traits = { workspace = true }
num-integer = { workspace = true }
parking_lot = { workspace = true }
once_cell = { workspace = true }
rand = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions raphtory/src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ pub mod layout;
pub mod metrics;
pub mod motifs;
pub mod pathing;
pub mod projections;
1 change: 1 addition & 0 deletions raphtory/src/algorithms/projections/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod temporal_bipartite_projection;
142 changes: 142 additions & 0 deletions raphtory/src/algorithms/projections/temporal_bipartite_projection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::any::Any;

use itertools::Itertools;
use num_integer::average_floor;
// use num::integer::average_floor;
extern crate num_integer;

use crate::{
core::entities::nodes::node_ref::{AsNodeRef, NodeRef},
db::{
api::{
mutation::AdditionOps,
view::{internal, *},
},
graph::graph::Graph,
},
prelude::*,
};

#[derive(Clone)]
struct Visitor {
name: String,
time: i64,
}

pub fn temporal_bipartite_projection<G: StaticGraphViewOps>(
graph: &G,
delta: i64,
pivot_type: String,
) -> Graph {
let new_graph = Graph::new();
let nodes = graph
.nodes()
.iter()
.filter(|v| v.node_type().unwrap() == pivot_type);
for v in nodes {
populate_edges(graph, &new_graph, v, delta)
}
new_graph
}

fn populate_edges<G: StaticGraphViewOps, V: AsNodeRef>(g: &G, new_graph: &Graph, v: V, delta: i64) {
if let Some(vertex) = g.node(v) {
// get vector of vertices which need connecting up
let mut visitors = vertex
.edges()
.explode()
.iter()
.map(|e| Visitor {
name: e.nbr().name(),
time: e.time().unwrap(),
})
.collect_vec();
visitors.sort_by_key(|vis| vis.time);

let mut start = 0;
let mut to_process: Vec<Visitor> = vec![];
for nb in visitors.iter() {
while visitors[start].time + delta < nb.time {
to_process.remove(0);
start += 1
}
for node in &to_process {
let new_time = average_floor(nb.time, node.time);
new_graph
.add_edge(new_time, node.name.clone(), nb.name.clone(), NO_PROPS, None)
.unwrap();
}
to_process.push(nb.clone());
}
} else {
return;
}
}

#[cfg(test)]
mod bipartite_graph_tests {
use itertools::Itertools;

use super::temporal_bipartite_projection;
use crate::{
db::{
api::{mutation::AdditionOps, view::*},
graph::graph::Graph,
},
prelude::{Prop, NO_PROPS},
};

#[test]
fn small_delta_test() {
let g = Graph::new();
let vs = vec![
(1, "A", "1"),
(3, "A", "2"),
(3, "B", "2"),
(4, "C", "3"),
(6, "B", "3"),
(8, "A", "3"),
(10, "C", "4"),
(11, "B", "4"),
];
for (t, src, dst) in &vs {
g.add_node(*t, *src, NO_PROPS, Some("Left")).unwrap();
g.add_node(*t, *dst, NO_PROPS, Some("Right")).unwrap();
g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap();
}
let new_graph = temporal_bipartite_projection(&g, 1, "Right".to_string());
assert!(new_graph.has_edge("A", "B"));
assert_eq!(new_graph.edge("A", "B").unwrap().latest_time(), Some(3));
assert!(new_graph.has_edge("C", "B"));
assert_eq!(new_graph.edge("C", "B").unwrap().latest_time(), Some(10));
assert!(!new_graph.has_edge("A", "C"));
}

#[test]
fn larger_delta_test() {
let g = Graph::new();
let vs = vec![
(1, "A", "1"),
(3, "A", "2"),
(3, "B", "2"),
(4, "C", "3"),
(6, "B", "3"),
(8, "A", "3"),
(10, "C", "4"),
(11, "B", "4"),
];
for (t, src, dst) in &vs {
g.add_node(*t, *src, NO_PROPS, Some("Left")).unwrap();
g.add_node(*t, *dst, NO_PROPS, Some("Right")).unwrap();
g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap();
}
let new_graph = temporal_bipartite_projection(&g, 3, "Right".to_string());
assert!(new_graph.has_edge("A", "B"));
assert_eq!(new_graph.edge("A", "B").unwrap().earliest_time(), Some(3));
assert_eq!(new_graph.edge("B", "A").unwrap().latest_time(), Some(7));
assert!(new_graph.has_edge("C", "B"));
assert_eq!(new_graph.edge("C", "B").unwrap().earliest_time(), Some(5));
assert_eq!(new_graph.edge("C", "B").unwrap().latest_time(), Some(10));
assert!(!new_graph.has_edge("A", "C"));
}
}
21 changes: 21 additions & 0 deletions raphtory/src/python/packages/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use crate::{
single_source_shortest_path::single_source_shortest_path as single_source_shortest_path_rs,
temporal_reachability::temporally_reachable_nodes as temporal_reachability_rs,
},
projections::temporal_bipartite_projection::temporal_bipartite_projection as temporal_bipartite_rs,
},
core::{entities::nodes::node_ref::NodeRef, Prop},
db::{api::view::internal::DynamicGraph, graph::node::NodeView},
Expand Down Expand Up @@ -422,6 +423,26 @@ pub fn global_temporal_three_node_motif(g: &PyGraphView, delta: i64) -> [usize;
global_temporal_three_node_motif_rs(&g.graph, delta, None)
}

/// Projects a temporal bipartite graph into an undirected temporal graph over the pivot node type. Let G be a bipartite graph with node types A and B. Given delta > 0, the projection graph G' pivoting over type B nodes,
/// will make a connection between nodes n1 and n2 (of type A) at time (t1 + t2)/2 if they respectively have an edge at time t1, t2 with the same node of type B in G, and |t2-t1| < delta.
///
/// Arguments:
/// g (raphtory graph) : A directed raphtory graph
/// delta (int): Time period
/// pivot (string) : node type to pivot over. If a bipartite graph has types A and B, and B is the pivot type, the new graph will consist of type A nodes.
///
/// Returns:
/// raphtory graph : Projected (unipartite) temporal graph.
#[pyfunction]
#[pyo3(signature = (g, delta, pivot_type))]
pub fn temporal_bipartite_graph_projection(
g: &PyGraphView,
delta: i64,
pivot_type: String,
) -> PyGraphView {
temporal_bipartite_rs(&g.graph, delta, pivot_type).into()
}

/// Computes the global counts of three-edge up-to-three node temporal motifs for a range of timescales. See `global_temporal_three_node_motif` for an interpretation of each row returned.
///
/// Arguments:
Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pandas
matplotlib
tqdm
raphtory
requests==2.31.0
requests==2.32.0
pyvis
seaborn
scipy
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ regex==2023.12.25
# via
# nltk
# transformers
requests==2.31.0
requests==2.32.0
# via
# -r requirements.in
# huggingface-hub
Expand Down

0 comments on commit 9afa2a9

Please sign in to comment.