Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion ortools/base/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,25 @@ cc_binary(
srcs = ["constant_divisor_benchmark.cc"],
deps = [
":constant_divisor",
"//ortools/base:benchmark_main",
"@abseil-cpp//absl/flags:flag",
"@abseil-cpp//absl/random",
"@abseil-cpp//absl/random:bit_gen_ref",
"@abseil-cpp//absl/random:distributions",
"@google_benchmark//:benchmark",
"@google_benchmark//:benchmark_main",
],
)

cc_library(
name = "benchmark_main",
srcs = ["benchmark_main.cc"],
deps = [
"//ortools/base",
"@google_benchmark//:benchmark",
],
alwayslink = True,
)

cc_library(
name = "container_logging",
hdrs = ["container_logging.h"],
Expand Down
1 change: 1 addition & 0 deletions ortools/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ file(GLOB _SRCS "*.h" "*.cc")
list(FILTER _SRCS EXCLUDE REGEX ".*/.*_benchmark.cc")
list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc")
list(FILTER _SRCS EXCLUDE REGEX "/gmock\.h")
list(FILTER _SRCS EXCLUDE REGEX ".*/benchmark_main\.cc")

set(NAME ${PROJECT_NAME}_base)

Expand Down
26 changes: 26 additions & 0 deletions ortools/base/benchmark_main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstdlib>

#include "benchmark/benchmark.h"
#include "ortools/base/init_google.h"

int main(int argc, char* argv[]) {
benchmark::MaybeReenterWithoutASLR(argc, argv);
benchmark::Initialize(&argc, argv);
InitGoogle(argv[0], &argc, &argv, false);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
return EXIT_SUCCESS;
}
2 changes: 2 additions & 0 deletions ortools/glop/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ cc_library(
"//ortools/util:time_limit",
"@abseil-cpp//absl/base:core_headers",
"@abseil-cpp//absl/flags:flag",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/log:die_if_null",
"@abseil-cpp//absl/log:vlog_is_on",
Expand Down Expand Up @@ -338,6 +339,7 @@ cc_library(
"//ortools/util:logging",
"//ortools/util:testing_utils",
"//ortools/util:time_limit",
"@abseil-cpp//absl/base:core_headers",
"@abseil-cpp//absl/flags:flag",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
Expand Down
33 changes: 15 additions & 18 deletions ortools/glop/entering_variable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@ EnteringVariable::EnteringVariable(const VariablesInfo& variables_info,
reduced_costs_(reduced_costs),
parameters_() {}

Status EnteringVariable::DualChooseEnteringColumn(
ColIndex EnteringVariable::DualChooseEnteringColumn(
bool nothing_to_recompute, const UpdateRow& update_row,
Fractional cost_variation, std::vector<ColIndex>* bound_flip_candidates,
ColIndex* entering_col) {
GLOP_RETURN_ERROR_IF_NULL(entering_col);
Fractional cost_variation, std::vector<ColIndex>* bound_flip_candidates) {
const auto update_coefficients = update_row.GetCoefficients().const_view();
const auto reduced_costs = reduced_costs_->GetReducedCosts();
SCOPED_TIME_STAT(&stats_);
Expand Down Expand Up @@ -145,7 +143,7 @@ Status EnteringVariable::DualChooseEnteringColumn(
// - We have processed all breakpoints with a ratio smaller than it.
harris_ratio = std::numeric_limits<Fractional>::max();

*entering_col = kInvalidCol;
ColIndex entering_col = kInvalidCol;
bound_flip_candidates->clear();
Fractional step = 0.0;
Fractional best_coeff = -1.0;
Expand Down Expand Up @@ -196,12 +194,12 @@ Status EnteringVariable::DualChooseEnteringColumn(
top.ratio + harris_tolerance / top.coeff_magnitude));

if (top.coeff_magnitude == best_coeff && top.ratio == step) {
DCHECK_NE(*entering_col, kInvalidCol);
DCHECK_NE(entering_col, kInvalidCol);
equivalent_entering_choices_.push_back(top.col);
} else {
equivalent_entering_choices_.clear();
best_coeff = top.coeff_magnitude;
*entering_col = top.col;
entering_col = top.col;

// Note that the step is not directly used, so it is okay to leave it
// negative.
Expand All @@ -217,15 +215,15 @@ Status EnteringVariable::DualChooseEnteringColumn(

// Break the ties randomly.
if (!equivalent_entering_choices_.empty()) {
equivalent_entering_choices_.push_back(*entering_col);
*entering_col =
equivalent_entering_choices_.push_back(entering_col);
entering_col =
equivalent_entering_choices_[std::uniform_int_distribution<int>(
0, equivalent_entering_choices_.size() - 1)(random_)];
IF_STATS_ENABLED(
stats_.num_perfect_ties.Add(equivalent_entering_choices_.size()));
}

if (*entering_col == kInvalidCol) return Status::OK();
if (entering_col == kInvalidCol) return entering_col;

// If best_coeff is small and they are potential bound flips, we can take a
// smaller step but use a good pivot.
Expand All @@ -239,18 +237,17 @@ Status EnteringVariable::DualChooseEnteringColumn(

VLOG(1) << "Used bound flip to avoid bad pivot. Before: " << best_coeff
<< " now: " << std::abs(update_coefficients[col]);
*entering_col = col;
entering_col = col;
break;
}
}

return Status::OK();
return entering_col;
}

Status EnteringVariable::DualPhaseIChooseEnteringColumn(
ColIndex EnteringVariable::DualPhaseIChooseEnteringColumn(
bool nothing_to_recompute, const UpdateRow& update_row,
Fractional cost_variation, ColIndex* entering_col) {
GLOP_RETURN_ERROR_IF_NULL(entering_col);
Fractional cost_variation) {
const auto update_coefficients = update_row.GetCoefficients().const_view();
const auto reduced_costs = reduced_costs_->GetReducedCosts();
SCOPED_TIME_STAT(&stats_);
Expand Down Expand Up @@ -334,7 +331,7 @@ Status EnteringVariable::DualPhaseIChooseEnteringColumn(

// Select the last breakpoint that still improves the infeasibility and has a
// numerically stable pivot.
*entering_col = kInvalidCol;
ColIndex entering_col = kInvalidCol;
Fractional step = -1.0;
Fractional improvement = std::abs(cost_variation);
while (!breakpoints_.empty()) {
Expand All @@ -344,7 +341,7 @@ Status EnteringVariable::DualPhaseIChooseEnteringColumn(
DCHECK(top.ratio > step ||
(top.ratio == step && top.coeff_magnitude <= pivot_magnitude));
if (top.ratio > step && top.coeff_magnitude >= pivot_magnitude) {
*entering_col = top.col;
entering_col = top.col;
step = top.ratio;
pivot_magnitude = top.coeff_magnitude;
}
Expand All @@ -362,7 +359,7 @@ Status EnteringVariable::DualPhaseIChooseEnteringColumn(
std::pop_heap(breakpoints_.begin(), breakpoints_.end());
breakpoints_.pop_back();
}
return Status::OK();
return entering_col;
}

void EnteringVariable::SetParameters(const GlopParameters& parameters) {
Expand Down
11 changes: 5 additions & 6 deletions ortools/glop/entering_variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,17 @@ class EnteringVariable {
// the "update" row vector in the direction given by the sign of
// cost_variation. Computes the smallest step that keeps the dual feasibility
// for all the columns.
ABSL_MUST_USE_RESULT Status DualChooseEnteringColumn(
ColIndex DualChooseEnteringColumn(
bool nothing_to_recompute, const UpdateRow& update_row,
Fractional cost_variation, std::vector<ColIndex>* bound_flip_candidates,
ColIndex* entering_col);
Fractional cost_variation, std::vector<ColIndex>* bound_flip_candidates);

// Dual feasibility phase (i.e. phase I) ratio test.
// Similar to the optimization phase test, but allows a step that increases
// the infeasibility of an already infeasible column. The step magnitude is
// the one that minimize the sum of infeasibilities when applied.
ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn(
bool nothing_to_recompute, const UpdateRow& update_row,
Fractional cost_variation, ColIndex* entering_col);
ColIndex DualPhaseIChooseEnteringColumn(bool nothing_to_recompute,
const UpdateRow& update_row,
Fractional cost_variation);

// Sets the parameters.
void SetParameters(const GlopParameters& parameters);
Expand Down
27 changes: 16 additions & 11 deletions ortools/glop/lp_solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,11 @@ SolverLogger& LPSolver::GetSolverLogger() { return logger_; }
ProblemStatus LPSolver::Solve(const LinearProgram& lp) {
std::unique_ptr<TimeLimit> time_limit =
TimeLimit::FromParameters(parameters_);
return SolveWithTimeLimit(lp, time_limit.get());
return SolveWithTimeLimit(lp, *time_limit);
}

ProblemStatus LPSolver::SolveWithTimeLimit(const LinearProgram& lp,
TimeLimit* time_limit) {
if (time_limit == nullptr) {
LOG(DFATAL) << "SolveWithTimeLimit() called with a nullptr time_limit.";
return ProblemStatus::ABNORMAL;
}
TimeLimit& time_limit) {
++num_solves_;
num_revised_simplex_iterations_ = 0;
DumpLinearProgramIfRequiredByFlags(lp, num_solves_);
Expand Down Expand Up @@ -227,7 +223,7 @@ ProblemStatus LPSolver::SolveWithTimeLimit(const LinearProgram& lp,
// Preprocess.
MainLpPreprocessor preprocessor(&parameters_);
preprocessor.SetLogger(&logger_);
preprocessor.SetTimeLimit(time_limit);
preprocessor.SetTimeLimit(&time_limit);

const bool postsolve_is_needed = preprocessor.Run(&current_linear_program_);

Expand Down Expand Up @@ -261,7 +257,7 @@ ProblemStatus LPSolver::SolveWithTimeLimit(const LinearProgram& lp,
// Do not launch the solver if the time limit was already reached. This might
// mean that the pre-processors were not all run, and current_linear_program_
// might not be in a completely safe state.
if (!time_limit->LimitReached()) {
if (!time_limit.LimitReached()) {
RunRevisedSimplexIfNeeded(&solution, time_limit);
}
if (postsolve_is_needed) preprocessor.DestructiveRecoverSolution(&solution);
Expand All @@ -271,15 +267,24 @@ ProblemStatus LPSolver::SolveWithTimeLimit(const LinearProgram& lp,
SOLVER_LOG(&logger_, "status: ", GetProblemStatusString(status));
SOLVER_LOG(&logger_, "objective: ", GetObjectiveValue());
SOLVER_LOG(&logger_, "iterations: ", GetNumberOfSimplexIterations());
SOLVER_LOG(&logger_, "time: ", time_limit->GetElapsedTime());
SOLVER_LOG(&logger_, "time: ", time_limit.GetElapsedTime());
SOLVER_LOG(&logger_, "deterministic_time: ",
time_limit->GetElapsedDeterministicTime());
time_limit.GetElapsedDeterministicTime());
SOLVER_LOG(&logger_, "");
}

return status;
}

ProblemStatus LPSolver::SolveWithTimeLimit(const LinearProgram& lp,
TimeLimit* time_limit) {
if (time_limit == nullptr) {
LOG(DFATAL) << "SolveWithTimeLimit() called with a nullptr time_limit.";
return ProblemStatus::ABNORMAL;
}
return SolveWithTimeLimit(lp, *time_limit);
}

void LPSolver::Clear() {
ResizeSolution(RowIndex(0), ColIndex(0));
revised_simplex_.reset(nullptr);
Expand Down Expand Up @@ -609,7 +614,7 @@ void LPSolver::ResizeSolution(RowIndex num_rows, ColIndex num_cols) {
}

void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution* solution,
TimeLimit* time_limit) {
TimeLimit& time_limit) {
// Note that the transpose matrix is no longer needed at this point.
// This helps reduce the peak memory usage of the solver.
//
Expand Down
10 changes: 9 additions & 1 deletion ortools/glop/lp_solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <memory>
#include <string>

#include "absl/base/attributes.h"
#include "ortools/glop/parameters.pb.h"
#include "ortools/glop/revised_simplex.h"
#include "ortools/lp_data/lp_data.h"
Expand Down Expand Up @@ -63,6 +64,13 @@ class LPSolver {

// Same as Solve() but use the given time limit rather than constructing a new
// one from the current GlopParameters.
ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram& lp,
TimeLimit& time_limit);

// Legacy version of SolveWithTimeLimit() passing a pointer on TimeLimit and
// expecting it to be non-null (it returns ProblemStatus::ABNORMAL and use
// LOG(DFATAL) when null).
ABSL_DEPRECATED("Use SolveWithTimeLimit(const LinearProgram&, TimeLimit&).");
ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram& lp,
TimeLimit* time_limit);

Expand Down Expand Up @@ -193,7 +201,7 @@ class LPSolver {
// Runs the revised simplex algorithm if needed (i.e. if the program was not
// already solved by the preprocessors).
void RunRevisedSimplexIfNeeded(ProblemSolution* solution,
TimeLimit* time_limit);
TimeLimit& time_limit);

// Checks that the returned solution values and statuses are consistent.
// Returns true if this is the case. See the code for the exact check
Expand Down
Loading
Loading