max_flow

The maximum flow problem consists of finding a flow through a graph such that it is the maximum possible flow.

The algorithm implementation is based on the Ford-Fulkerson method with capacity scaling. Ford-Fulkerson method is not itself an algorithm as it does not specify the procedure of finding augmenting paths in a residual graph. It is a greedy method, using augmenting paths as it comes across them. Input is a weighted graph with a defined source and sink, representing the beginning and end of the flow network. The algorithm begins with an empty flow and, at each step, finds a path, called an augmenting path, from the source to the sink that generates more flow. When flow cannot be increased anymore, the algorithm stops, and the maximum flow has been found.

The capacity scaling is a heuristic for finding augmenting paths in such a way that prioritizes taking edges with larger capacities, maintaining a threshold value that is only lowered once no larger path can be found. It speeds up the algorithm noticeably compared to a standard DFS search.

The algorithm is adapted to work with heterogeneous graphs, meaning not all edges need to have the defined edge property used for edge flow. When an edge doesn't have a flow, it is skipped, and when no edges have this property, the returning max flow value is 0.

Trait
Module typealgorithm
ImplementationPython
Graph directiondirected
Edge weightsweighted
Parallelismsequential

Too slow?

If this algorithm implementation is too slow for your use case, open an issue on Memgraph's GitHub repository (opens in a new tab) and request a rewrite to C++!

Procedures

get_flow()

Use the get_flow() procedure to calculate the maximum flow.

Input:

  • start_v: Vertex ➡ Source node from which the maximum flow is calculated.
  • end_v: Vertex ➡ Sink node to which the max flow is calculated.
  • edge_property: string (default="weight") ➡ Relationship property which is used as the flow capacity of the relationship.

Output:

  • max_flow: Number ➡ Maximum flow of the network, from the source node to the sink node.

Usage:

To calculate the maximum flow, use the following query:

MATCH (source {id: 0}), (sink {id: 5})
CALL max_flow.get_flow(source, sink, "weight")
YIELD max_flow
RETURN max_flow;

get_paths()

The get_paths() procedure returns paths that are a part of the flow with the maximum flow.

Input:

  • start_v: Vertex ➡ Source node from which the maximum flow is calculated.
  • end_v: Vertex ➡ Sink node to which the max flow is calculated.
  • edge_property: string (default="weight") ➡ Relationship property which is used as the flow capacity of the relationship.

Output:

  • path: Path ➡ The path with a flow in a maximum flow.
  • flow: Number ➡ The flow amount corresponding to that path.

Usage:

To get the paths creating the maximum flow, use the following query:

MATCH (source {id: 0}), (sink {id: 5})
CALL max_flow.get_paths(source, sink, "weight")
YIELD path, flow
RETURN path, flow;

Example

Database state

The database contains the following data:

Created with the following Cypher queries:

MERGE (a:Node {id: "A"}) MERGE (b:Node {id: "B"}) CREATE (a)-[:RELATION {weight: 9}]->(b);
MERGE (a:Node {id: "A"}) MERGE (b:Node {id: "C"}) CREATE (a)-[:RELATION {weight: 10}]->(b);
MERGE (a:Node {id: "B"}) MERGE (b:Node {id: "E"}) CREATE (a)-[:RELATION {weight: 8}]->(b);
MERGE (a:Node {id: "C"}) MERGE (b:Node {id: "F"}) CREATE (a)-[:RELATION {weight: 7}]->(b);
MERGE (a:Node {id: "C"}) MERGE (b:Node {id: "D"}) CREATE (a)-[:RELATION {weight: 1}]->(b);
MERGE (a:Node {id: "A"}) MERGE (b:Node {id: "D"}) CREATE (a)-[:RELATION {weight: 8}]->(b);
MERGE (a:Node {id: "E"}) MERGE (b:Node {id: "D"}) CREATE (a)-[:RELATION {weight: 2}]->(b);
MERGE (a:Node {id: "D"}) MERGE (b:Node {id: "F"}) CREATE (a)-[:RELATION {weight: 11}]->(b);
MERGE (a:Node {id: "E"}) MERGE (b:Node {id: "G"}) CREATE (a)-[:RELATION {weight: 5}]->(b);
MERGE (a:Node {id: "F"}) MERGE (b:Node {id: "G"}) CREATE (a)-[:RELATION {weight: 14}]->(b);

Calculate maximum flow

Calculate the maximum flow using the following query:

MATCH (source {id: "A"}), (sink {id: "G"})
CALL max_flow.get_flow(source, sink)
YIELD max_flow
RETURN max_flow;

Results:

+----------+
| max_flow |
+----------+
| 19       |
+----------+