use pybind11 for ortools/graph
This commit is contained in:
31
ortools/graph/python/BUILD.bazel
Normal file
31
ortools/graph/python/BUILD.bazel
Normal file
@@ -0,0 +1,31 @@
|
||||
# Python wrapper for graph libraries.
|
||||
|
||||
load("@ortools_deps//:requirements.bzl", "requirement")
|
||||
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
|
||||
|
||||
pybind_extension(
|
||||
name = "linear_sum_assignment",
|
||||
srcs = ["linear_sum_assignment.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//ortools/graph:assignment"
|
||||
],
|
||||
)
|
||||
|
||||
pybind_extension(
|
||||
name = "max_flow",
|
||||
srcs = ["max_flow.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//ortools/graph:max_flow"
|
||||
],
|
||||
)
|
||||
|
||||
pybind_extension(
|
||||
name = "min_cost_flow",
|
||||
srcs = ["min_cost_flow.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//ortools/graph:min_cost_flow"
|
||||
],
|
||||
)
|
||||
@@ -1,34 +1,53 @@
|
||||
set_property(SOURCE graph.i PROPERTY CPLUSPLUS ON)
|
||||
set_property(SOURCE graph.i PROPERTY SWIG_MODULE_NAME pywrapgraph)
|
||||
set_property(SOURCE graph.i PROPERTY COMPILE_DEFINITIONS
|
||||
${OR_TOOLS_COMPILE_DEFINITIONS} ABSL_MUST_USE_RESULT)
|
||||
swig_add_library(pywrapgraph
|
||||
TYPE SHARED
|
||||
LANGUAGE python
|
||||
OUTPUT_DIR ${PYTHON_PROJECT_DIR}/graph
|
||||
SOURCES graph.i)
|
||||
|
||||
target_include_directories(pywrapgraph PRIVATE ${Python3_INCLUDE_DIRS})
|
||||
set_property(TARGET pywrapgraph PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
|
||||
target_compile_definitions(pywrapgraph PUBLIC "PY3")
|
||||
|
||||
pybind11_add_module(linear_sum_assignment_pybind11 MODULE linear_sum_assignment.cc)
|
||||
# note: macOS is APPLE and also UNIX !
|
||||
set_target_properties(linear_sum_assignment_pybind11 PROPERTIES
|
||||
LIBRARY_OUTPUT_NAME "linear_sum_assignment")
|
||||
if(APPLE)
|
||||
set_target_properties(pywrapgraph PROPERTIES
|
||||
set_target_properties(linear_sum_assignment_pybind11 PROPERTIES
|
||||
SUFFIX ".so"
|
||||
INSTALL_RPATH "@loader_path;@loader_path/../../${PROJECT_NAME}/.libs")
|
||||
set_property(TARGET pywrapgraph APPEND PROPERTY
|
||||
LINK_FLAGS "-flat_namespace -undefined suppress")
|
||||
INSTALL_RPATH "@loader_path;@loader_path/../../../${PYTHON_PROJECT}/.libs"
|
||||
LINK_FLAGS "-flat_namespace -undefined suppress"
|
||||
)
|
||||
elseif(UNIX)
|
||||
set_target_properties(pywrapgraph PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN:$ORIGIN/../../${PROJECT_NAME}/.libs")
|
||||
set_target_properties(linear_sum_assignment_pybind11 PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN:$ORIGIN/../../../${PYTHON_PROJECT}/.libs"
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(pywrapgraph PRIVATE ortools::ortools)
|
||||
target_link_libraries(linear_sum_assignment_pybind11 PRIVATE ${PROJECT_NAMESPACE}::ortools)
|
||||
add_library(${PROJECT_NAMESPACE}::linear_sum_assignment_pybind11 ALIAS linear_sum_assignment_pybind11)
|
||||
|
||||
# Variable PYTHON_LIBRARIES can contains keyword `optimized`
|
||||
# which won't be interpreted inside a generator expression.
|
||||
# i.e. we can't use: $<$<PLATFORM_ID:Windows>:${PYTHON_LIBRARIES}>
|
||||
# see: https://cmake.org/cmake/help/git-stage/command/target_link_libraries.html#command:target_link_libraries
|
||||
if(MSVC)
|
||||
target_link_libraries(pywrapgraph PRIVATE ${Python3_LIBRARIES})
|
||||
pybind11_add_module(max_flow_pybind11 MODULE max_flow.cc)
|
||||
# note: macOS is APPLE and also UNIX !
|
||||
set_target_properties(max_flow_pybind11 PROPERTIES
|
||||
LIBRARY_OUTPUT_NAME "max_flow")
|
||||
if(APPLE)
|
||||
set_target_properties(max_flow_pybind11 PROPERTIES
|
||||
SUFFIX ".so"
|
||||
INSTALL_RPATH "@loader_path;@loader_path/../../../${PYTHON_PROJECT}/.libs"
|
||||
LINK_FLAGS "-flat_namespace -undefined suppress"
|
||||
)
|
||||
elseif(UNIX)
|
||||
set_target_properties(max_flow_pybind11 PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN:$ORIGIN/../../../${PYTHON_PROJECT}/.libs"
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(max_flow_pybind11 PRIVATE ${PROJECT_NAMESPACE}::ortools)
|
||||
add_library(${PROJECT_NAMESPACE}::max_flow_pybind11 ALIAS max_flow_pybind11)
|
||||
|
||||
pybind11_add_module(min_cost_flow_pybind11 MODULE min_cost_flow.cc)
|
||||
# note: macOS is APPLE and also UNIX !
|
||||
set_target_properties(min_cost_flow_pybind11 PROPERTIES
|
||||
LIBRARY_OUTPUT_NAME "min_cost_flow")
|
||||
if(APPLE)
|
||||
set_target_properties(min_cost_flow_pybind11 PROPERTIES
|
||||
SUFFIX ".so"
|
||||
INSTALL_RPATH "@loader_path;@loader_path/../../../${PYTHON_PROJECT}/.libs"
|
||||
LINK_FLAGS "-flat_namespace -undefined suppress"
|
||||
)
|
||||
elseif(UNIX)
|
||||
set_target_properties(min_cost_flow_pybind11 PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN:$ORIGIN/../../../${PYTHON_PROJECT}/.libs"
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(min_cost_flow_pybind11 PRIVATE ${PROJECT_NAMESPACE}::ortools)
|
||||
add_library(${PROJECT_NAMESPACE}::min_cost_flow_pybind11 ALIAS min_cost_flow_pybind11)
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2010-2021 Google LLC
|
||||
// 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.
|
||||
|
||||
// This .i files exposes some of the C++ classes in ../, namely :
|
||||
// - SimpleMaxFlow, from ../max_flow.h
|
||||
// - SimpleMinCostFlow, from ../min_cost_flow.h
|
||||
// - LinearSumAssignment, from ../assignment.h
|
||||
//
|
||||
// USAGE EXAMPLES:
|
||||
// - examples/python/pyflow_example.py
|
||||
// - examples/python/linear_assignment_api.py
|
||||
//
|
||||
// The rest of the code of ../ was not deemed worth exporting to python.
|
||||
// This could change; contact the code owners if you would like some C++
|
||||
// API to be exposed here.
|
||||
//
|
||||
// TODO(user): test all the APIs that are currently marked as 'untested'.
|
||||
//swiglint: disable full-signature
|
||||
|
||||
%include "ortools/base/base.i"
|
||||
|
||||
%include "ortools/util/python/functions.i"
|
||||
|
||||
%import "ortools/util/python/vector.i"
|
||||
|
||||
%import "ortools/graph/ebert_graph.h"
|
||||
|
||||
// Convert the "std::vector<int>* result" parameters to python outputs.
|
||||
// Note: the typemap itself is in "ortools/base/base.i".
|
||||
%apply std::vector<int>* OUTPUT {std::vector<int>* result}
|
||||
%apply std::vector<int>* OUTPUT {std::vector<int>* nodes}
|
||||
|
||||
%{
|
||||
#include "ortools/graph/assignment.h"
|
||||
#include "ortools/graph/max_flow.h"
|
||||
#include "ortools/graph/min_cost_flow.h"
|
||||
#include "ortools/graph/shortestpaths.h"
|
||||
%}
|
||||
|
||||
// ############ max_flow.h ############
|
||||
|
||||
%ignoreall
|
||||
|
||||
%unignore operations_research;
|
||||
%unignore operations_research::SimpleMaxFlow;
|
||||
%unignore operations_research::SimpleMaxFlow::SimpleMaxFlow;
|
||||
%unignore operations_research::SimpleMaxFlow::~SimpleMaxFlow;
|
||||
%unignore operations_research::SimpleMaxFlow::AddArcWithCapacity;
|
||||
%unignore operations_research::SimpleMaxFlow::SetArcCapacity; // untested
|
||||
%unignore operations_research::SimpleMaxFlow::NumNodes; // untested
|
||||
%unignore operations_research::SimpleMaxFlow::NumArcs;
|
||||
%unignore operations_research::SimpleMaxFlow::Tail;
|
||||
%unignore operations_research::SimpleMaxFlow::Head;
|
||||
%unignore operations_research::SimpleMaxFlow::Capacity;
|
||||
// Expose the "operations_research::SimpleMaxFlow::Status" enum.
|
||||
%unignore operations_research::SimpleMaxFlow::Status;
|
||||
%unignore operations_research::SimpleMaxFlow::OPTIMAL;
|
||||
%unignore operations_research::SimpleMaxFlow::POSSIBLE_OVERFLOW; // untested
|
||||
%unignore operations_research::SimpleMaxFlow::BAD_INPUT; // untested
|
||||
%unignore operations_research::SimpleMaxFlow::BAD_RESULT; // untested
|
||||
%unignore operations_research::SimpleMaxFlow::Solve;
|
||||
%unignore operations_research::SimpleMaxFlow::OptimalFlow;
|
||||
%unignore operations_research::SimpleMaxFlow::Flow;
|
||||
%unignore operations_research::SimpleMaxFlow::GetSourceSideMinCut; // untested
|
||||
%unignore operations_research::SimpleMaxFlow::GetSinkSideMinCut; // untested
|
||||
|
||||
%include "ortools/graph/max_flow.h"
|
||||
|
||||
%unignoreall
|
||||
|
||||
// ############ min_cost_flow.h ############
|
||||
|
||||
%ignoreall
|
||||
|
||||
%unignore operations_research;
|
||||
|
||||
// We prefer our users to access the Status enum via the "SimpleMinCostFlow"
|
||||
// class, i.e. SimpleMinCostFlow.OPTIMAL. But since they're defined on a
|
||||
// base class in the .h, we must expose it.
|
||||
%unignore operations_research::MinCostFlowBase;
|
||||
%unignore operations_research::MinCostFlowBase::Status;
|
||||
%unignore operations_research::MinCostFlowBase::OPTIMAL;
|
||||
%unignore operations_research::MinCostFlowBase::NOT_SOLVED; // untested
|
||||
%unignore operations_research::MinCostFlowBase::FEASIBLE; // untested
|
||||
%unignore operations_research::MinCostFlowBase::INFEASIBLE; // untested
|
||||
%unignore operations_research::MinCostFlowBase::UNBALANCED; // untested
|
||||
%unignore operations_research::MinCostFlowBase::BAD_RESULT; // untested
|
||||
%unignore operations_research::MinCostFlowBase::BAD_COST_RANGE; // untested
|
||||
|
||||
%unignore operations_research::SimpleMinCostFlow;
|
||||
%unignore operations_research::SimpleMinCostFlow::SimpleMinCostFlow;
|
||||
%unignore operations_research::SimpleMinCostFlow::~SimpleMinCostFlow;
|
||||
%unignore operations_research::SimpleMinCostFlow::AddArcWithCapacityAndUnitCost;
|
||||
%unignore operations_research::SimpleMinCostFlow::SetNodeSupply;
|
||||
%unignore operations_research::SimpleMinCostFlow::Solve;
|
||||
// untested
|
||||
%unignore operations_research::SimpleMinCostFlow::SolveMaxFlowWithMinCost;
|
||||
%unignore operations_research::SimpleMinCostFlow::OptimalCost;
|
||||
%unignore operations_research::SimpleMinCostFlow::MaximumFlow; // untested
|
||||
%unignore operations_research::SimpleMinCostFlow::Flow;
|
||||
%unignore operations_research::SimpleMinCostFlow::NumNodes; // untested
|
||||
%unignore operations_research::SimpleMinCostFlow::NumArcs;
|
||||
%unignore operations_research::SimpleMinCostFlow::Tail;
|
||||
%unignore operations_research::SimpleMinCostFlow::Head;
|
||||
%unignore operations_research::SimpleMinCostFlow::Capacity; // untested
|
||||
%unignore operations_research::SimpleMinCostFlow::Supply; // untested
|
||||
%unignore operations_research::SimpleMinCostFlow::UnitCost;
|
||||
|
||||
%include "ortools/graph/min_cost_flow.h"
|
||||
|
||||
%unignoreall
|
||||
|
||||
// ############ assignment.h ############
|
||||
|
||||
%ignoreall
|
||||
|
||||
%unignore operations_research;
|
||||
// We only expose the C++ "operations_research::SimpleLinearSumAssignment"
|
||||
// class, and we rename it "LinearSumAssignment".
|
||||
%rename(LinearSumAssignment) operations_research::SimpleLinearSumAssignment;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::SimpleLinearSumAssignment;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::~SimpleLinearSumAssignment;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::AddArcWithCost;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::NumNodes;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::NumArcs; // untested
|
||||
%unignore operations_research::SimpleLinearSumAssignment::LeftNode; // untested
|
||||
%unignore operations_research::SimpleLinearSumAssignment::RightNode; // untested
|
||||
%unignore operations_research::SimpleLinearSumAssignment::Cost; // untested
|
||||
%unignore operations_research::SimpleLinearSumAssignment::Status;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::OPTIMAL;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::INFEASIBLE;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::POSSIBLE_OVERFLOW;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::Solve;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::OptimalCost;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::RightMate;
|
||||
%unignore operations_research::SimpleLinearSumAssignment::AssignmentCost;
|
||||
|
||||
%include "ortools/graph/assignment.h"
|
||||
|
||||
%unignoreall
|
||||
|
||||
// ############ shortestpaths.h ############
|
||||
|
||||
%ignoreall
|
||||
|
||||
%unignore operations_research;
|
||||
%unignore operations_research::DijkstraShortestPath;
|
||||
%unignore operations_research::BellmanFordShortestPath;
|
||||
%unignore operations_research::AStarShortestPath;
|
||||
|
||||
%include "ortools/graph/shortestpaths.h"
|
||||
|
||||
%unignoreall
|
||||
46
ortools/graph/python/linear_sum_assignment.cc
Normal file
46
ortools/graph/python/linear_sum_assignment.cc
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2010-2021 Google LLC
|
||||
// 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 "ortools/graph/assignment.h"
|
||||
#include "pybind11/pybind11.h"
|
||||
#include "pybind11/stl.h"
|
||||
|
||||
using ::operations_research::NodeIndex;
|
||||
using ::operations_research::SimpleLinearSumAssignment;
|
||||
using ::pybind11::arg;
|
||||
|
||||
PYBIND11_MODULE(linear_sum_assignment, m) {
|
||||
pybind11::class_<SimpleLinearSumAssignment> slsa(m,
|
||||
"SimpleLinearSumAssignment");
|
||||
slsa.def(pybind11::init<>());
|
||||
slsa.def("add_arc_with_cost", &SimpleLinearSumAssignment::AddArcWithCost,
|
||||
arg("left_node"), arg("right_node"), arg("cost"));
|
||||
slsa.def("num_nodes", &SimpleLinearSumAssignment::NumNodes);
|
||||
slsa.def("num_arcs", &SimpleLinearSumAssignment::NumArcs);
|
||||
slsa.def("left_node", &SimpleLinearSumAssignment::LeftNode, arg("arc"));
|
||||
slsa.def("right_node", &SimpleLinearSumAssignment::RightNode, arg("arc"));
|
||||
slsa.def("cost", &SimpleLinearSumAssignment::Cost, arg("arc"));
|
||||
slsa.def("solve", &SimpleLinearSumAssignment::Solve);
|
||||
slsa.def("optimal_cost", &SimpleLinearSumAssignment::OptimalCost);
|
||||
slsa.def("right_mate", &SimpleLinearSumAssignment::RightMate,
|
||||
arg("left_node"));
|
||||
slsa.def("assignment_cost", &SimpleLinearSumAssignment::AssignmentCost,
|
||||
arg("left_node"));
|
||||
|
||||
pybind11::enum_<SimpleLinearSumAssignment::Status>(slsa, "Status")
|
||||
.value("OPTIMAL", SimpleLinearSumAssignment::Status::OPTIMAL)
|
||||
.value("INFEASIBLE", SimpleLinearSumAssignment::Status::INFEASIBLE)
|
||||
.value("POSSIBLE_OVERFLOW",
|
||||
SimpleLinearSumAssignment::Status::POSSIBLE_OVERFLOW)
|
||||
.export_values();
|
||||
}
|
||||
55
ortools/graph/python/max_flow.cc
Normal file
55
ortools/graph/python/max_flow.cc
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2010-2021 Google LLC
|
||||
// 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 "ortools/graph/max_flow.h"
|
||||
|
||||
#include "pybind11/pybind11.h"
|
||||
#include "pybind11/stl.h"
|
||||
|
||||
using ::operations_research::NodeIndex;
|
||||
using ::operations_research::SimpleMaxFlow;
|
||||
using ::pybind11::arg;
|
||||
|
||||
PYBIND11_MODULE(max_flow, m) {
|
||||
pybind11::class_<SimpleMaxFlow> smf(m, "SimpleMaxFlow");
|
||||
smf.def(pybind11::init<>());
|
||||
smf.def("add_arc_with_capacity", &SimpleMaxFlow::AddArcWithCapacity,
|
||||
arg("tail"), arg("head"), arg("capacity"));
|
||||
smf.def("set_arc_capacity", &SimpleMaxFlow::SetArcCapacity, arg("arc"),
|
||||
arg("capacity"));
|
||||
smf.def("num_nodes", &SimpleMaxFlow::NumNodes);
|
||||
smf.def("num_arcs", &SimpleMaxFlow::NumArcs);
|
||||
smf.def("tail", &SimpleMaxFlow::Tail, arg("arc"));
|
||||
smf.def("head", &SimpleMaxFlow::Head, arg("arc"));
|
||||
smf.def("capacity", &SimpleMaxFlow::Capacity, arg("arc"));
|
||||
smf.def("solve", &SimpleMaxFlow::Solve, arg("source"), arg("sink"));
|
||||
smf.def("optimal_flow", &SimpleMaxFlow::OptimalFlow);
|
||||
smf.def("flow", &SimpleMaxFlow::Flow, arg("arc"));
|
||||
smf.def("get_source_side_min_cut", [](SimpleMaxFlow* smf) {
|
||||
std::vector<NodeIndex> result;
|
||||
smf->GetSourceSideMinCut(&result);
|
||||
return result;
|
||||
});
|
||||
smf.def("get_sink_side_min_cut", [](SimpleMaxFlow* smf) {
|
||||
std::vector<NodeIndex> result;
|
||||
smf->GetSinkSideMinCut(&result);
|
||||
return result;
|
||||
});
|
||||
|
||||
pybind11::enum_<SimpleMaxFlow::Status>(smf, "Status")
|
||||
.value("OPTIMAL", SimpleMaxFlow::Status::OPTIMAL)
|
||||
.value("POSSIBLE_OVERFLOW", SimpleMaxFlow::Status::POSSIBLE_OVERFLOW)
|
||||
.value("BAD_INPUT", SimpleMaxFlow::Status::BAD_INPUT)
|
||||
.value("BAD_RESULT", SimpleMaxFlow::Status::BAD_RESULT)
|
||||
.export_values();
|
||||
}
|
||||
54
ortools/graph/python/min_cost_flow.cc
Normal file
54
ortools/graph/python/min_cost_flow.cc
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2010-2021 Google LLC
|
||||
// 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 "ortools/graph/min_cost_flow.h"
|
||||
|
||||
#include "pybind11/pybind11.h"
|
||||
#include "pybind11/stl.h"
|
||||
|
||||
using ::operations_research::MinCostFlowBase;
|
||||
using ::operations_research::SimpleMinCostFlow;
|
||||
using ::pybind11::arg;
|
||||
|
||||
PYBIND11_MODULE(min_cost_flow, m) {
|
||||
pybind11::class_<SimpleMinCostFlow> smcf(m, "SimpleMinCostFlow");
|
||||
smcf.def(pybind11::init<>());
|
||||
smcf.def("add_arc_with_capacity_and_unit_cost",
|
||||
&SimpleMinCostFlow::AddArcWithCapacityAndUnitCost, arg("tail"),
|
||||
arg("head"), arg("capacity"), arg("unit_cost"));
|
||||
smcf.def("set_node_supply", &SimpleMinCostFlow::SetNodeSupply, arg("node"),
|
||||
arg("supply"));
|
||||
smcf.def("num_nodes", &SimpleMinCostFlow::NumNodes);
|
||||
smcf.def("num_arcs", &SimpleMinCostFlow::NumArcs);
|
||||
smcf.def("tail", &SimpleMinCostFlow::Tail, arg("arc"));
|
||||
smcf.def("head", &SimpleMinCostFlow::Head, arg("arc"));
|
||||
smcf.def("capacity", &SimpleMinCostFlow::Capacity, arg("arc"));
|
||||
smcf.def("supply", &SimpleMinCostFlow::Supply, arg("node"));
|
||||
smcf.def("unit_cost", &SimpleMinCostFlow::UnitCost, arg("arc"));
|
||||
smcf.def("solve", &SimpleMinCostFlow::Solve);
|
||||
smcf.def("solve_max_flow_with_min_cost",
|
||||
&SimpleMinCostFlow::SolveMaxFlowWithMinCost);
|
||||
smcf.def("optimal_cost", &SimpleMinCostFlow::OptimalCost);
|
||||
smcf.def("maximum_flow", &SimpleMinCostFlow::MaximumFlow);
|
||||
smcf.def("flow", &SimpleMinCostFlow::Flow, arg("arc"));
|
||||
|
||||
pybind11::enum_<SimpleMinCostFlow::Status>(smcf, "Status")
|
||||
.value("BAD_COST_RANGE", MinCostFlowBase::Status::BAD_COST_RANGE)
|
||||
.value("BAD_RESULT", MinCostFlowBase::Status::BAD_RESULT)
|
||||
.value("FEASIBLE", MinCostFlowBase::Status::FEASIBLE)
|
||||
.value("INFEASIBLE", MinCostFlowBase::Status::INFEASIBLE)
|
||||
.value("NOT_SOLVED", MinCostFlowBase::Status::NOT_SOLVED)
|
||||
.value("OPTIMAL", MinCostFlowBase::Status::OPTIMAL)
|
||||
.value("UNBALANCED", MinCostFlowBase::Status::UNBALANCED)
|
||||
.export_values();
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
load(":code_samples.bzl", "code_sample_cc")
|
||||
load(":code_samples.bzl", "code_sample_cc_py")
|
||||
|
||||
code_sample_cc(name = "assignment_linear_sum_assignment")
|
||||
code_sample_cc_py(name = "assignment_linear_sum_assignment")
|
||||
|
||||
code_sample_cc(name = "assignment_min_flow")
|
||||
code_sample_cc_py(name = "assignment_min_flow")
|
||||
|
||||
code_sample_cc(name = "balance_min_flow")
|
||||
code_sample_cc_py(name = "balance_min_flow")
|
||||
|
||||
code_sample_cc(name = "simple_max_flow_program")
|
||||
code_sample_cc_py(name = "simple_max_flow_program")
|
||||
|
||||
code_sample_cc(name = "simple_min_cost_flow_program")
|
||||
code_sample_cc_py(name = "simple_min_cost_flow_program")
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
# [START program]
|
||||
"""Solve assignment problem using linear assignment solver."""
|
||||
# [START import]
|
||||
from ortools.graph import pywrapgraph
|
||||
from ortools.graph.python import linear_sum_assignment
|
||||
# [END import]
|
||||
|
||||
|
||||
def main():
|
||||
"""Linear Sum Assignment example."""
|
||||
# [START solver]
|
||||
assignment = pywrapgraph.LinearSumAssignment()
|
||||
assignment = linear_sum_assignment.SimpleLinearSumAssignment()
|
||||
# [END solver]
|
||||
|
||||
# [START data]
|
||||
@@ -39,19 +39,19 @@ def main():
|
||||
for worker in range(num_workers):
|
||||
for task in range(num_tasks):
|
||||
if costs[worker][task]:
|
||||
assignment.AddArcWithCost(worker, task, costs[worker][task])
|
||||
assignment.add_arc_with_cost(worker, task, costs[worker][task])
|
||||
# [END constraints]
|
||||
|
||||
# [START solve]
|
||||
status = assignment.Solve()
|
||||
status = assignment.solve()
|
||||
# [END solve]
|
||||
|
||||
# [START print_solution]
|
||||
if status == assignment.OPTIMAL:
|
||||
print(f'Total cost = {assignment.OptimalCost()}\n')
|
||||
for i in range(0, assignment.NumNodes()):
|
||||
print(f'Worker {i} assigned to task {assignment.RightMate(i)}.' +
|
||||
f' Cost = {assignment.AssignmentCost(i)}')
|
||||
print(f'Total cost = {assignment.optimal_cost()}\n')
|
||||
for i in range(0, assignment.num_nodes()):
|
||||
print(f'Worker {i} assigned to task {assignment.right_mate(i)}.' +
|
||||
f' Cost = {assignment.assignment_cost(i)}')
|
||||
elif status == assignment.INFEASIBLE:
|
||||
print('No assignment is possible.')
|
||||
elif status == assignment.POSSIBLE_OVERFLOW:
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# [START program]
|
||||
"""Linear assignment example."""
|
||||
# [START import]
|
||||
from ortools.graph import pywrapgraph
|
||||
from ortools.graph.python import min_cost_flow
|
||||
# [END import]
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ def main():
|
||||
"""Solving an Assignment Problem with MinCostFlow."""
|
||||
# [START solver]
|
||||
# Instantiate a SimpleMinCostFlow solver.
|
||||
min_cost_flow = pywrapgraph.SimpleMinCostFlow()
|
||||
smcf = min_cost_flow.SimpleMinCostFlow()
|
||||
# [END solver]
|
||||
|
||||
# [START data]
|
||||
@@ -49,34 +49,31 @@ def main():
|
||||
# [START constraints]
|
||||
# Add each arc.
|
||||
for i in range(len(start_nodes)):
|
||||
min_cost_flow.AddArcWithCapacityAndUnitCost(start_nodes[i],
|
||||
end_nodes[i], capacities[i],
|
||||
costs[i])
|
||||
smcf.add_arc_with_capacity_and_unit_cost(start_nodes[i], end_nodes[i],
|
||||
capacities[i], costs[i])
|
||||
# Add node supplies.
|
||||
for i in range(len(supplies)):
|
||||
min_cost_flow.SetNodeSupply(i, supplies[i])
|
||||
smcf.set_node_supply(i, supplies[i])
|
||||
# [END constraints]
|
||||
|
||||
# [START solve]
|
||||
# Find the minimum cost flow between node 0 and node 10.
|
||||
status = min_cost_flow.Solve()
|
||||
status = smcf.solve()
|
||||
# [END solve]
|
||||
|
||||
# [START print_solution]
|
||||
if status == min_cost_flow.OPTIMAL:
|
||||
print('Total cost = ', min_cost_flow.OptimalCost())
|
||||
if status == smcf.OPTIMAL:
|
||||
print('Total cost = ', smcf.optimal_cost())
|
||||
print()
|
||||
for arc in range(min_cost_flow.NumArcs()):
|
||||
for arc in range(smcf.num_arcs()):
|
||||
# Can ignore arcs leading out of source or into sink.
|
||||
if min_cost_flow.Tail(arc) != source and min_cost_flow.Head(
|
||||
arc) != sink:
|
||||
if smcf.tail(arc) != source and smcf.head(arc) != sink:
|
||||
|
||||
# Arcs in the solution have a flow value of 1. Their start and end nodes
|
||||
# give an assignment of worker to task.
|
||||
if min_cost_flow.Flow(arc) > 0:
|
||||
if smcf.flow(arc) > 0:
|
||||
print('Worker %d assigned to task %d. Cost = %d' %
|
||||
(min_cost_flow.Tail(arc), min_cost_flow.Head(arc),
|
||||
min_cost_flow.UnitCost(arc)))
|
||||
(smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc)))
|
||||
else:
|
||||
print('There was an issue with the min cost flow input.')
|
||||
print(f'Status: {status}')
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
# [START program]
|
||||
"""Assignment with teams of workers."""
|
||||
# [START import]
|
||||
from ortools.graph import pywrapgraph
|
||||
from ortools.graph.python import min_cost_flow
|
||||
# [END import]
|
||||
|
||||
|
||||
def main():
|
||||
"""Solving an Assignment with teams of worker."""
|
||||
# [START solver]
|
||||
min_cost_flow = pywrapgraph.SimpleMinCostFlow()
|
||||
smcf = min_cost_flow.SimpleMinCostFlow()
|
||||
# [END solver]
|
||||
|
||||
# [START data]
|
||||
@@ -54,38 +54,34 @@ def main():
|
||||
# [START constraints]
|
||||
# Add each arc.
|
||||
for i in range(0, len(start_nodes)):
|
||||
min_cost_flow.AddArcWithCapacityAndUnitCost(start_nodes[i],
|
||||
end_nodes[i], capacities[i],
|
||||
costs[i])
|
||||
smcf.add_arc_with_capacity_and_unit_cost(start_nodes[i], end_nodes[i],
|
||||
capacities[i], costs[i])
|
||||
|
||||
# Add node supplies.
|
||||
for i in range(0, len(supplies)):
|
||||
min_cost_flow.SetNodeSupply(i, supplies[i])
|
||||
smcf.set_node_supply(i, supplies[i])
|
||||
# [END constraints]
|
||||
|
||||
# [START solve]
|
||||
# Find the minimum cost flow between node 0 and node 10.
|
||||
status = min_cost_flow.Solve()
|
||||
status = smcf.solve()
|
||||
# [END solve]
|
||||
|
||||
# [START print_solution]
|
||||
if status == min_cost_flow.OPTIMAL:
|
||||
min_cost_flow.Solve()
|
||||
print('Total cost = ', min_cost_flow.OptimalCost())
|
||||
if status == smcf.OPTIMAL:
|
||||
smcf.solve()
|
||||
print('Total cost = ', smcf.optimal_cost())
|
||||
print()
|
||||
for arc in range(min_cost_flow.NumArcs()):
|
||||
for arc in range(smcf.num_arcs()):
|
||||
# Can ignore arcs leading out of source or intermediate, or into sink.
|
||||
if (min_cost_flow.Tail(arc) != source and
|
||||
min_cost_flow.Tail(arc) != 11 and
|
||||
min_cost_flow.Tail(arc) != 12 and
|
||||
min_cost_flow.Head(arc) != sink):
|
||||
if (smcf.tail(arc) != source and smcf.tail(arc) != 11 and
|
||||
smcf.tail(arc) != 12 and smcf.head(arc) != sink):
|
||||
|
||||
# Arcs in the solution will have a flow value of 1.
|
||||
# There start and end nodes give an assignment of worker to task.
|
||||
if min_cost_flow.Flow(arc) > 0:
|
||||
if smcf.flow(arc) > 0:
|
||||
print('Worker %d assigned to task %d. Cost = %d' %
|
||||
(min_cost_flow.Tail(arc), min_cost_flow.Head(arc),
|
||||
min_cost_flow.UnitCost(arc)))
|
||||
(smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc)))
|
||||
else:
|
||||
print('There was an issue with the min cost flow input.')
|
||||
print(f'Status: {status}')
|
||||
|
||||
@@ -1,32 +1,60 @@
|
||||
"""Helper macro to compile and test code samples."""
|
||||
|
||||
def code_sample_cc(name):
|
||||
native.cc_binary(
|
||||
name = name,
|
||||
srcs = [name + ".cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/graph:assignment",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
"//ortools/graph:max_flow",
|
||||
"//ortools/graph:min_cost_flow",
|
||||
"//ortools/graph:shortestpaths",
|
||||
],
|
||||
)
|
||||
load("@rules_python//python:defs.bzl", "py_binary")
|
||||
load("@ortools_deps//:requirements.bzl", "requirement")
|
||||
|
||||
native.cc_test(
|
||||
name = name+"_test",
|
||||
size = "small",
|
||||
srcs = [name + ".cc"],
|
||||
deps = [
|
||||
":"+name,
|
||||
"//ortools/base",
|
||||
"//ortools/graph:assignment",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
"//ortools/graph:max_flow",
|
||||
"//ortools/graph:min_cost_flow",
|
||||
"//ortools/graph:shortestpaths",
|
||||
],
|
||||
)
|
||||
def code_sample_cc(name):
|
||||
native.cc_binary(
|
||||
name = name + "_cc",
|
||||
srcs = [name + ".cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/graph:assignment",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
"//ortools/graph:max_flow",
|
||||
"//ortools/graph:min_cost_flow",
|
||||
"//ortools/graph:shortestpaths",
|
||||
],
|
||||
)
|
||||
|
||||
native.sh_test(
|
||||
name = name + "_cc_test",
|
||||
size = "small",
|
||||
srcs = ["code_samples_cc_test.sh"],
|
||||
args = [name],
|
||||
data = [
|
||||
":" + name + "_cc",
|
||||
],
|
||||
)
|
||||
|
||||
def code_sample_py(name):
|
||||
py_binary(
|
||||
name = name + "_py3",
|
||||
srcs = [name + ".py"],
|
||||
main = name + ".py",
|
||||
data = [
|
||||
"//ortools/graph/python:linear_sum_assignment.so",
|
||||
"//ortools/graph/python:min_cost_flow.so",
|
||||
"//ortools/graph/python:max_flow.so",
|
||||
],
|
||||
deps = [
|
||||
requirement("absl-py"),
|
||||
],
|
||||
python_version = "PY3",
|
||||
srcs_version = "PY3",
|
||||
)
|
||||
|
||||
native.sh_test(
|
||||
name = name + "_py_test",
|
||||
size = "small",
|
||||
srcs = ["code_samples_py_test.sh"],
|
||||
args = [name],
|
||||
data = [
|
||||
":" + name + "_py3",
|
||||
],
|
||||
)
|
||||
|
||||
def code_sample_cc_py(name):
|
||||
code_sample_cc(name = name)
|
||||
code_sample_py(name = name)
|
||||
|
||||
9
ortools/graph/samples/code_samples_cc_test.sh
Executable file
9
ortools/graph/samples/code_samples_cc_test.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -r DIR="${TEST_SRCDIR}/com_google_ortools/ortools/graph/samples"
|
||||
|
||||
function test::ortools::code_samples_graph_cc() {
|
||||
"${DIR}/$1_cc"
|
||||
}
|
||||
|
||||
test::ortools::code_samples_graph_cc $1
|
||||
9
ortools/graph/samples/code_samples_py_test.sh
Executable file
9
ortools/graph/samples/code_samples_py_test.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -r DIR="${TEST_SRCDIR}/com_google_ortools/ortools/graph/samples"
|
||||
|
||||
function test::ortools::code_samples_graph_py() {
|
||||
"${DIR}/$1_py3"
|
||||
}
|
||||
|
||||
test::ortools::code_samples_graph_py $1
|
||||
@@ -14,7 +14,7 @@
|
||||
# [START program]
|
||||
"""From Taha 'Introduction to Operations Research', example 6.4-2."""
|
||||
# [START import]
|
||||
from ortools.graph import pywrapgraph
|
||||
from ortools.graph.python import max_flow
|
||||
# [END import]
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ def main():
|
||||
"""MaxFlow simple interface example."""
|
||||
# [START solver]
|
||||
# Instantiate a SimpleMaxFlow solver.
|
||||
max_flow = pywrapgraph.SimpleMaxFlow()
|
||||
smf = max_flow.SimpleMaxFlow()
|
||||
# [END solver]
|
||||
|
||||
# [START data]
|
||||
@@ -37,28 +37,27 @@ def main():
|
||||
# [START constraints]
|
||||
# Add each arc.
|
||||
for arc in zip(start_nodes, end_nodes, capacities):
|
||||
max_flow.AddArcWithCapacity(arc[0], arc[1], arc[2])
|
||||
smf.add_arc_with_capacity(arc[0], arc[1], arc[2])
|
||||
# [END constraints]
|
||||
|
||||
# [START solve]
|
||||
# Find the maximum flow between node 0 and node 4.
|
||||
status = max_flow.Solve(0, 4)
|
||||
status = smf.solve(0, 4)
|
||||
# [END solve]
|
||||
|
||||
# [START print_solution]
|
||||
if status != max_flow.OPTIMAL:
|
||||
if status != smf.OPTIMAL:
|
||||
print('There was an issue with the max flow input.')
|
||||
print(f'Status: {status}')
|
||||
exit(1)
|
||||
print('Max flow:', max_flow.OptimalFlow())
|
||||
print('Max flow:', smf.optimal_flow())
|
||||
print('')
|
||||
print(' Arc Flow / Capacity')
|
||||
for i in range(max_flow.NumArcs()):
|
||||
for i in range(smf.num_arcs()):
|
||||
print('%1s -> %1s %3s / %3s' %
|
||||
(max_flow.Tail(i), max_flow.Head(i), max_flow.Flow(i),
|
||||
max_flow.Capacity(i)))
|
||||
print('Source side min-cut:', max_flow.GetSourceSideMinCut())
|
||||
print('Sink side min-cut:', max_flow.GetSinkSideMinCut())
|
||||
(smf.tail(i), smf.head(i), smf.flow(i), smf.capacity(i)))
|
||||
print('Source side min-cut:', smf.get_source_side_min_cut())
|
||||
print('Sink side min-cut:', smf.get_sink_side_min_cut())
|
||||
# [END print_solution]
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# [START program]
|
||||
"""From Bradley, Hax and Maganti, 'Applied Mathematical Programming', figure 8.1."""
|
||||
# [START import]
|
||||
from ortools.graph import pywrapgraph
|
||||
from ortools.graph.python import min_cost_flow
|
||||
# [END import]
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ def main():
|
||||
"""MinCostFlow simple interface example."""
|
||||
# [START solver]
|
||||
# Instantiate a SimpleMinCostFlow solver.
|
||||
min_cost_flow = pywrapgraph.SimpleMinCostFlow()
|
||||
smcf = min_cost_flow.SimpleMinCostFlow()
|
||||
# [END solver]
|
||||
|
||||
# [START data]
|
||||
@@ -41,32 +41,31 @@ def main():
|
||||
# [START constraints]
|
||||
# Add each arc.
|
||||
for arc in zip(start_nodes, end_nodes, capacities, unit_costs):
|
||||
min_cost_flow.AddArcWithCapacityAndUnitCost(arc[0], arc[1], arc[2],
|
||||
arc[3])
|
||||
smcf.add_arc_with_capacity_and_unit_cost(arc[0], arc[1], arc[2], arc[3])
|
||||
|
||||
# Add node supply.
|
||||
for count, supply in enumerate(supplies):
|
||||
min_cost_flow.SetNodeSupply(count, supply)
|
||||
smcf.set_node_supply(count, supply)
|
||||
# [END constraints]
|
||||
|
||||
# [START solve]
|
||||
# Find the min cost flow.
|
||||
status = min_cost_flow.Solve()
|
||||
status = smcf.solve()
|
||||
# [END solve]
|
||||
|
||||
# [START print_solution]
|
||||
if status != min_cost_flow.OPTIMAL:
|
||||
if status != smcf.OPTIMAL:
|
||||
print('There was an issue with the min cost flow input.')
|
||||
print(f'Status: {status}')
|
||||
exit(1)
|
||||
print('Minimum cost: ', min_cost_flow.OptimalCost())
|
||||
print('Minimum cost: ', smcf.optimal_cost())
|
||||
print('')
|
||||
print(' Arc Flow / Capacity Cost')
|
||||
for i in range(min_cost_flow.NumArcs()):
|
||||
cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
|
||||
print('%1s -> %1s %3s / %3s %3s' %
|
||||
(min_cost_flow.Tail(i), min_cost_flow.Head(i),
|
||||
min_cost_flow.Flow(i), min_cost_flow.Capacity(i), cost))
|
||||
for i in range(smcf.num_arcs()):
|
||||
cost = smcf.flow(i) * smcf.unit_cost(i)
|
||||
print(
|
||||
'%1s -> %1s %3s / %3s %3s' %
|
||||
(smcf.tail(i), smcf.head(i), smcf.flow(i), smcf.capacity(i), cost))
|
||||
# [END print_solution]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user