org.jgrapht.alg.flow.DinicMFImpl Maven / Gradle / Ivy
The newest version!
/*
* (C) Copyright 2018-2023, by Kirill Vishnyakov and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.alg.flow;
import org.jgrapht.*;
import org.jgrapht.alg.util.extension.*;
import java.util.*;
/**
* Implementation of {@literal }Dinic
* algorithm{@literal } with scaling for
* {@literal }.
*
* The running time of the algorithm is $O(n^2m)$.
*
* Dinic algorithm firstly was mentioned in {@literal }DINIC, E. A. 1970. Algorithm for Solution
* of a Problem of Maximum Flow in Networks With Power Estimation. Soviet Math. Dokl. 11,
* 1277-1280.{@literal >}
*
* Scheme of the algorithm:
*
* 1). Create a level graph. If we can't reach the sink return flow value.
*
* 2). Find a blocking flow $f'$ in the level graph.
*
* 3). Add $f'$ to the flow $f$. Move to the step $1$.
*
* @param the graph vertex type.
* @param the graph edge type.
*
* @author Kirill Vishnyakov
*/
public class DinicMFImpl
extends MaximumFlowAlgorithmBase
{
/**
* Current source vertex.
*/
private VertexExtension currentSource;
/**
* Current sink vertex.
*/
private VertexExtension currentSink;
private final ExtensionFactory vertexExtensionsFactory;
private final ExtensionFactory edgeExtensionsFactory;
/**
* Constructor. Constructs a new network on which we will calculate the maximum flow, using
* Dinic algorithm.
*
* @param network the network on which we calculate the maximum flow.
* @param epsilon the tolerance for the comparison of floating point values.
*/
public DinicMFImpl(Graph network, double epsilon)
{
super(network, epsilon);
this.vertexExtensionsFactory = VertexExtension::new;
this.edgeExtensionsFactory = AnnotatedFlowEdge::new;
if (epsilon <= 0) {
throw new IllegalArgumentException("Epsilon must be positive!");
}
for (E e : network.edgeSet()) {
if (network.getEdgeWeight(e) < -epsilon) {
throw new IllegalArgumentException("Capacity must be non-negative!");
}
}
}
/**
* Constructor. Constructs a new network on which we will calculate the maximum flow.
*
* @param network the network on which we calculate the maximum flow.
*/
public DinicMFImpl(Graph network)
{
this(network, DEFAULT_EPSILON);
}
@Override
public MaximumFlow getMaximumFlow(V source, V sink)
{
this.calculateMaxFlow(source, sink);
maxFlow = composeFlow();
return new MaximumFlowImpl<>(maxFlowValue, maxFlow);
}
/**
* Assigns source to currentSource and sink to currentSink. Afterwards invokes dinic() method to
* calculate the maximum flow in the network using Dinic algorithm with scaling.
*
* @param source source vertex.
* @param sink sink vertex.
* @return the value of the maximum flow in the network.
*/
private double calculateMaxFlow(V source, V sink)
{
super.init(source, sink, vertexExtensionsFactory, edgeExtensionsFactory);
if (!network.containsVertex(source)) {
throw new IllegalArgumentException("Network does not contain source!");
}
if (!network.containsVertex(sink)) {
throw new IllegalArgumentException("Network does not contain sink!");
}
if (source.equals(sink)) {
throw new IllegalArgumentException("Source is equal to sink!");
}
currentSource = getVertexExtension(source);
currentSink = getVertexExtension(sink);
dinic();
return maxFlowValue;
}
/**
* Creates a level graph. We can split all vertices of the graph in disjoint sets. In the same
* set will lie vertices with equal distance from the source. It's obvious that level network
* cannot contain edges $i \to j$, where $i$ and $j$ are two vertices for which holds: $|i.level
* - j.level| > 1$. It follows from a property of the shortest paths. Level graph contains only
* edges that lead from level $i$ to the level $i + 1$. Thus level graph does not contain
* backward edges or edges that lead from $i$-th level to $i$-th.
*
* @return true, if level graph has been constructed(i.e we reached the sink), otherwise false.
*/
private boolean bfs()
{
for (V v : network.vertexSet()) {
getVertexExtension(v).level = -1;
}
Queue queue = new ArrayDeque<>();
queue.offer(currentSource);
currentSource.level = 0;
while (!queue.isEmpty() && currentSink.level == -1) {
VertexExtension v = queue.poll();
for (AnnotatedFlowEdge edge : v.getOutgoing()) {
VertexExtension u = edge.getTarget();
if (comparator.compare(edge.flow, edge.capacity) < 0 && u.level == -1) {
u.level = v.level + 1;
queue.offer(u);
}
}
}
return currentSink.level != -1;
}
/**
* Finds a blocking flow in the network. For each vertex we have a pointer on the first edge
* which we can use to reach the sink. If we can't reach the sink using current edge, we
* increment the pointer. So on each iteration we either saturate at least one edge or we
* increment pointer.
*
* @param v current vertex.
* @param flow we can push through.
* @return value of the flow we can push.
*/
public double dfs(VertexExtension v, double flow)
{
if (comparator.compare(0.0, flow) == 0) {
return flow;
}
if (v.equals(currentSink)) {
return flow;
}
double pushed;
while (v.index < v.getOutgoing().size()) {
AnnotatedFlowEdge edge = v.getOutgoing().get(v.index);
VertexExtension u = edge.getTarget();
if (comparator.compare(edge.flow, edge.capacity) < 0 && u.level == v.level + 1) {
pushed = dfs(u, Math.min(flow, edge.capacity - edge.flow));
if (comparator.compare(pushed, 0.0) != 0) {
pushFlowThrough(edge, pushed);
return pushed;
}
}
v.index++;
}
return 0;
}
/**
* Runs Dinic algorithm with scaling. Construct a level graph, then find blocking flow and
* finally increase the flow.
*/
public void dinic()
{
for (;;) {
if (!bfs()) {
break;
}
for (V v : network.vertexSet()) {
getVertexExtension(v).index = 0;
}
while (true) {
double pushed = dfs(currentSource, Double.POSITIVE_INFINITY);
if (pushed == 0.0) {
break;
}
maxFlowValue += pushed;
}
}
}
private VertexExtension getVertexExtension(V v)
{
return (VertexExtension) vertexExtensionManager.getExtension(v);
}
/**
* Extension for vertex class.
*/
class VertexExtension
extends VertexExtensionBase
{
/**
* Stores index of the first unexplored edge from current vertex.
*/
int index;
/**
* Level of vertex in the level graph.
*/
int level;
}
}