Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
complete_optimizer.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <limits>
19#include <string>
20#include <vector>
21
22#include "absl/log/check.h"
23#include "absl/strings/string_view.h"
25#include "ortools/bop/bop_parameters.pb.h"
28#include "ortools/sat/boolean_problem.pb.h"
30#include "ortools/sat/model.h"
33#include "ortools/sat/sat_parameters.pb.h"
35#include "ortools/util/stats.h"
38
39namespace operations_research {
40namespace bop {
41
44 model_(std::string(name)),
45 sat_solver_(model_.GetOrCreate<sat::SatSolver>()),
46 encoder_(&model_),
47 state_update_stamp_(ProblemState::kInitialStampValue),
48 initialized_(false),
49 assumptions_already_added_(false) {
50 // This is in term of number of variables not at their minimal value.
51 lower_bound_ = sat::Coefficient(0);
52 upper_bound_ = sat::kCoefficientMax;
53}
54
56
57BopOptimizerBase::Status SatCoreBasedOptimizer::SynchronizeIfNeeded(
58 const ProblemState& problem_state) {
59 if (state_update_stamp_ == problem_state.update_stamp()) {
61 }
62 state_update_stamp_ = problem_state.update_stamp();
63
64 // Note that if the solver is not empty, this only load the newly learned
65 // information.
67 LoadStateProblemToSatSolver(problem_state, sat_solver_);
69
70 if (!initialized_) {
71 // Initialize the algorithm.
72 offset_ = 0;
73 const auto objective_proto = problem_state.original_problem().objective();
74 for (int i = 0; i < objective_proto.literals_size(); ++i) {
75 const sat::Literal literal(objective_proto.literals(i));
76 const sat::Coefficient coeff(objective_proto.coefficients(i));
77
78 // We want to maximize the cost when this literal is true.
79 if (coeff > 0) {
81 } else {
82 encoder_.AddBaseNode(
83 sat::EncodingNode::LiteralNode(literal.Negated(), -coeff));
84
85 // Note that this increase the offset since the coeff is negative.
86 offset_ -= objective_proto.coefficients(i);
87 }
88 }
89 initialized_ = true;
90
91 // This is used by the "stratified" approach.
92 stratified_lower_bound_ = sat::Coefficient(0);
93 for (const sat::EncodingNode* n : encoder_.nodes()) {
94 stratified_lower_bound_ = std::max(stratified_lower_bound_, n->weight());
95 }
96 }
97
98 // Extract the new upper bound.
99 if (problem_state.solution().IsFeasible()) {
100 upper_bound_ = problem_state.solution().GetCost() + offset_;
101 }
103}
104
105sat::SatSolver::Status SatCoreBasedOptimizer::SolveWithAssumptions() {
106 sat::ReduceNodes(upper_bound_, &lower_bound_, encoder_.mutable_nodes(),
107 sat_solver_);
108 const std::vector<sat::Literal> assumptions = sat::ExtractAssumptions(
109 stratified_lower_bound_, encoder_.nodes(), sat_solver_);
110 return sat_solver_->ResetAndSolveWithGivenAssumptions(assumptions);
111}
112
113// Only run this if there is an objective.
115 const ProblemState& problem_state) const {
116 return problem_state.original_problem().objective().literals_size() > 0;
117}
118
120 const BopParameters& parameters, const ProblemState& problem_state,
121 LearnedInfo* learned_info, TimeLimit* time_limit) {
123 CHECK(learned_info != nullptr);
124 CHECK(time_limit != nullptr);
125 learned_info->Clear();
126
127 const BopOptimizerBase::Status sync_status =
128 SynchronizeIfNeeded(problem_state);
129 if (sync_status != BopOptimizerBase::CONTINUE) {
130 return sync_status;
131 }
132
133 int64_t conflict_limit = parameters.max_number_of_conflicts_in_random_lns();
134 double deterministic_time_at_last_sync = sat_solver_->deterministic_time();
135 while (!time_limit->LimitReached()) {
136 sat::SatParameters sat_params = sat_solver_->parameters();
137 sat_params.set_max_time_in_seconds(time_limit->GetTimeLeft());
138 sat_params.set_max_deterministic_time(
139 time_limit->GetDeterministicTimeLeft());
140 sat_params.set_random_seed(parameters.random_seed());
141 sat_params.set_max_number_of_conflicts(conflict_limit);
142 sat_solver_->SetParameters(sat_params);
143
144 const int64_t old_num_conflicts = sat_solver_->num_failures();
145 const sat::SatSolver::Status sat_status = assumptions_already_added_
146 ? sat_solver_->Solve()
147 : SolveWithAssumptions();
148 time_limit->AdvanceDeterministicTime(sat_solver_->deterministic_time() -
149 deterministic_time_at_last_sync);
150 deterministic_time_at_last_sync = sat_solver_->deterministic_time();
151
152 assumptions_already_added_ = true;
153 conflict_limit -= sat_solver_->num_failures() - old_num_conflicts;
154 learned_info->lower_bound = lower_bound_.value() - offset_.value();
155
156 // This is possible because we over-constrain the objective.
157 if (sat_status == sat::SatSolver::INFEASIBLE) {
158 return problem_state.solution().IsFeasible()
161 }
162
163 ExtractLearnedInfoFromSatSolver(sat_solver_, learned_info);
164 if (sat_status == sat::SatSolver::LIMIT_REACHED || conflict_limit < 0) {
166 }
167 if (sat_status == sat::SatSolver::FEASIBLE) {
168 stratified_lower_bound_ =
169 MaxNodeWeightSmallerThan(encoder_.nodes(), stratified_lower_bound_);
170
171 // We found a better solution!
173 &learned_info->solution);
174 if (stratified_lower_bound_ > 0) {
175 assumptions_already_added_ = false;
177 }
179 }
180
181 // The interesting case: we have a core.
182 // TODO(user): Check that this cannot fail because of the conflict limit.
183 std::vector<sat::Literal> core =
184 sat_solver_->GetLastIncompatibleDecisions();
185 sat::MinimizeCore(sat_solver_, &core);
186
187 const sat::Coefficient min_weight =
188 sat::ComputeCoreMinWeight(encoder_.nodes(), core);
189 std::string info_str;
190 encoder_.ProcessCore(
191 core, min_weight,
192 /*gap=*/std::numeric_limits<sat::Coefficient::ValueType>::max(),
193 &info_str);
194 assumptions_already_added_ = false;
195 }
197}
198
199} // namespace bop
200} // namespace operations_research
const BopSolution & solution() const
Definition bop_base.h:205
const sat::LinearBooleanProblem & original_problem() const
Definition bop_base.h:210
bool ShouldBeRun(const ProblemState &problem_state) const override
Only run this if there is an objective.
Status Optimize(const BopParameters &parameters, const ProblemState &problem_state, LearnedInfo *learned_info, TimeLimit *time_limit) override
static EncodingNode LiteralNode(Literal l, Coefficient weight)
Definition encoding.cc:50
std::vector< EncodingNode * > * mutable_nodes()
Definition encoding.h:252
void AddBaseNode(EncodingNode node)
Definition encoding.h:245
const std::vector< EncodingNode * > & nodes() const
Definition encoding.h:251
bool ProcessCore(absl::Span< const Literal > core, Coefficient min_weight, Coefficient gap, std::string *info)
Definition encoding.cc:586
std::vector< Literal > GetLastIncompatibleDecisions()
const SatParameters & parameters() const
Status ResetAndSolveWithGivenAssumptions(const std::vector< Literal > &assumptions, int64_t max_number_of_conflicts=-1)
const VariablesAssignment & Assignment() const
Definition sat_solver.h:388
void SetParameters(const SatParameters &parameters)
SatParameters parameters
const std::string name
A name for logging purposes.
absl::Status status
Definition g_gurobi.cc:44
int literal
time_limit
Definition solve.cc:22
BopOptimizerBase::Status LoadStateProblemToSatSolver(const ProblemState &problem_state, sat::SatSolver *sat_solver)
Definition bop_util.cc:98
void SatAssignmentToBopSolution(const sat::VariablesAssignment &assignment, BopSolution *solution)
Definition bop_util.cc:132
void ExtractLearnedInfoFromSatSolver(sat::SatSolver *solver, LearnedInfo *info)
Definition bop_util.cc:109
std::vector< Literal > ExtractAssumptions(Coefficient stratified_lower_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver)
Definition encoding.cc:547
void ReduceNodes(Coefficient upper_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition encoding.cc:501
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
Definition encoding.cc:560
void MinimizeCore(SatSolver *solver, std::vector< Literal > *core)
const Coefficient kCoefficientMax(std::numeric_limits< Coefficient::ValueType >::max())
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.
#define SCOPED_TIME_STAT(stats)
Definition stats.h:418
int64_t lower_bound
A lower bound (for multi-threading purpose).
Definition bop_base.h:279
BopSolution solution
New solution. Note that the solution might be infeasible.
Definition bop_base.h:276