use std::sync::Arc;
use arrow::datatypes::DataType as ArrowDataType;
use super::{Expr, Field, NodeId, PlanNode, ProjectionNode, QueryPlan, Schema, SchemaRef};
mod centrality;
mod community;
mod definition;
mod layout;
mod residency;
mod similarity;
mod structure;
mod traversal;
pub use centrality::{
DEFAULT_BETWEENNESS_EXACT_VERTEX_THRESHOLD, GraphBetweennessCentralityParams, GraphBetweennessSeeds,
GraphEdgeBetweennessCentralityParams, GraphEigenvectorCentralityParams, GraphHitsParams, GraphKatzCentralityParams,
GraphPageRankParams, GraphPageRankPersonalizationInput, GraphPersonalizedPageRankParams,
};
pub use community::{
GraphEcgParams, GraphLeidenParams, GraphLouvainParams, GraphSccParams, GraphSpectralClusteringParams,
GraphWccParams,
};
pub use definition::{
EmptyGraphEdgeInputAction, EmptyGraphInputPolicy, EmptyGraphSideInputPolicy, GRAPH_OPERATION_REGISTRY,
GraphAlgorithm, GraphConstructionOptions, GraphConstructionPolicy, GraphEdgeColumnBindings,
GraphOperationDefinition, GraphOutputSchemaKind, GraphSourceVertexRequirement, GraphVertexIdTypeSupport,
GraphWeightPolicy,
};
pub use layout::{
GraphForceAtlas2InitialPositionsInput, GraphForceAtlas2Params, GraphForceAtlas2VertexAttributesInput,
GraphLayoutRequirement,
};
pub use residency::{
GraphResidencyDescriptor, GraphResidencyOrientation, GraphResidencyPlanAnalysis, GraphSourceFingerprint,
GraphSourceFingerprintBypassReason, ResidentGraphKey, edge_plan_has_simple_directed_edge_topology,
graph_residency_descriptor_for_edge_plan, graph_residency_descriptor_for_local_parquet_source,
};
pub use similarity::{GraphSimilarityParams, GraphVertexPairInput};
pub use structure::{
EdgeDedupStrategy, EdgeNormalizeSpec, EdgeWeightCombiner, GraphCoreNumberParams, GraphDegreesAllParams,
GraphInDegreesAllParams, GraphKCoreDegreeType, GraphKCoreParams, GraphMstParams, GraphOutDegreesAllParams,
GraphTriangleCountAllParams,
};
pub use traversal::{
GraphBfsEndpointPolicy, GraphBfsParams, GraphBfsPathOutput, GraphBfsPredicateInputs, GraphEdgePredicateInput,
GraphPredicateInput, GraphSsspParams, GraphSsspSource,
};
#[derive(Debug, Clone, PartialEq)]
pub enum GraphAlgorithmParams {
PageRank(GraphPageRankParams),
PersonalizedPageRank(GraphPersonalizedPageRankParams),
Bfs(Box<GraphBfsParams>),
Louvain(GraphLouvainParams),
EigenvectorCentrality(GraphEigenvectorCentralityParams),
KatzCentrality(GraphKatzCentralityParams),
Hits(GraphHitsParams),
BetweennessCentrality(GraphBetweennessCentralityParams),
EdgeBetweennessCentrality(GraphEdgeBetweennessCentralityParams),
Sssp(GraphSsspParams),
DegreesAll(GraphDegreesAllParams),
InDegreesAll(GraphInDegreesAllParams),
OutDegreesAll(GraphOutDegreesAllParams),
WeaklyConnectedComponents(GraphWccParams),
StronglyConnectedComponents(GraphSccParams),
TriangleCountAll(GraphTriangleCountAllParams),
CoreNumber(GraphCoreNumberParams),
KCore(GraphKCoreParams),
MinimumSpanningTree(GraphMstParams),
Leiden(GraphLeidenParams),
Ecg(GraphEcgParams),
SpectralModularityMaximization(GraphSpectralClusteringParams),
ForceAtlas2(GraphForceAtlas2Params),
Similarity(GraphSimilarityParams),
}
impl GraphAlgorithmParams {
pub const fn supports_algorithm(&self, algorithm: GraphAlgorithm) -> bool {
match self {
Self::PageRank(_) => matches!(algorithm, GraphAlgorithm::PageRank),
Self::PersonalizedPageRank(_) => matches!(algorithm, GraphAlgorithm::PersonalizedPageRank),
Self::Bfs(_) => matches!(algorithm, GraphAlgorithm::Bfs),
Self::Louvain(_) => matches!(algorithm, GraphAlgorithm::Louvain),
Self::EigenvectorCentrality(_) => matches!(algorithm, GraphAlgorithm::EigenvectorCentrality),
Self::KatzCentrality(_) => matches!(algorithm, GraphAlgorithm::KatzCentrality),
Self::Hits(_) => matches!(algorithm, GraphAlgorithm::Hits),
Self::BetweennessCentrality(_) => matches!(algorithm, GraphAlgorithm::BetweennessCentrality),
Self::EdgeBetweennessCentrality(_) => matches!(algorithm, GraphAlgorithm::EdgeBetweennessCentrality),
Self::Sssp(_) => matches!(algorithm, GraphAlgorithm::Sssp),
Self::DegreesAll(_) => matches!(algorithm, GraphAlgorithm::DegreesAll),
Self::InDegreesAll(_) => matches!(algorithm, GraphAlgorithm::InDegreesAll),
Self::OutDegreesAll(_) => matches!(algorithm, GraphAlgorithm::OutDegreesAll),
Self::WeaklyConnectedComponents(_) => matches!(algorithm, GraphAlgorithm::WeaklyConnectedComponents),
Self::StronglyConnectedComponents(_) => matches!(algorithm, GraphAlgorithm::StronglyConnectedComponents),
Self::TriangleCountAll(_) => matches!(algorithm, GraphAlgorithm::TriangleCountAll),
Self::CoreNumber(_) => matches!(algorithm, GraphAlgorithm::CoreNumber),
Self::KCore(_) => matches!(algorithm, GraphAlgorithm::KCore),
Self::MinimumSpanningTree(_) => matches!(algorithm, GraphAlgorithm::MinimumSpanningTree),
Self::Leiden(_) => matches!(algorithm, GraphAlgorithm::Leiden),
Self::Ecg(_) => matches!(algorithm, GraphAlgorithm::Ecg),
Self::SpectralModularityMaximization(_) => {
matches!(algorithm, GraphAlgorithm::SpectralModularityMaximization)
},
Self::ForceAtlas2(_) => matches!(algorithm, GraphAlgorithm::ForceAtlas2),
Self::Similarity(_) => algorithm.is_similarity(),
}
}
pub fn side_input_ordinals(&self) -> Box<dyn Iterator<Item = usize> + '_> {
match self {
Self::Bfs(params) => Box::new(params.side_input_ordinals()),
Self::PersonalizedPageRank(params) => Box::new(params.side_input_ordinals()),
Self::Similarity(params) => Box::new(params.side_input_ordinals()),
Self::Sssp(params) => Box::new(params.side_input_ordinals()),
Self::BetweennessCentrality(params) => Box::new(params.side_input_ordinals()),
Self::EdgeBetweennessCentrality(params) => Box::new(params.side_input_ordinals()),
Self::ForceAtlas2(params) => Box::new(params.side_input_ordinals()),
_ => Box::new(std::iter::empty()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GraphAlgorithmSpec {
pub algorithm: GraphAlgorithm,
pub edge_columns: GraphEdgeColumnBindings,
pub graph_options: GraphConstructionOptions,
pub edge_normalize_inserted: bool,
pub params: GraphAlgorithmParams,
pub output_schema: SchemaRef,
}
impl GraphAlgorithmSpec {
pub fn new(
algorithm: GraphAlgorithm,
edge_columns: GraphEdgeColumnBindings,
graph_options: GraphConstructionOptions,
params: GraphAlgorithmParams,
output_schema: SchemaRef,
) -> Self {
assert!(params.supports_algorithm(algorithm), "graph algorithm and parameter variant must match");
Self { algorithm, edge_columns, graph_options, edge_normalize_inserted: false, params, output_schema }
}
pub fn with_edge_normalize_inserted(mut self, inserted: bool) -> Self {
self.edge_normalize_inserted = inserted;
self
}
pub fn with_stable_schema(
algorithm: GraphAlgorithm,
edge_columns: GraphEdgeColumnBindings,
graph_options: GraphConstructionOptions,
params: GraphAlgorithmParams,
) -> Self {
let output_schema = algorithm.definition().result_schema_for_params(¶ms);
Self::new(algorithm, edge_columns, graph_options, params, output_schema)
}
pub fn unique_output_key_columns(&self) -> &'static [&'static str] {
if matches!(&self.params, GraphAlgorithmParams::Bfs(params) if params.path_output.is_some()) {
return &["path_index"];
}
if self.algorithm.definition().output_vertex_is_unique() { &["vertex"] } else { &[] }
}
}
pub fn planned_resident_graph_key(
descriptor: &GraphResidencyDescriptor,
spec: &GraphAlgorithmSpec,
) -> Option<ResidentGraphKey> {
if spec.edge_columns.edge_id.is_some() {
return None;
}
match (&spec.algorithm, &spec.params) {
(GraphAlgorithm::Bfs, GraphAlgorithmParams::Bfs(params)) if params.predicates.include_edges.is_none() => {
Some(descriptor.resident_key(spec, GraphResidencyOrientation::Forward))
},
(GraphAlgorithm::PageRank, GraphAlgorithmParams::PageRank(_)) => {
Some(descriptor.resident_key(spec, GraphResidencyOrientation::Transposed))
},
(
GraphAlgorithm::Jaccard | GraphAlgorithm::Sorensen | GraphAlgorithm::Overlap | GraphAlgorithm::Cosine,
GraphAlgorithmParams::Similarity(_),
) => Some(descriptor.resident_key(spec, GraphResidencyOrientation::Forward)),
_ => None,
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GraphAlgorithmNode {
pub inputs: Vec<NodeId>,
pub spec: GraphAlgorithmSpec,
}
impl GraphAlgorithmNode {
pub fn edge_input(&self) -> NodeId {
self.inputs[0]
}
pub fn output_schema(&self) -> &SchemaRef {
&self.spec.output_schema
}
}
pub fn stable_graph_result_schema(algorithm: GraphAlgorithm) -> SchemaRef {
let fields = match algorithm {
GraphAlgorithm::PageRank
| GraphAlgorithm::PersonalizedPageRank
| GraphAlgorithm::EigenvectorCentrality
| GraphAlgorithm::KatzCentrality
| GraphAlgorithm::BetweennessCentrality => {
vec![Field::new("vertex", ArrowDataType::Int64, false), Field::new("value", ArrowDataType::Float64, false)]
},
GraphAlgorithm::EdgeBetweennessCentrality => {
vec![
Field::new("src", ArrowDataType::Int64, false),
Field::new("dst", ArrowDataType::Int64, false),
Field::new("value", ArrowDataType::Float64, false),
]
},
GraphAlgorithm::Bfs => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("distance", ArrowDataType::Int64, false),
Field::new("predecessor", ArrowDataType::Int64, true),
],
GraphAlgorithm::Louvain => {
vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("community", ArrowDataType::Int64, false),
]
},
GraphAlgorithm::Hits => {
vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("hub_score", ArrowDataType::Float64, false),
Field::new("authority_score", ArrowDataType::Float64, false),
]
},
GraphAlgorithm::Sssp => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("distance", ArrowDataType::Float64, false),
Field::new("predecessor", ArrowDataType::Int64, true),
],
GraphAlgorithm::DegreesAll => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("in_degree", ArrowDataType::Int64, false),
Field::new("out_degree", ArrowDataType::Int64, false),
],
GraphAlgorithm::InDegreesAll => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("in_degree", ArrowDataType::Int64, false),
],
GraphAlgorithm::OutDegreesAll => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("out_degree", ArrowDataType::Int64, false),
],
GraphAlgorithm::WeaklyConnectedComponents | GraphAlgorithm::StronglyConnectedComponents => {
vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("component", ArrowDataType::Int64, false),
]
},
GraphAlgorithm::TriangleCountAll => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("triangle_count", ArrowDataType::Int64, false),
],
GraphAlgorithm::CoreNumber => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("core_number", ArrowDataType::Int64, false),
],
GraphAlgorithm::KCore | GraphAlgorithm::MinimumSpanningTree => {
vec![Field::new("src", ArrowDataType::Int64, false), Field::new("dst", ArrowDataType::Int64, false)]
},
GraphAlgorithm::Leiden | GraphAlgorithm::Ecg | GraphAlgorithm::SpectralModularityMaximization => {
vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("community", ArrowDataType::Int64, false),
]
},
GraphAlgorithm::ForceAtlas2 => vec![
Field::new("vertex", ArrowDataType::Int64, false),
Field::new("x", ArrowDataType::Float32, false),
Field::new("y", ArrowDataType::Float32, false),
],
GraphAlgorithm::Jaccard | GraphAlgorithm::Sorensen | GraphAlgorithm::Overlap | GraphAlgorithm::Cosine => vec![
Field::new("first", ArrowDataType::Int64, false),
Field::new("second", ArrowDataType::Int64, false),
Field::new("similarity", ArrowDataType::Float64, false),
],
};
Arc::new(Schema::new(fields))
}
pub fn graph_result_schema_for_params(algorithm: GraphAlgorithm, params: &GraphAlgorithmParams) -> SchemaRef {
algorithm.definition().result_schema_for_params(params)
}
pub fn graph_result_schema_for_vertex_type(
algorithm: GraphAlgorithm,
params: &GraphAlgorithmParams,
vertex_type: &ArrowDataType,
) -> SchemaRef {
assert!(
matches!(vertex_type, ArrowDataType::Int32 | ArrowDataType::Int64),
"native graph vertex type must be Int32 or Int64"
);
let vertex_columns = algorithm.definition().vertex_output_columns();
let fields = graph_result_schema_for_params(algorithm, params)
.fields()
.iter()
.map(|field| {
if vertex_columns.contains(&field.name().as_str()) {
field.as_ref().clone().with_data_type(vertex_type.clone())
} else {
field.as_ref().clone()
}
})
.collect::<Vec<_>>();
Arc::new(Schema::new(fields))
}