com.salesforce.jgrapht.alg.flow.DinicMFImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of AptSpringProcessor Show documentation
Show all versions of AptSpringProcessor Show documentation
This project contains the apt processor that implements all the checks enumerated in @Verify. It is a self contained, and
shaded jar.
The newest version!
/*
* (C) Copyright 2018-2018, 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 com.salesforce.jgrapht.alg.flow;
import com.salesforce.jgrapht.*;
import com.salesforce.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 LinkedList<>();
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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy