26#include "absl/log/check.h"
27#include "absl/strings/str_cat.h"
28#include "absl/types/span.h"
36#include "ortools/sat/sat_parameters.pb.h"
46 return std::max(1e-6, down_branch) * std::max(1e-6, up_branch);
48 const double min_value = std::min(up_branch, down_branch);
49 const double max_value = std::max(up_branch, down_branch);
50 const double mu = 1.0 / 6.0;
51 return (1.0 - mu) * min_value + mu * max_value;
55std::string PseudoCosts::ObjectiveInfo::DebugString()
const {
56 return absl::StrCat(
"lb: ", lb,
" ub:", ub,
" lp_bound:", lp_bound);
60 : parameters_(*
model->GetOrCreate<SatParameters>()),
66 pseudo_costs_.resize(num_vars);
67 is_relevant_.
resize(num_vars,
false);
68 scores_.
resize(num_vars, 0.0);
73 if (objective !=
nullptr) {
74 objective_var_ = objective->objective_var;
78PseudoCosts::ObjectiveInfo PseudoCosts::GetCurrentObjectiveInfo() {
82 result.lb = integer_trail_->
LowerBound(objective_var_);
83 result.ub = integer_trail_->
UpperBound(objective_var_);
87 result.lp_bound = 0.0;
88 result.lp_at_optimal =
true;
89 for (
const auto* lp : *lps_) {
90 if (!lp->AtOptimal()) result.lp_at_optimal =
false;
91 result.lp_bound += lp->ObjectiveLpLowerBound();
97 saved_info_ = GetCurrentObjectiveInfo();
98 return saved_info_.lp_at_optimal;
102 absl::Span<const double> lp_values) {
113 IntegerVariable
var, absl::Span<const double> lp_values) {
123 const double lp_value = lp_values[
var.value()];
124 double down_fractionality = lp_value - std::floor(lp_value);
125 IntegerValue down_target = IntegerValue(std::floor(lp_value));
127 down_fractionality = 1.0;
128 down_target = ub - 1;
129 }
else if (lp_value <=
ToDouble(lb)) {
130 down_fractionality = 0.0;
134 result.
is_integer = std::abs(lp_value - std::round(lp_value)) < 1e-6;
139 if (max_index < average_unit_objective_increase_.size()) {
142 average_unit_objective_increase_[
NegationOf(
var)].CurrentAverage();
143 result.
up_score = (1.0 - down_fractionality) *
144 average_unit_objective_increase_[
var].CurrentAverage();
147 const int reliablitity = std::min(
148 average_unit_objective_increase_[
var].NumRecords(),
149 average_unit_objective_increase_[
NegationOf(
var)].NumRecords());
157 IntegerValue objective_increase) {
158 const double relative_increase =
159 ToDouble(objective_increase) /
static_cast<double>(reason.size());
161 if (
lit.Index() >= lit_pseudo_costs_.size()) {
162 lit_pseudo_costs_.resize(
lit.Index() + 1);
164 lit_pseudo_costs_[
lit].AddData(relative_increase);
169 if (
lit.Index() >= lit_pseudo_costs_.size())
return 0.0;
171 const double down_fractionality = lp_value;
172 const double up_fractionality = 1.0 - lp_value;
173 const double up_branch =
174 up_fractionality * lit_pseudo_costs_[
lit].CurrentAverage();
175 const double down_branch =
177 lit_pseudo_costs_[
lit.NegatedIndex()].CurrentAverage();
182 const ObjectiveInfo new_info = GetCurrentObjectiveInfo();
183 const double obj_lp_diff =
184 std::max(0.0, new_info.lp_bound - saved_info_.lp_bound);
185 const IntegerValue obj_int_diff = new_info.lb - saved_info_.lb;
187 double obj_increase =
188 obj_lp_diff > 0.0 ? obj_lp_diff :
ToDouble(obj_int_diff);
198 const ObjectiveInfo new_info = GetCurrentObjectiveInfo();
206 if (saved_info_.lp_at_optimal) {
209 for (
const auto [
var, lb_change, lp_increase] : bound_changes_) {
210 if (lp_increase < 1e-6)
continue;
211 if (
var >= average_unit_objective_increase_.size()) {
212 average_unit_objective_increase_.resize(
var + 1);
214 average_unit_objective_increase_[
var].AddData(obj_increase / lp_increase);
219 if (conflict)
return;
222 const IntegerValue obj_bound_improvement =
223 (new_info.lb - saved_info_.lb) + (saved_info_.ub - new_info.ub);
224 DCHECK_GE(obj_bound_improvement, 0);
225 if (obj_bound_improvement == IntegerValue(0))
return;
227 for (
const auto [
var, lb_change, lp_increase] : bound_changes_) {
228 if (lb_change == IntegerValue(0))
continue;
230 if (
var >= pseudo_costs_.size()) {
233 is_relevant_.
resize(new_size,
false);
234 scores_.
resize(new_size, 0.0);
238 pseudo_costs_[
var].AddData(
ToDouble(obj_bound_improvement) /
242 const IntegerVariable negative_var =
NegationOf(positive_var);
243 const int64_t count = pseudo_costs_[positive_var].NumRecords() +
244 pseudo_costs_[negative_var].NumRecords();
245 if (count >= parameters_.pseudo_cost_reliability_threshold()) {
246 scores_[positive_var] =
248 if (!is_relevant_[positive_var]) {
249 is_relevant_[positive_var] =
true;
250 relevant_variables_.push_back(positive_var);
261 double best_score = -std::numeric_limits<double>::infinity();
266 for (
const IntegerVariable positive_var : relevant_variables_) {
267 const IntegerValue lb = integer_trail_->
LowerBound(positive_var);
268 const IntegerValue ub = integer_trail_->
UpperBound(positive_var);
269 if (lb >= ub)
continue;
270 if (scores_[positive_var] > best_score) {
271 chosen_var = positive_var;
272 best_score = scores_[positive_var];
285 Literal decision, absl::Span<const double> lp_values) {
286 std::vector<PseudoCosts::VariableBoundChange> bound_changes;
292 if (l.var < lp_values.size()) {
294 std::max(0.0,
ToDouble(l.bound) - lp_values[l.var.value()]);
296 bound_changes.push_back(entry);
305 bound_changes.push_back(entry);
314 bound_changes.push_back(entry);
318 return bound_changes;
Manages incremental averages.
const InlinedIntegerValueVector & GetEqualityLiterals(Literal lit) const
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
Returns the IntegerLiterals that were associated with the given Literal.
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerValue UpperBound(IntegerVariable i) const
IntegerVariable NumIntegerVariables() const
A class that stores the collection of all LP constraints in a model.
PseudoCosts(Model *model)
void AfterTakingDecision(bool conflict=false)
double CombineScores(double down_branch, double up_branch) const
Combines the score of the two branch into one score.
void UpdateBoolPseudoCosts(absl::Span< const Literal > reason, IntegerValue objective_increase)
void BeforeTakingDecision(Literal decision)
IntegerVariable GetBestDecisionVar()
Returns the variable with best reliable pseudo cost that is not fixed.
BranchingInfo EvaluateVar(IntegerVariable var, absl::Span< const double > lp_values)
double BoolPseudoCost(Literal lit, double lp_value) const
std::vector< VariableBoundChange > GetBoundChanges(Literal decision, absl::Span< const double > lp_values)
double ObjectiveIncrease(bool conflict)
void SaveBoundChanges(Literal decision, absl::Span< const double > lp_values)
double GetCost(IntegerVariable var) const
Returns the pseudo cost of given variable. Currently used for testing only.
void resize(size_type new_size)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
double ToDouble(IntegerValue value)
In SWIG mode, we don't want anything besides these top-level includes.
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
double down_fractionality
IntegerLiteral down_branch
IntegerValue lower_bound_change