From 08fadebede434c84c6d003f79ee0a4ad3a87633f Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Fri, 8 Nov 2024 12:30:01 +0100 Subject: [PATCH 01/23] can just use fn for ops --- raphtory/src/db/api/state/lazy_node_state.rs | 5 ++--- raphtory/src/db/api/view/node.rs | 7 ++----- raphtory/src/db/graph/node.rs | 5 +---- raphtory/src/db/graph/nodes.rs | 11 ++++++----- raphtory/src/db/graph/path.rs | 10 ++-------- raphtory/src/db/task/node/eval_node.rs | 14 ++++---------- 6 files changed, 17 insertions(+), 35 deletions(-) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index ebaced8da..e3fc27caf 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -15,7 +15,7 @@ use std::{marker::PhantomData, sync::Arc}; #[derive(Clone)] pub struct LazyNodeState<'graph, V, G, GH = G> { - op: Arc V + Send + Sync + 'graph>, + op: fn(&GraphStorage, &GH, VID) -> V, base_graph: G, graph: GH, node_types_filter: Option>, @@ -33,9 +33,8 @@ impl< base_graph: G, graph: GH, node_types_filter: Option>, - op: impl Fn(&GraphStorage, &GH, VID) -> V + Send + Sync + 'graph, + op: fn(&GraphStorage, &GH, VID) -> V, ) -> Self { - let op = Arc::new(op); Self { op, base_graph, diff --git a/raphtory/src/db/api/view/node.rs b/raphtory/src/db/api/view/node.rs index aba41b55f..04a60f2a0 100644 --- a/raphtory/src/db/api/view/node.rs +++ b/raphtory/src/db/api/view/node.rs @@ -30,12 +30,9 @@ pub trait BaseNodeViewOps<'graph>: Clone + TimeOps<'graph> + LayerOps<'graph> { + 'graph; type Edges: EdgeViewOps<'graph, Graph = Self::Graph, BaseGraph = Self::BaseGraph> + 'graph; - fn map< - O: Clone + Send + Sync + 'graph, - F: Fn(&GraphStorage, &Self::Graph, VID) -> O + Send + Sync + Clone + 'graph, - >( + fn map( &self, - op: F, + op: fn(&GraphStorage, &Self::Graph, VID) -> O, ) -> Self::ValueType; fn as_props(&self) -> Self::ValueType>; diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index 933d1fa25..4083ef08f 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -315,10 +315,7 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> BaseNodeViewOps< type PathType = PathFromNode<'graph, G, G>; type Edges = Edges<'graph, G, GH>; - fn map O>( - &self, - op: F, - ) -> Self::ValueType { + fn map(&self, op: fn(&GraphStorage, &Self::Graph, VID) -> O) -> Self::ValueType { let cg = self.graph.core_graph(); op(cg, &self.graph, self.node) } diff --git a/raphtory/src/db/graph/nodes.rs b/raphtory/src/db/graph/nodes.rs index 7a415c26f..3adcf2db5 100644 --- a/raphtory/src/db/graph/nodes.rs +++ b/raphtory/src/db/graph/nodes.rs @@ -169,6 +169,10 @@ where pub fn get_temporal_prop_id(&self, prop_name: &str) -> Option { self.graph.node_meta().get_prop_id(prop_name, false) } + + pub fn is_filtered(&self) -> bool { + self.node_types_filter.is_some() || self.graph.nodes_filtered() + } } impl<'graph, G, GH> BaseNodeViewOps<'graph> for Nodes<'graph, G, GH> @@ -183,12 +187,9 @@ where type PathType = PathFromGraph<'graph, G, G>; type Edges = NestedEdges<'graph, G, GH>; - fn map< - O: Clone + Send + Sync + 'graph, - F: Fn(&GraphStorage, &Self::Graph, VID) -> O + Send + Sync + 'graph, - >( + fn map( &self, - op: F, + op: fn(&GraphStorage, &Self::Graph, VID) -> O, ) -> Self::ValueType { let g = self.graph.clone(); let bg = self.base_graph.clone(); diff --git a/raphtory/src/db/graph/path.rs b/raphtory/src/db/graph/path.rs index bdb7e0d1b..3853846e5 100644 --- a/raphtory/src/db/graph/path.rs +++ b/raphtory/src/db/graph/path.rs @@ -152,10 +152,7 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> BaseNodeViewOps< type PathType = PathFromGraph<'graph, G, G>; type Edges = NestedEdges<'graph, G, GH>; - fn map O + Send + Clone + 'graph>( - &self, - op: F, - ) -> Self::ValueType { + fn map(&self, op: fn(&GraphStorage, &Self::Graph, VID) -> O) -> Self::ValueType { let graph = self.graph.clone(); self.iter_refs() .map(move |it| { @@ -401,10 +398,7 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> BaseNodeViewOps< type PathType = PathFromNode<'graph, G, G>; type Edges = Edges<'graph, G, GH>; - fn map O + Send + 'graph>( - &self, - op: F, - ) -> Self::ValueType { + fn map(&self, op: fn(&GraphStorage, &Self::Graph, VID) -> O) -> Self::ValueType { let graph = self.graph.clone(); Box::new( self.iter_refs() diff --git a/raphtory/src/db/task/node/eval_node.rs b/raphtory/src/db/task/node/eval_node.rs index a76cee069..bfb41183b 100644 --- a/raphtory/src/db/task/node/eval_node.rs +++ b/raphtory/src/db/task/node/eval_node.rs @@ -352,12 +352,9 @@ impl< type PathType = EvalPathFromNode<'graph, 'a, G, &'graph G, CS, S>; type Edges = EvalEdges<'graph, 'a, G, GH, CS, S>; - fn map< - O: Clone + Send + Sync + 'graph, - F: Fn(&GraphStorage, &Self::Graph, VID) -> O + Send + Sync + Clone + 'graph, - >( + fn map( &self, - op: F, + op: fn(&GraphStorage, &Self::Graph, VID) -> O, ) -> Self::ValueType { let graph = self.graph.clone(); let storage = self.base_graph.storage; @@ -503,12 +500,9 @@ impl< type PathType = EvalPathFromNode<'graph, 'a, G, &'graph G, CS, S>; type Edges = EvalEdges<'graph, 'a, G, GH, CS, S>; - fn map< - O: Clone + Send + Sync + 'graph, - F: Fn(&GraphStorage, &Self::Graph, VID) -> O + Send + Sync + Clone + 'graph, - >( + fn map( &self, - op: F, + op: fn(&GraphStorage, &Self::Graph, VID) -> O, ) -> Self::ValueType { op(self.eval_graph.storage, &self.graph, self.node) } From 43fe85e463fe9eaeee1838d69b5d5d87035470e2 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Fri, 8 Nov 2024 15:08:56 +0100 Subject: [PATCH 02/23] prototype implementation --- raphtory/src/db/api/state/lazy_node_state.rs | 143 ++++++++++++++++++- raphtory/src/db/api/state/mod.rs | 2 +- raphtory/src/db/graph/nodes.rs | 13 +- 3 files changed, 152 insertions(+), 6 deletions(-) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index e3fc27caf..d4b6e66d2 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -4,14 +4,105 @@ use crate::{ api::{ state::{NodeState, NodeStateOps}, storage::graph::{nodes::node_storage_ops::NodeStorageOps, storage_ops::GraphStorage}, - view::{internal::NodeList, IntoDynBoxed}, + view::{ + internal::{NodeList, OneHopFilter}, + IntoDynBoxed, + }, }, - graph::node::NodeView, + graph::{node::NodeView, nodes::Nodes}, }, - prelude::GraphViewOps, + prelude::{GraphViewOps, TimeOps}, }; +use raphtory_api::core::Direction; use rayon::prelude::*; -use std::{marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, ops::Deref, sync::Arc}; + +pub trait NodeOp: Send + Sync { + type Output: Send + Sync; + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output; +} + +pub struct Degree { + graph: G, +} + +impl<'graph, G: GraphViewOps<'graph>> NodeOp for Degree { + type Output = usize; + + fn apply(&self, storage: &GraphStorage, node: VID) -> usize { + storage.node_degree(node, Direction::BOTH, &self.graph) + } +} + +impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for Degree { + type BaseGraph = G; + type FilteredGraph = G; + type Filtered + 'graph> = Degree; + + fn current_filter(&self) -> &Self::FilteredGraph { + &self.graph + } + + fn base_graph(&self) -> &Self::BaseGraph { + &self.graph + } + + fn one_hop_filtered + 'graph>( + &self, + filtered_graph: GH, + ) -> Self::Filtered { + Degree { + graph: filtered_graph, + } + } +} + +impl NodeOp for Arc> { + type Output = V; + fn apply(&self, storage: &GraphStorage, node: VID) -> V { + self.deref().apply(storage, node) + } +} + +pub struct LazyNodeState2<'graph, Op, G, GH = G> { + nodes: Nodes<'graph, G, GH>, + op: Op, +} + +impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> + OneHopFilter<'graph> for LazyNodeState2<'graph, Op, G, GH> +{ + type BaseGraph = G; + type FilteredGraph = Op::FilteredGraph; + type Filtered + 'graph> = + LazyNodeState2<'graph, Op::Filtered, G, GH>; + + fn current_filter(&self) -> &Self::FilteredGraph { + self.op.current_filter() + } + + fn base_graph(&self) -> &Self::BaseGraph { + self.nodes.base_graph() + } + + fn one_hop_filtered + 'graph>( + &self, + filtered_graph: GHH, + ) -> Self::Filtered { + LazyNodeState2 { + nodes: self.nodes.clone(), + op: self.op.one_hop_filtered(filtered_graph), + } + } +} + +impl<'graph, Op: NodeOp, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> + LazyNodeState2<'graph, Op, G, GH> +{ + pub fn par_iter(&self) -> impl ParallelIterator + '_ { + self.nodes.apply(&self.op) + } +} #[derive(Clone)] pub struct LazyNodeState<'graph, V, G, GH = G> { @@ -252,3 +343,47 @@ impl< self.graph.count_nodes() } } + +#[cfg(test)] +mod test { + use crate::{ + db::api::{ + state::lazy_node_state::{Degree, LazyNodeState2, NodeOp}, + view::{internal::CoreGraphOps, IntoDynamic}, + }, + prelude::*, + }; + use raphtory_api::core::entities::VID; + use rayon::prelude::*; + use std::sync::Arc; + + struct TestWrapper(Op); + #[test] + fn test_compile() { + let g = Graph::new(); + g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); + + let nodes = g.nodes(); + + let node_state = LazyNodeState2 { + nodes, + op: Degree { graph: g.clone() }, + }; + let node_state_window = node_state.after(1); + + let deg: Vec<_> = node_state.par_iter().collect(); + let deg_w: Vec<_> = node_state_window.par_iter().collect(); + + let node_state_filter = node_state.valid_layers("bla"); + + assert_eq!(deg, [1, 1]); + assert_eq!(deg_w, [0, 0]); + let g_dyn = g.clone().into_dynamic(); + + let deg = Degree { graph: g_dyn }; + let arc_deg: Arc> = Arc::new(deg); + assert_eq!(arc_deg.apply(g.core_graph(), VID(0)), 1); + + let test_struct = TestWrapper(arc_deg); + } +} diff --git a/raphtory/src/db/api/state/mod.rs b/raphtory/src/db/api/state/mod.rs index 886b4a488..d56ce46ad 100644 --- a/raphtory/src/db/api/state/mod.rs +++ b/raphtory/src/db/api/state/mod.rs @@ -4,7 +4,7 @@ mod node_state; mod ops; mod ord_ops; -pub use lazy_node_state::LazyNodeState; +pub use lazy_node_state::{LazyNodeState, NodeOp}; pub(crate) use node_state::Index; pub use node_state::NodeState; pub use ops::NodeStateOps; diff --git a/raphtory/src/db/graph/nodes.rs b/raphtory/src/db/graph/nodes.rs index 3adcf2db5..799f6a3b1 100644 --- a/raphtory/src/db/graph/nodes.rs +++ b/raphtory/src/db/graph/nodes.rs @@ -15,7 +15,7 @@ use crate::{ prelude::*, }; -use crate::db::graph::create_node_type_filter; +use crate::db::{api::state::NodeOp, graph::create_node_type_filter}; use rayon::iter::ParallelIterator; use std::{marker::PhantomData, sync::Arc}; @@ -90,6 +90,17 @@ where } } + pub(crate) fn apply<'a, V: Send>( + &'a self, + op: &'a impl NodeOp, + ) -> impl ParallelIterator + 'a { + let cg = self.graph.core_graph().lock(); + let cg2 = cg.clone(); + let node_types_filter = self.node_types_filter.clone(); + cg.into_nodes_par(&self.graph, node_types_filter) + .map(move |v| op.apply(&cg2, v)) + } + #[inline] pub(crate) fn iter_refs(&self) -> impl Iterator + 'graph { let g = self.graph.core_graph().lock(); From ecb143fe5badb748baaaecc29f07be628e6f75c7 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 11 Nov 2024 09:27:13 +0100 Subject: [PATCH 03/23] flesh out implementation of LazyNodeState2 --- raphtory/src/db/api/state/lazy_node_state.rs | 174 +++++++++++++++++-- raphtory/src/db/graph/nodes.rs | 11 +- 2 files changed, 162 insertions(+), 23 deletions(-) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index d4b6e66d2..86290cdc9 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -5,8 +5,8 @@ use crate::{ state::{NodeState, NodeStateOps}, storage::graph::{nodes::node_storage_ops::NodeStorageOps, storage_ops::GraphStorage}, view::{ - internal::{NodeList, OneHopFilter}, - IntoDynBoxed, + internal::{CoreGraphOps, NodeList, OneHopFilter}, + BoxedLIter, IntoDynBoxed, }, }, graph::{node::NodeView, nodes::Nodes}, @@ -18,19 +18,20 @@ use rayon::prelude::*; use std::{marker::PhantomData, ops::Deref, sync::Arc}; pub trait NodeOp: Send + Sync { - type Output: Send + Sync; + type Output: Clone + Send + Sync; fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output; } pub struct Degree { - graph: G, + pub(crate) graph: G, + pub(crate) dir: Direction, } impl<'graph, G: GraphViewOps<'graph>> NodeOp for Degree { type Output = usize; fn apply(&self, storage: &GraphStorage, node: VID) -> usize { - storage.node_degree(node, Direction::BOTH, &self.graph) + storage.node_degree(node, self.dir, &self.graph) } } @@ -53,11 +54,12 @@ impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for Degree { ) -> Self::Filtered { Degree { graph: filtered_graph, + dir: self.dir, } } } -impl NodeOp for Arc> { +impl NodeOp for Arc> { type Output = V; fn apply(&self, storage: &GraphStorage, node: VID) -> V { self.deref().apply(storage, node) @@ -96,11 +98,137 @@ impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps } } -impl<'graph, Op: NodeOp, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> - LazyNodeState2<'graph, Op, G, GH> +impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> IntoIterator + for LazyNodeState2<'graph, Op, G, GH> { - pub fn par_iter(&self) -> impl ParallelIterator + '_ { - self.nodes.apply(&self.op) + type Item = Op::Output; + type IntoIter = BoxedLIter<'graph, Self::Item>; + + fn into_iter(self) -> Self::IntoIter { + self.into_values().into_dyn_boxed() + } +} + +impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> + NodeStateOps<'graph> for LazyNodeState2<'graph, Op, G, GH> +{ + type Graph = GH; + type BaseGraph = G; + type Value<'a> + = Op::Output + where + 'graph: 'a, + Self: 'a; + type OwnedValue = Op::Output; + + fn graph(&self) -> &Self::Graph { + &self.nodes.graph + } + + fn base_graph(&self) -> &Self::BaseGraph { + &self.nodes.base_graph + } + + fn values<'a>(&'a self) -> impl Iterator> + 'a + where + 'graph: 'a, + { + let storage = self.graph().core_graph().lock(); + self.nodes + .iter_refs() + .map(move |vid| self.op.apply(&storage, vid)) + } + + fn par_values<'a>(&'a self) -> impl ParallelIterator> + 'a + where + 'graph: 'a, + { + let storage = self.graph().core_graph().lock(); + self.nodes + .par_iter_refs() + .map(move |vid| self.op.apply(&storage, vid)) + } + + fn into_values(self) -> impl Iterator + 'graph { + let storage = self.graph().core_graph().lock(); + self.nodes + .iter_refs() + .map(move |vid| self.op.apply(&storage, vid)) + } + + fn into_par_values(self) -> impl ParallelIterator + 'graph { + let storage = self.graph().core_graph().lock(); + self.nodes + .par_iter_refs() + .map(move |vid| self.op.apply(&storage, vid)) + } + + fn iter<'a>( + &'a self, + ) -> impl Iterator< + Item = ( + NodeView<&'a Self::BaseGraph, &'a Self::Graph>, + Self::Value<'a>, + ), + > + 'a + where + 'graph: 'a, + { + let storage = self.graph().core_graph().lock(); + self.nodes + .iter() + .map(move |node| (node, self.op.apply(&storage, node.node))) + } + + fn par_iter<'a>( + &'a self, + ) -> impl ParallelIterator< + Item = ( + NodeView<&'a Self::BaseGraph, &'a Self::Graph>, + Self::Value<'a>, + ), + > + where + 'graph: 'a, + { + let storage = self.graph().core_graph().lock(); + self.nodes + .par_iter() + .map(move |node| (node, self.op.apply(&storage, node.node))) + } + + fn get_by_index( + &self, + index: usize, + ) -> Option<(NodeView<&Self::BaseGraph, &Self::Graph>, Self::Value<'_>)> { + if self.graph().nodes_filtered() { + self.iter().nth(index) + } else { + let vid = match self.graph().node_list() { + NodeList::All { num_nodes } => { + if index < num_nodes { + VID(index) + } else { + return None; + } + } + NodeList::List { nodes } => nodes.key(index)?, + }; + let cg = self.graph().core_graph(); + Some(( + NodeView::new_one_hop_filtered(self.base_graph(), self.graph(), vid), + self.op.apply(cg, vid), + )) + } + } + + fn get_by_node(&self, node: N) -> Option> { + let node = (&self.graph()).node(node); + node.map(|node| self.op.apply(self.graph().core_graph(), node.node)) + } + + fn len(&self) -> usize { + self.nodes.len() } } @@ -353,7 +481,8 @@ mod test { }, prelude::*, }; - use raphtory_api::core::entities::VID; + use itertools::Itertools; + use raphtory_api::core::{entities::VID, Direction}; use rayon::prelude::*; use std::sync::Arc; @@ -367,21 +496,36 @@ mod test { let node_state = LazyNodeState2 { nodes, - op: Degree { graph: g.clone() }, + op: Degree { + graph: g.clone(), + dir: Direction::BOTH, + }, }; let node_state_window = node_state.after(1); - let deg: Vec<_> = node_state.par_iter().collect(); - let deg_w: Vec<_> = node_state_window.par_iter().collect(); + let deg: Vec<_> = node_state.values().collect(); + let deg_w: Vec<_> = node_state_window.values().collect(); let node_state_filter = node_state.valid_layers("bla"); assert_eq!(deg, [1, 1]); assert_eq!(deg_w, [0, 0]); + let g_dyn = g.clone().into_dynamic(); - let deg = Degree { graph: g_dyn }; + let deg = Degree { + graph: g_dyn, + dir: Direction::BOTH, + }; let arc_deg: Arc> = Arc::new(deg); + + let node_state_dyn = LazyNodeState2 { + nodes: g.nodes(), + op: arc_deg.clone(), + }; + + let dyn_deg: Vec<_> = node_state_dyn.values().collect(); + assert_eq!(dyn_deg, [1, 1]); assert_eq!(arc_deg.apply(g.core_graph(), VID(0)), 1); let test_struct = TestWrapper(arc_deg); diff --git a/raphtory/src/db/graph/nodes.rs b/raphtory/src/db/graph/nodes.rs index 799f6a3b1..eef5e5ae6 100644 --- a/raphtory/src/db/graph/nodes.rs +++ b/raphtory/src/db/graph/nodes.rs @@ -90,15 +90,10 @@ where } } - pub(crate) fn apply<'a, V: Send>( - &'a self, - op: &'a impl NodeOp, - ) -> impl ParallelIterator + 'a { - let cg = self.graph.core_graph().lock(); - let cg2 = cg.clone(); + pub(crate) fn par_iter_refs(&self) -> impl ParallelIterator + 'graph { + let g = self.graph.core_graph().lock(); let node_types_filter = self.node_types_filter.clone(); - cg.into_nodes_par(&self.graph, node_types_filter) - .map(move |v| op.apply(&cg2, v)) + g.into_nodes_par(self.graph.clone(), node_types_filter) } #[inline] From 18fc730f8de48f99f63f7d4635669f218da6b98f Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 11 Nov 2024 09:44:17 +0100 Subject: [PATCH 04/23] less confusing module names --- raphtory/src/db/api/state/lazy_node_state.rs | 4 +--- raphtory/src/db/api/state/mod.rs | 8 ++++---- raphtory/src/db/api/state/node_state.rs | 2 +- raphtory/src/db/api/state/{ops.rs => node_state_ops.rs} | 4 ++-- .../db/api/state/{ord_ops.rs => node_state_ord_ops.rs} | 4 ++-- raphtory/src/db/graph/nodes.rs | 2 +- 6 files changed, 11 insertions(+), 13 deletions(-) rename raphtory/src/db/api/state/{ops.rs => node_state_ops.rs} (98%) rename raphtory/src/db/api/state/{ord_ops.rs => node_state_ord_ops.rs} (98%) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index 86290cdc9..dcc2afa5e 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -11,7 +11,7 @@ use crate::{ }, graph::{node::NodeView, nodes::Nodes}, }, - prelude::{GraphViewOps, TimeOps}, + prelude::GraphViewOps, }; use raphtory_api::core::Direction; use rayon::prelude::*; @@ -481,9 +481,7 @@ mod test { }, prelude::*, }; - use itertools::Itertools; use raphtory_api::core::{entities::VID, Direction}; - use rayon::prelude::*; use std::sync::Arc; struct TestWrapper(Op); diff --git a/raphtory/src/db/api/state/mod.rs b/raphtory/src/db/api/state/mod.rs index d56ce46ad..9476f2baa 100644 --- a/raphtory/src/db/api/state/mod.rs +++ b/raphtory/src/db/api/state/mod.rs @@ -1,11 +1,11 @@ mod group_by; mod lazy_node_state; mod node_state; -mod ops; -mod ord_ops; +mod node_state_ops; +mod node_state_ord_ops; pub use lazy_node_state::{LazyNodeState, NodeOp}; pub(crate) use node_state::Index; pub use node_state::NodeState; -pub use ops::NodeStateOps; -pub use ord_ops::{AsOrderedNodeStateOps, OrderedNodeStateOps}; +pub use node_state_ops::NodeStateOps; +pub use node_state_ord_ops::{AsOrderedNodeStateOps, OrderedNodeStateOps}; diff --git a/raphtory/src/db/api/state/node_state.rs b/raphtory/src/db/api/state/node_state.rs index d5d6d76da..cb158adaf 100644 --- a/raphtory/src/db/api/state/node_state.rs +++ b/raphtory/src/db/api/state/node_state.rs @@ -1,7 +1,7 @@ use crate::{ core::entities::{nodes::node_ref::AsNodeRef, VID}, db::{ - api::{state::ops::NodeStateOps, view::IntoDynBoxed}, + api::{state::node_state_ops::NodeStateOps, view::IntoDynBoxed}, graph::node::NodeView, }, prelude::GraphViewOps, diff --git a/raphtory/src/db/api/state/ops.rs b/raphtory/src/db/api/state/node_state_ops.rs similarity index 98% rename from raphtory/src/db/api/state/ops.rs rename to raphtory/src/db/api/state/node_state_ops.rs index b1fc2381b..732722beb 100644 --- a/raphtory/src/db/api/state/ops.rs +++ b/raphtory/src/db/api/state/node_state_ops.rs @@ -1,7 +1,7 @@ use crate::{ core::entities::nodes::node_ref::AsNodeRef, db::{ - api::state::{node_state::NodeState, ord_ops, Index}, + api::state::{node_state::NodeState, node_state_ord_ops, Index}, graph::node::NodeView, }, prelude::{GraphViewOps, NodeViewOps}, @@ -157,7 +157,7 @@ pub trait NodeStateOps<'graph>: IntoIterator { cmp: F, k: usize, ) -> NodeState<'graph, Self::OwnedValue, Self::BaseGraph, Self::Graph> { - let values = ord_ops::par_top_k( + let values = node_state_ord_ops::par_top_k( self.par_iter(), |(_, v1), (_, v2)| cmp(v1.borrow(), v2.borrow()), k, diff --git a/raphtory/src/db/api/state/ord_ops.rs b/raphtory/src/db/api/state/node_state_ord_ops.rs similarity index 98% rename from raphtory/src/db/api/state/ord_ops.rs rename to raphtory/src/db/api/state/node_state_ord_ops.rs index afa2537e6..24c2dab52 100644 --- a/raphtory/src/db/api/state/ord_ops.rs +++ b/raphtory/src/db/api/state/node_state_ord_ops.rs @@ -1,5 +1,5 @@ use crate::db::{ - api::state::{node_state::NodeState, ops::NodeStateOps}, + api::state::{node_state::NodeState, node_state_ops::NodeStateOps}, graph::node::NodeView, }; use num_traits::float::FloatCore; @@ -329,7 +329,7 @@ where #[cfg(test)] mod test { - use crate::db::api::state::ord_ops::par_top_k; + use crate::db::api::state::node_state_ord_ops::par_top_k; #[test] fn test_top_k() { diff --git a/raphtory/src/db/graph/nodes.rs b/raphtory/src/db/graph/nodes.rs index eef5e5ae6..cceecb469 100644 --- a/raphtory/src/db/graph/nodes.rs +++ b/raphtory/src/db/graph/nodes.rs @@ -15,7 +15,7 @@ use crate::{ prelude::*, }; -use crate::db::{api::state::NodeOp, graph::create_node_type_filter}; +use crate::db::graph::create_node_type_filter; use rayon::iter::ParallelIterator; use std::{marker::PhantomData, sync::Arc}; From a6b67fd50b279853874f07a94a496bc5555db770 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 11 Nov 2024 14:22:18 +0100 Subject: [PATCH 05/23] swap to new LazyNodeState everywhere, still need to fix python --- raphtory/src/db/api/state/lazy_node_state.rs | 331 ++---------------- raphtory/src/db/api/state/mod.rs | 4 +- raphtory/src/db/api/state/ops/history.rs | 115 ++++++ raphtory/src/db/api/state/ops/mod.rs | 7 + raphtory/src/db/api/state/ops/node.rs | 126 +++++++ raphtory/src/db/api/state/ops/properties.rs | 21 ++ raphtory/src/db/api/view/node.rs | 173 +++++---- raphtory/src/db/graph/node.rs | 20 +- raphtory/src/db/graph/nodes.rs | 20 +- raphtory/src/db/graph/path.rs | 40 ++- raphtory/src/db/task/node/eval_node.rs | 38 +- raphtory/src/python/graph/node.rs | 44 ++- .../types/macros/trait_impl/node_state.rs | 6 +- raphtory/src/python/types/repr.rs | 12 +- 14 files changed, 514 insertions(+), 443 deletions(-) create mode 100644 raphtory/src/db/api/state/ops/history.rs create mode 100644 raphtory/src/db/api/state/ops/mod.rs create mode 100644 raphtory/src/db/api/state/ops/node.rs create mode 100644 raphtory/src/db/api/state/ops/properties.rs diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index dcc2afa5e..96dbe1a64 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -2,7 +2,7 @@ use crate::{ core::entities::{nodes::node_ref::AsNodeRef, VID}, db::{ api::{ - state::{NodeState, NodeStateOps}, + state::{ops::node::NodeOp, NodeState, NodeStateOps}, storage::graph::{nodes::node_storage_ops::NodeStorageOps, storage_ops::GraphStorage}, view::{ internal::{CoreGraphOps, NodeList, OneHopFilter}, @@ -13,71 +13,21 @@ use crate::{ }, prelude::GraphViewOps, }; -use raphtory_api::core::Direction; use rayon::prelude::*; -use std::{marker::PhantomData, ops::Deref, sync::Arc}; +use std::{marker::PhantomData, sync::Arc}; -pub trait NodeOp: Send + Sync { - type Output: Clone + Send + Sync; - fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output; -} - -pub struct Degree { - pub(crate) graph: G, - pub(crate) dir: Direction, -} - -impl<'graph, G: GraphViewOps<'graph>> NodeOp for Degree { - type Output = usize; - - fn apply(&self, storage: &GraphStorage, node: VID) -> usize { - storage.node_degree(node, self.dir, &self.graph) - } -} - -impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for Degree { - type BaseGraph = G; - type FilteredGraph = G; - type Filtered + 'graph> = Degree; - - fn current_filter(&self) -> &Self::FilteredGraph { - &self.graph - } - - fn base_graph(&self) -> &Self::BaseGraph { - &self.graph - } - - fn one_hop_filtered + 'graph>( - &self, - filtered_graph: GH, - ) -> Self::Filtered { - Degree { - graph: filtered_graph, - dir: self.dir, - } - } -} - -impl NodeOp for Arc> { - type Output = V; - fn apply(&self, storage: &GraphStorage, node: VID) -> V { - self.deref().apply(storage, node) - } -} - -pub struct LazyNodeState2<'graph, Op, G, GH = G> { +pub struct LazyNodeState<'graph, Op, G, GH = G> { nodes: Nodes<'graph, G, GH>, op: Op, } impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> - OneHopFilter<'graph> for LazyNodeState2<'graph, Op, G, GH> + OneHopFilter<'graph> for LazyNodeState<'graph, Op, G, GH> { type BaseGraph = G; type FilteredGraph = Op::FilteredGraph; type Filtered + 'graph> = - LazyNodeState2<'graph, Op::Filtered, G, GH>; + LazyNodeState<'graph, Op::Filtered, G, GH>; fn current_filter(&self) -> &Self::FilteredGraph { self.op.current_filter() @@ -91,7 +41,7 @@ impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps &self, filtered_graph: GHH, ) -> Self::Filtered { - LazyNodeState2 { + LazyNodeState { nodes: self.nodes.clone(), op: self.op.one_hop_filtered(filtered_graph), } @@ -99,7 +49,7 @@ impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps } impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> IntoIterator - for LazyNodeState2<'graph, Op, G, GH> + for LazyNodeState<'graph, Op, G, GH> { type Item = Op::Output; type IntoIter = BoxedLIter<'graph, Self::Item>; @@ -110,7 +60,23 @@ impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'gra } impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> - NodeStateOps<'graph> for LazyNodeState2<'graph, Op, G, GH> + LazyNodeState<'graph, Op, G, GH> +{ + pub(crate) fn new(op: Op, nodes: Nodes<'graph, G, GH>) -> Self { + Self { nodes, op } + } + + pub fn collect>(&self) -> C { + self.par_values().collect() + } + + pub fn collect_vec(&self) -> Vec { + self.collect() + } +} + +impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> + NodeStateOps<'graph> for LazyNodeState<'graph, Op, G, GH> { type Graph = GH; type BaseGraph = G; @@ -232,251 +198,14 @@ impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'gra } } -#[derive(Clone)] -pub struct LazyNodeState<'graph, V, G, GH = G> { - op: fn(&GraphStorage, &GH, VID) -> V, - base_graph: G, - graph: GH, - node_types_filter: Option>, - _marker: PhantomData<&'graph ()>, -} - -impl< - 'graph, - G: GraphViewOps<'graph>, - GH: GraphViewOps<'graph>, - V: Clone + Send + Sync + 'graph, - > LazyNodeState<'graph, V, G, GH> -{ - pub(crate) fn new( - base_graph: G, - graph: GH, - node_types_filter: Option>, - op: fn(&GraphStorage, &GH, VID) -> V, - ) -> Self { - Self { - op, - base_graph, - graph, - node_types_filter, - _marker: Default::default(), - } - } - - fn apply(&self, cg: &GraphStorage, g: &GH, vid: VID) -> V { - (self.op)(cg, g, vid) - } - - pub fn compute(&self) -> NodeState<'graph, V, G, GH> { - let cg = self.graph.core_graph().lock(); - if self.graph.nodes_filtered() || self.node_types_filter.is_some() { - let keys: Vec<_> = cg - .nodes_par(&self.graph, self.node_types_filter.as_ref()) - .collect(); - let mut values = Vec::with_capacity(keys.len()); - keys.par_iter() - .map(|vid| self.apply(&cg, &self.graph, *vid)) - .collect_into_vec(&mut values); - NodeState::new( - self.base_graph.clone(), - self.graph.clone(), - values, - Some(keys.into()), - ) - } else { - let n = cg.nodes().len(); - let mut values = Vec::with_capacity(n); - (0..n) - .into_par_iter() - .map(|i| self.apply(&cg, &self.graph, VID(i))) - .collect_into_vec(&mut values); - NodeState::new(self.base_graph.clone(), self.graph.clone(), values, None) - } - } - - pub fn collect>(&self) -> C { - self.par_values().collect() - } - - pub fn collect_vec(&self) -> Vec { - self.collect() - } -} - -impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>, V: 'graph> IntoIterator - for LazyNodeState<'graph, V, G, GH> -{ - type Item = V; - type IntoIter = Box + Send + 'graph>; - - fn into_iter(self) -> Self::IntoIter { - let cg = self.graph.core_graph().lock(); - let graph = self.graph; - let op = self.op; - cg.clone() - .into_nodes_iter(graph.clone(), self.node_types_filter) - .map(move |v| op(&cg, &graph, v)) - .into_dyn_boxed() - } -} - -impl< - 'graph, - G: GraphViewOps<'graph>, - GH: GraphViewOps<'graph>, - V: Clone + Send + Sync + 'graph, - > NodeStateOps<'graph> for LazyNodeState<'graph, V, G, GH> -{ - type Graph = GH; - type BaseGraph = G; - type Value<'a> - = V - where - 'graph: 'a, - Self: 'a; - type OwnedValue = V; - - fn graph(&self) -> &Self::Graph { - &self.graph - } - - fn base_graph(&self) -> &Self::BaseGraph { - &self.base_graph - } - - fn values<'a>(&'a self) -> impl Iterator> + 'a - where - 'graph: 'a, - { - let cg = self.graph.core_graph().lock(); - cg.clone() - .into_nodes_iter(&self.graph, self.node_types_filter.clone()) - .map(move |vid| self.apply(&cg, &self.graph, vid)) - } - - fn par_values<'a>(&'a self) -> impl ParallelIterator> + 'a - where - 'graph: 'a, - { - let cg = self.graph.core_graph().lock(); - cg.clone() - .into_nodes_par(&self.graph, self.node_types_filter.clone()) - .map(move |vid| self.apply(&cg, &self.graph, vid)) - } - - fn into_values(self) -> impl Iterator + 'graph { - let cg = self.graph.core_graph().lock(); - let graph = self.graph.clone(); - let op = self.op; - cg.clone() - .into_nodes_iter(self.graph, self.node_types_filter) - .map(move |n| op(&cg, &graph, n)) - } - - fn into_par_values(self) -> impl ParallelIterator + 'graph { - let cg = self.graph.core_graph().lock(); - let graph = self.graph.clone(); - let op = self.op; - cg.clone() - .into_nodes_par(self.graph, self.node_types_filter) - .map(move |n| op(&cg, &graph, n)) - } - - fn iter<'a>( - &'a self, - ) -> impl Iterator< - Item = ( - NodeView<&'a Self::BaseGraph, &'a Self::Graph>, - Self::Value<'a>, - ), - > + 'a - where - 'graph: 'a, - { - let cg = self.graph.core_graph().lock(); - cg.clone() - .into_nodes_iter(self.graph.clone(), self.node_types_filter.clone()) - .map(move |n| { - ( - NodeView::new_one_hop_filtered(&self.base_graph, &self.graph, n), - (self.op)(&cg, &self.graph, n), - ) - }) - } - - fn par_iter<'a>( - &'a self, - ) -> impl ParallelIterator< - Item = ( - NodeView<&'a Self::BaseGraph, &'a Self::Graph>, - Self::Value<'a>, - ), - > - where - 'graph: 'a, - { - let cg = self.graph.core_graph().lock(); - cg.clone() - .into_nodes_par(self.graph.clone(), self.node_types_filter.clone()) - .map(move |n| { - ( - NodeView::new_one_hop_filtered(&self.base_graph, &self.graph, n), - (self.op)(&cg, &self.graph, n), - ) - }) - } - - fn get_by_index( - &self, - index: usize, - ) -> Option<(NodeView<&Self::BaseGraph, &Self::Graph>, Self::Value<'_>)> { - if self.graph.nodes_filtered() { - self.iter().nth(index) - } else { - let vid = match self.graph.node_list() { - NodeList::All { num_nodes } => { - if index < num_nodes { - VID(index) - } else { - return None; - } - } - NodeList::List { nodes } => nodes.key(index)?, - }; - let cg = self.graph.core_graph(); - Some(( - NodeView::new_one_hop_filtered(&self.base_graph, &self.graph, vid), - (self.op)(cg, &self.graph, vid), - )) - } - } - - fn get_by_node(&self, node: N) -> Option> { - let vid = self.graph.internalise_node(node.as_node_ref())?; - if !self.graph.has_node(vid) { - return None; - } - if let Some(type_filter) = self.node_types_filter.as_ref() { - let core_node_entry = &self.graph.core_node_entry(vid); - if !type_filter[core_node_entry.node_type_id()] { - return None; - } - } - - let cg = self.graph.core_graph(); - Some(self.apply(cg, &self.graph, vid)) - } - - fn len(&self) -> usize { - self.graph.count_nodes() - } -} - #[cfg(test)] mod test { use crate::{ db::api::{ - state::lazy_node_state::{Degree, LazyNodeState2, NodeOp}, + state::{ + lazy_node_state::LazyNodeState, + ops::node::{Degree, NodeOp}, + }, view::{internal::CoreGraphOps, IntoDynamic}, }, prelude::*, @@ -492,7 +221,7 @@ mod test { let nodes = g.nodes(); - let node_state = LazyNodeState2 { + let node_state = LazyNodeState { nodes, op: Degree { graph: g.clone(), @@ -517,7 +246,7 @@ mod test { }; let arc_deg: Arc> = Arc::new(deg); - let node_state_dyn = LazyNodeState2 { + let node_state_dyn = LazyNodeState { nodes: g.nodes(), op: arc_deg.clone(), }; diff --git a/raphtory/src/db/api/state/mod.rs b/raphtory/src/db/api/state/mod.rs index 9476f2baa..15230f049 100644 --- a/raphtory/src/db/api/state/mod.rs +++ b/raphtory/src/db/api/state/mod.rs @@ -3,9 +3,11 @@ mod lazy_node_state; mod node_state; mod node_state_ops; mod node_state_ord_ops; +pub(crate) mod ops; -pub use lazy_node_state::{LazyNodeState, NodeOp}; +pub use lazy_node_state::LazyNodeState; pub(crate) use node_state::Index; pub use node_state::NodeState; pub use node_state_ops::NodeStateOps; pub use node_state_ord_ops::{AsOrderedNodeStateOps, OrderedNodeStateOps}; +pub use ops::node::NodeOp; diff --git a/raphtory/src/db/api/state/ops/history.rs b/raphtory/src/db/api/state/ops/history.rs new file mode 100644 index 000000000..31eb7ada5 --- /dev/null +++ b/raphtory/src/db/api/state/ops/history.rs @@ -0,0 +1,115 @@ +use crate::{ + db::api::{ + state::NodeOp, storage::graph::storage_ops::GraphStorage, view::internal::OneHopFilter, + }, + prelude::GraphViewOps, +}; +use raphtory_api::core::entities::VID; + +#[derive(Debug, Clone)] +pub struct EarliestTime { + pub(crate) graph: G, +} + +impl<'graph, G: GraphViewOps<'graph>> NodeOp for EarliestTime { + type Output = Option; + + fn apply(&self, _storage: &GraphStorage, node: VID) -> Self::Output { + self.graph.node_earliest_time(node) + } +} + +impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for EarliestTime { + type BaseGraph = G; + type FilteredGraph = G; + type Filtered + 'graph> = EarliestTime; + + fn current_filter(&self) -> &Self::FilteredGraph { + &self.graph + } + + fn base_graph(&self) -> &Self::BaseGraph { + &self.graph + } + + fn one_hop_filtered + 'graph>( + &self, + filtered_graph: GH, + ) -> Self::Filtered { + EarliestTime { + graph: filtered_graph, + } + } +} + +#[derive(Debug, Clone)] +pub struct LatestTime { + pub(crate) graph: G, +} + +impl<'graph, G: GraphViewOps<'graph>> NodeOp for LatestTime { + type Output = Option; + + fn apply(&self, _storage: &GraphStorage, node: VID) -> Self::Output { + self.graph.node_latest_time(node) + } +} + +impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for LatestTime { + type BaseGraph = G; + type FilteredGraph = G; + type Filtered + 'graph> = LatestTime; + + fn current_filter(&self) -> &Self::FilteredGraph { + &self.graph + } + + fn base_graph(&self) -> &Self::BaseGraph { + &self.graph + } + + fn one_hop_filtered + 'graph>( + &self, + filtered_graph: GH, + ) -> Self::Filtered { + LatestTime { + graph: filtered_graph, + } + } +} + +#[derive(Debug, Clone)] +pub struct History { + pub(crate) graph: G, +} + +impl<'graph, G: GraphViewOps<'graph>> NodeOp for History { + type Output = Vec; + + fn apply(&self, _storage: &GraphStorage, node: VID) -> Self::Output { + self.graph.node_history(node) + } +} + +impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for History { + type BaseGraph = G; + type FilteredGraph = G; + type Filtered + 'graph> = History; + + fn current_filter(&self) -> &Self::FilteredGraph { + &self.graph + } + + fn base_graph(&self) -> &Self::BaseGraph { + &self.graph + } + + fn one_hop_filtered + 'graph>( + &self, + filtered_graph: GH, + ) -> Self::Filtered { + History { + graph: filtered_graph, + } + } +} diff --git a/raphtory/src/db/api/state/ops/mod.rs b/raphtory/src/db/api/state/ops/mod.rs new file mode 100644 index 000000000..9677afc53 --- /dev/null +++ b/raphtory/src/db/api/state/ops/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod history; +pub(crate) mod node; +mod properties; + +pub use history::*; +pub use node::*; +pub use properties::*; diff --git a/raphtory/src/db/api/state/ops/node.rs b/raphtory/src/db/api/state/ops/node.rs new file mode 100644 index 000000000..82ccdab8b --- /dev/null +++ b/raphtory/src/db/api/state/ops/node.rs @@ -0,0 +1,126 @@ +use crate::{ + db::api::{ + storage::graph::storage_ops::GraphStorage, + view::internal::{CoreGraphOps, OneHopFilter}, + }, + prelude::GraphViewOps, +}; +use raphtory_api::core::{ + entities::{GID, VID}, + storage::arc_str::ArcStr, + Direction, +}; +use std::{ops::Deref, sync::Arc}; + +pub trait NodeOp: Send + Sync { + type Output: Clone + Send + Sync; + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output; + + fn map(self, map: fn(Self::Output) -> V) -> Map + where + Self: Sized, + { + Map { op: self, map } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Name; + +impl NodeOp for Name { + type Output = String; + + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output { + storage.node_name(node) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Id; + +impl NodeOp for Id { + type Output = GID; + + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output { + storage.node_id(node) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Type; +impl NodeOp for Type { + type Output = Option; + + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output { + storage.node_type(node) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct TypeId; +impl NodeOp for TypeId { + type Output = usize; + + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output { + storage.node_type_id(node) + } +} + +#[derive(Debug, Clone)] +pub struct Degree { + pub(crate) graph: G, + pub(crate) dir: Direction, +} + +impl<'graph, G: GraphViewOps<'graph>> NodeOp for Degree { + type Output = usize; + + fn apply(&self, storage: &GraphStorage, node: VID) -> usize { + storage.node_degree(node, self.dir, &self.graph) + } +} + +impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for Degree { + type BaseGraph = G; + type FilteredGraph = G; + type Filtered + 'graph> = Degree; + + fn current_filter(&self) -> &Self::FilteredGraph { + &self.graph + } + + fn base_graph(&self) -> &Self::BaseGraph { + &self.graph + } + + fn one_hop_filtered + 'graph>( + &self, + filtered_graph: GH, + ) -> Self::Filtered { + Degree { + graph: filtered_graph, + dir: self.dir, + } + } +} + +impl NodeOp for Arc> { + type Output = V; + fn apply(&self, storage: &GraphStorage, node: VID) -> V { + self.deref().apply(storage, node) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Map { + op: Op, + map: fn(Op::Output) -> V, +} + +impl NodeOp for Map { + type Output = V; + + fn apply(&self, storage: &GraphStorage, node: VID) -> Self::Output { + (self.map)(self.op.apply(storage, node)) + } +} diff --git a/raphtory/src/db/api/state/ops/properties.rs b/raphtory/src/db/api/state/ops/properties.rs new file mode 100644 index 000000000..458b8d5e9 --- /dev/null +++ b/raphtory/src/db/api/state/ops/properties.rs @@ -0,0 +1,21 @@ +use crate::{ + db::{ + api::{properties::Properties, state::NodeOp, storage::graph::storage_ops::GraphStorage}, + graph::node::NodeView, + }, + prelude::GraphViewOps, +}; +use raphtory_api::core::entities::VID; + +#[derive(Debug, Clone)] +pub struct GetProperties { + pub(crate) graph: G, +} + +impl<'graph, G: GraphViewOps<'graph>> NodeOp for GetProperties { + type Output = Properties>; + + fn apply(&self, _storage: &GraphStorage, node: VID) -> Self::Output { + Properties::new(NodeView::new_internal(self.graph.clone(), node)) + } +} diff --git a/raphtory/src/db/api/view/node.rs b/raphtory/src/db/api/view/node.rs index 04a60f2a0..e23fe93d2 100644 --- a/raphtory/src/db/api/view/node.rs +++ b/raphtory/src/db/api/view/node.rs @@ -6,6 +6,7 @@ use crate::{ }, db::api::{ properties::{internal::PropertiesOps, Properties}, + state::{ops, NodeOp}, storage::graph::{nodes::node_storage_ops::NodeStorageOps, storage_ops::GraphStorage}, view::{ internal::{CoreGraphOps, OneHopFilter, TimeSemantics}, @@ -21,22 +22,19 @@ use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; pub trait BaseNodeViewOps<'graph>: Clone + TimeOps<'graph> + LayerOps<'graph> { type BaseGraph: GraphViewOps<'graph>; type Graph: GraphViewOps<'graph>; - type ValueType: 'graph + type ValueType: 'graph where - T: 'graph; + Op: NodeOp + 'graph, + Op::Output: 'graph; type PropType: PropertiesOps + Clone + 'graph; type PathType: NodeViewOps<'graph, BaseGraph = Self::BaseGraph, Graph = Self::BaseGraph> + 'graph; type Edges: EdgeViewOps<'graph, Graph = Self::Graph, BaseGraph = Self::BaseGraph> + 'graph; - fn map( - &self, - op: fn(&GraphStorage, &Self::Graph, VID) -> O, - ) -> Self::ValueType; - - fn as_props(&self) -> Self::ValueType>; + fn graph(&self) -> &Self::Graph; + fn map(&self, op: F) -> Self::ValueType; fn map_edges< I: Iterator + Send + 'graph, F: Fn(&GraphStorage, &Self::Graph, VID) -> I + Send + Sync + Clone + 'graph, @@ -58,73 +56,79 @@ pub trait BaseNodeViewOps<'graph>: Clone + TimeOps<'graph> + LayerOps<'graph> { pub trait NodeViewOps<'graph>: Clone + TimeOps<'graph> + LayerOps<'graph> { type BaseGraph: GraphViewOps<'graph>; type Graph: GraphViewOps<'graph>; - type ValueType: 'graph + type ValueType: 'graph where - T: 'graph; + T: 'graph, + T::Output: 'graph; type PathType: NodeViewOps<'graph, BaseGraph = Self::BaseGraph, Graph = Self::BaseGraph> + 'graph; - type PropType: PropertiesOps + Clone + 'graph; type Edges: EdgeViewOps<'graph, Graph = Self::Graph, BaseGraph = Self::BaseGraph> + 'graph; /// Get the numeric id of the node - fn id(&self) -> Self::ValueType; + fn id(&self) -> Self::ValueType; /// Get the name of this node if a user has set one otherwise it returns the ID. /// /// Returns: /// /// The name of the node if one exists, otherwise the ID as a string. - fn name(&self) -> Self::ValueType; + fn name(&self) -> Self::ValueType; /// Returns the type of node - fn node_type(&self) -> Self::ValueType>; - fn node_type_id(&self) -> Self::ValueType; + fn node_type(&self) -> Self::ValueType; + fn node_type_id(&self) -> Self::ValueType; /// Get the timestamp for the earliest activity of the node - fn earliest_time(&self) -> Self::ValueType>; + fn earliest_time(&self) -> Self::ValueType>; - fn earliest_date_time(&self) -> Self::ValueType>>; + fn earliest_date_time( + &self, + ) -> Self::ValueType, Option>>>; /// Get the timestamp for the latest activity of the node - fn latest_time(&self) -> Self::ValueType>; + fn latest_time(&self) -> Self::ValueType>; - fn latest_date_time(&self) -> Self::ValueType>>; + fn latest_date_time( + &self, + ) -> Self::ValueType, Option>>>; /// Gets the history of the node (time that the node was added and times when changes were made to the node) - fn history(&self) -> Self::ValueType>; + fn history(&self) -> Self::ValueType>; /// Gets the history of the node (time that the node was added and times when changes were made to the node) as `DateTime` objects if parseable - fn history_date_time(&self) -> Self::ValueType>>>; + fn history_date_time( + &self, + ) -> Self::ValueType, Option>>>>; //Returns true if the node has any updates within the current window, otherwise false - fn is_active(&self) -> Self::ValueType; + fn is_active(&self) -> Self::ValueType, bool>>; /// Get a view of the temporal properties of this node. /// /// Returns: /// /// A view with the names of the properties as keys and the property values as values. - fn properties(&self) -> Self::ValueType>; + fn properties(&self) -> Self::ValueType>; /// Get the degree of this node (i.e., the number of edges that are incident to it). /// /// Returns: /// /// The degree of this node. - fn degree(&self) -> Self::ValueType; + fn degree(&self) -> Self::ValueType>; /// Get the in-degree of this node (i.e., the number of edges that point into it). /// /// Returns: /// /// The in-degree of this node. - fn in_degree(&self) -> Self::ValueType; + fn in_degree(&self) -> Self::ValueType>; /// Get the out-degree of this node (i.e., the number of edges that point out of it). /// /// Returns: /// /// The out-degree of this node. - fn out_degree(&self) -> Self::ValueType; + fn out_degree(&self) -> Self::ValueType>; /// Get the edges that are incident to this node. /// @@ -172,80 +176,123 @@ pub trait NodeViewOps<'graph>: Clone + TimeOps<'graph> + LayerOps<'graph> { impl<'graph, V: BaseNodeViewOps<'graph> + 'graph> NodeViewOps<'graph> for V { type BaseGraph = V::BaseGraph; type Graph = V::Graph; - type ValueType = V::ValueType; + type ValueType + = V::ValueType + where + T::Output: 'graph; type PathType = V::PathType; - type PropType = V::PropType; type Edges = V::Edges; #[inline] - fn id(&self) -> Self::ValueType { - self.map(|cg, _g, v| cg.node_entry(v).id().into()) + fn id(&self) -> Self::ValueType { + self.map(ops::Id) } #[inline] - fn name(&self) -> Self::ValueType { - self.map(|_cg, g, v| g.node_name(v)) + fn name(&self) -> Self::ValueType { + self.map(ops::Name) } #[inline] - fn node_type(&self) -> Self::ValueType> { - self.map(|_cg, g, v| g.node_type(v)) + fn node_type(&self) -> Self::ValueType { + self.map(ops::Type) } #[inline] - fn node_type_id(&self) -> Self::ValueType { - self.map(|_cg, g, v| g.node_type_id(v)) + fn node_type_id(&self) -> Self::ValueType { + self.map(ops::TypeId) } #[inline] - fn earliest_time(&self) -> Self::ValueType> { - self.map(|_cg, g, v| g.node_earliest_time(v)) + fn earliest_time(&self) -> Self::ValueType> { + let op = ops::EarliestTime { + graph: self.graph().clone(), + }; + self.map(op) } #[inline] - fn earliest_date_time(&self) -> Self::ValueType>> { - self.map(|_cg, g, v| g.node_earliest_time(v)?.dt()) + fn earliest_date_time( + &self, + ) -> Self::ValueType, Option>>> { + let op = ops::EarliestTime { + graph: self.graph().clone(), + } + .map(|t| t.and_then(|t| t.dt())); + self.map(op) } #[inline] - fn latest_time(&self) -> Self::ValueType> { - self.map(|_cg, g, v| g.node_latest_time(v)) + fn latest_time(&self) -> Self::ValueType> { + let op = ops::LatestTime { + graph: self.graph().clone(), + }; + self.map(op) } #[inline] - fn latest_date_time(&self) -> Self::ValueType>> { - self.map(|_cg, g, v| g.node_latest_time(v)?.dt()) + fn latest_date_time( + &self, + ) -> Self::ValueType, Option>>> { + let op = ops::LatestTime { + graph: self.graph().clone(), + } + .map(|t| t.and_then(|t| t.dt())); + self.map(op) } #[inline] - fn history(&self) -> Self::ValueType> { - self.map(|_cg, g, v| g.node_history(v)) + fn history(&self) -> Self::ValueType> { + let op = ops::History { + graph: self.graph().clone(), + }; + self.map(op) } #[inline] - fn history_date_time(&self) -> Self::ValueType>>> { - self.map(|_cg, g, v| { - g.node_history(v) - .iter() - .map(|t| t.dt()) - .collect::>>() - }) + fn history_date_time( + &self, + ) -> Self::ValueType, Option>>>> { + let op = ops::History { + graph: self.graph().clone(), + } + .map(|h| h.into_iter().map(|t| t.dt()).collect()); + self.map(op) } - fn is_active(&self) -> Self::ValueType { - self.map(|_cg, g, v| !g.node_history(v).is_empty()) + fn is_active(&self) -> Self::ValueType, bool>> { + let op = ops::History { + graph: self.graph().clone(), + } + .map(|h| !h.is_empty()); + self.map(op) } #[inline] - fn properties(&self) -> Self::ValueType> { - self.as_props() + fn properties(&self) -> Self::ValueType> { + let op = ops::GetProperties { + graph: self.graph().clone(), + }; + self.map(op) } #[inline] - fn degree(&self) -> Self::ValueType { - self.map(|cg, g, v| cg.node_degree(v, Direction::BOTH, g)) + fn degree(&self) -> Self::ValueType> { + let op = ops::Degree { + graph: self.graph().clone(), + dir: Direction::BOTH, + }; + self.map(op) } #[inline] - fn in_degree(&self) -> Self::ValueType { - self.map(|cg, g, v| cg.node_degree(v, Direction::IN, g)) + fn in_degree(&self) -> Self::ValueType> { + let op = ops::Degree { + graph: self.graph().clone(), + dir: Direction::IN, + }; + self.map(op) } #[inline] - fn out_degree(&self) -> Self::ValueType { - self.map(|cg, g, v| cg.node_degree(v, Direction::OUT, g)) + fn out_degree(&self) -> Self::ValueType> { + let op = ops::Degree { + graph: self.graph().clone(), + dir: Direction::OUT, + }; + self.map(op) } #[inline] fn edges(&self) -> Self::Edges { diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index 4083ef08f..ad37f476d 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -27,7 +27,10 @@ use crate::{ use crate::{ core::{entities::nodes::node_ref::AsNodeRef, storage::timeindex::AsTime, PropType}, - db::{api::storage::graph::storage_ops::GraphStorage, graph::edges::Edges}, + db::{ + api::{state::NodeOp, storage::graph::storage_ops::GraphStorage}, + graph::edges::Edges, + }, }; use chrono::{DateTime, Utc}; use raphtory_api::core::storage::arc_str::ArcStr; @@ -308,20 +311,21 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> BaseNodeViewOps< type BaseGraph = G; type Graph = GH; type ValueType - = T + = T::Output where - T: 'graph; + T: NodeOp + 'graph, + T::Output: 'graph; type PropType = Self; type PathType = PathFromNode<'graph, G, G>; type Edges = Edges<'graph, G, GH>; - fn map(&self, op: fn(&GraphStorage, &Self::Graph, VID) -> O) -> Self::ValueType { - let cg = self.graph.core_graph(); - op(cg, &self.graph, self.node) + fn graph(&self) -> &Self::Graph { + &self.graph } - fn as_props(&self) -> Self::ValueType> { - Properties::new(self.clone()) + fn map(&self, op: F) -> Self::ValueType { + let cg = self.graph.core_graph(); + op.apply(cg, self.node) } fn map_edges< diff --git a/raphtory/src/db/graph/nodes.rs b/raphtory/src/db/graph/nodes.rs index cceecb469..1896ff965 100644 --- a/raphtory/src/db/graph/nodes.rs +++ b/raphtory/src/db/graph/nodes.rs @@ -15,7 +15,7 @@ use crate::{ prelude::*, }; -use crate::db::graph::create_node_type_filter; +use crate::db::{api::state::NodeOp, graph::create_node_type_filter}; use rayon::iter::ParallelIterator; use std::{marker::PhantomData, sync::Arc}; @@ -188,22 +188,20 @@ where { type BaseGraph = G; type Graph = GH; - type ValueType = LazyNodeState<'graph, T, G, GH>; + type ValueType = LazyNodeState<'graph, T, G, GH>; type PropType = NodeView; type PathType = PathFromGraph<'graph, G, G>; type Edges = NestedEdges<'graph, G, GH>; - fn map( - &self, - op: fn(&GraphStorage, &Self::Graph, VID) -> O, - ) -> Self::ValueType { - let g = self.graph.clone(); - let bg = self.base_graph.clone(); - LazyNodeState::new(bg, g, self.node_types_filter.clone(), op) + fn graph(&self) -> &Self::Graph { + &self.graph } - fn as_props(&self) -> Self::ValueType> { - self.map(|_cg, g, v| Properties::new(NodeView::new_internal(g.clone(), v))) + fn map(&self, op: F) -> Self::ValueType + where + ::Output: 'graph, + { + LazyNodeState::new(op, self.clone()) } fn map_edges< diff --git a/raphtory/src/db/graph/path.rs b/raphtory/src/db/graph/path.rs index 3853846e5..2b8bdfc29 100644 --- a/raphtory/src/db/graph/path.rs +++ b/raphtory/src/db/graph/path.rs @@ -3,6 +3,7 @@ use crate::{ db::{ api::{ properties::Properties, + state::NodeOp, storage::graph::storage_ops::GraphStorage, view::{ internal::OneHopFilter, BaseNodeViewOps, BoxedLIter, DynamicGraph, IntoDynBoxed, @@ -147,27 +148,30 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> BaseNodeViewOps< { type BaseGraph = G; type Graph = GH; - type ValueType = BoxedLIter<'graph, BoxedLIter<'graph, T>>; + type ValueType = BoxedLIter<'graph, BoxedLIter<'graph, T::Output>>; type PropType = NodeView; type PathType = PathFromGraph<'graph, G, G>; type Edges = NestedEdges<'graph, G, GH>; - fn map(&self, op: fn(&GraphStorage, &Self::Graph, VID) -> O) -> Self::ValueType { - let graph = self.graph.clone(); + fn graph(&self) -> &Self::Graph { + &self.graph + } + + fn map(&self, op: F) -> Self::ValueType + where + ::Output: 'graph, + { + let storage = self.graph.core_graph().lock(); self.iter_refs() .map(move |it| { - let graph = graph.clone(); let op = op.clone(); - it.map(move |node| op(graph.core_graph(), &graph, node)) + let storage = storage.clone(); + it.map(move |node| op.apply(&storage, node)) .into_dyn_boxed() }) .into_dyn_boxed() } - fn as_props(&self) -> Self::ValueType> { - self.map(|_cg, g, v| Properties::new(NodeView::new_internal(g.clone(), v))) - } - fn map_edges< I: Iterator + Send + 'graph, F: Fn(&GraphStorage, &Self::Graph, VID) -> I + Send + Sync + Clone + 'graph, @@ -393,21 +397,21 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> BaseNodeViewOps< { type BaseGraph = G; type Graph = GH; - type ValueType = BoxedLIter<'graph, T>; + type ValueType = BoxedLIter<'graph, T::Output>; type PropType = NodeView; type PathType = PathFromNode<'graph, G, G>; type Edges = Edges<'graph, G, GH>; - fn map(&self, op: fn(&GraphStorage, &Self::Graph, VID) -> O) -> Self::ValueType { - let graph = self.graph.clone(); - Box::new( - self.iter_refs() - .map(move |node| op(graph.core_graph(), &graph, node)), - ) + fn graph(&self) -> &Self::Graph { + &self.graph } - fn as_props(&self) -> Self::ValueType> { - self.map(|_cg, g, v| Properties::new(NodeView::new_internal(g.clone(), v))) + fn map(&self, op: F) -> Self::ValueType + where + ::Output: 'graph, + { + let storage = self.graph.core_graph().lock(); + Box::new(self.iter_refs().map(move |node| op.apply(&storage, node))) } fn map_edges< diff --git a/raphtory/src/db/task/node/eval_node.rs b/raphtory/src/db/task/node/eval_node.rs index bfb41183b..a535e9bea 100644 --- a/raphtory/src/db/task/node/eval_node.rs +++ b/raphtory/src/db/task/node/eval_node.rs @@ -11,6 +11,7 @@ use crate::{ db::{ api::{ properties::Properties, + state::NodeOp, storage::graph::storage_ops::GraphStorage, view::{internal::OneHopFilter, BaseNodeViewOps, BoxedLIter, IntoDynBoxed}, }, @@ -347,22 +348,21 @@ impl< { type BaseGraph = &'graph G; type Graph = GH; - type ValueType = Box + 'graph>; + type ValueType = Box + 'graph>; type PropType = NodeView; type PathType = EvalPathFromNode<'graph, 'a, G, &'graph G, CS, S>; type Edges = EvalEdges<'graph, 'a, G, GH, CS, S>; - fn map( - &self, - op: fn(&GraphStorage, &Self::Graph, VID) -> O, - ) -> Self::ValueType { - let graph = self.graph.clone(); - let storage = self.base_graph.storage; - Box::new(self.iter_refs().map(move |node| op(storage, &graph, node))) + fn graph(&self) -> &Self::Graph { + &self.graph } - fn as_props(&self) -> Self::ValueType> { - self.map(|_cg, g, v| Properties::new(NodeView::new_internal(g.clone(), v))) + fn map(&self, op: F) -> Self::ValueType + where + ::Output: 'graph, + { + let storage = self.base_graph.storage; + Box::new(self.iter_refs().map(move |node| op.apply(storage, node))) } fn map_edges< @@ -492,23 +492,23 @@ impl< { type BaseGraph = &'graph G; type Graph = GH; - type ValueType - = T + type ValueType + = T::Output where T: 'graph; type PropType = NodeView; type PathType = EvalPathFromNode<'graph, 'a, G, &'graph G, CS, S>; type Edges = EvalEdges<'graph, 'a, G, GH, CS, S>; - fn map( - &self, - op: fn(&GraphStorage, &Self::Graph, VID) -> O, - ) -> Self::ValueType { - op(self.eval_graph.storage, &self.graph, self.node) + fn graph(&self) -> &Self::Graph { + &self.graph } - fn as_props(&self) -> Self::ValueType> { - Properties::new(NodeView::new_internal(self.graph.clone(), self.node)) + fn map(&self, op: F) -> Self::ValueType + where + ::Output: 'graph, + { + op.apply(self.eval_graph.storage, self.node) } fn map_edges< diff --git a/raphtory/src/python/graph/node.rs b/raphtory/src/python/graph/node.rs index 7d03edc09..8f6b2a725 100644 --- a/raphtory/src/python/graph/node.rs +++ b/raphtory/src/python/graph/node.rs @@ -10,7 +10,7 @@ use crate::{ db::{ api::{ properties::Properties, - state::{LazyNodeState, NodeStateOps}, + state::{ops, LazyNodeState, NodeStateOps}, view::{ internal::{CoreGraphOps, DynamicGraph, Immutable, IntoDynamic, MaterializedGraph}, *, @@ -502,19 +502,21 @@ impl PyNodes { /// Returns an iterator over the nodes ids #[getter] - fn id(&self) -> LazyNodeState<'static, GID, DynamicGraph, DynamicGraph> { + fn id(&self) -> LazyNodeState<'static, ops::Id, DynamicGraph, DynamicGraph> { self.nodes.id() } /// Returns an iterator over the nodes name #[getter] - fn name(&self) -> LazyNodeState<'static, String, DynamicGraph, DynamicGraph> { + fn name(&self) -> LazyNodeState<'static, ops::Name, DynamicGraph, DynamicGraph> { self.nodes.name() } /// Returns an iterator over the nodes earliest time #[getter] - fn earliest_time(&self) -> LazyNodeState<'static, Option, DynamicGraph, DynamicGraph> { + fn earliest_time( + &self, + ) -> LazyNodeState<'static, ops::EarliestTime, DynamicGraph, DynamicGraph> { self.nodes.earliest_time() } @@ -523,13 +525,19 @@ impl PyNodes { /// Returns: /// Earliest time of the nodes. #[getter] - fn earliest_date_time(&self) -> LazyNodeState<'static, Option>, DynamicGraph> { + fn earliest_date_time( + &self, + ) -> LazyNodeState< + 'static, + ops::Map, Option>>, + DynamicGraph, + > { self.nodes.earliest_date_time() } /// Returns an iterator over the nodes latest time #[getter] - fn latest_time(&self) -> LazyNodeState<'static, Option, DynamicGraph> { + fn latest_time(&self) -> LazyNodeState<'static, ops::LatestTime, DynamicGraph> { self.nodes.latest_time() } @@ -538,7 +546,13 @@ impl PyNodes { /// Returns: /// Latest date time of the nodes. #[getter] - fn latest_date_time(&self) -> LazyNodeState<'static, Option>, DynamicGraph> { + fn latest_date_time( + &self, + ) -> LazyNodeState< + 'static, + ops::Map, Option>>, + DynamicGraph, + > { self.nodes.latest_date_time() } @@ -547,13 +561,13 @@ impl PyNodes { /// Returns: /// A list of unix timestamps. /// - fn history(&self) -> LazyNodeState<'static, Vec, DynamicGraph> { + fn history(&self) -> LazyNodeState<'static, ops::History, DynamicGraph> { self.nodes.history() } /// Returns the type of node #[getter] - fn node_type(&self) -> LazyNodeState<'static, Option, DynamicGraph> { + fn node_type(&self) -> LazyNodeState<'static, ops::Type, DynamicGraph> { self.nodes.node_type() } @@ -564,7 +578,11 @@ impl PyNodes { /// fn history_date_time( &self, - ) -> LazyNodeState<'static, Option>>, DynamicGraph> { + ) -> LazyNodeState< + 'static, + ops::Map, Option>>>, + DynamicGraph, + > { self.nodes.history_date_time() } @@ -582,7 +600,7 @@ impl PyNodes { /// /// Returns: /// An iterator of the number of edges of the nodes - fn degree(&self) -> LazyNodeState<'static, usize, DynamicGraph> { + fn degree(&self) -> LazyNodeState<'static, ops::Degree, DynamicGraph> { self.nodes.degree() } @@ -590,7 +608,7 @@ impl PyNodes { /// /// Returns: /// An iterator of the number of in edges of the nodes - fn in_degree(&self) -> LazyNodeState<'static, usize, DynamicGraph> { + fn in_degree(&self) -> LazyNodeState<'static, ops::Degree, DynamicGraph> { self.nodes.in_degree() } @@ -598,7 +616,7 @@ impl PyNodes { /// /// Returns: /// An iterator of the number of out edges of the nodes - fn out_degree(&self) -> LazyNodeState<'static, usize, DynamicGraph> { + fn out_degree(&self) -> LazyNodeState<'static, ops::Degree, DynamicGraph> { self.nodes.out_degree() } diff --git a/raphtory/src/python/types/macros/trait_impl/node_state.rs b/raphtory/src/python/types/macros/trait_impl/node_state.rs index fe804a59f..6c3b721a7 100644 --- a/raphtory/src/python/types/macros/trait_impl/node_state.rs +++ b/raphtory/src/python/types/macros/trait_impl/node_state.rs @@ -2,7 +2,9 @@ use crate::{ core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, db::{ api::{ - state::{LazyNodeState, NodeState, NodeStateOps, OrderedNodeStateOps}, + state::{ + ops::node::Degree, LazyNodeState, NodeState, NodeStateOps, OrderedNodeStateOps, + }, view::{DynamicGraph, GraphViewOps}, }, graph::node::NodeView, @@ -271,7 +273,7 @@ macro_rules! impl_node_state_num { }; } -impl_lazy_node_state_num!(LazyNodeStateUsize); +impl_lazy_node_state_num!(DegreeView>); impl_node_state_num!(NodeStateUsize); impl_lazy_node_state_num!(LazyNodeStateU64); diff --git a/raphtory/src/python/types/repr.rs b/raphtory/src/python/types/repr.rs index 16900697d..f11335048 100644 --- a/raphtory/src/python/types/repr.rs +++ b/raphtory/src/python/types/repr.rs @@ -1,6 +1,6 @@ use crate::{ core::storage::locked_view::LockedView, - db::api::state::{LazyNodeState, NodeState}, + db::api::state::{LazyNodeState, NodeOp, NodeState}, prelude::{GraphViewOps, NodeStateOps, NodeViewOps}, }; use chrono::{DateTime, NaiveDateTime, TimeZone}; @@ -221,12 +221,10 @@ impl<'a, R: Repr> Repr for &'a R { } } -impl< - 'graph, - G: GraphViewOps<'graph>, - GH: GraphViewOps<'graph>, - V: Repr + Clone + Send + Sync + 'graph, - > Repr for LazyNodeState<'graph, V, G, GH> +impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>, Op: NodeOp + 'graph> Repr + for LazyNodeState<'graph, Op, G, GH> +where + Op::Output: Repr + Send + Sync + 'graph, { fn repr(&self) -> String { StructReprBuilder::new("LazyNodeState") From ac69a22f0d72c884393331d607b6f29b855fd8b5 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 11 Nov 2024 15:07:44 +0100 Subject: [PATCH 06/23] fix python --- raphtory/src/db/api/state/lazy_node_state.rs | 39 +++++++++- raphtory/src/db/api/view/node.rs | 11 +-- raphtory/src/db/graph/node.rs | 5 +- raphtory/src/db/graph/nodes.rs | 15 +++- raphtory/src/db/graph/path.rs | 1 - raphtory/src/db/task/node/eval_node.rs | 1 - .../types/macros/trait_impl/node_state.rs | 74 +++++++++++-------- 7 files changed, 97 insertions(+), 49 deletions(-) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index 96dbe1a64..73d572c01 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -3,9 +3,8 @@ use crate::{ db::{ api::{ state::{ops::node::NodeOp, NodeState, NodeStateOps}, - storage::graph::{nodes::node_storage_ops::NodeStorageOps, storage_ops::GraphStorage}, view::{ - internal::{CoreGraphOps, NodeList, OneHopFilter}, + internal::{NodeList, OneHopFilter}, BoxedLIter, IntoDynBoxed, }, }, @@ -14,13 +13,24 @@ use crate::{ prelude::GraphViewOps, }; use rayon::prelude::*; -use std::{marker::PhantomData, sync::Arc}; +use std::fmt::{Debug, Formatter}; +#[derive(Clone)] pub struct LazyNodeState<'graph, Op, G, GH = G> { nodes: Nodes<'graph, G, GH>, op: Op, } +impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>, Op: NodeOp + 'graph> Debug + for LazyNodeState<'graph, Op, G, GH> +where + Op::Output: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.values()).finish() + } +} + impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> OneHopFilter<'graph> for LazyNodeState<'graph, Op, G, GH> { @@ -73,6 +83,29 @@ impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'gra pub fn collect_vec(&self) -> Vec { self.collect() } + + pub fn compute(&self) -> NodeState<'graph, Op::Output, G, GH> { + if self.nodes.is_filtered() { + let (keys, values): (Vec<_>, Vec<_>) = self + .par_iter() + .map(|(node, value)| (node.node, value)) + .unzip(); + NodeState::new( + self.nodes.base_graph.clone(), + self.nodes.graph.clone(), + values, + Some(keys.into()), + ) + } else { + let values = self.collect_vec(); + NodeState::new( + self.nodes.base_graph.clone(), + self.nodes.graph.clone(), + values, + None, + ) + } + } } impl<'graph, Op: NodeOp + 'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> diff --git a/raphtory/src/db/api/view/node.rs b/raphtory/src/db/api/view/node.rs index e23fe93d2..6097ae294 100644 --- a/raphtory/src/db/api/view/node.rs +++ b/raphtory/src/db/api/view/node.rs @@ -5,19 +5,14 @@ use crate::{ Direction, }, db::api::{ - properties::{internal::PropertiesOps, Properties}, + properties::internal::PropertiesOps, state::{ops, NodeOp}, - storage::graph::{nodes::node_storage_ops::NodeStorageOps, storage_ops::GraphStorage}, - view::{ - internal::{CoreGraphOps, OneHopFilter, TimeSemantics}, - reset_filter::ResetFilter, - TimeOps, - }, + storage::graph::storage_ops::GraphStorage, + view::{internal::OneHopFilter, reset_filter::ResetFilter, TimeOps}, }, prelude::{EdgeViewOps, GraphViewOps, LayerOps}, }; use chrono::{DateTime, Utc}; -use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; pub trait BaseNodeViewOps<'graph>: Clone + TimeOps<'graph> + LayerOps<'graph> { type BaseGraph: GraphViewOps<'graph>; diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index ad37f476d..1887c7d21 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -11,9 +11,8 @@ use crate::{ internal::{InternalAdditionOps, InternalPropertyAdditionOps}, time_from_input, CollectProperties, TryIntoInputTime, }, - properties::{ - internal::{ConstPropertiesOps, TemporalPropertiesOps, TemporalPropertyViewOps}, - Properties, + properties::internal::{ + ConstPropertiesOps, TemporalPropertiesOps, TemporalPropertyViewOps, }, view::{ internal::{CoreGraphOps, OneHopFilter, Static, TimeSemantics}, diff --git a/raphtory/src/db/graph/nodes.rs b/raphtory/src/db/graph/nodes.rs index 1896ff965..52905cf1d 100644 --- a/raphtory/src/db/graph/nodes.rs +++ b/raphtory/src/db/graph/nodes.rs @@ -2,7 +2,6 @@ use crate::{ core::entities::{edges::edge_ref::EdgeRef, nodes::node_ref::AsNodeRef, VID}, db::{ api::{ - properties::Properties, state::LazyNodeState, storage::graph::storage_ops::GraphStorage, view::{ @@ -17,7 +16,11 @@ use crate::{ use crate::db::{api::state::NodeOp, graph::create_node_type_filter}; use rayon::iter::ParallelIterator; -use std::{marker::PhantomData, sync::Arc}; +use std::{ + fmt::{Debug, Formatter}, + marker::PhantomData, + sync::Arc, +}; #[derive(Clone)] pub struct Nodes<'graph, G, GH = G> { @@ -27,6 +30,14 @@ pub struct Nodes<'graph, G, GH = G> { _marker: PhantomData<&'graph ()>, } +impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph> + Debug> Debug + for Nodes<'graph, G, GH> +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl<'graph, G, GH> From> for Nodes<'graph, DynamicGraph, DynamicGraph> where G: GraphViewOps<'graph> + IntoDynamic, diff --git a/raphtory/src/db/graph/path.rs b/raphtory/src/db/graph/path.rs index 2b8bdfc29..d897aae92 100644 --- a/raphtory/src/db/graph/path.rs +++ b/raphtory/src/db/graph/path.rs @@ -2,7 +2,6 @@ use crate::{ core::entities::{edges::edge_ref::EdgeRef, VID}, db::{ api::{ - properties::Properties, state::NodeOp, storage::graph::storage_ops::GraphStorage, view::{ diff --git a/raphtory/src/db/task/node/eval_node.rs b/raphtory/src/db/task/node/eval_node.rs index a535e9bea..6cb3cc396 100644 --- a/raphtory/src/db/task/node/eval_node.rs +++ b/raphtory/src/db/task/node/eval_node.rs @@ -10,7 +10,6 @@ use crate::{ }, db::{ api::{ - properties::Properties, state::NodeOp, storage::graph::storage_ops::GraphStorage, view::{internal::OneHopFilter, BaseNodeViewOps, BoxedLIter, IntoDynBoxed}, diff --git a/raphtory/src/python/types/macros/trait_impl/node_state.rs b/raphtory/src/python/types/macros/trait_impl/node_state.rs index 6c3b721a7..e47114354 100644 --- a/raphtory/src/python/types/macros/trait_impl/node_state.rs +++ b/raphtory/src/python/types/macros/trait_impl/node_state.rs @@ -2,9 +2,7 @@ use crate::{ core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, db::{ api::{ - state::{ - ops::node::Degree, LazyNodeState, NodeState, NodeStateOps, OrderedNodeStateOps, - }, + state::{ops, LazyNodeState, NodeOp, NodeState, NodeStateOps, OrderedNodeStateOps}, view::{DynamicGraph, GraphViewOps}, }, graph::node::NodeView, @@ -25,7 +23,7 @@ use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; use std::{collections::HashMap, sync::Arc}; macro_rules! impl_node_state_ops { - ($name:ident<$value:ty>, $inner_t:ty, $to_owned:expr) => { + ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr) => { impl $name { pub fn iter(&self) -> impl Iterator + '_ { self.inner.values().map($to_owned) @@ -93,7 +91,7 @@ macro_rules! impl_node_state_ops { } macro_rules! impl_node_state_ord_ops { - ($name:ident<$value:ty>, $to_owned:expr) => { + ($name:ident, $value:ty, $to_owned:expr) => { #[pymethods] impl $name { #[pyo3(signature = (reverse = false))] @@ -164,7 +162,7 @@ macro_rules! impl_node_state_ord_ops { } macro_rules! impl_node_state_num_ops { - ($name:ident<$value:ty>) => { + ($name:ident, $value:ty) => { #[pymethods] impl $name { fn sum(&self) -> $value { @@ -179,36 +177,39 @@ macro_rules! impl_node_state_num_ops { } macro_rules! impl_lazy_node_state { - ($name:ident<$value:ty>) => { + ($name:ident<$op:ty>) => { #[pyclass] pub struct $name { - inner: LazyNodeState<'static, $value, DynamicGraph, DynamicGraph>, + inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, } #[pymethods] impl $name { - fn compute(&self) -> NodeState<'static, $value, DynamicGraph, DynamicGraph> { + fn compute( + &self, + ) -> NodeState<'static, <$op as NodeOp>::Output, DynamicGraph, DynamicGraph> { self.inner.compute() } - fn collect(&self) -> Vec<$value> { + fn collect(&self) -> Vec<<$op as NodeOp>::Output> { self.inner.collect() } } impl_node_state_ops!( - $name<$value>, - LazyNodeState<'static, $value, DynamicGraph, DynamicGraph>, - |v: $value| v + $name, + <$op as NodeOp>::Output, + LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, + |v: <$op as NodeOp>::Output| v ); - impl From> for $name { - fn from(inner: LazyNodeState<'static, $value, DynamicGraph, DynamicGraph>) -> Self { + impl From> for $name { + fn from(inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>) -> Self { $name { inner } } } - impl pyo3::IntoPy for LazyNodeState<'static, $value, DynamicGraph, DynamicGraph> { + impl pyo3::IntoPy for LazyNodeState<'static, $op, DynamicGraph, DynamicGraph> { fn into_py(self, py: Python<'_>) -> PyObject { $name::from(self).into_py(py) } @@ -224,7 +225,8 @@ macro_rules! impl_node_state { } impl_node_state_ops!( - $name<$value>, + $name, + $value, Arc>, |v: &$value| v.clone() ); @@ -248,57 +250,67 @@ macro_rules! impl_node_state { macro_rules! impl_lazy_node_state_ord { ($name:ident<$value:ty>) => { impl_lazy_node_state!($name<$value>); - impl_node_state_ord_ops!($name<$value>, |v: $value| v); + impl_node_state_ord_ops!( + $name, + <$value as NodeOp>::Output, + |v: <$value as NodeOp>::Output| v + ); }; } macro_rules! impl_node_state_ord { ($name:ident<$value:ty>) => { impl_node_state!($name<$value>); - impl_node_state_ord_ops!($name<$value>, |v: &$value| v.clone()); + impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone()); }; } macro_rules! impl_lazy_node_state_num { ($name:ident<$value:ty>) => { impl_lazy_node_state_ord!($name<$value>); - impl_node_state_num_ops!($name<$value>); + impl_node_state_num_ops!($name, <$value as NodeOp>::Output); }; } macro_rules! impl_node_state_num { ($name:ident<$value:ty>) => { impl_node_state_ord!($name<$value>); - impl_node_state_num_ops!($name<$value>); + impl_node_state_num_ops!($name, $value); }; } -impl_lazy_node_state_num!(DegreeView>); +impl_lazy_node_state_num!(DegreeView>); impl_node_state_num!(NodeStateUsize); -impl_lazy_node_state_num!(LazyNodeStateU64); impl_node_state_num!(NodeStateU64); +impl_lazy_node_state_ord!(IdView); impl_node_state_ord!(NodeStateGID); -impl_lazy_node_state_ord!(LazyNodeStateGID); -impl_lazy_node_state_ord!(LazyNodeStateOptionI64>); +impl_lazy_node_state_ord!(EarliestTimeView>); +impl_lazy_node_state_ord!(LatestTimeView>); impl_node_state_ord!(NodeStateOptionI64>); -impl_lazy_node_state_ord!(LazyNodeStateString); +impl_lazy_node_state_ord!(NameView); impl_node_state_ord!(NodeStateString); -impl_lazy_node_state_ord!(LazyNodeStateOptionDateTime>>); +impl_lazy_node_state_ord!( + EarliestDateTimeView, Option>>> +); +impl_lazy_node_state_ord!( + LatestDateTimeView, Option>>> +); impl_node_state_ord!(NodeStateOptionDateTime>>); -impl_lazy_node_state_ord!(LazyNodeStateListI64>); +impl_lazy_node_state_ord!(HistoryView>); impl_node_state_ord!(NodeStateListI64>); -impl_lazy_node_state_ord!(LazyNodeStateOptionListDateTime>>>); +impl_lazy_node_state_ord!( + HistoryDateTimeView, Option>>>> +); impl_node_state_ord!(NodeStateOptionListDateTime>>>); -impl_lazy_node_state_ord!(LazyNodeStateOptionStr>); +impl_lazy_node_state_ord!(NodeTypeView); impl_node_state_ord!(NodeStateOptionStr>); -impl_lazy_node_state_ord!(LazyNodeStateListDateTime>>); impl_node_state_ord!(NodeStateListDateTime>>); From 846ae5e8fb9657999ce35f99a97faf5c7f5807c1 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Tue, 12 Nov 2024 13:22:35 +0100 Subject: [PATCH 07/23] fix intopy for degree --- raphtory/src/db/api/state/lazy_node_state.rs | 24 +++++-------------- raphtory/src/db/api/state/ops/mod.rs | 17 +++++++++++++ raphtory/src/db/api/state/ops/node.rs | 5 +++- .../src/db/api/view/internal/into_dynamic.rs | 13 +++++++++- raphtory/src/db/api/view/internal/mod.rs | 2 +- raphtory/src/db/api/view/mod.rs | 3 ++- raphtory/src/python/graph/edges.rs | 2 +- raphtory/src/python/graph/views/graph_view.rs | 2 +- .../types/macros/trait_impl/node_state.rs | 21 +++++++++++++++- .../python/types/macros/trait_impl/timeops.rs | 18 +++++++------- 10 files changed, 73 insertions(+), 34 deletions(-) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index 73d572c01..d3b4eb0e0 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -5,12 +5,12 @@ use crate::{ state::{ops::node::NodeOp, NodeState, NodeStateOps}, view::{ internal::{NodeList, OneHopFilter}, - BoxedLIter, IntoDynBoxed, + BoxedLIter, DynamicGraph, IntoDynBoxed, }, }, graph::{node::NodeView, nodes::Nodes}, }, - prelude::GraphViewOps, + prelude::*, }; use rayon::prelude::*; use std::fmt::{Debug, Formatter}; @@ -18,7 +18,7 @@ use std::fmt::{Debug, Formatter}; #[derive(Clone)] pub struct LazyNodeState<'graph, Op, G, GH = G> { nodes: Nodes<'graph, G, GH>, - op: Op, + pub(crate) op: Op, } impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>, Op: NodeOp + 'graph> Debug @@ -254,22 +254,10 @@ mod test { let nodes = g.nodes(); - let node_state = LazyNodeState { - nodes, - op: Degree { - graph: g.clone(), - dir: Direction::BOTH, - }, - }; - let node_state_window = node_state.after(1); - - let deg: Vec<_> = node_state.values().collect(); - let deg_w: Vec<_> = node_state_window.values().collect(); - - let node_state_filter = node_state.valid_layers("bla"); + let deg = g.nodes().degree(); - assert_eq!(deg, [1, 1]); - assert_eq!(deg_w, [0, 0]); + assert_eq!(deg.collect_vec(), [1, 1]); + assert_eq!(deg.after(1).collect_vec(), [0, 0]); let g_dyn = g.clone().into_dynamic(); diff --git a/raphtory/src/db/api/state/ops/mod.rs b/raphtory/src/db/api/state/ops/mod.rs index 9677afc53..f15c11e72 100644 --- a/raphtory/src/db/api/state/ops/mod.rs +++ b/raphtory/src/db/api/state/ops/mod.rs @@ -1,7 +1,24 @@ +macro_rules! impl_dynamic_from { + ($name:ident) => { + impl From<$name> + for $name + { + fn from(v: $name) -> Self { + let graph = v.current_filter().clone().into_dynamic(); + v.one_hop_filtered(graph) + } + } + }; +} + pub(crate) mod history; pub(crate) mod node; mod properties; +use crate::db::api::view::{ + internal::{OneHopFilter, Static}, + DynamicGraph, IntoDynamic, +}; pub use history::*; pub use node::*; pub use properties::*; diff --git a/raphtory/src/db/api/state/ops/node.rs b/raphtory/src/db/api/state/ops/node.rs index 82ccdab8b..84bd3cb86 100644 --- a/raphtory/src/db/api/state/ops/node.rs +++ b/raphtory/src/db/api/state/ops/node.rs @@ -1,7 +1,10 @@ use crate::{ db::api::{ storage::graph::storage_ops::GraphStorage, - view::internal::{CoreGraphOps, OneHopFilter}, + view::{ + internal::{CoreGraphOps, OneHopFilter, Static}, + IntoDynamic, + }, }, prelude::GraphViewOps, }; diff --git a/raphtory/src/db/api/view/internal/into_dynamic.rs b/raphtory/src/db/api/view/internal/into_dynamic.rs index 099454205..3fde32c8c 100644 --- a/raphtory/src/db/api/view/internal/into_dynamic.rs +++ b/raphtory/src/db/api/view/internal/into_dynamic.rs @@ -1,5 +1,5 @@ use crate::db::api::view::{ - internal::{DynamicGraph, Static}, + internal::{DynamicGraph, OneHopFilter, Static}, StaticGraphViewOps, }; @@ -18,3 +18,14 @@ impl IntoDynamic for DynamicGraph { self } } + +pub trait IntoDynHop: OneHopFilter<'static, FilteredGraph: IntoDynamic> { + fn into_dyn_hop(self) -> Self::Filtered; +} + +impl> IntoDynHop for T { + fn into_dyn_hop(self) -> Self::Filtered { + let graph = self.current_filter().clone().into_dynamic(); + self.one_hop_filtered(graph) + } +} diff --git a/raphtory/src/db/api/view/internal/mod.rs b/raphtory/src/db/api/view/internal/mod.rs index 744cbfc44..f4d263e07 100644 --- a/raphtory/src/db/api/view/internal/mod.rs +++ b/raphtory/src/db/api/view/internal/mod.rs @@ -27,7 +27,7 @@ pub use core_ops::*; pub use edge_filter_ops::*; pub use filter_ops::*; pub use inherit::Base; -pub use into_dynamic::IntoDynamic; +pub use into_dynamic::{IntoDynHop, IntoDynamic}; pub use layer_ops::{DelegateLayerOps, InheritLayerOps, InternalLayerOps}; pub use list_ops::*; pub use materialize::*; diff --git a/raphtory/src/db/api/view/mod.rs b/raphtory/src/db/api/view/mod.rs index 8456aa491..26f808953 100644 --- a/raphtory/src/db/api/view/mod.rs +++ b/raphtory/src/db/api/view/mod.rs @@ -18,7 +18,8 @@ pub use edge_property_filter::EdgePropertyFilterOps; pub use exploded_edge_property_filter::ExplodedEdgePropertyFilterOps; pub use graph::*; pub use internal::{ - Base, BoxableGraphView, DynamicGraph, InheritViewOps, IntoDynamic, MaterializedGraph, + Base, BoxableGraphView, DynamicGraph, InheritViewOps, IntoDynHop, IntoDynamic, + MaterializedGraph, }; pub use layer::*; pub(crate) use node::BaseNodeViewOps; diff --git a/raphtory/src/python/graph/edges.rs b/raphtory/src/python/graph/edges.rs index 429ae9cea..c643bc7e6 100644 --- a/raphtory/src/python/graph/edges.rs +++ b/raphtory/src/python/graph/edges.rs @@ -24,7 +24,7 @@ use crate::{ }, utils::{ export::{create_row, extract_properties, get_column_names_from_props}, - NumpyArray, PyGenericIterable, PyTime, + NumpyArray, PyGenericIterable, }, }, }; diff --git a/raphtory/src/python/graph/views/graph_view.rs b/raphtory/src/python/graph/views/graph_view.rs index 7a08c43e1..7a231034e 100644 --- a/raphtory/src/python/graph/views/graph_view.rs +++ b/raphtory/src/python/graph/views/graph_view.rs @@ -36,7 +36,7 @@ use crate::{ repr::{Repr, StructReprBuilder}, wrappers::prop::PyPropertyFilter, }, - utils::{PyNodeRef, PyTime}, + utils::PyNodeRef, }, }; use chrono::prelude::*; diff --git a/raphtory/src/python/types/macros/trait_impl/node_state.rs b/raphtory/src/python/types/macros/trait_impl/node_state.rs index e47114354..06875e66b 100644 --- a/raphtory/src/python/types/macros/trait_impl/node_state.rs +++ b/raphtory/src/python/types/macros/trait_impl/node_state.rs @@ -3,10 +3,14 @@ use crate::{ db::{ api::{ state::{ops, LazyNodeState, NodeOp, NodeState, NodeStateOps, OrderedNodeStateOps}, - view::{DynamicGraph, GraphViewOps}, + view::{ + internal::Static, DynamicGraph, GraphViewOps, IntoDynHop, IntoDynamic, + StaticGraphViewOps, + }, }, graph::node::NodeView, }, + prelude::*, py_borrowing_iter, python::{ types::{repr::Repr, wrappers::iterators::PyBorrowingIterator}, @@ -280,6 +284,21 @@ macro_rules! impl_node_state_num { } impl_lazy_node_state_num!(DegreeView>); + +impl IntoPy + for LazyNodeState<'static, ops::Degree, DynamicGraph, DynamicGraph> +{ + fn into_py(self, py: Python<'_>) -> PyObject { + self.into_dyn_hop().into_py(py) + } +} + +impl_timeops!( + DegreeView, + inner, + LazyNodeState<'static, ops::Degree, DynamicGraph>, + "DegreeView" +); impl_node_state_num!(NodeStateUsize); impl_node_state_num!(NodeStateU64); diff --git a/raphtory/src/python/types/macros/trait_impl/timeops.rs b/raphtory/src/python/types/macros/trait_impl/timeops.rs index 143583fb0..ae2e522f0 100644 --- a/raphtory/src/python/types/macros/trait_impl/timeops.rs +++ b/raphtory/src/python/types/macros/trait_impl/timeops.rs @@ -95,8 +95,8 @@ macro_rules! impl_timeops { #[doc = concat!("r A ", $name, " object.")] pub fn window( &self, - start: PyTime, - end: PyTime, + start: $crate::python::utils::PyTime, + end: $crate::python::utils::PyTime, ) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field .window(start, end) @@ -109,7 +109,7 @@ macro_rules! impl_timeops { /// /// Returns: #[doc = concat!(r" A ", $name, r" object.")] - pub fn at(&self, time: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + pub fn at(&self, time: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.at(time) } @@ -130,7 +130,7 @@ macro_rules! impl_timeops { /// /// Returns: #[doc = concat!(r" A ", $name, r" object.")] - pub fn snapshot_at(&self, time: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + pub fn snapshot_at(&self, time: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.snapshot_at(time) } @@ -151,7 +151,7 @@ macro_rules! impl_timeops { /// /// Returns: #[doc = concat!(r" A ", $name, r" object.")] - pub fn before(&self, end: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + pub fn before(&self, end: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.before(end) } @@ -162,7 +162,7 @@ macro_rules! impl_timeops { /// /// Returns: #[doc = concat!(r" A ", $name, r" object.")] - pub fn after(&self, start: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + pub fn after(&self, start: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.after(start) } @@ -173,7 +173,7 @@ macro_rules! impl_timeops { /// /// Returns: #[doc = concat!(r" A ", $name, r" object.")] - pub fn shrink_start(&self, start: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + pub fn shrink_start(&self, start: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.shrink_start(start) } @@ -183,7 +183,7 @@ macro_rules! impl_timeops { /// end (TimeInput): the new end time of the window /// Returns: #[doc = concat!(r" A ", $name, r" object.")] - fn shrink_end(&self, end: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + fn shrink_end(&self, end: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.shrink_end(end) } @@ -193,7 +193,7 @@ macro_rules! impl_timeops { /// start (TimeInput): the new start time for the window /// end (TimeInput): the new end time for the window /// - fn shrink_window(&self, start: PyTime, end: PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { + fn shrink_window(&self, start: $crate::python::utils::PyTime, end: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.shrink_window(start, end) } } From 7d10e582e29a5b948665fdd5224074c2a154c89f Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 12:19:51 +0100 Subject: [PATCH 08/23] refactor the filtering on NodeOp so it can work for Map --- .../motifs/temporal_rich_club_coefficient.rs | 4 +- raphtory/src/db/api/state/lazy_node_state.rs | 13 +++--- raphtory/src/db/api/state/ops/history.rs | 43 +++++++------------ raphtory/src/db/api/state/ops/node.rs | 41 ++++++++++++++---- .../types/macros/trait_impl/node_state.rs | 29 +++++++------ raphtory/src/vectors/vector_selection.rs | 1 - 6 files changed, 73 insertions(+), 58 deletions(-) diff --git a/raphtory/src/algorithms/motifs/temporal_rich_club_coefficient.rs b/raphtory/src/algorithms/motifs/temporal_rich_club_coefficient.rs index 999c3bd6a..4c5103c32 100644 --- a/raphtory/src/algorithms/motifs/temporal_rich_club_coefficient.rs +++ b/raphtory/src/algorithms/motifs/temporal_rich_club_coefficient.rs @@ -56,7 +56,7 @@ where .collect(); if s_k.len() <= 1 { - return 0.0 as f64; + return 0.0f64; } let temp_rich_club_val = SlidingWindows::new(views.into_iter(), window_size) @@ -93,7 +93,7 @@ where let poss_edges = (s_k.len() * (s_k.len() - 1)) / 2; return (edges.len() as f64) / (poss_edges as f64); } - None => return 0 as f64, + None => return 0f64, } } diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index d3b4eb0e0..fb00def11 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -2,7 +2,10 @@ use crate::{ core::entities::{nodes::node_ref::AsNodeRef, VID}, db::{ api::{ - state::{ops::node::NodeOp, NodeState, NodeStateOps}, + state::{ + ops::{node::NodeOp, NodeOpFilter}, + NodeState, NodeStateOps, + }, view::{ internal::{NodeList, OneHopFilter}, BoxedLIter, DynamicGraph, IntoDynBoxed, @@ -31,16 +34,16 @@ where } } -impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> +impl<'graph, Op: NodeOpFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> OneHopFilter<'graph> for LazyNodeState<'graph, Op, G, GH> { type BaseGraph = G; - type FilteredGraph = Op::FilteredGraph; + type FilteredGraph = Op::Graph; type Filtered + 'graph> = LazyNodeState<'graph, Op::Filtered, G, GH>; fn current_filter(&self) -> &Self::FilteredGraph { - self.op.current_filter() + self.op.graph() } fn base_graph(&self) -> &Self::BaseGraph { @@ -53,7 +56,7 @@ impl<'graph, Op: OneHopFilter<'graph>, G: GraphViewOps<'graph>, GH: GraphViewOps ) -> Self::Filtered { LazyNodeState { nodes: self.nodes.clone(), - op: self.op.one_hop_filtered(filtered_graph), + op: self.op.filtered(filtered_graph), } } } diff --git a/raphtory/src/db/api/state/ops/history.rs b/raphtory/src/db/api/state/ops/history.rs index 31eb7ada5..5867700af 100644 --- a/raphtory/src/db/api/state/ops/history.rs +++ b/raphtory/src/db/api/state/ops/history.rs @@ -1,6 +1,8 @@ use crate::{ db::api::{ - state::NodeOp, storage::graph::storage_ops::GraphStorage, view::internal::OneHopFilter, + state::{ops::NodeOpFilter, NodeOp}, + storage::graph::storage_ops::GraphStorage, + view::internal::OneHopFilter, }, prelude::GraphViewOps, }; @@ -19,20 +21,15 @@ impl<'graph, G: GraphViewOps<'graph>> NodeOp for EarliestTime { } } -impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for EarliestTime { - type BaseGraph = G; - type FilteredGraph = G; +impl<'graph, G: GraphViewOps<'graph>> NodeOpFilter<'graph> for EarliestTime { + type Graph = G; type Filtered + 'graph> = EarliestTime; - fn current_filter(&self) -> &Self::FilteredGraph { + fn graph(&self) -> &Self::Graph { &self.graph } - fn base_graph(&self) -> &Self::BaseGraph { - &self.graph - } - - fn one_hop_filtered + 'graph>( + fn filtered + 'graph>( &self, filtered_graph: GH, ) -> Self::Filtered { @@ -55,20 +52,15 @@ impl<'graph, G: GraphViewOps<'graph>> NodeOp for LatestTime { } } -impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for LatestTime { - type BaseGraph = G; - type FilteredGraph = G; +impl<'graph, G: GraphViewOps<'graph>> NodeOpFilter<'graph> for LatestTime { + type Graph = G; type Filtered + 'graph> = LatestTime; - fn current_filter(&self) -> &Self::FilteredGraph { + fn graph(&self) -> &Self::Graph { &self.graph } - fn base_graph(&self) -> &Self::BaseGraph { - &self.graph - } - - fn one_hop_filtered + 'graph>( + fn filtered + 'graph>( &self, filtered_graph: GH, ) -> Self::Filtered { @@ -91,20 +83,15 @@ impl<'graph, G: GraphViewOps<'graph>> NodeOp for History { } } -impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for History { - type BaseGraph = G; - type FilteredGraph = G; +impl<'graph, G: GraphViewOps<'graph>> NodeOpFilter<'graph> for History { + type Graph = G; type Filtered + 'graph> = History; - fn current_filter(&self) -> &Self::FilteredGraph { - &self.graph - } - - fn base_graph(&self) -> &Self::BaseGraph { + fn graph(&self) -> &Self::Graph { &self.graph } - fn one_hop_filtered + 'graph>( + fn filtered + 'graph>( &self, filtered_graph: GH, ) -> Self::Filtered { diff --git a/raphtory/src/db/api/state/ops/node.rs b/raphtory/src/db/api/state/ops/node.rs index 84bd3cb86..2debbbabe 100644 --- a/raphtory/src/db/api/state/ops/node.rs +++ b/raphtory/src/db/api/state/ops/node.rs @@ -27,6 +27,18 @@ pub trait NodeOp: Send + Sync { } } +// Cannot use OneHopFilter because there is no way to specify the bound on Output +pub trait NodeOpFilter<'graph>: NodeOp + 'graph { + type Graph: GraphViewOps<'graph>; + type Filtered>: NodeOp + + NodeOpFilter<'graph, Graph = G> + + 'graph; + + fn graph(&self) -> &Self::Graph; + + fn filtered>(&self, graph: G) -> Self::Filtered; +} + #[derive(Debug, Clone, Copy)] pub struct Name; @@ -83,20 +95,15 @@ impl<'graph, G: GraphViewOps<'graph>> NodeOp for Degree { } } -impl<'graph, G: GraphViewOps<'graph>> OneHopFilter<'graph> for Degree { - type BaseGraph = G; - type FilteredGraph = G; +impl<'graph, G: GraphViewOps<'graph>> NodeOpFilter<'graph> for Degree { + type Graph = G; type Filtered + 'graph> = Degree; - fn current_filter(&self) -> &Self::FilteredGraph { - &self.graph - } - - fn base_graph(&self) -> &Self::BaseGraph { + fn graph(&self) -> &Self::Graph { &self.graph } - fn one_hop_filtered + 'graph>( + fn filtered + 'graph>( &self, filtered_graph: GH, ) -> Self::Filtered { @@ -127,3 +134,19 @@ impl NodeOp for Map { (self.map)(self.op.apply(storage, node)) } } + +impl<'graph, Op: NodeOpFilter<'graph>, V: Clone + Send + Sync + 'graph> NodeOpFilter<'graph> + for Map +{ + type Graph = Op::Graph; + type Filtered> = Map, V>; + + fn graph(&self) -> &Self::Graph { + self.op.graph() + } + + fn filtered>(&self, graph: G) -> Self::Filtered { + let op = self.op.filtered(graph); + Map { op, map: self.map } + } +} diff --git a/raphtory/src/python/types/macros/trait_impl/node_state.rs b/raphtory/src/python/types/macros/trait_impl/node_state.rs index 06875e66b..6e17b0df7 100644 --- a/raphtory/src/python/types/macros/trait_impl/node_state.rs +++ b/raphtory/src/python/types/macros/trait_impl/node_state.rs @@ -283,22 +283,23 @@ macro_rules! impl_node_state_num { }; } -impl_lazy_node_state_num!(DegreeView>); - -impl IntoPy - for LazyNodeState<'static, ops::Degree, DynamicGraph, DynamicGraph> -{ - fn into_py(self, py: Python<'_>) -> PyObject { - self.into_dyn_hop().into_py(py) +macro_rules! impl_one_hop { + ($name:ident<$($path:ident)::+>) => { + impl IntoPy + for LazyNodeState<'static, $($path)::+, DynamicGraph, DynamicGraph> + { + fn into_py(self, py: Python<'_>) -> PyObject { + self.into_dyn_hop().into_py(py) + } + } + impl_timeops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>,"DegreeView"); + impl_layerops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>,"DegreeView"); } } -impl_timeops!( - DegreeView, - inner, - LazyNodeState<'static, ops::Degree, DynamicGraph>, - "DegreeView" -); +impl_lazy_node_state_num!(DegreeView>); +impl_one_hop!(DegreeView); + impl_node_state_num!(NodeStateUsize); impl_node_state_num!(NodeStateU64); @@ -307,7 +308,9 @@ impl_lazy_node_state_ord!(IdView); impl_node_state_ord!(NodeStateGID); impl_lazy_node_state_ord!(EarliestTimeView>); +impl_one_hop!(EarliestTimeView); impl_lazy_node_state_ord!(LatestTimeView>); +impl_one_hop!(LatestTimeView); impl_node_state_ord!(NodeStateOptionI64>); impl_lazy_node_state_ord!(NameView); diff --git a/raphtory/src/vectors/vector_selection.rs b/raphtory/src/vectors/vector_selection.rs index 371a386e6..ea41b4cc8 100644 --- a/raphtory/src/vectors/vector_selection.rs +++ b/raphtory/src/vectors/vector_selection.rs @@ -2,7 +2,6 @@ use itertools::{chain, Itertools}; use std::{ collections::{HashMap, HashSet}, ops::Deref, - usize, }; use crate::{ From bfe4ece38f0c7077a55f544a80c69c2306602030 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 12:31:47 +0100 Subject: [PATCH 09/23] implement the filters in python --- .../python/types/macros/trait_impl/node_state.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/raphtory/src/python/types/macros/trait_impl/node_state.rs b/raphtory/src/python/types/macros/trait_impl/node_state.rs index 6e17b0df7..0ba6c0708 100644 --- a/raphtory/src/python/types/macros/trait_impl/node_state.rs +++ b/raphtory/src/python/types/macros/trait_impl/node_state.rs @@ -316,20 +316,24 @@ impl_node_state_ord!(NodeStateOptionI64>); impl_lazy_node_state_ord!(NameView); impl_node_state_ord!(NodeStateString); -impl_lazy_node_state_ord!( - EarliestDateTimeView, Option>>> -); +type EarliestDateTime = ops::Map, Option>>; +impl_lazy_node_state_ord!(EarliestDateTimeView>); +impl_one_hop!(EarliestDateTimeView); + +type LatestDateTime = ops::Map, Option>>; impl_lazy_node_state_ord!( LatestDateTimeView, Option>>> ); +impl_one_hop!(LatestDateTimeView); impl_node_state_ord!(NodeStateOptionDateTime>>); impl_lazy_node_state_ord!(HistoryView>); +impl_one_hop!(HistoryView); impl_node_state_ord!(NodeStateListI64>); -impl_lazy_node_state_ord!( - HistoryDateTimeView, Option>>>> -); +type HistoryDateTime = ops::Map, Option>>>; +impl_lazy_node_state_ord!(HistoryDateTimeView>); +impl_one_hop!(HistoryDateTimeView); impl_node_state_ord!(NodeStateOptionListDateTime>>>); impl_lazy_node_state_ord!(NodeTypeView); From 19681824cbb9a1bcc245fdb76d5d3e9d1fd4101e Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 13:15:29 +0100 Subject: [PATCH 10/23] cleanup --- raphtory/src/db/api/state/lazy_node_state.rs | 2 +- raphtory/src/db/api/state/ops/history.rs | 1 - raphtory/src/db/api/state/ops/mod.rs | 17 ----------------- raphtory/src/db/api/state/ops/node.rs | 5 +---- raphtory/src/python/graph/mod.rs | 1 + .../macros/trait_impl => graph}/node_state.rs | 0 .../src/python/types/macros/trait_impl/mod.rs | 2 -- 7 files changed, 3 insertions(+), 25 deletions(-) rename raphtory/src/python/{types/macros/trait_impl => graph}/node_state.rs (100%) diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index fb00def11..4a3f60d22 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -8,7 +8,7 @@ use crate::{ }, view::{ internal::{NodeList, OneHopFilter}, - BoxedLIter, DynamicGraph, IntoDynBoxed, + BoxedLIter, IntoDynBoxed, }, }, graph::{node::NodeView, nodes::Nodes}, diff --git a/raphtory/src/db/api/state/ops/history.rs b/raphtory/src/db/api/state/ops/history.rs index 5867700af..1904f03cb 100644 --- a/raphtory/src/db/api/state/ops/history.rs +++ b/raphtory/src/db/api/state/ops/history.rs @@ -2,7 +2,6 @@ use crate::{ db::api::{ state::{ops::NodeOpFilter, NodeOp}, storage::graph::storage_ops::GraphStorage, - view::internal::OneHopFilter, }, prelude::GraphViewOps, }; diff --git a/raphtory/src/db/api/state/ops/mod.rs b/raphtory/src/db/api/state/ops/mod.rs index f15c11e72..9677afc53 100644 --- a/raphtory/src/db/api/state/ops/mod.rs +++ b/raphtory/src/db/api/state/ops/mod.rs @@ -1,24 +1,7 @@ -macro_rules! impl_dynamic_from { - ($name:ident) => { - impl From<$name> - for $name - { - fn from(v: $name) -> Self { - let graph = v.current_filter().clone().into_dynamic(); - v.one_hop_filtered(graph) - } - } - }; -} - pub(crate) mod history; pub(crate) mod node; mod properties; -use crate::db::api::view::{ - internal::{OneHopFilter, Static}, - DynamicGraph, IntoDynamic, -}; pub use history::*; pub use node::*; pub use properties::*; diff --git a/raphtory/src/db/api/state/ops/node.rs b/raphtory/src/db/api/state/ops/node.rs index 2debbbabe..40be5b1fa 100644 --- a/raphtory/src/db/api/state/ops/node.rs +++ b/raphtory/src/db/api/state/ops/node.rs @@ -1,10 +1,7 @@ use crate::{ db::api::{ storage::graph::storage_ops::GraphStorage, - view::{ - internal::{CoreGraphOps, OneHopFilter, Static}, - IntoDynamic, - }, + view::internal::{CoreGraphOps, OneHopFilter}, }, prelude::GraphViewOps, }; diff --git a/raphtory/src/python/graph/mod.rs b/raphtory/src/python/graph/mod.rs index 008b47086..39bc5381a 100644 --- a/raphtory/src/python/graph/mod.rs +++ b/raphtory/src/python/graph/mod.rs @@ -10,5 +10,6 @@ pub mod edges; pub mod index; pub mod io; pub mod node; +pub mod node_state; pub mod properties; pub mod views; diff --git a/raphtory/src/python/types/macros/trait_impl/node_state.rs b/raphtory/src/python/graph/node_state.rs similarity index 100% rename from raphtory/src/python/types/macros/trait_impl/node_state.rs rename to raphtory/src/python/graph/node_state.rs diff --git a/raphtory/src/python/types/macros/trait_impl/mod.rs b/raphtory/src/python/types/macros/trait_impl/mod.rs index 89c1a8f03..e5dba46e7 100644 --- a/raphtory/src/python/types/macros/trait_impl/mod.rs +++ b/raphtory/src/python/types/macros/trait_impl/mod.rs @@ -15,8 +15,6 @@ mod repr; #[macro_use] mod iterable_mixin; -mod node_state; - #[macro_use] mod serialise; From 3a7ccd578c83ca7c6053741d46191953d5d78f6a Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 14:35:21 +0100 Subject: [PATCH 11/23] clean up module annotations --- raphtory/src/python/graph/algorithm_result.rs | 2 +- raphtory/src/python/graph/edge.rs | 4 +-- raphtory/src/python/graph/edges.rs | 2 +- raphtory/src/python/graph/graph.rs | 6 ++-- .../src/python/graph/graph_with_deletions.rs | 2 +- raphtory/src/python/graph/index.rs | 2 +- raphtory/src/python/graph/node.rs | 6 ++-- .../python/graph/properties/constant_props.rs | 2 +- raphtory/src/python/graph/properties/props.rs | 2 +- .../python/graph/properties/temporal_props.rs | 4 +-- raphtory/src/python/graph/views/graph_view.rs | 2 +- .../python/types/macros/trait_impl/timeops.rs | 29 ++++++++++--------- raphtory/src/python/types/wrappers/prop.rs | 4 +-- raphtory/src/python/utils/mod.rs | 2 +- 14 files changed, 36 insertions(+), 33 deletions(-) diff --git a/raphtory/src/python/graph/algorithm_result.rs b/raphtory/src/python/graph/algorithm_result.rs index 6e3402ee3..3cf3b6f74 100644 --- a/raphtory/src/python/graph/algorithm_result.rs +++ b/raphtory/src/python/graph/algorithm_result.rs @@ -23,7 +23,7 @@ impl Repr for AlgorithmResultRs { - #[pyclass] + #[pyclass(module = "raphtory", frozen)] pub struct $objectName( $crate::algorithms::algorithm_result::AlgorithmResult< $rustGraph, diff --git a/raphtory/src/python/graph/edge.rs b/raphtory/src/python/graph/edge.rs index d0399c2c6..277b52837 100644 --- a/raphtory/src/python/graph/edge.rs +++ b/raphtory/src/python/graph/edge.rs @@ -32,13 +32,13 @@ use std::{ /// PyEdge is a Python class that represents an edge in the graph. /// An edge is a directed connection between two nodes. -#[pyclass(name = "Edge", subclass)] +#[pyclass(name = "Edge", subclass, module = "raphtory", frozen)] #[derive(Clone)] pub struct PyEdge { pub(crate) edge: EdgeView, } -#[pyclass(name="MutableEdge", extends=PyEdge)] +#[pyclass(name="MutableEdge", extends=PyEdge, module="raphtory", frozen)] pub struct PyMutableEdge { edge: EdgeView, } diff --git a/raphtory/src/python/graph/edges.rs b/raphtory/src/python/graph/edges.rs index c643bc7e6..6ee19100b 100644 --- a/raphtory/src/python/graph/edges.rs +++ b/raphtory/src/python/graph/edges.rs @@ -34,7 +34,7 @@ use rayon::{iter::IntoParallelIterator, prelude::*}; use std::collections::HashMap; /// A list of edges that can be iterated over. -#[pyclass(name = "Edges")] +#[pyclass(name = "Edges", module = "raphtory", frozen)] pub struct PyEdges { edges: Edges<'static, DynamicGraph>, } diff --git a/raphtory/src/python/graph/graph.rs b/raphtory/src/python/graph/graph.rs index 5f4849017..09dca54a2 100644 --- a/raphtory/src/python/graph/graph.rs +++ b/raphtory/src/python/graph/graph.rs @@ -34,7 +34,7 @@ use std::{ /// Arguments: /// num_shards (int, optional): The number of locks to use in the storage to allow for multithreaded updates. #[derive(Clone)] -#[pyclass(name = "Graph", extends = PyGraphView, module = "raphtory")] +#[pyclass(name = "Graph", extends = PyGraphView, module = "raphtory", frozen)] pub struct PyGraph { pub graph: Graph, } @@ -118,7 +118,7 @@ impl PyGraph { } } -#[pyclass(module = "raphtory")] +#[pyclass(module = "raphtory", frozen)] pub struct PyGraphEncoder; #[pymethods] @@ -131,7 +131,7 @@ impl PyGraphEncoder { fn __call__(&self, bytes: Vec) -> Result { MaterializedGraph::decode_from_bytes(&bytes) } - fn __setstate__(&mut self) {} + fn __setstate__(&self) {} fn __getstate__(&self) {} } diff --git a/raphtory/src/python/graph/graph_with_deletions.rs b/raphtory/src/python/graph/graph_with_deletions.rs index e3f78469b..eb12e305a 100644 --- a/raphtory/src/python/graph/graph_with_deletions.rs +++ b/raphtory/src/python/graph/graph_with_deletions.rs @@ -36,7 +36,7 @@ use std::{ /// A temporal graph that allows edges and nodes to be deleted. #[derive(Clone)] -#[pyclass(name = "PersistentGraph", extends = PyGraphView, frozen)] +#[pyclass(name = "PersistentGraph", extends = PyGraphView, frozen, module="raphtory")] pub struct PyPersistentGraph { pub(crate) graph: PersistentGraph, } diff --git a/raphtory/src/python/graph/index.rs b/raphtory/src/python/graph/index.rs index e06f25308..0e9b6d83e 100644 --- a/raphtory/src/python/graph/index.rs +++ b/raphtory/src/python/graph/index.rs @@ -24,7 +24,7 @@ impl PyGraphView { /// A searchable Index for a `Graph`. This allows for fuzzy and exact searches of nodes and edges. /// This makes use of Tantivity internally to provide the search functionality. /// To create a graph index, call `graph.index()` on any `Graph` object in python. -#[pyclass] +#[pyclass(frozen, module = "raphtory")] pub struct GraphIndex { graph: IndexedGraph, } diff --git a/raphtory/src/python/graph/node.rs b/raphtory/src/python/graph/node.rs index 8f6b2a725..2758bf1b2 100644 --- a/raphtory/src/python/graph/node.rs +++ b/raphtory/src/python/graph/node.rs @@ -61,7 +61,7 @@ use rayon::{iter::IntoParallelIterator, prelude::*}; use std::collections::HashMap; /// A node (or node) in the graph. -#[pyclass(name = "Node", subclass)] +#[pyclass(name = "Node", subclass, module = "raphtory", frozen)] #[derive(Clone)] pub struct PyNode { pub node: NodeView, @@ -295,7 +295,7 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> Repr for NodeVie } } -#[pyclass(name = "MutableNode", extends = PyNode)] +#[pyclass(name = "MutableNode", extends = PyNode, module="raphtory", frozen)] pub struct PyMutableNode { node: NodeView, } @@ -423,7 +423,7 @@ impl PyMutableNode { } /// A list of nodes that can be iterated over. -#[pyclass(name = "Nodes")] +#[pyclass(name = "Nodes", module = "raphtory", frozen)] pub struct PyNodes { pub(crate) nodes: Nodes<'static, DynamicGraph, DynamicGraph>, } diff --git a/raphtory/src/python/graph/properties/constant_props.rs b/raphtory/src/python/graph/properties/constant_props.rs index 622208b19..d177a0e22 100644 --- a/raphtory/src/python/graph/properties/constant_props.rs +++ b/raphtory/src/python/graph/properties/constant_props.rs @@ -33,7 +33,7 @@ impl<'a, P: PropertiesOps> Repr for ConstProperties<'a, P> { } /// A view of constant properties of an entity -#[pyclass(name = "ConstProperties")] +#[pyclass(name = "ConstProperties", module = "raphtory", frozen)] pub struct PyConstProperties { props: DynConstProperties, } diff --git a/raphtory/src/python/graph/properties/props.rs b/raphtory/src/python/graph/properties/props.rs index 4082bc625..6c586753c 100644 --- a/raphtory/src/python/graph/properties/props.rs +++ b/raphtory/src/python/graph/properties/props.rs @@ -76,7 +76,7 @@ impl From for PyPropsComp { } /// A view of the properties of an entity -#[pyclass(name = "Properties")] +#[pyclass(name = "Properties", module = "raphtory", frozen)] pub struct PyProperties { props: DynProperties, } diff --git a/raphtory/src/python/graph/properties/temporal_props.rs b/raphtory/src/python/graph/properties/temporal_props.rs index 67bd03a91..181e536d7 100644 --- a/raphtory/src/python/graph/properties/temporal_props.rs +++ b/raphtory/src/python/graph/properties/temporal_props.rs @@ -75,7 +75,7 @@ impl<'source> FromPyObject<'source> for PyTemporalPropsCmp { } /// A view of the temporal properties of an entity -#[pyclass(name = "TemporalProperties")] +#[pyclass(name = "TemporalProperties", module = "raphtory", frozen)] pub struct PyTemporalProperties { props: DynTemporalProperties, } @@ -180,7 +180,7 @@ impl PyTemporalProperties { } /// A view of a temporal property -#[pyclass(name = "TemporalProp")] +#[pyclass(name = "TemporalProp", module = "raphtory", frozen)] pub struct PyTemporalProp { prop: DynTemporalProperty, } diff --git a/raphtory/src/python/graph/views/graph_view.rs b/raphtory/src/python/graph/views/graph_view.rs index 7a231034e..45cf935ff 100644 --- a/raphtory/src/python/graph/views/graph_view.rs +++ b/raphtory/src/python/graph/views/graph_view.rs @@ -67,7 +67,7 @@ impl<'source> FromPyObject<'source> for DynamicGraph { } /// Graph view is a read-only version of a graph at a certain point in time. -#[pyclass(name = "GraphView", frozen, subclass)] +#[pyclass(name = "GraphView", frozen, subclass, module = "raphtory")] #[derive(Clone)] #[repr(C)] pub struct PyGraphView { diff --git a/raphtory/src/python/types/macros/trait_impl/timeops.rs b/raphtory/src/python/types/macros/trait_impl/timeops.rs index ae2e522f0..b4969e763 100644 --- a/raphtory/src/python/types/macros/trait_impl/timeops.rs +++ b/raphtory/src/python/types/macros/trait_impl/timeops.rs @@ -13,7 +13,7 @@ macro_rules! impl_timeops { #[doc = concat!(r" Gets the start time for rolling and expanding windows for this ", $name)] /// /// Returns: - #[doc = concat!(r" The earliest time that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] + #[doc = concat!(r" Optional[int]: The earliest time that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] #[getter] pub fn start(&self) -> Option { self.$field.start() @@ -22,7 +22,7 @@ macro_rules! impl_timeops { #[doc = concat!(r" Gets the earliest datetime that this ", $name, r" is valid")] /// /// Returns: - #[doc = concat!(r" The earliest datetime that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] + #[doc = concat!(r" Optional[Datetime]: The earliest datetime that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] #[getter] pub fn start_date_time(&self) -> Option> { self.$field.start_date_time() @@ -31,7 +31,7 @@ macro_rules! impl_timeops { #[doc = concat!(r" Gets the latest time that this ", $name, r" is valid.")] /// /// Returns: - #[doc = concat!(" The latest time that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] + #[doc = concat!(" Optional[int]: The latest time that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] #[getter] pub fn end(&self) -> Option { self.$field.end() @@ -40,13 +40,16 @@ macro_rules! impl_timeops { #[doc = concat!(r" Gets the latest datetime that this ", $name, r" is valid")] /// /// Returns: - #[doc = concat!(r" The latest datetime that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] + #[doc = concat!(r" Optional[Datetime]: The latest datetime that this ", $name, r" is valid or None if the ", $name, r" is valid for all times.")] #[getter] pub fn end_date_time(&self) -> Option> { self.$field.end_date_time() } #[doc = concat!(r" Get the window size (difference between start and end) for this ", $name)] + /// + /// Returns: + /// Optional[int] #[getter] pub fn window_size(&self) -> Option { self.$field.window_size() @@ -92,7 +95,7 @@ macro_rules! impl_timeops { /// end (TimeInput | None): The end time of the window (unbounded if `None`). /// /// Returns: - #[doc = concat!("r A ", $name, " object.")] + #[doc = concat!("r ", $name)] pub fn window( &self, start: $crate::python::utils::PyTime, @@ -108,7 +111,7 @@ macro_rules! impl_timeops { /// time (TimeInput): The time of the window. /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn at(&self, time: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.at(time) } @@ -116,7 +119,7 @@ macro_rules! impl_timeops { #[doc = concat!(r" Create a view of the ", $name, r" including all events at the latest time.")] /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn latest(&self) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.latest() } @@ -129,7 +132,7 @@ macro_rules! impl_timeops { /// time (TimeInput): The time of the window. /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn snapshot_at(&self, time: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.snapshot_at(time) } @@ -139,7 +142,7 @@ macro_rules! impl_timeops { /// This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn snapshot_latest(&self) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.snapshot_latest() } @@ -150,7 +153,7 @@ macro_rules! impl_timeops { /// end (TimeInput): The end time of the window. /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn before(&self, end: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.before(end) } @@ -161,7 +164,7 @@ macro_rules! impl_timeops { /// start (TimeInput): The start time of the window. /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn after(&self, start: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.after(start) } @@ -172,7 +175,7 @@ macro_rules! impl_timeops { /// start (TimeInput): the new start time of the window /// /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] pub fn shrink_start(&self, start: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.shrink_start(start) } @@ -182,7 +185,7 @@ macro_rules! impl_timeops { /// Arguments: /// end (TimeInput): the new end time of the window /// Returns: - #[doc = concat!(r" A ", $name, r" object.")] + #[doc = concat!(r" ", $name)] fn shrink_end(&self, end: $crate::python::utils::PyTime) -> <$base_type as TimeOps<'static>>::WindowedViewType { self.$field.shrink_end(end) } diff --git a/raphtory/src/python/types/wrappers/prop.rs b/raphtory/src/python/types/wrappers/prop.rs index 61a790d81..d449154e6 100644 --- a/raphtory/src/python/types/wrappers/prop.rs +++ b/raphtory/src/python/types/wrappers/prop.rs @@ -131,7 +131,7 @@ impl Repr for Prop { pub type PropValue = Option; pub type PropHistItems = Vec<(i64, Prop)>; -#[pyclass(frozen, name = "PropertyFilter")] +#[pyclass(frozen, name = "PropertyFilter", module = "raphtory")] #[derive(Clone)] pub struct PyPropertyFilter(PropertyFilter); @@ -186,7 +186,7 @@ impl InternalNodePropertyFilterOps for PyPropertyFilter { /// property value (these filters always exclude entities that do not /// have the property) or use one of the methods to construct /// other kinds of filters. -#[pyclass(frozen, name = "Prop")] +#[pyclass(frozen, name = "Prop", module = "raphtory")] #[derive(Clone)] pub struct PyPropertyRef { name: String, diff --git a/raphtory/src/python/utils/mod.rs b/raphtory/src/python/utils/mod.rs index 59811d87f..32553f95d 100644 --- a/raphtory/src/python/utils/mod.rs +++ b/raphtory/src/python/utils/mod.rs @@ -238,7 +238,7 @@ where } } -#[pyclass(name = "WindowSet")] +#[pyclass(name = "WindowSet", module = "raphtory", frozen)] pub struct PyWindowSet { window_set: Box, } From 7c31e3ca40626babc73bd614d7f94978125dc32a Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 17:53:39 +0100 Subject: [PATCH 12/23] add docs --- python/python/raphtory/__init__.pyi | 165 ++++++------ python/python/raphtory/typing.py | 2 + raphtory/src/db/api/state/ops/node.rs | 5 +- .../python/algorithm/max_weight_matching.rs | 2 +- raphtory/src/python/graph/node_state.rs | 236 ++++++++++++++---- 5 files changed, 284 insertions(+), 126 deletions(-) diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index 7a6ad44fc..650405529 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -274,7 +274,7 @@ class Edge(object): start (TimeInput): The start time of the window. Returns: - A Edge object. + Edge """ def at(self, time: TimeInput): @@ -285,7 +285,7 @@ class Edge(object): time (TimeInput): The time of the window. Returns: - A Edge object. + Edge """ def before(self, end: TimeInput): @@ -296,7 +296,7 @@ class Edge(object): end (TimeInput): The end time of the window. Returns: - A Edge object. + Edge """ @property @@ -359,7 +359,7 @@ class Edge(object): Gets the latest time that this Edge is valid. Returns: - The latest time that this Edge is valid or None if the Edge is valid for all times. + Optional[int]: The latest time that this Edge is valid or None if the Edge is valid for all times. """ @property @@ -368,7 +368,7 @@ class Edge(object): Gets the latest datetime that this Edge is valid Returns: - The latest datetime that this Edge is valid or None if the Edge is valid for all times. + Optional[Datetime]: The latest datetime that this Edge is valid or None if the Edge is valid for all times. """ def exclude_layer(self, name: str) -> Edge: @@ -499,7 +499,7 @@ class Edge(object): Create a view of the Edge including all events at the latest time. Returns: - A Edge object. + Edge """ @property @@ -594,7 +594,7 @@ class Edge(object): Arguments: end (TimeInput): the new end time of the window Returns: - A Edge object. + Edge """ def shrink_start(self, start: TimeInput): @@ -605,7 +605,7 @@ class Edge(object): start (TimeInput): the new start time of the window Returns: - A Edge object. + Edge """ def shrink_window(self, start: TimeInput, end: TimeInput): @@ -628,7 +628,7 @@ class Edge(object): time (TimeInput): The time of the window. Returns: - A Edge object. + Edge """ def snapshot_latest(self): @@ -638,7 +638,7 @@ class Edge(object): This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s Returns: - A Edge object. + Edge """ @property @@ -651,7 +651,7 @@ class Edge(object): Gets the start time for rolling and expanding windows for this Edge Returns: - The earliest time that this Edge is valid or None if the Edge is valid for all times. + Optional[int]: The earliest time that this Edge is valid or None if the Edge is valid for all times. """ @property @@ -660,7 +660,7 @@ class Edge(object): Gets the earliest datetime that this Edge is valid Returns: - The earliest datetime that this Edge is valid or None if the Edge is valid for all times. + Optional[Datetime]: The earliest datetime that this Edge is valid or None if the Edge is valid for all times. """ @property @@ -693,12 +693,17 @@ class Edge(object): end (TimeInput | None): The end time of the window (unbounded if `None`). Returns: - r A Edge object. + r Edge """ @property def window_size(self): - """Get the window size (difference between start and end) for this Edge""" + """ + Get the window size (difference between start and end) for this Edge + + Returns: + Optional[int] + """ class Edges(object): """A list of edges that can be iterated over.""" @@ -723,7 +728,7 @@ class Edges(object): start (TimeInput): The start time of the window. Returns: - A Edges object. + Edges """ def at(self, time: TimeInput): @@ -734,7 +739,7 @@ class Edges(object): time (TimeInput): The time of the window. Returns: - A Edges object. + Edges """ def before(self, end: TimeInput): @@ -745,7 +750,7 @@ class Edges(object): end (TimeInput): The end time of the window. Returns: - A Edges object. + Edges """ def collect(self) -> list[Edge]: @@ -819,7 +824,7 @@ class Edges(object): Gets the latest time that this Edges is valid. Returns: - The latest time that this Edges is valid or None if the Edges is valid for all times. + Optional[int]: The latest time that this Edges is valid or None if the Edges is valid for all times. """ @property @@ -828,7 +833,7 @@ class Edges(object): Gets the latest datetime that this Edges is valid Returns: - The latest datetime that this Edges is valid or None if the Edges is valid for all times. + Optional[Datetime]: The latest datetime that this Edges is valid or None if the Edges is valid for all times. """ def exclude_layer(self, name: str) -> Edges: @@ -933,7 +938,7 @@ class Edges(object): Create a view of the Edges including all events at the latest time. Returns: - A Edges object. + Edges """ @property @@ -1023,7 +1028,7 @@ class Edges(object): Arguments: end (TimeInput): the new end time of the window Returns: - A Edges object. + Edges """ def shrink_start(self, start: TimeInput): @@ -1034,7 +1039,7 @@ class Edges(object): start (TimeInput): the new start time of the window Returns: - A Edges object. + Edges """ def shrink_window(self, start: TimeInput, end: TimeInput): @@ -1057,7 +1062,7 @@ class Edges(object): time (TimeInput): The time of the window. Returns: - A Edges object. + Edges """ def snapshot_latest(self): @@ -1067,7 +1072,7 @@ class Edges(object): This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s Returns: - A Edges object. + Edges """ @property @@ -1080,7 +1085,7 @@ class Edges(object): Gets the start time for rolling and expanding windows for this Edges Returns: - The earliest time that this Edges is valid or None if the Edges is valid for all times. + Optional[int]: The earliest time that this Edges is valid or None if the Edges is valid for all times. """ @property @@ -1089,7 +1094,7 @@ class Edges(object): Gets the earliest datetime that this Edges is valid Returns: - The earliest datetime that this Edges is valid or None if the Edges is valid for all times. + Optional[Datetime]: The earliest datetime that this Edges is valid or None if the Edges is valid for all times. """ @property @@ -1147,12 +1152,17 @@ class Edges(object): end (TimeInput | None): The end time of the window (unbounded if `None`). Returns: - r A Edges object. + r Edges """ @property def window_size(self): - """Get the window size (difference between start and end) for this Edges""" + """ + Get the window size (difference between start and end) for this Edges + + Returns: + Optional[int] + """ class Graph(GraphView): """ @@ -1724,7 +1734,7 @@ class GraphView(object): start (TimeInput): The start time of the window. Returns: - A GraphView object. + GraphView """ def at(self, time: TimeInput): @@ -1735,7 +1745,7 @@ class GraphView(object): time (TimeInput): The time of the window. Returns: - A GraphView object. + GraphView """ def before(self, end: TimeInput): @@ -1746,7 +1756,7 @@ class GraphView(object): end (TimeInput): The end time of the window. Returns: - A GraphView object. + GraphView """ def count_edges(self): @@ -1825,7 +1835,7 @@ class GraphView(object): Gets the latest time that this GraphView is valid. Returns: - The latest time that this GraphView is valid or None if the GraphView is valid for all times. + Optional[int]: The latest time that this GraphView is valid or None if the GraphView is valid for all times. """ @property @@ -1834,7 +1844,7 @@ class GraphView(object): Gets the latest datetime that this GraphView is valid Returns: - The latest datetime that this GraphView is valid or None if the GraphView is valid for all times. + Optional[Datetime]: The latest datetime that this GraphView is valid or None if the GraphView is valid for all times. """ def exclude_layer(self, name: str) -> GraphView: @@ -2000,7 +2010,7 @@ class GraphView(object): Create a view of the GraphView including all events at the latest time. Returns: - A GraphView object. + GraphView """ @property @@ -2102,7 +2112,7 @@ class GraphView(object): Arguments: end (TimeInput): the new end time of the window Returns: - A GraphView object. + GraphView """ def shrink_start(self, start: TimeInput): @@ -2113,7 +2123,7 @@ class GraphView(object): start (TimeInput): the new start time of the window Returns: - A GraphView object. + GraphView """ def shrink_window(self, start: TimeInput, end: TimeInput): @@ -2136,7 +2146,7 @@ class GraphView(object): time (TimeInput): The time of the window. Returns: - A GraphView object. + GraphView """ def snapshot_latest(self): @@ -2146,7 +2156,7 @@ class GraphView(object): This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s Returns: - A GraphView object. + GraphView """ @property @@ -2155,7 +2165,7 @@ class GraphView(object): Gets the start time for rolling and expanding windows for this GraphView Returns: - The earliest time that this GraphView is valid or None if the GraphView is valid for all times. + Optional[int]: The earliest time that this GraphView is valid or None if the GraphView is valid for all times. """ @property @@ -2164,7 +2174,7 @@ class GraphView(object): Gets the earliest datetime that this GraphView is valid Returns: - The earliest datetime that this GraphView is valid or None if the GraphView is valid for all times. + Optional[Datetime]: The earliest datetime that this GraphView is valid or None if the GraphView is valid for all times. """ def subgraph(self, nodes): @@ -2303,12 +2313,17 @@ class GraphView(object): end (TimeInput | None): The end time of the window (unbounded if `None`). Returns: - r A GraphView object. + r GraphView """ @property def window_size(self): - """Get the window size (difference between start and end) for this GraphView""" + """ + Get the window size (difference between start and end) for this GraphView + + Returns: + Optional[int] + """ class MutableEdge(Edge): def __repr__(self): @@ -2449,7 +2464,7 @@ class Node(object): start (TimeInput): The start time of the window. Returns: - A Node object. + Node """ def at(self, time: TimeInput): @@ -2460,7 +2475,7 @@ class Node(object): time (TimeInput): The time of the window. Returns: - A Node object. + Node """ def before(self, end: TimeInput): @@ -2471,7 +2486,7 @@ class Node(object): end (TimeInput): The end time of the window. Returns: - A Node object. + Node """ def default_layer(self) -> Node: @@ -2523,7 +2538,7 @@ class Node(object): Gets the latest time that this Node is valid. Returns: - The latest time that this Node is valid or None if the Node is valid for all times. + Optional[int]: The latest time that this Node is valid or None if the Node is valid for all times. """ @property @@ -2532,7 +2547,7 @@ class Node(object): Gets the latest datetime that this Node is valid Returns: - The latest datetime that this Node is valid or None if the Node is valid for all times. + Optional[Datetime]: The latest datetime that this Node is valid or None if the Node is valid for all times. """ def exclude_layer(self, name: str) -> Node: @@ -2692,7 +2707,7 @@ class Node(object): Create a view of the Node including all events at the latest time. Returns: - A Node object. + Node """ @property @@ -2819,7 +2834,7 @@ class Node(object): Arguments: end (TimeInput): the new end time of the window Returns: - A Node object. + Node """ def shrink_start(self, start: TimeInput): @@ -2830,7 +2845,7 @@ class Node(object): start (TimeInput): the new start time of the window Returns: - A Node object. + Node """ def shrink_window(self, start: TimeInput, end: TimeInput): @@ -2853,7 +2868,7 @@ class Node(object): time (TimeInput): The time of the window. Returns: - A Node object. + Node """ def snapshot_latest(self): @@ -2863,7 +2878,7 @@ class Node(object): This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s Returns: - A Node object. + Node """ @property @@ -2872,7 +2887,7 @@ class Node(object): Gets the start time for rolling and expanding windows for this Node Returns: - The earliest time that this Node is valid or None if the Node is valid for all times. + Optional[int]: The earliest time that this Node is valid or None if the Node is valid for all times. """ @property @@ -2881,7 +2896,7 @@ class Node(object): Gets the earliest datetime that this Node is valid Returns: - The earliest datetime that this Node is valid or None if the Node is valid for all times. + Optional[Datetime]: The earliest datetime that this Node is valid or None if the Node is valid for all times. """ def valid_layers(self, names: list[str]) -> Node: @@ -2905,12 +2920,17 @@ class Node(object): end (TimeInput | None): The end time of the window (unbounded if `None`). Returns: - r A Node object. + r Node """ @property def window_size(self): - """Get the window size (difference between start and end) for this Node""" + """ + Get the window size (difference between start and end) for this Node + + Returns: + Optional[int] + """ class Nodes(object): """A list of nodes that can be iterated over.""" @@ -2956,7 +2976,7 @@ class Nodes(object): start (TimeInput): The start time of the window. Returns: - A Nodes object. + Nodes """ def at(self, time: TimeInput): @@ -2967,7 +2987,7 @@ class Nodes(object): time (TimeInput): The time of the window. Returns: - A Nodes object. + Nodes """ def before(self, end: TimeInput): @@ -2978,7 +2998,7 @@ class Nodes(object): end (TimeInput): The end time of the window. Returns: - A Nodes object. + Nodes """ def collect(self) -> list[Node]: @@ -3033,7 +3053,7 @@ class Nodes(object): Gets the latest time that this Nodes is valid. Returns: - The latest time that this Nodes is valid or None if the Nodes is valid for all times. + Optional[int]: The latest time that this Nodes is valid or None if the Nodes is valid for all times. """ @property @@ -3042,7 +3062,7 @@ class Nodes(object): Gets the latest datetime that this Nodes is valid Returns: - The latest datetime that this Nodes is valid or None if the Nodes is valid for all times. + Optional[Datetime]: The latest datetime that this Nodes is valid or None if the Nodes is valid for all times. """ def exclude_layer(self, name: str) -> Nodes: @@ -3196,7 +3216,7 @@ class Nodes(object): Create a view of the Nodes including all events at the latest time. Returns: - A Nodes object. + Nodes """ @property @@ -3310,7 +3330,7 @@ class Nodes(object): Arguments: end (TimeInput): the new end time of the window Returns: - A Nodes object. + Nodes """ def shrink_start(self, start: TimeInput): @@ -3321,7 +3341,7 @@ class Nodes(object): start (TimeInput): the new start time of the window Returns: - A Nodes object. + Nodes """ def shrink_window(self, start: TimeInput, end: TimeInput): @@ -3344,7 +3364,7 @@ class Nodes(object): time (TimeInput): The time of the window. Returns: - A Nodes object. + Nodes """ def snapshot_latest(self): @@ -3354,7 +3374,7 @@ class Nodes(object): This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s Returns: - A Nodes object. + Nodes """ @property @@ -3363,7 +3383,7 @@ class Nodes(object): Gets the start time for rolling and expanding windows for this Nodes Returns: - The earliest time that this Nodes is valid or None if the Nodes is valid for all times. + Optional[int]: The earliest time that this Nodes is valid or None if the Nodes is valid for all times. """ @property @@ -3372,7 +3392,7 @@ class Nodes(object): Gets the earliest datetime that this Nodes is valid Returns: - The earliest datetime that this Nodes is valid or None if the Nodes is valid for all times. + Optional[Datetime]: The earliest datetime that this Nodes is valid or None if the Nodes is valid for all times. """ def to_df( @@ -3416,12 +3436,17 @@ class Nodes(object): end (TimeInput | None): The end time of the window (unbounded if `None`). Returns: - r A Nodes object. + r Nodes """ @property def window_size(self): - """Get the window size (difference between start and end) for this Nodes""" + """ + Get the window size (difference between start and end) for this Nodes + + Returns: + Optional[int] + """ class PersistentGraph(GraphView): """A temporal graph that allows edges and nodes to be deleted.""" diff --git a/python/python/raphtory/typing.py b/python/python/raphtory/typing.py index 186ccac1b..13bce1341 100644 --- a/python/python/raphtory/typing.py +++ b/python/python/raphtory/typing.py @@ -14,6 +14,8 @@ dict[str, "Prop"], ] +GID = Union[int, str] + PropInput = Mapping[str, PropValue] Direction = Literal["in", "out", "both"] diff --git a/raphtory/src/db/api/state/ops/node.rs b/raphtory/src/db/api/state/ops/node.rs index 40be5b1fa..3fce15671 100644 --- a/raphtory/src/db/api/state/ops/node.rs +++ b/raphtory/src/db/api/state/ops/node.rs @@ -1,8 +1,5 @@ use crate::{ - db::api::{ - storage::graph::storage_ops::GraphStorage, - view::internal::{CoreGraphOps, OneHopFilter}, - }, + db::api::{storage::graph::storage_ops::GraphStorage, view::internal::CoreGraphOps}, prelude::GraphViewOps, }; use raphtory_api::core::{ diff --git a/raphtory/src/python/algorithm/max_weight_matching.rs b/raphtory/src/python/algorithm/max_weight_matching.rs index ef7d8f788..12eda6669 100644 --- a/raphtory/src/python/algorithm/max_weight_matching.rs +++ b/raphtory/src/python/algorithm/max_weight_matching.rs @@ -13,7 +13,7 @@ use crate::{ use pyo3::prelude::*; /// A Matching (i.e., a set of edges that do not share any nodes) -#[pyclass(frozen, name = "Matching")] +#[pyclass(frozen, name = "Matching", module = "raphtory.algorithms")] pub struct PyMatching { inner: Matching, } diff --git a/raphtory/src/python/graph/node_state.rs b/raphtory/src/python/graph/node_state.rs index 0ba6c0708..002a0a3b8 100644 --- a/raphtory/src/python/graph/node_state.rs +++ b/raphtory/src/python/graph/node_state.rs @@ -27,7 +27,7 @@ use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; use std::{collections::HashMap, sync::Arc}; macro_rules! impl_node_state_ops { - ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr) => { + ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { impl $name { pub fn iter(&self) -> impl Iterator + '_ { self.inner.values().map($to_owned) @@ -40,6 +40,10 @@ macro_rules! impl_node_state_ops { self.inner.len() } + /// Iterate over nodes + /// + /// Returns: + /// Iterator[Node] fn nodes(&self) -> PyBorrowingIterator { py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| { inner.nodes().map(|n| n.cloned()) @@ -73,16 +77,24 @@ macro_rules! impl_node_state_ops { }) } + /// Returns: + #[doc = concat!(" Iterator[Tuple[Node, ", $py_value, "]]")] fn items(&self) -> PyBorrowingIterator { py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner .iter() .map(|(n, v)| (n.cloned(), ($to_owned)(v)))) } + /// Returns: + #[doc = concat!(" Iterator[",$py_value, "]")] fn values(&self) -> PyBorrowingIterator { self.__iter__() } + /// Sort results by node id + /// + /// Returns: + #[doc = concat!(" ", $computed)] fn sorted_by_id(&self) -> NodeState<'static, $value, DynamicGraph> { self.inner.sort_by_id() } @@ -95,46 +107,91 @@ macro_rules! impl_node_state_ops { } macro_rules! impl_node_state_ord_ops { - ($name:ident, $value:ty, $to_owned:expr) => { + ($name:ident, $value:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { #[pymethods] impl $name { + /// Sort by value + /// + /// Arguments: + /// reverse (bool): If `True`, sort in descending order, otherwise ascending + /// + /// Returns: + #[doc = concat!(" ", $computed)] #[pyo3(signature = (reverse = false))] fn sorted(&self, reverse: bool) -> NodeState<'static, $value, DynamicGraph> { self.inner.sort_by_values(reverse) } + /// Compute the k largest values + /// + /// Arguments: + /// k (int): The number of values to return + /// + /// Returns: + #[doc = concat!(" ", $computed)] fn top_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { self.inner.top_k(k) } + /// Compute the k smallest values + /// + /// Arguments: + /// k (int): The number of values to return + /// + /// Returns: + #[doc = concat!(" ", $computed)] fn bottom_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { self.inner.bottom_k(k) } + /// Return smallest value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] fn min_item(&self) -> Option<(NodeView, $value)> { self.inner .min_item() .map(|(n, v)| (n.cloned(), ($to_owned)(v))) } + /// Return the minimum value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] fn min(&self) -> Option<$value> { self.inner.min().map($to_owned) } + /// Return largest value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] fn max_item(&self) -> Option<(NodeView, $value)> { self.inner .max_item() .map(|(n, v)| (n.cloned(), ($to_owned)(v))) } + /// Return the maximum value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] fn max(&self) -> Option<$value> { self.inner.max().map($to_owned) } + /// Return the median value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] fn median(&self) -> Option<$value> { self.inner.median().map($to_owned) } + /// Return medain value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] fn median_item(&self) -> Option<(NodeView, $value)> { self.inner .median_item() @@ -166,13 +223,21 @@ macro_rules! impl_node_state_ord_ops { } macro_rules! impl_node_state_num_ops { - ($name:ident, $value:ty) => { + ($name:ident, $value:ty, $py_value:literal) => { #[pymethods] impl $name { + /// sum of values over all nodes + /// + /// Returns: + #[doc= concat!(" ", $py_value)] fn sum(&self) -> $value { self.inner.sum() } + /// mean of values over all nodes + /// + /// Returns: + /// float fn mean(&self) -> f64 { self.inner.mean() } @@ -181,20 +246,29 @@ macro_rules! impl_node_state_num_ops { } macro_rules! impl_lazy_node_state { - ($name:ident<$op:ty>) => { - #[pyclass] + ($name:ident<$op:ty>, $computed:literal, $py_value:literal) => { + /// A lazy view over node values + #[pyclass(module = "raphtory.node_state", frozen)] pub struct $name { inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, } #[pymethods] impl $name { + /// Compute all values and return the result as a node view + /// + /// Returns: + #[doc = concat!(" ", $computed)] fn compute( &self, ) -> NodeState<'static, <$op as NodeOp>::Output, DynamicGraph, DynamicGraph> { self.inner.compute() } + /// Compute all values and return the result as a list + /// + /// Returns + #[doc = concat!(" list[", $py_value, "]")] fn collect(&self) -> Vec<<$op as NodeOp>::Output> { self.inner.collect() } @@ -204,7 +278,9 @@ macro_rules! impl_lazy_node_state { $name, <$op as NodeOp>::Output, LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, - |v: <$op as NodeOp>::Output| v + |v: <$op as NodeOp>::Output| v, + $computed, + $py_value ); impl From> for $name { @@ -222,8 +298,8 @@ macro_rules! impl_lazy_node_state { } macro_rules! impl_node_state { - ($name:ident<$value:ty>) => { - #[pyclass] + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + #[pyclass(module = "raphtory.node_state", frozen)] pub struct $name { inner: Arc>, } @@ -232,7 +308,9 @@ macro_rules! impl_node_state { $name, $value, Arc>, - |v: &$value| v.clone() + |v: &$value| v.clone(), + $computed, + $py_value ); impl From> for $name { @@ -252,39 +330,41 @@ macro_rules! impl_node_state { } macro_rules! impl_lazy_node_state_ord { - ($name:ident<$value:ty>) => { - impl_lazy_node_state!($name<$value>); + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_lazy_node_state!($name<$value>, $computed, $py_value); impl_node_state_ord_ops!( $name, <$value as NodeOp>::Output, - |v: <$value as NodeOp>::Output| v + |v: <$value as NodeOp>::Output| v, + $computed, + $py_value ); }; } macro_rules! impl_node_state_ord { - ($name:ident<$value:ty>) => { - impl_node_state!($name<$value>); - impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone()); + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_node_state!($name<$value>, $computed, $py_value); + impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone(), $computed, $py_value); }; } macro_rules! impl_lazy_node_state_num { - ($name:ident<$value:ty>) => { - impl_lazy_node_state_ord!($name<$value>); - impl_node_state_num_ops!($name, <$value as NodeOp>::Output); + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_lazy_node_state_ord!($name<$value>, $computed, $py_value); + impl_node_state_num_ops!($name, <$value as NodeOp>::Output, $py_value); }; } macro_rules! impl_node_state_num { - ($name:ident<$value:ty>) => { - impl_node_state_ord!($name<$value>); - impl_node_state_num_ops!($name, $value); + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_node_state_ord!($name<$value>, $computed, $py_value); + impl_node_state_num_ops!($name, $value, $py_value); }; } macro_rules! impl_one_hop { - ($name:ident<$($path:ident)::+>) => { + ($name:ident<$($path:ident)::+>, $py_name:literal) => { impl IntoPy for LazyNodeState<'static, $($path)::+, DynamicGraph, DynamicGraph> { @@ -292,51 +372,105 @@ macro_rules! impl_one_hop { self.into_dyn_hop().into_py(py) } } - impl_timeops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>,"DegreeView"); - impl_layerops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>,"DegreeView"); + + impl_timeops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>, $py_name); + impl_layerops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>, $py_name); } } -impl_lazy_node_state_num!(DegreeView>); -impl_one_hop!(DegreeView); +impl_lazy_node_state_num!( + DegreeView>, + "NodeStateUsize", + "int" +); +impl_one_hop!(DegreeView, "DegreeView"); -impl_node_state_num!(NodeStateUsize); +impl_node_state_num!(NodeStateUsize, "NodeStateUsize", "int"); -impl_node_state_num!(NodeStateU64); +impl_node_state_num!(NodeStateU64, "NodeStateU64", "int"); -impl_lazy_node_state_ord!(IdView); -impl_node_state_ord!(NodeStateGID); +impl_lazy_node_state_ord!(IdView, "NodeStateGID", "GID"); +impl_node_state_ord!(NodeStateGID, "NodeStateGID", "GID"); -impl_lazy_node_state_ord!(EarliestTimeView>); -impl_one_hop!(EarliestTimeView); -impl_lazy_node_state_ord!(LatestTimeView>); -impl_one_hop!(LatestTimeView); -impl_node_state_ord!(NodeStateOptionI64>); +impl_lazy_node_state_ord!( + EarliestTimeView>, + "NodeStateOptionI64", + "Optional[int]" +); +impl_one_hop!(EarliestTimeView, "EarliestTimeView"); +impl_lazy_node_state_ord!( + LatestTimeView>, + "NodeStateOptionI64", + "Optional[int]" +); +impl_one_hop!(LatestTimeView, "LatestTimeView"); +impl_node_state_ord!( + NodeStateOptionI64>, + "NodeStateOptionI64", + "Optional[int]" +); -impl_lazy_node_state_ord!(NameView); -impl_node_state_ord!(NodeStateString); +impl_lazy_node_state_ord!(NameView, "NodeStateString", "str"); +impl_node_state_ord!(NodeStateString, "NodeStateString", "str"); type EarliestDateTime = ops::Map, Option>>; -impl_lazy_node_state_ord!(EarliestDateTimeView>); -impl_one_hop!(EarliestDateTimeView); +impl_lazy_node_state_ord!( + EarliestDateTimeView>, + "NodeStateOptionDateTime", + "Optional[Datetime]" +); +impl_one_hop!( + EarliestDateTimeView, + "EarliestDateTimeView" +); type LatestDateTime = ops::Map, Option>>; impl_lazy_node_state_ord!( - LatestDateTimeView, Option>>> + LatestDateTimeView, Option>>>, + "NodeStateOptionDateTime", + "Optional[Datetime]" +); +impl_one_hop!(LatestDateTimeView, "LatestDateTimeView"); +impl_node_state_ord!( + NodeStateOptionDateTime>>, + "NodeStateOptionDateTime", + "Optional[Datetime]" ); -impl_one_hop!(LatestDateTimeView); -impl_node_state_ord!(NodeStateOptionDateTime>>); -impl_lazy_node_state_ord!(HistoryView>); -impl_one_hop!(HistoryView); -impl_node_state_ord!(NodeStateListI64>); +impl_lazy_node_state_ord!( + HistoryView>, + "NodeStateListI64", + "list[int]" +); +impl_one_hop!(HistoryView, "HistoryView"); +impl_node_state_ord!(NodeStateListI64>, "NodeStateListI64", "list[int]"); type HistoryDateTime = ops::Map, Option>>>; -impl_lazy_node_state_ord!(HistoryDateTimeView>); -impl_one_hop!(HistoryDateTimeView); -impl_node_state_ord!(NodeStateOptionListDateTime>>>); +impl_lazy_node_state_ord!( + HistoryDateTimeView>, + "NodeStateOptionListDateTime", + "Optional[list[Datetime]]" +); +impl_one_hop!(HistoryDateTimeView, "HistoryDateTimeView"); +impl_node_state_ord!( + NodeStateOptionListDateTime>>>, + "NodeStateOptionListDateTime", + "Optional[list[Datetime]]" +); -impl_lazy_node_state_ord!(NodeTypeView); -impl_node_state_ord!(NodeStateOptionStr>); +impl_lazy_node_state_ord!( + NodeTypeView, + "NodeStateOptionStr", + "Optional[str]" +); +impl_node_state_ord!( + NodeStateOptionStr>, + "NodeStateOptionStr", + "Optional[str]" +); -impl_node_state_ord!(NodeStateListDateTime>>); +impl_node_state_ord!( + NodeStateListDateTime>>, + "NodeStateListDateTime", + "list[Datetime]" +); From b926d646b1faec8152da54513cfbb364be920bb3 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 20:19:56 +0100 Subject: [PATCH 13/23] keep classes if they are in the current module --- python/scripts/gen-stubs.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/scripts/gen-stubs.py b/python/scripts/gen-stubs.py index 336535dcb..0e725efe0 100755 --- a/python/scripts/gen-stubs.py +++ b/python/scripts/gen-stubs.py @@ -160,10 +160,12 @@ def cls_signature(cls: type) -> Optional[inspect.Signature]: pass -def from_raphtory(obj) -> bool: +def from_raphtory(obj, name: str) -> bool: module = inspect.getmodule(obj) if module: - return any(module.__name__.startswith(target) for target in TARGET_MODULES) + return module.__name__ == name or any( + module.__name__.startswith(target) for target in TARGET_MODULES + ) return False @@ -316,7 +318,7 @@ def gen_module(module: ModuleType, name: str, path: Path) -> None: logger = logging.getLogger(str(path)) for obj_name, obj in objs: - if isinstance(obj, type) and from_raphtory(obj): + if isinstance(obj, type) and from_raphtory(obj, name): stubs.append(gen_class(obj, obj_name)) elif isinstance(obj, BuiltinFunctionType): stubs.append(gen_fn(obj, obj_name)) From 4ac5e00f46a9a34afaa3f8767eb6c05052a1d4bf Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Thu, 14 Nov 2024 20:38:17 +0100 Subject: [PATCH 14/23] clean up first parameter name in __new__ --- python/python/raphtory/__init__.pyi | 8 ++++---- python/python/raphtory/graphql/__init__.pyi | 16 ++++++++-------- python/python/raphtory/vectors/__init__.pyi | 2 +- python/scripts/gen-stubs.py | 15 +++++++++++++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index 650405529..0d29c016c 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -1172,7 +1172,7 @@ class Graph(GraphView): num_shards (int, optional): The number of locks to use in the storage to allow for multithreaded updates. """ - def __new__(self, num_shards: Optional[int] = None) -> Graph: + def __new__(cls, num_shards: Optional[int] = None) -> Graph: """Create and return a new object. See help(type) for accurate signature.""" def __reduce__(self): ... @@ -3451,7 +3451,7 @@ class Nodes(object): class PersistentGraph(GraphView): """A temporal graph that allows edges and nodes to be deleted.""" - def __new__(self) -> PersistentGraph: + def __new__(cls) -> PersistentGraph: """Create and return a new object. See help(type) for accurate signature.""" def __reduce__(self): ... @@ -4034,7 +4034,7 @@ class Prop(object): def __ne__(self, value): """Return self!=value.""" - def __new__(self, name) -> Prop: + def __new__(cls, name) -> Prop: """Create and return a new object. See help(type) for accurate signature.""" def any(self, values): @@ -4128,7 +4128,7 @@ class PyGraphEncoder(object): """Call self as a function.""" def __getstate__(self): ... - def __new__(self) -> PyGraphEncoder: + def __new__(cls) -> PyGraphEncoder: """Create and return a new object. See help(type) for accurate signature.""" def __setstate__(self): ... diff --git a/python/python/raphtory/graphql/__init__.pyi b/python/python/raphtory/graphql/__init__.pyi index 28827d487..17ba72a5d 100644 --- a/python/python/raphtory/graphql/__init__.pyi +++ b/python/python/raphtory/graphql/__init__.pyi @@ -19,7 +19,7 @@ class GraphServer(object): """A class for defining and running a Raphtory GraphQL server""" def __new__( - self, + cls, work_dir, cache_capacity=None, cache_tti_seconds=None, @@ -147,7 +147,7 @@ class GraphqlGraphs(object): class RaphtoryClient(object): """A client for handling GraphQL operations in the context of Raphtory.""" - def __new__(self, url) -> RaphtoryClient: + def __new__(cls, url) -> RaphtoryClient: """Create and return a new object. See help(type) for accurate signature.""" def copy_graph(self, path, new_path): @@ -268,7 +268,7 @@ class RaphtoryClient(object): """ class RemoteEdge(object): - def __new__(self, path, client, src, dst) -> RemoteEdge: + def __new__(cls, path, client, src, dst) -> RemoteEdge: """Create and return a new object. See help(type) for accurate signature.""" def add_constant_properties( @@ -324,12 +324,12 @@ class RemoteEdge(object): class RemoteEdgeAddition(object): def __new__( - self, src, dst, layer=None, constant_properties=None, updates=None + cls, src, dst, layer=None, constant_properties=None, updates=None ) -> RemoteEdgeAddition: """Create and return a new object. See help(type) for accurate signature.""" class RemoteGraph(object): - def __new__(self, path, client) -> RemoteGraph: + def __new__(cls, path, client) -> RemoteGraph: """Create and return a new object. See help(type) for accurate signature.""" def add_constant_properties(self, properties: dict): @@ -458,7 +458,7 @@ class RemoteGraph(object): """ class RemoteNode(object): - def __new__(self, path, client, id) -> RemoteNode: + def __new__(cls, path, client, id) -> RemoteNode: """Create and return a new object. See help(type) for accurate signature.""" def add_constant_properties(self, properties: Dict[str, Prop]): @@ -504,12 +504,12 @@ class RemoteNode(object): class RemoteNodeAddition(object): def __new__( - self, name, node_type=None, constant_properties=None, updates=None + cls, name, node_type=None, constant_properties=None, updates=None ) -> RemoteNodeAddition: """Create and return a new object. See help(type) for accurate signature.""" class RemoteUpdate(object): - def __new__(self, time, properties=None) -> RemoteUpdate: + def __new__(cls, time, properties=None) -> RemoteUpdate: """Create and return a new object. See help(type) for accurate signature.""" class RunningGraphServer(object): diff --git a/python/python/raphtory/vectors/__init__.pyi b/python/python/raphtory/vectors/__init__.pyi index efc95d211..cbae5f12e 100644 --- a/python/python/raphtory/vectors/__init__.pyi +++ b/python/python/raphtory/vectors/__init__.pyi @@ -16,7 +16,7 @@ from datetime import datetime from pandas import DataFrame class Document(object): - def __new__(self, content, life=None) -> Document: + def __new__(cls, content, life=None) -> Document: """Create and return a new object. See help(type) for accurate signature.""" def __repr__(self): diff --git a/python/scripts/gen-stubs.py b/python/scripts/gen-stubs.py index 0e725efe0..92bc0492c 100755 --- a/python/scripts/gen-stubs.py +++ b/python/scripts/gen-stubs.py @@ -153,9 +153,14 @@ def insert_self(signature: inspect.Signature) -> inspect.Signature: return signature.replace(parameters=[self_param, *signature.parameters.values()]) +def insert_cls(signature: inspect.Signature) -> inspect.Signature: + cls_param = inspect.Parameter("cls", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD) + return signature.replace(parameters=[cls_param, *signature.parameters.values()]) + + def cls_signature(cls: type) -> Optional[inspect.Signature]: try: - return insert_self(inspect.signature(cls)) + return inspect.signature(cls) except ValueError: pass @@ -249,6 +254,9 @@ def gen_fn( type_annotations, return_type, ) + if name == "__new__": + # new is special and not a class method + decorator = None fn_str = f"{init_tab}def {name}{signature}:\n{docstr}" @@ -282,7 +290,10 @@ def gen_class(cls: type, name) -> str: signature = cls_signature(cls) if signature is not None: if obj_name == "__new__": - signature = signature.replace(return_annotation=name) + signature = insert_cls(signature.replace(return_annotation=name)) + else: + signature = insert_self(signature) + entities.append( gen_fn( entity, From c6941c76208e7adc20075d3081b619401d6e51c2 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Fri, 15 Nov 2024 10:19:55 +0100 Subject: [PATCH 15/23] add all the node_state wrappers to stubs --- python/src/lib.rs | 1 - raphtory/src/python/graph/node_state.rs | 852 ++++++++++--------- raphtory/src/python/packages/base_modules.rs | 4 +- 3 files changed, 433 insertions(+), 424 deletions(-) diff --git a/python/src/lib.rs b/python/src/lib.rs index 753e88987..fa522ca90 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -21,6 +21,5 @@ fn raphtory(py: Python<'_>, m: &Bound) -> PyResult<()> { m.add_submodule(&graph_loader_module)?; m.add_submodule(&graph_gen_module)?; m.add_submodule(&vectors_module)?; - Ok(()) } diff --git a/raphtory/src/python/graph/node_state.rs b/raphtory/src/python/graph/node_state.rs index 002a0a3b8..f2fe8d789 100644 --- a/raphtory/src/python/graph/node_state.rs +++ b/raphtory/src/python/graph/node_state.rs @@ -1,476 +1,484 @@ -use crate::{ - core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, - db::{ - api::{ - state::{ops, LazyNodeState, NodeOp, NodeState, NodeStateOps, OrderedNodeStateOps}, - view::{ - internal::Static, DynamicGraph, GraphViewOps, IntoDynHop, IntoDynamic, - StaticGraphViewOps, +use pyo3::prelude::*; + +#[pymodule(submodule)] +pub mod node_state { + use crate::{ + core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, + db::{ + api::{ + state::{ops, LazyNodeState, NodeOp, NodeState, NodeStateOps, OrderedNodeStateOps}, + view::{ + internal::Static, DynamicGraph, GraphViewOps, IntoDynHop, IntoDynamic, + StaticGraphViewOps, + }, }, + graph::node::NodeView, }, - graph::node::NodeView, - }, - prelude::*, - py_borrowing_iter, - python::{ - types::{repr::Repr, wrappers::iterators::PyBorrowingIterator}, - utils::PyNodeRef, - }, -}; -use chrono::{DateTime, Utc}; -use pyo3::{ - exceptions::{PyKeyError, PyTypeError}, - prelude::*, - types::PyNotImplemented, -}; -use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; -use std::{collections::HashMap, sync::Arc}; - -macro_rules! impl_node_state_ops { - ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { - impl $name { - pub fn iter(&self) -> impl Iterator + '_ { - self.inner.values().map($to_owned) + prelude::*, + py_borrowing_iter, + python::{ + types::{repr::Repr, wrappers::iterators::PyBorrowingIterator}, + utils::PyNodeRef, + }, + }; + use chrono::{DateTime, Utc}; + use pyo3::{ + exceptions::{PyKeyError, PyTypeError}, + prelude::*, + types::PyNotImplemented, + }; + use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; + use std::{collections::HashMap, sync::Arc}; + + macro_rules! impl_node_state_ops { + ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { + impl $name { + pub fn iter(&self) -> impl Iterator + '_ { + self.inner.values().map($to_owned) + } } - } - #[pymethods] - impl $name { - fn __len__(&self) -> usize { - self.inner.len() - } + #[pymethods] + impl $name { + fn __len__(&self) -> usize { + self.inner.len() + } - /// Iterate over nodes - /// - /// Returns: - /// Iterator[Node] - fn nodes(&self) -> PyBorrowingIterator { - py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| { - inner.nodes().map(|n| n.cloned()) - }) - } + /// Iterate over nodes + /// + /// Returns: + /// Iterator[Node] + fn nodes(&self) -> PyBorrowingIterator { + py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| { + inner.nodes().map(|n| n.cloned()) + }) + } - fn __iter__(&self) -> PyBorrowingIterator { - py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner - .values() - .map($to_owned)) - } + fn __iter__(&self) -> PyBorrowingIterator { + py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner + .values() + .map($to_owned)) + } - fn __getitem__(&self, node: PyNodeRef) -> PyResult<$value> { - let node = node.as_node_ref(); - self.inner - .get_by_node(node) - .map($to_owned) - .ok_or_else(|| match node { - NodeRef::External(id) => { - PyKeyError::new_err(format!("Missing value for node with id {id}")) - } - NodeRef::Internal(vid) => { - let node = self.inner.graph().node(vid); - match node { - Some(node) => { - PyKeyError::new_err(format!("Missing value {}", node.repr())) + fn __getitem__(&self, node: PyNodeRef) -> PyResult<$value> { + let node = node.as_node_ref(); + self.inner + .get_by_node(node) + .map($to_owned) + .ok_or_else(|| match node { + NodeRef::External(id) => { + PyKeyError::new_err(format!("Missing value for node with id {id}")) + } + NodeRef::Internal(vid) => { + let node = self.inner.graph().node(vid); + match node { + Some(node) => PyKeyError::new_err(format!( + "Missing value {}", + node.repr() + )), + None => PyTypeError::new_err("Invalid node reference"), } - None => PyTypeError::new_err("Invalid node reference"), } - } - }) - } + }) + } - /// Returns: - #[doc = concat!(" Iterator[Tuple[Node, ", $py_value, "]]")] - fn items(&self) -> PyBorrowingIterator { - py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner - .iter() - .map(|(n, v)| (n.cloned(), ($to_owned)(v)))) - } + /// Returns: + #[doc = concat!(" Iterator[Tuple[Node, ", $py_value, "]]")] + fn items(&self) -> PyBorrowingIterator { + py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner + .iter() + .map(|(n, v)| (n.cloned(), ($to_owned)(v)))) + } - /// Returns: - #[doc = concat!(" Iterator[",$py_value, "]")] - fn values(&self) -> PyBorrowingIterator { - self.__iter__() - } + /// Returns: + #[doc = concat!(" Iterator[",$py_value, "]")] + fn values(&self) -> PyBorrowingIterator { + self.__iter__() + } - /// Sort results by node id - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn sorted_by_id(&self) -> NodeState<'static, $value, DynamicGraph> { - self.inner.sort_by_id() - } + /// Sort results by node id + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn sorted_by_id(&self) -> NodeState<'static, $value, DynamicGraph> { + self.inner.sort_by_id() + } - fn __repr__(&self) -> String { - self.inner.repr() + fn __repr__(&self) -> String { + self.inner.repr() + } } - } - }; -} + }; + } -macro_rules! impl_node_state_ord_ops { - ($name:ident, $value:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { - #[pymethods] - impl $name { - /// Sort by value - /// - /// Arguments: - /// reverse (bool): If `True`, sort in descending order, otherwise ascending - /// - /// Returns: - #[doc = concat!(" ", $computed)] - #[pyo3(signature = (reverse = false))] - fn sorted(&self, reverse: bool) -> NodeState<'static, $value, DynamicGraph> { - self.inner.sort_by_values(reverse) - } + macro_rules! impl_node_state_ord_ops { + ($name:ident, $value:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { + #[pymethods] + impl $name { + /// Sort by value + /// + /// Arguments: + /// reverse (bool): If `True`, sort in descending order, otherwise ascending + /// + /// Returns: + #[doc = concat!(" ", $computed)] + #[pyo3(signature = (reverse = false))] + fn sorted(&self, reverse: bool) -> NodeState<'static, $value, DynamicGraph> { + self.inner.sort_by_values(reverse) + } - /// Compute the k largest values - /// - /// Arguments: - /// k (int): The number of values to return - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn top_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { - self.inner.top_k(k) - } + /// Compute the k largest values + /// + /// Arguments: + /// k (int): The number of values to return + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn top_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { + self.inner.top_k(k) + } - /// Compute the k smallest values - /// - /// Arguments: - /// k (int): The number of values to return - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn bottom_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { - self.inner.bottom_k(k) - } + /// Compute the k smallest values + /// + /// Arguments: + /// k (int): The number of values to return + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn bottom_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { + self.inner.bottom_k(k) + } - /// Return smallest value and corresponding node - /// - /// Returns: - #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] - fn min_item(&self) -> Option<(NodeView, $value)> { - self.inner - .min_item() - .map(|(n, v)| (n.cloned(), ($to_owned)(v))) - } + /// Return smallest value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] + fn min_item(&self) -> Option<(NodeView, $value)> { + self.inner + .min_item() + .map(|(n, v)| (n.cloned(), ($to_owned)(v))) + } - /// Return the minimum value - /// - /// Returns: - #[doc = concat!(" Optional[", $py_value, "]")] - fn min(&self) -> Option<$value> { - self.inner.min().map($to_owned) - } + /// Return the minimum value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] + fn min(&self) -> Option<$value> { + self.inner.min().map($to_owned) + } - /// Return largest value and corresponding node - /// - /// Returns: - #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] - fn max_item(&self) -> Option<(NodeView, $value)> { - self.inner - .max_item() - .map(|(n, v)| (n.cloned(), ($to_owned)(v))) - } + /// Return largest value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] + fn max_item(&self) -> Option<(NodeView, $value)> { + self.inner + .max_item() + .map(|(n, v)| (n.cloned(), ($to_owned)(v))) + } - /// Return the maximum value - /// - /// Returns: - #[doc = concat!(" Optional[", $py_value, "]")] - fn max(&self) -> Option<$value> { - self.inner.max().map($to_owned) - } + /// Return the maximum value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] + fn max(&self) -> Option<$value> { + self.inner.max().map($to_owned) + } - /// Return the median value - /// - /// Returns: - #[doc = concat!(" Optional[", $py_value, "]")] - fn median(&self) -> Option<$value> { - self.inner.median().map($to_owned) - } + /// Return the median value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] + fn median(&self) -> Option<$value> { + self.inner.median().map($to_owned) + } - /// Return medain value and corresponding node - /// - /// Returns: - #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] - fn median_item(&self) -> Option<(NodeView, $value)> { - self.inner - .median_item() - .map(|(n, v)| (n.cloned(), ($to_owned)(v))) - } + /// Return medain value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] + fn median_item(&self) -> Option<(NodeView, $value)> { + self.inner + .median_item() + .map(|(n, v)| (n.cloned(), ($to_owned)(v))) + } - fn __eq__<'py>(&self, other: &Bound<'py, PyAny>, py: Python<'py>) -> PyObject { - if let Ok(other) = other.downcast::() { - let other = Bound::borrow(other); - return self.inner.values().eq(other.inner.values()).into_py(py); - } else if let Ok(other) = other.extract::>() { - return self - .inner - .values() - .map($to_owned) - .eq(other.into_iter()) + fn __eq__<'py>(&self, other: &Bound<'py, PyAny>, py: Python<'py>) -> PyObject { + if let Ok(other) = other.downcast::() { + let other = Bound::borrow(other); + return self.inner.values().eq(other.inner.values()).into_py(py); + } else if let Ok(other) = other.extract::>() { + return self + .inner + .values() + .map($to_owned) + .eq(other.into_iter()) + .into_py(py); + } else if let Ok(other) = other.extract::>() { + return (self.inner.len() == other.len() + && other.into_iter().all(|(node, value)| { + self.inner.get_by_node(node).map($to_owned) == Some(value) + })) .into_py(py); - } else if let Ok(other) = other.extract::>() { - return (self.inner.len() == other.len() - && other.into_iter().all(|(node, value)| { - self.inner.get_by_node(node).map($to_owned) == Some(value) - })) - .into_py(py); + } + PyNotImplemented::get_bound(py).into_py(py) } - PyNotImplemented::get_bound(py).into_py(py) } - } - }; -} + }; + } + + macro_rules! impl_node_state_num_ops { + ($name:ident, $value:ty, $py_value:literal) => { + #[pymethods] + impl $name { + /// sum of values over all nodes + /// + /// Returns: + #[doc= concat!(" ", $py_value)] + fn sum(&self) -> $value { + self.inner.sum() + } -macro_rules! impl_node_state_num_ops { - ($name:ident, $value:ty, $py_value:literal) => { - #[pymethods] - impl $name { - /// sum of values over all nodes - /// - /// Returns: - #[doc= concat!(" ", $py_value)] - fn sum(&self) -> $value { - self.inner.sum() + /// mean of values over all nodes + /// + /// Returns: + /// float + fn mean(&self) -> f64 { + self.inner.mean() + } } + }; + } - /// mean of values over all nodes - /// - /// Returns: - /// float - fn mean(&self) -> f64 { - self.inner.mean() + macro_rules! impl_lazy_node_state { + ($name:ident<$op:ty>, $computed:literal, $py_value:literal) => { + /// A lazy view over node values + #[pyclass(module = "raphtory.node_state", frozen)] + pub struct $name { + inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, } - } - }; -} -macro_rules! impl_lazy_node_state { - ($name:ident<$op:ty>, $computed:literal, $py_value:literal) => { - /// A lazy view over node values - #[pyclass(module = "raphtory.node_state", frozen)] - pub struct $name { - inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, - } + #[pymethods] + impl $name { + /// Compute all values and return the result as a node view + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn compute( + &self, + ) -> NodeState<'static, <$op as NodeOp>::Output, DynamicGraph, DynamicGraph> { + self.inner.compute() + } - #[pymethods] - impl $name { - /// Compute all values and return the result as a node view - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn compute( - &self, - ) -> NodeState<'static, <$op as NodeOp>::Output, DynamicGraph, DynamicGraph> { - self.inner.compute() + /// Compute all values and return the result as a list + /// + /// Returns + #[doc = concat!(" list[", $py_value, "]")] + fn collect(&self) -> Vec<<$op as NodeOp>::Output> { + self.inner.collect() + } } - /// Compute all values and return the result as a list - /// - /// Returns - #[doc = concat!(" list[", $py_value, "]")] - fn collect(&self) -> Vec<<$op as NodeOp>::Output> { - self.inner.collect() + impl_node_state_ops!( + $name, + <$op as NodeOp>::Output, + LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, + |v: <$op as NodeOp>::Output| v, + $computed, + $py_value + ); + + impl From> for $name { + fn from(inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>) -> Self { + $name { inner } + } } - } - impl_node_state_ops!( - $name, - <$op as NodeOp>::Output, - LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, - |v: <$op as NodeOp>::Output| v, - $computed, - $py_value - ); - - impl From> for $name { - fn from(inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>) -> Self { - $name { inner } + impl pyo3::IntoPy + for LazyNodeState<'static, $op, DynamicGraph, DynamicGraph> + { + fn into_py(self, py: Python<'_>) -> PyObject { + $name::from(self).into_py(py) + } } - } + }; + } - impl pyo3::IntoPy for LazyNodeState<'static, $op, DynamicGraph, DynamicGraph> { - fn into_py(self, py: Python<'_>) -> PyObject { - $name::from(self).into_py(py) + macro_rules! impl_node_state { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + #[pyclass(module = "raphtory.node_state", frozen)] + pub struct $name { + inner: Arc>, } - } - }; -} - -macro_rules! impl_node_state { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - #[pyclass(module = "raphtory.node_state", frozen)] - pub struct $name { - inner: Arc>, - } - impl_node_state_ops!( - $name, - $value, - Arc>, - |v: &$value| v.clone(), - $computed, - $py_value - ); - - impl From> for $name { - fn from(inner: NodeState<'static, $value, DynamicGraph, DynamicGraph>) -> Self { - $name { - inner: inner.into(), + impl_node_state_ops!( + $name, + $value, + Arc>, + |v: &$value| v.clone(), + $computed, + $py_value + ); + + impl From> for $name { + fn from(inner: NodeState<'static, $value, DynamicGraph, DynamicGraph>) -> Self { + $name { + inner: inner.into(), + } } } - } - impl pyo3::IntoPy for NodeState<'static, $value, DynamicGraph, DynamicGraph> { - fn into_py(self, py: Python<'_>) -> PyObject { - $name::from(self).into_py(py) + impl pyo3::IntoPy for NodeState<'static, $value, DynamicGraph, DynamicGraph> { + fn into_py(self, py: Python<'_>) -> PyObject { + $name::from(self).into_py(py) + } } - } - }; -} + }; + } -macro_rules! impl_lazy_node_state_ord { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_lazy_node_state!($name<$value>, $computed, $py_value); - impl_node_state_ord_ops!( - $name, - <$value as NodeOp>::Output, - |v: <$value as NodeOp>::Output| v, - $computed, - $py_value - ); - }; -} + macro_rules! impl_lazy_node_state_ord { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_lazy_node_state!($name<$value>, $computed, $py_value); + impl_node_state_ord_ops!( + $name, + <$value as NodeOp>::Output, + |v: <$value as NodeOp>::Output| v, + $computed, + $py_value + ); + }; + } -macro_rules! impl_node_state_ord { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_node_state!($name<$value>, $computed, $py_value); - impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone(), $computed, $py_value); - }; -} + macro_rules! impl_node_state_ord { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_node_state!($name<$value>, $computed, $py_value); + impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone(), $computed, $py_value); + }; + } -macro_rules! impl_lazy_node_state_num { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_lazy_node_state_ord!($name<$value>, $computed, $py_value); - impl_node_state_num_ops!($name, <$value as NodeOp>::Output, $py_value); - }; -} + macro_rules! impl_lazy_node_state_num { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_lazy_node_state_ord!($name<$value>, $computed, $py_value); + impl_node_state_num_ops!($name, <$value as NodeOp>::Output, $py_value); + }; + } -macro_rules! impl_node_state_num { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_node_state_ord!($name<$value>, $computed, $py_value); - impl_node_state_num_ops!($name, $value, $py_value); - }; -} + macro_rules! impl_node_state_num { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_node_state_ord!($name<$value>, $computed, $py_value); + impl_node_state_num_ops!($name, $value, $py_value); + }; + } -macro_rules! impl_one_hop { - ($name:ident<$($path:ident)::+>, $py_name:literal) => { - impl IntoPy - for LazyNodeState<'static, $($path)::+, DynamicGraph, DynamicGraph> - { - fn into_py(self, py: Python<'_>) -> PyObject { - self.into_dyn_hop().into_py(py) + macro_rules! impl_one_hop { + ($name:ident<$($path:ident)::+>, $py_name:literal) => { + impl IntoPy + for LazyNodeState<'static, $($path)::+, DynamicGraph, DynamicGraph> + { + fn into_py(self, py: Python<'_>) -> PyObject { + self.into_dyn_hop().into_py(py) + } } - } - impl_timeops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>, $py_name); - impl_layerops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>, $py_name); + impl_timeops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>, $py_name); + impl_layerops!($name, inner, LazyNodeState<'static, $($path)::+, DynamicGraph>, $py_name); + } } -} -impl_lazy_node_state_num!( - DegreeView>, - "NodeStateUsize", - "int" -); -impl_one_hop!(DegreeView, "DegreeView"); - -impl_node_state_num!(NodeStateUsize, "NodeStateUsize", "int"); - -impl_node_state_num!(NodeStateU64, "NodeStateU64", "int"); - -impl_lazy_node_state_ord!(IdView, "NodeStateGID", "GID"); -impl_node_state_ord!(NodeStateGID, "NodeStateGID", "GID"); - -impl_lazy_node_state_ord!( - EarliestTimeView>, - "NodeStateOptionI64", - "Optional[int]" -); -impl_one_hop!(EarliestTimeView, "EarliestTimeView"); -impl_lazy_node_state_ord!( - LatestTimeView>, - "NodeStateOptionI64", - "Optional[int]" -); -impl_one_hop!(LatestTimeView, "LatestTimeView"); -impl_node_state_ord!( - NodeStateOptionI64>, - "NodeStateOptionI64", - "Optional[int]" -); - -impl_lazy_node_state_ord!(NameView, "NodeStateString", "str"); -impl_node_state_ord!(NodeStateString, "NodeStateString", "str"); - -type EarliestDateTime = ops::Map, Option>>; -impl_lazy_node_state_ord!( - EarliestDateTimeView>, - "NodeStateOptionDateTime", - "Optional[Datetime]" -); -impl_one_hop!( - EarliestDateTimeView, - "EarliestDateTimeView" -); - -type LatestDateTime = ops::Map, Option>>; -impl_lazy_node_state_ord!( - LatestDateTimeView, Option>>>, - "NodeStateOptionDateTime", - "Optional[Datetime]" -); -impl_one_hop!(LatestDateTimeView, "LatestDateTimeView"); -impl_node_state_ord!( - NodeStateOptionDateTime>>, - "NodeStateOptionDateTime", - "Optional[Datetime]" -); - -impl_lazy_node_state_ord!( - HistoryView>, - "NodeStateListI64", - "list[int]" -); -impl_one_hop!(HistoryView, "HistoryView"); -impl_node_state_ord!(NodeStateListI64>, "NodeStateListI64", "list[int]"); - -type HistoryDateTime = ops::Map, Option>>>; -impl_lazy_node_state_ord!( - HistoryDateTimeView>, - "NodeStateOptionListDateTime", - "Optional[list[Datetime]]" -); -impl_one_hop!(HistoryDateTimeView, "HistoryDateTimeView"); -impl_node_state_ord!( - NodeStateOptionListDateTime>>>, - "NodeStateOptionListDateTime", - "Optional[list[Datetime]]" -); - -impl_lazy_node_state_ord!( - NodeTypeView, - "NodeStateOptionStr", - "Optional[str]" -); -impl_node_state_ord!( - NodeStateOptionStr>, - "NodeStateOptionStr", - "Optional[str]" -); - -impl_node_state_ord!( - NodeStateListDateTime>>, - "NodeStateListDateTime", - "list[Datetime]" -); + impl_lazy_node_state_num!( + DegreeView>, + "NodeStateUsize", + "int" + ); + impl_one_hop!(DegreeView, "DegreeView"); + + impl_node_state_num!(NodeStateUsize, "NodeStateUsize", "int"); + + impl_node_state_num!(NodeStateU64, "NodeStateU64", "int"); + + impl_lazy_node_state_ord!(IdView, "NodeStateGID", "GID"); + impl_node_state_ord!(NodeStateGID, "NodeStateGID", "GID"); + + impl_lazy_node_state_ord!( + EarliestTimeView>, + "NodeStateOptionI64", + "Optional[int]" + ); + impl_one_hop!(EarliestTimeView, "EarliestTimeView"); + impl_lazy_node_state_ord!( + LatestTimeView>, + "NodeStateOptionI64", + "Optional[int]" + ); + impl_one_hop!(LatestTimeView, "LatestTimeView"); + impl_node_state_ord!( + NodeStateOptionI64>, + "NodeStateOptionI64", + "Optional[int]" + ); + + impl_lazy_node_state_ord!(NameView, "NodeStateString", "str"); + impl_node_state_ord!(NodeStateString, "NodeStateString", "str"); + + type EarliestDateTime = ops::Map, Option>>; + impl_lazy_node_state_ord!( + EarliestDateTimeView>, + "NodeStateOptionDateTime", + "Optional[Datetime]" + ); + impl_one_hop!( + EarliestDateTimeView, + "EarliestDateTimeView" + ); + + type LatestDateTime = ops::Map, Option>>; + impl_lazy_node_state_ord!( + LatestDateTimeView, Option>>>, + "NodeStateOptionDateTime", + "Optional[Datetime]" + ); + impl_one_hop!(LatestDateTimeView, "LatestDateTimeView"); + impl_node_state_ord!( + NodeStateOptionDateTime>>, + "NodeStateOptionDateTime", + "Optional[Datetime]" + ); + + impl_lazy_node_state_ord!( + HistoryView>, + "NodeStateListI64", + "list[int]" + ); + impl_one_hop!(HistoryView, "HistoryView"); + impl_node_state_ord!(NodeStateListI64>, "NodeStateListI64", "list[int]"); + + type HistoryDateTime = ops::Map, Option>>>; + impl_lazy_node_state_ord!( + HistoryDateTimeView>, + "NodeStateOptionListDateTime", + "Optional[list[Datetime]]" + ); + impl_one_hop!(HistoryDateTimeView, "HistoryDateTimeView"); + impl_node_state_ord!( + NodeStateOptionListDateTime>>>, + "NodeStateOptionListDateTime", + "Optional[list[Datetime]]" + ); + + impl_lazy_node_state_ord!( + NodeTypeView, + "NodeStateOptionStr", + "Optional[str]" + ); + impl_node_state_ord!( + NodeStateOptionStr>, + "NodeStateOptionStr", + "Optional[str]" + ); + + impl_node_state_ord!( + NodeStateListDateTime>>, + "NodeStateListDateTime", + "list[Datetime]" + ); +} diff --git a/raphtory/src/python/packages/base_modules.rs b/raphtory/src/python/packages/base_modules.rs index 46f8814a0..844e82615 100644 --- a/raphtory/src/python/packages/base_modules.rs +++ b/raphtory/src/python/packages/base_modules.rs @@ -14,6 +14,7 @@ use crate::{ graph_with_deletions::PyPersistentGraph, index::GraphIndex, node::{PyMutableNode, PyNode, PyNodes}, + node_state::node_state, properties::{PyConstProperties, PyProperties, PyTemporalProp, PyTemporalProperties}, views::graph_view::PyGraphView, }, @@ -30,7 +31,7 @@ use crate::{ utils::PyWindowSet, }, }; -use pyo3::prelude::*; +use pyo3::{prelude::*, wrap_pymodule}; pub fn add_raphtory_classes(m: &Bound) -> PyResult<()> { //Graph classes @@ -56,6 +57,7 @@ pub fn add_raphtory_classes(m: &Bound) -> PyResult<()> { AlgorithmResult, GraphIndex ); + m.add_wrapped(wrap_pymodule!(node_state))?; #[cfg(feature = "storage")] add_classes!(m, PyDiskGraph); From 796c5335d6cf98afded3746dfd8dd07fbd672969 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Fri, 15 Nov 2024 11:14:01 +0100 Subject: [PATCH 16/23] expose node state for docs --- python/src/lib.rs | 11 +- raphtory/src/db/api/state/lazy_node_state.rs | 5 +- raphtory/src/python/graph/node_state.rs | 853 ++++++++++--------- raphtory/src/python/packages/base_modules.rs | 6 +- 4 files changed, 449 insertions(+), 426 deletions(-) diff --git a/python/src/lib.rs b/python/src/lib.rs index fa522ca90..91e010ef0 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -1,8 +1,11 @@ extern crate core; use pyo3::prelude::*; -use raphtory_core::python::packages::base_modules::{ - add_raphtory_classes, base_algorithm_module, base_graph_gen_module, base_graph_loader_module, - base_vectors_module, +use raphtory_core::python::{ + graph::node_state::base_node_state_module, + packages::base_modules::{ + add_raphtory_classes, base_algorithm_module, base_graph_gen_module, + base_graph_loader_module, base_vectors_module, + }, }; use raphtory_graphql::python::pymodule::base_graphql_module; @@ -16,10 +19,12 @@ fn raphtory(py: Python<'_>, m: &Bound) -> PyResult<()> { let graph_loader_module = base_graph_loader_module(py)?; let graph_gen_module = base_graph_gen_module(py)?; let vectors_module = base_vectors_module(py)?; + let node_state_module = base_node_state_module(py)?; m.add_submodule(&graphql_module)?; m.add_submodule(&algorithm_module)?; m.add_submodule(&graph_loader_module)?; m.add_submodule(&graph_gen_module)?; m.add_submodule(&vectors_module)?; + m.add_submodule(&node_state_module)?; Ok(()) } diff --git a/raphtory/src/db/api/state/lazy_node_state.rs b/raphtory/src/db/api/state/lazy_node_state.rs index 4a3f60d22..65a1f0fdf 100644 --- a/raphtory/src/db/api/state/lazy_node_state.rs +++ b/raphtory/src/db/api/state/lazy_node_state.rs @@ -254,9 +254,6 @@ mod test { fn test_compile() { let g = Graph::new(); g.add_edge(0, 0, 1, NO_PROPS, None).unwrap(); - - let nodes = g.nodes(); - let deg = g.nodes().degree(); assert_eq!(deg.collect_vec(), [1, 1]); @@ -279,6 +276,6 @@ mod test { assert_eq!(dyn_deg, [1, 1]); assert_eq!(arc_deg.apply(g.core_graph(), VID(0)), 1); - let test_struct = TestWrapper(arc_deg); + let _test_struct = TestWrapper(arc_deg); } } diff --git a/raphtory/src/python/graph/node_state.rs b/raphtory/src/python/graph/node_state.rs index f2fe8d789..81edac361 100644 --- a/raphtory/src/python/graph/node_state.rs +++ b/raphtory/src/python/graph/node_state.rs @@ -1,376 +1,371 @@ use pyo3::prelude::*; -#[pymodule(submodule)] -pub mod node_state { - use crate::{ - core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, - db::{ - api::{ - state::{ops, LazyNodeState, NodeOp, NodeState, NodeStateOps, OrderedNodeStateOps}, - view::{ - internal::Static, DynamicGraph, GraphViewOps, IntoDynHop, IntoDynamic, - StaticGraphViewOps, - }, +use crate::{ + add_classes, + core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, + db::{ + api::{ + state::{ops, LazyNodeState, NodeOp, NodeState, NodeStateOps, OrderedNodeStateOps}, + view::{ + internal::Static, DynamicGraph, GraphViewOps, IntoDynHop, IntoDynamic, + StaticGraphViewOps, }, - graph::node::NodeView, }, - prelude::*, - py_borrowing_iter, - python::{ - types::{repr::Repr, wrappers::iterators::PyBorrowingIterator}, - utils::PyNodeRef, - }, - }; - use chrono::{DateTime, Utc}; - use pyo3::{ - exceptions::{PyKeyError, PyTypeError}, - prelude::*, - types::PyNotImplemented, - }; - use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; - use std::{collections::HashMap, sync::Arc}; - - macro_rules! impl_node_state_ops { - ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { - impl $name { - pub fn iter(&self) -> impl Iterator + '_ { - self.inner.values().map($to_owned) - } + graph::node::NodeView, + }, + prelude::*, + py_borrowing_iter, + python::{ + types::{repr::Repr, wrappers::iterators::PyBorrowingIterator}, + utils::PyNodeRef, + }, +}; +use chrono::{DateTime, Utc}; +use pyo3::{ + exceptions::{PyKeyError, PyTypeError}, + types::PyNotImplemented, +}; +use raphtory_api::core::{entities::GID, storage::arc_str::ArcStr}; +use std::{collections::HashMap, sync::Arc}; + +macro_rules! impl_node_state_ops { + ($name:ident, $value:ty, $inner_t:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { + impl $name { + pub fn iter(&self) -> impl Iterator + '_ { + self.inner.values().map($to_owned) } + } - #[pymethods] - impl $name { - fn __len__(&self) -> usize { - self.inner.len() - } + #[pymethods] + impl $name { + fn __len__(&self) -> usize { + self.inner.len() + } - /// Iterate over nodes - /// - /// Returns: - /// Iterator[Node] - fn nodes(&self) -> PyBorrowingIterator { - py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| { - inner.nodes().map(|n| n.cloned()) - }) - } + /// Iterate over nodes + /// + /// Returns: + /// Iterator[Node] + fn nodes(&self) -> PyBorrowingIterator { + py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| { + inner.nodes().map(|n| n.cloned()) + }) + } - fn __iter__(&self) -> PyBorrowingIterator { - py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner - .values() - .map($to_owned)) - } + fn __iter__(&self) -> PyBorrowingIterator { + py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner + .values() + .map($to_owned)) + } - fn __getitem__(&self, node: PyNodeRef) -> PyResult<$value> { - let node = node.as_node_ref(); - self.inner - .get_by_node(node) - .map($to_owned) - .ok_or_else(|| match node { - NodeRef::External(id) => { - PyKeyError::new_err(format!("Missing value for node with id {id}")) - } - NodeRef::Internal(vid) => { - let node = self.inner.graph().node(vid); - match node { - Some(node) => PyKeyError::new_err(format!( - "Missing value {}", - node.repr() - )), - None => PyTypeError::new_err("Invalid node reference"), + fn __getitem__(&self, node: PyNodeRef) -> PyResult<$value> { + let node = node.as_node_ref(); + self.inner + .get_by_node(node) + .map($to_owned) + .ok_or_else(|| match node { + NodeRef::External(id) => { + PyKeyError::new_err(format!("Missing value for node with id {id}")) + } + NodeRef::Internal(vid) => { + let node = self.inner.graph().node(vid); + match node { + Some(node) => { + PyKeyError::new_err(format!("Missing value {}", node.repr())) } + None => PyTypeError::new_err("Invalid node reference"), } - }) - } + } + }) + } - /// Returns: - #[doc = concat!(" Iterator[Tuple[Node, ", $py_value, "]]")] - fn items(&self) -> PyBorrowingIterator { - py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner - .iter() - .map(|(n, v)| (n.cloned(), ($to_owned)(v)))) - } + /// Returns: + #[doc = concat!(" Iterator[Tuple[Node, ", $py_value, "]]")] + fn items(&self) -> PyBorrowingIterator { + py_borrowing_iter!(self.inner.clone(), $inner_t, |inner| inner + .iter() + .map(|(n, v)| (n.cloned(), ($to_owned)(v)))) + } - /// Returns: - #[doc = concat!(" Iterator[",$py_value, "]")] - fn values(&self) -> PyBorrowingIterator { - self.__iter__() - } + /// Returns: + #[doc = concat!(" Iterator[",$py_value, "]")] + fn values(&self) -> PyBorrowingIterator { + self.__iter__() + } - /// Sort results by node id - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn sorted_by_id(&self) -> NodeState<'static, $value, DynamicGraph> { - self.inner.sort_by_id() - } + /// Sort results by node id + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn sorted_by_id(&self) -> NodeState<'static, $value, DynamicGraph> { + self.inner.sort_by_id() + } - fn __repr__(&self) -> String { - self.inner.repr() - } + fn __repr__(&self) -> String { + self.inner.repr() } - }; - } + } + }; +} - macro_rules! impl_node_state_ord_ops { - ($name:ident, $value:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { - #[pymethods] - impl $name { - /// Sort by value - /// - /// Arguments: - /// reverse (bool): If `True`, sort in descending order, otherwise ascending - /// - /// Returns: - #[doc = concat!(" ", $computed)] - #[pyo3(signature = (reverse = false))] - fn sorted(&self, reverse: bool) -> NodeState<'static, $value, DynamicGraph> { - self.inner.sort_by_values(reverse) - } +macro_rules! impl_node_state_ord_ops { + ($name:ident, $value:ty, $to_owned:expr, $computed:literal, $py_value:literal) => { + #[pymethods] + impl $name { + /// Sort by value + /// + /// Arguments: + /// reverse (bool): If `True`, sort in descending order, otherwise ascending + /// + /// Returns: + #[doc = concat!(" ", $computed)] + #[pyo3(signature = (reverse = false))] + fn sorted(&self, reverse: bool) -> NodeState<'static, $value, DynamicGraph> { + self.inner.sort_by_values(reverse) + } - /// Compute the k largest values - /// - /// Arguments: - /// k (int): The number of values to return - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn top_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { - self.inner.top_k(k) - } + /// Compute the k largest values + /// + /// Arguments: + /// k (int): The number of values to return + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn top_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { + self.inner.top_k(k) + } - /// Compute the k smallest values - /// - /// Arguments: - /// k (int): The number of values to return - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn bottom_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { - self.inner.bottom_k(k) - } + /// Compute the k smallest values + /// + /// Arguments: + /// k (int): The number of values to return + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn bottom_k(&self, k: usize) -> NodeState<'static, $value, DynamicGraph> { + self.inner.bottom_k(k) + } - /// Return smallest value and corresponding node - /// - /// Returns: - #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] - fn min_item(&self) -> Option<(NodeView, $value)> { - self.inner - .min_item() - .map(|(n, v)| (n.cloned(), ($to_owned)(v))) - } + /// Return smallest value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] + fn min_item(&self) -> Option<(NodeView, $value)> { + self.inner + .min_item() + .map(|(n, v)| (n.cloned(), ($to_owned)(v))) + } - /// Return the minimum value - /// - /// Returns: - #[doc = concat!(" Optional[", $py_value, "]")] - fn min(&self) -> Option<$value> { - self.inner.min().map($to_owned) - } + /// Return the minimum value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] + fn min(&self) -> Option<$value> { + self.inner.min().map($to_owned) + } - /// Return largest value and corresponding node - /// - /// Returns: - #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] - fn max_item(&self) -> Option<(NodeView, $value)> { - self.inner - .max_item() - .map(|(n, v)| (n.cloned(), ($to_owned)(v))) - } + /// Return largest value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] + fn max_item(&self) -> Option<(NodeView, $value)> { + self.inner + .max_item() + .map(|(n, v)| (n.cloned(), ($to_owned)(v))) + } - /// Return the maximum value - /// - /// Returns: - #[doc = concat!(" Optional[", $py_value, "]")] - fn max(&self) -> Option<$value> { - self.inner.max().map($to_owned) - } + /// Return the maximum value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] + fn max(&self) -> Option<$value> { + self.inner.max().map($to_owned) + } - /// Return the median value - /// - /// Returns: - #[doc = concat!(" Optional[", $py_value, "]")] - fn median(&self) -> Option<$value> { - self.inner.median().map($to_owned) - } + /// Return the median value + /// + /// Returns: + #[doc = concat!(" Optional[", $py_value, "]")] + fn median(&self) -> Option<$value> { + self.inner.median().map($to_owned) + } - /// Return medain value and corresponding node - /// - /// Returns: - #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] - fn median_item(&self) -> Option<(NodeView, $value)> { - self.inner - .median_item() - .map(|(n, v)| (n.cloned(), ($to_owned)(v))) - } + /// Return medain value and corresponding node + /// + /// Returns: + #[doc = concat!(" Optional[Tuple[Node, ", $py_value,"]]")] + fn median_item(&self) -> Option<(NodeView, $value)> { + self.inner + .median_item() + .map(|(n, v)| (n.cloned(), ($to_owned)(v))) + } - fn __eq__<'py>(&self, other: &Bound<'py, PyAny>, py: Python<'py>) -> PyObject { - if let Ok(other) = other.downcast::() { - let other = Bound::borrow(other); - return self.inner.values().eq(other.inner.values()).into_py(py); - } else if let Ok(other) = other.extract::>() { - return self - .inner - .values() - .map($to_owned) - .eq(other.into_iter()) - .into_py(py); - } else if let Ok(other) = other.extract::>() { - return (self.inner.len() == other.len() - && other.into_iter().all(|(node, value)| { - self.inner.get_by_node(node).map($to_owned) == Some(value) - })) + fn __eq__<'py>(&self, other: &Bound<'py, PyAny>, py: Python<'py>) -> PyObject { + if let Ok(other) = other.downcast::() { + let other = Bound::borrow(other); + return self.inner.values().eq(other.inner.values()).into_py(py); + } else if let Ok(other) = other.extract::>() { + return self + .inner + .values() + .map($to_owned) + .eq(other.into_iter()) .into_py(py); - } - PyNotImplemented::get_bound(py).into_py(py) + } else if let Ok(other) = other.extract::>() { + return (self.inner.len() == other.len() + && other.into_iter().all(|(node, value)| { + self.inner.get_by_node(node).map($to_owned) == Some(value) + })) + .into_py(py); } + PyNotImplemented::get_bound(py).into_py(py) } - }; - } - - macro_rules! impl_node_state_num_ops { - ($name:ident, $value:ty, $py_value:literal) => { - #[pymethods] - impl $name { - /// sum of values over all nodes - /// - /// Returns: - #[doc= concat!(" ", $py_value)] - fn sum(&self) -> $value { - self.inner.sum() - } + } + }; +} - /// mean of values over all nodes - /// - /// Returns: - /// float - fn mean(&self) -> f64 { - self.inner.mean() - } +macro_rules! impl_node_state_num_ops { + ($name:ident, $value:ty, $py_value:literal) => { + #[pymethods] + impl $name { + /// sum of values over all nodes + /// + /// Returns: + #[doc= concat!(" ", $py_value)] + fn sum(&self) -> $value { + self.inner.sum() } - }; - } - macro_rules! impl_lazy_node_state { - ($name:ident<$op:ty>, $computed:literal, $py_value:literal) => { - /// A lazy view over node values - #[pyclass(module = "raphtory.node_state", frozen)] - pub struct $name { - inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, + /// mean of values over all nodes + /// + /// Returns: + /// float + fn mean(&self) -> f64 { + self.inner.mean() } + } + }; +} - #[pymethods] - impl $name { - /// Compute all values and return the result as a node view - /// - /// Returns: - #[doc = concat!(" ", $computed)] - fn compute( - &self, - ) -> NodeState<'static, <$op as NodeOp>::Output, DynamicGraph, DynamicGraph> { - self.inner.compute() - } +macro_rules! impl_lazy_node_state { + ($name:ident<$op:ty>, $computed:literal, $py_value:literal) => { + /// A lazy view over node values + #[pyclass(module = "node_state", frozen)] + pub struct $name { + inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, + } - /// Compute all values and return the result as a list - /// - /// Returns - #[doc = concat!(" list[", $py_value, "]")] - fn collect(&self) -> Vec<<$op as NodeOp>::Output> { - self.inner.collect() - } + #[pymethods] + impl $name { + /// Compute all values and return the result as a node view + /// + /// Returns: + #[doc = concat!(" ", $computed)] + fn compute( + &self, + ) -> NodeState<'static, <$op as NodeOp>::Output, DynamicGraph, DynamicGraph> { + self.inner.compute() } - impl_node_state_ops!( - $name, - <$op as NodeOp>::Output, - LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, - |v: <$op as NodeOp>::Output| v, - $computed, - $py_value - ); - - impl From> for $name { - fn from(inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>) -> Self { - $name { inner } - } + /// Compute all values and return the result as a list + /// + /// Returns + #[doc = concat!(" list[", $py_value, "]")] + fn collect(&self) -> Vec<<$op as NodeOp>::Output> { + self.inner.collect() } + } - impl pyo3::IntoPy - for LazyNodeState<'static, $op, DynamicGraph, DynamicGraph> - { - fn into_py(self, py: Python<'_>) -> PyObject { - $name::from(self).into_py(py) - } + impl_node_state_ops!( + $name, + <$op as NodeOp>::Output, + LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, + |v: <$op as NodeOp>::Output| v, + $computed, + $py_value + ); + + impl From> for $name { + fn from(inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>) -> Self { + $name { inner } } - }; - } + } - macro_rules! impl_node_state { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - #[pyclass(module = "raphtory.node_state", frozen)] - pub struct $name { - inner: Arc>, + impl pyo3::IntoPy for LazyNodeState<'static, $op, DynamicGraph, DynamicGraph> { + fn into_py(self, py: Python<'_>) -> PyObject { + $name::from(self).into_py(py) } + } + }; +} - impl_node_state_ops!( - $name, - $value, - Arc>, - |v: &$value| v.clone(), - $computed, - $py_value - ); - - impl From> for $name { - fn from(inner: NodeState<'static, $value, DynamicGraph, DynamicGraph>) -> Self { - $name { - inner: inner.into(), - } +macro_rules! impl_node_state { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + #[pyclass(module = "node_state", frozen)] + pub struct $name { + inner: Arc>, + } + + impl_node_state_ops!( + $name, + $value, + Arc>, + |v: &$value| v.clone(), + $computed, + $py_value + ); + + impl From> for $name { + fn from(inner: NodeState<'static, $value, DynamicGraph, DynamicGraph>) -> Self { + $name { + inner: inner.into(), } } + } - impl pyo3::IntoPy for NodeState<'static, $value, DynamicGraph, DynamicGraph> { - fn into_py(self, py: Python<'_>) -> PyObject { - $name::from(self).into_py(py) - } + impl pyo3::IntoPy for NodeState<'static, $value, DynamicGraph, DynamicGraph> { + fn into_py(self, py: Python<'_>) -> PyObject { + $name::from(self).into_py(py) } - }; - } + } + }; +} - macro_rules! impl_lazy_node_state_ord { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_lazy_node_state!($name<$value>, $computed, $py_value); - impl_node_state_ord_ops!( - $name, - <$value as NodeOp>::Output, - |v: <$value as NodeOp>::Output| v, - $computed, - $py_value - ); - }; - } +macro_rules! impl_lazy_node_state_ord { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_lazy_node_state!($name<$value>, $computed, $py_value); + impl_node_state_ord_ops!( + $name, + <$value as NodeOp>::Output, + |v: <$value as NodeOp>::Output| v, + $computed, + $py_value + ); + }; +} - macro_rules! impl_node_state_ord { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_node_state!($name<$value>, $computed, $py_value); - impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone(), $computed, $py_value); - }; - } +macro_rules! impl_node_state_ord { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_node_state!($name<$value>, $computed, $py_value); + impl_node_state_ord_ops!($name, $value, |v: &$value| v.clone(), $computed, $py_value); + }; +} - macro_rules! impl_lazy_node_state_num { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_lazy_node_state_ord!($name<$value>, $computed, $py_value); - impl_node_state_num_ops!($name, <$value as NodeOp>::Output, $py_value); - }; - } +macro_rules! impl_lazy_node_state_num { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_lazy_node_state_ord!($name<$value>, $computed, $py_value); + impl_node_state_num_ops!($name, <$value as NodeOp>::Output, $py_value); + }; +} - macro_rules! impl_node_state_num { - ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - impl_node_state_ord!($name<$value>, $computed, $py_value); - impl_node_state_num_ops!($name, $value, $py_value); - }; - } +macro_rules! impl_node_state_num { + ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { + impl_node_state_ord!($name<$value>, $computed, $py_value); + impl_node_state_num_ops!($name, $value, $py_value); + }; +} - macro_rules! impl_one_hop { +macro_rules! impl_one_hop { ($name:ident<$($path:ident)::+>, $py_name:literal) => { impl IntoPy for LazyNodeState<'static, $($path)::+, DynamicGraph, DynamicGraph> @@ -385,100 +380,126 @@ pub mod node_state { } } - impl_lazy_node_state_num!( - DegreeView>, - "NodeStateUsize", - "int" - ); - impl_one_hop!(DegreeView, "DegreeView"); - - impl_node_state_num!(NodeStateUsize, "NodeStateUsize", "int"); - - impl_node_state_num!(NodeStateU64, "NodeStateU64", "int"); - - impl_lazy_node_state_ord!(IdView, "NodeStateGID", "GID"); - impl_node_state_ord!(NodeStateGID, "NodeStateGID", "GID"); - - impl_lazy_node_state_ord!( - EarliestTimeView>, - "NodeStateOptionI64", - "Optional[int]" - ); - impl_one_hop!(EarliestTimeView, "EarliestTimeView"); - impl_lazy_node_state_ord!( - LatestTimeView>, - "NodeStateOptionI64", - "Optional[int]" - ); - impl_one_hop!(LatestTimeView, "LatestTimeView"); - impl_node_state_ord!( - NodeStateOptionI64>, - "NodeStateOptionI64", - "Optional[int]" - ); - - impl_lazy_node_state_ord!(NameView, "NodeStateString", "str"); - impl_node_state_ord!(NodeStateString, "NodeStateString", "str"); - - type EarliestDateTime = ops::Map, Option>>; - impl_lazy_node_state_ord!( - EarliestDateTimeView>, - "NodeStateOptionDateTime", - "Optional[Datetime]" - ); - impl_one_hop!( - EarliestDateTimeView, - "EarliestDateTimeView" - ); - - type LatestDateTime = ops::Map, Option>>; - impl_lazy_node_state_ord!( - LatestDateTimeView, Option>>>, - "NodeStateOptionDateTime", - "Optional[Datetime]" - ); - impl_one_hop!(LatestDateTimeView, "LatestDateTimeView"); - impl_node_state_ord!( - NodeStateOptionDateTime>>, - "NodeStateOptionDateTime", - "Optional[Datetime]" - ); - - impl_lazy_node_state_ord!( - HistoryView>, - "NodeStateListI64", - "list[int]" - ); - impl_one_hop!(HistoryView, "HistoryView"); - impl_node_state_ord!(NodeStateListI64>, "NodeStateListI64", "list[int]"); - - type HistoryDateTime = ops::Map, Option>>>; - impl_lazy_node_state_ord!( - HistoryDateTimeView>, - "NodeStateOptionListDateTime", - "Optional[list[Datetime]]" - ); - impl_one_hop!(HistoryDateTimeView, "HistoryDateTimeView"); - impl_node_state_ord!( - NodeStateOptionListDateTime>>>, - "NodeStateOptionListDateTime", - "Optional[list[Datetime]]" - ); - - impl_lazy_node_state_ord!( - NodeTypeView, - "NodeStateOptionStr", - "Optional[str]" - ); - impl_node_state_ord!( - NodeStateOptionStr>, - "NodeStateOptionStr", - "Optional[str]" - ); - - impl_node_state_ord!( - NodeStateListDateTime>>, - "NodeStateListDateTime", - "list[Datetime]" +impl_lazy_node_state_num!( + DegreeView>, + "NodeStateUsize", + "int" +); +impl_one_hop!(DegreeView, "DegreeView"); + +impl_node_state_num!(NodeStateUsize, "NodeStateUsize", "int"); + +impl_node_state_num!(NodeStateU64, "NodeStateU64", "int"); + +impl_lazy_node_state_ord!(IdView, "NodeStateGID", "GID"); +impl_node_state_ord!(NodeStateGID, "NodeStateGID", "GID"); + +impl_lazy_node_state_ord!( + EarliestTimeView>, + "NodeStateOptionI64", + "Optional[int]" +); +impl_one_hop!(EarliestTimeView, "EarliestTimeView"); +impl_lazy_node_state_ord!( + LatestTimeView>, + "NodeStateOptionI64", + "Optional[int]" +); +impl_one_hop!(LatestTimeView, "LatestTimeView"); +impl_node_state_ord!( + NodeStateOptionI64>, + "NodeStateOptionI64", + "Optional[int]" +); + +impl_lazy_node_state_ord!(NameView, "NodeStateString", "str"); +impl_node_state_ord!(NodeStateString, "NodeStateString", "str"); + +type EarliestDateTime = ops::Map, Option>>; +impl_lazy_node_state_ord!( + EarliestDateTimeView>, + "NodeStateOptionDateTime", + "Optional[Datetime]" +); +impl_one_hop!( + EarliestDateTimeView, + "EarliestDateTimeView" +); + +type LatestDateTime = ops::Map, Option>>; +impl_lazy_node_state_ord!( + LatestDateTimeView, Option>>>, + "NodeStateOptionDateTime", + "Optional[Datetime]" +); +impl_one_hop!(LatestDateTimeView, "LatestDateTimeView"); +impl_node_state_ord!( + NodeStateOptionDateTime>>, + "NodeStateOptionDateTime", + "Optional[Datetime]" +); + +impl_lazy_node_state_ord!( + HistoryView>, + "NodeStateListI64", + "list[int]" +); +impl_one_hop!(HistoryView, "HistoryView"); +impl_node_state_ord!(NodeStateListI64>, "NodeStateListI64", "list[int]"); + +type HistoryDateTime = ops::Map, Option>>>; +impl_lazy_node_state_ord!( + HistoryDateTimeView>, + "NodeStateOptionListDateTime", + "Optional[list[Datetime]]" +); +impl_one_hop!(HistoryDateTimeView, "HistoryDateTimeView"); +impl_node_state_ord!( + NodeStateOptionListDateTime>>>, + "NodeStateOptionListDateTime", + "Optional[list[Datetime]]" +); + +impl_lazy_node_state_ord!( + NodeTypeView, + "NodeStateOptionStr", + "Optional[str]" +); +impl_node_state_ord!( + NodeStateOptionStr>, + "NodeStateOptionStr", + "Optional[str]" +); + +impl_node_state_ord!( + NodeStateListDateTime>>, + "NodeStateListDateTime", + "list[Datetime]" +); + +pub fn base_node_state_module(py: Python<'_>) -> PyResult> { + let m = PyModule::new_bound(py, "node_state")?; + add_classes!( + &m, + DegreeView, + NodeStateUsize, + NodeStateU64, + IdView, + NodeStateGID, + EarliestTimeView, + LatestTimeView, + NameView, + NodeStateString, + EarliestDateTimeView, + LatestDateTimeView, + NodeStateOptionDateTime, + HistoryView, + NodeStateListI64, + HistoryDateTimeView, + NodeStateOptionListDateTime, + NodeTypeView, + NodeStateOptionStr, + NodeStateListDateTime ); + Ok(m) } diff --git a/raphtory/src/python/packages/base_modules.rs b/raphtory/src/python/packages/base_modules.rs index 844e82615..792b6c8c0 100644 --- a/raphtory/src/python/packages/base_modules.rs +++ b/raphtory/src/python/packages/base_modules.rs @@ -14,7 +14,6 @@ use crate::{ graph_with_deletions::PyPersistentGraph, index::GraphIndex, node::{PyMutableNode, PyNode, PyNodes}, - node_state::node_state, properties::{PyConstProperties, PyProperties, PyTemporalProp, PyTemporalProperties}, views::graph_view::PyGraphView, }, @@ -31,7 +30,7 @@ use crate::{ utils::PyWindowSet, }, }; -use pyo3::{prelude::*, wrap_pymodule}; +use pyo3::prelude::*; pub fn add_raphtory_classes(m: &Bound) -> PyResult<()> { //Graph classes @@ -57,7 +56,6 @@ pub fn add_raphtory_classes(m: &Bound) -> PyResult<()> { AlgorithmResult, GraphIndex ); - m.add_wrapped(wrap_pymodule!(node_state))?; #[cfg(feature = "storage")] add_classes!(m, PyDiskGraph); @@ -145,3 +143,5 @@ pub fn base_vectors_module(py: Python<'_>) -> Result, PyErr> { vectors_module.add_class::()?; Ok(vectors_module) } + +pub use crate::python::graph::node_state::base_node_state_module; From 3e3121e125574091bdf5d0ba5054b5cd95c49974 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Fri, 15 Nov 2024 11:28:07 +0100 Subject: [PATCH 17/23] properly add the node_state module to stubs --- python/python/raphtory/__init__.py | 1 + .../python/raphtory/node_state/__init__.pyi | 4773 +++++++++++++++++ raphtory/src/python/graph/node_state.rs | 4 +- 3 files changed, 4776 insertions(+), 2 deletions(-) create mode 100644 python/python/raphtory/node_state/__init__.pyi diff --git a/python/python/raphtory/__init__.py b/python/python/raphtory/__init__.py index 494f6be6e..a043b3a28 100644 --- a/python/python/raphtory/__init__.py +++ b/python/python/raphtory/__init__.py @@ -1,6 +1,7 @@ import sys from .raphtory import * +sys.modules["raphtory.node_state"] = node_state sys.modules["raphtory.algorithms"] = algorithms sys.modules["raphtory.graph_gen"] = graph_gen sys.modules["raphtory.graph_loader"] = graph_loader diff --git a/python/python/raphtory/node_state/__init__.pyi b/python/python/raphtory/node_state/__init__.pyi new file mode 100644 index 000000000..6828d400e --- /dev/null +++ b/python/python/raphtory/node_state/__init__.pyi @@ -0,0 +1,4773 @@ +############################################################################### +# # +# AUTOGENERATED TYPE STUB FILE # +# # +# This file was automatically generated. Do not modify it directly. # +# Any changes made here may be lost when the file is regenerated. # +# # +############################################################################### + +from typing import * +from raphtory import * +from raphtory.vectors import * +from raphtory.graphql import * +from raphtory.typing import * +from datetime import datetime +from pandas import DataFrame + +class DegreeView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self DegreeView: + """ + Return a view of DegreeView containing only the default edge layer + Returns: + DegreeView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this DegreeView is valid. + + Returns: + Optional[int]: The latest time that this DegreeView is valid or None if the DegreeView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this DegreeView is valid + + Returns: + Optional[Datetime]: The latest datetime that this DegreeView is valid or None if the DegreeView is valid for all times. + """ + + def exclude_layer(self, name: str) -> DegreeView: + """ + Return a view of DegreeView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + DegreeView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> DegreeView: + """ + Return a view of DegreeView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + DegreeView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> DegreeView: + """ + Return a view of DegreeView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + DegreeView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> DegreeView: + """ + Return a view of DegreeView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + DegreeView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if DegreeView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, int]] + """ + + def latest(self): + """ + Create a view of the DegreeView including all events at the latest time. + + Returns: + DegreeView + """ + + def layer(self, name) -> DegreeView: + """ + Return a view of DegreeView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + DegreeView: The layered view + """ + + def layers(self, names: list[str]) -> DegreeView: + """ + Return a view of DegreeView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + DegreeView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[int] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, int]] + """ + + def mean(self): + """ + mean of values over all nodes + + Returns: + float + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[int] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, int]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[int] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, int]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + DegreeView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + DegreeView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the DegreeView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + DegreeView + """ + + def snapshot_latest(self): + """ + Create a view of the DegreeView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + DegreeView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateUsize + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateUsize + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this DegreeView + + Returns: + Optional[int]: The earliest time that this DegreeView is valid or None if the DegreeView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this DegreeView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this DegreeView is valid or None if the DegreeView is valid for all times. + """ + + def sum(self): + """ + sum of values over all nodes + + Returns: + int + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateUsize + """ + + def valid_layers(self, names: list[str]) -> DegreeView: + """ + Return a view of DegreeView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + DegreeView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[int] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the DegreeView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r DegreeView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this DegreeView + + Returns: + Optional[int] + """ + +class EarliestDateTimeView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing only the default edge layer + Returns: + EarliestDateTimeView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this EarliestDateTimeView is valid. + + Returns: + Optional[int]: The latest time that this EarliestDateTimeView is valid or None if the EarliestDateTimeView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this EarliestDateTimeView is valid + + Returns: + Optional[Datetime]: The latest datetime that this EarliestDateTimeView is valid or None if the EarliestDateTimeView is valid for all times. + """ + + def exclude_layer(self, name: str) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + EarliestDateTimeView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + EarliestDateTimeView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + EarliestDateTimeView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + EarliestDateTimeView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if EarliestDateTimeView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, Optional[Datetime]]] + """ + + def latest(self): + """ + Create a view of the EarliestDateTimeView including all events at the latest time. + + Returns: + EarliestDateTimeView + """ + + def layer(self, name) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + EarliestDateTimeView: The layered view + """ + + def layers(self, names: list[str]) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + EarliestDateTimeView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[Optional[Datetime]] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[Datetime]]] + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[Optional[Datetime]] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[Datetime]]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[Optional[Datetime]] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[Datetime]]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + EarliestDateTimeView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + EarliestDateTimeView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the EarliestDateTimeView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + EarliestDateTimeView + """ + + def snapshot_latest(self): + """ + Create a view of the EarliestDateTimeView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + EarliestDateTimeView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateOptionDateTime + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateOptionDateTime + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this EarliestDateTimeView + + Returns: + Optional[int]: The earliest time that this EarliestDateTimeView is valid or None if the EarliestDateTimeView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this EarliestDateTimeView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this EarliestDateTimeView is valid or None if the EarliestDateTimeView is valid for all times. + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateOptionDateTime + """ + + def valid_layers(self, names: list[str]) -> EarliestDateTimeView: + """ + Return a view of EarliestDateTimeView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + EarliestDateTimeView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[Optional[Datetime]] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the EarliestDateTimeView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r EarliestDateTimeView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this EarliestDateTimeView + + Returns: + Optional[int] + """ + +class EarliestTimeView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self EarliestTimeView: + """ + Return a view of EarliestTimeView containing only the default edge layer + Returns: + EarliestTimeView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this EarliestTimeView is valid. + + Returns: + Optional[int]: The latest time that this EarliestTimeView is valid or None if the EarliestTimeView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this EarliestTimeView is valid + + Returns: + Optional[Datetime]: The latest datetime that this EarliestTimeView is valid or None if the EarliestTimeView is valid for all times. + """ + + def exclude_layer(self, name: str) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + EarliestTimeView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + EarliestTimeView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + EarliestTimeView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + EarliestTimeView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if EarliestTimeView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, Optional[int]]] + """ + + def latest(self): + """ + Create a view of the EarliestTimeView including all events at the latest time. + + Returns: + EarliestTimeView + """ + + def layer(self, name) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + EarliestTimeView: The layered view + """ + + def layers(self, names: list[str]) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + EarliestTimeView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[Optional[int]] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[int]]] + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[Optional[int]] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[int]]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[Optional[int]] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[int]]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + EarliestTimeView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + EarliestTimeView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the EarliestTimeView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + EarliestTimeView + """ + + def snapshot_latest(self): + """ + Create a view of the EarliestTimeView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + EarliestTimeView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateOptionI64 + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateOptionI64 + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this EarliestTimeView + + Returns: + Optional[int]: The earliest time that this EarliestTimeView is valid or None if the EarliestTimeView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this EarliestTimeView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this EarliestTimeView is valid or None if the EarliestTimeView is valid for all times. + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateOptionI64 + """ + + def valid_layers(self, names: list[str]) -> EarliestTimeView: + """ + Return a view of EarliestTimeView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + EarliestTimeView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[Optional[int]] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the EarliestTimeView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r EarliestTimeView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this EarliestTimeView + + Returns: + Optional[int] + """ + +class HistoryDateTimeView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing only the default edge layer + Returns: + HistoryDateTimeView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this HistoryDateTimeView is valid. + + Returns: + Optional[int]: The latest time that this HistoryDateTimeView is valid or None if the HistoryDateTimeView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this HistoryDateTimeView is valid + + Returns: + Optional[Datetime]: The latest datetime that this HistoryDateTimeView is valid or None if the HistoryDateTimeView is valid for all times. + """ + + def exclude_layer(self, name: str) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + HistoryDateTimeView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + HistoryDateTimeView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + HistoryDateTimeView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + HistoryDateTimeView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if HistoryDateTimeView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, Optional[list[Datetime]]]] + """ + + def latest(self): + """ + Create a view of the HistoryDateTimeView including all events at the latest time. + + Returns: + HistoryDateTimeView + """ + + def layer(self, name) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + HistoryDateTimeView: The layered view + """ + + def layers(self, names: list[str]) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + HistoryDateTimeView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[Optional[list[Datetime]]] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[list[Datetime]]]] + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[Optional[list[Datetime]]] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[list[Datetime]]]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[Optional[list[Datetime]]] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[list[Datetime]]]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + HistoryDateTimeView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + HistoryDateTimeView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the HistoryDateTimeView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + HistoryDateTimeView + """ + + def snapshot_latest(self): + """ + Create a view of the HistoryDateTimeView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + HistoryDateTimeView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateOptionListDateTime + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateOptionListDateTime + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this HistoryDateTimeView + + Returns: + Optional[int]: The earliest time that this HistoryDateTimeView is valid or None if the HistoryDateTimeView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this HistoryDateTimeView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this HistoryDateTimeView is valid or None if the HistoryDateTimeView is valid for all times. + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateOptionListDateTime + """ + + def valid_layers(self, names: list[str]) -> HistoryDateTimeView: + """ + Return a view of HistoryDateTimeView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + HistoryDateTimeView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[Optional[list[Datetime]]] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the HistoryDateTimeView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r HistoryDateTimeView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this HistoryDateTimeView + + Returns: + Optional[int] + """ + +class HistoryView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self HistoryView: + """ + Return a view of HistoryView containing only the default edge layer + Returns: + HistoryView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this HistoryView is valid. + + Returns: + Optional[int]: The latest time that this HistoryView is valid or None if the HistoryView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this HistoryView is valid + + Returns: + Optional[Datetime]: The latest datetime that this HistoryView is valid or None if the HistoryView is valid for all times. + """ + + def exclude_layer(self, name: str) -> HistoryView: + """ + Return a view of HistoryView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + HistoryView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> HistoryView: + """ + Return a view of HistoryView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + HistoryView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> HistoryView: + """ + Return a view of HistoryView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + HistoryView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> HistoryView: + """ + Return a view of HistoryView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + HistoryView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if HistoryView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, list[int]]] + """ + + def latest(self): + """ + Create a view of the HistoryView including all events at the latest time. + + Returns: + HistoryView + """ + + def layer(self, name) -> HistoryView: + """ + Return a view of HistoryView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + HistoryView: The layered view + """ + + def layers(self, names: list[str]) -> HistoryView: + """ + Return a view of HistoryView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + HistoryView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[list[int]] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, list[int]]] + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[list[int]] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, list[int]]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[list[int]] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, list[int]]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + HistoryView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + HistoryView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the HistoryView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + HistoryView + """ + + def snapshot_latest(self): + """ + Create a view of the HistoryView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + HistoryView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateListI64 + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateListI64 + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this HistoryView + + Returns: + Optional[int]: The earliest time that this HistoryView is valid or None if the HistoryView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this HistoryView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this HistoryView is valid or None if the HistoryView is valid for all times. + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateListI64 + """ + + def valid_layers(self, names: list[str]) -> HistoryView: + """ + Return a view of HistoryView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + HistoryView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[list[int]] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the HistoryView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r HistoryView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this HistoryView + + Returns: + Optional[int] + """ + +class IdView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing only the default edge layer + Returns: + LatestDateTimeView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this LatestDateTimeView is valid. + + Returns: + Optional[int]: The latest time that this LatestDateTimeView is valid or None if the LatestDateTimeView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this LatestDateTimeView is valid + + Returns: + Optional[Datetime]: The latest datetime that this LatestDateTimeView is valid or None if the LatestDateTimeView is valid for all times. + """ + + def exclude_layer(self, name: str) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + LatestDateTimeView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + LatestDateTimeView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + LatestDateTimeView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + LatestDateTimeView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if LatestDateTimeView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, Optional[Datetime]]] + """ + + def latest(self): + """ + Create a view of the LatestDateTimeView including all events at the latest time. + + Returns: + LatestDateTimeView + """ + + def layer(self, name) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + LatestDateTimeView: The layered view + """ + + def layers(self, names: list[str]) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + LatestDateTimeView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[Optional[Datetime]] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[Datetime]]] + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[Optional[Datetime]] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[Datetime]]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[Optional[Datetime]] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[Datetime]]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + LatestDateTimeView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + LatestDateTimeView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the LatestDateTimeView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + LatestDateTimeView + """ + + def snapshot_latest(self): + """ + Create a view of the LatestDateTimeView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + LatestDateTimeView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateOptionDateTime + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateOptionDateTime + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this LatestDateTimeView + + Returns: + Optional[int]: The earliest time that this LatestDateTimeView is valid or None if the LatestDateTimeView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this LatestDateTimeView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this LatestDateTimeView is valid or None if the LatestDateTimeView is valid for all times. + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateOptionDateTime + """ + + def valid_layers(self, names: list[str]) -> LatestDateTimeView: + """ + Return a view of LatestDateTimeView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + LatestDateTimeView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[Optional[Datetime]] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the LatestDateTimeView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r LatestDateTimeView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this LatestDateTimeView + + Returns: + Optional[int] + """ + +class LatestTimeView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self LatestTimeView: + """ + Return a view of LatestTimeView containing only the default edge layer + Returns: + LatestTimeView: The layered view + """ + + @property + def end(self): + """ + Gets the latest time that this LatestTimeView is valid. + + Returns: + Optional[int]: The latest time that this LatestTimeView is valid or None if the LatestTimeView is valid for all times. + """ + + @property + def end_date_time(self): + """ + Gets the latest datetime that this LatestTimeView is valid + + Returns: + Optional[Datetime]: The latest datetime that this LatestTimeView is valid or None if the LatestTimeView is valid for all times. + """ + + def exclude_layer(self, name: str) -> LatestTimeView: + """ + Return a view of LatestTimeView containing all layers except the excluded `name` + Errors if any of the layers do not exist. + + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + LatestTimeView: The layered view + """ + + def exclude_layers(self, names: list[str]) -> LatestTimeView: + """ + Return a view of LatestTimeView containing all layers except the excluded `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + LatestTimeView: The layered view + """ + + def exclude_valid_layer(self, name: str) -> LatestTimeView: + """ + Return a view of LatestTimeView containing all layers except the excluded `name` + Arguments: + name (str): layer name that is excluded for the new view + + Returns: + LatestTimeView: The layered view + """ + + def exclude_valid_layers(self, names: list[str]) -> LatestTimeView: + """ + Return a view of LatestTimeView containing all layers except the excluded `names` + Arguments: + names (list[str]): list of layer names that are excluded for the new view + + Returns: + LatestTimeView: The layered view + """ + + def expanding(self, step: int | str) -> WindowSet: + """ + Creates a `WindowSet` with the given `step` size using an expanding window. + + An expanding window is a window that grows by `step` size at each iteration. + + Arguments: + step (int | str): The step size of the window. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def has_layer(self, name): + """Check if LatestTimeView has the layer `"name"`""" + + def items(self): + """ + Returns: + Iterator[Tuple[Node, Optional[int]]] + """ + + def latest(self): + """ + Create a view of the LatestTimeView including all events at the latest time. + + Returns: + LatestTimeView + """ + + def layer(self, name) -> LatestTimeView: + """ + Return a view of LatestTimeView containing the layer `"name"` + Errors if the layer does not exist + + Returns: + LatestTimeView: The layered view + """ + + def layers(self, names: list[str]) -> LatestTimeView: + """ + Return a view of LatestTimeView containing all layers `names` + Errors if any of the layers do not exist. + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + LatestTimeView: The layered view + """ + + def max(self): + """ + Return the maximum value + + Returns: + Optional[Optional[int]] + """ + + def max_item(self): + """ + Return largest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[int]]] + """ + + def median(self): + """ + Return the median value + + Returns: + Optional[Optional[int]] + """ + + def median_item(self): + """ + Return medain value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[int]]] + """ + + def min(self): + """ + Return the minimum value + + Returns: + Optional[Optional[int]] + """ + + def min_item(self): + """ + Return smallest value and corresponding node + + Returns: + Optional[Tuple[Node, Optional[int]]] + """ + + def nodes(self): + """ + Iterate over nodes + + Returns: + Iterator[Node] + """ + + def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: + """ + Creates a `WindowSet` with the given `window` size and optional `step` using a rolling window. + + A rolling window is a window that moves forward by `step` size at each iteration. + + Arguments: + window (int | str): The size of the window. + step (int | str | None): The step size of the window. + `step` defaults to `window`. + + Returns: + WindowSet: A `WindowSet` object. + """ + + def shrink_end(self, end: TimeInput): + """ + Set the end of the window to the smaller of `end` and `self.end()` + + Arguments: + end (TimeInput): the new end time of the window + Returns: + LatestTimeView + """ + + def shrink_start(self, start: TimeInput): + """ + Set the start of the window to the larger of `start` and `self.start()` + + Arguments: + start (TimeInput): the new start time of the window + + Returns: + LatestTimeView + """ + + def shrink_window(self, start: TimeInput, end: TimeInput): + """ + Shrink both the start and end of the window (same as calling `shrink_start` followed by `shrink_end` but more efficient) + + Arguments: + start (TimeInput): the new start time for the window + end (TimeInput): the new end time for the window + + """ + + def snapshot_at(self, time: TimeInput): + """ + Create a view of the LatestTimeView including all events that have not been explicitly deleted at `time`. + + This is equivalent to `before(time + 1)` for `EventGraph`s and `at(time)` for `PersitentGraph`s + + Arguments: + time (TimeInput): The time of the window. + + Returns: + LatestTimeView + """ + + def snapshot_latest(self): + """ + Create a view of the LatestTimeView including all events that have not been explicitly deleted at the latest time. + + This is equivalent to a no-op for `EventGraph`s and `latest()` for `PersitentGraph`s + + Returns: + LatestTimeView + """ + + def sorted(self, reverse: bool = False): + """ + Sort by value + + Arguments: + reverse (bool): If `True`, sort in descending order, otherwise ascending + + Returns: + NodeStateOptionI64 + """ + + def sorted_by_id(self): + """ + Sort results by node id + + Returns: + NodeStateOptionI64 + """ + + @property + def start(self): + """ + Gets the start time for rolling and expanding windows for this LatestTimeView + + Returns: + Optional[int]: The earliest time that this LatestTimeView is valid or None if the LatestTimeView is valid for all times. + """ + + @property + def start_date_time(self): + """ + Gets the earliest datetime that this LatestTimeView is valid + + Returns: + Optional[Datetime]: The earliest datetime that this LatestTimeView is valid or None if the LatestTimeView is valid for all times. + """ + + def top_k(self, k: int): + """ + Compute the k largest values + + Arguments: + k (int): The number of values to return + + Returns: + NodeStateOptionI64 + """ + + def valid_layers(self, names: list[str]) -> LatestTimeView: + """ + Return a view of LatestTimeView containing all layers `names` + Any layers that do not exist are ignored + + Arguments: + names (list[str]): list of layer names for the new view + + Returns: + LatestTimeView: The layered view + """ + + def values(self): + """ + Returns: + Iterator[Optional[int]] + """ + + def window(self, start: TimeInput | None, end: TimeInput | None): + """ + Create a view of the LatestTimeView including all events between `start` (inclusive) and `end` (exclusive) + + Arguments: + start (TimeInput | None): The start time of the window (unbounded if `None`). + end (TimeInput | None): The end time of the window (unbounded if `None`). + + Returns: + r LatestTimeView + """ + + @property + def window_size(self): + """ + Get the window size (difference between start and end) for this LatestTimeView + + Returns: + Optional[int] + """ + +class NameView(object): + """A lazy view over node values""" + + def __eq__(self, value): + """Return self==value.""" + + def __ge__(self, value): + """Return self>=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self=value.""" + + def __getitem__(self, key): + """Return self[key].""" + + def __gt__(self, value): + """Return self>value.""" + + def __iter__(self): + """Implement iter(self).""" + + def __le__(self, value): + """Return self<=value.""" + + def __len__(self): + """Return len(self).""" + + def __lt__(self, value): + """Return self, $computed:literal, $py_value:literal) => { /// A lazy view over node values - #[pyclass(module = "node_state", frozen)] + #[pyclass(module = "raphtory.node_state", frozen)] pub struct $name { inner: LazyNodeState<'static, $op, DynamicGraph, DynamicGraph>, } @@ -301,7 +301,7 @@ macro_rules! impl_lazy_node_state { macro_rules! impl_node_state { ($name:ident<$value:ty>, $computed:literal, $py_value:literal) => { - #[pyclass(module = "node_state", frozen)] + #[pyclass(module = "raphtory.node_state", frozen)] pub struct $name { inner: Arc>, } From 335b259731e7b8ea9c71253bf637efecfb5098c9 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Fri, 15 Nov 2024 12:54:55 +0100 Subject: [PATCH 18/23] add some tests and fix some doc strings --- python/python/raphtory/__init__.pyi | 83 ++++++++++--------- python/tests/test_graphdb/test_node_state.py | 26 ++++++ raphtory/src/python/graph/views/graph_view.rs | 53 ++++++------ 3 files changed, 98 insertions(+), 64 deletions(-) create mode 100644 python/tests/test_graphdb/test_node_state.py diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index 0d29c016c..a1a6e49a6 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -1759,28 +1759,28 @@ class GraphView(object): GraphView """ - def count_edges(self): + def count_edges(self) -> int: """ Number of edges in the graph Returns: - the number of edges in the graph + int: the number of edges in the graph """ - def count_nodes(self): + def count_nodes(self) -> int: """ Number of nodes in the graph Returns: - the number of nodes in the graph + int: the number of nodes in the graph """ - def count_temporal_edges(self): + def count_temporal_edges(self) -> int: """ Number of edges in the graph Returns: - the number of temporal edges in the graph + int: the number of temporal edges in the graph """ def default_layer(self) -> GraphView: @@ -1796,7 +1796,7 @@ class GraphView(object): DateTime of earliest activity in the graph Returns: - the datetime of the earliest activity in the graph + Optional[Datetime]: the datetime of the earliest activity in the graph """ @property @@ -1805,10 +1805,10 @@ class GraphView(object): Timestamp of earliest activity in the graph Returns: - the timestamp of the earliest activity in the graph + Optional[int]: the timestamp of the earliest activity in the graph """ - def edge(self, src: str or int, dst: str or int): + def edge(self, src: str or int, dst: str or int) -> Optional[Edge]: """ Gets the edge with the specified source and destination nodes @@ -1817,7 +1817,7 @@ class GraphView(object): dst (str or int): the destination node id Returns: - the edge with the specified source and destination nodes, or None if the edge does not exist + Optional[Edge]: the edge with the specified source and destination nodes, or None if the edge does not exist """ @property @@ -1826,7 +1826,7 @@ class GraphView(object): Gets all edges in the graph Returns: - the edges in the graph + Edges: the edges in the graph """ @property @@ -1871,15 +1871,15 @@ class GraphView(object): GraphView: The layered view """ - def exclude_nodes(self, nodes): + def exclude_nodes(self, nodes: list[InputNode]) -> GraphView: """ Returns a subgraph given a set of nodes that are excluded from the subgraph Arguments: - * `nodes`: set of nodes + nodes (list[InputNode]): set of nodes Returns: - GraphView - Returns the subgraph + GraphView: Returns the subgraph """ def exclude_valid_layer(self, name: str) -> GraphView: @@ -1951,25 +1951,25 @@ class GraphView(object): GraphView: The filtered view """ - def find_edges(self, properties_dict): + def find_edges(self, properties_dict) -> list[Edge]: """ Get the edges that match the properties name and value Arguments: - property_dict (dict): the properties name and value + property_dict (dict[str, Prop]): the properties name and value Returns: - the edges that match the properties name and value + list[Edge]: the edges that match the properties name and value """ - def find_nodes(self, properties_dict): + def find_nodes(self, properties_dict) -> list[Node]: """ Get the nodes that match the properties name and value Arguments: - property_dict (dict): the properties name and value + property_dict (dict[str, Prop]): the properties name and value Returns: - the nodes that match the properties name and value + list[Node]: the nodes that match the properties name and value """ - def has_edge(self, src: str or int, dst: str or int): + def has_edge(self, src: str or int, dst: str or int) -> bool: """ Returns true if the graph contains the specified edge @@ -1978,13 +1978,13 @@ class GraphView(object): dst (str or int): the destination node id Returns: - true if the graph contains the specified edge, false otherwise + bool: true if the graph contains the specified edge, false otherwise """ def has_layer(self, name): """Check if GraphView has the layer `"name"`""" - def has_node(self, id: str or int): + def has_node(self, id: str or int) -> bool: """ Returns true if the graph contains the specified node @@ -1992,7 +1992,7 @@ class GraphView(object): id (str or int): the node id Returns: - true if the graph contains the specified node, false otherwise + bool: true if the graph contains the specified node, false otherwise """ def index(self): @@ -2019,7 +2019,7 @@ class GraphView(object): DateTime of latest activity in the graph Returns: - the datetime of the latest activity in the graph + Optional[Datetime]: the datetime of the latest activity in the graph """ @property @@ -2028,7 +2028,7 @@ class GraphView(object): Timestamp of latest activity in the graph Returns: - the timestamp of the latest activity in the graph + Optional[int]: the timestamp of the latest activity in the graph """ def layer(self, name) -> GraphView: @@ -2052,15 +2052,15 @@ class GraphView(object): GraphView: The layered view """ - def materialize(self): + def materialize(self) -> GraphView: """ Returns a 'materialized' clone of the graph view - i.e. a new graph with a copy of the data seen within the view instead of just a mask over the original graph Returns: - GraphView - Returns a graph clone + GraphView: Returns a graph clone """ - def node(self, id: str or int): + def node(self, id: str or int) -> Optional[Node]: """ Gets the node with the specified id @@ -2068,7 +2068,7 @@ class GraphView(object): id (str or int): the node id Returns: - the node with the specified id, or None if the node does not exist + Optional[Node]: the node with the specified id, or None if the node does not exist """ @property @@ -2077,7 +2077,7 @@ class GraphView(object): Gets the nodes in the graph Returns: - the nodes in the graph + Nodes: the nodes in the graph """ @property @@ -2087,7 +2087,7 @@ class GraphView(object): Returns: - HashMap - Properties paired with their names + Properties: Properties paired with their names """ def rolling(self, window: int | str, step: int | str | None = None) -> WindowSet: @@ -2177,26 +2177,26 @@ class GraphView(object): Optional[Datetime]: The earliest datetime that this GraphView is valid or None if the GraphView is valid for all times. """ - def subgraph(self, nodes): + def subgraph(self, nodes: list[InputNode]) -> GraphView: """ Returns a subgraph given a set of nodes Arguments: - * `nodes`: set of nodes + nodes (list[InputNode]): set of nodes Returns: - GraphView - Returns the subgraph + GraphView: Returns the subgraph """ - def subgraph_node_types(self, node_types): + def subgraph_node_types(self, node_types: list[str]) -> GraphView: """ Returns a subgraph filtered by node types given a set of node types Arguments: - * `node_types`: set of node types + node_types (list[str]): set of node types Returns: - GraphView - Returns the subgraph + GraphView: Returns the subgraph """ def to_networkx( @@ -2263,7 +2263,12 @@ class GraphView(object): @property def unique_layers(self): - """Return all the layer ids in the graph""" + """ + Return all the layer ids in the graph + + Returns: + list[str] + """ def valid_layers(self, names: list[str]) -> GraphView: """ diff --git a/python/tests/test_graphdb/test_node_state.py b/python/tests/test_graphdb/test_node_state.py new file mode 100644 index 000000000..a8015ff77 --- /dev/null +++ b/python/tests/test_graphdb/test_node_state.py @@ -0,0 +1,26 @@ +from raphtory import Graph + + +def test_degree_window(): + g = Graph() + g.add_edge(0, 1, 2) + g.add_edge(1, 1, 3) + g.add_edge(2, 1, 4) + + degs = g.nodes.out_degree() + assert degs == [3, 0, 0, 0] + assert degs.before(1) == [1, 0, 0, 0] + assert degs[1] == 3 + assert degs.before(1)[1] == 1 + + +def test_degree_layer(): + g = Graph() + g.add_edge(0, 1, 2, layer="1") + g.add_edge(0, 1, 3, layer="2") + g.add_edge(0, 1, 4, layer="2") + + degs = g.nodes.out_degree() + assert degs == [3, 0, 0, 0] + assert degs.layers(["1"]) == [1, 0, 0, 0] + assert degs.layers(["2"]) == [2, 0, 0, 0] diff --git a/raphtory/src/python/graph/views/graph_view.rs b/raphtory/src/python/graph/views/graph_view.rs index 45cf935ff..47096dd30 100644 --- a/raphtory/src/python/graph/views/graph_view.rs +++ b/raphtory/src/python/graph/views/graph_view.rs @@ -136,6 +136,9 @@ impl IntoPy #[pymethods] impl PyGraphView { /// Return all the layer ids in the graph + /// + /// Returns: + /// list[str] #[getter] pub fn unique_layers(&self) -> Vec { self.graph.unique_layers().collect() @@ -146,7 +149,7 @@ impl PyGraphView { /// Timestamp of earliest activity in the graph /// /// Returns: - /// the timestamp of the earliest activity in the graph + /// Optional[int]: the timestamp of the earliest activity in the graph #[getter] pub fn earliest_time(&self) -> Option { self.graph.earliest_time() @@ -155,7 +158,7 @@ impl PyGraphView { /// DateTime of earliest activity in the graph /// /// Returns: - /// the datetime of the earliest activity in the graph + /// Optional[Datetime]: the datetime of the earliest activity in the graph #[getter] pub fn earliest_date_time(&self) -> Option> { self.graph.earliest_date_time() @@ -164,7 +167,7 @@ impl PyGraphView { /// Timestamp of latest activity in the graph /// /// Returns: - /// the timestamp of the latest activity in the graph + /// Optional[int]: the timestamp of the latest activity in the graph #[getter] pub fn latest_time(&self) -> Option { self.graph.latest_time() @@ -173,7 +176,7 @@ impl PyGraphView { /// DateTime of latest activity in the graph /// /// Returns: - /// the datetime of the latest activity in the graph + /// Optional[Datetime]: the datetime of the latest activity in the graph #[getter] pub fn latest_date_time(&self) -> Option> { self.graph.latest_date_time() @@ -182,7 +185,7 @@ impl PyGraphView { /// Number of edges in the graph /// /// Returns: - /// the number of edges in the graph + /// int: the number of edges in the graph pub fn count_edges(&self) -> usize { self.graph.count_edges() } @@ -190,7 +193,7 @@ impl PyGraphView { /// Number of edges in the graph /// /// Returns: - /// the number of temporal edges in the graph + /// int: the number of temporal edges in the graph pub fn count_temporal_edges(&self) -> usize { self.graph.count_temporal_edges() } @@ -198,7 +201,7 @@ impl PyGraphView { /// Number of nodes in the graph /// /// Returns: - /// the number of nodes in the graph + /// int: the number of nodes in the graph pub fn count_nodes(&self) -> usize { self.graph.count_nodes() } @@ -209,7 +212,7 @@ impl PyGraphView { /// id (str or int): the node id /// /// Returns: - /// true if the graph contains the specified node, false otherwise + /// bool: true if the graph contains the specified node, false otherwise pub fn has_node(&self, id: PyNodeRef) -> bool { self.graph.has_node(id) } @@ -221,7 +224,7 @@ impl PyGraphView { /// dst (str or int): the destination node id /// /// Returns: - /// true if the graph contains the specified edge, false otherwise + /// bool: true if the graph contains the specified edge, false otherwise #[pyo3(signature = (src, dst))] pub fn has_edge(&self, src: PyNodeRef, dst: PyNodeRef) -> bool { self.graph.has_edge(src, dst) @@ -235,16 +238,16 @@ impl PyGraphView { /// id (str or int): the node id /// /// Returns: - /// the node with the specified id, or None if the node does not exist + /// Optional[Node]: the node with the specified id, or None if the node does not exist pub fn node(&self, id: PyNodeRef) -> Option> { self.graph.node(id) } /// Get the nodes that match the properties name and value /// Arguments: - /// property_dict (dict): the properties name and value + /// property_dict (dict[str, Prop]): the properties name and value /// Returns: - /// the nodes that match the properties name and value + /// list[Node]: the nodes that match the properties name and value #[pyo3(signature = (properties_dict))] pub fn find_nodes(&self, properties_dict: HashMap) -> Vec { let iter = self.nodes().into_iter().par_bridge(); @@ -268,7 +271,7 @@ impl PyGraphView { /// Gets the nodes in the graph /// /// Returns: - /// the nodes in the graph + /// Nodes: the nodes in the graph #[getter] pub fn nodes(&self) -> Nodes<'static, DynamicGraph> { self.graph.nodes() @@ -281,7 +284,7 @@ impl PyGraphView { /// dst (str or int): the destination node id /// /// Returns: - /// the edge with the specified source and destination nodes, or None if the edge does not exist + /// Optional[Edge]: the edge with the specified source and destination nodes, or None if the edge does not exist #[pyo3(signature = (src, dst))] pub fn edge( &self, @@ -293,9 +296,9 @@ impl PyGraphView { /// Get the edges that match the properties name and value /// Arguments: - /// property_dict (dict): the properties name and value + /// property_dict (dict[str, Prop]): the properties name and value /// Returns: - /// the edges that match the properties name and value + /// list[Edge]: the edges that match the properties name and value #[pyo3(signature = (properties_dict))] pub fn find_edges(&self, properties_dict: HashMap) -> Vec { let iter = self.edges().into_iter().par_bridge(); @@ -319,7 +322,7 @@ impl PyGraphView { /// Gets all edges in the graph /// /// Returns: - /// the edges in the graph + /// Edges: the edges in the graph #[getter] pub fn edges(&self) -> Edges<'static, DynamicGraph> { self.graph.edges() @@ -329,7 +332,7 @@ impl PyGraphView { /// /// /// Returns: - /// HashMap - Properties paired with their names + /// Properties: Properties paired with their names #[getter] fn properties(&self) -> Properties { self.graph.properties() @@ -338,10 +341,10 @@ impl PyGraphView { /// Returns a subgraph given a set of nodes /// /// Arguments: - /// * `nodes`: set of nodes + /// nodes (list[InputNode]): set of nodes /// /// Returns: - /// GraphView - Returns the subgraph + /// GraphView: Returns the subgraph fn subgraph(&self, nodes: Vec) -> NodeSubgraph { self.graph.subgraph(nodes) } @@ -349,10 +352,10 @@ impl PyGraphView { /// Returns a subgraph filtered by node types given a set of node types /// /// Arguments: - /// * `node_types`: set of node types + /// node_types (list[str]): set of node types /// /// Returns: - /// GraphView - Returns the subgraph + /// GraphView: Returns the subgraph fn subgraph_node_types(&self, node_types: Vec) -> TypeFilteredSubgraph { self.graph.subgraph_node_types(node_types) } @@ -360,10 +363,10 @@ impl PyGraphView { /// Returns a subgraph given a set of nodes that are excluded from the subgraph /// /// Arguments: - /// * `nodes`: set of nodes + /// nodes (list[InputNode]): set of nodes /// /// Returns: - /// GraphView - Returns the subgraph + /// GraphView: Returns the subgraph fn exclude_nodes(&self, nodes: Vec) -> NodeSubgraph { self.graph.exclude_nodes(nodes) } @@ -371,7 +374,7 @@ impl PyGraphView { /// Returns a 'materialized' clone of the graph view - i.e. a new graph with a copy of the data seen within the view instead of just a mask over the original graph /// /// Returns: - /// GraphView - Returns a graph clone + /// GraphView: Returns a graph clone fn materialize(&self) -> Result { self.graph.materialize() } From f76d7ab2b23147307f0dd625ed63f6d21960d637 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 18 Nov 2024 11:21:53 +0100 Subject: [PATCH 19/23] tidy stubs and add more warnings to stub generator for bad formatting and missing docs --- python/python/raphtory/__init__.pyi | 39 ++++ .../python/raphtory/algorithms/__init__.pyi | 6 +- python/python/raphtory/graphql/__init__.pyi | 22 ++- python/scripts/gen-stubs.py | 73 ++++--- .../tests/graphql/edit_graph/test_graphql.py | 133 ++++++++++--- python/tests/test_graphdb/test_graphdb.py | 181 ++++++++++-------- raphtory/src/python/packages/algorithms.rs | 4 +- 7 files changed, 313 insertions(+), 145 deletions(-) diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index a1a6e49a6..ee28a406d 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -1245,6 +1245,25 @@ class Graph(GraphView): path (str): The path to the cache file """ + def create_node( + self, + timestamp: TimeInput, + id: str | int, + properties: Optional[PropInput] = None, + node_type: Optional[str] = None, + ) -> MutableNode: + """ + Creates a new node with the given id and properties to the graph. It fails if the node already exists. + + Arguments: + timestamp (TimeInput): The timestamp of the node. + id (str|int): The id of the node. + properties (PropInput, optional): The properties of the node. + node_type (str, optional): The optional string which will be used as a node type + Returns: + MutableNode: The created node + """ + @staticmethod def deserialise(bytes: bytes): """ @@ -3536,6 +3555,26 @@ class PersistentGraph(GraphView): path (str): The path to the cache file """ + def create_node( + self, + timestamp: TimeInput, + id: str | int, + properties: dict = None, + node_type: str = None, + ): + """ + Creates a new node with the given id and properties to the graph. It fails if the node already exists. + + Arguments: + timestamp (TimeInput): The timestamp of the node. + id (str | int): The id of the node. + properties (dict): The properties of the node. + node_type (str) : The optional string which will be used as a node type + + Returns: + MutableNode + """ + def delete_edge( self, timestamp: int, src: str | int, dst: str | int, layer: str = None ): diff --git a/python/python/raphtory/algorithms/__init__.pyi b/python/python/raphtory/algorithms/__init__.pyi index 880a5c91e..e2f43e942 100644 --- a/python/python/raphtory/algorithms/__init__.pyi +++ b/python/python/raphtory/algorithms/__init__.pyi @@ -628,7 +628,7 @@ def strongly_connected_components(g: GraphView): def temporal_SEIR( graph: GraphView, - seeds: int | float | list[Node], + seeds: int | float | list[InputNode], infection_prob: float, initial_infection: int | str | datetime, recovery_rate: float | None = None, @@ -642,8 +642,8 @@ def temporal_SEIR( Arguments: graph (GraphView): the graph view - seeds (int | float | list[Node]): the seeding strategy to use for the initial infection (if `int`, choose fixed number - of nodes at random, if `float` infect each node with this probability, if `[Node]` + seeds (int | float | list[InputNode]): the seeding strategy to use for the initial infection (if `int`, choose fixed number + of nodes at random, if `float` infect each node with this probability, if `list` initially infect the specified nodes infection_prob (float): the probability for a contact between infected and susceptible nodes to lead to a transmission diff --git a/python/python/raphtory/graphql/__init__.pyi b/python/python/raphtory/graphql/__init__.pyi index 17ba72a5d..d0d8e6953 100644 --- a/python/python/raphtory/graphql/__init__.pyi +++ b/python/python/raphtory/graphql/__init__.pyi @@ -329,9 +329,6 @@ class RemoteEdgeAddition(object): """Create and return a new object. See help(type) for accurate signature.""" class RemoteGraph(object): - def __new__(cls, path, client) -> RemoteGraph: - """Create and return a new object. See help(type) for accurate signature.""" - def add_constant_properties(self, properties: dict): """ Adds constant properties to the remote graph. @@ -406,6 +403,25 @@ class RemoteGraph(object): properties (dict): The temporal properties of the graph. """ + def create_node( + self, + timestamp: int | str | datetime, + id: str | int, + properties: Optional[dict] = None, + node_type: Optional[str] = None, + ): + """ + Create a new node with the given id and properties to the remote graph and fail if the node already exists. + + Arguments: + timestamp (int|str|datetime): The timestamp of the node. + id (str|int): The id of the node. + properties (dict, optional): The properties of the node. + node_type (str, optional): The optional string which will be used as a node type + Returns: + RemoteNode + """ + def delete_edge( self, timestamp: int, diff --git a/python/scripts/gen-stubs.py b/python/scripts/gen-stubs.py index 92bc0492c..c61252527 100755 --- a/python/scripts/gen-stubs.py +++ b/python/scripts/gen-stubs.py @@ -5,6 +5,7 @@ import textwrap import types from importlib import import_module +from logging import ERROR from pathlib import Path from types import ( BuiltinFunctionType, @@ -22,6 +23,9 @@ from pandas import DataFrame logger = logging.getLogger(__name__) +fn_logger = logging.getLogger(__name__) +cls_logger = logging.getLogger(__name__) + TARGET_MODULES = ["raphtory", "builtins"] TAB = " " * 4 @@ -107,23 +111,38 @@ def same_default(doc_default: Optional[str], param_default: Any) -> bool: def clean_parameter( - param: inspect.Parameter, type_annotations: dict[str, dict[str, Any]] + param: inspect.Parameter, + type_annotations: dict[str, dict[str, Any]], ): annotations = {} if param.default is not inspect.Parameter.empty: annotations["default"] = format_type(param.default) - if param.name in type_annotations: - annotations["annotation"] = type_annotations[param.name]["annotation"] - if "default" in type_annotations[param.name]: - default_from_docs = type_annotations[param.name]["default"] - if param.default is not param.empty and param.default is not ...: - if not same_default(default_from_docs, param.default): - fn_logger.warning( - f"mismatched default value: docs={repr(default_from_docs)}, signature={param.default}" - ) + doc_annotation = type_annotations.pop(param.name, None) + if doc_annotation is not None: + annotations["annotation"] = doc_annotation["annotation"] + default_from_docs = doc_annotation.get("default", None) + if default_from_docs is not None: + if param.default is not param.empty: + if param.default is not ...: + if not same_default(default_from_docs, param.default): + fn_logger.warning( + f"mismatched default value: docs={repr(default_from_docs)}, signature={param.default}" + ) + else: + annotations["default"] = default_from_docs else: - annotations["default"] = default_from_docs + fn_logger.error( + f"parameter {param.name} has default value {repr(default_from_docs)} in documentation but no default in signature." + ) + else: + if param.default is not param.empty and param.default is not None: + fn_logger.warning( + f"default value for parameter {param.name} with value {repr(param.default)} in signature is not documented" + ) + else: + if param.name not in {"self", "cls"}: + fn_logger.warning(f"missing parameter {param.name} in docs.") return param.replace(**annotations) @@ -142,6 +161,10 @@ def clean_signature( decorator = None new_params = [clean_parameter(p, type_annotations) for p in sig.parameters.values()] + for param_name, annotations in type_annotations.items(): + fn_logger.warning( + f"parameter {param_name} appears in documentation but does not exist." + ) sig = sig.replace(parameters=new_params) if return_type is not None: sig = sig.replace(return_annotation=return_type) @@ -241,8 +264,6 @@ def gen_fn( signature_overwrite: Optional[inspect.Signature] = None, docs_overwrite: Optional[str] = None, ) -> str: - global fn_logger - fn_logger = logging.getLogger(repr(function)) init_tab = TAB if is_method else "" fn_tab = TAB * 2 if is_method else TAB type_annotations, return_type = extract_types(function, docs_overwrite) @@ -282,8 +303,16 @@ def gen_class(cls: type, name) -> str: contents = list(vars(cls).items()) contents.sort(key=lambda x: x[0]) entities: list[str] = [] - + global cls_logger + global fn_logger + cls_logger = logger.getChild(name) for obj_name, entity in contents: + fn_logger = cls_logger.getChild(obj_name) + if obj_name.startswith("__") and not ( + obj_name == "__init__" or obj_name == "__new__" + ): + # Cannot get doc strings for __ methods (except for __new__ which we special-case by passing in the class docstring) + fn_logger.setLevel(ERROR) entity = inspect.unwrap(entity) if obj_name == "__init__" or obj_name == "__new__": # Get __init__ signature from class info @@ -317,17 +346,15 @@ def gen_class(cls: type, name) -> str: return f"class {name}{bases}: \n{docstr}\n{str_entities}" -def gen_module(module: ModuleType, name: str, path: Path) -> None: - logging.info("starting") +def gen_module(module: ModuleType, name: str, path: Path, log_path) -> None: + global logger objs = list(vars(module).items()) objs.sort(key=lambda x: x[0]) stubs: List[str] = [] modules: List[(ModuleType, str)] = [] path = path / name - global logger - logger = logging.getLogger(str(path)) - + logger = logging.getLogger(log_path) for obj_name, obj in objs: if isinstance(obj, type) and from_raphtory(obj, name): stubs.append(gen_class(obj, obj_name)) @@ -341,13 +368,15 @@ def gen_module(module: ModuleType, name: str, path: Path) -> None: file = path / "__init__.pyi" file.write_text(stub_file) - for module in modules: - gen_module(*module, path) + for module, name in modules: + gen_module(module, name, path, f"{log_path}.{name}") return if __name__ == "__main__": + logger = logging.getLogger("gen_stubs") + logging.basicConfig(level=logging.INFO) raphtory = import_module("raphtory") path = Path(__file__).parent.parent / "python" - gen_module(raphtory, "raphtory", path) + gen_module(raphtory, "raphtory", path, "raphtory") diff --git a/python/tests/graphql/edit_graph/test_graphql.py b/python/tests/graphql/edit_graph/test_graphql.py index 28b36f959..a75d6259f 100644 --- a/python/tests/graphql/edit_graph/test_graphql.py +++ b/python/tests/graphql/edit_graph/test_graphql.py @@ -21,8 +21,8 @@ def test_encode_graph(): encoded = encode_graph(g) assert ( - encoded - == "EgxaCgoIX2RlZmF1bHQSDBIKCghfZGVmYXVsdBoFCgNiZW4aCQoFaGFtemEYARoLCgdoYWFyb29uGAIiAhABIgYIAhABGAEiBBACGAIqAhoAKgQSAhABKgQSAhADKgIKACoGEgQIARABKgYSBAgBEAIqBAoCCAEqBhIECAIQAioGEgQIAhADKgQKAggCKgQ6AhABKgIyACoIOgYIARACGAEqBDICCAEqCDoGCAIQAxgCKgQyAggC" + encoded + == "EgxaCgoIX2RlZmF1bHQSDBIKCghfZGVmYXVsdBoFCgNiZW4aCQoFaGFtemEYARoLCgdoYWFyb29uGAIiAhABIgYIAhABGAEiBBACGAIqAhoAKgQSAhABKgQSAhADKgIKACoGEgQIARABKgYSBAgBEAIqBAoCCAEqBhIECAIQAioGEgQIAhADKgQKAggCKgQ6AhABKgIyACoIOgYIARACGAEqBDICCAEqCDoGCAIQAxgCKgQyAggC" ) @@ -42,8 +42,8 @@ def test_wrong_url(): with pytest.raises(Exception) as excinfo: client = RaphtoryClient("http://broken_url.com") assert ( - str(excinfo.value) - == "Could not connect to the given server - no response --error sending request for url (http://broken_url.com/)" + str(excinfo.value) + == "Could not connect to the given server - no response --error sending request for url (http://broken_url.com/)" ) @@ -393,16 +393,14 @@ def test_create_node(): query_nodes = """{graph(path: "g") {nodes {list {name}}}}""" assert client.query(query_nodes) == { - "graph": { - "nodes": { - "list": [{"name": "ben"}, {"name": "shivam"}] - } - } + "graph": {"nodes": {"list": [{"name": "ben"}, {"name": "shivam"}]}} } create_node_query = """{updateGraph(path: "g") { createNode(time: 0, name: "oogway") { success } }}""" - assert client.query(create_node_query) == {"updateGraph": {"createNode": {"success": True}}} + assert client.query(create_node_query) == { + "updateGraph": {"createNode": {"success": True}} + } assert client.query(query_nodes) == { "graph": { "nodes": { @@ -428,11 +426,7 @@ def test_create_node_using_client(): query_nodes = """{graph(path: "g") {nodes {list {name}}}}""" assert client.query(query_nodes) == { - "graph": { - "nodes": { - "list": [{"name": "ben"}, {"name": "shivam"}] - } - } + "graph": {"nodes": {"list": [{"name": "ben"}, {"name": "shivam"}]}} } remote_graph = client.remote_graph(path="g") @@ -460,23 +454,56 @@ def test_create_node_using_client_with_properties(): client = RaphtoryClient("http://localhost:1737") client.send_graph(path="g", graph=g) - query_nodes = """{graph(path: "g") {nodes {list {name, properties { keys }}}}}""" + query_nodes = ( + """{graph(path: "g") {nodes {list {name, properties { keys }}}}}""" + ) assert client.query(query_nodes) == { "graph": { "nodes": { - "list": [{"name": "ben", 'properties': {'keys': []}}, {"name": "shivam", 'properties': {'keys': []}}] + "list": [ + {"name": "ben", "properties": {"keys": []}}, + {"name": "shivam", "properties": {"keys": []}}, + ] } } } remote_graph = client.remote_graph(path="g") - remote_graph.create_node(timestamp=0, id="oogway", properties={"prop1": 60, "prop2": 31.3, "prop3": "abc123", "prop4": True, "prop5": [1, 2, 3]}) - nodes = json.loads(json.dumps(client.query(query_nodes)))['graph']['nodes']['list'] - node_oogway = next(node for node in nodes if node['name'] == 'oogway') - assert sorted(node_oogway['properties']['keys']) == ['prop1', 'prop2', 'prop3', 'prop4', 'prop5'] + remote_graph.create_node( + timestamp=0, + id="oogway", + properties={ + "prop1": 60, + "prop2": 31.3, + "prop3": "abc123", + "prop4": True, + "prop5": [1, 2, 3], + }, + ) + nodes = json.loads(json.dumps(client.query(query_nodes)))["graph"]["nodes"][ + "list" + ] + node_oogway = next(node for node in nodes if node["name"] == "oogway") + assert sorted(node_oogway["properties"]["keys"]) == [ + "prop1", + "prop2", + "prop3", + "prop4", + "prop5", + ] with pytest.raises(Exception) as excinfo: - remote_graph.create_node(timestamp=0, id="oogway", properties={"prop1": 60, "prop2": 31.3, "prop3": "abc123", "prop4": True, "prop5": [1, 2, 3]}) + remote_graph.create_node( + timestamp=0, + id="oogway", + properties={ + "prop1": 60, + "prop2": 31.3, + "prop3": "abc123", + "prop4": True, + "prop5": [1, 2, 3], + }, + ) assert "Node already exists" in str(excinfo.value) @@ -494,20 +521,57 @@ def test_create_node_using_client_with_properties_node_type(): assert client.query(query_nodes) == { "graph": { "nodes": { - "list": [{"name": "ben", 'nodeType': None, 'properties': {'keys': []}}, {"name": "shivam", 'nodeType': None, 'properties': {'keys': []}}] + "list": [ + {"name": "ben", "nodeType": None, "properties": {"keys": []}}, + { + "name": "shivam", + "nodeType": None, + "properties": {"keys": []}, + }, + ] } } } remote_graph = client.remote_graph(path="g") - remote_graph.create_node(timestamp=0, id="oogway", properties={"prop1": 60, "prop2": 31.3, "prop3": "abc123", "prop4": True, "prop5": [1, 2, 3]}, node_type="master") - nodes = json.loads(json.dumps(client.query(query_nodes)))['graph']['nodes']['list'] - node_oogway = next(node for node in nodes if node['name'] == 'oogway') - assert node_oogway['nodeType'] == 'master' - assert sorted(node_oogway['properties']['keys']) == ['prop1', 'prop2', 'prop3', 'prop4', 'prop5'] + remote_graph.create_node( + timestamp=0, + id="oogway", + properties={ + "prop1": 60, + "prop2": 31.3, + "prop3": "abc123", + "prop4": True, + "prop5": [1, 2, 3], + }, + node_type="master", + ) + nodes = json.loads(json.dumps(client.query(query_nodes)))["graph"]["nodes"][ + "list" + ] + node_oogway = next(node for node in nodes if node["name"] == "oogway") + assert node_oogway["nodeType"] == "master" + assert sorted(node_oogway["properties"]["keys"]) == [ + "prop1", + "prop2", + "prop3", + "prop4", + "prop5", + ] with pytest.raises(Exception) as excinfo: - remote_graph.create_node(timestamp=0, id="oogway", properties={"prop1": 60, "prop2": 31.3, "prop3": "abc123", "prop4": True, "prop5": [1, 2, 3]}, node_type="master") + remote_graph.create_node( + timestamp=0, + id="oogway", + properties={ + "prop1": 60, + "prop2": 31.3, + "prop3": "abc123", + "prop4": True, + "prop5": [1, 2, 3], + }, + node_type="master", + ) assert "Node already exists" in str(excinfo.value) @@ -525,7 +589,10 @@ def test_create_node_using_client_with_node_type(): assert client.query(query_nodes) == { "graph": { "nodes": { - "list": [{"name": "ben", 'nodeType': None}, {"name": "shivam", 'nodeType': None}] + "list": [ + {"name": "ben", "nodeType": None}, + {"name": "shivam", "nodeType": None}, + ] } } } @@ -535,7 +602,11 @@ def test_create_node_using_client_with_node_type(): assert client.query(query_nodes) == { "graph": { "nodes": { - "list": [{"name": "ben", 'nodeType': None}, {"name": "shivam", 'nodeType': None}, {"name": "oogway", 'nodeType': "master"}] + "list": [ + {"name": "ben", "nodeType": None}, + {"name": "shivam", "nodeType": None}, + {"name": "oogway", "nodeType": "master"}, + ] } } } diff --git a/python/tests/test_graphdb/test_graphdb.py b/python/tests/test_graphdb/test_graphdb.py index eb1c43b6b..bed24790c 100644 --- a/python/tests/test_graphdb/test_graphdb.py +++ b/python/tests/test_graphdb/test_graphdb.py @@ -353,8 +353,8 @@ def test_getitem(): @with_disk_graph def check(g): assert ( - g.node(1).properties.temporal.get("cost") - == g.node(1).properties.temporal["cost"] + g.node(1).properties.temporal.get("cost") + == g.node(1).properties.temporal["cost"] ) check(g) @@ -607,7 +607,7 @@ def time_history_test(time, key, value): assert g.at(time).node(1).properties.temporal.get(key) is None assert g.at(time).nodes.properties.temporal.get(key) is None assert ( - g.at(time).nodes.out_neighbours.properties.temporal.get(key) is None + g.at(time).nodes.out_neighbours.properties.temporal.get(key) is None ) else: assert g.at(time).node(1).properties.temporal.get(key).items() == value @@ -812,22 +812,22 @@ def no_static_property_test(key, value): assert sorted(g.node(1).properties.temporal.keys()) == expected_names_no_static assert sorted(g.nodes.properties.temporal.keys()) == expected_names_no_static assert ( - sorted(g.nodes.out_neighbours.properties.temporal.keys()) - == expected_names_no_static + sorted(g.nodes.out_neighbours.properties.temporal.keys()) + == expected_names_no_static ) expected_names_no_static_at_1 = sorted(["prop 4", "prop 1", "prop 3"]) assert ( - sorted(g.at(1).node(1).properties.temporal.keys()) - == expected_names_no_static_at_1 + sorted(g.at(1).node(1).properties.temporal.keys()) + == expected_names_no_static_at_1 ) assert ( - sorted(g.at(1).nodes.properties.temporal.keys()) - == expected_names_no_static_at_1 + sorted(g.at(1).nodes.properties.temporal.keys()) + == expected_names_no_static_at_1 ) assert ( - sorted(g.at(1).nodes.out_neighbours.properties.temporal.keys()) - == expected_names_no_static_at_1 + sorted(g.at(1).nodes.out_neighbours.properties.temporal.keys()) + == expected_names_no_static_at_1 ) # testing has_property @@ -1325,11 +1325,11 @@ def test_constant_property_update(): def updates(v): v.update_constant_properties({"prop1": "value1", "prop2": 123}) assert ( - v.properties.get("prop1") == "value1" and v.properties.get("prop2") == 123 + v.properties.get("prop1") == "value1" and v.properties.get("prop2") == 123 ) v.update_constant_properties({"prop1": "value2", "prop2": 345}) assert ( - v.properties.get("prop1") == "value2" and v.properties.get("prop2") == 345 + v.properties.get("prop1") == "value2" and v.properties.get("prop2") == 345 ) v.add_constant_properties({"name": "value1"}) @@ -1666,18 +1666,18 @@ def check(g): assert g.exclude_layer("layer2").count_edges() == 4 with pytest.raises( - Exception, - match=re.escape( - "Invalid layer: test_layer. Valid layers: _default, layer1, layer2" - ), + Exception, + match=re.escape( + "Invalid layer: test_layer. Valid layers: _default, layer1, layer2" + ), ): g.layers(["test_layer"]) with pytest.raises( - Exception, - match=re.escape( - "Invalid layer: test_layer. Valid layers: _default, layer1, layer2" - ), + Exception, + match=re.escape( + "Invalid layer: test_layer. Valid layers: _default, layer1, layer2" + ), ): g.edge(1, 2).layers(["test_layer"]) @@ -1754,20 +1754,20 @@ def test_layer_name(): assert str(e.value) == error_msg assert [ - list(iterator) for iterator in g.nodes.neighbours.edges.explode().layer_name - ] == [ - ["_default", "awesome layer"], - ["_default", "awesome layer"], - ["_default", "awesome layer"], - ] + list(iterator) for iterator in g.nodes.neighbours.edges.explode().layer_name + ] == [ + ["_default", "awesome layer"], + ["_default", "awesome layer"], + ["_default", "awesome layer"], + ] assert [ - list(iterator) - for iterator in g.nodes.neighbours.edges.explode_layers().layer_name - ] == [ - ["_default", "awesome layer"], - ["_default", "awesome layer"], - ["_default", "awesome layer"], - ] + list(iterator) + for iterator in g.nodes.neighbours.edges.explode_layers().layer_name + ] == [ + ["_default", "awesome layer"], + ["_default", "awesome layer"], + ["_default", "awesome layer"], + ] def test_time(): @@ -1801,12 +1801,12 @@ def check(g): # assert str(e.value) == error_msg assert [ - list(iterator) for iterator in g.nodes.neighbours.edges.explode().time - ] == [ - [0, 0, 1], - [0, 0, 1], - [0, 0, 1], - ] + list(iterator) for iterator in g.nodes.neighbours.edges.explode().time + ] == [ + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + ] check(g) @@ -2371,8 +2371,8 @@ def test_weird_windows(): @with_disk_graph def check(g): with pytest.raises( - Exception, - match="'ddd' is not a valid datetime, valid formats are RFC3339, RFC2822, %Y-%m-%d, %Y-%m-%dT%H:%M:%S%.3f, %Y-%m-%dT%H:%M:%S%, %Y-%m-%d %H:%M:%S%.3f and %Y-%m-%d %H:%M:%S%", + Exception, + match="'ddd' is not a valid datetime, valid formats are RFC3339, RFC2822, %Y-%m-%d, %Y-%m-%dT%H:%M:%S%.3f, %Y-%m-%dT%H:%M:%S%, %Y-%m-%d %H:%M:%S%.3f and %Y-%m-%d %H:%M:%S%", ): g.window("ddd", "aaa") @@ -2569,9 +2569,9 @@ def check(g): assert g.nodes.type_filter(["a"]).neighbours.type_filter( ["c"] ).name.collect() == [ - [], - ["5"], - ] + [], + ["5"], + ] assert g.nodes.type_filter(["a"]).neighbours.type_filter([]).name.collect() == [ [], [], @@ -2582,9 +2582,9 @@ def check(g): assert g.nodes.type_filter(["a"]).neighbours.type_filter( ["d"] ).name.collect() == [ - [], - [], - ] + [], + [], + ] assert g.nodes.type_filter(["a"]).neighbours.neighbours.name.collect() == [ ["1", "3", "4"], ["1", "3", "4", "4", "6"], @@ -2639,20 +2639,20 @@ def check(g): for edge in edges: time_nested.append(edge.time) assert [ - item - for sublist in g.nodes.edges.explode().time.collect() - for item in sublist - ] == time_nested + item + for sublist in g.nodes.edges.explode().time.collect() + for item in sublist + ] == time_nested date_time_nested = [] for edges in g.nodes.edges.explode(): for edge in edges: date_time_nested.append(edge.date_time) assert [ - item - for sublist in g.nodes.edges.explode().date_time.collect() - for item in sublist - ] == date_time_nested + item + for sublist in g.nodes.edges.explode().date_time.collect() + for item in sublist + ] == date_time_nested check(g) @@ -2966,44 +2966,44 @@ def check(g): assert len(index.fuzzy_search_nodes("name:habza", levenshtein_distance=1)) == 1 assert ( - len( - index.fuzzy_search_nodes( - "name:haa", levenshtein_distance=1, prefix=True - ) + len( + index.fuzzy_search_nodes( + "name:haa", levenshtein_distance=1, prefix=True ) - == 2 + ) + == 2 ) assert ( - len( - index.fuzzy_search_nodes( - "value_str:abc123", levenshtein_distance=2, prefix=True - ) + len( + index.fuzzy_search_nodes( + "value_str:abc123", levenshtein_distance=2, prefix=True ) - == 2 + ) + == 2 ) assert ( - len( - index.fuzzy_search_nodes( - "value_str:dsss312", levenshtein_distance=2, prefix=False - ) + len( + index.fuzzy_search_nodes( + "value_str:dsss312", levenshtein_distance=2, prefix=False ) - == 1 + ) + == 1 ) assert len(index.fuzzy_search_edges("from:bon", levenshtein_distance=1)) == 2 assert ( - len( - index.fuzzy_search_edges("from:bo", levenshtein_distance=1, prefix=True) - ) - == 2 + len( + index.fuzzy_search_edges("from:bo", levenshtein_distance=1, prefix=True) + ) + == 2 ) assert ( - len( - index.fuzzy_search_edges( - "from:eon", levenshtein_distance=2, prefix=True - ) + len( + index.fuzzy_search_edges( + "from:eon", levenshtein_distance=2, prefix=True ) - == 2 + ) + == 2 ) check(g) @@ -3011,26 +3011,38 @@ def check(g): def test_create_node_graph(): g = Graph() - g.create_node(1, "shivam", properties={"value": 60, "value_f": 31.3, "value_str": "abc123"}) + g.create_node( + 1, "shivam", properties={"value": 60, "value_f": 31.3, "value_str": "abc123"} + ) node = g.node("shivam") assert node.name == "shivam" assert node.properties == {"value": 60, "value_f": 31.3, "value_str": "abc123"} with pytest.raises(Exception) as excinfo: - g.create_node(1, "shivam", properties={"value": 60, "value_f": 31.3, "value_str": "abc123"}) + g.create_node( + 1, + "shivam", + properties={"value": 60, "value_f": 31.3, "value_str": "abc123"}, + ) assert "Node already exists" in str(excinfo.value) def test_create_node_graph_with_deletion(): g = PersistentGraph() - g.create_node(1, "shivam", properties={"value": 60, "value_f": 31.3, "value_str": "abc123"}) + g.create_node( + 1, "shivam", properties={"value": 60, "value_f": 31.3, "value_str": "abc123"} + ) node = g.node("shivam") assert node.name == "shivam" assert node.properties == {"value": 60, "value_f": 31.3, "value_str": "abc123"} with pytest.raises(Exception) as excinfo: - g.create_node(1, "shivam", properties={"value": 60, "value_f": 31.3, "value_str": "abc123"}) + g.create_node( + 1, + "shivam", + properties={"value": 60, "value_f": 31.3, "value_str": "abc123"}, + ) assert "Node already exists" in str(excinfo.value) @@ -3047,6 +3059,7 @@ def datadir(tmpdir, request): raise e return tmpdir + # def currently_broken_fuzzy_search(): #TODO: Fix fuzzy searching for properties # g = Graph() # g.add_edge(2,"haaroon","hamza", properties={"value":60,"value_f":31.3,"value_str":"abc123"}) diff --git a/raphtory/src/python/packages/algorithms.rs b/raphtory/src/python/packages/algorithms.rs index 7aef68bd3..5c6ff5b41 100644 --- a/raphtory/src/python/packages/algorithms.rs +++ b/raphtory/src/python/packages/algorithms.rs @@ -704,8 +704,8 @@ pub fn label_propagation( /// /// Arguments: /// graph (GraphView): the graph view -/// seeds (int | float | list[Node]): the seeding strategy to use for the initial infection (if `int`, choose fixed number -/// of nodes at random, if `float` infect each node with this probability, if `[Node]` +/// seeds (int | float | list[InputNode]): the seeding strategy to use for the initial infection (if `int`, choose fixed number +/// of nodes at random, if `float` infect each node with this probability, if `list` /// initially infect the specified nodes /// infection_prob (float): the probability for a contact between infected and susceptible nodes to lead /// to a transmission From e1bbc2ff2c260c4b77fd684ec718e1953cfb11cf Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 18 Nov 2024 11:48:28 +0100 Subject: [PATCH 20/23] fix logging for module-level functions --- python/scripts/gen-stubs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/scripts/gen-stubs.py b/python/scripts/gen-stubs.py index c61252527..f8f51825d 100755 --- a/python/scripts/gen-stubs.py +++ b/python/scripts/gen-stubs.py @@ -348,9 +348,9 @@ def gen_class(cls: type, name) -> str: def gen_module(module: ModuleType, name: str, path: Path, log_path) -> None: global logger + global fn_logger objs = list(vars(module).items()) objs.sort(key=lambda x: x[0]) - stubs: List[str] = [] modules: List[(ModuleType, str)] = [] path = path / name @@ -359,6 +359,7 @@ def gen_module(module: ModuleType, name: str, path: Path, log_path) -> None: if isinstance(obj, type) and from_raphtory(obj, name): stubs.append(gen_class(obj, obj_name)) elif isinstance(obj, BuiltinFunctionType): + fn_logger = logger.getChild(obj_name) stubs.append(gen_fn(obj, obj_name)) elif isinstance(obj, ModuleType) and obj.__loader__ is None: modules.append((obj, obj_name)) From d1cd67be3c3cf763758f957eb69f818d753e38c2 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Mon, 18 Nov 2024 11:57:49 +0100 Subject: [PATCH 21/23] fix some stubs warnings --- python/python/raphtory/__init__.pyi | 97 ++++++++--- .../python/raphtory/node_state/__init__.pyi | 157 +++++++++++++----- raphtory/src/python/graph/algorithm_result.rs | 10 +- raphtory/src/python/graph/node_state.rs | 2 +- .../types/macros/trait_impl/layerops.rs | 9 + 5 files changed, 208 insertions(+), 67 deletions(-) diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index ee28a406d..b10d2cf21 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -67,12 +67,12 @@ class AlgorithmResult(object): def min(self): """Returns a tuple of the min result with its key""" - def sort_by_node(self, reverse=True): + def sort_by_node(self, reverse: Any = True): """ Sorts by node id in ascending or descending order. Arguments: - `reverse`: If `true`, sorts the result in descending order; otherwise, sorts in ascending order. + reverse: If `true`, sorts the result in descending order; otherwise, sorts in ascending order. Defaults to True. Returns: A sorted list of tuples containing node names and values. @@ -84,7 +84,7 @@ class AlgorithmResult(object): value by the node name in either ascending or descending order. Arguments: - reverse (bool): A boolean value indicating whether the sorting should be done in reverse order or not. + reverse (bool): A boolean value indicating whether the sorting should be done in reverse order or not. Defaults to True. If reverse is true, the sorting will be done in descending order, otherwise it will be done in ascending order. @@ -97,7 +97,7 @@ class AlgorithmResult(object): Sorts the `AlgorithmResult` by its values in ascending or descending order. Arguments: - reverse (bool): If `true`, sorts the result in descending order; otherwise, sorts in ascending order. + reverse (bool): If `true`, sorts the result in descending order, otherwise, sorts in ascending order. Defaults to True. Returns: A sorted vector of tuples containing keys of type `H` and values of type `Y`. @@ -120,8 +120,8 @@ class AlgorithmResult(object): Arguments: k (int): The number of elements to retrieve. - percentage (bool): If `true`, the `k` parameter is treated as a percentage of total elements. - reverse (bool): If `true`, retrieves the elements in descending order; otherwise, in ascending order. + percentage (bool): If `True`, the `k` parameter is treated as a percentage of total elements. Defaults to False. + reverse (bool): If `True`, retrieves the elements in descending order, otherwise, in ascending order. Defaults to True. Returns: An Option containing a vector of tuples with keys of type `H` and values of type `Y`. @@ -432,8 +432,16 @@ class Edge(object): """Explodes an edge and returns all instances it had been updated as seperate edges""" def explode_layers(self): ... - def has_layer(self, name): - """Check if Edge has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if Edge has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def history(self) -> List[int]: """ @@ -520,11 +528,14 @@ class Edge(object): int: The latest time of an edge """ - def layer(self, name) -> Edge: + def layer(self, name: str) -> Edge: """ Return a view of Edge containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: Edge: The layered view """ @@ -897,8 +908,16 @@ class Edges(object): """Explodes an edge and returns all instances it had been updated as seperate edges""" def explode_layers(self): ... - def has_layer(self, name): - """Check if Edges has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if Edges has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def history(self): """ @@ -959,11 +978,14 @@ class Edges(object): Latest time of the edges. """ - def layer(self, name) -> Edges: + def layer(self, name: str) -> Edges: """ Return a view of Edges containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: Edges: The layered view """ @@ -2000,8 +2022,16 @@ class GraphView(object): bool: true if the graph contains the specified edge, false otherwise """ - def has_layer(self, name): - """Check if GraphView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if GraphView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def has_node(self, id: str or int) -> bool: """ @@ -2050,11 +2080,14 @@ class GraphView(object): Optional[int]: the timestamp of the latest activity in the graph """ - def layer(self, name) -> GraphView: + def layer(self, name: str) -> GraphView: """ Return a view of GraphView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: GraphView: The layered view """ @@ -2667,8 +2700,16 @@ class Node(object): Node: The filtered view """ - def has_layer(self, name): - """Check if Node has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if Node has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def history(self) -> List[int]: """ @@ -2755,11 +2796,14 @@ class Node(object): int: The latest time that the node exists as an integer. """ - def layer(self, name) -> Node: + def layer(self, name: str) -> Node: """ Return a view of Node containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: Node: The layered view """ @@ -3182,8 +3226,16 @@ class Nodes(object): Nodes: The filtered view """ - def has_layer(self, name): - """Check if Nodes has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if Nodes has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def history(self): """ @@ -3256,11 +3308,14 @@ class Nodes(object): def latest_time(self): """Returns an iterator over the nodes latest time""" - def layer(self, name) -> Nodes: + def layer(self, name: str) -> Nodes: """ Return a view of Nodes containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: Nodes: The layered view """ diff --git a/python/python/raphtory/node_state/__init__.pyi b/python/python/raphtory/node_state/__init__.pyi index 6828d400e..c57fa183a 100644 --- a/python/python/raphtory/node_state/__init__.pyi +++ b/python/python/raphtory/node_state/__init__.pyi @@ -190,8 +190,16 @@ class DegreeView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if DegreeView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if DegreeView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -207,11 +215,14 @@ class DegreeView(object): DegreeView """ - def layer(self, name) -> DegreeView: + def layer(self, name: str) -> DegreeView: """ Return a view of DegreeView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: DegreeView: The layered view """ @@ -366,7 +377,7 @@ class DegreeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateUsize @@ -631,8 +642,16 @@ class EarliestDateTimeView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if EarliestDateTimeView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if EarliestDateTimeView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -648,11 +667,14 @@ class EarliestDateTimeView(object): EarliestDateTimeView """ - def layer(self, name) -> EarliestDateTimeView: + def layer(self, name: str) -> EarliestDateTimeView: """ Return a view of EarliestDateTimeView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: EarliestDateTimeView: The layered view """ @@ -799,7 +821,7 @@ class EarliestDateTimeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionDateTime @@ -1056,8 +1078,16 @@ class EarliestTimeView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if EarliestTimeView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if EarliestTimeView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -1073,11 +1103,14 @@ class EarliestTimeView(object): EarliestTimeView """ - def layer(self, name) -> EarliestTimeView: + def layer(self, name: str) -> EarliestTimeView: """ Return a view of EarliestTimeView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: EarliestTimeView: The layered view """ @@ -1224,7 +1257,7 @@ class EarliestTimeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionI64 @@ -1481,8 +1514,16 @@ class HistoryDateTimeView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if HistoryDateTimeView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if HistoryDateTimeView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -1498,11 +1539,14 @@ class HistoryDateTimeView(object): HistoryDateTimeView """ - def layer(self, name) -> HistoryDateTimeView: + def layer(self, name: str) -> HistoryDateTimeView: """ Return a view of HistoryDateTimeView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: HistoryDateTimeView: The layered view """ @@ -1649,7 +1693,7 @@ class HistoryDateTimeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionListDateTime @@ -1906,8 +1950,16 @@ class HistoryView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if HistoryView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if HistoryView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -1923,11 +1975,14 @@ class HistoryView(object): HistoryView """ - def layer(self, name) -> HistoryView: + def layer(self, name: str) -> HistoryView: """ Return a view of HistoryView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: HistoryView: The layered view """ @@ -2074,7 +2129,7 @@ class HistoryView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateListI64 @@ -2283,7 +2338,7 @@ class IdView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateGID @@ -2489,8 +2544,16 @@ class LatestDateTimeView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if LatestDateTimeView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if LatestDateTimeView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -2506,11 +2569,14 @@ class LatestDateTimeView(object): LatestDateTimeView """ - def layer(self, name) -> LatestDateTimeView: + def layer(self, name: str) -> LatestDateTimeView: """ Return a view of LatestDateTimeView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: LatestDateTimeView: The layered view """ @@ -2657,7 +2723,7 @@ class LatestDateTimeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionDateTime @@ -2914,8 +2980,16 @@ class LatestTimeView(object): WindowSet: A `WindowSet` object. """ - def has_layer(self, name): - """Check if LatestTimeView has the layer `"name"`""" + def has_layer(self, name: str): + """ + Check if LatestTimeView has the layer `"name"` + + Arguments: + name (str): the name of the layer to check + + Returns: + bool + """ def items(self): """ @@ -2931,11 +3005,14 @@ class LatestTimeView(object): LatestTimeView """ - def layer(self, name) -> LatestTimeView: + def layer(self, name: str) -> LatestTimeView: """ Return a view of LatestTimeView containing the layer `"name"` Errors if the layer does not exist + Arguments: + name (str): then name of the layer. + Returns: LatestTimeView: The layered view """ @@ -3082,7 +3159,7 @@ class LatestTimeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionI64 @@ -3291,7 +3368,7 @@ class NameView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateString @@ -3431,7 +3508,7 @@ class NodeStateGID(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateGID @@ -3571,7 +3648,7 @@ class NodeStateListDateTime(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateListDateTime @@ -3711,7 +3788,7 @@ class NodeStateListI64(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateListI64 @@ -3851,7 +3928,7 @@ class NodeStateOptionDateTime(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionDateTime @@ -3991,7 +4068,7 @@ class NodeStateOptionListDateTime(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionListDateTime @@ -4131,7 +4208,7 @@ class NodeStateOptionStr(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionStr @@ -4271,7 +4348,7 @@ class NodeStateString(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateString @@ -4419,7 +4496,7 @@ class NodeStateU64(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateU64 @@ -4575,7 +4652,7 @@ class NodeStateUsize(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateUsize @@ -4741,7 +4818,7 @@ class NodeTypeView(object): Sort by value Arguments: - reverse (bool): If `True`, sort in descending order, otherwise ascending + reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. Returns: NodeStateOptionStr diff --git a/raphtory/src/python/graph/algorithm_result.rs b/raphtory/src/python/graph/algorithm_result.rs index 3cf3b6f74..fe08e3acd 100644 --- a/raphtory/src/python/graph/algorithm_result.rs +++ b/raphtory/src/python/graph/algorithm_result.rs @@ -93,7 +93,7 @@ macro_rules! py_algorithm_result_base { /// Sorts by node id in ascending or descending order. /// /// Arguments: - /// `reverse`: If `true`, sorts the result in descending order; otherwise, sorts in ascending order. + /// reverse: If `true`, sorts the result in descending order; otherwise, sorts in ascending order. Defaults to True. /// /// Returns: /// A sorted list of tuples containing node names and values. @@ -156,7 +156,7 @@ macro_rules! py_algorithm_result_partial_ord { /// Sorts the `AlgorithmResult` by its values in ascending or descending order. /// /// Arguments: - /// reverse (bool): If `true`, sorts the result in descending order; otherwise, sorts in ascending order. + /// reverse (bool): If `true`, sorts the result in descending order, otherwise, sorts in ascending order. Defaults to True. /// /// Returns: /// A sorted vector of tuples containing keys of type `H` and values of type `Y`. @@ -175,7 +175,7 @@ macro_rules! py_algorithm_result_partial_ord { /// value by the node name in either ascending or descending order. /// /// Arguments: - /// reverse (bool): A boolean value indicating whether the sorting should be done in reverse order or not. + /// reverse (bool): A boolean value indicating whether the sorting should be done in reverse order or not. Defaults to True. /// If reverse is true, the sorting will be done in descending order, otherwise it will be done in /// ascending order. /// @@ -196,8 +196,8 @@ macro_rules! py_algorithm_result_partial_ord { /// /// Arguments: /// k (int): The number of elements to retrieve. - /// percentage (bool): If `true`, the `k` parameter is treated as a percentage of total elements. - /// reverse (bool): If `true`, retrieves the elements in descending order; otherwise, in ascending order. + /// percentage (bool): If `True`, the `k` parameter is treated as a percentage of total elements. Defaults to False. + /// reverse (bool): If `True`, retrieves the elements in descending order, otherwise, in ascending order. Defaults to True. /// /// Returns: /// An Option containing a vector of tuples with keys of type `H` and values of type `Y`. diff --git a/raphtory/src/python/graph/node_state.rs b/raphtory/src/python/graph/node_state.rs index 5427f2ab8..f2162b30c 100644 --- a/raphtory/src/python/graph/node_state.rs +++ b/raphtory/src/python/graph/node_state.rs @@ -115,7 +115,7 @@ macro_rules! impl_node_state_ord_ops { /// Sort by value /// /// Arguments: - /// reverse (bool): If `True`, sort in descending order, otherwise ascending + /// reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False. /// /// Returns: #[doc = concat!(" ", $computed)] diff --git a/raphtory/src/python/types/macros/trait_impl/layerops.rs b/raphtory/src/python/types/macros/trait_impl/layerops.rs index 74acea9e6..83f677a93 100644 --- a/raphtory/src/python/types/macros/trait_impl/layerops.rs +++ b/raphtory/src/python/types/macros/trait_impl/layerops.rs @@ -20,6 +20,9 @@ macro_rules! impl_layerops { #[doc = concat!(" Return a view of ", $name, r#" containing the layer `"name"`"#)] /// Errors if the layer does not exist /// + /// Arguments: + /// name (str): then name of the layer. + /// /// Returns: #[doc = concat!(" ", $name, ": The layered view")] fn layer( @@ -58,6 +61,12 @@ macro_rules! impl_layerops { } #[doc = concat!(" Check if ", $name, r#" has the layer `"name"`"#)] + /// + /// Arguments: + /// name (str): the name of the layer to check + /// + /// Returns: + /// bool fn has_layer( &self, name: &str, From 6a35a71c1667b235ac973c5afa712e777e5d8362 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Tue, 19 Nov 2024 14:56:37 +0100 Subject: [PATCH 22/23] improved validation of annotations --- python/python/raphtory/__init__.pyi | 6 ++- .../python/raphtory/algorithms/__init__.pyi | 2 + python/python/raphtory/graph_gen/__init__.pyi | 2 + .../python/raphtory/graph_loader/__init__.pyi | 2 + python/python/raphtory/graphql/__init__.pyi | 2 + .../python/raphtory/node_state/__init__.pyi | 2 + python/python/raphtory/vectors/__init__.pyi | 2 + python/scripts/gen-stubs.py | 51 ++++++++++++++----- 8 files changed, 53 insertions(+), 16 deletions(-) diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index b10d2cf21..903ada5c5 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime @@ -2451,7 +2453,7 @@ class MutableNode(Node): properties (PropInput): A dictionary of properties to be added to the node. Each key is a string representing the property name, and each value is of type Prop representing the property value. """ - def add_updates(self, t: TimeInput, properties: PropInput = None) -> Result: + def add_updates(self, t: TimeInput, properties: PropInput = None): """ Add updates to a node in the graph at a specified time. This function allows for the addition of property updates to a node within the graph. The updates are time-stamped, meaning they are applied at the specified time. @@ -2719,7 +2721,7 @@ class Node(object): List[int]: A list of unix timestamps of the event history of node. """ - def history_date_time(self) -> List[Datetime]: + def history_date_time(self): """ Returns the history of a node, including node additions and changes made to node. diff --git a/python/python/raphtory/algorithms/__init__.pyi b/python/python/raphtory/algorithms/__init__.pyi index e2f43e942..dd41e3282 100644 --- a/python/python/raphtory/algorithms/__init__.pyi +++ b/python/python/raphtory/algorithms/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime diff --git a/python/python/raphtory/graph_gen/__init__.pyi b/python/python/raphtory/graph_gen/__init__.pyi index 43456b021..b532d6fbd 100644 --- a/python/python/raphtory/graph_gen/__init__.pyi +++ b/python/python/raphtory/graph_gen/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime diff --git a/python/python/raphtory/graph_loader/__init__.pyi b/python/python/raphtory/graph_loader/__init__.pyi index 99a7ba12d..f16cb055d 100644 --- a/python/python/raphtory/graph_loader/__init__.pyi +++ b/python/python/raphtory/graph_loader/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime diff --git a/python/python/raphtory/graphql/__init__.pyi b/python/python/raphtory/graphql/__init__.pyi index d0d8e6953..4852e73e9 100644 --- a/python/python/raphtory/graphql/__init__.pyi +++ b/python/python/raphtory/graphql/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime diff --git a/python/python/raphtory/node_state/__init__.pyi b/python/python/raphtory/node_state/__init__.pyi index c57fa183a..27e6f9bc8 100644 --- a/python/python/raphtory/node_state/__init__.pyi +++ b/python/python/raphtory/node_state/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime diff --git a/python/python/raphtory/vectors/__init__.pyi b/python/python/raphtory/vectors/__init__.pyi index cbae5f12e..445e6caf3 100644 --- a/python/python/raphtory/vectors/__init__.pyi +++ b/python/python/raphtory/vectors/__init__.pyi @@ -9,7 +9,9 @@ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime diff --git a/python/scripts/gen-stubs.py b/python/scripts/gen-stubs.py index f8f51825d..b8b364767 100755 --- a/python/scripts/gen-stubs.py +++ b/python/scripts/gen-stubs.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 - +import ast import inspect import logging import textwrap @@ -14,13 +14,9 @@ MethodDescriptorType, ModuleType, ) +import builtins from typing import * -from raphtory import * -from raphtory.graphql import * -from raphtory.typing import * from docstring_parser import parse, DocstringStyle, DocstringParam, ParseError -from datetime import datetime -from pandas import DataFrame logger = logging.getLogger(__name__) fn_logger = logging.getLogger(__name__) @@ -48,13 +44,19 @@ imports = """ from typing import * from raphtory import * +from raphtory.algorithms import * from raphtory.vectors import * +from raphtory.node_state import * from raphtory.graphql import * from raphtory.typing import * from datetime import datetime from pandas import DataFrame """ +# imports for type checking +global_ns = {} +exec(imports, global_ns) + def format_type(obj) -> str: if isinstance(obj, type): @@ -69,6 +71,18 @@ def format_type(obj) -> str: return repr(obj) +class AnnotationError(Exception): + pass + + +def validate_annotation(annotation: str): + parsed = ast.parse(f"_: {annotation}") + for node in ast.walk(parsed.body[0].annotation): + if isinstance(node, ast.Name): + if node.id not in global_ns and node.id not in builtins.__dict__: + raise AnnotationError(f"Unknown type {node.id}") + + def format_param(param: inspect.Parameter) -> str: if param.kind == param.VAR_KEYWORD: name = f"**{param.name}" @@ -214,20 +228,24 @@ def extract_param_annotation(param: DocstringParam) -> dict: else: type_val = param.type_name try: - eval(type_val) + validate_annotation(type_val) + if param.is_optional: + type_val = f"Optional[{type_val}]" + res["annotation"] = type_val except Exception as e: - raise ParseError(f"Invalid type name {type_val}: {e}") + fn_logger.error( + f"Invalid annotation {repr(type_val)} for parameter {param.arg_name}: {e}" + ) - if param.is_optional: - type_val = f"Optional[{type_val}]" - res["annotation"] = type_val if param.default is not None or param.is_optional: if param.default is not None: try: - eval(param.default) + validate_annotation(param.default) + res["default"] = param.default except Exception as e: - raise ParseError(f"Invalid default value {param.default}: {e}") - res["default"] = param.default + fn_logger.error( + f"Invalid default value {repr(param.default)} for parameter {param.arg_name}: {e}" + ) return res @@ -247,6 +265,11 @@ def extract_types( } if parse_result.returns is not None: return_type = parse_result.returns.type_name + try: + validate_annotation(return_type) + except Exception as e: + fn_logger.error(f"Invalid return type {repr(return_type)}: {e}") + return_type = None else: return_type = None return type_annotations, return_type From 9b721c67502efdf3c0a733184cdae87c3985df88 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Tue, 19 Nov 2024 15:06:14 +0100 Subject: [PATCH 23/23] fix the docs --- python/python/raphtory/__init__.pyi | 7 ++----- raphtory/src/python/graph/node.rs | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi index 903ada5c5..0f53ef53e 100644 --- a/python/python/raphtory/__init__.pyi +++ b/python/python/raphtory/__init__.pyi @@ -2461,9 +2461,6 @@ class MutableNode(Node): Parameters: t (TimeInput): The timestamp at which the updates should be applied. properties (PropInput): A dictionary of properties to update. Each key is a string representing the property name, and each value is of type Prop representing the property value. If None, no properties are updated. - - Returns: - Result: A result object indicating success or failure. On failure, it contains a GraphError. """ def set_node_type(self, new_type: str): @@ -2721,12 +2718,12 @@ class Node(object): List[int]: A list of unix timestamps of the event history of node. """ - def history_date_time(self): + def history_date_time(self) -> List[datetime]: """ Returns the history of a node, including node additions and changes made to node. Returns: - List[Datetime]: A list of timestamps of the event history of node. + List[datetime]: A list of timestamps of the event history of node. """ diff --git a/raphtory/src/python/graph/node.rs b/raphtory/src/python/graph/node.rs index 2758bf1b2..9bd320199 100644 --- a/raphtory/src/python/graph/node.rs +++ b/raphtory/src/python/graph/node.rs @@ -239,7 +239,7 @@ impl PyNode { /// Returns the history of a node, including node additions and changes made to node. /// /// Returns: - /// List[Datetime]: A list of timestamps of the event history of node. + /// List[datetime]: A list of timestamps of the event history of node. /// pub fn history_date_time(&self) -> Option>> { self.node.history_date_time() @@ -373,9 +373,6 @@ impl PyMutableNode { /// Parameters: /// t (TimeInput): The timestamp at which the updates should be applied. /// properties (PropInput): A dictionary of properties to update. Each key is a string representing the property name, and each value is of type Prop representing the property value. If None, no properties are updated. - /// - /// Returns: - /// Result: A result object indicating success or failure. On failure, it contains a GraphError. #[pyo3(signature = (t, properties=None))] pub fn add_updates( &self,