32 absl::BitGenRef random)
36 variables_info_(variables_info),
37 basis_factorization_(basis_factorization),
41 must_refactorize_basis_(false),
42 recompute_basic_objective_left_inverse_(true),
43 recompute_basic_objective_(true),
44 recompute_reduced_costs_(true),
45 are_reduced_costs_precise_(false),
46 are_reduced_costs_recomputed_(false),
49 basic_objective_left_inverse_(),
50 dual_feasibility_tolerance_() {}
53 return must_refactorize_basis_;
60 if (recompute_basic_objective_) {
61 ComputeBasicObjective();
63 const Fractional old_reduced_cost = reduced_costs_[entering_col];
65 objective_[entering_col] + cost_perturbations_[entering_col] -
69 reduced_costs_[entering_col] = precise_reduced_cost;
70 *reduced_cost = precise_reduced_cost;
73 VLOG(1) <<
"Entering candidate is not valid under precise reduced costs.";
79 if (!are_reduced_costs_precise_) {
91 if (!recompute_reduced_costs_) {
92 const Fractional estimated_reduced_costs_accuracy =
93 old_reduced_cost - precise_reduced_cost;
95 (std::abs(precise_reduced_cost) <= 1.0) ? 1.0 : precise_reduced_cost;
96 stats_.reduced_costs_accuracy.Add(estimated_reduced_costs_accuracy / scale);
97 if (std::abs(estimated_reduced_costs_accuracy) / scale >
98 parameters_.recompute_reduced_costs_threshold()) {
99 VLOG(1) <<
"Recomputing reduced costs, value = " << precise_reduced_cost
101 << std::abs(precise_reduced_cost - old_reduced_cost);
110 DCHECK(!recompute_reduced_costs_);
111 if (recompute_reduced_costs_)
return 0.0;
115 const RowIndex num_rows = matrix_.
num_rows();
118 for (RowIndex
row(0);
row < num_rows; ++
row) {
120 const ColIndex slack_col = first_slack_col + row_as_col;
121 dual_values[row_as_col] = objective_[slack_col] +
122 cost_perturbations_[slack_col] -
123 reduced_costs_[slack_col];
126 for (RowIndex
row(0);
row < num_rows; ++
row) {
127 const ColIndex basic_col = basis_[
row];
129 objective_[basic_col] + cost_perturbations_[basic_col] -
131 dual_residual_error =
std::max(dual_residual_error, std::abs(residual));
133 return dual_residual_error;
138 DCHECK(!recompute_reduced_costs_);
139 if (recompute_reduced_costs_)
return 0.0;
145 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
146 (can_decrease.
IsSet(
col) && rc > 0.0)) {
147 maximum_dual_infeasibility =
148 std::max(maximum_dual_infeasibility, std::abs(rc));
151 return maximum_dual_infeasibility;
162 if (is_boxed[
col])
continue;
164 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
165 (can_decrease.
IsSet(
col) && rc > 0.0)) {
166 maximum_dual_infeasibility =
167 std::max(maximum_dual_infeasibility, std::abs(rc));
170 return maximum_dual_infeasibility;
175 DCHECK(!recompute_reduced_costs_);
176 if (recompute_reduced_costs_)
return 0.0;
182 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
183 (can_decrease.
IsSet(
col) && rc > 0.0)) {
184 dual_infeasibility_sum += std::abs(std::abs(rc));
187 return dual_infeasibility_sum;
191 RowIndex leaving_row,
195 const ColIndex leaving_col = basis_[leaving_row];
200 if (!recompute_reduced_costs_) {
201 UpdateReducedCosts(entering_col, leaving_col, leaving_row,
202 direction[leaving_row], update_row);
207 UpdateBasicObjective(entering_col, leaving_row);
214 reduced_costs_[
col] -= objective_[
col];
224 recompute_basic_objective_ =
true;
225 recompute_basic_objective_left_inverse_ =
true;
226 are_reduced_costs_precise_ =
false;
227 SetRecomputeReducedCostsAndNotifyWatchers();
232 recompute_basic_objective_ =
true;
233 recompute_basic_objective_left_inverse_ =
true;
238 if (are_reduced_costs_precise_)
return;
239 must_refactorize_basis_ =
true;
240 recompute_basic_objective_left_inverse_ =
true;
241 SetRecomputeReducedCostsAndNotifyWatchers();
246 VLOG(1) <<
"Perturbing the costs ... ";
249 const ColIndex structural_size =
251 for (ColIndex
col(0);
col < structural_size; ++
col) {
253 std::max(max_cost_magnitude, std::abs(objective_[
col]));
257 for (ColIndex
col(0);
col < structural_size; ++
col) {
260 (1.0 + std::uniform_real_distribution<double>()(random_)) *
261 (parameters_.relative_cost_perturbation() * std::abs(objective) +
262 parameters_.relative_max_cost_perturbation() * max_cost_magnitude);
274 cost_perturbations_[
col] = magnitude;
277 cost_perturbations_[
col] = -magnitude;
285 if (objective > 0.0) {
286 cost_perturbations_[
col] = magnitude;
287 }
else if (objective < 0.0) {
288 cost_perturbations_[
col] = -magnitude;
302 parameters_.degenerate_ministep_factor() * dual_feasibility_tolerance_;
303 if (increasing_rc_is_needed && reduced_costs_[
col] <= -minimum_delta)
return;
304 if (!increasing_rc_is_needed && reduced_costs_[
col] >= minimum_delta)
return;
307 increasing_rc_is_needed ? minimum_delta : -minimum_delta;
309 cost_perturbations_[
col] -= reduced_costs_[
col] +
delta;
311 has_cost_shift_ =
true;
316 if (increasing_rc_is_needed && reduced_costs_[
col] >= 0.0)
return true;
317 if (!increasing_rc_is_needed && reduced_costs_[
col] <= 0.0)
return true;
323 has_cost_shift_ =
false;
325 recompute_basic_objective_ =
true;
326 recompute_basic_objective_left_inverse_ =
true;
327 are_reduced_costs_precise_ =
false;
328 SetRecomputeReducedCostsAndNotifyWatchers();
333 if (!are_reduced_costs_recomputed_) {
334 SetRecomputeReducedCostsAndNotifyWatchers();
342 must_refactorize_basis_ =
false;
344 if (recompute_reduced_costs_) {
345 ComputeReducedCosts();
347 return reduced_costs_;
352 ComputeBasicObjectiveLeftInverse();
356 void ReducedCosts::ComputeBasicObjective() {
360 basic_objective_.
resize(num_cols_in_basis, 0.0);
361 for (ColIndex
col(0);
col < num_cols_in_basis; ++
col) {
363 basic_objective_[
col] =
364 objective_[basis_col] + cost_perturbations_[basis_col];
366 recompute_basic_objective_ =
false;
367 recompute_basic_objective_left_inverse_ =
true;
370 void ReducedCosts::ComputeReducedCosts() {
372 if (recompute_basic_objective_left_inverse_) {
373 ComputeBasicObjectiveLeftInverse();
376 const ColIndex num_cols = matrix_.
num_cols();
378 reduced_costs_.
resize(num_cols, 0.0);
381 const int num_omp_threads = parameters_.num_omp_threads();
383 const int num_omp_threads = 1;
385 if (num_omp_threads == 1) {
386 for (ColIndex
col(0);
col < num_cols; ++
col) {
387 reduced_costs_[
col] = objective_[
col] + cost_perturbations_[
col] -
389 col, basic_objective_left_inverse_.
values);
392 if (is_basic.IsSet(
col)) {
393 dual_residual_error =
394 std::max(dual_residual_error, std::abs(reduced_costs_[
col]));
401 std::vector<Fractional> thread_local_dual_residual_error(num_omp_threads,
403 const int parallel_loop_size = num_cols.value();
404 #pragma omp parallel for num_threads(num_omp_threads)
405 for (
int i = 0; i < parallel_loop_size; i++) {
406 const ColIndex
col(i);
407 reduced_costs_[
col] = objective_[
col] + objective_perturbation_[
col] -
409 col, basic_objective_left_inverse_.
values);
411 if (is_basic.IsSet(
col)) {
412 thread_local_dual_residual_error[omp_get_thread_num()] =
413 std::max(thread_local_dual_residual_error[omp_get_thread_num()],
414 std::abs(reduced_costs_[
col]));
418 for (
int i = 0; i < num_omp_threads; i++) {
419 dual_residual_error =
420 std::max(dual_residual_error, thread_local_dual_residual_error[i]);
425 deterministic_time_ +=
427 recompute_reduced_costs_ =
false;
428 are_reduced_costs_recomputed_ =
true;
429 are_reduced_costs_precise_ = basis_factorization_.
IsRefactorized();
436 dual_feasibility_tolerance_ = parameters_.dual_feasibility_tolerance();
437 if (dual_residual_error > dual_feasibility_tolerance_) {
438 VLOG(2) <<
"Changing dual_feasibility_tolerance to " << dual_residual_error;
439 dual_feasibility_tolerance_ = dual_residual_error;
443 void ReducedCosts::ComputeBasicObjectiveLeftInverse() {
445 if (recompute_basic_objective_) {
446 ComputeBasicObjective();
448 basic_objective_left_inverse_.
values = basic_objective_;
449 basic_objective_left_inverse_.
non_zeros.clear();
450 basis_factorization_.
LeftSolve(&basic_objective_left_inverse_);
451 recompute_basic_objective_left_inverse_ =
false;
462 void ReducedCosts::UpdateReducedCosts(ColIndex entering_col,
463 ColIndex leaving_col,
465 UpdateRow* update_row) {
468 if (recompute_reduced_costs_)
return;
471 const Fractional entering_reduced_cost = reduced_costs_[entering_col];
475 if (entering_reduced_cost == 0.0) {
476 VLOG(2) <<
"Reduced costs didn't change.";
482 are_reduced_costs_precise_ =
false;
486 are_reduced_costs_recomputed_ =
false;
487 update_row->ComputeUpdateRow(leaving_row);
490 const ColIndex first_slack_col =
497 const Fractional new_leaving_reduced_cost = entering_reduced_cost / -pivot;
498 for (
const ColIndex
col : update_row->GetNonZeroPositions()) {
500 if (
col >= first_slack_col)
break;
502 reduced_costs_[
col] += new_leaving_reduced_cost * coeff;
504 are_reduced_costs_precise_ =
false;
508 const ScatteredRow& unit_row_left_inverse =
509 update_row->GetUnitRowLeftInverse();
510 if (unit_row_left_inverse.non_zeros.empty()) {
511 const ColIndex num_cols = unit_row_left_inverse.values.size();
512 for (ColIndex
col(0);
col < num_cols; ++
col) {
513 const ColIndex slack_col = first_slack_col +
col;
515 reduced_costs_[slack_col] += new_leaving_reduced_cost * coeff;
518 for (
const ColIndex
col : unit_row_left_inverse.non_zeros) {
519 const ColIndex slack_col = first_slack_col +
col;
521 reduced_costs_[slack_col] += new_leaving_reduced_cost * coeff;
524 reduced_costs_[leaving_col] = new_leaving_reduced_cost;
529 reduced_costs_[entering_col] = 0.0;
536 const Fractional tolerance = dual_feasibility_tolerance_;
537 return (can_increase.
IsSet(
col) && (reduced_cost < -tolerance)) ||
538 (can_decrease.
IsSet(
col) && (reduced_cost > tolerance));
541 void ReducedCosts::UpdateBasicObjective(ColIndex entering_col,
542 RowIndex leaving_row) {
545 objective_[entering_col] + cost_perturbations_[entering_col];
546 recompute_basic_objective_left_inverse_ =
true;
549 void ReducedCosts::SetRecomputeReducedCostsAndNotifyWatchers() {
550 recompute_reduced_costs_ =
true;
551 for (
bool* watcher : watchers_) *watcher =
true;
559 variables_info_(variables_info),
560 primal_edge_norms_(primal_edge_norms),
561 reduced_costs_(reduced_costs) {
569 if (recompute_)
return;
574 UpdateEnteringCandidates<
false>(
579 if (recompute_)
return;
593 if (recompute_)
return;
603 UpdateEnteringCandidates<
true>(
615 template <
bool from_clean_state,
typename ColumnsToUpdate>
616 void PrimalPrices::UpdateEnteringCandidates(
const ColumnsToUpdate& cols) {
622 for (
const ColIndex
col : cols) {
631 col, reduced_cost > tolerance, can_decrease, reduced_cost < -tolerance,
633 if (is_dual_infeasible) {
639 if (!from_clean_state) prices_.
Remove(
col);
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
bool IsSet(IndexType i) const
static uint64_t ConditionalXorOfTwoBits(IndexType i, uint64_t use1, const Bitset64< IndexType > &set1, uint64_t use2, const Bitset64< IndexType > &set2)
bool IsRefactorized() const
void LeftSolve(ScatteredRow *y) const
ColIndex num_cols() const
RowIndex num_rows() const
Fractional ColumnScalarProduct(ColIndex col, const DenseRow &vector) const
EntryIndex num_entries() const
void AddOrUpdate(Index position, Fractional value)
void Remove(Index position)
void ClearAndResize(Index n)
const DenseRow & GetSquaredNorms()
void AddRecomputationWatcher(bool *watcher)
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
void UpdateBeforeBasisPivot(ColIndex entering_col, UpdateRow *update_row)
PrimalPrices(absl::BitGenRef random, const VariablesInfo &variables_info, PrimalEdgeNorms *primal_edge_norms, ReducedCosts *reduced_costs)
void RecomputePriceAt(ColIndex col)
ColIndex GetBestEnteringColumn()
ReducedCosts(const CompactSparseMatrix &matrix_, const DenseRow &objective, const RowToColMapping &basis, const VariablesInfo &variables_info, const BasisFactorization &basis_factorization, absl::BitGenRef random)
void AddRecomputationWatcher(bool *watcher)
void ResetForNewObjective()
Fractional ComputeMaximumDualResidual() const
void MakeReducedCostsPrecise()
bool TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction, Fractional *reduced_cost)
bool IsValidPrimalEnteringCandidate(ColIndex col) const
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables() const
const DenseRow & GetFullReducedCosts()
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
const DenseRow & GetReducedCosts()
Fractional GetDualFeasibilityTolerance() const
Fractional ComputeMaximumDualInfeasibility() const
const DenseColumn & GetDualValues()
void ClearAndRemoveCostShifts()
void UpdateDataOnBasisPermutation()
Fractional ComputeSumOfDualInfeasibilities() const
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
void SetParameters(const GlopParameters ¶meters)
bool NeedsBasisRefactorization() const
void AssignToZero(IntType size)
void resize(IntType size)
const ColIndexVector & GetNonZeroPositions() const
const DenseBitRow & GetIsBasicBitRow() const
const DenseBitRow & GetNonBasicBoxedVariables() const
const DenseBitRow & GetCanIncreaseBitRow() const
const DenseBitRow & GetCanDecreaseBitRow() const
const VariableTypeRow & GetTypeRow() const
const DenseBitRow & GetNotBasicBitRow() const
const VariableStatusRow & GetStatusRow() const
const DenseBitRow & GetIsRelevantBitRow() const
Fractional Square(Fractional f)
@ UPPER_AND_LOWER_BOUNDED
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
double Density(const DenseRow &row)
ColIndex RowToColIndex(RowIndex row)
const DenseRow & Transpose(const DenseColumn &col)
Bitset64< ColIndex > DenseBitRow
RowIndex ColToRowIndex(ColIndex col)
static double DeterministicTimeForFpOperations(int64_t n)
Collection of objects used to extend the Constraint Solver library.
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
std::vector< Index > non_zeros
StrictITIVector< Index, Fractional > values