ortools中 pseudo_costs SubSolver cp_constrains cp_model_utils cumulative_energy cp_model h头文件源码功能分析详解
文章目录
功能
这段源代码定义了用于计算变量伪成本的类和相关函数。
其中包含了以下类和函数:
-
class PseudoCosts
:变量伪成本类,用于计算变量在目标边界和变量边界变化时的平均观察到的变化量。explicit PseudoCosts(Model* model)
:构造函数,初始化PseudoCosts对象。void UpdateCost(const std::vector<VariableBoundChange>& bound_changes, IntegerValue obj_bound_improvement)
:更新给定决策的变量伪成本。IntegerVariable GetBestDecisionVar()
:返回具有最佳可靠伪成本且未固定的变量。double GetCost(IntegerVariable var) const
:获取给定变量的伪成本值(仅用于测试)。int GetRecordings(IntegerVariable var) const
:获取给定变量的记录次数(仅用于测试)。
-
struct PseudoCosts::VariableBoundChange
:帮助结构体,用于从分支决策中获取与伪成本相关的信息。IntegerVariable var
:变量的整数变量。IntegerValue lower_bound_change
:下界变化的整数值。
-
std::vector<PseudoCosts::VariableBoundChange> GetBoundChanges(LiteralIndex decision, Model* model)
:从给定的分支决策中提取更新伪成本所需的信息。
这些类和函数提供了计算变量伪成本的功能,用于衡量变量边界变化对目标边界的影响。这可以用于指导搜索和分支决策,以提高求解器的效率和性能。
源码
#ifndef OR_TOOLS_SAT_PSEUDO_COSTS_H_
#define OR_TOOLS_SAT_PSEUDO_COSTS_H_
#include <vector>
#include "ortools/base/logging.h"
#include "ortools/base/strong_vector.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_base.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/sat/util.h"
#include "ortools/util/strong_integers.h"
namespace operations_research {
namespace sat {
// 变量的伪成本是指在变量边界单位变化时,目标边界平均观察到的变化量。
class PseudoCosts {
public:
// 用于从分支决策中获取与伪成本相关信息的辅助结构。
struct VariableBoundChange {
IntegerVariable var = kNoIntegerVariable;
IntegerValue lower_bound_change = IntegerValue(0);
};
explicit PseudoCosts(Model* model);
// 更新给定决策的伪成本。
void UpdateCost(const std::vector<VariableBoundChange>& bound_changes,
IntegerValue obj_bound_improvement);
// 返回可靠伪成本最佳且不固定的变量。
IntegerVariable GetBestDecisionVar();
// 返回给定变量的伪成本。仅用于测试。
double GetCost(IntegerVariable var) const {
CHECK_LT(var, pseudo_costs_.size());
return pseudo_costs_[var].CurrentAverage();
}
// 返回给定变量的记录次数。仅用于测试。
int GetRecordings(IntegerVariable var) const {
CHECK_LT(var, pseudo_costs_.size());
return pseudo_costs_[var].NumRecords();
}
private:
// 更新给定变量的成本。
void UpdateCostForVar(IntegerVariable var, double new_cost);
// 用于访问变量的当前边界的整数轨迹的引用。
const IntegerTrail& integer_trail_;
const SatParameters& parameters_;
absl::StrongVector<IntegerVariable, IncrementalAverage> pseudo_costs_;
};
// 从给定的分支决策中提取更新伪成本所需的信息。
std::vector<PseudoCosts::VariableBoundChange> GetBoundChanges(
LiteralIndex decision, Model* model);
} // namespace sat
} // namespace operations_research
#endif // OR_TOOLS_SAT_PSEUDO_COSTS_H_
SubSolver.h
功能
该源码文件是OR-Tools库中用于分发和同步子求解器的头文件。用于选择和在一组线程上分发求解器“子任务”的简单框架。以下是对其中几个类和函数的简要说明:
class SubSolver
:表示一个子求解器,用于生成任务、与外部世界同步以及记录评分和确定性时间等信息。派生类需要实现具体的任务生成和同步逻辑。
TaskIsAvailable()
:检查是否可以调用GenerateTask()
生成任务。GenerateTask(int64_t task_id)
:生成要运行的任务并返回其对应的函数对象。Synchronize()
:从子求解器的角度与外部世界进行同步,并合并最近完成的任务结果。score()
:获取上次同步前已完成任务更新的分数。deterministic_time()
:获取上次同步前已完成任务所花费的总确定性时间。name()
:获取子求解器的名称。StatisticsString()
:获取搜索统计信息的字符串表示。
-
class SynchronizationPoint
:一个简单的包装器,用于在子求解器列表中添加同步点。 -
NonDeterministicLoop()
:非确定性循环,按顺序同步所有子求解器,并从当前“最佳”子求解器生成和调度任务,直到无法生成额外的任务并且所有任务都完成。 -
DeterministicLoop()
:确定性循环,按顺序同步所有子求解器,并使用启发式方法生成和调度任务,直到无法生成更多的任务。 -
SequentialLoop()
:与DeterministicLoop()
类似,但是针对单线程情况进行了优化,避免使用线程池。
这些函数和类提供了一个简单的框架,用于在多线程环境中分发和同步子求解器的任务。通过调用适当的循环函数,可以根据不同的需求实现非确定性或确定性的求解过程,并利用多线程加速求解器的执行。
源码
// 用于选择和在一组线程上分发求解器“子任务”的简单框架。
#ifndef OR_TOOLS_SAT_SUBSOLVER_H_
#define OR_TOOLS_SAT_SUBSOLVER_H_
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ortools/base/integral_types.h"
#if !defined(__PORTABLE_PLATFORM__)
#include "ortools/base/threadpool.h"
#endif // __PORTABLE_PLATFORM__
namespace operations_research {
namespace sat {
// 用于分发工作的API。每个子求解器可以生成任务,并与世界其他部分进行同步。
// 注意,目前只有主线程与子求解器进行交互。只有通过GenerateTask()生成的任务在线程池中并行执行。
class SubSolver {
public:
explicit SubSolver(const std::string& name) : name_(name) {
}
virtual ~SubSolver() {
}
// 当且仅当GenerateTask()可调用时返回true。
//
// 注意:在当前设计中,直到创建它的Solve()结束之前,SubSolver不会被删除。但是,如果没有必要再次调用此Subsolver,则始终在这里返回false,并释放Subsolver内部使用的内存。在主求解循环中迭代它的开销应该是最小的。
virtual bool TaskIsAvailable() = 0;
// 返回一个要运行的任务。task_id只是一个递增的计数器,对应于对GenerateTask()的总调用次数。
//
// 用户(user)注意:在当前设计中,任务异步更新(因此非确定性地)全局“共享”类,但只有在调用Synchronize()时Subsolver才会将这个全局状态合并到自身中。
virtual std::function<void()> GenerateTask(int64_t task_id) = 0;
// 从SubSolver的角度与外部世界进行同步。还会合并最近完成的任务的结果(如果有)。
virtual void Synchronize() = 0;
// 返回上一次Synchronize()调用之前已完成的任务更新的分数。其他条件相等的情况下,我们更喜欢运行得分最高的SubSolver。
//
// 注意:目前未使用此功能。
double score() const {
return score_; }
// 返回上一次Synchronize()调用之前已完成的任务所花费的总确定性时间。
double deterministic_time() const {
return deterministic_time_; }
// 返回此SubSolver的名称。用于日志记录。
std::string name() const {
return name_; }
// 返回搜索统计信息。
virtual std::string StatisticsString() const {
return std::string(); }
protected:
const std::string name_;
double score_ = 0.0;
double deterministic_time_ = 0.0;
};
// 添加一个同步点以在子求解器列表中添加同步点。
class SynchronizationPoint : public SubSolver {
public:
explicit SynchronizationPoint(std::function<void()> f)
: SubSolver(""), f_(std::move(f)) {
}
bool TaskIsAvailable() final {
return false; }
std::function<void()> GenerateTask(int64_t task_id) final {
return nullptr; }
void Synchronize() final {
f_(); }
private:
std::function<void()> f_;
};
// 执行以下循环:
// 1/ 按给定顺序同步所有子求解器。
// 2/ 从当前“最佳”子求解器生成并安排一个任务。
// 3/ 重复,直到无法生成额外的任务并且所有任务都完成。
//
// 每次选择的复杂度为O(num_subsolvers),但鉴于我们不希望有超过100个这样的子求解器,这应该是可以接受的。
//
// 注意,可以包含从不生成任何任务的“特殊”子求解器。这可用于仅一次同步由许多子求解器使用的类。
void NonDeterministicLoop(
const std::vector<std::unique_ptr<SubSolver>>& subsolvers, int num_threads);
// 与NonDeterministicLoop()类似,但只要所有的SubSolver遵守Synchronize()约定,就能得到确定性求解器。
// 执行以下循环:
// 1/ 按给定顺序同步所有子求解器。
// 2/ 使用启发式方法生成和安排最多batch_size个任务。
// 3/ 等待所有任务完成。
// 4/ 重复,直到在第2步中无法生成任务。
void DeterministicLoop(
const std::vector<std::unique_ptr<SubSolver>>& subsolvers, int num_threads,
int batch_size);
// 与上述相同,但针对num_threads=1的情况进行了专门实现。这样就完全避免使用Threadpool。它应该与上述函数在num_threads=1和batch_size=1时具有相同的行为。请注意,即使num_threads=1,较高的batch_size也不会产生相同的行为。
void SequentialLoop(const std::vector<std::unique_ptr<SubSolver>>& subsolvers);
} // namespace sat
} // namespace operations_research
#endif // OR_TOOLS_SAT_SUBSOLVER_H_
cp_constrains.h
功能
该头文件定义了一些用于CP模型的约束类和相关函数。以下是对其中几个类和函数的简要说明:
-
class BooleanXorPropagator
:实现了异或约束的传播器。根据给定的文字集合和值,将异或约束传播为等式。 -
class GreaterThanAtLeastOneOfPropagator
:实现了至少一个选择约束的传播器。根据给定的目标变量、候选变量、偏移量、选择文字和强制文字,当还没有选择任何选择文字时,传播目标变量的下界。 -
ToIntegerValueVector()
:将int64_t
类型的向量转换为IntegerValue
类型的向量。 -
LiteralXorIs()
:约束函数,强制一组文字的异或结果等于给定值。 -
PartialIsOneOfVar()
:约束函数,强制目标变量等于候选变量中的一个。等式由给定的选择文字控制。
这些约束类和函数提供了一种方便的方式来添加特定的约束到CP模型中。可以根据需要选择适当的约束,并使用它们来建模和求解问题。
源码
#ifndef OR_TOOLS_SAT_CP_CONSTRAINTS_H_
#define OR_TOOLS_SAT_CP_CONSTRAINTS_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
#include "absl/types/span.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_base.h"
#include "ortools/util/rev.h"
#include "ortools/util/strong_integers.h"
namespace operations_research {
namespace sat {
// 传播一组文字的异或等于给定值的事实。时间复杂度为O(n)。
//
// 注意:通过使用两个watcher机制,我们可以更快地传播这个约束。
class BooleanXorPropagator : public PropagatorInterface {
public:
BooleanXorPropagator(const std::vector<Literal>& literals, bool value,
Trail* trail, IntegerTrail* integer_trail)
: literals_(literals),
value_(value),
trail_(trail),
integer_trail_(integer_trail) {
}
bool Propagate() final;
void RegisterWith(GenericLiteralWatcher* watcher);
private:
const std::vector<Literal> literals_;
const bool value_;
std::vector<Literal> literal_reason_;
Trail* trail_;
IntegerTrail* integer_trail_;
DISALLOW_COPY_AND_ASSIGN(BooleanXorPropagator);
};
// 如果我们有:
// - selectors[i] => (target_var >= vars[i] + offset[i])
// - 并且我们知道至少一个selectors[i]必须为true
// 那么我们可以传播这样的事实:如果还没有选择任何selectors[i],则target_var的下界大于仍然可能的候选方案中的最小值。
//
// 当还没有选择selectors[i]时,此约束会处理这种情况。
//
// 此约束支持重复的selectors[i]。
class GreaterThanAtLeastOneOfPropagator : public PropagatorInterface {
public:
GreaterThanAtLeastOneOfPropagator(
IntegerVariable target_var, const absl::Span<const IntegerVariable> vars,
const absl::Span<const IntegerValue> offsets,
const absl::Span<const Literal> selectors,
const absl::Span<const Literal> enforcements, Model* model);
bool Propagate() final;
void RegisterWith(GenericLiteralWatcher* watcher);
private:
const IntegerVariable target_var_;
const std::vector<IntegerVariable> vars_;
const std::vector<IntegerValue> offsets_;
const std::vector<Literal> selectors_;
const std::vector<Literal> enforcements_;
Trail* trail_;
IntegerTrail* integer_trail_;
std::vector<Literal> literal_reason_;
std::vector<IntegerLiteral> integer_reason_;
DISALLOW_COPY_AND_ASSIGN(GreaterThanAtLeastOneOfPropagator);
};
// ============================================================================
// Model based functions.
// ============================================================================
inline std::vector<IntegerValue> ToIntegerValueVector(
const std::vector<int64_t>& input) {
std::vector<IntegerValue> result(input.size());
for (int i = 0; i < input.size(); ++i) {
result[i] = IntegerValue(input[i]);
}
return result;
}
// 强制一组文字的异或等于给定值。
inline std::function<void(Model*)> LiteralXorIs(
const std::vector<Literal>& literals, bool value) {
return [=](Model* model) {
Trail* trail = model->GetOrCreate<Trail>();
IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
BooleanXorPropagator* constraint =
new BooleanXorPropagator(literals, value, trail, integer_trail);
constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
model->TakeOwnership(constraint);
};
}
// 强制target_var等于vars中的一个。等式由给定的“selector”文字控制。
//
// 注意:这仅从可能的候选项的最小/最大值传播到目标变量的最小/最大值。完整的约束还需要处理其中一个文字为true的情况。
//
// 注意:如果只有一个或两个候选项,这不会添加任何内容。
inline std::function<void(Model*)> PartialIsOneOfVar(
IntegerVariable target_var, const std::vector<IntegerVariable>& vars,
const std::vector<Literal>& selectors) {
CHECK_EQ(vars.size(), selectors.size());
return [=](Model* model) {
const std::vector<IntegerValue> offsets(vars.size(), IntegerValue(0));
if (vars.size() > 2) {
// 传播最小值。
model->Add(GreaterThanAtLeastOneOf(target_var, vars, offsets, selectors));
}
if (vars.size() > 2) {
// 传播最大值。
model->Add(GreaterThanAtLeastOneOf(NegationOf(target_var),
NegationOf(vars), offsets, selectors));
}
};
}
} // namespace sat
} // namespace operations_research
#endif // OR_TOOLS_SAT_CP_CONSTRAINTS_H_
cuts.h
#ifndef OR_TOOLS_SAT_CUTS_H_
#define OR_TOOLS_SAT_CUTS_H_
#include <array>
#include <functional>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "ortools/base/strong_vector.h"
#include "ortools/sat/implied_bounds.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/linear_constraint.h"
#include "ortools/sat/linear_constraint_manager.h"
#include "ortools/sat/model.h"
#include "ortools/sat/synchronization.h"
#include "ortools/util/strong_integers.h"
namespace operations_research {
namespace sat {
// 一个整数规划的“割”生成器。
//
// generate_cuts()函数通常会使用当前LP最优解(但也可以使用任何lp_values)调用。注意,一个CutGenerator应该:
// - 只查看与它的'vars'或其取反相关的lp_values位置。
// - 仅以相同的变量或它们的否定形式添加割。
struct CutGenerator {
bool only_run_at_level_zero = false;
std::vector<IntegerVariable> vars;
std::function<bool(
const absl::StrongVector<IntegerVariable, double>& lp_values,
LinearConstraintManager* manager)>
generate_cuts;
};
// 为了简化割生成代码,我们使用比线性约束更复杂的数据结构来表示具有位移/补充变量和隐含界限替代的割。
struct CutTerm {
bool IsBoolean() const {
return bound_diff == 1; }
bool HasRelevantLpValue() const {
return lp_value > 1e-2; }
std::string DebugString() const;
// 如果这样做会导致溢出,则返回false并不执行任何操作。否则,进行替换X -> (1 - X')并更新rhs。
bool Complement(IntegerValue* rhs);
// 每个术语都采用coeff * X的形式,其中X是具有给定lp_value和在[0, bound_diff]范围内的定义域的变量。注意X始终>= 0。
double lp_value = 0.0;
IntegerValue coeff = IntegerValue(0);
IntegerValue bound_diff = IntegerValue(0);
// X = 给定的LinearExpression。
// 我们这里只支持大小为1或2的情况,以便内联内存。
int expr_size = 0;
IntegerValue expr_offset = IntegerValue(0);
std::array<IntegerVariable, 2> expr_vars;
std::array<IntegerValue, 2> expr_coeffs;
};
// 我们的割总是具有线性表达式<= rhs的形式。
struct CutData {
// 我们需要零级界限和LP松弛值来填充CutData。
// 如果我们遇到任何整数溢出,返回false。
bool FillFromLinearConstraint(
const LinearConstraint& cut,
const absl::StrongVector<IntegerVariable, double>& lp_values,
IntegerTrail* integer_trail);
IntegerValue rhs;
std::vector<CutTerm> terms;
// 这将对术语进行排序并填充num_relevant_entries和max_magnitude。
void Canonicalize();
IntegerValue max_magnitude;
int num_relevant_entries;
};