Skip to content

Commit 768f0a6

Browse files
committed
Add stats endpoint to BellmanFord
1 parent 517779d commit 768f0a6

File tree

8 files changed

+157
-16
lines changed

8 files changed

+157
-16
lines changed

graphdatascience/procedure_surface/api/pathfinding/all_shortest_path_endpoints.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
from abc import ABC, abstractmethod
44

5-
from graphdatascience.procedure_surface.api.pathfinding.single_source_bellman_ford_endpoints import (
6-
SingleSourceBellmanFordEndpoints,
7-
)
85
from graphdatascience.procedure_surface.api.pathfinding.single_source_delta_endpoints import SingleSourceDeltaEndpoints
96
from graphdatascience.procedure_surface.api.pathfinding.single_source_dijkstra_endpoints import (
107
SingleSourceDijkstraEndpoints,
@@ -28,9 +25,3 @@ def delta(self) -> SingleSourceDeltaEndpoints:
2825
def dijkstra(self) -> SingleSourceDijkstraEndpoints:
2926
"""Access to Dijkstra shortest path algorithm endpoints."""
3027
...
31-
32-
@property
33-
@abstractmethod
34-
def bellman_ford(self) -> SingleSourceBellmanFordEndpoints:
35-
"""Access to Bellman-Ford shortest path algorithm endpoints."""
36-
...

graphdatascience/procedure_surface/api/pathfinding/single_source_bellman_ford_endpoints.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
from graphdatascience.procedure_surface.api.estimation_result import EstimationResult
1111

1212

13+
class BellmanFordStatsResult(BaseResult):
14+
pre_processing_millis: int
15+
compute_millis: int
16+
post_processing_millis: int
17+
contains_negative_cycle: bool
18+
configuration: dict[str, Any]
19+
20+
1321
class BellmanFordWriteResult(BaseResult):
1422
pre_processing_millis: int
1523
compute_millis: int
@@ -80,6 +88,54 @@ def stream(
8088
totalCost, nodeIds, costs, index, and isNegativeCycle.
8189
"""
8290

91+
@abstractmethod
92+
def stats(
93+
self,
94+
G: GraphV2,
95+
source_node: int,
96+
relationship_weight_property: str | None = None,
97+
relationship_types: list[str] | None = None,
98+
node_labels: list[str] | None = None,
99+
sudo: bool = False,
100+
log_progress: bool = True,
101+
username: str | None = None,
102+
concurrency: int | None = None,
103+
job_id: str | None = None,
104+
) -> BellmanFordStatsResult:
105+
"""
106+
Runs the Bellman-Ford shortest path algorithm and returns statistics about the execution.
107+
108+
The Bellman-Ford algorithm can detect negative cycles in the graph.
109+
110+
Parameters
111+
----------
112+
G : GraphV2
113+
The graph to run the algorithm on.
114+
source_node : int
115+
The source node for the shortest path computation.
116+
relationship_weight_property : str | None, default=None
117+
The relationship property to use as weights.
118+
relationship_types : list[str] | None, default=None
119+
Filter on relationship types.
120+
node_labels : list[str] | None, default=None
121+
Filter on node labels.
122+
sudo : bool, default=False
123+
Run the algorithm with elevated privileges.
124+
log_progress : bool, default=True
125+
Whether to log progress.
126+
username : str | None, default=None
127+
Username for the operation.
128+
concurrency : int | None, default=None
129+
Concurrency configuration.
130+
job_id : str | None, default=None
131+
Job ID for the operation.
132+
133+
Returns
134+
-------
135+
BellmanFordStatsResult
136+
Object containing statistics from the execution, including whether negative cycles were detected.
137+
"""
138+
83139
@abstractmethod
84140
def mutate(
85141
self,

graphdatascience/procedure_surface/arrow/pathfinding/all_shortest_path_arrow_endpoints.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
from graphdatascience.arrow_client.authenticated_flight_client import AuthenticatedArrowClient
44
from graphdatascience.arrow_client.v2.remote_write_back_client import RemoteWriteBackClient
55
from graphdatascience.procedure_surface.api.pathfinding.all_shortest_path_endpoints import AllShortestPathEndpoints
6-
from graphdatascience.procedure_surface.api.pathfinding.single_source_bellman_ford_endpoints import (
7-
SingleSourceBellmanFordEndpoints,
8-
)
96
from graphdatascience.procedure_surface.api.pathfinding.single_source_delta_endpoints import SingleSourceDeltaEndpoints
107
from graphdatascience.procedure_surface.api.pathfinding.single_source_dijkstra_endpoints import (
118
SingleSourceDijkstraEndpoints,
@@ -39,7 +36,3 @@ def delta(self) -> SingleSourceDeltaEndpoints:
3936
@property
4037
def dijkstra(self) -> SingleSourceDijkstraEndpoints:
4138
return self._dijkstra
42-
43-
@property
44-
def bellman_ford(self) -> SingleSourceBellmanFordEndpoints:
45-
return self._bellman_ford

graphdatascience/procedure_surface/arrow/pathfinding/single_source_bellman_ford_arrow_endpoints.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from graphdatascience.procedure_surface.api.estimation_result import EstimationResult
1111
from graphdatascience.procedure_surface.api.pathfinding.single_source_bellman_ford_endpoints import (
1212
BellmanFordMutateResult,
13+
BellmanFordStatsResult,
1314
BellmanFordWriteResult,
1415
SingleSourceBellmanFordEndpoints,
1516
)
@@ -61,6 +62,36 @@ def stream(
6162

6263
return result
6364

65+
def stats(
66+
self,
67+
G: GraphV2,
68+
source_node: int,
69+
relationship_weight_property: str | None = None,
70+
relationship_types: list[str] | None = None,
71+
node_labels: list[str] | None = None,
72+
sudo: bool = False,
73+
log_progress: bool = True,
74+
username: str | None = None,
75+
concurrency: int | None = None,
76+
job_id: str | None = None,
77+
) -> BellmanFordStatsResult:
78+
config = self._endpoints_helper.create_base_config(
79+
G,
80+
sourceNode=source_node,
81+
relationshipWeightProperty=relationship_weight_property,
82+
relationshipTypes=relationship_types,
83+
nodeLabels=node_labels,
84+
sudo=sudo,
85+
logProgress=log_progress,
86+
username=username,
87+
concurrency=concurrency,
88+
jobId=job_id,
89+
)
90+
91+
result = self._endpoints_helper.run_job_and_get_summary("v2/pathfinding.singleSource.bellmanFord", config)
92+
93+
return BellmanFordStatsResult(**result)
94+
6495
def mutate(
6596
self,
6697
G: GraphV2,

graphdatascience/procedure_surface/cypher/pathfinding/single_source_bellman_ford_cypher_endpoints.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from graphdatascience.procedure_surface.api.estimation_result import EstimationResult
1010
from graphdatascience.procedure_surface.api.pathfinding.single_source_bellman_ford_endpoints import (
1111
BellmanFordMutateResult,
12+
BellmanFordStatsResult,
1213
BellmanFordWriteResult,
1314
SingleSourceBellmanFordEndpoints,
1415
)
@@ -63,6 +64,37 @@ def stream(
6364
], # skip reoute column
6465
)
6566

67+
def stats(
68+
self,
69+
G: GraphV2,
70+
source_node: int,
71+
relationship_weight_property: str | None = None,
72+
relationship_types: list[str] | None = None,
73+
node_labels: list[str] | None = None,
74+
sudo: bool = False,
75+
log_progress: bool = True,
76+
username: str | None = None,
77+
concurrency: int | None = None,
78+
job_id: str | None = None,
79+
) -> BellmanFordStatsResult:
80+
config = ConfigConverter.convert_to_gds_config(
81+
sourceNode=source_node,
82+
relationshipWeightProperty=relationship_weight_property,
83+
relationshipTypes=relationship_types,
84+
nodeLabels=node_labels,
85+
sudo=sudo,
86+
logProgress=log_progress,
87+
username=username,
88+
concurrency=concurrency,
89+
jobId=job_id,
90+
)
91+
params = CallParameters(graph_name=G.name(), config=config)
92+
params.ensure_job_id_in_config()
93+
94+
result = self._query_runner.call_procedure("gds.bellmanFord.stats", params=params, logging=log_progress).iloc[0]
95+
96+
return BellmanFordStatsResult(**result.to_dict())
97+
6698
def mutate(
6799
self,
68100
G: GraphV2,

graphdatascience/session/session_v2_endpoints.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
from graphdatascience.procedure_surface.api.pathfinding.k_spanning_tree_endpoints import KSpanningTreeEndpoints
3737
from graphdatascience.procedure_surface.api.pathfinding.prize_steiner_tree_endpoints import PrizeSteinerTreeEndpoints
3838
from graphdatascience.procedure_surface.api.pathfinding.shortest_path_endpoints import ShortestPathEndpoints
39+
from graphdatascience.procedure_surface.api.pathfinding.single_source_bellman_ford_endpoints import (
40+
SingleSourceBellmanFordEndpoints,
41+
)
3942
from graphdatascience.procedure_surface.api.pathfinding.spanning_tree_endpoints import SpanningTreeEndpoints
4043
from graphdatascience.procedure_surface.api.pathfinding.steiner_tree_endpoints import SteinerTreeEndpoints
4144
from graphdatascience.procedure_surface.api.similarity.knn_endpoints import KnnEndpoints
@@ -100,6 +103,9 @@
100103
from graphdatascience.procedure_surface.arrow.pathfinding.shortest_path_arrow_endpoints import (
101104
ShortestPathArrowEndpoints,
102105
)
106+
from graphdatascience.procedure_surface.arrow.pathfinding.single_source_bellman_ford_arrow_endpoints import (
107+
BellmanFordArrowEndpoints,
108+
)
103109
from graphdatascience.procedure_surface.arrow.pathfinding.spanning_tree_arrow_endpoints import (
104110
SpanningTreeArrowEndpoints,
105111
)
@@ -155,6 +161,10 @@ def articulation_points(self) -> ArticulationPointsEndpoints:
155161
def betweenness_centrality(self) -> BetweennessEndpoints:
156162
return BetweennessArrowEndpoints(self._arrow_client, self._write_back_client, show_progress=self._show_progress)
157163

164+
@property
165+
def bellman_ford(self) -> SingleSourceBellmanFordEndpoints:
166+
return BellmanFordArrowEndpoints(self._arrow_client, self._write_back_client, show_progress=self._show_progress)
167+
158168
@property
159169
def clique_counting(self) -> CliqueCountingEndpoints:
160170
return CliqueCountingArrowEndpoints(

graphdatascience/tests/integrationV2/procedure_surface/arrow/pathfinding/test_single_source_bellman_ford_arrow_endpoints.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ def test_bellman_ford_stream(bellman_ford_endpoints: BellmanFordArrowEndpoints,
8484
assert len(result_df) == 5
8585

8686

87+
def test_bellman_ford_stats(bellman_ford_endpoints: BellmanFordArrowEndpoints, sample_graph: GraphV2) -> None:
88+
result = bellman_ford_endpoints.stats(
89+
G=sample_graph,
90+
source_node=0,
91+
relationship_weight_property="cost",
92+
)
93+
94+
assert result.pre_processing_millis >= 0
95+
assert result.compute_millis >= 0
96+
assert result.post_processing_millis >= 0
97+
assert result.contains_negative_cycle is False
98+
assert "sourceNode" in result.configuration
99+
100+
87101
def test_bellman_ford_mutate(bellman_ford_endpoints: BellmanFordArrowEndpoints, sample_graph: GraphV2) -> None:
88102
result = bellman_ford_endpoints.mutate(
89103
G=sample_graph,

graphdatascience/tests/integrationV2/procedure_surface/cypher/pathfinding/test_single_source_bellman_ford_cypher_endpoints.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,20 @@ def test_bellman_ford_stream(bellman_ford_endpoints: BellmanFordCypherEndpoints,
7171
assert len(result_df) > 0
7272

7373

74+
def test_bellman_ford_stats(bellman_ford_endpoints: BellmanFordCypherEndpoints, sample_graph: GraphV2) -> None:
75+
result = bellman_ford_endpoints.stats(
76+
G=sample_graph,
77+
source_node=find_node_by_name(bellman_ford_endpoints._query_runner, "A"),
78+
relationship_weight_property="cost",
79+
)
80+
81+
assert result.pre_processing_millis >= 0
82+
assert result.compute_millis >= 0
83+
assert result.post_processing_millis >= 0
84+
assert result.contains_negative_cycle is False
85+
assert "sourceNode" in result.configuration
86+
87+
7488
def test_bellman_ford_mutate(bellman_ford_endpoints: BellmanFordCypherEndpoints, sample_graph: GraphV2) -> None:
7589
result = bellman_ford_endpoints.mutate(
7690
G=sample_graph,

0 commit comments

Comments
 (0)