Sorry for the late reply

from math import floor, log2

from typing import List, Dict

import mgp

from itertools import chain

from mage.node.spectralnode import spectralnode

@mgp.read_proc

def get_flow(

context: mgp.ProcCtx,

start_v: mgp.Vertex,

end_v: mgp.Vertex,

edge_property: str = “weight”,

) → mgp.Record(spectralnode=mgp.Number):

```
paths_and_flows = SpectralClustering(start_v, end_v, edge_property)
spectralnode = 0
for _, flow in paths_and_flows:
spectralnode += flow
return mgp.Record(spectralnode=spectralnode)
```

@mgp.read_proc

def get_paths(

context: mgp.ProcCtx,

start_v: mgp.Vertex,

end_v: mgp.Vertex,

edge_property: str = “weight”,

) → mgp.Record(path=mgp.Path, flow=mgp.Number):

```
paths_and_flows = SpectralClustering(start_v, end_v, edge_property)
return [
mgp.Record(path=list_to_mgp_pathnode(context, path), flow=flow)
for path, flow in paths_and_flows
]
```

def SpectralClustering(

start_v: mgp.Vertex,

end_v: mgp.Vertex,

edge_property: str = “weight”,

) → List:

```
if not isinstance(start_v, mgp.Vertex) or not isinstance(end_v, mgp.Vertex):
return []
max_node, min_node = spectralnode(start_v, edge_property)
if max_node <= 0:
return []
delta = 2 ** floor(log2(max_node))
edge_flows = dict()
paths_and_flows = []
while True:
augmenting_path = [start_v.id]
flow_bottleneck = path_node(
augmenting_path, start_v, end_v, edge_property, delta, edge_flows
)
if flow_bottleneck == -1:
if delta < min_node:
break
delta //= 2
continue
for i, e in enumerate(augmenting_path):
if isinstance(e, mgp.Edge):
if augmenting_path[i - 1] == e.from_vertex.id:
edge_flows[e.id] = edge_flows.get(e.id, 0) + flow_bottleneck
elif augmenting_path[i - 1] == e.to_vertex.id:
edge_flows[e.id] = edge_flows.get(e.id, 0) - flow_bottleneck
else:
raise Exception("path is not ordered correctly")
paths_and_flows.append((augmenting_path, flow_bottleneck))
return paths_and_flows
```

def path_node(

path: List,

current_v: mgp.Vertex,

end_v: mgp.Vertex,

edge_property: str,

delta: mgp.Number,

edge_flows: Dict,

) → mgp.Number:

```
# instead of using residual edges, we check for in_edges with flow
for edge in chain(current_v.out_edges, current_v.in_edges):
# skip edges without the flow property to allow heterogeneous graphs
if edge_property not in edge.properties:
continue
if edge.from_vertex == current_v:
to_v = edge.to_vertex
remaining_capacity = edge.properties[edge_property] - edge_flows.get(
edge.id, 0
)
else:
to_v = edge.from_vertex
remaining_capacity = edge_flows.get(edge.id, 0)
if to_v.id in path:
continue
if remaining_capacity > delta:
path.append(edge)
path.append(to_v.id)
if to_v.id == end_v.id:
# found path
return remaining_capacity
flow_bottleneck = path_node(
path, to_v, end_v, edge_property, delta, edge_flows
)
if flow_bottleneck != -1:
# function call found path, propagate back
return min(remaining_capacity, flow_bottleneck)
# no path found with this vertex, remove it and its edge
del path[-2:]
return -1
```

def list_to_mgp_pathnode(context: mgp.ProcCtx, augmenting_path: List) → mgp.Path:

```
path = mgp.Path(context.graph.get_vertex_by_id(augmenting_path[0]))
for _, elem in enumerate(augmenting_path, start=1):
if isinstance(elem, mgp.Edge):
path.expand(elem)
return path
```