31#include "absl/flags/flag.h"
32#include "absl/log/check.h"
33#include "absl/strings/match.h"
34#include "absl/strings/str_format.h"
35#include "absl/strings/string_view.h"
46#include "ortools/graph/flow_problem.pb.h"
55ABSL_FLAG(std::string, output_dimacs,
"",
"Output problem as a dimacs file.");
56ABSL_FLAG(std::string, output_proto,
"",
"Output problem as a flow proto.");
57ABSL_FLAG(
bool, use_flow_graph,
true,
"Use special kind of graph.");
58ABSL_FLAG(
bool, sort_heads,
false,
"Sort outgoing arcs by head.");
59ABSL_FLAG(
bool, detect_reverse_arcs,
true,
"Detect reverse arcs.");
68 std::string* dimacs) {
69 CHECK_EQ(FlowModelProto::MIN_COST_FLOW, flow_model.problem_type());
71 dimacs->append(
"c Min-Cost flow problem generated from a FlowModelProto.\n");
74 int64_t num_nodes = 0;
75 for (int64_t i = 0; i < flow_model.arcs_size(); ++i) {
76 num_nodes = std::max(num_nodes, flow_model.arcs(i).tail() + 1);
77 num_nodes = std::max(num_nodes, flow_model.arcs(i).head() + 1);
81 const int64_t num_arcs = flow_model.arcs_size();
82 dimacs->append(
"c\nc Problem line (problem_type, num nodes, num arcs).\n");
83 dimacs->append(absl::StrFormat(
"p min %d %d\n", num_nodes, num_arcs));
86 dimacs->append(
"c\nc Node descriptor lines (id, supply/demand).\n");
87 for (int64_t i = 0; i < flow_model.nodes_size(); ++i) {
88 const int64_t
id = flow_model.nodes(i).id() + 1;
89 const int64_t supply = flow_model.nodes(i).supply();
91 dimacs->append(absl::StrFormat(
"n %d %d\n",
id, supply));
97 "c\nc Arc descriptor Lines (tail, head, minflow, maxflow, cost).\n");
98 for (int64_t i = 0; i < flow_model.arcs_size(); ++i) {
99 const int64_t tail = flow_model.arcs(i).tail() + 1;
100 const int64_t head = flow_model.arcs(i).head() + 1;
101 const int64_t unit_cost = flow_model.arcs(i).unit_cost();
102 const int64_t capacity = flow_model.arcs(i).capacity();
103 dimacs->append(absl::StrFormat(
"a %d %d %d %d %d\n", tail, head, int64_t{0},
104 capacity, unit_cost));
106 dimacs->append(
"c\n");
113 FlowModelProto* flow_model) {
115 FlowModelProto::ProblemType problem_type;
117 if (line.empty())
continue;
118 if (line[0] ==
'p') {
119 if (absl::StartsWith(line,
"p min")) {
120 problem_type = FlowModelProto::MIN_COST_FLOW;
121 }
else if (absl::StartsWith(line,
"p max")) {
122 problem_type = FlowModelProto::MAX_FLOW;
124 LOG(ERROR) <<
"Unknown dimacs problem format.";
127 flow_model->set_problem_type(problem_type);
129 if (line[0] ==
'n') {
132 std::stringstream ss(line.substr(1));
133 switch (problem_type) {
134 case FlowModelProto::MIN_COST_FLOW:
137 case FlowModelProto::MAX_FLOW: {
140 supply = (type ==
"s" ? 1 : -1);
144 LOG(ERROR) <<
"Node line before the problem type definition.";
147 FlowNodeProto* node = flow_model->add_nodes();
148 node->set_id(
id - 1);
149 node->set_supply(supply);
151 if (line[0] ==
'a') {
154 int64_t min_capacity = 0;
155 int64_t max_capacity = 0;
156 int64_t unit_cost = 0;
157 std::stringstream ss(line.substr(1));
158 switch (problem_type) {
159 case FlowModelProto::MIN_COST_FLOW:
160 ss >> tail >> head >> min_capacity >> max_capacity >> unit_cost;
162 case FlowModelProto::MAX_FLOW:
163 ss >> tail >> head >> max_capacity;
166 LOG(ERROR) <<
"Arc line before the problem type definition.";
169 FlowArcProto* arc = flow_model->add_arcs();
170 arc->set_tail(tail - 1);
171 arc->set_head(head - 1);
172 arc->set_capacity(max_capacity);
173 arc->set_unit_cost(unit_cost);
174 if (min_capacity != 0) {
175 LOG(ERROR) <<
"We do not support minimum capacity.";
188 double* solving_time) {
193 int64_t num_nodes = 0;
194 for (
int i = 0; i < flow_model.arcs_size(); ++i) {
195 num_nodes = std::max(num_nodes, flow_model.arcs(i).tail() + 1);
196 num_nodes = std::max(num_nodes, flow_model.arcs(i).head() + 1);
200 Graph graph(num_nodes, flow_model.arcs_size());
201 for (
int i = 0; i < flow_model.arcs_size(); ++i) {
202 graph.
AddArc(flow_model.arcs(i).tail(), flow_model.arcs(i).head());
204 std::vector<Graph::ArcIndex> permutation;
205 graph.
Build(&permutation);
207 absl::PrintF(
"%d,", graph.
num_arcs());
210 for (
int i = 0; i < flow_model.arcs_size(); ++i) {
211 const Graph::ArcIndex image = i < permutation.size() ? permutation[i] : i;
212 min_cost_flow.
SetArcUnitCost(image, flow_model.arcs(i).unit_cost());
213 min_cost_flow.
SetArcCapacity(image, flow_model.arcs(i).capacity());
215 for (
int i = 0; i < flow_model.nodes_size(); ++i) {
217 flow_model.nodes(i).supply());
220 *loading_time = timer.
Get();
221 absl::PrintF(
"%f,", *loading_time);
225 CHECK(min_cost_flow.
Solve());
227 *solving_time = timer.
Get();
228 absl::PrintF(
"%f,", *solving_time);
234template <
typename GraphType>
236 const FlowModelProto& flow_model,
double* loading_time,
237 double* solving_time,
238 std::function<
void(GraphType* graph)> configure_graph_options =
nullptr) {
243 GraphType graph(flow_model.nodes_size(), flow_model.arcs_size());
244 for (
int i = 0; i < flow_model.arcs_size(); ++i) {
245 graph.AddArc(flow_model.arcs(i).tail(), flow_model.arcs(i).head());
247 std::vector<typename GraphType::ArcIndex> permutation;
249 if (configure_graph_options !=
nullptr) {
250 configure_graph_options(&graph);
252 graph.Build(&permutation);
254 absl::PrintF(
"%d,", graph.num_nodes());
255 absl::PrintF(
"%d,", graph.num_arcs());
258 typename GraphType::NodeIndex source = -1;
259 typename GraphType::NodeIndex sink = -1;
260 CHECK_EQ(2, flow_model.nodes_size());
261 for (
int i = 0; i < flow_model.nodes_size(); ++i) {
262 if (flow_model.nodes(i).supply() > 0) {
263 source = flow_model.nodes(i).id();
265 if (flow_model.nodes(i).supply() < 0) {
266 sink = flow_model.nodes(i).id();
269 CHECK_NE(source, -1);
274 for (
int i = 0; i < flow_model.arcs_size(); ++i) {
275 const typename GraphType::ArcIndex image =
276 i < permutation.size() ? permutation[i] : i;
280 *loading_time = timer.
Get();
281 absl::PrintF(
"%f,", *loading_time);
285 CHECK(max_flow.
Solve());
287 *solving_time = timer.
Get();
288 absl::PrintF(
"%f,", *solving_time);
295using operations_research::FlowModelProto;
300int main(
int argc,
char** argv) {
302 "Runs the OR-tools min-cost flow on a pattern of files given by --input. "
303 "The files must be in Dimacs text format or in binary FlowModelProto "
306 absl::SetFlag(&FLAGS_stderrthreshold, 0);
307 if (absl::GetFlag(FLAGS_input).empty()) {
308 LOG(FATAL) <<
"Please specify input pattern via --input=...";
310 std::vector<std::string> file_list;
319 "file_name, parsing_time, num_nodes, num_arcs,loading_time, "
320 "solving_time, optimal_cost\n");
321 for (
int i = 0; i < file_list.size(); ++i) {
322 const std::string file_name = file_list[i];
327 double parsing_time = 0;
328 operations_research::FlowModelProto proto;
329 if (absl::EndsWith(file_name,
".bin") ||
330 absl::EndsWith(file_name,
".bin.gz")) {
332 if (absl::EndsWith(file_name,
"gz")) {
333 std::string raw_data;
335 CHECK_OK(StringToProto(raw_data, &proto));
341 if (!ConvertDimacsToFlowModel(file_name, &proto))
continue;
343 absl::PrintF(
"%f,", parsing_time);
346 if (!absl::GetFlag(FLAGS_output_proto).empty()) {
347 LOG(INFO) <<
"Dumping binary proto to '"
348 << absl::GetFlag(FLAGS_output_proto) <<
"'.";
354 if (!absl::GetFlag(FLAGS_output_dimacs).empty()) {
355 LOG(INFO) <<
"Converting first input file to dimacs format.";
360 ConvertFlowModelToDimacs(proto, &dimacs);
364 LOG(INFO) <<
"Done: " << time <<
"s.";
368 double loading_time = 0;
369 double solving_time = 0;
370 switch (proto.problem_type()) {
371 case FlowModelProto::MIN_COST_FLOW:
374 case FlowModelProto::MAX_FLOW:
375 if (absl::GetFlag(FLAGS_use_flow_graph)) {
377 proto, &loading_time, &solving_time,
379 graph->SetDetectReverse(
380 absl::GetFlag(FLAGS_detect_reverse_arcs));
381 graph->SetSortByHead(absl::GetFlag(FLAGS_sort_heads));
389 LOG(ERROR) <<
"Problem type not supported: " << proto.problem_type();
397 absl::PrintF(
"%s", parsing_time_distribution.
StatString());
398 absl::PrintF(
"%s", loading_time_distribution.
StatString());
399 absl::PrintF(
"%s", solving_time_distribution.
StatString());
void Start()
When Start() is called multiple times, only the most recent is used.
bool Solve()
Returns true if a maximum flow was solved.
FlowSumType GetOptimalFlow() const
Returns the total flow found by the algorithm.
void SetArcCapacity(ArcIndex arc, ArcFlowType new_capacity)
void SetArcUnitCost(ArcIndex arc, ArcScaledCostType unit_cost)
Sets the unit cost for the given arc.
bool Solve()
Solves the problem, returning true if a min-cost flow could be found.
void SetArcCapacity(ArcIndex arc, ArcFlowType new_capacity)
Sets the capacity for the given arc.
void SetNodeSupply(NodeIndex node, FlowQuantity supply)
CostValue GetOptimalCost()
std::string StatString() const
void AddTimeInSec(double seconds)
Adds a time in seconds to this distribution.
NodeIndexType num_nodes() const
ArcIndexType num_arcs() const
Returns the number of valid arcs in the graph.
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
absl::Status SetBinaryProto(absl::string_view filename, const google::protobuf::Message &proto, Options options)
absl::Status Match(std::string_view pattern, std::vector< std::string > *result, const file::Options &options)
absl::Status SetContents(absl::string_view filename, absl::string_view contents, Options options)
absl::string_view Basename(absl::string_view path)
absl::Status GetBinaryProto(const absl::string_view filename, google::protobuf::Message *proto, Options options)
In SWIG mode, we don't want anything besides these top-level includes.
void ConvertFlowModelToDimacs(const FlowModelProto &flow_model, std::string *dimacs)
bool ConvertDimacsToFlowModel(absl::string_view file, FlowModelProto *flow_model)
void SolveMinCostFlow(const FlowModelProto &flow_model, double *loading_time, double *solving_time)
Loads a FlowModelProto proto into the MinCostFlow class and solves it.
util::ReverseArcStaticGraph Graph
Type of graph to use.
void SolveMaxFlow(const FlowModelProto &flow_model, double *loading_time, double *solving_time, std::function< void(GraphType *graph)> configure_graph_options=nullptr)
Loads a FlowModelProto proto into the MaxFlow class and solves it.
static int input(yyscan_t yyscanner)
int main(int argc, char **argv)
void SolveMinCostFlow(const FlowModelProto &flow_model, double *loading_time, double *solving_time)
Loads a FlowModelProto proto into the MinCostFlow class and solves it.
ABSL_FLAG(std::string, input, "", "Input file of the problem.")
void SolveMaxFlow(const FlowModelProto &flow_model, double *loading_time, double *solving_time, std::function< void(GraphType *graph)> configure_graph_options=nullptr)
Loads a FlowModelProto proto into the MaxFlow class and solves it.