run model_builder python tests, rearrange linear solver testdata

This commit is contained in:
Laurent Perron
2023-01-19 16:09:45 +01:00
parent 5a3b2f3044
commit 81f3fa0458
10 changed files with 458 additions and 36 deletions

View File

@@ -1,3 +1,4 @@
absl-py >= 0.13
numpy >= 1.13.3
protobuf >= 4.21.10
scipy >= 1.10.0

View File

@@ -14,7 +14,7 @@
# Python wrapper for model_builder.
load("@ortools_deps//:requirements.bzl", "requirement")
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
load("@rules_python//python:defs.bzl", "py_library")
load("@rules_python//python:defs.bzl", "py_library", "py_test")
pybind_extension(
name = "pywrap_model_builder_helper",
@@ -42,6 +42,22 @@ py_library(
],
)
py_test(
name = "model_builder_helper_test",
srcs = ["model_builder_helper_test.py"],
data = [
":pywrap_model_builder_helper.so",
"//ortools/linear_solver/testdata:large_model.mps.gz",
"//ortools/linear_solver/testdata:maximization.mps",
],
python_version = "PY3",
deps = [
":model_builder_helper",
requirement("numpy"),
requirement("scipy"),
],
)
py_library(
name = "model_builder",
srcs = ["model_builder.py"],
@@ -51,6 +67,23 @@ py_library(
visibility = ["//visibility:public"],
deps = [
":model_builder_helper",
requirement("numpy"),
"//ortools/linear_solver:linear_solver_py_pb2",
],
)
py_test(
name = "model_builder_test",
srcs = ["model_builder_test.py"],
data = [
":pywrap_model_builder_helper.so",
"//ortools/linear_solver/testdata:maximization.mps",
"//ortools/linear_solver/testdata:small_model.lp",
],
python_version = "PY3",
deps = [
":model_builder",
":model_builder_helper",
requirement("numpy"),
],
)

View File

@@ -32,6 +32,7 @@ rather than for solving specific optimization problems.
"""
import math
import numpy as np
from ortools.linear_solver.python import model_builder_helper as mbh
from ortools.linear_solver.python import pywrap_model_builder_helper as pwmb
@@ -40,7 +41,7 @@ from ortools.linear_solver.python import pywrap_model_builder_helper as pwmb
SolveStatus = pwmb.SolveStatus
class LinearExpr(object):
class LinearExpr:
"""Holds an linear expression.
A linear expression is built from constants and variables.
@@ -425,11 +426,11 @@ class Variable(LinearExpr):
# helper is a ModelBuilderHelper, lb is an index (int), ub is None,
# is_integral is None, and name is None.
if mbh.is_integral(lb) and ub is None and is_integral is None:
self.__index = int(lb)
self.__index = np.int32(lb)
self.__helper = helper
else:
index = helper.add_var()
self.__index = index
self.__index = np.int32(index)
self.__helper = helper
helper.set_var_lower_bound(index, lb)
helper.set_var_upper_bound(index, ub)
@@ -464,10 +465,10 @@ class Variable(LinearExpr):
def __repr__(self):
index = self.__index
name = self.__helper.VarName(index)
lb = self.__helper.VarLowerBound(index)
ub = self.__helper.VarUpperBound(index)
is_integer = self.__helper.VarIsInteger(index)
name = self.__helper.var_name(index)
lb = self.__helper.var_lower_bound(index)
ub = self.__helper.var_upper_bound(index)
is_integer = self.__helper.var_is_integral(index)
if name:
if is_integer:
return f'{name}(index={index}, lb={lb}, ub={ub}, integer)'
@@ -549,7 +550,61 @@ class Variable(LinearExpr):
return hash((self.__helper, self.__index))
class VarCompVar(object):
class VariableContainer:
"""Variable container."""
def __init__(self, helper, indices):
self.__helper = helper
self.__indices = indices
def __getitem__(self, pos):
index_or_slice = self.__indices[pos]
if mbh.is_integral(index_or_slice):
return Variable(self.__helper, self.__indices[pos], None, None,
None)
else:
return VariableContainer(self.__helper, index_or_slice)
def index_at(self, pos):
"""Returns the index of the variable at the position 'pos'."""
return self.__indices[pos]
# pylint: disable=invalid-name
@property
def T(self):
"""Returns a view upon the transposed numpy array of variables."""
return VariableContainer(self.__helper, self.__indices.T)
# pylint: enable=invalid-name
@property
def shape(self):
"""Returns the shape of the numpy array."""
return self.__indices.shape
@property
def size(self):
"""Returns the number of variables in the numpy array."""
return self.__indices.size
@property
def ravel(self):
"""returns the flattened array of variables."""
return VariableContainer(self.__helper, self.__indices.ravel())
@property
def flatten(self):
"""returns the flattened array of variables."""
return VariableContainer(self.__helper, self.__indices.flatten())
def __str__(self):
return f'VariableContainer({self.__indices})'
def __repr__(self):
return f'VariableContainer({self.__helper}, {repr(self.__indices)})'
class VarCompVar:
"""Represents var == /!= var."""
def __init__(self, left, right, is_equality):
@@ -579,10 +634,11 @@ class VarCompVar(object):
return self.__is_equality
def __bool__(self):
return (self.__left.index == self.__right.index) == self.__is_equality
return bool(
self.__left.index == self.__right.index) == self.__is_equality
class BoundedLinearExpression(object):
class BoundedLinearExpression:
"""Represents a linear constraint: `lb <= linear expression <= ub`.
The only use of this class is to be added to the ModelBuilder through
@@ -627,7 +683,7 @@ class BoundedLinearExpression(object):
f'Cannot use a BoundedLinearExpression {self} as a Boolean value')
class LinearConstraint(object):
class LinearConstraint:
"""Stores a linear equation.
Example:
@@ -680,7 +736,7 @@ class LinearConstraint(object):
self.__helper.add_term_to_constraint(self.__index, var.index, coeff)
class ModelBuilder(object):
class ModelBuilder:
"""Methods for building a linear model.
Methods beginning with:
@@ -745,6 +801,53 @@ class ModelBuilder(object):
"""Declares a constant variable."""
return self.new_var(value, value, False, '')
def new_var_ndarray_with_bounds(self, lbs, ubs, ints, name):
"""Creates a vector of continuous variables from two vector of bounds."""
if np.shape(lbs) != np.shape(ubs):
raise ValueError('The lbs and ubs vectors must have the same size')
if np.shape(lbs) != np.shape(ints):
raise ValueError('The lbs and ints vectors must have the same size')
var_indices = self.__helper.add_var_ndarray_with_bounds(
lbs, ubs, ints, name)
return VariableContainer(self.__helper, var_indices)
def new_num_var_ndarray(self, shape, lb, ub, name):
"""Creates a vector of continuous variables with the same bounds."""
if mbh.is_integral(shape):
shape = [shape]
var_indices = self.__helper.add_var_ndarray(shape, lb, ub, False, name)
return VariableContainer(self.__helper, var_indices)
def new_num_var_ndarray_with_bounds(self, lbs, ubs, name):
"""Creates a vector of continuous variables from two vector of bounds."""
if np.shape(lbs) != np.shape(ubs):
raise ValueError('The lbs and ubs vectors must have the same size')
var_indices = self.__helper.add_var_ndarray_with_bounds(
lbs, ubs, np.zeros(len(lbs), dtype=bool), name)
return VariableContainer(self.__helper, var_indices)
def new_int_var_ndarray(self, shape, lb, ub, name):
"""Creates a vector of integer variables with the same bounds."""
if mbh.is_integral(shape):
shape = [shape]
var_indices = self.__helper.add_var_ndarray(shape, lb, ub, True, name)
return VariableContainer(self.__helper, var_indices)
def new_int_var_ndarray_with_bounds(self, lbs, ubs, name):
"""Creates a vector of integer variables from two vector of bounds."""
if np.shape(lbs) != np.shape(ubs):
raise ValueError('The lbs and ubs vectors must have the same size')
var_indices = self.__helper.add_var_ndarray_with_bounds(
lbs, ubs, np.ones(len(lbs), dtype=bool), name)
return VariableContainer(self.__helper, var_indices)
def new_bool_var_ndarray(self, shape, name):
"""Creates a vector of Boolean variables."""
if mbh.is_integral(shape):
shape = [shape]
var_indices = self.__helper.add_var_ndarray(shape, 0.0, 1.0, True, name)
return VariableContainer(self.__helper, var_indices)
def var_from_index(self, index):
"""Rebuilds a variable object from the model and its index."""
return Variable(self.__helper, index, None, None, None)
@@ -896,7 +999,7 @@ class ModelBuilder(object):
return self.__helper
class ModelSolver(object):
class ModelSolver:
"""Main solver class.
The purpose of this class is to search for a solution to the model provided

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
# 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 for model_builder_helper."""
import gzip
import os
import threading
import numpy as np
from scipy import sparse
import unittest
from ortools.linear_solver import linear_solver_pb2
from ortools.linear_solver.python import pywrap_model_builder_helper
class PywrapModelBuilderHelperTest(unittest.TestCase):
def test_export_model_proto_to_mps_string(self):
model = pywrap_model_builder_helper.ModelBuilderHelper()
model.set_name('testmodel')
result = model.export_to_mps_string()
self.assertIn('testmodel', result)
self.assertIn('ENDATA', result)
def test_export_model_proto_to_lp_string(self):
model = pywrap_model_builder_helper.ModelBuilderHelper()
model.set_maximize(True)
lp_string = model.export_to_lp_string()
self.assertIn('Maximize', lp_string)
def test_import_from_mps_string(self):
model = pywrap_model_builder_helper.ModelBuilderHelper()
self.assertTrue(model.import_from_mps_string('NAME testmodel'))
self.assertEqual(model.name(), 'testmodel')
# ImportFromMpsFile doesn't read from files yet
def test_import_from_mps_file(self):
path = os.path.dirname(__file__)
mps_path = f'{path}/../testdata/maximization.mps'
model = pywrap_model_builder_helper.ModelBuilderHelper()
self.assertTrue(model.import_from_mps_file(mps_path))
self.assertEqual(model.name(), 'SupportedMaximizationProblem')
def test_import_from_lp_string(self):
model = pywrap_model_builder_helper.ModelBuilderHelper()
model.import_from_lp_string('max:')
self.assertTrue(model.maximize())
# TODO(user): Add test_import_from_lp_file after the implementation is fixed
def test_solve_with_glop(self):
model = linear_solver_pb2.MPModelProto()
model.variable.append(
linear_solver_pb2.MPVariableProto(lower_bound=0.0,
upper_bound=1.0,
objective_coefficient=1.0))
model.maximize = True
request = linear_solver_pb2.MPModelRequest(
model=model,
solver_type=linear_solver_pb2.MPModelRequest.GLOP_LINEAR_PROGRAMMING
)
solver_helper = pywrap_model_builder_helper.ModelSolverHelper('')
result = solver_helper.solve_serialized_request(
request.SerializeToString())
response = linear_solver_pb2.MPSolutionResponse().FromString(result)
self.assertEqual(
response.status,
linear_solver_pb2.MPSolverResponseStatus.MPSOLVER_OPTIMAL)
self.assertAlmostEqual(response.objective_value, 1.0)
def test_solve_with_glop_direct(self):
model = pywrap_model_builder_helper.ModelBuilderHelper()
self.assertEqual(0, model.add_var())
model.set_var_lower_bound(0, 0.0)
model.set_var_upper_bound(0, 1.0)
model.set_var_objective_coefficient(0, 1.0)
model.set_maximize(True)
solver = pywrap_model_builder_helper.ModelSolverHelper('glop')
solver.solve(model)
self.assertEqual(
solver.status(),
linear_solver_pb2.MPSolverResponseStatus.MPSOLVER_OPTIMAL)
self.assertAlmostEqual(solver.objective_value(), 1.0)
self.assertAlmostEqual(solver.var_value(0), 1.0)
values = solver.variable_values()
self.assertEqual(1, len(values))
self.assertAlmostEqual(1.0, values[0])
def test_solve_with_pdlp(self):
model = linear_solver_pb2.MPModelProto()
model.variable.append(
linear_solver_pb2.MPVariableProto(lower_bound=0.0,
upper_bound=1.0,
objective_coefficient=1.0))
model.maximize = True
request = linear_solver_pb2.MPModelRequest(
model=model,
solver_type=linear_solver_pb2.MPModelRequest.PDLP_LINEAR_PROGRAMMING
)
solver_helper = pywrap_model_builder_helper.ModelSolverHelper('')
result = solver_helper.solve_serialized_request(
request.SerializeToString())
response = linear_solver_pb2.MPSolutionResponse().FromString(result)
self.assertEqual(
response.status,
linear_solver_pb2.MPSolverResponseStatus.MPSOLVER_OPTIMAL)
self.assertAlmostEqual(response.objective_value, 1.0)
# TODO(user): Test the log callback after the implementation is completed.
def test_interrupt_solve(self):
# This is an instance that we know Glop won't solve quickly.
path = os.path.dirname(__file__)
mps_path = f'{path}/../testdata/large_model.mps.gz'
with gzip.open(mps_path, 'r') as f:
mps_data = f.read()
model_helper = pywrap_model_builder_helper.ModelBuilderHelper()
self.assertTrue(model_helper.import_from_mps_string(mps_data))
solver_helper = pywrap_model_builder_helper.ModelSolverHelper('glop')
result = []
solve_thread = threading.Thread(
target=lambda: result.append(solver_helper.solve(model_helper)))
solve_thread.start()
self.assertTrue(solver_helper.interrupt_solve())
solve_thread.join(timeout=30.0)
self.assertTrue(solver_helper.has_response())
self.assertEqual(
solver_helper.status(),
pywrap_model_builder_helper.SolveStatus.CANCELLED_BY_USER)
def test_build_model(self):
var_lb = np.array([-1.0])
var_ub = np.array([np.inf])
obj = np.array([10.0])
con_lb = np.array([-5.0, -6.0])
con_ub = np.array([5.0, 6.0])
constraint_matrix = sparse.csr_matrix(np.array([[1.0], [2.0]]))
model = pywrap_model_builder_helper.ModelBuilderHelper()
model.fill_model_from_sparse_data(var_lb, var_ub, obj, con_lb, con_ub,
constraint_matrix)
self.assertEqual(1, model.num_variables())
self.assertEqual(-1.0, model.var_lower_bound(0))
self.assertEqual(np.inf, model.var_upper_bound(0))
self.assertEqual(10.0, model.var_objective_coefficient(0))
self.assertEqual(2, model.num_constraints())
self.assertEqual(-5.0, model.constraint_lower_bound(0))
self.assertEqual(5.0, model.constraint_upper_bound(0))
self.assertEqual([0], model.constraint_var_indices(0))
self.assertEqual([1.0], model.constraint_coefficients(0))
self.assertEqual(-6.0, model.constraint_lower_bound(1))
self.assertEqual(6.0, model.constraint_upper_bound(1))
self.assertEqual([0], model.constraint_var_indices(1))
self.assertEqual([2.0], model.constraint_coefficients(1))
var_array = model.add_var_ndarray([10], 1.0, 5.0, True, 'var_')
self.assertEqual(1, var_array.ndim)
self.assertEqual(10, var_array.size)
self.assertEqual((10,), var_array.shape)
self.assertEqual(model.var_name(var_array[3]), 'var_3')
if __name__ == '__main__':
unittest.main()

View File

@@ -14,9 +14,11 @@
"""Tests for model_builder."""
import math
import numpy as np
import os
from ortools.linear_solver.python import model_builder
import unittest
import os
class ModelBuilderTest(unittest.TestCase):
@@ -124,7 +126,7 @@ ENDATA
def test_import_from_mps_file(self):
path = os.path.dirname(__file__)
mps_path = f'{path}/maximization.mps'
mps_path = f'{path}/../testdata/maximization.mps'
model = model_builder.ModelBuilder()
self.assertTrue(model.import_from_mps_file(mps_path))
self.assertEqual(model.name, 'SupportedMaximizationProblem')
@@ -148,7 +150,7 @@ ENDATA
def test_import_from_lp_file(self):
path = os.path.dirname(__file__)
lp_path = f'{path}/small_model.lp'
lp_path = f'{path}/../testdata/small_model.lp'
model = model_builder.ModelBuilder()
self.assertTrue(model.import_from_lp_file(lp_path))
self.assertEqual(6, model.num_variables)
@@ -177,6 +179,33 @@ ENDATA
self.assertEqual(x, x_copy)
self.assertNotEqual(x, y)
# array
xs = model.new_int_var_ndarray(10, 0.0, 5.0, 'xs_')
self.assertEqual(10, xs.size)
self.assertEqual('xs_4', str(xs[4]))
lbs = np.array([1.0, 2.0, 3.0])
ubs = np.array([3.0, 4.0, 5.0])
ys = model.new_int_var_ndarray_with_bounds(lbs, ubs, 'ys_')
self.assertEqual('VariableContainer([12 13 14])', str(ys))
zs = model.new_int_var_ndarray_with_bounds([1.0, 2.0, 3], [4, 4, 4],
'zs_')
self.assertEqual(3, zs.size)
self.assertEqual((3,), zs.shape)
self.assertEqual('zs_1', str(zs[1]))
self.assertEqual('zs_2(index=17, lb=3.0, ub=4.0, integer)', repr(zs[2]))
bs = model.new_bool_var_ndarray([4, 5], 'bs_')
self.assertEqual((4, 5), bs.shape)
self.assertEqual((5, 4), bs.T.shape)
self.assertEqual(31, bs.index_at((2, 3)))
self.assertEqual(20, bs.size)
self.assertEqual((20,), bs.flatten.shape)
self.assertEqual((20,), bs.ravel.shape)
# Slices are [lb, ub) closed - open.
self.assertEqual(5, bs[3, :].size)
self.assertEqual(6, bs[1:3, 2:5].size)
# Tests the hash method.
var_set = set()
var_set.add(x)

View File

@@ -16,6 +16,7 @@
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>
#include "Eigen/Core"
#include "Eigen/SparseCore"
@@ -39,7 +40,9 @@ using ::operations_research::MPModelRequest;
using ::operations_research::MPSolutionResponse;
using ::operations_research::MPVariableProto;
using ::operations_research::SolveStatus;
using ::pybind11::arg;
namespace py = pybind11;
using ::py::arg;
// TODO(user): The interface uses serialized protos because of issues building
// pybind11_protobuf. See
@@ -103,8 +106,8 @@ void BuildModelFromSparseData(
}
PYBIND11_MODULE(pywrap_model_builder_helper, m) {
pybind11::class_<MPModelExportOptions>(m, "MPModelExportOptions")
.def(pybind11::init<>())
py::class_<MPModelExportOptions>(m, "MPModelExportOptions")
.def(py::init<>())
.def_readwrite("obfuscate", &MPModelExportOptions::obfuscate)
.def_readwrite("log_invalid_names",
&MPModelExportOptions::log_invalid_names)
@@ -112,8 +115,8 @@ PYBIND11_MODULE(pywrap_model_builder_helper, m) {
&MPModelExportOptions::show_unused_variables)
.def_readwrite("max_line_length", &MPModelExportOptions::max_line_length);
pybind11::class_<ModelBuilderHelper>(m, "ModelBuilderHelper")
.def(pybind11::init<>())
py::class_<ModelBuilderHelper>(m, "ModelBuilderHelper")
.def(py::init<>())
.def("export_to_mps_string", &ModelBuilderHelper::ExportToMpsString,
arg("options") = MPModelExportOptions())
.def("export_to_lp_string", &ModelBuilderHelper::ExportToLpString,
@@ -147,6 +150,68 @@ PYBIND11_MODULE(pywrap_model_builder_helper, m) {
arg("objective_coefficients"), arg("constraint_lower_bounds"),
arg("constraint_upper_bounds"), arg("constraint_matrix"))
.def("add_var", &ModelBuilderHelper::AddVar)
.def("add_var_ndarray",
[](ModelBuilderHelper* helper, std::vector<size_t> shape, double lb,
double ub, bool is_integral, const std::string& name_prefix) {
int size = shape[0];
for (int i = 1; i < shape.size(); ++i) {
size *= shape[i];
}
py::array_t<int> result(size);
py::buffer_info info = result.request();
result.resize(shape);
auto ptr = static_cast<int*>(info.ptr);
for (int i = 0; i < size; ++i) {
const int index = helper->AddVar();
ptr[i] = index;
helper->SetVarLowerBound(index, lb);
helper->SetVarUpperBound(index, ub);
helper->SetVarIntegrality(index, is_integral);
if (!name_prefix.empty()) {
helper->SetVarName(index, absl::StrCat(name_prefix, i));
}
}
return result;
})
.def("add_var_ndarray_with_bounds",
[](ModelBuilderHelper* helper, py::array_t<double> lbs,
py::array_t<double> ubs, py::array_t<bool> are_integral,
const std::string& name_prefix) {
py::buffer_info buf_lbs = lbs.request();
py::buffer_info buf_ubs = ubs.request();
py::buffer_info buf_are_integral = are_integral.request();
if (buf_lbs.ndim != 1 || buf_ubs.ndim != 1 ||
buf_are_integral.ndim != 1) {
throw std::runtime_error("Number of dimensions must be one");
}
const int size = buf_lbs.size;
if (size != buf_ubs.size || size != buf_are_integral.size) {
throw std::runtime_error("Input sizes must match");
}
const auto shape = buf_lbs.shape;
if (shape != buf_ubs.shape || shape != buf_are_integral.shape) {
throw std::runtime_error("Input shapes must match");
}
auto lower_bounds = static_cast<double*>(buf_lbs.ptr);
auto upper_bounds = static_cast<double*>(buf_ubs.ptr);
auto integers = static_cast<bool*>(buf_are_integral.ptr);
py::array_t<int> result(size);
result.resize(shape);
py::buffer_info result_info = result.request();
auto ptr = static_cast<int*>(result_info.ptr);
for (int i = 0; i < size; ++i) {
const int index = helper->AddVar();
ptr[i] = index;
helper->SetVarLowerBound(index, lower_bounds[i]);
helper->SetVarUpperBound(index, upper_bounds[i]);
helper->SetVarIntegrality(index, integers[i]);
if (!name_prefix.empty()) {
helper->SetVarName(index, absl::StrCat(name_prefix, i));
}
}
return result;
})
.def("set_var_lower_bound", &ModelBuilderHelper::SetVarLowerBound,
arg("var_index"), arg("lb"))
.def("set_var_upper_bound", &ModelBuilderHelper::SetVarUpperBound,
@@ -199,7 +264,7 @@ PYBIND11_MODULE(pywrap_model_builder_helper, m) {
arg("offset"))
.def("objective_offset", &ModelBuilderHelper::ObjectiveOffset);
pybind11::enum_<SolveStatus>(m, "SolveStatus")
py::enum_<SolveStatus>(m, "SolveStatus")
.value("OPTIMAL", SolveStatus::OPTIMAL)
.value("FEASIBLE", SolveStatus::FEASIBLE)
.value("INFEASIBLE", SolveStatus::INFEASIBLE)
@@ -216,20 +281,20 @@ PYBIND11_MODULE(pywrap_model_builder_helper, m) {
.value("INCOMPATIBLE_OPTIONS", SolveStatus::INCOMPATIBLE_OPTIONS)
.export_values();
pybind11::class_<ModelSolverHelper>(m, "ModelSolverHelper")
.def(pybind11::init<const std::string&>())
py::class_<ModelSolverHelper>(m, "ModelSolverHelper")
.def(py::init<const std::string&>())
.def("solver_is_supported", &ModelSolverHelper::SolverIsSupported)
.def("solve", &ModelSolverHelper::Solve, arg("model"),
// The GIL is released during the solve to allow Python threads to do
// other things in parallel, e.g., log and interrupt.
pybind11::call_guard<pybind11::gil_scoped_release>())
py::call_guard<py::gil_scoped_release>())
.def("solve_serialized_request",
[](ModelSolverHelper* solver, const std::string& request_str) {
std::string result;
{
// The GIL is released during the solve to allow Python threads
// to do other things in parallel, e.g., log and interrupt.
pybind11::gil_scoped_release release;
py::gil_scoped_release release;
MPModelRequest request;
if (!request.ParseFromString(request_str)) {
@@ -242,7 +307,7 @@ PYBIND11_MODULE(pywrap_model_builder_helper, m) {
result = solution.value().SerializeAsString();
}
}
return pybind11::bytes(result);
return py::bytes(result);
})
.def("interrupt_solve", &ModelSolverHelper::InterruptSolve,
"Returns true if the interrupt signal was correctly sent, that is, "

View File

@@ -1,7 +0,0 @@
min: x + y;
bin: b1, b2, b3;
1 <= x <= 42;
constraint_num1: 5 b1 + 3b2 + x <= 7;
4 y + b2 - 3 b3 <= 2;
constraint_num2: -4 b1 + b2 - 3 z <= -2;

View File

@@ -0,0 +1,20 @@
# 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.
# Test files for the linear_solver packages.
exports_files([
"maximization.mps",
"small_model.lp",
"large_model.mps.gz",
])

Binary file not shown.

View File

@@ -16,4 +16,3 @@ COLUMNS
BOUNDS
UP BOUND X_ONE 4
ENDATA