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_(),
51 is_dual_infeasible_(),
52 are_dual_infeasible_positions_maintained_(false) {}
55 return must_refactorize_basis_;
62 if (recompute_basic_objective_) {
63 ComputeBasicObjective();
65 const Fractional old_reduced_cost = reduced_costs_[entering_col];
67 objective_[entering_col] + cost_perturbations_[entering_col] -
71 reduced_costs_[entering_col] = precise_reduced_cost;
72 *reduced_cost = precise_reduced_cost;
73 if (are_dual_infeasible_positions_maintained_) {
74 is_dual_infeasible_.
Set(entering_col,
78 if (!is_dual_infeasible_.
IsSet(entering_col)) {
83 if (!are_reduced_costs_precise_) {
96 if (!recompute_reduced_costs_) {
97 const Fractional estimated_reduced_costs_accuracy =
98 old_reduced_cost - precise_reduced_cost;
100 (std::abs(precise_reduced_cost) <= 1.0) ? 1.0 : precise_reduced_cost;
101 stats_.reduced_costs_accuracy.Add(estimated_reduced_costs_accuracy / scale);
102 if (std::abs(estimated_reduced_costs_accuracy) / scale >
103 parameters_.recompute_reduced_costs_threshold()) {
104 VLOG(1) <<
"Recomputing reduced costs, value = " << precise_reduced_cost
106 << std::abs(precise_reduced_cost - old_reduced_cost);
115 DCHECK(!recompute_reduced_costs_);
116 if (recompute_reduced_costs_)
return 0.0;
120 const RowIndex num_rows = matrix_.
num_rows();
123 for (RowIndex
row(0);
row < num_rows; ++
row) {
125 const ColIndex slack_col = first_slack_col + row_as_col;
126 dual_values[row_as_col] = objective_[slack_col] +
127 cost_perturbations_[slack_col] -
128 reduced_costs_[slack_col];
131 for (RowIndex
row(0);
row < num_rows; ++
row) {
132 const ColIndex basic_col = basis_[
row];
134 objective_[basic_col] + cost_perturbations_[basic_col] -
136 dual_residual_error =
std::max(dual_residual_error, std::abs(residual));
138 return dual_residual_error;
143 DCHECK(!recompute_reduced_costs_);
144 if (recompute_reduced_costs_)
return 0.0;
150 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
151 (can_decrease.
IsSet(
col) && rc > 0.0)) {
152 maximum_dual_infeasibility =
153 std::max(maximum_dual_infeasibility, std::abs(rc));
156 return maximum_dual_infeasibility;
161 DCHECK(!recompute_reduced_costs_);
162 if (recompute_reduced_costs_)
return 0.0;
168 if ((can_increase.
IsSet(
col) && rc < 0.0) ||
169 (can_decrease.
IsSet(
col) && rc > 0.0)) {
170 dual_infeasibility_sum += std::abs(std::abs(rc));
173 return dual_infeasibility_sum;
177 RowIndex leaving_row,
181 const ColIndex leaving_col = basis_[leaving_row];
185 if (are_dual_infeasible_positions_maintained_) {
186 is_dual_infeasible_.
Clear(entering_col);
188 UpdateReducedCosts(entering_col, leaving_col, leaving_row,
189 direction[leaving_row], update_row);
190 if (are_dual_infeasible_positions_maintained_) {
197 UpdateBasicObjective(entering_col, leaving_row);
210 reduced_costs_[
col] -= objective_[
col];
220 recompute_basic_objective_ =
true;
221 recompute_basic_objective_left_inverse_ =
true;
222 recompute_reduced_costs_ =
true;
223 are_reduced_costs_precise_ =
false;
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 recompute_reduced_costs_ =
true;
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);
265 case VariableType::UNCONSTRAINED:
269 case VariableType::LOWER_BOUNDED:
270 cost_perturbations_[
col] = magnitude;
272 case VariableType::UPPER_BOUNDED:
273 cost_perturbations_[
col] = -magnitude;
275 case VariableType::UPPER_AND_LOWER_BOUNDED:
281 if (objective > 0.0) {
282 cost_perturbations_[
col] = magnitude;
283 }
else if (objective < 0.0) {
284 cost_perturbations_[
col] = -magnitude;
293 const Fractional kToleranceFactor = parameters_.degenerate_ministep_factor();
295 dual_feasibility_tolerance_ *
296 (reduced_costs_[
col] > 0.0 ? kToleranceFactor : -kToleranceFactor);
298 cost_perturbations_[
col] -= reduced_costs_[
col] + small_step;
299 reduced_costs_[
col] = -small_step;
305 recompute_basic_objective_ =
true;
306 recompute_basic_objective_left_inverse_ =
true;
307 recompute_reduced_costs_ =
true;
308 are_reduced_costs_precise_ =
false;
312 are_dual_infeasible_positions_maintained_ = maintain;
313 if (are_dual_infeasible_positions_maintained_ && !recompute_reduced_costs_) {
314 ResetDualInfeasibilityBitSet();
320 RecomputeReducedCostsAndPrimalEnteringCandidatesIfNeeded();
321 return reduced_costs_;
326 ComputeBasicObjectiveLeftInverse();
330 void ReducedCosts::RecomputeReducedCostsAndPrimalEnteringCandidatesIfNeeded() {
332 must_refactorize_basis_ =
false;
334 if (recompute_reduced_costs_) {
335 ComputeReducedCosts();
336 if (are_dual_infeasible_positions_maintained_) {
337 ResetDualInfeasibilityBitSet();
342 void ReducedCosts::ComputeBasicObjective() {
346 basic_objective_.
resize(num_cols_in_basis, 0.0);
347 for (ColIndex
col(0);
col < num_cols_in_basis; ++
col) {
349 basic_objective_[
col] =
350 objective_[basis_col] + cost_perturbations_[basis_col];
352 recompute_basic_objective_ =
false;
353 recompute_basic_objective_left_inverse_ =
true;
356 void ReducedCosts::ComputeReducedCosts() {
358 if (recompute_basic_objective_left_inverse_) {
359 ComputeBasicObjectiveLeftInverse();
362 const ColIndex num_cols = matrix_.
num_cols();
364 reduced_costs_.
resize(num_cols, 0.0);
367 const int num_omp_threads = parameters_.num_omp_threads();
369 const int num_omp_threads = 1;
371 if (num_omp_threads == 1) {
372 for (ColIndex
col(0);
col < num_cols; ++
col) {
373 reduced_costs_[
col] = objective_[
col] + cost_perturbations_[
col] -
375 col, basic_objective_left_inverse_.
values);
378 if (is_basic.IsSet(
col)) {
379 dual_residual_error =
380 std::max(dual_residual_error, std::abs(reduced_costs_[
col]));
387 std::vector<Fractional> thread_local_dual_residual_error(num_omp_threads,
389 const int parallel_loop_size = num_cols.value();
390 #pragma omp parallel for num_threads(num_omp_threads)
391 for (
int i = 0; i < parallel_loop_size; i++) {
392 const ColIndex
col(i);
393 reduced_costs_[
col] = objective_[
col] + objective_perturbation_[
col] -
395 col, basic_objective_left_inverse_.
values);
397 if (is_basic.IsSet(
col)) {
398 thread_local_dual_residual_error[omp_get_thread_num()] =
399 std::max(thread_local_dual_residual_error[omp_get_thread_num()],
400 std::abs(reduced_costs_[
col]));
404 for (
int i = 0; i < num_omp_threads; i++) {
405 dual_residual_error =
406 std::max(dual_residual_error, thread_local_dual_residual_error[i]);
411 recompute_reduced_costs_ =
false;
412 are_reduced_costs_recomputed_ =
true;
413 are_reduced_costs_precise_ = basis_factorization_.
IsRefactorized();
420 dual_feasibility_tolerance_ = parameters_.dual_feasibility_tolerance();
421 if (dual_residual_error > dual_feasibility_tolerance_) {
422 VLOG(2) <<
"Changing dual_feasibility_tolerance to " << dual_residual_error;
423 dual_feasibility_tolerance_ = dual_residual_error;
427 void ReducedCosts::ComputeBasicObjectiveLeftInverse() {
429 if (recompute_basic_objective_) {
430 ComputeBasicObjective();
432 basic_objective_left_inverse_.
values = basic_objective_;
433 basic_objective_left_inverse_.
non_zeros.clear();
434 basis_factorization_.
LeftSolve(&basic_objective_left_inverse_);
435 recompute_basic_objective_left_inverse_ =
false;
446 void ReducedCosts::UpdateReducedCosts(ColIndex entering_col,
447 ColIndex leaving_col,
449 UpdateRow* update_row) {
452 if (recompute_reduced_costs_)
return;
455 const Fractional entering_reduced_cost = reduced_costs_[entering_col];
459 if (entering_reduced_cost == 0.0) {
460 VLOG(2) <<
"Reduced costs didn't change.";
466 are_reduced_costs_precise_ =
false;
470 are_reduced_costs_recomputed_ =
false;
471 update_row->ComputeUpdateRow(leaving_row);
474 const ColIndex first_slack_col =
481 const Fractional new_leaving_reduced_cost = entering_reduced_cost / -pivot;
482 for (
const ColIndex
col : update_row->GetNonZeroPositions()) {
484 if (
col >= first_slack_col)
break;
486 reduced_costs_[
col] += new_leaving_reduced_cost * coeff;
488 are_reduced_costs_precise_ =
false;
492 const ScatteredRow& unit_row_left_inverse =
493 update_row->GetUnitRowLeftInverse();
494 if (unit_row_left_inverse.non_zeros.empty()) {
495 const ColIndex num_cols = unit_row_left_inverse.values.size();
496 for (ColIndex
col(0);
col < num_cols; ++
col) {
497 const ColIndex slack_col = first_slack_col +
col;
499 reduced_costs_[slack_col] += new_leaving_reduced_cost * coeff;
502 for (
const ColIndex
col : unit_row_left_inverse.non_zeros) {
503 const ColIndex slack_col = first_slack_col +
col;
505 reduced_costs_[slack_col] += new_leaving_reduced_cost * coeff;
508 reduced_costs_[leaving_col] = new_leaving_reduced_cost;
513 reduced_costs_[entering_col] = 0.0;
520 const Fractional tolerance = dual_feasibility_tolerance_;
521 return (can_increase.
IsSet(
col) && (reduced_cost < -tolerance)) ||
522 (can_decrease.
IsSet(
col) && (reduced_cost > tolerance));
525 void ReducedCosts::ResetDualInfeasibilityBitSet() {
527 const ColIndex num_cols = matrix_.
num_cols();
543 template <
typename ColumnsToUpdate>
544 void ReducedCosts::UpdateEnteringCandidates(
const ColumnsToUpdate& cols) {
546 const Fractional tolerance = dual_feasibility_tolerance_;
549 for (
const ColIndex
col : cols) {
552 col, can_decrease, reduced_cost > tolerance, can_increase,
553 reduced_cost < -tolerance);
559 void ReducedCosts::UpdateBasicObjective(ColIndex entering_col,
560 RowIndex leaving_row) {
563 objective_[entering_col] + cost_perturbations_[entering_col];
564 recompute_basic_objective_left_inverse_ =
true;