Files
ortools-clone/ortools/sat/max_hs.h
Corentin Le Molgat a66a6daac7 Bump Copyright to 2025
2025-01-10 11:35:44 +01:00

188 lines
7.0 KiB
C++

// Copyright 2010-2025 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_SAT_MAX_HS_H_
#define OR_TOOLS_SAT_MAX_HS_H_
#include <stdint.h>
#include <cstdint>
#include <functional>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/types/span.h"
#include "ortools/base/strong_vector.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_mapping.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/integer_base.h"
#include "ortools/sat/linear_constraint.h"
#include "ortools/sat/linear_relaxation.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_base.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/sat/sat_solver.h"
#include "ortools/sat/synchronization.h"
#include "ortools/sat/util.h"
#include "ortools/util/time_limit.h"
namespace operations_research {
namespace sat {
// Generalization of the max-HS algorithm (HS stands for Hitting Set). This is
// similar to MinimizeWithCoreAndLazyEncoding() but it uses a hybrid approach
// with a MIP solver to handle the discovered infeasibility cores.
//
// See, Jessica Davies and Fahiem Bacchus, "Solving MAXSAT by Solving a
// Sequence of Simpler SAT Instances",
// http://www.cs.toronto.edu/~jdavies/daviesCP11.pdf"
//
// Note that an implementation of this approach won the 2016 max-SAT competition
// on the industrial category, see
// http://maxsat.ia.udl.cat/results/#wpms-industrial
//
// TODO(user): This class requires linking with the SCIP MIP solver which is
// quite big, maybe we should find a way not to do that.
class HittingSetOptimizer {
public:
HittingSetOptimizer(const CpModelProto& model_proto,
const ObjectiveDefinition& objective_definition,
const std::function<void()>& feasible_solution_observer,
Model* model);
SatSolver::Status Optimize();
private:
const int kUnextracted = -1;
SatSolver::Status FindMultipleCoresForMaxHs(
std::vector<Literal> assumptions,
std::vector<std::vector<Literal>>* core);
// Extract the objective variables, which is the smallest possible useful set.
void ExtractObjectiveVariables();
// Calls ComputeAdditionalVariablesToExtract() and extract all new variables.
// This must be called after the linear relaxation has been filled.
void ExtractAdditionalVariables(absl::Span<const IntegerVariable> to_extract);
// Heuristic to decide which variables (in addition to the objective
// variables) to extract.
// We use a sorted map to have a deterministic model.
std::vector<IntegerVariable> ComputeAdditionalVariablesToExtract();
// Checks whethers a variable is extracted, and return the index in the MIP
// model. It returns the same indef for both the variable and its negation.
//
// Node that the domain of the MIP variable is equal to the domain of the
// positive variable.
int GetExtractedIndex(IntegerVariable var) const;
// Returns false if the model is unsat.
bool ComputeInitialMpModel();
// Project the at_most_one constraint on the set of extracted variables.
void ProjectAndAddAtMostOne(absl::Span<const Literal> literals);
// Project the linear constraint on the set of extracted variables. Non
// extracted variables are used to 'extend' the lower and upper bound of the
// linear equation.
//
// It returns a non-null proto if the constraints was successfully extracted.
MPConstraintProto* ProjectAndAddLinear(const LinearConstraint& linear);
// Auxiliary method used by ProjectAndAddLinear(), and TightenLinear().
void ProjectLinear(const LinearConstraint& linear, MPConstraintProto* ct);
// Imports variable bounds from the shared bound manager (in parallel),
// updates the domains of the SAT variables, lower and upper bounds of
// extracted variables. Then it scans the extracted linear constraints and
// recompute its lower and upper bounds.
void TightenMpModel();
// Processes the cores from the SAT solver and add them to the MPModel.
void AddCoresToTheMpModel(absl::Span<const std::vector<Literal>> cores);
// Builds the assumptions from the current MP solution.
std::vector<Literal> BuildAssumptions(
IntegerValue stratified_threshold,
IntegerValue* next_stratified_threshold);
// This will be called each time a feasible solution is found.
bool ProcessSolution();
// Import shared information. Returns false if the model is unsat.
bool ImportFromOtherWorkers();
// Problem definition
const CpModelProto& model_proto_;
const ObjectiveDefinition& objective_definition_;
const std::function<void()> feasible_solution_observer_;
// Model object.
Model* model_;
SatSolver* sat_solver_;
TimeLimit* time_limit_;
const SatParameters& parameters_;
ModelRandomGenerator* random_;
SharedResponseManager* shared_response_;
IntegerTrail* integer_trail_;
IntegerEncoder* integer_encoder_;
// Linear model.
MPConstraintProto* obj_constraint_ = nullptr;
MPModelRequest request_;
MPSolutionResponse response_;
// Linear relaxation of the SAT model.
LinearRelaxation relaxation_;
// TODO(user): The core is returned in the same order as the assumptions,
// so we don't really need this map, we could just do a linear scan to
// recover which node are part of the core.
absl::flat_hash_map<LiteralIndex, std::vector<int>> assumption_to_indices_;
// New Booleans variable in the MIP model to represent X OP cte (OP is => if
// the variable of the objective is positive, <= otherwise).
absl::flat_hash_map<std::pair<int, int64_t>, int> mp_integer_literals_;
// Extraction info used in the projection of the model onto the extracted
// variables.
// By convention, we always associate the MPVariableProto with both the
// positive and the negative SAT variable.
util_intops::StrongVector<IntegerVariable, int> sat_var_to_mp_var_;
// The list of <positive sat var, mp var proto> created during the
// ExtractVariable() method.
std::vector<std::pair<IntegerVariable, MPVariableProto*>>
extracted_variables_info_;
int num_extracted_at_most_ones_ = 0;
std::vector<std::pair<int, MPConstraintProto*>> linear_extract_info_;
// Normalized objective definition. All coefficients are positive.
std::vector<IntegerVariable> normalized_objective_variables_;
std::vector<IntegerValue> normalized_objective_coefficients_;
// Temporary vector to store cores.
std::vector<std::vector<Literal>> temp_cores_;
};
} // namespace sat
} // namespace operations_research
#endif // OR_TOOLS_SAT_MAX_HS_H_