1use std::sync::Arc;
8
9use arrow::datatypes::DataType as ArrowDataType;
10
11use super::{Expr, Field, NodeId, PlanNode, ProjectionNode, QueryPlan, Schema, SchemaRef};
12
13mod centrality;
14mod community;
15mod definition;
16mod layout;
17mod residency;
18mod similarity;
19mod structure;
20mod traversal;
21
22pub use centrality::{
23 DEFAULT_BETWEENNESS_EXACT_VERTEX_THRESHOLD, GraphBetweennessCentralityParams, GraphBetweennessSeeds,
24 GraphEdgeBetweennessCentralityParams, GraphEigenvectorCentralityParams, GraphHitsParams, GraphKatzCentralityParams,
25 GraphPageRankParams, GraphPageRankPersonalizationInput, GraphPersonalizedPageRankParams,
26};
27pub use community::{
28 GraphEcgParams, GraphLeidenParams, GraphLouvainParams, GraphSccParams, GraphSpectralClusteringParams,
29 GraphWccParams,
30};
31pub use definition::{
32 EmptyGraphEdgeInputAction, EmptyGraphInputPolicy, EmptyGraphSideInputPolicy, GRAPH_OPERATION_REGISTRY,
33 GraphAlgorithm, GraphConstructionOptions, GraphConstructionPolicy, GraphEdgeColumnBindings,
34 GraphOperationDefinition, GraphOutputSchemaKind, GraphSourceVertexRequirement, GraphVertexIdTypeSupport,
35 GraphWeightPolicy,
36};
37pub use layout::{
38 GraphForceAtlas2InitialPositionsInput, GraphForceAtlas2Params, GraphForceAtlas2VertexAttributesInput,
39 GraphLayoutRequirement,
40};
41pub use residency::{
42 GraphResidencyDescriptor, GraphResidencyOrientation, GraphResidencyPlanAnalysis, GraphSourceFingerprint,
43 GraphSourceFingerprintBypassReason, ResidentGraphKey, edge_plan_has_simple_directed_edge_topology,
44 graph_residency_descriptor_for_edge_plan, graph_residency_descriptor_for_local_parquet_source,
45};
46pub use similarity::{GraphSimilarityParams, GraphVertexPairInput};
47pub use structure::{
48 EdgeDedupStrategy, EdgeNormalizeSpec, EdgeWeightCombiner, GraphCoreNumberParams, GraphDegreesAllParams,
49 GraphInDegreesAllParams, GraphKCoreDegreeType, GraphKCoreParams, GraphMstParams, GraphOutDegreesAllParams,
50 GraphTriangleCountAllParams,
51};
52pub use traversal::{
53 GraphBfsEndpointPolicy, GraphBfsParams, GraphBfsPathOutput, GraphBfsPredicateInputs, GraphEdgePredicateInput,
54 GraphPredicateInput, GraphSsspParams, GraphSsspSource,
55};
56
57#[derive(Debug, Clone, PartialEq)]
59pub enum GraphAlgorithmParams {
60 PageRank(GraphPageRankParams),
62 PersonalizedPageRank(GraphPersonalizedPageRankParams),
64 Bfs(Box<GraphBfsParams>),
66 Louvain(GraphLouvainParams),
68 EigenvectorCentrality(GraphEigenvectorCentralityParams),
70 KatzCentrality(GraphKatzCentralityParams),
72 Hits(GraphHitsParams),
74 BetweennessCentrality(GraphBetweennessCentralityParams),
76 EdgeBetweennessCentrality(GraphEdgeBetweennessCentralityParams),
78 Sssp(GraphSsspParams),
80 DegreesAll(GraphDegreesAllParams),
82 InDegreesAll(GraphInDegreesAllParams),
84 OutDegreesAll(GraphOutDegreesAllParams),
86 WeaklyConnectedComponents(GraphWccParams),
88 StronglyConnectedComponents(GraphSccParams),
90 TriangleCountAll(GraphTriangleCountAllParams),
92 CoreNumber(GraphCoreNumberParams),
94 KCore(GraphKCoreParams),
96 MinimumSpanningTree(GraphMstParams),
98 Leiden(GraphLeidenParams),
100 Ecg(GraphEcgParams),
102 SpectralModularityMaximization(GraphSpectralClusteringParams),
104 ForceAtlas2(GraphForceAtlas2Params),
106 Similarity(GraphSimilarityParams),
108}
109
110impl GraphAlgorithmParams {
111 pub const fn supports_algorithm(&self, algorithm: GraphAlgorithm) -> bool {
117 match self {
118 Self::PageRank(_) => matches!(algorithm, GraphAlgorithm::PageRank),
119 Self::PersonalizedPageRank(_) => matches!(algorithm, GraphAlgorithm::PersonalizedPageRank),
120 Self::Bfs(_) => matches!(algorithm, GraphAlgorithm::Bfs),
121 Self::Louvain(_) => matches!(algorithm, GraphAlgorithm::Louvain),
122 Self::EigenvectorCentrality(_) => matches!(algorithm, GraphAlgorithm::EigenvectorCentrality),
123 Self::KatzCentrality(_) => matches!(algorithm, GraphAlgorithm::KatzCentrality),
124 Self::Hits(_) => matches!(algorithm, GraphAlgorithm::Hits),
125 Self::BetweennessCentrality(_) => matches!(algorithm, GraphAlgorithm::BetweennessCentrality),
126 Self::EdgeBetweennessCentrality(_) => matches!(algorithm, GraphAlgorithm::EdgeBetweennessCentrality),
127 Self::Sssp(_) => matches!(algorithm, GraphAlgorithm::Sssp),
128 Self::DegreesAll(_) => matches!(algorithm, GraphAlgorithm::DegreesAll),
129 Self::InDegreesAll(_) => matches!(algorithm, GraphAlgorithm::InDegreesAll),
130 Self::OutDegreesAll(_) => matches!(algorithm, GraphAlgorithm::OutDegreesAll),
131 Self::WeaklyConnectedComponents(_) => matches!(algorithm, GraphAlgorithm::WeaklyConnectedComponents),
132 Self::StronglyConnectedComponents(_) => matches!(algorithm, GraphAlgorithm::StronglyConnectedComponents),
133 Self::TriangleCountAll(_) => matches!(algorithm, GraphAlgorithm::TriangleCountAll),
134 Self::CoreNumber(_) => matches!(algorithm, GraphAlgorithm::CoreNumber),
135 Self::KCore(_) => matches!(algorithm, GraphAlgorithm::KCore),
136 Self::MinimumSpanningTree(_) => matches!(algorithm, GraphAlgorithm::MinimumSpanningTree),
137 Self::Leiden(_) => matches!(algorithm, GraphAlgorithm::Leiden),
138 Self::Ecg(_) => matches!(algorithm, GraphAlgorithm::Ecg),
139 Self::SpectralModularityMaximization(_) => {
140 matches!(algorithm, GraphAlgorithm::SpectralModularityMaximization)
141 },
142 Self::ForceAtlas2(_) => matches!(algorithm, GraphAlgorithm::ForceAtlas2),
143 Self::Similarity(_) => algorithm.is_similarity(),
144 }
145 }
146
147 pub fn side_input_ordinals(&self) -> Box<dyn Iterator<Item = usize> + '_> {
149 match self {
150 Self::Bfs(params) => Box::new(params.side_input_ordinals()),
151 Self::PersonalizedPageRank(params) => Box::new(params.side_input_ordinals()),
152 Self::Similarity(params) => Box::new(params.side_input_ordinals()),
153 Self::Sssp(params) => Box::new(params.side_input_ordinals()),
154 Self::BetweennessCentrality(params) => Box::new(params.side_input_ordinals()),
155 Self::EdgeBetweennessCentrality(params) => Box::new(params.side_input_ordinals()),
156 Self::ForceAtlas2(params) => Box::new(params.side_input_ordinals()),
157 _ => Box::new(std::iter::empty()),
158 }
159 }
160}
161
162#[derive(Debug, Clone, PartialEq)]
164pub struct GraphAlgorithmSpec {
165 pub algorithm: GraphAlgorithm,
167 pub edge_columns: GraphEdgeColumnBindings,
169 pub graph_options: GraphConstructionOptions,
171 pub edge_normalize_inserted: bool,
174 pub params: GraphAlgorithmParams,
176 pub output_schema: SchemaRef,
178}
179
180impl GraphAlgorithmSpec {
181 pub fn new(
187 algorithm: GraphAlgorithm,
188 edge_columns: GraphEdgeColumnBindings,
189 graph_options: GraphConstructionOptions,
190 params: GraphAlgorithmParams,
191 output_schema: SchemaRef,
192 ) -> Self {
193 assert!(params.supports_algorithm(algorithm), "graph algorithm and parameter variant must match");
194 Self { algorithm, edge_columns, graph_options, edge_normalize_inserted: false, params, output_schema }
195 }
196
197 pub fn with_edge_normalize_inserted(mut self, inserted: bool) -> Self {
199 self.edge_normalize_inserted = inserted;
200 self
201 }
202
203 pub fn with_stable_schema(
209 algorithm: GraphAlgorithm,
210 edge_columns: GraphEdgeColumnBindings,
211 graph_options: GraphConstructionOptions,
212 params: GraphAlgorithmParams,
213 ) -> Self {
214 let output_schema = algorithm.definition().result_schema_for_params(¶ms);
215 Self::new(algorithm, edge_columns, graph_options, params, output_schema)
216 }
217
218 pub fn unique_output_key_columns(&self) -> &'static [&'static str] {
220 if matches!(&self.params, GraphAlgorithmParams::Bfs(params) if params.path_output.is_some()) {
221 return &["path_index"];
222 }
223 if self.algorithm.definition().output_vertex_is_unique() { &["vertex"] } else { &[] }
224 }
225}
226
227pub fn planned_resident_graph_key(
235 descriptor: &GraphResidencyDescriptor,
236 spec: &GraphAlgorithmSpec,
237) -> Option<ResidentGraphKey> {
238 if spec.edge_columns.edge_id.is_some() {
239 return None;
240 }
241 match (&spec.algorithm, &spec.params) {
242 (GraphAlgorithm::Bfs, GraphAlgorithmParams::Bfs(params)) if params.predicates.include_edges.is_none() => {
243 Some(descriptor.resident_key(spec, GraphResidencyOrientation::Forward))
244 },
245 (GraphAlgorithm::PageRank, GraphAlgorithmParams::PageRank(_)) => {
246 Some(descriptor.resident_key(spec, GraphResidencyOrientation::Transposed))
247 },
248 (
249 GraphAlgorithm::Jaccard | GraphAlgorithm::Sorensen | GraphAlgorithm::Overlap | GraphAlgorithm::Cosine,
250 GraphAlgorithmParams::Similarity(_),
251 ) => Some(descriptor.resident_key(spec, GraphResidencyOrientation::Forward)),
252 _ => None,
253 }
254}
255
256#[derive(Debug, Clone, PartialEq)]
258pub struct GraphAlgorithmNode {
259 pub inputs: Vec<NodeId>,
262 pub spec: GraphAlgorithmSpec,
264}
265
266impl GraphAlgorithmNode {
267 pub fn edge_input(&self) -> NodeId {
269 self.inputs[0]
270 }
271
272 pub fn output_schema(&self) -> &SchemaRef {
274 &self.spec.output_schema
275 }
276}
277
278pub fn stable_graph_result_schema(algorithm: GraphAlgorithm) -> SchemaRef {
286 let fields = match algorithm {
287 GraphAlgorithm::PageRank
288 | GraphAlgorithm::PersonalizedPageRank
289 | GraphAlgorithm::EigenvectorCentrality
290 | GraphAlgorithm::KatzCentrality
291 | GraphAlgorithm::BetweennessCentrality => {
292 vec![Field::new("vertex", ArrowDataType::Int64, false), Field::new("value", ArrowDataType::Float64, false)]
294 },
295 GraphAlgorithm::EdgeBetweennessCentrality => {
296 vec![
297 Field::new("src", ArrowDataType::Int64, false),
298 Field::new("dst", ArrowDataType::Int64, false),
299 Field::new("value", ArrowDataType::Float64, false),
300 ]
301 },
302 GraphAlgorithm::Bfs => vec![
303 Field::new("vertex", ArrowDataType::Int64, false),
304 Field::new("distance", ArrowDataType::Int64, false),
305 Field::new("predecessor", ArrowDataType::Int64, true),
306 ],
307 GraphAlgorithm::Louvain => {
308 vec![
309 Field::new("vertex", ArrowDataType::Int64, false),
310 Field::new("community", ArrowDataType::Int64, false),
311 ]
312 },
313 GraphAlgorithm::Hits => {
314 vec![
320 Field::new("vertex", ArrowDataType::Int64, false),
321 Field::new("hub_score", ArrowDataType::Float64, false),
322 Field::new("authority_score", ArrowDataType::Float64, false),
323 ]
324 },
325 GraphAlgorithm::Sssp => vec![
326 Field::new("vertex", ArrowDataType::Int64, false),
327 Field::new("distance", ArrowDataType::Float64, false),
329 Field::new("predecessor", ArrowDataType::Int64, true),
332 ],
333 GraphAlgorithm::DegreesAll => vec![
334 Field::new("vertex", ArrowDataType::Int64, false),
335 Field::new("in_degree", ArrowDataType::Int64, false),
336 Field::new("out_degree", ArrowDataType::Int64, false),
337 ],
338 GraphAlgorithm::InDegreesAll => vec![
339 Field::new("vertex", ArrowDataType::Int64, false),
340 Field::new("in_degree", ArrowDataType::Int64, false),
341 ],
342 GraphAlgorithm::OutDegreesAll => vec![
343 Field::new("vertex", ArrowDataType::Int64, false),
344 Field::new("out_degree", ArrowDataType::Int64, false),
345 ],
346 GraphAlgorithm::WeaklyConnectedComponents | GraphAlgorithm::StronglyConnectedComponents => {
350 vec![
351 Field::new("vertex", ArrowDataType::Int64, false),
352 Field::new("component", ArrowDataType::Int64, false),
353 ]
354 },
355 GraphAlgorithm::TriangleCountAll => vec![
356 Field::new("vertex", ArrowDataType::Int64, false),
357 Field::new("triangle_count", ArrowDataType::Int64, false),
358 ],
359 GraphAlgorithm::CoreNumber => vec![
360 Field::new("vertex", ArrowDataType::Int64, false),
361 Field::new("core_number", ArrowDataType::Int64, false),
362 ],
363 GraphAlgorithm::KCore | GraphAlgorithm::MinimumSpanningTree => {
366 vec![Field::new("src", ArrowDataType::Int64, false), Field::new("dst", ArrowDataType::Int64, false)]
367 },
368 GraphAlgorithm::Leiden | GraphAlgorithm::Ecg | GraphAlgorithm::SpectralModularityMaximization => {
372 vec![
373 Field::new("vertex", ArrowDataType::Int64, false),
374 Field::new("community", ArrowDataType::Int64, false),
375 ]
376 },
377 GraphAlgorithm::ForceAtlas2 => vec![
378 Field::new("vertex", ArrowDataType::Int64, false),
379 Field::new("x", ArrowDataType::Float32, false),
380 Field::new("y", ArrowDataType::Float32, false),
381 ],
382 GraphAlgorithm::Jaccard | GraphAlgorithm::Sorensen | GraphAlgorithm::Overlap | GraphAlgorithm::Cosine => vec![
383 Field::new("first", ArrowDataType::Int64, false),
384 Field::new("second", ArrowDataType::Int64, false),
385 Field::new("similarity", ArrowDataType::Float64, false),
386 ],
387 };
388 Arc::new(Schema::new(fields))
389}
390
391pub fn graph_result_schema_for_params(algorithm: GraphAlgorithm, params: &GraphAlgorithmParams) -> SchemaRef {
396 algorithm.definition().result_schema_for_params(params)
397}
398
399pub fn graph_result_schema_for_vertex_type(
401 algorithm: GraphAlgorithm,
402 params: &GraphAlgorithmParams,
403 vertex_type: &ArrowDataType,
404) -> SchemaRef {
405 assert!(
406 matches!(vertex_type, ArrowDataType::Int32 | ArrowDataType::Int64),
407 "native graph vertex type must be Int32 or Int64"
408 );
409 let vertex_columns = algorithm.definition().vertex_output_columns();
410 let fields = graph_result_schema_for_params(algorithm, params)
411 .fields()
412 .iter()
413 .map(|field| {
414 if vertex_columns.contains(&field.name().as_str()) {
415 field.as_ref().clone().with_data_type(vertex_type.clone())
416 } else {
417 field.as_ref().clone()
418 }
419 })
420 .collect::<Vec<_>>();
421 Arc::new(Schema::new(fields))
422}