【ortools 源码系列6】pseudo_costs SubSolver cp_constrains cumulative_energy cp_model h头文件功能详解

本文深入探讨ortools库中的关键组件,包括伪成本计算、SubSolver子求解器和CP模型相关类。详细解析了用于计算变量伪成本的类、SubSolver的多线程任务分发与同步机制,以及cp_constrains、cuts、cp_model_utils、cumulative_energy和cp_model头文件中的函数和约束。这些内容对于理解ortools的求解效率和性能优化至关重要。
摘要由CSDN通过智能技术生成

ortools中 pseudo_costs SubSolver cp_constrains cp_model_utils cumulative_energy cp_model h头文件源码功能分析详解

功能

这段源代码定义了用于计算变量伪成本的类和相关函数

其中包含了以下类和函数:

  1. 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:获取给定变量的记录次数(仅用于测试)。
  2. struct PseudoCosts::VariableBoundChange:帮助结构体,用于从分支决策中获取与伪成本相关的信息。

    • IntegerVariable var:变量的整数变量。
    • IntegerValue lower_bound_change:下界变化的整数值。
  3. 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库中用于分发和同步子求解器的头文件。用于选择和在一组线程上分发求解器“子任务”的简单框架。以下是对其中几个类和函数的简要说明:

  1. class SubSolver:表示一个子求解器,用于生成任务、与外部世界同步以及记录评分和确定性时间等信息。派生类需要实现具体的任务生成和同步逻辑。
  • TaskIsAvailable():检查是否可以调用GenerateTask()生成任务。
    • GenerateTask(int64_t task_id):生成要运行的任务并返回其对应的函数对象。
    • Synchronize():从子求解器的角度与外部世界进行同步,并合并最近完成的任务结果。
    • score():获取上次同步前已完成任务更新的分数。
    • deterministic_time():获取上次同步前已完成任务所花费的总确定性时间。
    • name():获取子求解器的名称。
    • StatisticsString():获取搜索统计信息的字符串表示。
  1. class SynchronizationPoint:一个简单的包装器,用于在子求解器列表中添加同步点。

  2. NonDeterministicLoop():非确定性循环,按顺序同步所有子求解器,并从当前“最佳”子求解器生成和调度任务,直到无法生成额外的任务并且所有任务都完成。

  3. DeterministicLoop():确定性循环,按顺序同步所有子求解器,并使用启发式方法生成和调度任务,直到无法生成更多的任务。

  4. 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模型的约束类和相关函数。以下是对其中几个类和函数的简要说明:

  1. class BooleanXorPropagator:实现了异或约束的传播器。根据给定的文字集合和值,将异或约束传播为等式。

  2. class GreaterThanAtLeastOneOfPropagator:实现了至少一个选择约束的传播器。根据给定的目标变量、候选变量、偏移量、选择文字和强制文字,当还没有选择任何选择文字时,传播目标变量的下界。

  3. ToIntegerValueVector():将int64_t类型的向量转换为IntegerValue类型的向量。

  4. LiteralXorIs():约束函数,强制一组文字的异或结果等于给定值。

  5. 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();
  Int
  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值