2021-04-01 21:00:53 +02:00
|
|
|
|
// Copyright 2010-2021 Google LLC
|
2014-05-23 14:33:13 +00:00
|
|
|
|
// 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.
|
2014-07-09 15:18:27 +00:00
|
|
|
|
|
2014-05-23 14:33:13 +00:00
|
|
|
|
#ifndef OR_TOOLS_SAT_OPTIMIZATION_H_
|
|
|
|
|
|
#define OR_TOOLS_SAT_OPTIMIZATION_H_
|
|
|
|
|
|
|
2017-07-27 11:28:55 -07:00
|
|
|
|
#include <functional>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
2022-02-15 18:00:11 +01:00
|
|
|
|
#include "absl/random/bit_gen_ref.h"
|
2017-07-27 11:28:55 -07:00
|
|
|
|
#include "ortools/sat/boolean_problem.pb.h"
|
2021-06-04 08:35:20 +02:00
|
|
|
|
#include "ortools/sat/cp_model_mapping.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
|
#include "ortools/sat/integer.h"
|
2017-12-08 14:52:49 +01:00
|
|
|
|
#include "ortools/sat/integer_search.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
|
#include "ortools/sat/model.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
|
#include "ortools/sat/pb_constraint.h"
|
2017-07-27 11:28:55 -07:00
|
|
|
|
#include "ortools/sat/sat_base.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
|
#include "ortools/sat/sat_parameters.pb.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
|
#include "ortools/sat/sat_solver.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
|
#include "ortools/util/time_limit.h"
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
|
namespace sat {
|
|
|
|
|
|
|
2017-09-06 10:18:51 +02:00
|
|
|
|
// Like MinimizeCore() with a slower but strictly better heuristic. This
|
|
|
|
|
|
// algorithm should produce a minimal core with respect to propagation. We put
|
|
|
|
|
|
// each literal of the initial core "last" at least once, so if such literal can
|
2018-07-02 15:35:40 +02:00
|
|
|
|
// be inferred by propagation by any subset of the other literal, it will be
|
2017-09-06 10:18:51 +02:00
|
|
|
|
// removed.
|
2018-03-06 18:17:36 +01:00
|
|
|
|
//
|
2022-02-14 13:31:52 +01:00
|
|
|
|
// Note that the literal of the minimized core will stay in the same order.
|
2021-02-17 16:42:54 +01:00
|
|
|
|
//
|
|
|
|
|
|
// TODO(user): Avoid spending too much time trying to minimize a core.
|
|
|
|
|
|
void MinimizeCoreWithPropagation(TimeLimit* limit, SatSolver* solver,
|
|
|
|
|
|
std::vector<Literal>* core);
|
2017-09-06 10:18:51 +02:00
|
|
|
|
|
2014-05-23 14:33:13 +00:00
|
|
|
|
// Because the Solve*() functions below are also used in scripts that requires a
|
|
|
|
|
|
// special output format, we use this to tell them whether or not to use the
|
|
|
|
|
|
// default logging framework or simply stdout. Most users should just use
|
|
|
|
|
|
// DEFAULT_LOG.
|
2020-10-22 23:36:58 +02:00
|
|
|
|
enum LogBehavior { DEFAULT_LOG, STDOUT_LOG };
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
|
|
|
|
|
// All the Solve*() functions below reuse the SatSolver::Status with a slightly
|
|
|
|
|
|
// different meaning:
|
2018-07-02 15:35:40 +02:00
|
|
|
|
// - FEASIBLE: The problem has been solved to optimality.
|
|
|
|
|
|
// - INFEASIBLE: Same meaning, the decision version is already unsat.
|
2014-05-23 14:33:13 +00:00
|
|
|
|
// - LIMIT_REACHED: we may have some feasible solution (if solution is
|
2018-07-02 15:35:40 +02:00
|
|
|
|
// non-empty), but the optimality is not proven.
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
|
|
|
|
|
// Implements the "Fu & Malik" algorithm described in:
|
|
|
|
|
|
// Zhaohui Fu, Sharad Malik, "On solving the Partial MAX-SAT problem", 2006,
|
|
|
|
|
|
// International Conference on Theory and Applications of Satisfiability
|
|
|
|
|
|
// Testing. (SAT’06), LNCS 4121.
|
|
|
|
|
|
//
|
|
|
|
|
|
// This algorithm requires all the objective weights to be the same (CHECKed)
|
2018-07-02 15:35:40 +02:00
|
|
|
|
// and currently only works on minimization problems. The problem is assumed to
|
|
|
|
|
|
// be already loaded into the given solver.
|
2014-05-23 14:33:13 +00:00
|
|
|
|
//
|
|
|
|
|
|
// TODO(user): double-check the correctness if the objective coefficients are
|
|
|
|
|
|
// negative.
|
2014-06-11 20:11:19 +00:00
|
|
|
|
SatSolver::Status SolveWithFuMalik(LogBehavior log,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
const LinearBooleanProblem& problem,
|
|
|
|
|
|
SatSolver* solver,
|
|
|
|
|
|
std::vector<bool>* solution);
|
2014-06-11 20:11:19 +00:00
|
|
|
|
|
|
|
|
|
|
// The WPM1 algorithm is a generalization of the Fu & Malik algorithm to
|
|
|
|
|
|
// weighted problems. Note that if all objective weights are the same, this is
|
|
|
|
|
|
// almost the same as SolveWithFuMalik() but the encoding of the constraints is
|
|
|
|
|
|
// slightly different.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Ansotegui, C., Bonet, M.L., Levy, J.: Solving (weighted) partial MaxSAT
|
2020-10-28 13:42:36 +01:00
|
|
|
|
// through satisfiability testing. In: Proc. of the 12th Int. Conf. on Theory and
|
2018-09-10 13:32:30 +02:00
|
|
|
|
// Applications of Satisfiability Testing (SAT’09). pp. 427-440 (2009)
|
2014-06-11 20:11:19 +00:00
|
|
|
|
SatSolver::Status SolveWithWPM1(LogBehavior log,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
const LinearBooleanProblem& problem,
|
|
|
|
|
|
SatSolver* solver, std::vector<bool>* solution);
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
|
|
|
|
|
// Solves num_times the decision version of the given problem with different
|
|
|
|
|
|
// random parameters. Keep the best solution (regarding the objective) and
|
|
|
|
|
|
// returns it in solution. The problem is assumed to be already loaded into the
|
|
|
|
|
|
// given solver.
|
2021-12-06 14:03:19 +01:00
|
|
|
|
SatSolver::Status SolveWithRandomParameters(
|
|
|
|
|
|
LogBehavior log, const LinearBooleanProblem& problem, int num_times,
|
|
|
|
|
|
absl::BitGenRef random, SatSolver* solver, std::vector<bool>* solution);
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
|
|
|
|
|
// Starts by solving the decision version of the given LinearBooleanProblem and
|
|
|
|
|
|
// then simply add a constraint to find a lower objective that the current best
|
|
|
|
|
|
// solution and repeat until the problem becomes unsat.
|
|
|
|
|
|
//
|
|
|
|
|
|
// The problem is assumed to be already loaded into the given solver. If
|
|
|
|
|
|
// solution is initially a feasible solution, the search will starts from there.
|
|
|
|
|
|
// solution will be updated with the best solution found so far.
|
2014-06-11 20:11:19 +00:00
|
|
|
|
SatSolver::Status SolveWithLinearScan(LogBehavior log,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
const LinearBooleanProblem& problem,
|
|
|
|
|
|
SatSolver* solver,
|
|
|
|
|
|
std::vector<bool>* solution);
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
2014-07-08 09:27:02 +00:00
|
|
|
|
// Similar algorithm as the one used by qmaxsat, this is a linear scan with the
|
2018-07-02 15:35:40 +02:00
|
|
|
|
// at-most k constraint encoded in SAT. This only works on problems with
|
|
|
|
|
|
// constant weights.
|
2014-07-08 09:27:02 +00:00
|
|
|
|
SatSolver::Status SolveWithCardinalityEncoding(
|
2020-10-28 13:42:36 +01:00
|
|
|
|
LogBehavior log, const LinearBooleanProblem& problem, SatSolver* solver,
|
|
|
|
|
|
std::vector<bool>* solution);
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
|
|
// This is an original algorithm. It is a mix between the cardinality encoding
|
|
|
|
|
|
// and the Fu & Malik algorithm. It also works on general weighted instances.
|
|
|
|
|
|
SatSolver::Status SolveWithCardinalityEncodingAndCore(
|
2020-10-28 13:42:36 +01:00
|
|
|
|
LogBehavior log, const LinearBooleanProblem& problem, SatSolver* solver,
|
|
|
|
|
|
std::vector<bool>* solution);
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
2022-02-11 13:40:22 +01:00
|
|
|
|
// Model-based API to minimize a given IntegerVariable by solving a sequence of
|
|
|
|
|
|
// decision problem. Each problem is solved using SolveIntegerProblem(). Returns
|
|
|
|
|
|
// the status of the last solved decision problem.
|
2016-03-16 10:10:38 +01:00
|
|
|
|
//
|
2019-04-18 13:29:21 +02:00
|
|
|
|
// The feasible_solution_observer function will be called each time a new
|
|
|
|
|
|
// feasible solution is found.
|
2019-05-10 23:26:36 +02:00
|
|
|
|
//
|
|
|
|
|
|
// Note that this function will resume the search from the current state of the
|
|
|
|
|
|
// solver, and it is up to the client to backtrack to the root node if needed.
|
2016-09-12 13:42:16 +02:00
|
|
|
|
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(
|
2019-04-18 13:29:21 +02:00
|
|
|
|
IntegerVariable objective_var,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
const std::function<void()>& feasible_solution_observer, Model* model);
|
2016-09-12 13:42:16 +02:00
|
|
|
|
|
2018-05-03 15:00:06 +02:00
|
|
|
|
// Use a low conflict limit and performs a binary search to try to restrict the
|
|
|
|
|
|
// domain of objective_var.
|
|
|
|
|
|
void RestrictObjectiveDomainWithBinarySearch(
|
|
|
|
|
|
IntegerVariable objective_var,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
const std::function<void()>& feasible_solution_observer, Model* model);
|
2018-05-03 15:00:06 +02:00
|
|
|
|
|
2022-05-09 14:44:50 +02:00
|
|
|
|
// Transforms the given linear expression so that:
|
|
|
|
|
|
// - duplicate terms are merged.
|
|
|
|
|
|
// - terms with a literal and its negation are merged.
|
|
|
|
|
|
// - all weight are positive.
|
|
|
|
|
|
//
|
|
|
|
|
|
// TODO(user): Merge this with similar code like
|
|
|
|
|
|
// ComputeBooleanLinearExpressionCanonicalForm().
|
|
|
|
|
|
void PresolveBooleanLinearExpression(std::vector<Literal>* literals,
|
|
|
|
|
|
std::vector<Coefficient>* coefficients,
|
|
|
|
|
|
Coefficient* offset);
|
|
|
|
|
|
|
2017-05-16 10:43:07 +02:00
|
|
|
|
// Same as MinimizeIntegerVariableWithLinearScanAndLazyEncoding() but use
|
2017-07-05 16:27:00 -07:00
|
|
|
|
// a core-based approach instead. Note that the given objective_var is just used
|
2019-04-18 16:34:36 +02:00
|
|
|
|
// for reporting the lower-bound/upper-bound and do not need to be linked with
|
|
|
|
|
|
// its linear representation.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Unlike MinimizeIntegerVariableWithLinearScanAndLazyEncoding() this function
|
|
|
|
|
|
// just return the last solver status. In particular if it is INFEASIBLE but
|
|
|
|
|
|
// feasible_solution_observer() was called, it means we are at OPTIMAL.
|
2019-06-17 18:27:56 +02:00
|
|
|
|
class CoreBasedOptimizer {
|
2020-10-22 23:36:58 +02:00
|
|
|
|
public:
|
2019-06-17 18:27:56 +02:00
|
|
|
|
CoreBasedOptimizer(IntegerVariable objective_var,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
const std::vector<IntegerVariable>& variables,
|
|
|
|
|
|
const std::vector<IntegerValue>& coefficients,
|
2019-06-17 18:27:56 +02:00
|
|
|
|
std::function<void()> feasible_solution_observer,
|
2020-10-28 13:42:36 +01:00
|
|
|
|
Model* model);
|
2019-06-17 18:27:56 +02:00
|
|
|
|
|
|
|
|
|
|
// TODO(user): Change the algo slighlty to allow resuming from the last
|
2019-07-05 09:33:04 +02:00
|
|
|
|
// aborted position. Currently, the search is "resumable", but it will restart
|
|
|
|
|
|
// some of the work already done, so it might just never find anything.
|
2019-06-17 18:27:56 +02:00
|
|
|
|
SatSolver::Status Optimize();
|
|
|
|
|
|
|
2022-03-14 15:06:11 +01:00
|
|
|
|
// A different way to encode the objective as core are found.
|
|
|
|
|
|
//
|
|
|
|
|
|
// If the vector if literals is passed it will use that, otherwise it will
|
|
|
|
|
|
// encode the passed integer variables. In both cases, the vector used should
|
|
|
|
|
|
// be of the same size as the coefficients vector.
|
2022-02-14 13:31:52 +01:00
|
|
|
|
//
|
|
|
|
|
|
// It seems to be more powerful, but it isn't completely implemented yet.
|
|
|
|
|
|
// TODO(user):
|
|
|
|
|
|
// - Support resuming for interleaved search.
|
|
|
|
|
|
// - Implement all core heurisitics.
|
|
|
|
|
|
SatSolver::Status OptimizeWithSatEncoding(
|
|
|
|
|
|
const std::vector<Literal>& literals,
|
2022-03-14 15:06:11 +01:00
|
|
|
|
const std::vector<IntegerVariable>& vars,
|
2022-02-16 14:57:41 +01:00
|
|
|
|
const std::vector<Coefficient>& coefficients, Coefficient offset);
|
2022-02-14 13:31:52 +01:00
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
|
private:
|
2020-10-28 13:42:36 +01:00
|
|
|
|
CoreBasedOptimizer(const CoreBasedOptimizer&) = delete;
|
|
|
|
|
|
CoreBasedOptimizer& operator=(const CoreBasedOptimizer&) = delete;
|
2019-06-17 18:27:56 +02:00
|
|
|
|
|
|
|
|
|
|
struct ObjectiveTerm {
|
|
|
|
|
|
IntegerVariable var;
|
|
|
|
|
|
IntegerValue weight;
|
2020-10-22 23:36:58 +02:00
|
|
|
|
int depth; // Only for logging/debugging.
|
2019-06-17 18:27:56 +02:00
|
|
|
|
IntegerValue old_var_lb;
|
|
|
|
|
|
|
|
|
|
|
|
// An upper bound on the optimal solution if we were to optimize only this
|
|
|
|
|
|
// term. This is used by the cover optimization code.
|
|
|
|
|
|
IntegerValue cover_ub;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// This will be called each time a feasible solution is found. Returns false
|
|
|
|
|
|
// if a conflict was detected while trying to constrain the objective to a
|
|
|
|
|
|
// smaller value.
|
|
|
|
|
|
bool ProcessSolution();
|
|
|
|
|
|
|
|
|
|
|
|
// Use the gap an implied bounds to propagated the bounds of the objective
|
|
|
|
|
|
// variables and of its terms.
|
|
|
|
|
|
bool PropagateObjectiveBounds();
|
|
|
|
|
|
|
|
|
|
|
|
// Heuristic that aim to find the "real" lower bound of the objective on each
|
|
|
|
|
|
// core by using a linear scan optimization approach.
|
|
|
|
|
|
bool CoverOptimization();
|
|
|
|
|
|
|
|
|
|
|
|
// Computes the next stratification threshold.
|
|
|
|
|
|
// Sets it to zero if all the assumptions where already considered.
|
|
|
|
|
|
void ComputeNextStratificationThreshold();
|
|
|
|
|
|
|
2022-02-16 14:57:41 +01:00
|
|
|
|
// If we have an "at most one can be false" between literals with a positive
|
|
|
|
|
|
// cost, you then know that at least n - 1 will contribute to the cost, and
|
|
|
|
|
|
// you can increase the objective lower bound. This is the same as having
|
|
|
|
|
|
// a real "at most one" constraint on the negation of such literals.
|
|
|
|
|
|
//
|
|
|
|
|
|
// This detects such "at most ones" and rewrite the objective accordingly.
|
|
|
|
|
|
// For each at most one, the rewrite create a new Boolean variable and update
|
|
|
|
|
|
// the cost so that the trivial objective lower bound reflect the increase.
|
|
|
|
|
|
//
|
|
|
|
|
|
// TODO(user) : Code that as a general presolve rule? I am not sure adding
|
|
|
|
|
|
// the extra Booleans is always a good idea though. Especially since the LP
|
|
|
|
|
|
// will see the same lower bound that what is computed by this.
|
|
|
|
|
|
void PresolveObjectiveWithAtMostOne(std::vector<Literal>* literals,
|
|
|
|
|
|
std::vector<Coefficient>* coefficients,
|
|
|
|
|
|
Coefficient* offset);
|
|
|
|
|
|
|
2020-10-28 13:42:36 +01:00
|
|
|
|
SatParameters* parameters_;
|
|
|
|
|
|
SatSolver* sat_solver_;
|
|
|
|
|
|
TimeLimit* time_limit_;
|
2022-02-16 14:57:41 +01:00
|
|
|
|
BinaryImplicationGraph* implications_;
|
2020-10-28 13:42:36 +01:00
|
|
|
|
IntegerTrail* integer_trail_;
|
|
|
|
|
|
IntegerEncoder* integer_encoder_;
|
2022-02-16 14:57:41 +01:00
|
|
|
|
Model* model_;
|
2019-06-17 18:27:56 +02:00
|
|
|
|
|
|
|
|
|
|
IntegerVariable objective_var_;
|
|
|
|
|
|
std::vector<ObjectiveTerm> terms_;
|
|
|
|
|
|
IntegerValue stratification_threshold_;
|
|
|
|
|
|
std::function<void()> feasible_solution_observer_;
|
|
|
|
|
|
|
2019-07-05 09:33:04 +02:00
|
|
|
|
// This is used to not add the objective equation more than once if we
|
|
|
|
|
|
// solve in "chunk".
|
|
|
|
|
|
bool already_switched_to_linear_scan_ = false;
|
|
|
|
|
|
|
2019-06-17 18:27:56 +02:00
|
|
|
|
// Set to true when we need to abort early.
|
|
|
|
|
|
//
|
|
|
|
|
|
// TODO(user): This is only used for the stop after first solution parameter
|
|
|
|
|
|
// which should likely be handled differently by simply using the normal way
|
|
|
|
|
|
// to stop a solver from the feasible solution callback.
|
|
|
|
|
|
bool stop_ = false;
|
|
|
|
|
|
};
|
2017-05-16 10:43:07 +02:00
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
|
} // namespace sat
|
|
|
|
|
|
} // namespace operations_research
|
2014-05-23 14:33:13 +00:00
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
|
#endif // OR_TOOLS_SAT_OPTIMIZATION_H_
|