Skip to content

Commit

Permalink
sat: Export from google3
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Oct 7, 2024
1 parent 59d9c4a commit b9c5e21
Show file tree
Hide file tree
Showing 29 changed files with 1,415 additions and 194 deletions.
27 changes: 27 additions & 0 deletions ortools/base/parse_text_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#ifndef OR_TOOLS_BASE_PARSE_TEXT_PROTO_H_
#define OR_TOOLS_BASE_PARSE_TEXT_PROTO_H_

#include <string_view>

#include "absl/log/absl_check.h"
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
Expand All @@ -32,6 +34,31 @@ T ParseTextOrDie(const std::string& input) {
return result;
}

namespace text_proto_internal {

class ParseProtoHelper {
public:
explicit ParseProtoHelper(std::string_view asciipb) : asciipb_(asciipb) {}
template <class T>
operator T() { // NOLINT(runtime/explicit)
T result;
const bool ok = ::google::protobuf::TextFormat::TextFormat::ParseFromString(
asciipb_, &result);
CHECK(ok) << "Failed to parse text proto: " << asciipb_;
return result;
}

private:
const std::string asciipb_;
};

} // namespace text_proto_internal

text_proto_internal::ParseProtoHelper ParseTextProtoOrDie(
std::string_view input) {
return text_proto_internal::ParseProtoHelper(input);
}

} // namespace google::protobuf::contrib::parse_proto

#endif // OR_TOOLS_BASE_PARSE_TEXT_PROTO_H_
109 changes: 78 additions & 31 deletions ortools/glop/revised_simplex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -154,29 +154,72 @@ void RevisedSimplex::SetStartingVariableValuesForNextSolve(
variable_starting_values_ = values;
}

void RevisedSimplex::NotifyThatMatrixIsUnchangedForNextSolve() {
notify_that_matrix_is_unchanged_ = true;
}
Status RevisedSimplex::MinimizeFromTransposedMatrixWithSlack(
const DenseRow& objective, Fractional objective_scaling_factor,
Fractional objective_offset, TimeLimit* time_limit) {
const double start_time = time_limit->GetElapsedTime();
default_logger_.EnableLogging(parameters_.log_search_progress());
default_logger_.SetLogToStdOut(parameters_.log_to_stdout());
parameters_ = initial_parameters_;
PropagateParameters();

// The source of truth is the transposed matrix.
if (transpose_was_changed_) {
compact_matrix_.PopulateFromTranspose(transposed_matrix_);
num_rows_ = compact_matrix_.num_rows();
num_cols_ = compact_matrix_.num_cols();
first_slack_col_ = num_cols_ - RowToColIndex(num_rows_);
}

DCHECK_EQ(num_cols_, objective.size());

// Copy objective
objective_scaling_factor_ = objective_scaling_factor;
objective_offset_ = objective_offset;
const bool objective_is_unchanged = objective_ == objective;
objective_ = objective;
InitializeObjectiveLimit();

// Initialize variable infos from the mutated bounds.
variables_info_.InitializeFromMutatedState();

if (objective_is_unchanged && parameters_.use_dual_simplex() &&
!transpose_was_changed_ && !solution_state_has_been_set_externally_ &&
!solution_state_.IsEmpty()) {
// Fast track if we just changed variable bounds.
primal_edge_norms_.Clear();
variables_info_.InitializeFromBasisState(first_slack_col_, ColIndex(0),
solution_state_);
variable_values_.ResetAllNonBasicVariableValues(variable_starting_values_);
variable_values_.RecomputeBasicVariableValues();
return SolveInternal(start_time, false, objective, time_limit);
} else {
GLOP_RETURN_IF_ERROR(FinishInitialization(true));
}

void RevisedSimplex::NotifyThatMatrixIsChangedForNextSolve() {
notify_that_matrix_is_unchanged_ = false;
return SolveInternal(start_time, false, objective, time_limit);
}

Status RevisedSimplex::Solve(const LinearProgram& lp, TimeLimit* time_limit) {
SCOPED_TIME_STAT(&function_stats_);
const double start_time = time_limit->GetElapsedTime();
default_logger_.EnableLogging(parameters_.log_search_progress());
default_logger_.SetLogToStdOut(parameters_.log_to_stdout());

DCHECK(lp.IsCleanedUp());
GLOP_RETURN_IF_ERROR(Initialize(lp));
return SolveInternal(start_time, lp.IsMaximizationProblem(),
lp.objective_coefficients(), time_limit);
}

ABSL_MUST_USE_RESULT Status RevisedSimplex::SolveInternal(
double start_time, bool is_maximization_problem,
const DenseRow& objective_coefficients, TimeLimit* time_limit) {
SCOPED_TIME_STAT(&function_stats_);
GLOP_RETURN_ERROR_IF_NULL(time_limit);
Cleanup update_deterministic_time_on_return(
[this, time_limit]() { AdvanceDeterministicTime(time_limit); });

default_logger_.EnableLogging(parameters_.log_search_progress());
default_logger_.SetLogToStdOut(parameters_.log_to_stdout());
SOLVER_LOG(logger_, "");

// Initialization. Note That Initialize() must be called first since it
// analyzes the current solver state.
const double start_time = time_limit->GetElapsedTime();
GLOP_RETURN_IF_ERROR(Initialize(lp));
if (logger_->LoggingIsEnabled()) {
DisplayBasicVariableStatistics();
}
Expand Down Expand Up @@ -310,7 +353,13 @@ Status RevisedSimplex::Solve(const LinearProgram& lp, TimeLimit* time_limit) {

// After the primal phase I, we need to restore the objective.
if (problem_status_ != ProblemStatus::PRIMAL_INFEASIBLE) {
InitializeObjectiveAndTestIfUnchanged(lp);
objective_ = objective_coefficients;
if (is_maximization_problem) {
for (Fractional& value : objective_) {
value = -value;
}
}
objective_.resize(num_cols_, 0.0); // For the slack.
reduced_costs_.ResetForNewObjective();
}
}
Expand Down Expand Up @@ -639,7 +688,7 @@ Status RevisedSimplex::Solve(const LinearProgram& lp, TimeLimit* time_limit) {
solution_reduced_costs_ = reduced_costs_.GetReducedCosts();
SaveState();

if (lp.IsMaximizationProblem()) {
if (is_maximization_problem) {
ChangeSign(&solution_dual_values_);
ChangeSign(&solution_reduced_costs_);
}
Expand All @@ -650,7 +699,7 @@ Status RevisedSimplex::Solve(const LinearProgram& lp, TimeLimit* time_limit) {
solution_objective_value_ =
(problem_status_ == ProblemStatus::DUAL_UNBOUNDED) ? kInfinity
: -kInfinity;
if (lp.IsMaximizationProblem()) {
if (is_maximization_problem) {
solution_objective_value_ = -solution_objective_value_;
}
}
Expand Down Expand Up @@ -1379,21 +1428,13 @@ Status RevisedSimplex::Initialize(const LinearProgram& lp) {
ColIndex num_new_cols(0);
bool only_change_is_new_rows = false;
bool only_change_is_new_cols = false;
bool matrix_is_unchanged = true;
bool only_new_bounds = false;
if (solution_state_.IsEmpty() || !notify_that_matrix_is_unchanged_) {
matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
lp, lp_is_in_equation_form, &only_change_is_new_rows,
&only_change_is_new_cols, &num_new_cols);
only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
lp, lp_is_in_equation_form, num_new_cols);
} else if (DEBUG_MODE) {
CHECK(InitializeMatrixAndTestIfUnchanged(
lp, lp_is_in_equation_form, &only_change_is_new_rows,
&only_change_is_new_cols, &num_new_cols));
}
notify_that_matrix_is_unchanged_ = false;
const bool matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
lp, lp_is_in_equation_form, &only_change_is_new_rows,
&only_change_is_new_cols, &num_new_cols);
const bool only_new_bounds =
only_change_is_new_cols && num_new_cols > 0 &&
OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
lp, lp_is_in_equation_form, num_new_cols);

// TODO(user): move objective with ReducedCosts class.
const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
Expand Down Expand Up @@ -1509,6 +1550,10 @@ Status RevisedSimplex::Initialize(const LinearProgram& lp) {
}
}

return FinishInitialization(solve_from_scratch);
}

Status RevisedSimplex::FinishInitialization(bool solve_from_scratch) {
// If we couldn't perform a "quick" warm start above, we can at least try to
// reuse the variable statuses.
if (solve_from_scratch && !solution_state_.IsEmpty()) {
Expand Down Expand Up @@ -1589,6 +1634,8 @@ Status RevisedSimplex::Initialize(const LinearProgram& lp) {
SOLVER_LOG(logger_, "Starting basis: incremental solve.");
}
DCHECK(BasisIsConsistent());

transpose_was_changed_ = false;
return Status::OK();
}

Expand Down
36 changes: 25 additions & 11 deletions ortools/glop/revised_simplex.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,6 @@ class RevisedSimplex {
// variables.
void SetStartingVariableValuesForNextSolve(const DenseRow& values);

// Advanced usage. Tells the next Solve() that the matrix inside the linear
// program will not change compared to the one used the last time Solve() was
// called. This allows to bypass the somewhat costly check of comparing both
// matrices. Note that this call will be ignored if Solve() was never called
// or if ClearStateForNextSolve() was called.
void NotifyThatMatrixIsUnchangedForNextSolve();
void NotifyThatMatrixIsChangedForNextSolve();

// Getters to retrieve all the information computed by the last Solve().
RowIndex GetProblemNumRows() const;
ColIndex GetProblemNumCols() const;
Expand Down Expand Up @@ -252,6 +244,24 @@ class RevisedSimplex {

void SetLogger(SolverLogger* logger) { logger_ = logger; }

// Advanced usage. For fast incremental call to the solver, it is better not
// to use LinearProgram at all. This api allows to directly modify the
// internal data of glop and then call solve.
const CompactSparseMatrix& MatrixWithSlack() const { return compact_matrix_; }
CompactSparseMatrix* MutableTransposedMatrixWithSlack() {
transpose_was_changed_ = true;
return &transposed_matrix_;
}
DenseRow* MutableLowerBounds() {
return variables_info_.MutableLowerBounds();
}
DenseRow* MutableUpperBounds() {
return variables_info_.MutableUpperBounds();
}
ABSL_MUST_USE_RESULT Status MinimizeFromTransposedMatrixWithSlack(
const DenseRow& objective, Fractional objective_scaling_factor,
Fractional objective_offset, TimeLimit* time_limit);

private:
struct IterationStats : public StatsGroup {
IterationStats()
Expand Down Expand Up @@ -303,6 +313,10 @@ class RevisedSimplex {
FINAL_CHECK
};

ABSL_MUST_USE_RESULT Status SolveInternal(double start_time, bool maximize,
const DenseRow& objective,
TimeLimit* time_limit);

// Propagates parameters_ to all the other classes that need it.
//
// TODO(user): Maybe a better design is for them to have a reference to a
Expand Down Expand Up @@ -427,6 +441,7 @@ class RevisedSimplex {

// Entry point for the solver initialization.
ABSL_MUST_USE_RESULT Status Initialize(const LinearProgram& lp);
ABSL_MUST_USE_RESULT Status FinishInitialization(bool solve_from_scratch);

// Saves the current variable statuses in solution_state_.
void SaveState();
Expand Down Expand Up @@ -715,9 +730,8 @@ class RevisedSimplex {
// If this is cleared, we assume they are none.
DenseRow variable_starting_values_;

// Flag used by NotifyThatMatrixIsUnchangedForNextSolve() and changing
// the behavior of Initialize().
bool notify_that_matrix_is_unchanged_ = false;
// See MutableTransposedMatrixWithSlack().
bool transpose_was_changed_ = false;

// This is known as 'd' in the literature and is set during each pivot to the
// right inverse of the basic entering column of A by ComputeDirection().
Expand Down
10 changes: 10 additions & 0 deletions ortools/glop/variables_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ bool VariablesInfo::LoadBoundsAndReturnTrueIfUnchanged(
return false;
}

void VariablesInfo::InitializeFromMutatedState() {
const ColIndex num_cols = matrix_.num_cols();
DCHECK_EQ(num_cols, lower_bounds_.size());
DCHECK_EQ(num_cols, upper_bounds_.size());
variable_type_.resize(num_cols, VariableType::UNCONSTRAINED);
for (ColIndex col(0); col < num_cols; ++col) {
variable_type_[col] = ComputeVariableType(col);
}
}

bool VariablesInfo::LoadBoundsAndReturnTrueIfUnchanged(
const DenseRow& variable_lower_bounds,
const DenseRow& variable_upper_bounds,
Expand Down
6 changes: 6 additions & 0 deletions ortools/glop/variables_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@ class VariablesInfo {
void EndDualPhaseI(Fractional dual_feasibility_tolerance,
DenseRow::ConstView reduced_costs);

// Advanced incremental API to reuse directly the internal storage.
// This saves two copy per solves, and only matter on large easy problems.
void InitializeFromMutatedState();
DenseRow* MutableLowerBounds() { return &lower_bounds_; }
DenseRow* MutableUpperBounds() { return &upper_bounds_; }

private:
// Computes the initial/default variable status from its type. A constrained
// variable is set to the lowest of its 2 bounds in absolute value.
Expand Down
Loading

0 comments on commit b9c5e21

Please sign in to comment.