Files
ortools-clone/graph/max_flow.cc
lperron@google.com 4ab59ec3be add missing includes
2011-12-05 14:13:54 +00:00

315 lines
11 KiB
C++

// Copyright 2010-2011 Google
// Licensed 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.
#include "graph/max_flow.h"
#include <algorithm>
#include "base/commandlineflags.h"
#include "base/stringprintf.h"
DEFINE_bool(max_flow_check_input, false,
"Check that the input is consistent.");
DEFINE_bool(max_flow_check_result, false,
"Check that the result is valid.");
namespace operations_research {
MaxFlow::MaxFlow(const StarGraph* graph,
NodeIndex source,
NodeIndex sink)
: graph_(graph),
node_excess_(),
node_potential_(),
residual_arc_capacity_(),
first_admissible_arc_(),
active_nodes_(),
source_(source),
sink_(sink) {
const NodeIndex max_num_nodes = graph_->max_num_nodes();
if (max_num_nodes > 0) {
node_excess_.Reserve(StarGraph::kFirstNode, max_num_nodes - 1);
node_excess_.SetAll(0);
node_potential_.Reserve(StarGraph::kFirstNode, max_num_nodes - 1);
node_potential_.SetAll(0);
first_admissible_arc_.Reserve(StarGraph::kFirstNode, max_num_nodes - 1);
first_admissible_arc_.SetAll(StarGraph::kNilArc);
}
const ArcIndex max_num_arcs = graph_->max_num_arcs();
if (max_num_arcs > 0) {
residual_arc_capacity_.Reserve(-max_num_arcs, max_num_arcs - 1);
residual_arc_capacity_.SetAll(0);
}
DCHECK(graph_->IsNodeValid(source_));
DCHECK(graph_->IsNodeValid(sink_));
}
bool MaxFlow::CheckInputConsistency() const {
bool ok = true;
for (StarGraph::ArcIterator arc_it(*graph_); arc_it.Ok(); arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
if (residual_arc_capacity_[arc] < 0) {
ok = false;
}
}
return ok;
}
void MaxFlow::SetArcCapacity(ArcIndex arc, FlowQuantity new_capacity) {
DCHECK_LE(0, new_capacity);
DCHECK(graph_->CheckArcValidity(arc));
const FlowQuantity free_capacity = residual_arc_capacity_[arc];
const FlowQuantity capacity_delta = new_capacity - Capacity(arc);
VLOG(2) << "Changing capacity on arc " << arc
<< " from " << Capacity(arc) << " to " << new_capacity
<< ". Current free capacity = " << free_capacity;
if (capacity_delta == 0) {
return; // Nothing to do.
}
status_ = NOT_SOLVED;
if (free_capacity + capacity_delta >= 0) {
// The above condition is true if one of the two conditions is true:
// 1/ (capacity_delta > 0), meaning we are increasing the capacity
// 2/ (capacity_delta < 0 && free_capacity + capacity_delta >= 0)
// meaning we are reducing the capacity, but that the capacity
// reduction is not larger than the free capacity.
DCHECK((capacity_delta > 0) ||
(capacity_delta < 0 && free_capacity + capacity_delta >= 0));
residual_arc_capacity_.Set(arc, free_capacity + capacity_delta);
DCHECK_LE(0, residual_arc_capacity_[arc]);
VLOG(2) << "Now: capacity = " << Capacity(arc) << " flow = " << Flow(arc);
} else {
// We have to reduce the flow on the arc, and update the excesses
// accordingly.
const FlowQuantity flow = residual_arc_capacity_[Opposite(arc)];
const FlowQuantity flow_excess = flow - new_capacity;
VLOG(2) << "Flow value " << flow << " exceeds new capacity "
<< new_capacity << " by " << flow_excess;
SetCapacitySaturate(arc, new_capacity);
const NodeIndex head = Head(arc);
node_excess_.Set(head, node_excess_[head] + flow_excess);
DCHECK_LE(0, residual_arc_capacity_[arc]);
DCHECK_LE(0, residual_arc_capacity_[Opposite(arc)]);
VLOG(2) << DebugString("After SetArcCapacity:", arc);
}
}
bool MaxFlow::CheckResult() const {
bool ok = true;
for (StarGraph::NodeIterator node_it(*graph_); node_it.Ok(); node_it.Next()) {
const NodeIndex node = node_it.Index();
if (node != source_ && node != sink_) {
if (node_excess_[node] != 0) {
LOG(DFATAL) << "node_excess_[" << node << "] = " << node_excess_[node]
<< " != 0";
ok = false;
}
}
}
for (StarGraph::ArcIterator arc_it(*graph_); arc_it.Ok(); arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
const ArcIndex opposite = Opposite(arc);
const FlowQuantity direct_capacity = residual_arc_capacity_[arc];
const FlowQuantity opposite_capacity = residual_arc_capacity_[opposite];
if (direct_capacity < 0) {
LOG(DFATAL) << "residual_arc_capacity_[" << arc << "] = "
<< direct_capacity << " < 0";
ok = false;
}
if (opposite_capacity < 0) {
LOG(DFATAL) << "residual_arc_capacity_[" << opposite << "] = "
<< opposite_capacity << " < 0";
ok = false;
}
// The initial capacity of the direct arcs is non-negative.
if (direct_capacity + opposite_capacity < 0) {
LOG(DFATAL) << "initial capacity [" << arc << "] = "
<< direct_capacity + opposite_capacity << " < 0";
ok = false;
}
}
return ok;
}
bool MaxFlow::CheckRelabelPrecondition(NodeIndex node) const {
DCHECK(IsActive(node));
for (StarGraph::IncidentArcIterator arc_it(*graph_, node);
arc_it.Ok();
arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
DCHECK(!IsAdmissible(arc));
}
return true;
}
string MaxFlow::DebugString(const string& context, ArcIndex arc) const {
const NodeIndex tail = Tail(arc);
const NodeIndex head = Head(arc);
return StringPrintf("%s Arc %d, from %d to %d, "
"Capacity = %lld, Residual capacity = %lld, "
"Flow = residual capacity for reverse arc = %lld, "
"Height(tail) = %lld, Height(head) = %lld, "
"Excess(tail) = %lld, Excess(head) = %lld",
context.c_str(), arc, tail, head, Capacity(arc),
residual_arc_capacity_[arc], Flow(arc),
node_potential_[tail], node_potential_[head],
node_excess_[tail], node_excess_[head]);
}
bool MaxFlow::Solve() {
status_ = NOT_SOLVED;
if (FLAGS_max_flow_check_input && !CheckInputConsistency()) {
status_ = BAD_INPUT;
return false;
}
InitializePreflow();
ResetFirstAdmissibleArcs();
Refine();
if (FLAGS_max_flow_check_result && !CheckResult()) {
status_ = BAD_RESULT;
return false;
}
total_flow_ = 0;
for (StarGraph::OutgoingArcIterator arc_it(*graph_, source_);
arc_it.Ok();
arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
total_flow_ += Flow(arc);
}
status_ = OPTIMAL;
return true;
}
void MaxFlow::ResetFirstAdmissibleArcs() {
for (StarGraph::NodeIterator node_it(*graph_); node_it.Ok(); node_it.Next()) {
const NodeIndex node = node_it.Index();
first_admissible_arc_.Set(node, GetFirstIncidentArc(node));
}
}
void MaxFlow::InitializePreflow() {
// InitializePreflow() clears the whole flow that could have been computed
// by a previous Solve(). This is not optimal in terms of complexity.
// TODO(user): find a way to make the re-solving incremental (not an obvious
// task, and there has not been a lot of literature on the subject.)
node_potential_.SetAll(0);
node_excess_.SetAll(0);
for (StarGraph::ArcIterator arc_it(*graph_); arc_it.Ok(); arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
SetCapacityResetFlow(arc, Capacity(arc));
}
// The initial height of the source is equal to the number of nodes.
node_potential_.Set(source_, graph_->num_nodes());
for (StarGraph::OutgoingArcIterator arc_it(*graph_, source_);
arc_it.Ok();
arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
const FlowQuantity arc_capacity = Capacity(arc);
// Saturate arcs outgoing from the source. This is different from PushFlow,
// since the preconditions for PushFlow are not (yet) met, and we do not
// need to update the excess at the source.
SetCapacitySaturate(arc, arc_capacity);
node_excess_.Set(Head(arc), arc_capacity);
VLOG(2) << DebugString("InitializePreflow:", arc);
}
}
void MaxFlow::PushFlow(FlowQuantity flow, ArcIndex arc) {
DCHECK_GT(residual_arc_capacity_[arc], 0);
DCHECK_GT(node_excess_[Tail(arc)], 0);
VLOG(2) << "PushFlow: pushing " << flow << " on arc " << arc
<< " from node " << Tail(arc) << " to node " << Head(arc);
PushFlowUnsafe(arc, flow);
// Update the excesses at the tail and head of the arc.
const NodeIndex tail = Tail(arc);
node_excess_.Set(tail, node_excess_[tail] - flow);
const NodeIndex head = Head(arc);
node_excess_.Set(head, node_excess_[head] + flow);
VLOG(3) << DebugString("PushFlow: ", arc);
}
void MaxFlow::InitializeActiveNodeContainer() {
DCHECK(IsEmptyActiveNodeContainer());
for (StarGraph::NodeIterator node_it(*graph_); node_it.Ok(); node_it.Next()) {
const NodeIndex node = node_it.Index();
if (IsActive(node)) {
PushActiveNode(node);
VLOG(2) << "InitializeActiveNodeStack: node " << node << " added.";
}
}
}
void MaxFlow::Refine() {
InitializeActiveNodeContainer();
while (!IsEmptyActiveNodeContainer()) {
const NodeIndex node = GetAndRemoveFirstActiveNode();
if (IsActive(node)) {
VLOG(2) << "Refine: calling Discharge for node " << node;
Discharge(node);
}
}
}
void MaxFlow::Discharge(NodeIndex node) {
DCHECK(IsActive(node));
VLOG(2) << "Discharging node " << node << ", excess = " << node_excess_[node];
while (IsActive(node)) {
for (StarGraph::IncidentArcIterator arc_it(*graph_, node,
first_admissible_arc_[node]);
arc_it.Ok();
arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
VLOG(3) << DebugString("Discharge: considering", arc);
if (IsAdmissible(arc)) {
if (node_excess_[node] != 0) {
VLOG(2) << "Discharge: calling PushFlow.";
const NodeIndex head = Head(arc);
const bool head_active_before_push = IsActive(head);
const FlowQuantity delta = std::min(node_excess_[node],
residual_arc_capacity_[arc]);
PushFlow(delta, arc);
if (IsActive(head) && !head_active_before_push) {
PushActiveNode(Head(arc));
}
}
if (node_excess_[node] == 0) {
first_admissible_arc_.Set(node, arc); // arc may still be admissible.
return;
}
}
}
Relabel(node);
}
}
void MaxFlow::Relabel(NodeIndex node) {
DCHECK(CheckRelabelPrecondition(node));
CostValue min_height = node_potential_[node];
for (StarGraph::IncidentArcIterator arc_it(*graph_, node);
arc_it.Ok();
arc_it.Next()) {
const ArcIndex arc = arc_it.Index();
DCHECK_EQ(Tail(arc), node);
if (residual_arc_capacity_[arc] > 0) {
// Update min_height only for arcs with available capacity.
min_height = std::min(min_height, node_potential_[Head(arc)]);
}
}
VLOG(2) << "Relabel: height(" << node << ") relabeled from "
<< node_potential_[node] << " to " << min_height + 1;
node_potential_.Set(node, min_height + 1);
first_admissible_arc_.Set(node, GetFirstIncidentArc(node));
}
} // namespace operations_research