blob: ac4697e713df32833e903051572014ec12378c78 [file] [log] [blame]
package org.apache.commons.graph.flow;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import static org.apache.commons.graph.Graphs.populate;
import static org.apache.commons.graph.utils.Assertions.checkNotNull;
import static org.apache.commons.graph.visit.GraphVisitor.visit;
import org.apache.commons.graph.DirectedGraph;
import org.apache.commons.graph.Mapper;
import org.apache.commons.graph.VertexPair;
import org.apache.commons.graph.builder.AbstractGraphConnection;
import org.apache.commons.graph.math.monoid.OrderedMonoid;
import org.apache.commons.graph.model.DirectedMutableGraph;
/**
* {@link MaxFlowAlgorithmSelector} implementation.
*
* @param <V> the Graph vertices type
* @param <WE> the Graph edges type
* @param <W> the Graph weight type
*/
final class DefaultMaxFlowAlgorithmSelector<V, WE, W>
implements MaxFlowAlgorithmSelector<V, WE, W>
{
private final DirectedGraph<V, WE> graph;
private final Mapper<WE, W> weightedEdges;
private final V source;
private final V target;
public DefaultMaxFlowAlgorithmSelector( DirectedGraph<V, WE> graph, Mapper<WE, W> weightedEdges, V source, V target )
{
this.graph = graph;
this.weightedEdges = weightedEdges;
this.source = source;
this.target = target;
}
/**
* {@inheritDoc}
*/
public <WO extends OrderedMonoid<W>> W applyingFordFulkerson( WO weightOperations )
{
final WO checkedWeightOperations = checkNotNull( weightOperations, "Weight operations can not be null to find the max flow in the graph" );
// create flow network
final DirectedGraph<V, EdgeWrapper<WE>> flowNetwork = newFlowNetwok( graph, checkedWeightOperations );
// create flow network handler
final FlowNetworkHandler<V, EdgeWrapper<WE>, W> flowNetworkHandler =
new FlowNetworkHandler<V, EdgeWrapper<WE>, W>( flowNetwork, source, target, checkedWeightOperations, new MapperWrapper<WE, W, WO>( checkedWeightOperations, weightedEdges ) );
// perform depth first search
visit( flowNetwork ).from( source ).applyingDepthFirstSearch( flowNetworkHandler );
while ( flowNetworkHandler.hasAugmentingPath() )
{
// update flow network
flowNetworkHandler.updateResidualNetworkWithCurrentAugmentingPath();
// look for another augmenting path with depth first search
visit( flowNetwork ).from( source ).applyingDepthFirstSearch( flowNetworkHandler );
}
return flowNetworkHandler.onCompleted();
}
/**
* {@inheritDoc}
*/
public <WO extends OrderedMonoid<W>> W applyingEdmondsKarp( WO weightOperations )
{
final WO checkedWeightOperations = checkNotNull( weightOperations, "Weight operations can not be null to find the max flow in the graph" );
// create flow network
final DirectedGraph<V, EdgeWrapper<WE>> flowNetwork = newFlowNetwok( graph, checkedWeightOperations );
// create flow network handler
final FlowNetworkHandler<V, EdgeWrapper<WE>, W> flowNetworkHandler =
new FlowNetworkHandler<V, EdgeWrapper<WE>, W>( flowNetwork, source, target, checkedWeightOperations, new MapperWrapper<WE, W, WO>( checkedWeightOperations, weightedEdges ) );
// perform breadth first search
visit( flowNetwork ).from( source ).applyingBreadthFirstSearch( flowNetworkHandler );
while ( flowNetworkHandler.hasAugmentingPath() )
{
// update flow network
flowNetworkHandler.updateResidualNetworkWithCurrentAugmentingPath();
// look for another augmenting path with breadth first search
visit( flowNetwork ).from( source ).applyingBreadthFirstSearch( flowNetworkHandler );
}
return flowNetworkHandler.onCompleted();
}
private <WO extends OrderedMonoid<W>> DirectedGraph<V, EdgeWrapper<WE>> newFlowNetwok( final DirectedGraph<V, WE> graph,
final WO weightOperations )
{
return populate( new DirectedMutableGraph<V, EdgeWrapper<WE>>() )
.withConnections( new AbstractGraphConnection<V, EdgeWrapper<WE>>()
{
@Override
public void connect()
{
// vertices
for ( V vertex : graph.getVertices() )
{
addVertex( vertex );
}
// edges
for ( WE edge : graph.getEdges() )
{
VertexPair<V> edgeVertices = graph.getVertices( edge );
V head = edgeVertices.getHead();
V tail = edgeVertices.getTail();
addEdge( new EdgeWrapper<WE>( edge ) ).from( head ).to( tail );
if ( graph.getEdge( tail, head ) == null )
{
// complete the flow network with a zero-capacity inverse edge
addEdge( new EdgeWrapper<WE>() ).from( tail ).to( head );
}
}
}
} );
}
private static final class EdgeWrapper<WE>
{
private final WE wrapped;
public EdgeWrapper()
{
this( null );
}
public EdgeWrapper( WE wrapped )
{
this.wrapped = wrapped;
}
public WE getWrapped()
{
return wrapped;
}
}
@SuppressWarnings( "serial" ) //the algorithm isn't serializable.
private static final class MapperWrapper<WE, W, WO extends OrderedMonoid<W>>
implements Mapper<EdgeWrapper<WE>, W>
{
private final WO weightOperations;
private final Mapper<WE, W> weightedEdges;
public MapperWrapper( WO weightOperations, Mapper<WE, W> weightedEdges )
{
this.weightOperations = weightOperations;
this.weightedEdges = weightedEdges;
}
public W map( EdgeWrapper<WE> input )
{
if ( input.getWrapped() == null )
{
return weightOperations.identity();
}
return weightedEdges.map( input.getWrapped() );
}
}
}