Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
optimization.h
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
14#ifndef OR_TOOLS_SAT_OPTIMIZATION_H_
15#define OR_TOOLS_SAT_OPTIMIZATION_H_
16
17#include <functional>
18#include <vector>
19
20#include "absl/types/span.h"
21#include "ortools/sat/clause.h"
23#include "ortools/sat/integer.h"
25#include "ortools/sat/model.h"
27#include "ortools/sat/sat_parameters.pb.h"
30
31namespace operations_research {
32namespace sat {
33
34// Like MinimizeCore() with a slower but strictly better heuristic. This
35// algorithm should produce a minimal core with respect to propagation. We put
36// each literal of the initial core "last" at least once, so if such literal can
37// be inferred by propagation by any subset of the other literal, it will be
38// removed.
39//
40// Note that the literal of the minimized core will stay in the same order.
41//
42// TODO(user): Avoid spending too much time trying to minimize a core.
43void MinimizeCoreWithPropagation(TimeLimit* limit, SatSolver* solver,
44 std::vector<Literal>* core);
45
46void MinimizeCoreWithSearch(TimeLimit* limit, SatSolver* solver,
47 std::vector<Literal>* core);
48
49bool ProbeLiteral(Literal assumption, SatSolver* solver);
50
51// Remove fixed literals from the core.
52void FilterAssignedLiteral(const VariablesAssignment& assignment,
53 std::vector<Literal>* core);
54
55// Model-based API to minimize a given IntegerVariable by solving a sequence of
56// decision problem. Each problem is solved using SolveIntegerProblem(). Returns
57// the status of the last solved decision problem.
58//
59// The feasible_solution_observer function will be called each time a new
60// feasible solution is found.
61//
62// Note that this function will resume the search from the current state of the
63// solver, and it is up to the client to backtrack to the root node if needed.
65 IntegerVariable objective_var,
66 const std::function<void()>& feasible_solution_observer, Model* model);
67
68// Use a low conflict limit and performs a binary search to try to restrict the
69// domain of objective_var.
71 IntegerVariable objective_var,
72 const std::function<void()>& feasible_solution_observer, Model* model);
73
74// Transforms the given linear expression so that:
75// - duplicate terms are merged.
76// - terms with a literal and its negation are merged.
77// - all weight are positive.
78//
79// TODO(user): Merge this with similar code like
80// ComputeBooleanLinearExpressionCanonicalForm().
81void PresolveBooleanLinearExpression(std::vector<Literal>* literals,
82 std::vector<Coefficient>* coefficients,
83 Coefficient* offset);
84
85// Same as MinimizeIntegerVariableWithLinearScanAndLazyEncoding() but use
86// a core-based approach instead. Note that the given objective_var is just used
87// for reporting the lower-bound/upper-bound and do not need to be linked with
88// its linear representation.
89//
90// Unlike MinimizeIntegerVariableWithLinearScanAndLazyEncoding() this function
91// just return the last solver status. In particular if it is INFEASIBLE but
92// feasible_solution_observer() was called, it means we are at OPTIMAL.
94 public:
95 CoreBasedOptimizer(IntegerVariable objective_var,
96 absl::Span<const IntegerVariable> variables,
97 absl::Span<const IntegerValue> coefficients,
98 std::function<void()> feasible_solution_observer,
99 Model* model);
100
101 // TODO(user): Change the algo slighlty to allow resuming from the last
102 // aborted position. Currently, the search is "resumable", but it will restart
103 // some of the work already done, so it might just never find anything.
105
106 // A different way to encode the objective as core are found.
107 //
108 // If the vector if literals is passed it will use that, otherwise it will
109 // encode the passed integer variables. In both cases, the vector used should
110 // be of the same size as the coefficients vector.
111 //
112 // It seems to be more powerful, but it isn't completely implemented yet.
113 // TODO(user):
114 // - Support resuming for interleaved search.
115 // - Implement all core heurisitics.
117 absl::Span<const Literal> literals,
118 absl::Span<const IntegerVariable> vars,
119 absl::Span<const Coefficient> coefficients, Coefficient offset);
120
121 private:
122 CoreBasedOptimizer(const CoreBasedOptimizer&) = delete;
123 CoreBasedOptimizer& operator=(const CoreBasedOptimizer&) = delete;
124
125 struct ObjectiveTerm {
126 IntegerVariable var;
127 IntegerValue weight;
128 int depth; // Only for logging/debugging.
129 IntegerValue old_var_lb;
130
131 // An upper bound on the optimal solution if we were to optimize only this
132 // term. This is used by the cover optimization code.
133 IntegerValue cover_ub;
134 };
135
136 // This will be called each time a feasible solution is found. Returns false
137 // if a conflict was detected while trying to constrain the objective to a
138 // smaller value.
139 bool ProcessSolution();
140
141 // Use the gap an implied bounds to propagated the bounds of the objective
142 // variables and of its terms.
143 bool PropagateObjectiveBounds();
144
145 // Heuristic that aim to find the "real" lower bound of the objective on each
146 // core by using a linear scan optimization approach.
147 bool CoverOptimization();
148
149 // Computes the next stratification threshold.
150 // Sets it to zero if all the assumptions where already considered.
151 void ComputeNextStratificationThreshold();
152
153 // If we have an "at most one can be false" between literals with a positive
154 // cost, you then know that at least n - 1 will contribute to the cost, and
155 // you can increase the objective lower bound. This is the same as having
156 // a real "at most one" constraint on the negation of such literals.
157 //
158 // This detects such "at most ones" and rewrite the objective accordingly.
159 // For each at most one, the rewrite create a new Boolean variable and update
160 // the cost so that the trivial objective lower bound reflect the increase.
161 //
162 // TODO(user) : Code that as a general presolve rule? I am not sure adding
163 // the extra Booleans is always a good idea though. Especially since the LP
164 // will see the same lower bound that what is computed by this.
165 void PresolveObjectiveWithAtMostOne(std::vector<Literal>* literals,
166 std::vector<Coefficient>* coefficients,
167 Coefficient* offset);
168
169 SatParameters* parameters_;
170 SatSolver* sat_solver_;
171 ClauseManager* clauses_;
172 TimeLimit* time_limit_;
173 BinaryImplicationGraph* implications_;
174 IntegerTrail* integer_trail_;
175 IntegerEncoder* integer_encoder_;
176 Model* model_;
177
178 IntegerVariable objective_var_;
179 std::vector<ObjectiveTerm> terms_;
180 IntegerValue stratification_threshold_;
181 std::function<void()> feasible_solution_observer_;
182
183 // This is used to not add the objective equation more than once if we
184 // solve in "chunk".
185 bool already_switched_to_linear_scan_ = false;
186
187 // Set to true when we need to abort early.
188 //
189 // TODO(user): This is only used for the stop after first solution parameter
190 // which should likely be handled differently by simply using the normal way
191 // to stop a solver from the feasible solution callback.
192 bool stop_ = false;
193};
194
195} // namespace sat
196} // namespace operations_research
197
198#endif // OR_TOOLS_SAT_OPTIMIZATION_H_
SatSolver::Status OptimizeWithSatEncoding(absl::Span< const Literal > literals, absl::Span< const IntegerVariable > vars, absl::Span< const Coefficient > coefficients, Coefficient offset)
CoreBasedOptimizer(IntegerVariable objective_var, absl::Span< const IntegerVariable > variables, absl::Span< const IntegerValue > coefficients, std::function< void()> feasible_solution_observer, Model *model)
absl::Span< const double > coefficients
GRBmodel * model
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void FilterAssignedLiteral(const VariablesAssignment &assignment, std::vector< Literal > *core)
A core cannot be all true.
void MinimizeCoreWithSearch(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void PresolveBooleanLinearExpression(std::vector< Literal > *literals, std::vector< Coefficient > *coefficients, Coefficient *offset)
bool ProbeLiteral(Literal assumption, SatSolver *solver)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
In SWIG mode, we don't want anything besides these top-level includes.