-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
67 changed files
with
1,987 additions
and
696 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2010-2022 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. | ||
|
||
// Demonstrates how to call the MathOpt C API defined in solver.h from C++. | ||
// | ||
// At a high level, the example: | ||
// * builds a ModelProto in C++, | ||
// * serializes the model to binary, | ||
// * calls MathOptSolve() from the C-API on the model binary, which outputs a | ||
// SolveResultProto in binary, | ||
// * parses a C++ SolveResultProto from the binary, | ||
// * prints some key parts of the SolveResultProto. | ||
// | ||
// Actual C++ users should use MathOpt's various C++ APIs. This is just a | ||
// demonstration of how the C API is intended to be used (from any language that | ||
// an interoperate with C). | ||
|
||
#include <cstddef> | ||
#include <cstdlib> | ||
#include <iostream> | ||
#include <string> | ||
|
||
#include "absl/status/status.h" | ||
#include "ortools/base/init_google.h" | ||
#include "ortools/math_opt/core/c_api/solver.h" | ||
#include "ortools/math_opt/model.pb.h" | ||
#include "ortools/math_opt/parameters.pb.h" | ||
#include "ortools/math_opt/result.pb.h" | ||
#include "ortools/math_opt/sparse_containers.pb.h" | ||
|
||
// This example solves the optimization problem: | ||
// max x | ||
// x in [0, 1] | ||
// and then prints out the termination reason and objective value. | ||
int main(int argc, char** argv) { | ||
InitGoogle(argv[0], &argc, &argv, true); | ||
|
||
// Create a serialized ModelProto for the problem. | ||
operations_research::math_opt::ModelProto model; | ||
model.mutable_variables()->add_ids(0); | ||
model.mutable_variables()->add_lower_bounds(0.0); | ||
model.mutable_variables()->add_upper_bounds(1.0); | ||
model.mutable_variables()->add_names("x"); | ||
model.mutable_variables()->add_integers(false); | ||
model.mutable_objective()->set_maximize(true); | ||
model.mutable_objective()->mutable_linear_coefficients()->add_ids(0); | ||
model.mutable_objective()->mutable_linear_coefficients()->add_values(1.0); | ||
const std::string model_str = model.SerializeAsString(); | ||
const void* model_bin = model_str.data(); | ||
const size_t model_bin_size = model_str.size(); | ||
|
||
// Pick a solver. | ||
const int solver_type = | ||
static_cast<int>(operations_research::math_opt::SOLVER_TYPE_GLOP); | ||
|
||
// Set up the output arguments for MathOptSolve() | ||
void* result_bin = nullptr; | ||
size_t result_bin_size = 0; | ||
char* status_msg = nullptr; | ||
|
||
// Call the C API to do solve the model and populate the output arguments. | ||
const int status_code = MathOptSolve(model_bin, model_bin_size, solver_type, | ||
/*interrupter=*/nullptr, &result_bin, | ||
&result_bin_size, &status_msg); | ||
|
||
// If MathOptSolve() failed, print the error and abort. | ||
if (status_code != 0) { | ||
std::cerr << absl::Status(static_cast<absl::StatusCode>(status_code), | ||
status_msg) | ||
<< std::endl; | ||
// If you handle the error instead of crashing, be sure to free status_msg. | ||
std::abort(); | ||
} | ||
|
||
// Recover the SolveResultProto from the output arguments (stored as a | ||
// serialized proto). | ||
operations_research::math_opt::SolveResultProto result; | ||
if (!result.ParseFromArray(result_bin, static_cast<int>(result_bin_size))) { | ||
std::cout << "failed to parse SolveResultProto" << std::endl; | ||
std::abort(); | ||
} | ||
|
||
// Print out the desired output. | ||
std::cout << "Termination is optimal: " | ||
<< (result.termination().reason() == | ||
operations_research::math_opt::TERMINATION_REASON_OPTIMAL) | ||
<< std::endl; | ||
std::cout << "Objective value: " | ||
<< result.termination().objective_bounds().primal_bound() | ||
<< std::endl; | ||
|
||
// Clean up any memory allocated by MathOptSolve(). Note that invoking these | ||
// functions on nullptr is safe. | ||
MathOptFree(result_bin); | ||
MathOptFree(status_msg); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Copyright 2010-2022 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. | ||
|
||
"""Tests that cpp_example.cc solves a small mip and prints out the answer. | ||
This is done by running the program in a subprocess and then asserting against | ||
what was printed to standard out. | ||
""" | ||
|
||
import unittest | ||
from ortools.math_opt.examples import log_scraping | ||
from ortools.math_opt.testing import binary_testing | ||
|
||
|
||
class CppExampleTest( | ||
binary_testing.BinaryAssertions, | ||
log_scraping.LogScraping, | ||
unittest.TestCase, | ||
): | ||
def test_regression(self): | ||
result = self.assert_binary_succeeds("ortools/math_opt/core/c_api/cpp_example") | ||
is_optimal = self.assert_has_line_with_prefixed_number( | ||
"Termination is optimal: ", result.stdout | ||
) | ||
self.assertEqual(is_optimal, 1) | ||
objective_value = self.assert_has_line_with_prefixed_number( | ||
"Objective value: ", result.stdout | ||
) | ||
self.assertAlmostEqual(objective_value, 1.0) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Copyright 2010-2022 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/math_opt/core/c_api/solver.h" | ||
|
||
#include <stddef.h> | ||
|
||
#include <cstdlib> | ||
#include <cstring> | ||
#include <ios> | ||
#include <limits> | ||
#include <utility> | ||
|
||
#include "absl/log/check.h" | ||
#include "absl/status/status.h" | ||
#include "absl/status/statusor.h" | ||
#include "ortools/base/status_builder.h" | ||
#include "ortools/base/status_macros.h" | ||
#include "ortools/math_opt/core/solve_interrupter.h" | ||
#include "ortools/math_opt/core/solver.h" | ||
#include "ortools/math_opt/model.pb.h" | ||
#include "ortools/math_opt/parameters.pb.h" | ||
#include "ortools/math_opt/result.pb.h" | ||
|
||
struct MathOptInterrupter { | ||
operations_research::math_opt::SolveInterrupter cpp_interrupter; | ||
}; | ||
|
||
namespace operations_research::math_opt { | ||
namespace { | ||
|
||
// Returns a serialized SolveResultProto and its size. The caller is responsible | ||
// for freeing the result. | ||
absl::StatusOr<std::pair<void*, size_t>> SolveImpl( | ||
const void* model_bytes, const size_t model_size, const int solver_type, | ||
MathOptInterrupter* const interrupter, const bool build_result) { | ||
if (model_bytes == nullptr && model_size != 0) { | ||
return absl::InvalidArgumentError( | ||
"model cannot be null unless model_size is zero"); | ||
} | ||
if (model_size > std::numeric_limits<int>::max()) { | ||
return util::InvalidArgumentErrorBuilder() | ||
<< "model_size must be at most max int, was: " << model_size; | ||
} | ||
ModelProto model; | ||
if (model_size > 0) { | ||
if (!model.ParseFromArray(model_bytes, static_cast<int>(model_size))) { | ||
return absl::InvalidArgumentError("bad model proto"); | ||
} | ||
} | ||
Solver::InitArgs init_args; | ||
Solver::SolveArgs solve_args; | ||
if (interrupter != nullptr) { | ||
solve_args.interrupter = &interrupter->cpp_interrupter; | ||
} | ||
ASSIGN_OR_RETURN(const SolveResultProto result, | ||
Solver::NonIncrementalSolve( | ||
model, static_cast<SolverTypeProto>(solver_type), | ||
init_args, solve_args)); | ||
const size_t result_size_bytes = result.ByteSizeLong(); | ||
if (result_size_bytes > std::numeric_limits<int>::max()) { | ||
return util::InvalidArgumentErrorBuilder() | ||
<< "cannot serialize a SolveResultProto with more than INT_MAX = " | ||
<< std::numeric_limits<int>::max() << "(0x" << std::hex | ||
<< std::numeric_limits<int>::max() << std::dec | ||
<< ") bytes, but solve result proto needed " << result_size_bytes | ||
<< " bytes in binary format"; | ||
} | ||
void* result_bin = nullptr; | ||
if (build_result) { | ||
result_bin = malloc(result_size_bytes); | ||
// For current implementation, only fails on proto3 when the size is bigger | ||
// than 2 gigs. | ||
const bool serialize_ok = result.SerializeToArray( | ||
result_bin, static_cast<int>(result_size_bytes)); | ||
if (!serialize_ok) { | ||
free(result_bin); | ||
return absl::InternalError("fail to serialize SolveResultProto"); | ||
} | ||
} | ||
return std::make_pair(result_bin, result_size_bytes); | ||
} | ||
|
||
} // namespace | ||
} // namespace operations_research::math_opt | ||
|
||
MathOptInterrupter* MathOptNewInterrupter() { return new MathOptInterrupter(); } | ||
|
||
void MathOptFreeInterrupter(MathOptInterrupter* interrupter) { | ||
delete interrupter; | ||
} | ||
|
||
void MathOptInterrupt(MathOptInterrupter* interrupter) { | ||
CHECK(interrupter != nullptr); | ||
interrupter->cpp_interrupter.Interrupt(); | ||
} | ||
int MathOptIsInterrupted(const MathOptInterrupter* interrupter) { | ||
CHECK(interrupter != nullptr); | ||
return static_cast<int>(interrupter->cpp_interrupter.IsInterrupted()); | ||
} | ||
|
||
int MathOptSolve(const void* model, const size_t model_size, | ||
const int solver_type, MathOptInterrupter* const interrupter, | ||
void** solve_result, size_t* solve_result_size, | ||
char** status_msg) { | ||
const absl::StatusOr<std::pair<void*, size_t>> result = | ||
operations_research::math_opt::SolveImpl( | ||
model, model_size, solver_type, interrupter, | ||
/*build_result=*/solve_result != nullptr); | ||
if (result.ok()) { | ||
if (solve_result_size != nullptr) { | ||
*solve_result_size = result->second; | ||
} | ||
if (solve_result != nullptr) { | ||
*solve_result = result->first; | ||
} | ||
if (status_msg != nullptr) { | ||
*status_msg = nullptr; | ||
} | ||
return 0; | ||
} | ||
// WARNING: failure could be caused by null arguments! | ||
if (status_msg != nullptr) { | ||
const size_t num_bytes = result.status().message().size() + 1; | ||
*status_msg = static_cast<char*>(malloc(num_bytes)); | ||
std::memcpy(*status_msg, result.status().message().data(), num_bytes); | ||
} | ||
if (solve_result != nullptr) { | ||
*solve_result = nullptr; | ||
} | ||
if (solve_result_size != nullptr) { | ||
*solve_result_size = 0; | ||
} | ||
return result.status().raw_code(); | ||
} | ||
|
||
void MathOptFree(void* ptr) { free(ptr); } |
Oops, something went wrong.