Skip to content

Commit

Permalink
math_opt: export from google3
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Sep 1, 2023
1 parent 33b2a93 commit 546f612
Show file tree
Hide file tree
Showing 67 changed files with 1,987 additions and 696 deletions.
5 changes: 4 additions & 1 deletion ortools/math_opt/core/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ cc_library(
"//ortools/base",
"//ortools/base:status_macros",
"//ortools/math_opt:callback_cc_proto",
"//ortools/math_opt:infeasible_subsystem_cc_proto",
"//ortools/math_opt:model_cc_proto",
"//ortools/math_opt:model_parameters_cc_proto",
"//ortools/math_opt:model_update_cc_proto",
"//ortools/math_opt:result_cc_proto",
"//ortools/math_opt:solution_cc_proto",
"//ortools/math_opt:sparse_containers_cc_proto",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
Expand All @@ -44,6 +46,7 @@ cc_library(
":sparse_vector",
"//ortools/base",
"//ortools/base:map_util",
"//ortools/base:types",
"//ortools/math_opt:sparse_containers_cc_proto",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/types:span",
Expand All @@ -60,7 +63,6 @@ cc_library(
"//ortools/base:status_macros",
"//ortools/math_opt:model_cc_proto",
"//ortools/math_opt:model_update_cc_proto",
"//ortools/util:status_macros",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/log:check",
Expand Down Expand Up @@ -103,6 +105,7 @@ cc_library(
hdrs = ["solver.h"],
deps = [
":concurrent_calls_guard",
":math_opt_proto_utils",
":model_summary",
":non_streamable_solver_init_arguments",
":solve_interrupter",
Expand Down
107 changes: 107 additions & 0 deletions ortools/math_opt/core/c_api/cpp_example.cc
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;
}
43 changes: 43 additions & 0 deletions ortools/math_opt/core/c_api/cpp_example_test.py
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()
147 changes: 147 additions & 0 deletions ortools/math_opt/core/c_api/solver.cc
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); }
Loading

0 comments on commit 546f612

Please sign in to comment.