Files
ortools-clone/ortools/bop/bop_portfolio.h
Corentin Le Molgat c7120439d4 Bump license date
2022-06-17 14:23:23 +02:00

209 lines
7.6 KiB
C++

// Copyright 2010-2022 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.
#ifndef OR_TOOLS_BOP_BOP_PORTFOLIO_H_
#define OR_TOOLS_BOP_BOP_PORTFOLIO_H_
#include <cstdint>
#include <string>
#include "ortools/base/strong_vector.h"
#include "ortools/bop/bop_base.h"
#include "ortools/bop/bop_lns.h"
#include "ortools/bop/bop_parameters.pb.h"
#include "ortools/bop/bop_solution.h"
#include "ortools/bop/bop_types.h"
#include "ortools/glop/lp_solver.h"
#include "ortools/sat/boolean_problem.pb.h"
#include "ortools/sat/sat_solver.h"
#include "ortools/util/random_engine.h"
#include "ortools/util/stats.h"
#include "ortools/util/strong_integers.h"
#include "ortools/util/time_limit.h"
namespace operations_research {
namespace bop {
DEFINE_STRONG_INDEX_TYPE(OptimizerIndex);
const OptimizerIndex kInvalidOptimizerIndex(-1);
// Forward declaration.
class OptimizerSelector;
// This class implements a portfolio optimizer.
// The portfolio currently includes all the following optimizers:
// - SAT_CORE_BASED
// - SAT_LINEAR_SEARCH
// - LINEAR_RELAXATION
// - LOCAL_SEARCH
// - RANDOM_FIRST_SOLUTION
// - RANDOM_CONSTRAINT_LNS
// - RANDOM_VARIABLE_LNS
// - COMPLETE_LNS
// - LP_FIRST_SOLUTION
// - OBJECTIVE_FIRST_SOLUTION
// - USER_GUIDED_FIRST_SOLUTION
// - FEASIBILITY_PUMP_FIRST_SOLUTION
// - RANDOM_CONSTRAINT_LNS_GUIDED_BY_LP
// - RANDOM_VARIABLE_LNS_GUIDED_BY_LP
// - RELATION_GRAPH_LNS
// - RELATION_GRAPH_LNS_GUIDED_BY_LP
//
// At each call of Optimize(), the portfolio optimizer selects the next
// optimizer to run and runs it. The selection is auto-adaptative, meaning that
// optimizers that succeeded more in the previous calls to Optimizer() are more
// likely to be selected.
class PortfolioOptimizer : public BopOptimizerBase {
public:
PortfolioOptimizer(const ProblemState& problem_state,
const BopParameters& parameters,
const BopSolverOptimizerSet& optimizer_set,
const std::string& name);
~PortfolioOptimizer() override;
bool ShouldBeRun(const ProblemState& problem_state) const override {
return true;
}
Status Optimize(const BopParameters& parameters,
const ProblemState& problem_state, LearnedInfo* learned_info,
TimeLimit* time_limit) override;
private:
BopOptimizerBase::Status SynchronizeIfNeeded(
const ProblemState& problem_state);
void AddOptimizer(const sat::LinearBooleanProblem& problem,
const BopParameters& parameters,
const BopOptimizerMethod& optimizer_method);
void CreateOptimizers(const sat::LinearBooleanProblem& problem,
const BopParameters& parameters,
const BopSolverOptimizerSet& optimizer_set);
random_engine_t random_;
int64_t state_update_stamp_;
BopConstraintTerms objective_terms_;
std::unique_ptr<OptimizerSelector> selector_;
absl::StrongVector<OptimizerIndex, BopOptimizerBase*> optimizers_;
sat::SatSolver sat_propagator_;
BopParameters parameters_;
double lower_bound_;
double upper_bound_;
int number_of_consecutive_failing_optimizers_;
};
// This class is providing an adaptative selector for optimizers based on
// their past successes and deterministic time spent.
class OptimizerSelector {
public:
// Note that the list of optimizers is only used to get the names for
// debug purposes, the ownership of the optimizers is not transferred.
explicit OptimizerSelector(
const absl::StrongVector<OptimizerIndex, BopOptimizerBase*>& optimizers);
// Selects the next optimizer to run based on the user defined order and
// history of success. Returns kInvalidOptimizerIndex if no optimizer is
// selectable and runnable (see the functions below).
//
// The optimizer is selected using the following algorithm (L being the
// sorted list of optimizers, and l the position of the last selected
// optimizer):
// a- If a new solution has been found by optimizer l, select the first
// optimizer l' in L, l' >= 0, that can run.
// b- If optimizer l didn't find a new solution, select the first
// optimizer l', with l' > l, such that its deterministic time spent
// since last solution is smaller than the deterministic time spent
// by any runnable optimizer in 1..l since last solution.
// If no such optimizer is available, go to option a.
OptimizerIndex SelectOptimizer();
// Updates the internal metrics to decide which optimizer to select.
// This method should be called each time the selected optimizer is run.
//
// The gain corresponds to the reward to assign to the solver; It could for
// instance be the difference in cost between the last and the current
// solution.
//
// The time spent corresponds to the time the optimizer spent; To make the
// behavior deterministic, it is recommended to use the deterministic time
// instead of the elapsed time.
//
// The optimizers are sorted based on their score each time a new solution is
// found.
void UpdateScore(int64_t gain, double time_spent);
// Marks the given optimizer as not selectable until UpdateScore() is called
// with a positive gain. In which case, all optimizer will become selectable
// again.
void TemporarilyMarkOptimizerAsUnselectable(OptimizerIndex optimizer_index);
// Sets whether or not an optimizer is "runnable". Like a non-selectable one,
// a non-runnable optimizer will never be returned by SelectOptimizer().
//
// TODO(user): Maybe we should simply have the notion of selectability here
// and let the client handle the logic to decide what optimizer are selectable
// or not.
void SetOptimizerRunnability(OptimizerIndex optimizer_index, bool runnable);
// Returns statistics about the given optimizer.
std::string PrintStats(OptimizerIndex optimizer_index) const;
int NumCallsForOptimizer(OptimizerIndex optimizer_index) const;
// Prints some debug information. Should not be used in production.
void DebugPrint() const;
private:
// Updates internals when a solution has been found using the selected
// optimizer.
void NewSolutionFound(int64_t gain);
// Updates the deterministic time spent by the selected optimizer.
void UpdateDeterministicTime(double time_spent);
// Sorts optimizers based on their scores.
void UpdateOrder();
struct RunInfo {
RunInfo(OptimizerIndex i, const std::string& n)
: optimizer_index(i),
name(n),
num_successes(0),
num_calls(0),
total_gain(0),
time_spent(0.0),
time_spent_since_last_solution(0),
runnable(true),
selectable(true),
score(0.0) {}
bool RunnableAndSelectable() const { return runnable && selectable; }
OptimizerIndex optimizer_index;
std::string name;
int num_successes;
int num_calls;
int64_t total_gain;
double time_spent;
double time_spent_since_last_solution;
bool runnable;
bool selectable;
double score;
};
std::vector<RunInfo> run_infos_;
absl::StrongVector<OptimizerIndex, int> info_positions_;
int selected_index_;
};
} // namespace bop
} // namespace operations_research
#endif // OR_TOOLS_BOP_BOP_PORTFOLIO_H_