math_opt: update

This commit is contained in:
Corentin Le Molgat
2022-06-22 17:49:58 +02:00
parent 5899cbe811
commit 0f16563548
10 changed files with 157 additions and 98 deletions

View File

@@ -16,6 +16,7 @@
#include <cstdint>
#include "absl/status/status.h"
#include "ortools/base/status_macros.h"
#include "ortools/math_opt/core/model_summary.h"
#include "ortools/math_opt/core/sparse_vector_view.h"
#include "ortools/math_opt/model.pb.h"
@@ -24,7 +25,6 @@
#include "ortools/math_opt/validators/scalar_validator.h"
#include "ortools/math_opt/validators/sparse_matrix_validator.h"
#include "ortools/math_opt/validators/sparse_vector_validator.h"
#include "ortools/base/status_macros.h"
namespace operations_research::math_opt {

View File

@@ -66,6 +66,7 @@ cc_library(
srcs = ["model.cc"],
hdrs = ["model.h"],
deps = [
":formatters",
":key_types",
":linear_constraint",
":update_tracker",
@@ -104,6 +105,7 @@ cc_library(
srcs = ["variable_and_expressions.cc"],
hdrs = ["variable_and_expressions.h"],
deps = [
":formatters",
":id_map",
":key_types",
"//ortools/base",
@@ -390,3 +392,8 @@ cc_library(
"//ortools/math_opt/storage:model_storage",
],
)
cc_library(
name = "formatters",
hdrs = ["formatters.h"],
)

View File

@@ -0,0 +1,88 @@
// 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.
#ifndef OR_TOOLS_MATH_OPT_CPP_FORMATTERS_H_
#define OR_TOOLS_MATH_OPT_CPP_FORMATTERS_H_
#include <cmath>
#include <iostream>
namespace operations_research::math_opt {
// Streaming formatter for a coefficient of a linear/quadratic term, along with
// any leading "+"/"-"'s to connect it with preceding terms in a sum, and
// potentially a "*" postfix. The `is_first` parameter specifies if the term is
// the first appearing in the sum, in which case the handling of the +/-
// connectors is different.
struct LeadingCoefficientFormatter {
LeadingCoefficientFormatter(const double coeff, const bool is_first)
: coeff(coeff), is_first(is_first) {}
const double coeff;
const bool is_first;
};
inline std::ostream& operator<<(std::ostream& out,
const LeadingCoefficientFormatter formatter) {
const double coeff = formatter.coeff;
if (formatter.is_first) {
if (coeff == 1.0) {
// Do nothing.
} else if (coeff == -1.0) {
out << "-";
} else {
out << coeff << "*";
}
} else {
if (coeff == 1.0) {
out << " + ";
} else if (coeff == -1.0) {
out << " - ";
} else if (std::isnan(coeff)) {
out << " + nan*";
} else if (coeff >= 0) {
out << " + " << coeff << "*";
} else {
out << " - " << -coeff << "*";
}
}
return out;
}
// Streaming formatter for a constant in a linear/quadratic expression.
struct ConstantFormatter {
ConstantFormatter(const double constant, const bool is_first)
: constant(constant), is_first(is_first) {}
const double constant;
const bool is_first;
};
inline std::ostream& operator<<(std::ostream& out,
const ConstantFormatter formatter) {
const double constant = formatter.constant;
if (formatter.is_first) {
out << constant;
} else if (constant == 0) {
// Do nothing.
} else if (std::isnan(constant)) {
out << " + nan";
} else if (constant > 0) {
out << " + " << constant;
} else {
out << " - " << -constant;
}
return out;
}
} // namespace operations_research::math_opt
#endif // OR_TOOLS_MATH_OPT_CPP_FORMATTERS_H_

View File

@@ -14,23 +14,28 @@
#include "ortools/math_opt/cpp/model.h"
#include <algorithm>
#include <limits>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "ortools/base/logging.h"
#include "ortools/base/status_macros.h"
#include "ortools/base/strong_int.h"
#include "ortools/math_opt/cpp/linear_constraint.h"
#include "ortools/math_opt/storage/model_storage.h"
namespace operations_research {
namespace math_opt {
constexpr double kInf = std::numeric_limits<double>::infinity();
absl::StatusOr<std::unique_ptr<Model>> Model::FromModelProto(
const ModelProto& model_proto) {
ASSIGN_OR_RETURN(std::unique_ptr<ModelStorage> storage,
@@ -220,5 +225,47 @@ absl::Status Model::ApplyUpdateProto(const ModelUpdateProto& update_proto) {
return storage()->ApplyUpdateProto(update_proto);
}
std::ostream& operator<<(std::ostream& ostr, const Model& model) {
ostr << "Model";
if (!model.name().empty()) ostr << " " << model.name();
ostr << ":\n";
ostr << " Objective:\n"
<< (model.is_maximize() ? " maximize " : " minimize ")
<< model.ObjectiveAsQuadraticExpression() << "\n";
ostr << " Linear constraints:\n";
for (const LinearConstraint constraint : model.SortedLinearConstraints()) {
ostr << " " << constraint << ": "
<< model.AsBoundedLinearExpression(constraint) << "\n";
}
ostr << " Variables:\n";
for (const Variable v : model.SortedVariables()) {
ostr << " " << v;
if (v.is_integer()) {
if (v.lower_bound() == 0 && v.upper_bound() == 1) {
ostr << " (binary)\n";
continue;
}
ostr << " (integer)";
}
ostr << " in ";
if (v.lower_bound() == -kInf) {
ostr << "(-∞";
} else {
ostr << "[" << v.lower_bound();
}
ostr << ", ";
if (v.upper_bound() == kInf) {
ostr << "+∞)";
} else {
ostr << v.upper_bound() << "]";
}
ostr << "\n";
}
return ostr;
}
} // namespace math_opt
} // namespace operations_research

View File

@@ -455,6 +455,11 @@ class Model {
// it.
ModelStorage* storage() { return storage_.get(); }
// Prints the objective, the constraints and the variables of the model over
// several lines in a human-readable way. Includes a new line at the end of
// the model.
friend std::ostream& operator<<(std::ostream& ostr, const Model& model);
private:
// Asserts (with CHECK) that the input pointer is either nullptr or that it
// points to the same model as storage_.

View File

@@ -22,6 +22,7 @@
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
#include "ortools/base/strong_int.h"
#include "ortools/math_opt/cpp/formatters.h" // IWYU pragma: export
namespace operations_research {
namespace math_opt {
@@ -92,73 +93,6 @@ double LinearExpression::EvaluateWithDefaultZero(
return result;
}
namespace {
// Streaming formatter for a coefficient of a linear/quadratic term, along with
// any leading "+"/"-"'s to connect it with preceding terms in a sum, and
// potentially a "*" postfix. The `is_first` parameter specifies if the term is
// the first appearing in the sum, in which case the handling of the +/-
// connectors is different.
struct LeadingCoefficientFormatter {
LeadingCoefficientFormatter(const double coeff, const bool is_first)
: coeff(coeff), is_first(is_first) {}
const double coeff;
const bool is_first;
};
std::ostream& operator<<(std::ostream& out,
const LeadingCoefficientFormatter formatter) {
const double coeff = formatter.coeff;
if (formatter.is_first) {
if (coeff == 1.0) {
// Do nothing.
} else if (coeff == -1.0) {
out << "-";
} else {
out << coeff << "*";
}
} else {
if (coeff == 1.0) {
out << " + ";
} else if (coeff == -1.0) {
out << " - ";
} else if (std::isnan(coeff)) {
out << " + nan*";
} else if (coeff >= 0) {
out << " + " << coeff << "*";
} else {
out << " - " << -coeff << "*";
}
}
return out;
}
// Streaming formatter for a constant in a linear/quadratic expression.
struct ConstantFormatter {
ConstantFormatter(const double constant, const bool is_first)
: constant(constant), is_first(is_first) {}
const double constant;
const bool is_first;
};
std::ostream& operator<<(std::ostream& out, const ConstantFormatter formatter) {
const double constant = formatter.constant;
if (formatter.is_first) {
out << constant;
} else if (constant == 0) {
// Do nothing.
} else if (std::isnan(constant)) {
out << " + nan";
} else if (constant > 0) {
out << " + " << constant;
} else {
out << " - " << -constant;
}
return out;
}
} // namespace
std::ostream& operator<<(std::ostream& ostr,
const LinearExpression& expression) {
// TODO(b/169415597): improve linear expression format:

View File

@@ -18,13 +18,13 @@
#include <utility>
#include <vector>
#include "ortools/base/logging.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "ortools/base/logging.h"
#include "ortools/base/status_macros.h"
#include "ortools/glpk/glpk_computational_form.h"
#include "ortools/glpk/glpk_formatters.h"
#include "ortools/base/status_macros.h"
namespace operations_research::math_opt {
namespace {

View File

@@ -17,11 +17,11 @@
#include <string_view>
#include <utility>
#include "ortools/base/logging.h"
#include "ortools/base/cleanup.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "ortools/base/cleanup.h"
#include "ortools/base/logging.h"
#include "ortools/base/source_location.h"
#include "ortools/base/status_builder.h"
#include "ortools/base/status_macros.h"

View File

@@ -40,9 +40,8 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "ortools/base/source_location.h"
#include "absl/types/span.h"
#include "ortools/base/source_location.h"
#include "ortools/gurobi/environment.h"
namespace operations_research::math_opt {

View File

@@ -197,28 +197,6 @@ absl::StatusOr<ModelUpdateProto> ReadModelUpdate(
absl::StrCat("invalid format in ReadModelUpdate(): ", format));
}
// Prints the model.
void PrintModel(const Model& model) {
if (model.is_maximize()) {
std::cout << "max";
} else {
std::cout << "min";
}
std::cout << ' ' << model.ObjectiveAsQuadraticExpression() << std::endl;
std::cout << "variables:" << std::endl;
for (const Variable v : model.SortedVariables()) {
std::cout << ' ' << v.lower_bound() << "" << v << ""
<< v.upper_bound() << std::endl;
}
std::cout << "constraints:" << std::endl;
for (const LinearConstraint c : model.SortedLinearConstraints()) {
std::cout << ' ' << c << ": " << model.AsBoundedLinearExpression(c)
<< std::endl;
}
}
// Prints the summary of the solve result.
absl::Status PrintSummary(const SolveResult& result) {
std::cout << "Solve finished:\n"
@@ -306,7 +284,8 @@ absl::Status RunSolver() {
// Optionally prints the problem.
if (absl::GetFlag(FLAGS_print_model)) {
PrintModel(*model);
std::cout << model;
std::cout.flush();
}
// Solve the problem.