20#include "absl/log/check.h"
21#include "absl/random/bit_gen_ref.h"
24#include "ortools/glop/parameters.pb.h"
48 absl::BitGenRef random)
50 objective_(objective),
52 variables_info_(variables_info),
53 basis_factorization_(basis_factorization),
57 must_refactorize_basis_(false),
58 recompute_basic_objective_left_inverse_(true),
59 recompute_basic_objective_(true),
60 recompute_reduced_costs_(true),
61 are_reduced_costs_precise_(false),
62 are_reduced_costs_recomputed_(false),
65 basic_objective_left_inverse_(),
66 dual_feasibility_tolerance_() {}
69 return must_refactorize_basis_;
75 if (recompute_basic_objective_) {
76 ComputeBasicObjective();
78 const Fractional old_reduced_cost = reduced_costs_[entering_col];
80 objective_[entering_col] + cost_perturbations_[entering_col] -
84 reduced_costs_[entering_col] = precise_reduced_cost;
92 if (!recompute_reduced_costs_) {
93 const Fractional estimated_reduced_costs_accuracy =
94 old_reduced_cost - precise_reduced_cost;
96 (std::abs(precise_reduced_cost) <= 1.0) ? 1.0 : precise_reduced_cost;
97 stats_.reduced_costs_accuracy.
Add(estimated_reduced_costs_accuracy / scale);
98 if (std::abs(estimated_reduced_costs_accuracy) / scale >
99 parameters_.recompute_reduced_costs_threshold()) {
100 VLOG(1) <<
"Recomputing reduced costs, value = " << precise_reduced_cost
102 << std::abs(precise_reduced_cost - old_reduced_cost);
107 return precise_reduced_cost;
113 const RowIndex num_rows = matrix_.
num_rows();
115 for (RowIndex
row(0);
row < num_rows; ++
row) {
116 const ColIndex basic_col = basis_[
row];
118 objective_[basic_col] + cost_perturbations_[basic_col] -
120 dual_residual_error = std::max(dual_residual_error, std::abs(residual));
122 return dual_residual_error;
136 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
137 (can_decrease.
IsSet(
col) && rc > 0.0)) {
138 maximum_dual_infeasibility =
139 std::max(maximum_dual_infeasibility, std::abs(rc));
142 return maximum_dual_infeasibility;
156 if (is_boxed[
col])
continue;
158 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
159 (can_decrease.
IsSet(
col) && rc > 0.0)) {
160 maximum_dual_infeasibility =
161 std::max(maximum_dual_infeasibility, std::abs(rc));
164 return maximum_dual_infeasibility;
178 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
179 (can_decrease.
IsSet(
col) && rc > 0.0)) {
180 dual_infeasibility_sum += std::abs(std::abs(rc));
183 return dual_infeasibility_sum;
187 RowIndex leaving_row,
191 const ColIndex leaving_col = basis_[leaving_row];
196 if (!recompute_reduced_costs_) {
197 UpdateReducedCosts(entering_col, leaving_col, leaving_row,
198 direction[leaving_row], update_row);
203 UpdateBasicObjective(entering_col, leaving_row);
209 DCHECK_EQ(current_cost, &objective_[
col]);
210 reduced_costs_[
col] -= objective_[
col];
220 recompute_basic_objective_ =
true;
221 recompute_basic_objective_left_inverse_ =
true;
222 are_reduced_costs_precise_ =
false;
223 SetRecomputeReducedCostsAndNotifyWatchers();
228 recompute_basic_objective_ =
true;
229 recompute_basic_objective_left_inverse_ =
true;
234 if (are_reduced_costs_precise_)
return;
235 must_refactorize_basis_ =
true;
236 recompute_basic_objective_left_inverse_ =
true;
237 SetRecomputeReducedCostsAndNotifyWatchers();
242 VLOG(1) <<
"Perturbing the costs ... ";
245 const ColIndex structural_size =
247 for (ColIndex
col(0);
col < structural_size; ++
col) {
249 std::max(max_cost_magnitude, std::abs(objective_[
col]));
253 for (ColIndex
col(0);
col < structural_size; ++
col) {
256 (1.0 + std::uniform_real_distribution<double>()(random_)) *
257 (parameters_.relative_cost_perturbation() * std::abs(objective) +
258 parameters_.relative_max_cost_perturbation() * max_cost_magnitude);
259 DCHECK_GE(magnitude, 0.0);
270 cost_perturbations_[
col] = magnitude;
273 cost_perturbations_[
col] = -magnitude;
281 if (objective > 0.0) {
282 cost_perturbations_[
col] = magnitude;
283 }
else if (objective < 0.0) {
284 cost_perturbations_[
col] = -magnitude;
298 parameters_.degenerate_ministep_factor() * dual_feasibility_tolerance_;
299 if (increasing_rc_is_needed && reduced_costs_[
col] <= -minimum_delta)
return;
300 if (!increasing_rc_is_needed && reduced_costs_[
col] >= minimum_delta)
return;
303 increasing_rc_is_needed ? minimum_delta : -minimum_delta;
305 cost_perturbations_[
col] -= reduced_costs_[
col] +
delta;
307 has_cost_shift_ =
true;
312 if (increasing_rc_is_needed && reduced_costs_[
col] >= 0.0)
return true;
313 if (!increasing_rc_is_needed && reduced_costs_[
col] <= 0.0)
return true;
319 has_cost_shift_ =
false;
321 recompute_basic_objective_ =
true;
322 recompute_basic_objective_left_inverse_ =
true;
323 are_reduced_costs_precise_ =
false;
324 SetRecomputeReducedCostsAndNotifyWatchers();
329 if (!are_reduced_costs_recomputed_) {
330 SetRecomputeReducedCostsAndNotifyWatchers();
338 must_refactorize_basis_ =
false;
340 if (recompute_reduced_costs_) {
341 ComputeReducedCosts();
348 ComputeBasicObjectiveLeftInverse();
352void ReducedCosts::ComputeBasicObjective() {
356 basic_objective_.
resize(num_cols_in_basis, 0.0);
357 for (ColIndex
col(0);
col < num_cols_in_basis; ++
col) {
359 basic_objective_[
col] =
360 objective_[basis_col] + cost_perturbations_[basis_col];
362 recompute_basic_objective_ =
false;
363 recompute_basic_objective_left_inverse_ =
true;
366void ReducedCosts::ComputeReducedCosts() {
368 if (recompute_basic_objective_left_inverse_) {
369 ComputeBasicObjectiveLeftInverse();
372 const ColIndex num_cols = matrix_.
num_cols();
374 reduced_costs_.
resize(num_cols, 0.0);
377 const int num_omp_threads = parameters_.num_omp_threads();
379 const int num_omp_threads = 1;
381 if (num_omp_threads == 1) {
382 for (ColIndex
col(0);
col < num_cols; ++
col) {
383 reduced_costs_[
col] = objective_[
col] + cost_perturbations_[
col] -
385 col, basic_objective_left_inverse_.
values);
388 if (is_basic.IsSet(
col)) {
389 dual_residual_error =
390 std::max(dual_residual_error, std::abs(reduced_costs_[
col]));
397 std::vector<Fractional> thread_local_dual_residual_error(num_omp_threads,
399 const int parallel_loop_size = num_cols.value();
400#pragma omp parallel for num_threads(num_omp_threads)
401 for (
int i = 0;
i < parallel_loop_size;
i++) {
402 const ColIndex
col(i);
403 reduced_costs_[
col] = objective_[
col] + objective_perturbation_[
col] -
405 col, basic_objective_left_inverse_.
values);
407 if (is_basic.IsSet(
col)) {
408 thread_local_dual_residual_error[omp_get_thread_num()] =
409 std::max(thread_local_dual_residual_error[omp_get_thread_num()],
410 std::abs(reduced_costs_[
col]));
414 for (
int i = 0;
i < num_omp_threads;
i++) {
415 dual_residual_error =
416 std::max(dual_residual_error, thread_local_dual_residual_error[i]);
421 deterministic_time_ +=
423 recompute_reduced_costs_ =
false;
424 are_reduced_costs_recomputed_ =
true;
425 are_reduced_costs_precise_ = basis_factorization_.
IsRefactorized();
432 dual_feasibility_tolerance_ = parameters_.dual_feasibility_tolerance();
433 if (dual_residual_error > dual_feasibility_tolerance_) {
434 VLOG(2) <<
"Changing dual_feasibility_tolerance to " << dual_residual_error;
435 dual_feasibility_tolerance_ = dual_residual_error;
439void ReducedCosts::ComputeBasicObjectiveLeftInverse() {
441 if (recompute_basic_objective_) {
442 ComputeBasicObjective();
444 basic_objective_left_inverse_.
values = basic_objective_;
445 basic_objective_left_inverse_.
non_zeros.clear();
446 basis_factorization_.
LeftSolve(&basic_objective_left_inverse_);
447 recompute_basic_objective_left_inverse_ =
false;
458void ReducedCosts::UpdateReducedCosts(ColIndex entering_col,
459 ColIndex leaving_col,
461 UpdateRow* update_row) {
462 DCHECK_NE(entering_col, leaving_col);
463 DCHECK_NE(pivot, 0.0);
464 if (recompute_reduced_costs_)
return;
467 const Fractional entering_reduced_cost = reduced_costs_[entering_col];
471 if (entering_reduced_cost == 0.0) {
472 VLOG(2) <<
"Reduced costs didn't change.";
478 are_reduced_costs_precise_ =
false;
482 are_reduced_costs_recomputed_ =
false;
483 are_reduced_costs_precise_ =
false;
484 update_row->ComputeUpdateRow(leaving_row);
491 const Fractional new_leaving_reduced_cost = entering_reduced_cost / -pivot;
492 auto rc = reduced_costs_.
view();
493 auto update_coeffs = update_row->GetCoefficients().const_view();
494 for (
const ColIndex
col : update_row->GetNonZeroPositions()) {
495 rc[
col] += new_leaving_reduced_cost * update_coeffs[
col];
497 rc[leaving_col] = new_leaving_reduced_cost;
502 rc[entering_col] = 0.0;
509 const Fractional tolerance = dual_feasibility_tolerance_;
510 return (can_increase.
IsSet(
col) && (reduced_cost < -tolerance)) ||
511 (can_decrease.
IsSet(
col) && (reduced_cost > tolerance));
514void ReducedCosts::UpdateBasicObjective(ColIndex entering_col,
515 RowIndex leaving_row) {
518 objective_[entering_col] + cost_perturbations_[entering_col];
519 recompute_basic_objective_left_inverse_ =
true;
522void ReducedCosts::SetRecomputeReducedCostsAndNotifyWatchers() {
523 recompute_reduced_costs_ =
true;
524 for (
bool* watcher : watchers_) *watcher =
true;
532 variables_info_(variables_info),
533 primal_edge_norms_(primal_edge_norms),
534 reduced_costs_(reduced_costs) {
542 if (recompute_)
return;
547 UpdateEnteringCandidates<
false>(
552 prices_.
Remove(entering_col);
556 if (recompute_)
return;
561 DCHECK_NE(0.0, squared_norms[
col]);
571 if (recompute_)
return;
581 UpdateEnteringCandidates<
true>(
593template <
bool from_clean_state,
typename ColumnsToUpdate>
594void PrimalPrices::UpdateEnteringCandidates(
const ColumnsToUpdate& cols) {
596 const DenseBitRow::ConstView can_decrease =
598 const DenseBitRow::ConstView can_increase =
603 for (
const ColIndex
col : cols) {
612 col, reduced_cost > tolerance, can_decrease, reduced_cost < -tolerance,
614 if (is_dual_infeasible) {
620 if (!from_clean_state) prices_.
Remove(
col);
bool IsSet(IndexType i) const
Returns true if the bit at position i is set.
static uint64_t ConditionalXorOfTwoBits(IndexType i, uint64_t use1, Bitset64< IndexType >::ConstView set1, uint64_t use2, Bitset64< IndexType >::ConstView set2)
ConstView const_view() const
bool IsRefactorized() const
Returns true if the factorization was just recomputed.
void LeftSolve(ScatteredRow *y) const
Left solves the system y.B = rhs, where y initially contains rhs.
EntryIndex num_entries() const
Returns the matrix dimensions. See same functions in SparseMatrix.
RowIndex num_rows() const
ColIndex num_cols() const
Fractional ColumnScalarProduct(ColIndex col, const DenseRow &vector) const
void ClearAndResize(Index n)
void Remove(Index position)
Removes the given index from the set of candidates.
void AddOrUpdate(Index position, Fractional value)
DenseRow::ConstView GetSquaredNorms()
void AddRecomputationWatcher(bool *watcher)
void RecomputePriceAt(ColIndex col)
Triggers a recomputation of the price at the given column only.
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)
ColIndex GetBestEnteringColumn()
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables()
Fractional ComputeMaximumDualInfeasibility()
Fractional GetDualFeasibilityTolerance() const
Returns the current dual feasibility tolerance.
void SetParameters(const GlopParameters ¶meters)
Sets the pricing parameters. This does not change the pricing rule.
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)
bool NeedsBasisRefactorization() const
Fractional ComputeMaximumDualResidual()
Fractional TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction)
bool IsValidPrimalEnteringCandidate(ColIndex col) const
Does basic checking of an entering candidate.
void UpdateDataOnBasisPermutation()
Invalidates the data that depends on the order of the column in basis_.
void ClearAndRemoveCostShifts()
ReducedCosts(const CompactSparseMatrix &matrix_, const DenseRow &objective, const RowToColMapping &basis, const VariablesInfo &variables_info, const BasisFactorization &basis_factorization, absl::BitGenRef random)
Takes references to the linear program data we need.
const DenseColumn & GetDualValues()
Returns the dual values associated to the current basis.
void AddRecomputationWatcher(bool *watcher)
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
DenseRow::ConstView GetFullReducedCosts()
DenseRow::ConstView GetReducedCosts()
void MakeReducedCostsPrecise()
Fractional ComputeSumOfDualInfeasibilities()
void ResetForNewObjective()
Invalidates all internal structure that depends on the objective function.
void AssignToZero(IntType size)
ConstView const_view() const
void resize(IntType size)
absl::Span< const ColIndex > GetNonZeroPositions() const
const DenseBitRow & GetCanDecreaseBitRow() const
const DenseBitRow & GetCanIncreaseBitRow() const
const VariableStatusRow & GetStatusRow() const
const VariableTypeRow & GetTypeRow() const
const DenseBitRow & GetNotBasicBitRow() const
const DenseBitRow & GetIsRelevantBitRow() const
const DenseBitRow & GetNonBasicBoxedVariables() const
const DenseBitRow & GetIsBasicBitRow() const
double Density(const DenseRow &row)
Fractional Square(Fractional f)
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
Bitset64< ColIndex > DenseBitRow
Row of bits.
const DenseRow & Transpose(const DenseColumn &col)
Similar comment as the other Transpose() implementation above.
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
VariableType
Different types of variables.
@ UPPER_AND_LOWER_BOUNDED
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
static double DeterministicTimeForFpOperations(int64_t n)
In SWIG mode, we don't want anything besides these top-level includes.
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
StrictITIVector< Index, Fractional > values
std::vector< Index > non_zeros