diff --git a/ortools/algorithms/java/BUILD.bazel b/ortools/algorithms/java/BUILD.bazel index 4c7a714aff..daa689dc73 100644 --- a/ortools/algorithms/java/BUILD.bazel +++ b/ortools/algorithms/java/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Description: java wrapping of the C++ code at ../ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") @@ -5,8 +18,8 @@ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") ortools_java_wrap_cc( name = "knapsacksolver", src = "knapsack_solver.i", - package = "com.google.ortools.algorithms", module = "operations_research_algorithms", + package = "com.google.ortools.algorithms", swig_includes = [ "//ortools/base:base_swig", "//ortools/util/java:vector_swig", @@ -15,4 +28,4 @@ ortools_java_wrap_cc( deps = [ "//ortools/algorithms:knapsack_solver_lib", ], -) \ No newline at end of file +) diff --git a/ortools/algorithms/samples/code_samples.bzl b/ortools/algorithms/samples/code_samples.bzl index 1e84c78a25..f2bcb90162 100644 --- a/ortools/algorithms/samples/code_samples.bzl +++ b/ortools/algorithms/samples/code_samples.bzl @@ -44,9 +44,9 @@ def code_sample_java(name): ], deps = [ "//ortools/algorithms/java:knapsacksolver", - "//ortools/java/com/google/ortools:Loader" + "//ortools/java/com/google/ortools:Loader", ], - ) + ) native.sh_test( name = name + "_java_test", diff --git a/ortools/algorithms/samples/code_samples_java_test.sh b/ortools/algorithms/samples/code_samples_java_test.sh index e3f1ab60af..543ac9473d 100755 --- a/ortools/algorithms/samples/code_samples_java_test.sh +++ b/ortools/algorithms/samples/code_samples_java_test.sh @@ -12,11 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. - declare -r DIR="${TEST_SRCDIR}/com_google_ortools/ortools/algorithms/samples" function test::ortools::code_samples_algorithms_java() { "${DIR}/$1_java" } -test::ortools::code_samples_algorithms_java $1 \ No newline at end of file +test::ortools::code_samples_algorithms_java $1 diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index 491866728e..0f241dfb02 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -32,8 +32,6 @@ filegroup( name = "base_swig", srcs = [ "base.i", -# "//base:swig_includes", -# "//third_party/absl/base:swig_includes", ], visibility = ["//visibility:public"], ) diff --git a/ortools/glop/BUILD.bazel b/ortools/glop/BUILD.bazel index 564c866d7f..9a63da0fb0 100644 --- a/ortools/glop/BUILD.bazel +++ b/ortools/glop/BUILD.bazel @@ -44,7 +44,7 @@ config_setting( SAFE_FP_CODE = select({ "on_linux": ["-fno-fast-math"], "on_macos": [], # no_fast_math is the default. - "on_windows": ["/fp:precise"], + "on_windows": [], # /fp:precise is the default. "//conditions:default": [], }) diff --git a/ortools/graph/java/BUILD.bazel b/ortools/graph/java/BUILD.bazel index 0458320ab6..a304186c01 100644 --- a/ortools/graph/java/BUILD.bazel +++ b/ortools/graph/java/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Description: java wrapping of the C++ code at ../ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") @@ -5,15 +18,15 @@ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") ortools_java_wrap_cc( name = "graph", src = "graph.i", - package = "com.google.ortools.graph", module = "graph", + package = "com.google.ortools.graph", swig_includes = [ "//ortools/base:base_swig", ], visibility = ["//visibility:public"], deps = [ "//ortools/graph:assignment", - "//ortools/graph:min_cost_flow", "//ortools/graph:max_flow", + "//ortools/graph:min_cost_flow", ], -) \ No newline at end of file +) diff --git a/ortools/graph/samples/code_samples.bzl b/ortools/graph/samples/code_samples.bzl index 85c9d67294..55e6382821 100644 --- a/ortools/graph/samples/code_samples.bzl +++ b/ortools/graph/samples/code_samples.bzl @@ -98,7 +98,7 @@ def code_sample_java(name): ], deps = [ "//ortools/graph/java:graph", - "//ortools/java/com/google/ortools:Loader" + "//ortools/java/com/google/ortools:Loader", ], ) diff --git a/ortools/graph/samples/code_samples_java_test.sh b/ortools/graph/samples/code_samples_java_test.sh index aa2c9620c7..65cdf78f10 100755 --- a/ortools/graph/samples/code_samples_java_test.sh +++ b/ortools/graph/samples/code_samples_java_test.sh @@ -12,11 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. - declare -r DIR="${TEST_SRCDIR}/com_google_ortools/ortools/graph/samples" function test::ortools::code_samples_graph_java() { "${DIR}/$1_java" } -test::ortools::code_samples_graph_java $1 \ No newline at end of file +test::ortools::code_samples_graph_java $1 diff --git a/ortools/init/java/BUILD.bazel b/ortools/init/java/BUILD.bazel index 33964145bc..33a4dcfb75 100644 --- a/ortools/init/java/BUILD.bazel +++ b/ortools/init/java/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Description: java wrapping of the C++ code at ../ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") @@ -5,8 +18,8 @@ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") ortools_java_wrap_cc( name = "init", src = "init.i", - package = "com.google.ortools.init", module = "operations_research_init", + package = "com.google.ortools.init", swig_includes = [ "//ortools/base:base_swig", ], @@ -14,4 +27,4 @@ ortools_java_wrap_cc( deps = [ "//ortools/init", ], -) \ No newline at end of file +) diff --git a/ortools/java/com/google/ortools/BUILD.bazel b/ortools/java/com/google/ortools/BUILD.bazel index bfaf595346..6f78ead4e0 100644 --- a/ortools/java/com/google/ortools/BUILD.bazel +++ b/ortools/java/com/google/ortools/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Utilities to load native libraries in java or-tools. java_library( name = "Loader", @@ -10,42 +23,42 @@ java_library( cc_binary( name = "libjniortools.so", + linkshared = True, + visibility = ["//visibility:public"], deps = [ "//ortools/algorithms/java:knapsacksolver_cc", "//ortools/graph/java:graph_cc", - "//ortools/linear_solver/java:modelbuilder_cc", "//ortools/init/java:init_cc", + "//ortools/linear_solver/java:modelbuilder_cc", "//ortools/sat/java:sat_cc", "//ortools/util/java:sorted_interval_list_cc", ], - linkshared = True, - visibility = ["//visibility:public"], ) cc_binary( name = "libjniortools.dylib", + linkshared = True, + visibility = ["//visibility:public"], deps = [ "//ortools/algorithms/java:knapsacksolver_cc", "//ortools/graph/java:graph_cc", - "//ortools/linear_solver/java:modelbuilder_cc", "//ortools/init/java:init_cc", + "//ortools/linear_solver/java:modelbuilder_cc", "//ortools/sat/java:sat_cc", "//ortools/util/java:sorted_interval_list_cc", ], - linkshared = True, - visibility = ["//visibility:public"], ) cc_binary( name = "jniortools.dll", + linkshared = True, + visibility = ["//visibility:public"], deps = [ "//ortools/algorithms/java:knapsacksolver_cc", "//ortools/graph/java:graph_cc", - "//ortools/linear_solver/java:modelbuilder_cc", "//ortools/init/java:init_cc", + "//ortools/linear_solver/java:modelbuilder_cc", "//ortools/sat/java:sat_cc", "//ortools/util/java:sorted_interval_list_cc", ], - linkshared = True, - visibility = ["//visibility:public"], ) diff --git a/ortools/java/com/google/ortools/modelbuilder/BUILD.bazel b/ortools/java/com/google/ortools/modelbuilder/BUILD.bazel index 1c9c950215..8bf9945a73 100644 --- a/ortools/java/com/google/ortools/modelbuilder/BUILD.bazel +++ b/ortools/java/com/google/ortools/modelbuilder/BUILD.bazel @@ -30,9 +30,9 @@ java_library( ], visibility = ["//visibility:public"], deps = [ - "//ortools/java/com/google/ortools:libjniortools.so", - "//ortools/java/com/google/ortools:libjniortools.dylib", - "//ortools/java/com/google/ortools:jniortools.dll", + "//ortools/java/com/google/ortools:jniortools.dll", + "//ortools/java/com/google/ortools:libjniortools.dylib", + "//ortools/java/com/google/ortools:libjniortools.so", "//ortools/linear_solver/java:modelbuilder", ], -) \ No newline at end of file +) diff --git a/ortools/java/com/google/ortools/sat/BUILD.bazel b/ortools/java/com/google/ortools/sat/BUILD.bazel index 8e0c0c9668..0852595113 100644 --- a/ortools/java/com/google/ortools/sat/BUILD.bazel +++ b/ortools/java/com/google/ortools/sat/BUILD.bazel @@ -43,12 +43,12 @@ java_library( ], visibility = ["//visibility:public"], deps = [ - "//ortools/java/com/google/ortools:libjniortools.so", - "//ortools/java/com/google/ortools:libjniortools.dylib", - "//ortools/java/com/google/ortools:jniortools.dll", - "//ortools/sat/java:sat", + "//ortools/java/com/google/ortools:jniortools.dll", + "//ortools/java/com/google/ortools:libjniortools.dylib", + "//ortools/java/com/google/ortools:libjniortools.so", "//ortools/sat:cp_model_java_proto", "//ortools/sat:sat_parameters_java_proto", + "//ortools/sat/java:sat", "//ortools/util/java:sorted_interval_list", ], -) \ No newline at end of file +) diff --git a/ortools/linear_solver/BUILD.bazel b/ortools/linear_solver/BUILD.bazel index 786c3a115b..3387fc8bef 100644 --- a/ortools/linear_solver/BUILD.bazel +++ b/ortools/linear_solver/BUILD.bazel @@ -222,4 +222,3 @@ cc_library( "@com_google_absl//absl/strings:str_format", ], ) - diff --git a/ortools/linear_solver/csharp/linear_solver.i b/ortools/linear_solver/csharp/linear_solver.i index ccf713c197..346b454f8d 100644 --- a/ortools/linear_solver/csharp/linear_solver.i +++ b/ortools/linear_solver/csharp/linear_solver.i @@ -82,6 +82,7 @@ CONVERT_VECTOR(operations_research::MPVariable, MPVariable) %rename (Constraint) operations_research::MPConstraint; %rename (Objective) operations_research::MPObjective; %rename (SolverParameters) operations_research::MPSolverParameters; +%unignore operations_research::MPSolver::SolverVersion; // Expose the MPSolver::OptimizationProblemType enum. %unignore operations_research::MPSolver::OptimizationProblemType; diff --git a/ortools/linear_solver/gurobi_interface.cc b/ortools/linear_solver/gurobi_interface.cc index b8ffc49183..24558ccae2 100644 --- a/ortools/linear_solver/gurobi_interface.cc +++ b/ortools/linear_solver/gurobi_interface.cc @@ -1079,13 +1079,13 @@ void GurobiInterface::SetDualTolerance(double value) { void GurobiInterface::SetPresolveMode(int value) { switch (value) { case MPSolverParameters::PRESOLVE_OFF: { - CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, - GRB_PRESOLVE_OFF)); + CheckedGurobiCall( + GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, false)); break; } case MPSolverParameters::PRESOLVE_ON: { - CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, - GRB_PRESOLVE_AUTO)); + CheckedGurobiCall( + GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, true)); break; } default: { diff --git a/ortools/linear_solver/java/BUILD.bazel b/ortools/linear_solver/java/BUILD.bazel index 92bb7aa5e8..3f042ddee2 100644 --- a/ortools/linear_solver/java/BUILD.bazel +++ b/ortools/linear_solver/java/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Description: java wrapping of the C++ code at ../ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") @@ -5,15 +18,15 @@ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") ortools_java_wrap_cc( name = "modelbuilder", src = "modelbuilder.i", - package = "com.google.ortools.modelbuilder", module = "modelbuilder", - swig_opt = "-DUSE_LP_PARSER", + package = "com.google.ortools.modelbuilder", swig_includes = [ "//ortools/base:base_swig", "//ortools/util/java:vector_swig", ], + swig_opt = "-DUSE_LP_PARSER", visibility = ["//visibility:public"], deps = [ "//ortools/linear_solver/wrappers:model_builder_helper", ], -) \ No newline at end of file +) diff --git a/ortools/linear_solver/java/linear_solver.i b/ortools/linear_solver/java/linear_solver.i index 71c083e62f..fd82f060d1 100644 --- a/ortools/linear_solver/java/linear_solver.i +++ b/ortools/linear_solver/java/linear_solver.i @@ -291,6 +291,7 @@ PROTO2_RETURN( %unignore operations_research::MPSolver::~MPSolver; %newobject operations_research::MPSolver::CreateSolver; %rename (createSolver) operations_research::MPSolver::CreateSolver; +%rename (solverVersion) operations_research::MPSolver::SolverVersion; %unignore operations_research::MPConstraint; %unignore operations_research::MPVariable; diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 050737f0ee..993183603b 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -853,20 +853,20 @@ void MPSolver::FillSolutionResponseProto(MPSolutionResponse* response) const { if (interface_->result_status_ == MPSolver::OPTIMAL || interface_->result_status_ == MPSolver::FEASIBLE) { response->set_objective_value(Objective().Value()); - for (int i = 0; i < variables_.size(); ++i) { - response->add_variable_value(variables_[i]->solution_value()); + for (MPVariable* variable : variables_) { + response->add_variable_value(variable->solution_value()); } if (interface_->IsMIP()) { response->set_best_objective_bound(interface_->best_objective_bound()); } else { // Dual values have no meaning in MIP. - for (int j = 0; j < constraints_.size(); ++j) { - response->add_dual_value(constraints_[j]->dual_value()); + for (MPConstraint* constraint : constraints_) { + response->add_dual_value(constraint->dual_value()); } // Reduced cost have no meaning in MIP. - for (int i = 0; i < variables_.size(); ++i) { - response->add_reduced_cost(variables_[i]->reduced_cost()); + for (MPVariable* variable : variables_) { + response->add_reduced_cost(variable->reduced_cost()); } } } @@ -1076,8 +1076,7 @@ void MPSolver::ExportModelToProto(MPModelProto* output_model) const { // Name output_model->set_name(Name()); // Variables - for (int j = 0; j < variables_.size(); ++j) { - const MPVariable* const var = variables_[j]; + for (const MPVariable* var : variables_) { MPVariableProto* const variable_proto = output_model->add_variable(); // TODO(user): Add option to avoid filling the var name to avoid overly // large protocol buffers. @@ -1101,13 +1100,12 @@ void MPSolver::ExportModelToProto(MPModelProto* output_model) const { // underlying solver at the time of model extraction. // TODO(user): remove this step. absl::flat_hash_map var_to_index; - for (int j = 0; j < variables_.size(); ++j) { + for (int j = 0; j < static_cast(variables_.size()); ++j) { var_to_index[variables_[j]] = j; } // Constraints - for (int i = 0; i < constraints_.size(); ++i) { - MPConstraint* const constraint = constraints_[i]; + for (MPConstraint* const constraint : constraints_) { MPConstraintProto* constraint_proto; if (constraint->indicator_variable() != nullptr) { MPGeneralConstraintProto* const general_constraint_proto = @@ -1172,7 +1170,8 @@ absl::Status MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response, // Before touching the variables, verify that the solution looks legit: // each variable of the MPSolver must have its value listed exactly once, and // each listed solution should correspond to a known variable. - if (response.variable_value_size() != variables_.size()) { + if (static_cast(response.variable_value_size()) != + variables_.size()) { return absl::InvalidArgumentError(absl::StrCat( "Trying to load a solution whose number of variables (", response.variable_value_size(), @@ -1212,7 +1211,8 @@ absl::Status MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response, variables_[i]->set_solution_value(response.variable_value(i)); } if (response.dual_value_size() > 0) { - if (response.dual_value_size() != constraints_.size()) { + if (static_cast(response.dual_value_size()) != + constraints_.size()) { return absl::InvalidArgumentError(absl::StrCat( "Trying to load a dual solution whose number of entries (", response.dual_value_size(), ") does not correspond to the Solver's (", @@ -1223,7 +1223,8 @@ absl::Status MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response, } } if (response.reduced_cost_size() > 0) { - if (response.reduced_cost_size() != variables_.size()) { + if (static_cast(response.reduced_cost_size()) != + variables_.size()) { return absl::InvalidArgumentError(absl::StrCat( "Trying to load a reduced cost solution whose number of entries (", response.reduced_cost_size(), @@ -1389,7 +1390,7 @@ int MPSolver::ComputeMaxConstraintSize(int min_constraint_index, DCHECK_LE(max_constraint_index, constraints_.size()); for (int i = min_constraint_index; i < max_constraint_index; ++i) { MPConstraint* const ct = constraints_[i]; - if (ct->coefficients_.size() > max_constraint_size) { + if (static_cast(ct->coefficients_.size()) > max_constraint_size) { max_constraint_size = ct->coefficients_.size(); } } @@ -1398,7 +1399,7 @@ int MPSolver::ComputeMaxConstraintSize(int min_constraint_index, bool MPSolver::HasInfeasibleConstraints() const { bool hasInfeasibleConstraints = false; - for (int i = 0; i < constraints_.size(); ++i) { + for (int i = 0; i < static_cast(constraints_.size()); ++i) { if (constraints_[i]->lb() > constraints_[i]->ub()) { LOG(WARNING) << "Constraint " << constraints_[i]->name() << " (" << i << ") has contradictory bounds:" @@ -1536,7 +1537,7 @@ std::vector MPSolver::ComputeConstraintActivities() const { // TODO(user): test this failure case. if (!interface_->CheckSolutionIsSynchronizedAndExists()) return {}; std::vector activities(constraints_.size(), 0.0); - for (int i = 0; i < constraints_.size(); ++i) { + for (int i = 0; i < static_cast(constraints_.size()); ++i) { const MPConstraint& constraint = *constraints_[i]; AccurateSum sum; for (const auto& entry : constraint.coefficients_) { @@ -1554,8 +1555,8 @@ bool MPSolver::VerifySolution(double tolerance, bool log_errors) const { int num_errors = 0; // Verify variables. - for (int i = 0; i < variables_.size(); ++i) { - const MPVariable& var = *variables_[i]; + for (MPVariable* variable : variables_) { + const MPVariable& var = *variable; const double value = var.solution_value(); // Check for NaN. if (std::isnan(value)) { @@ -1602,7 +1603,7 @@ bool MPSolver::VerifySolution(double tolerance, bool log_errors) const { // Verify constraints. const std::vector activities = ComputeConstraintActivities(); - for (int i = 0; i < constraints_.size(); ++i) { + for (int i = 0; i < static_cast(constraints_.size()); ++i) { const MPConstraint& constraint = *constraints_[i]; const double activity = activities[i]; // Re-compute the activity with a inaccurate summing algorithm. @@ -1708,7 +1709,7 @@ double MPSolver::ComputeExactConditionNumber() const { bool MPSolver::OwnsVariable(const MPVariable* var) const { if (var == nullptr) return false; - if (var->index() >= 0 && var->index() < variables_.size()) { + if (var->index() >= 0 && var->index() < static_cast(variables_.size())) { // Then, verify that the variable with this index has the same address. return variables_[var->index()] == var; } diff --git a/ortools/linear_solver/model_exporter.cc b/ortools/linear_solver/model_exporter.cc index 09de29a3a4..7630c56515 100644 --- a/ortools/linear_solver/model_exporter.cc +++ b/ortools/linear_solver/model_exporter.cc @@ -56,7 +56,7 @@ class LineBreaker { // Returns true if string s will fit on the current line without adding a // carriage return. bool WillFit(const std::string& s) { - return line_size_ + s.size() < max_line_size_; + return line_size_ + static_cast(s.size()) < max_line_size_; } // "Consumes" size characters on the line. Used when starting the constraint @@ -450,7 +450,8 @@ bool IsBoolean(const MPVariableProto& var) { } void UpdateMaxSize(const std::string& new_string, int* size) { - if (new_string.size() > *size) *size = new_string.size(); + const int new_size = new_string.size(); + if (new_size > *size) *size = new_size; } void UpdateMaxSize(double new_number, int* size) { diff --git a/ortools/linear_solver/samples/BUILD.bazel b/ortools/linear_solver/samples/BUILD.bazel index 00b46428c6..0221c51fa8 100644 --- a/ortools/linear_solver/samples/BUILD.bazel +++ b/ortools/linear_solver/samples/BUILD.bazel @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -load(":code_samples.bzl", "code_sample_cc", "code_sample_py", "code_sample_java") +load(":code_samples.bzl", "code_sample_cc", "code_sample_java", "code_sample_py") # Linear Solver code_sample_cc(name = "assignment_mip") diff --git a/ortools/linear_solver/samples/code_samples.bzl b/ortools/linear_solver/samples/code_samples.bzl index af09017983..4589dd061d 100644 --- a/ortools/linear_solver/samples/code_samples.bzl +++ b/ortools/linear_solver/samples/code_samples.bzl @@ -81,7 +81,7 @@ def code_sample_java(name): deps = [ "//ortools/linear_solver/java:modelbuilder", "//ortools/java/com/google/ortools/modelbuilder", - "//ortools/java/com/google/ortools:Loader" + "//ortools/java/com/google/ortools:Loader", ], ) diff --git a/ortools/linear_solver/samples/code_samples_java_test.sh b/ortools/linear_solver/samples/code_samples_java_test.sh index 9724a1b7ad..271ba374bf 100755 --- a/ortools/linear_solver/samples/code_samples_java_test.sh +++ b/ortools/linear_solver/samples/code_samples_java_test.sh @@ -12,11 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. - declare -r DIR="${TEST_SRCDIR}/com_google_ortools/ortools/linear_solver/samples" function test::ortools::code_samples_linear_solver_java() { "${DIR}/$1_java" } -test::ortools::code_samples_linear_solver_java $1 \ No newline at end of file +test::ortools::code_samples_linear_solver_java $1 diff --git a/ortools/lp_data/BUILD.bazel b/ortools/lp_data/BUILD.bazel index e0af01e8a9..06b85f8091 100644 --- a/ortools/lp_data/BUILD.bazel +++ b/ortools/lp_data/BUILD.bazel @@ -40,7 +40,7 @@ config_setting( SAFE_FP_CODE = select({ "on_linux": ["-fno-fast-math"], "on_macos": [], # no_fast_math is the default. - "on_windows": ["/fp:precise"], + "on_windows": [], # /fp:precise is the default. "//conditions:default": [], }) diff --git a/ortools/sat/cp_model.cc b/ortools/sat/cp_model.cc index 3e1f1d281c..de16c62d61 100644 --- a/ortools/sat/cp_model.cc +++ b/ortools/sat/cp_model.cc @@ -32,8 +32,6 @@ namespace operations_research { namespace sat { -BoolVar::BoolVar() : builder_(nullptr), index_(0) {} - BoolVar::BoolVar(int index, CpModelBuilder* builder) : builder_(builder), index_(index) {} @@ -92,8 +90,6 @@ std::ostream& operator<<(std::ostream& os, const BoolVar& var) { return os; } -IntVar::IntVar() : builder_(nullptr), index_(0) {} - IntVar::IntVar(int index, CpModelBuilder* builder) : builder_(builder), index_(index) { DCHECK(RefIsPositive(index_)); diff --git a/ortools/sat/cp_model.h b/ortools/sat/cp_model.h index 99673d3b00..d19e58f6aa 100644 --- a/ortools/sat/cp_model.h +++ b/ortools/sat/cp_model.h @@ -77,7 +77,7 @@ class BoolVar { /// However, it shouldn't be passed to any of the functions in this file. /// Doing so will crash in debug mode and will result in an invalid model in /// opt mode. - BoolVar(); + BoolVar() = default; /// Sets the name of the variable. /// Note that this will always set the "positive" version of this Boolean. @@ -145,7 +145,7 @@ class IntVar { /// However, it shouldn't be passed to any of the functions in this file. /// Doing so will crash in debug mode and will result in an invalid model in /// opt mode. - IntVar(); + IntVar() = default; /// Cast BoolVar -> IntVar. /// The IntVar will take the value 1 (when the bool is true) and 0 otherwise. diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index d26f601d63..efd8af3667 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -585,19 +585,18 @@ bool CpModelPresolver::PresolveAtMostOrExactlyOne(ConstraintProto* ct) { } const int literal = singleton_literal_with_cost[0].first; - const int64_t min_obj = singleton_literal_with_cost[0].second; - if (is_at_most_one && min_obj >= 0) { + const int64_t literal_cost = singleton_literal_with_cost[0].second; + if (is_at_most_one && literal_cost >= 0) { // We can just always set it to false in this case. context_->UpdateRuleStats("at_most_one: singleton"); if (!context_->SetLiteralToFalse(literal)) return false; - } else { + } else if (context_->ShiftCostInExactlyOne(*literals, literal_cost)) { // We can make the constraint an exactly one if needed since it is always // beneficial to set this literal to true if everything else is zero. Now // that we have an exactly one, we can transfer the cost to the other // terms. The objective of literal should become zero, and we can then // decide its value at postsolve and just have an at most one on the other // literals. - context_->ShiftCostInExactlyOne(*literals, min_obj); DCHECK(!context_->ObjectiveMap().contains(PositiveRef(literal))); if (!is_at_most_one) transform_to_at_most_one = true; @@ -893,88 +892,95 @@ bool CpModelPresolver::PresolveLinMax(ConstraintProto* ct) { return changed; } - // If everything is Boolean and affine, do not use a lin max! - if (context_->ExpressionIsAffineBoolean(target)) { - const int target_ref = context_->LiteralForExpressionMax(target); + changed |= PresolveLinMaxWhenAllBoolean(ct); + return changed; +} - bool abort = false; +// If everything is Boolean and affine, do not use a lin max! +bool CpModelPresolver::PresolveLinMaxWhenAllBoolean(ConstraintProto* ct) { + if (context_->ModelIsUnsat()) return false; + if (HasEnforcementLiteral(*ct)) return false; - bool min_is_reachable = false; - std::vector min_literals; - std::vector literals_above_min; - std::vector max_literals; + const LinearExpressionProto& target = ct->lin_max().target(); + if (!context_->ExpressionIsAffineBoolean(target)) return false; - for (const LinearExpressionProto& expr : ct->lin_max().exprs()) { - const int64_t value_min = context_->MinOf(expr); - const int64_t value_max = context_->MaxOf(expr); + const int64_t target_min = context_->MinOf(target); + const int64_t target_max = context_->MaxOf(target); + const int target_ref = context_->LiteralForExpressionMax(target); - // This shouldn't happen, but it document the fact. - if (value_min > target_min) { - context_->UpdateRuleStats("lin_max: fix target"); - if (!context_->SetLiteralToTrue(target_ref)) return true; - abort = true; - break; - } + bool min_is_reachable = false; + std::vector min_literals; + std::vector literals_above_min; + std::vector max_literals; - // expr is fixed. - if (value_min == value_max) { - if (value_min == target_min) min_is_reachable = true; - continue; - } + for (const LinearExpressionProto& expr : ct->lin_max().exprs()) { + if (!context_->ExpressionIsAffineBoolean(expr)) return false; + const int64_t value_min = context_->MinOf(expr); + const int64_t value_max = context_->MaxOf(expr); + const int ref = context_->LiteralForExpressionMax(expr); - if (!context_->ExpressionIsAffineBoolean(expr)) { - abort = true; - break; - } - - const int ref = context_->LiteralForExpressionMax(expr); - CHECK_LE(value_min, target_min); - if (value_min == target_min) { - min_literals.push_back(NegatedRef(ref)); - } - - CHECK_LE(value_max, target_max); - if (value_max == target_max) { - max_literals.push_back(ref); - literals_above_min.push_back(ref); - } else if (value_max > target_min) { - literals_above_min.push_back(ref); - } else if (value_max == target_min) { - min_literals.push_back(ref); - } + // Get corner case out of the way, and wait for the constraint to be + // processed again in these case. + if (value_min > target_min) { + context_->UpdateRuleStats("lin_max: fix target"); + (void)context_->SetLiteralToTrue(target_ref); + return false; + } + if (value_max > target_max) { + context_->UpdateRuleStats("lin_max: fix bool expr"); + (void)context_->SetLiteralToFalse(ref); + return false; } - if (!abort) { - context_->UpdateRuleStats("lin_max: all Booleans."); - // target_ref => at_least_one(max_literals); - ConstraintProto* clause = context_->working_model->add_constraints(); - clause->add_enforcement_literal(target_ref); - clause->mutable_bool_or(); - for (const int lit : max_literals) { - clause->mutable_bool_or()->add_literals(lit); - } + // expr is fixed. + if (value_min == value_max) { + if (value_min == target_min) min_is_reachable = true; + continue; + } - // not(target_ref) => not(lit) for lit in literals_above_min - for (const int lit : literals_above_min) { - context_->AddImplication(lit, target_ref); - } + CHECK_LE(value_min, target_min); + if (value_min == target_min) { + min_literals.push_back(NegatedRef(ref)); + } - if (!min_is_reachable) { - // not(target_ref) => at_least_one(min_literals). - ConstraintProto* clause = context_->working_model->add_constraints(); - clause->add_enforcement_literal(NegatedRef(target_ref)); - clause->mutable_bool_or(); - for (const int lit : min_literals) { - clause->mutable_bool_or()->add_literals(lit); - } - } - - context_->UpdateNewConstraintsVariableUsage(); - return RemoveConstraint(ct); + CHECK_LE(value_max, target_max); + if (value_max == target_max) { + max_literals.push_back(ref); + literals_above_min.push_back(ref); + } else if (value_max > target_min) { + literals_above_min.push_back(ref); + } else if (value_max == target_min) { + min_literals.push_back(ref); } } - return changed; + context_->UpdateRuleStats("lin_max: all Booleans."); + + // target_ref => at_least_one(max_literals); + ConstraintProto* clause = context_->working_model->add_constraints(); + clause->add_enforcement_literal(target_ref); + clause->mutable_bool_or(); + for (const int lit : max_literals) { + clause->mutable_bool_or()->add_literals(lit); + } + + // not(target_ref) => not(lit) for lit in literals_above_min + for (const int lit : literals_above_min) { + context_->AddImplication(lit, target_ref); + } + + if (!min_is_reachable) { + // not(target_ref) => at_least_one(min_literals). + ConstraintProto* clause = context_->working_model->add_constraints(); + clause->add_enforcement_literal(NegatedRef(target_ref)); + clause->mutable_bool_or(); + for (const int lit : min_literals) { + clause->mutable_bool_or()->add_literals(lit); + } + } + + context_->UpdateNewConstraintsVariableUsage(); + return RemoveConstraint(ct); } // This presolve expect that the constraint only contains affine expressions. @@ -8157,6 +8163,7 @@ void CpModelPresolver::DetectDominatedLinearConstraints() { subset_ct_domain.MultiplicationBy(-factor)), mutable_linear); PropagateDomainsInLinear(/*ct_index=*/-1, &temp_ct_); + if (context_->ModelIsUnsat()) detector.Stop(); } if (superset_ct_domain.IsFixed()) { if (subset_lin.vars().size() + 1 == superset_lin.vars().size()) { @@ -9003,32 +9010,33 @@ void CpModelPresolver::ProcessVariableInTwoAtMostOrExactlyOne(int var) { // If the cost is non-zero, we can use an exactly one to make it zero. // Use that exactly one in the postsolve to recover the value of var. - auto* postsolve_literals = context_->mapping_model->add_constraints() - ->mutable_exactly_one() - ->mutable_literals(); + int64_t cost_shift = 0; + absl::Span literals; if (ct1.constraint_case() == ConstraintProto::kExactlyOne) { - context_->ShiftCostInExactlyOne(ct1.exactly_one().literals(), - RefIsPositive(c1_ref) ? cost : -cost); - *postsolve_literals = ct1.exactly_one().literals(); + cost_shift = RefIsPositive(c1_ref) ? cost : -cost; + literals = ct1.exactly_one().literals(); } else if (ct2.constraint_case() == ConstraintProto::kExactlyOne) { - context_->ShiftCostInExactlyOne(ct2.exactly_one().literals(), - RefIsPositive(c2_ref) ? cost : -cost); - *postsolve_literals = ct2.exactly_one().literals(); + cost_shift = RefIsPositive(c2_ref) ? cost : -cost; + literals = ct2.exactly_one().literals(); } else { // Dual argument. The one with a negative cost can be transformed to // an exactly one. if (context_->keep_all_feasible_solutions) return; if (RefIsPositive(c1_ref) == (cost < 0)) { - context_->ShiftCostInExactlyOne(ct1.at_most_one().literals(), - RefIsPositive(c1_ref) ? cost : -cost); - *postsolve_literals = ct1.at_most_one().literals(); + cost_shift = RefIsPositive(c1_ref) ? cost : -cost; + literals = ct1.at_most_one().literals(); } else { - context_->ShiftCostInExactlyOne(ct2.at_most_one().literals(), - RefIsPositive(c2_ref) ? cost : -cost); - *postsolve_literals = ct2.at_most_one().literals(); + cost_shift = RefIsPositive(c2_ref) ? cost : -cost; + literals = ct2.at_most_one().literals(); } } + + if (!context_->ShiftCostInExactlyOne(literals, cost_shift)) return; DCHECK(!context_->ObjectiveMap().contains(var)); + context_->mapping_model->add_constraints() + ->mutable_exactly_one() + ->mutable_literals() + ->Assign(literals.begin(), literals.end()); // We can now replace the two constraint by a single one, and delete var! const int new_ct_index = context_->working_model->constraints().size(); diff --git a/ortools/sat/cp_model_presolve.h b/ortools/sat/cp_model_presolve.h index fb470c80a2..1404d7c56a 100644 --- a/ortools/sat/cp_model_presolve.h +++ b/ortools/sat/cp_model_presolve.h @@ -116,6 +116,7 @@ class CpModelPresolver { bool PresolveInterval(int c, ConstraintProto* ct); bool PresolveInverse(ConstraintProto* ct); bool PresolveLinMax(ConstraintProto* ct); + bool PresolveLinMaxWhenAllBoolean(ConstraintProto* ct); bool PresolveTable(ConstraintProto* ct); bool PresolveCumulative(ConstraintProto* ct); diff --git a/ortools/sat/cp_model_utils.cc b/ortools/sat/cp_model_utils.cc index 8d0ed508b7..16805bad1b 100644 --- a/ortools/sat/cp_model_utils.cc +++ b/ortools/sat/cp_model_utils.cc @@ -20,9 +20,8 @@ #include #include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" #include "absl/strings/match.h" -#include "ortools/base/file.h" +#include "ortools/base/helpers.h" #include "ortools/base/logging.h" #include "ortools/base/stl_util.h" #include "ortools/sat/cp_model.pb.h" diff --git a/ortools/sat/cp_model_utils.h b/ortools/sat/cp_model_utils.h index d9c2e2745e..2670c2a00b 100644 --- a/ortools/sat/cp_model_utils.h +++ b/ortools/sat/cp_model_utils.h @@ -216,7 +216,8 @@ uint64_t FingerprintExpression(const LinearExpressionProto& lin, uint64_t seed); uint64_t FingerprintModel(const CpModelProto& model, uint64_t seed = kDefaultFingerprintSeed); -bool WriteModelProtoToFile(const CpModelProto& model, const std::string& filename); +bool WriteModelProtoToFile(const CpModelProto& model, + const std::string& filename); } // namespace sat } // namespace operations_research diff --git a/ortools/sat/java/BUILD.bazel b/ortools/sat/java/BUILD.bazel index a87d856890..ee0ea224c9 100644 --- a/ortools/sat/java/BUILD.bazel +++ b/ortools/sat/java/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Description: java wrapping of the code in ../ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") @@ -5,24 +18,24 @@ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") ortools_java_wrap_cc( name = "sat", src = "sat.i", - package = "com.google.ortools.sat", - use_directors = True, - swig_includes = [ - "//ortools/base:base_swig", - "//ortools/util/java:sorted_interval_list_swig", - "//ortools/util/java:proto_swig", - "//ortools/util/java:vector_swig", - ], - visibility = ["//visibility:public"], - deps = [ - "//ortools/base:jniutil", - "//ortools/sat:swig_helper", - "//ortools/util:sorted_interval_list", - ], java_deps = [ "//ortools/sat:cp_model_java_proto", "//ortools/sat:sat_parameters_java_proto", "//ortools/util/java:sorted_interval_list", "@com_google_protobuf//java/core", ], -) \ No newline at end of file + package = "com.google.ortools.sat", + swig_includes = [ + "//ortools/base:base_swig", + "//ortools/util/java:proto_swig", + "//ortools/util/java:sorted_interval_list_swig", + "//ortools/util/java:vector_swig", + ], + use_directors = True, + visibility = ["//visibility:public"], + deps = [ + "//ortools/base:jniutil", + "//ortools/sat:swig_helper", + "//ortools/util:sorted_interval_list", + ], +) diff --git a/ortools/sat/lp_utils.cc b/ortools/sat/lp_utils.cc index 84c9e0a3e0..a214e4f14b 100644 --- a/ortools/sat/lp_utils.cc +++ b/ortools/sat/lp_utils.cc @@ -238,7 +238,43 @@ bool MakeBoundsOfIntegerVariablesInteger(const SatParameters& params, void RemoveNearZeroTerms(const SatParameters& params, MPModelProto* mp_model, SolverLogger* logger) { + // Having really low bounds or rhs can be problematic. We set them to zero. + int num_dropped = 0; + double max_dropped = 0.0; + const double drop = params.mip_drop_tolerance(); const int num_variables = mp_model->variable_size(); + for (int i = 0; i < num_variables; ++i) { + MPVariableProto* var = mp_model->mutable_variable(i); + if (var->lower_bound() != 0.0 && std::abs(var->lower_bound()) < drop) { + ++num_dropped; + max_dropped = std::max(max_dropped, std::abs(var->lower_bound())); + var->set_lower_bound(0.0); + } + if (var->upper_bound() != 0.0 && std::abs(var->upper_bound()) < drop) { + ++num_dropped; + max_dropped = std::max(max_dropped, std::abs(var->upper_bound())); + var->set_upper_bound(0.0); + } + } + const int num_constraints = mp_model->constraint_size(); + for (int i = 0; i < num_constraints; ++i) { + MPConstraintProto* ct = mp_model->mutable_constraint(i); + if (ct->lower_bound() != 0.0 && std::abs(ct->lower_bound()) < drop) { + ++num_dropped; + max_dropped = std::max(max_dropped, std::abs(ct->lower_bound())); + ct->set_lower_bound(0.0); + } + if (ct->upper_bound() != 0.0 && std::abs(ct->upper_bound()) < drop) { + ++num_dropped; + max_dropped = std::max(max_dropped, std::abs(ct->upper_bound())); + ct->set_upper_bound(0.0); + } + } + if (num_dropped > 0) { + SOLVER_LOG(logger, "Set to zero ", num_dropped, + " variable or constraint bounds with largest magnitude ", + max_dropped); + } // Compute for each variable its current maximum magnitude. Note that we will // only scale variable with a coefficient >= 1, so it is safe to use this @@ -251,14 +287,16 @@ void RemoveNearZeroTerms(const SatParameters& params, MPModelProto* mp_model, max_bounds[i] = value; } + // Note that when a variable is fixed to zero, the code here remove all its + // coefficients. But we do not count them here. + double largest_removed = 0.0; + // We want the maximum absolute error while setting coefficients to zero to // not exceed our mip wanted precision. So for a binary variable we might set // to zero coefficient around 1e-7. But for large domain, we need lower coeff // than that, around 1e-12 with the default params.mip_max_bound(). This also // depends on the size of the constraint. int64_t num_removed = 0; - double largest_removed = 0.0; - const int num_constraints = mp_model->constraint_size(); for (int c = 0; c < num_constraints; ++c) { MPConstraintProto* ct = mp_model->mutable_constraint(c); int new_size = 0; @@ -302,8 +340,6 @@ void RemoveNearZeroTerms(const SatParameters& params, MPModelProto* mp_model, } if (num_removed > 0) { - // Note that when a variable is fixed to zero, the code here remove all its - // coefficients, so the largest magnitude can be quite large. SOLVER_LOG(logger, "Removed ", num_removed, " near zero terms with largest magnitude of ", largest_removed, "."); diff --git a/ortools/sat/lp_utils.h b/ortools/sat/lp_utils.h index 7c3345938e..8a072e8507 100644 --- a/ortools/sat/lp_utils.h +++ b/ortools/sat/lp_utils.h @@ -35,7 +35,7 @@ namespace sat { // Returns the smallest factor f such that f * abs(x) is integer modulo the // given tolerance relative to f (we use f * tolerance). It is only looking // for f smaller than the given limit. Returns zero if no such factor exist -// below the limit. The limit must be at most 2^62. +// below the limit. // // The complexity is a lot less than O(limit), but it is possible that we might // miss the smallest such factor if the tolerance used is too low. This is diff --git a/ortools/sat/parameters_validation.cc b/ortools/sat/parameters_validation.cc index 27164f76c8..fcb9f0589d 100644 --- a/ortools/sat/parameters_validation.cc +++ b/ortools/sat/parameters_validation.cc @@ -84,6 +84,7 @@ std::string ValidateParameters(const SatParameters& params) { TEST_IS_FINITE(mip_wanted_precision); TEST_IS_FINITE(mip_check_precision); TEST_IS_FINITE(mip_max_valid_magnitude); + TEST_IS_FINITE(mip_drop_tolerance); TEST_NOT_NAN(max_time_in_seconds); TEST_NOT_NAN(max_deterministic_time); @@ -99,6 +100,7 @@ std::string ValidateParameters(const SatParameters& params) { TEST_NON_NEGATIVE(mip_wanted_precision); TEST_NON_NEGATIVE(max_time_in_seconds); TEST_NON_NEGATIVE(max_deterministic_time); + TEST_NON_NEGATIVE(new_constraints_batch_size); TEST_NON_NEGATIVE(num_workers); TEST_NON_NEGATIVE(num_search_workers); TEST_NON_NEGATIVE(min_num_lns_workers); diff --git a/ortools/sat/presolve_context.cc b/ortools/sat/presolve_context.cc index c4e15db690..79dd6591c2 100644 --- a/ortools/sat/presolve_context.cc +++ b/ortools/sat/presolve_context.cc @@ -1522,7 +1522,7 @@ void PresolveContext::ReadObjectiveFromProto() { // This is an upper bound of the higher magnitude that can be reach by // summing an objective partial sum. Because of the model validation, this // shouldn't overflow, and we make sure it stays this way. - objective_overflow_detection_ = 0; + objective_overflow_detection_ = std::abs(objective_integer_before_offset_); objective_map_.clear(); for (int i = 0; i < obj.vars_size(); ++i) { @@ -1651,14 +1651,26 @@ bool PresolveContext::CanonicalizeObjective(bool simplify_domain) { entry.second /= gcd; } objective_domain_ = objective_domain_.InverseMultiplicationBy(gcd); + if (objective_domain_.IsEmpty()) return false; + objective_offset_ /= static_cast(gcd); objective_scaling_factor_ *= static_cast(gcd); // We update the offset accordingly. - absl::int128 offset = absl::int128(objective_integer_before_offset_) * - absl::int128(objective_integer_scaling_factor_) + - absl::int128(objective_integer_after_offset_); - objective_integer_scaling_factor_ *= gcd; + const absl::int128 offset = + absl::int128(objective_integer_before_offset_) * + absl::int128(objective_integer_scaling_factor_) + + absl::int128(objective_integer_after_offset_); + + if (objective_domain_.IsFixed() && objective_domain_.FixedValue() == 0) { + // We avoid a corner case where this would overflow but the objective is + // zero. In this case any factor work, so we just take 1 and avoid the + // overflow. + objective_integer_scaling_factor_ = 1; + } else { + objective_integer_scaling_factor_ *= gcd; + } + objective_integer_before_offset_ = static_cast( offset / absl::int128(objective_integer_scaling_factor_)); objective_integer_after_offset_ = static_cast( @@ -1835,13 +1847,36 @@ bool PresolveContext::ExploitExactlyOneInObjective( } } - ShiftCostInExactlyOne(exactly_one, min_coeff); - return true; + return ShiftCostInExactlyOne(exactly_one, min_coeff); } -void PresolveContext::ShiftCostInExactlyOne(absl::Span exactly_one, +bool PresolveContext::ShiftCostInExactlyOne(absl::Span exactly_one, int64_t shift) { - if (shift == 0) return; + if (shift == 0) return true; + + // We have to be careful because shifting cost like this might increase the + // min/max possible activity of the sum. + // + // TODO(user): Be more precise with this objective_overflow_detection_ and + // always keep it up to date on each offset / coeff change. + int64_t sum = 0; + int64_t new_sum = 0; + for (const int ref : exactly_one) { + const int var = PositiveRef(ref); + const int64_t obj = ObjectiveCoeff(var); + sum = CapAdd(sum, std::abs(obj)); + + const int64_t new_obj = RefIsPositive(ref) ? obj - shift : obj + shift; + new_sum = CapAdd(new_sum, std::abs(new_obj)); + } + if (AtMinOrMaxInt64(new_sum)) return false; + if (new_sum > sum) { + const int64_t new_value = + CapAdd(objective_overflow_detection_, new_sum - sum); + if (AtMinOrMaxInt64(new_value)) return false; + objective_overflow_detection_ = new_value; + } + int64_t offset = shift; for (const int ref : exactly_one) { const int var = PositiveRef(ref); @@ -1871,6 +1906,7 @@ void PresolveContext::ShiftCostInExactlyOne(absl::Span exactly_one, // Note that the domain never include the offset, so we need to update it. if (offset != 0) AddToObjectiveOffset(offset); + return true; } void PresolveContext::WriteObjectiveToProto() const { diff --git a/ortools/sat/presolve_context.h b/ortools/sat/presolve_context.h index b6904fa237..fb12613f97 100644 --- a/ortools/sat/presolve_context.h +++ b/ortools/sat/presolve_context.h @@ -428,7 +428,12 @@ class PresolveContext { // // Returns true if a simplification was done. bool ExploitExactlyOneInObjective(absl::Span exactly_one); - void ShiftCostInExactlyOne(absl::Span exactly_one, int64_t shift); + + // We can always add a multiple of sum X - 1 == 0 to the objective. + // However, depending on which multiple we choose, this might break our + // overflow preconditions on the objective. So we return false and do nothing + // if this happens. + bool ShiftCostInExactlyOne(absl::Span exactly_one, int64_t shift); // Allows to manipulate the objective coefficients. void RemoveVariableFromObjective(int ref); diff --git a/ortools/sat/samples/BUILD.bazel b/ortools/sat/samples/BUILD.bazel index d6dfb03f6a..c1b428aafb 100644 --- a/ortools/sat/samples/BUILD.bazel +++ b/ortools/sat/samples/BUILD.bazel @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -load(":code_samples.bzl", "code_sample_cc_py", "code_sample_py", "code_sample_java") +load(":code_samples.bzl", "code_sample_cc_py", "code_sample_java", "code_sample_py") code_sample_cc_py(name = "assignment_sat") diff --git a/ortools/sat/samples/code_samples.bzl b/ortools/sat/samples/code_samples.bzl index 1954f1dc48..c2e76e5697 100644 --- a/ortools/sat/samples/code_samples.bzl +++ b/ortools/sat/samples/code_samples.bzl @@ -102,4 +102,4 @@ def code_sample_java(name): data = [ ":" + name + "_java", ], - ) \ No newline at end of file + ) diff --git a/ortools/sat/samples/code_samples_java_test.sh b/ortools/sat/samples/code_samples_java_test.sh index fb64ebb5a8..57231f6a56 100755 --- a/ortools/sat/samples/code_samples_java_test.sh +++ b/ortools/sat/samples/code_samples_java_test.sh @@ -12,11 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. - declare -r DIR="${TEST_SRCDIR}/com_google_ortools/ortools/sat/samples" function test::ortools::code_samples_sat_java() { "${DIR}/$1_java" } -test::ortools::code_samples_sat_java $1 \ No newline at end of file +test::ortools::code_samples_sat_java $1 diff --git a/ortools/sat/sat_parameters.proto b/ortools/sat/sat_parameters.proto index cca26bf8f2..be2da5dd88 100644 --- a/ortools/sat/sat_parameters.proto +++ b/ortools/sat/sat_parameters.proto @@ -23,7 +23,7 @@ option csharp_namespace = "Google.OrTools.Sat"; // Contains the definitions for all the sat algorithm parameters and their // default values. // -// NEXT TAG: 232 +// NEXT TAG: 233 message SatParameters { // In some context, like in a portfolio of search, it makes sense to name a // given parameters set for logging purpose. @@ -1312,4 +1312,8 @@ message SatParameters { // defensive, but in practice, users shouldn't use super large values in a // MIP. optional double mip_max_valid_magnitude = 199 [default = 1e30]; + + // Any value in the input mip with a magnitude lower than this will be set to + // zero. This is to avoid some issue in LP presolving. + optional double mip_drop_tolerance = 232 [default = 1e-16]; } diff --git a/ortools/sat/swig_helper.cc b/ortools/sat/swig_helper.cc index b8c9d3bc73..491019a6ed 100644 --- a/ortools/sat/swig_helper.cc +++ b/ortools/sat/swig_helper.cc @@ -157,7 +157,7 @@ operations_research::Domain CpSatHelper::VariableDomain( bool CpSatHelper::WriteModelToFile( const operations_research::sat::CpModelProto& model_proto, const std::string& filename) { - return WriteModelProtoToFile(model_proto, filename); + return WriteModelProtoToFile(model_proto, filename); } } // namespace sat diff --git a/ortools/util/java/BUILD.bazel b/ortools/util/java/BUILD.bazel index 13430d11d5..cab45782b6 100644 --- a/ortools/util/java/BUILD.bazel +++ b/ortools/util/java/BUILD.bazel @@ -1,3 +1,16 @@ +# 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. + # Description: java wrapping of the code in ../ load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") @@ -46,15 +59,15 @@ ortools_java_wrap_cc( src = "sorted_interval_list.i", package = "com.google.ortools.util", swig_includes = [ - "//ortools/base:base_swig", ":vector_swig", + "//ortools/base:base_swig", ], swig_opt = select({ - "on_linux": "-DSWIGWORDSIZE64", - "//conditions:default": "", + "on_linux": "-DSWIGWORDSIZE64", + "//conditions:default": "", }), visibility = ["//visibility:public"], deps = [ "//ortools/util:sorted_interval_list", ], -) \ No newline at end of file +)