【ortools源码系列4】ortools cp_model_presolve cp_model_postsolve model_solver用法功能源码分析

本文详细分析了ortools库中CP模型的预处理(cp_model_presolve)和后处理(cp_model_postsolve)的源码,包括变量删除、约束合并、约束改写和变量替换等技术,以及如何修复模型、约束处理和后处理操作。同时,介绍了presolve_util、cp_model_solver、cp_model_symmetries等相关功能和源码。
摘要由CSDN通过智能技术生成

ortools cp_model_presolve cp_model_postsolve model_solver用法功能源码分析

cp_model_presolve.h

功能

CpModelPresolver是一个CP模型的预处理器,用于优化和简化模型。 它实施了一系列的转换和简化技术,以减少模型的复杂性,并且提高求解效率。

CpModelPresolver的主要功能包括:

  1. 删除无用变量和约束:删除没有任何影响的变量和约束,以减少模型的大小和求解时间。
  2. 合并等价约束:合并具有相同约束条件的约束,以减少模型的复杂度。
  3. 约束改写:将复杂的约束改写为更简单的形式,以提高求解效率。
  4. 变量替换:将一组变量替换为新的变量,以简化模型并减少搜索空间。
  5. 约束簇分析:识别具有相似特征的约束,并将它们分组,以便更有效地求解。

源码

#ifndef OR_TOOLS_SAT_CP_MODEL_PRESOLVE_H_
#define OR_TOOLS_SAT_CP_MODEL_PRESOLVE_H_

#include <array>
#include <cstdint>
#include <utility>
#include <vector>

#include "absl/base/attributes.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_utils.h"
#include "ortools/sat/presolve_context.h"
#include "ortools/sat/presolve_util.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/affine_relation.h"
#include "ortools/util/bitset.h"
#include "ortools/util/logging.h"
#include "ortools/util/sorted_interval_list.h"
#include "ortools/util/time_limit.h"

namespace operations_research {
   
namespace sat {
   

// 替换所有变量i(以及引用它的文字)为mapping[i]。变量i的定义也被移动到其新索引。
// 负数映射值的变量将被忽略,如果任何地方引用了此类变量,则出错(这是CHECKed)。
//
// 映射的图像应在[0,new_num_variables)中密集,这也是CHECKed。
void ApplyVariableMapping(const std::vector<int>& mapping,
                          const PresolveContext& context);

// 对presolved_model进行预处理。
//
// 这还创建了一个模型映射,用于编码两个问题之间的对应关系。具体工作如下:
// - mapping_model的第一个变量与初始模型的变量一一对应。
// - presolved_model的变量与映射模型中postsolve_mapping指定索引处的变量一一对应。
// - 通过固定其中一个变量并求解模型,将另一个变量集分配给另一个问题的可行解。此外,这些解的目标值将相同。
//   注意,在实践中,解决这类问题所需的时间很短,因为传播基本上会完成所有工作。
//
// 注意(用户):如果优化模型可以转换为决策问题,例如目标被固定或与问题的其余部分无关,则可以将其转换为决策问题。
//
// TODO(用户):识别断开的组件并返回预处理模型的向量?如果我们采用这种方式,将索引存储在模型内部可能更好。
// 可以向模型添加IntegerVariableProto :: initial_index;
class CpModelPresolver {
   
 public:
  CpModelPresolver(PresolveContext* context,
                   std::vector<int>* postsolve_mapping);

  // 返回预处理后的问题状态:
  // - 如果一切正常,则返回UNKNOWN。
  // - 如果在预处理期间证明了模型是不可行的,则返回INFEASIBLE。
  // - 如果模型引起了某些问题,例如我们无法以足够的精度缩放浮点目标,则返回MODEL_INVALID。
  CpSolverStatus Presolve();

  // 对给定约束执行预处理方法。仅供测试使用。
  bool PresolveOneConstraint(int c);

  // 仅供测试使用。
  void RemoveEmptyConstraints();

 private:
  // 记录已应用的规则并返回INFEASIBLE。
  CpSolverStatus InfeasibleStatus();

  // 运行预处理器的内部循环。
  void PresolveToFixPoint();

  // 运行探测过程。
  void Probe();

  // 预处理函数。
  //
  // 仅当约束<->变量图未更改时才应返回false。这只是一种优化,返回true始终是正确的。
  //
  // 不变式关于UNSAT:如果context_.IsUnsat()为true,则所有这些函数都应立即中止。
  // 更改状态为不可满足的唯一方法是通过ABSL_MUST_USE_RESULT函数,该函数也应立即中止当前代码。
  // 这样,我们不应在不一致状态上进行计算。
  // TODO(用户):将这些公开并进行单元测试。
  bool PresolveAllDiff(ConstraintProto* ct);
  bool PresolveAutomaton(ConstraintProto* ct);
  bool PresolveElement(ConstraintProto* ct);
  bool PresolveIntAbs(ConstraintProto* ct);
  bool PresolveIntDiv(ConstraintProto* ct);
  bool PresolveIntMod(ConstraintProto* ct);
  bool PresolveIntProd(ConstraintProto* ct);
  bool PresolveInterval(int c, ConstraintProto* ct);
  bool PresolveInverse(ConstraintProto* ct);
  bool PresolveLinMax(ConstraintProto* ct);
  bool PresolveTable(ConstraintProto* ct);

  bool PresolveCumulative(ConstraintProto* ct);
  bool PresolveNoOverlap(ConstraintProto* ct);
  bool PresolveNoOverlap2D(int c, ConstraintProto* ct);
  bool PresolveReservoir(ConstraintProto* ct);

  bool PresolveCircuit(ConstraintProto* ct);
  bool PresolveRoutes(ConstraintProto* ct);

  bool PresolveAtMostOrExactlyOne(ConstraintProto* ct);
  bool PresolveAtMostOne(ConstraintProto* ct);
  bool PresolveExactlyOne(ConstraintProto* ct);

  bool PresolveBoolAnd(ConstraintProto* ct);
  bool PresolveBoolOr(ConstraintProto* ct);
  bool PresolveBoolXor(ConstraintProto* ct);
  bool PresolveEnforcementLiteral(ConstraintProto* ct);

  // 对线性约束进行重组,并替换仿射关系。
  // 如果表达式中的变量集合发生更改,则返回true。
  template <typename ProtoWithVarsAndCoeffs>
  bool CanonicalizeLinearExpressionInternal(const ConstraintProto& ct,
                                            ProtoWithVarsAndCoeffs* proto,
                                            int64_t* offset);
  bool CanonicalizeLinearExpression(const ConstraintProto& ct,
                                    LinearExpressionProto* exp);
  bool CanonicalizeLinearArgument(const ConstraintProto& ct,
                                  LinearArgumentProto* proto);

  // 对于线性约束,我们有多个函数。
  bool CanonicalizeLinear(ConstraintProto* ct);
  bool PropagateDomainsInLinear(int ct_index, ConstraintProto* ct);
  bool RemoveSingletonInLinear(ConstraintProto* ct);
  bool PresolveSmallLinear(ConstraintProto* ct);
  bool PresolveLinearOfSizeOne(ConstraintProto* ct);
  bool PresolveLinearOfSizeTwo(ConstraintProto* ct);
  bool PresolveLinearOnBooleans(ConstraintProto* ct);
  bool PresolveDiophantine(ConstraintProto* ct);
  bool AddVarAffineRepresentativeFromLinearEquality(int target_index,
                                                    ConstraintProto* ct);
  bool PresolveLinearEqualityWithModulo(ConstraintProto* ct);

  // 检测线性约束的子集是否在另一个线性约束中,并进行相关的预处理。
  void DetectAndProcessAtMostOneInLinear(int ct_index, ConstraintProto* ct,
                                         ActivityBoundHelper* helper);

  // 如果约束的形式是“a * expr_X + expr_Y”,其中expr_Y只能取相对于a的较小值,
  // 取决于边界,约束可以等效为仅针对expr_X的约束。
  //
  // 例如,“10'001 X + 9'999 Y <= 105'000,其中X,Y在[0,100]”可以重写为X + Y <= 10!
  // 在将浮点约束缩放到整数系数时,这很容易发生。
  void TryToReduceCoefficientsOfLinearConstraint(int c, ConstraintProto* ct);

  // 检测并转换形式为:“X = sum Boolean * value”的约束,
  // 其中“sum Boolean <= 1”。
  //
  // 请注意,这个函数不是特别快,因此不应该经常调用。
  void ExtractEncodingFromLinear();
  bool ProcessEncodingFromLinear(int linear_encoding_ct_index,
                                 const ConstraintProto& at_most_or_exactly_one,
                                 int64_t* num_unique_terms,
                                 int64_t* num_multiple_terms);

  // 移除重复约束。这还合并具有重复线性表达式的线性约束的定义域。
  void DetectDuplicateConstraints();

  // 检测线性约束是否“包含”在另一个约束中,并进行相关的预处理。
  void DetectDominatedLinearConstraints();

  // 如果变量x的域由当前约束隐含,则返回true。
  //
  // 警告:这可能会扫描x出现的所有约束条目。
  // 工作量将增加与扫描的条目数相应地增加。
  bool IsImpliedFree(const std::vector<int>& constraints, int x,
                     int64_t* work_done);

  // 检测包含仅包含线性约束的两列的共享大量公共条目的情况。
  // 然后,在有前途的候选项上调用PerformFreeColumnSubstitution(),
  // 以减少非零元素的总数。
  void DetectOverlappingColumns();

  // 假设x是被暗示自由的,且仅出现在线性约束中,此时将x替换为x + factor * y。
  // 这假定我们预先计算了其中出现x的所有约束以及x内的系数。
  void PerformFreeColumnSubstitution(
      const std::vector<std::pair<int, int64_t>>& constraints_with_x_coeff,
      int x, int y, int64_t factor);

  // SetPPC代表了集合打包、分割和覆盖约束。它们分别是sum of booleans <=、= 和 >= 1。
  // 我们检测这些约束的包含关系,从而进行一些简化。
  void ProcessSetPPC();

  // 删除支配约束或为给定的两个相互包含的setppc约束修复一些变量。
  bool ProcessSetPPCSubset(int subset_c, int superset_c,
                           absl::flat_hash_set<int>* tmp_set,
                           bool* remove_subset, bool* remove_superset,
                           bool* stop_processing_superset);

  // 运行SAT特定的预处理代码。
  void PresolvePureSatPart();

  // 从线性约束中提取AtMostOne约束。
  void ExtractAtMostOneFromLinear(ConstraintProto* ct);

  // 如果约束发生了变化,则返回true。
  bool DivideLinearByGcd(ConstraintProto* ct);

  void ExtractEnforcementLiteralFromLinearConstraint(int ct_index,
                                                     ConstraintProto* ct);
  void LowerThanCoeffStrengthening(bool from_lower_bound, int64_t min_magnitude,
                                   int64_t threshold, ConstraintProto* ct);

  // 从bool_and和small at_most_one约束中提取团,并将其转换为最大团。
  void TransformIntoMaxCliques();

  // 将bool_or和size为2的at_most_one转换为bool_and。
  void ExtractBoolAnd();

  void ExpandObjective();

  void ProcessVariableOnlyUsedInEncoding(int var);
  void TryToSimplifyDomain(int var);

  void LookAtVariableWithDegreeTwo(int var);
  void ProcessVariableInTwoAtMostOrExactlyOne(int var);

  void MergeNoOverlapConstraints();

  // 合并仅在一个文字上不同的子句的启发式方法。
  // 这种思路是将一堆子句合并为一个bool_and。
  // 这样做有几个目的:
  // - 模型更小。
  // - 更强大的对偶推理,因为锁定较少。
  // - 如果布尔值的否定在at_most_one中至多出现一次,我们将获得更强的LP松弛度。
  //
  // 用户注意:如果合并过程成功,我们可能希望为这样的bool_and开发自定义推理器。
  // 理论上,与子句的两个观察文字方案相比,它应该更有效率。调查一下!
  void MergeClauses();

  // 这两个函数都负责处理仿射关系。
  // 第二个函数返回false表示UNSAT。
  void EncodeAllAffineRelations();
  bool PresolveAffineRelationIfAny(int var);

  bool ExploitEquivalenceRelations(int c, ConstraintProto* ct);

  ABSL_MUST_USE_RESULT bool RemoveConstraint(ConstraintProto* ct);
  ABSL_MUST_USE_RESULT bool MarkConstraintAsFalse(ConstraintProto* ct);

  std::vector<int>* postsolve_mapping_;
  PresolveContext* context_;
  SolverLogger* logger_;

  // 用于CanonicalizeLinearExpressionInternal()。
  std::vector<std::pair<int, int64_t>> tmp_terms_;

  // 用于DetectAndProcessAtMostOneInLinear()。
  std::vector<std::array<int64_t, 2>> conditional_mins_;
  std::vector<std::array<int64_t, 2>> conditional_maxs_;

  // 用于ProcessSetPPCSubset()和DetectAndProcessAtMostOneInLinear()以在内部传播具有at_most_one或exactly_one的线性。
  absl::flat_hash_map<int, int> temp_map_;
  absl::flat_hash_set<int> temp_set_;
  ConstraintProto temp_ct_;

  // 用于TryToReduceCoefficientsOfLinearConstraint()。
  MaxBoundedSubsetSum lb_feasible_;
  MaxBoundedSubsetSum lb_infeasible_;
  MaxBoundedSubsetSum ub_feasible_;
  MaxBoundedSubsetSum ub_infeasible_;
};

// 此辅助类执行从模型和部分赋值到另一个模型的复制和简化。
// 目的是最小化所复制模型的大小,以及减少对内存子系统的压力。
//
// 目前仅由LNS部分使用,但可以与任何生成部分赋值的其他方案一起使用。
class ModelCopy {
   
 public:
  explicit ModelCopy(PresolveContext* context);

  // 将所有约束从in_model复制到上下文的工作模型。
  //
  // 在此过程中,它将从上下文中读取变量域,并简化约束以最小化所复制模型的大小。
  // 因此,重要的是context->working_model已经复制了变量部分。
  //
  // 如果模型被证明不可行,则返回false。
  //
  // 它不清除上下文的工作模型的约束部分。
  //
  // 注意(用户):如果first_copy为true,我们将重新安排调度约束,以便它们仅引用先前定义的间隔。
  // 这样可以在后续的预处理步骤中更高效地进行操作。
  bool ImportAndSimplifyConstraints(const CpModelProto& in_model,
                                    const std::vector<int>& ignored_constraints,
                                    bool first_copy = false);

  // 将变量从in_model复制到工作模型。
  // 它从上下文中读取'ignore_names'参数,并相应地保留或删除名称。
  void ImportVariablesAndMaybeIgnoreNames(const CpModelProto& in_model);

 private:
  // 覆盖out_model使其不可满足。返回false。
  bool CreateUnsatModel();

  void CopyEnforcementLiterals(const ConstraintProto& orig,
                               ConstraintProto* dest);
  bool OneEnforcementLiteralIsFalse(const ConstraintProto& ct) const;

  // 所有这些函数都返回false,如果约束被发现是不可行的。
  bool CopyBoolOr(const ConstraintProto& ct);
  bool CopyBoolOrWithDupSupport(const ConstraintProto& ct);
  bool CopyBoolAnd(const ConstraintProto& ct);
  bool CopyLinear(const ConstraintProto& ct);
  bool CopyAtMostOne(const ConstraintProto& ct);
  bool CopyExactlyOne(const ConstraintProto& ct);
  bool CopyInterval(const ConstraintProto& ct, int c, bool ignore_names);

  // 这些函数删除未执行的间隔。请注意,它们要求间隔先出现(经过验证),
  // 因为它们测试未执行通过测试interval_mapping_是否为空。
  void CopyAndMapNoOverlap(const ConstraintProto& ct);
  void CopyAndMapNoOverlap2D(const ConstraintProto& ct);
  void CopyAndMapCumulative(const ConstraintProto& ct);

  PresolveContext* context_;
  int64_t skipped_non_zero_ = 0;

  // 临时向量。
std::vector<int> non_fixed_variables_;
std::vector<int64_t> non_fixed_coefficients_;
absl::flat_hash_map<int, int> interval_mapping_;
int starting_constraint_index_ = 0;
std::vector<int> temp_enforcement_literals_;

std::vector<int> temp_literals_;
absl::flat_hash_set<int> tmp_literals_set_;
};

// 将in_model复制到presolve上下文中的模型。
// 它在运行时进行简化,并且如果模型被证明不可行,则返回false。
// 它读取参数'ignore_names',并根据需要保留或删除变量和约束名称。
//
// 这只应在用户给定模型的第一个副本上调用。
// 注意,这会重新排列使用间隔的所有约束放在最后。我们失去了用户定义的顺序,但希望这不会太重要。
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto& in_model,
                                             PresolveContext* context);

// 复制除变量和约束部分以外的模型的所有内容。
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(
    const CpModelProto& in_model, PresolveContext* context);

// 方便的包装器来调用完整的presolve。
CpSolverStatus PresolveCpModel(PresolveContext* context,
                               std::vector<int>* postsolve_mapping);

// 返回给定proto中重复约束的索引,
// 每对的第一个元素是重复约束中的第一个元素"代表"。
//
// 忽略空约束。我们还做了一些额外的工作:
// - 在比较约束时,我们忽略名称。
// - 对于线性约束,我们忽略域。这是因为如果约束相同,我们可以将它们合并。
// - 如果线性约束与目标平行且没有强制执法文字,我们返回特殊的kObjectiveConstraint (< 0)代表。
//   这样的约束的域可以与目标域合并。
//
// 如果ignore_enforcement为true,则忽略执行文字,但不进行线性域或目标特殊情况处理。
// 这允许处理一些其他情况,例如:
// - 实施的约束重复非执行的约束。
// - 两个实施的约束具有单个执行(vpphard)。
//
// 可见于此以进行测试。这意味着在规范化约束之后调用。
std::vector<std::pair<int, int>> FindDuplicateConstraints(
    const CpModelProto& model_proto, bool ignore_enforcement = false);

}  // namespace sat
}  // namespace operations_research

#endif  // OR_TOOLS_SAT_CP_MODEL_PRESOLVE_H_

cp_model_presolve .cc

#include "ortools/sat/cp_model_presolve.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <deque>
#include <iostream>
#include <limits>
#include <numeric>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "absl/base/attributes.h"
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/hash/hash.h"
#include "absl/numeric/int128.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/mathutil.h"
#include "ortools/base/stl_util.h"
#include "ortools/base/timer.h"
#include "ortools/sat/circuit.h"
#include "ortools/sat/clause.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_checker.h"
#include "ortools/sat/cp_model_expand.h"
#include "ortools/sat/cp_model_mapping.h"
#include "ortools/sat/cp_model_symmetries.h"
#include "ortools/sat/cp_model_utils.h"
#include "ortools/sat/diffn_util.h"
#include "ortools/sat/diophantine.h"
#include "ortools/sat/inclusion.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/model.h"
#include "ortools/sat/presolve_context.h"
#include "ortools/sat/presolve_util.h"
#include "ortools/sat/probing.h"
#include "ortools/sat/sat_base.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/sat/sat_solver.h"
#include "ortools/sat/simplification.h"
#include "ortools/sat/util.h"
#include "ortools/sat/var_domination.h"
#include "ortools/util/affine_relation.h"
#include "ortools/util/bitset.h"
#include "ortools/util/logging.h"
#include "ortools/util/saturated_arithmetic.h"
#include "ortools/util/sorted_interval_list.h"
#include "ortools/util/time_limit.h"

namespace operations_research {
   
namespace sat {
   

bool CpModelPresolver::RemoveConstraint(ConstraintProto* ct) {
   
  ct->Clear();
  return true;
}

// 移除所有空的约束。需要注意重新映射区间引用。
//
// 现在这些约束已经完成了它们的目的,我们还会移除虚拟约束,否则这会导致问题,因为我们的模型在测试中是无效的。
void CpModelPresolver::RemoveEmptyConstraints() {
   
  std::vector<int> interval_mapping(context_->working_model->constraints_size(),
                                    -1);
  int new_num_constraints = 0;
  const int old_num_non_empty_constraints =
      context_->working_model->constraints_size();
  for (int c = 0; c < old_num_non_empty_constraints; ++c) {
   
    const auto type = context_->working_model->constraints(c).constraint_case();
    if (type == ConstraintProto::CONSTRAINT_NOT_SET) continue;
    if (type == ConstraintProto::kDummyConstraint) continue;
    if (type == ConstraintProto::kInterval) {
   
      interval_mapping[c] = new_num_constraints;
    }
    context_->working_model->mutable_constraints(new_num_constraints++)
        ->Swap(context_->working_model->mutable_constraints(c));
  }
  context_->working_model->mutable_constraints()->DeleteSubrange(
      new_num_constraints, old_num_non_empty_constraints - new_num_constraints);
  for (ConstraintProto& ct_ref :
       *context_->working_model->mutable_constraints()) {
   
    ApplyToAllIntervalIndices(
        [&interval_mapping](int* ref) {
   
          *ref = interval_mapping[*ref];
          CHECK_NE(-1, *ref);
        },
        &ct_ref);
  }
}

bool CpModelPresolver::PresolveEnforcementLiteral(ConstraintProto* ct) {
   
  if (context_->ModelIsUnsat()) return false;
  if (!HasEnforcementLiteral(*ct)) return false;

  int new_size = 0;
  const int old_size = ct->enforcement_literal().size();
  context_->tmp_literal_set.clear();
  for (const int literal : ct->enforcement_literal()) {
   
    if (context_->LiteralIsTrue(literal)) {
   
      // 我们可以移除一个为真的文字。
      context_->UpdateRuleStats("enforcement: true literal");
      continue;
    }

    if (context_->LiteralIsFalse(literal)) {
   
      context_->UpdateRuleStats("enforcement: false literal");
      return RemoveConstraint(ct);
    }

    if (context_->VariableIsUniqueAndRemovable(literal)) {
   
      // 我们可以将其设置为假并忽略此约束。
      context_->UpdateRuleStats("enforcement: literal not used");
      CHECK(context_->SetLiteralToFalse(literal));
      return RemoveConstraint(ct);
    }

    // 如果文字仅出现在目标函数中,我们可能能够将其固定为假。如果文字始终以相同的极性出现,这种情况下需要进行推广。
    if (context_->VariableWithCostIsUniqueAndRemovable(literal)) {
   
      const int64_t obj_coeff =
          context_->ObjectiveMap().at(PositiveRef(literal));
      if (RefIsPositive(literal) == (obj_coeff > 0)) {
   
        // 将其设置为假更有利!
        context_->UpdateRuleStats("enforcement: literal with unique direction");
        CHECK(context_->SetLiteralToFalse(literal));
        return RemoveConstraint(ct);
      }
    }

    // 处理重复的文字。
    //
    // 理想情况下,在第一次复制期间仅执行一次此操作,并且在以后永远不会创建这样的约束。
    if (old_size > 1) {
   
      const auto [_, inserted] = context_->tmp_literal_set.insert(literal);
      if (!inserted) {
   
        context_->UpdateRuleStats("enforcement: removed duplicate literal");
        continue;
      }
      if (context_->tmp_literal_set.contains(NegatedRef(literal))) {
   
        context_->UpdateRuleStats("enforcement: can never be true");
        return RemoveConstraint(ct);
      }
    }

    ct->set_enforcement_literal(new_size++, literal);
  }
  ct->mutable_enforcement_literal()->Truncate(new_size);
  return new_size != old_size;
}

bool CpModelPresolver::PresolveBoolXor(ConstraintProto* ct) {
   
  if (context_->ModelIsUnsat()) return false;
  if (HasEnforcementLiteral(*ct)) return false;

  int new_size = 0;
  bool changed = false;
  int num_true_literals = 0;
  int true_literal = std::numeric_limits<int32_t>::min();
  for (const int literal : ct->bool_xor().literals()) {
   
    if (context_->VariableIsUniqueAndRemovable(literal)) {
   
      context_->UpdateRuleStats("TODO bool_xor: remove constraint");
    }

    if (context_->LiteralIsFalse(literal)) {
   
      context_->UpdateRuleStats("bool_xor: remove false literal");
      changed = true;
      continue;
    } else if (context_->LiteralIsTrue(literal)) {
   
      true_literal = literal;  // 如果需要,保留下来。
      num_true_literals++;
      continue;
    }

    ct->mutable_bool_xor()->set_literals(new_size++, literal);
  }

  if (new_size == 0) {
   
    if (num_true_literals % 2 == 0) {
   
      return context_->NotifyThatModelIsUnsat("bool_xor: always false");
    } else {
   
      context_->UpdateRuleStats("bool_xor: always true");
      return RemoveConstraint(ct);
    }
  } else if (new_size == 1) {
     // 我们可以修复唯一的文字。
    if (num_true_literals % 2 == 0) {
   
      if (!context_->SetLiteralToTrue(ct->bool_xor().literals(0))) {
   
        return context_->NotifyThatModelIsUnsat(
            "bool_xor: cannot fix last literal");
      }
    } else {
   
      if (!context_->SetLiteralToFalse(ct->bool_xor().literals(0))) {
   
        return context_->NotifyThatModelIsUnsat(
            "bool_xor: cannot fix last literal");
      }
    }
    context_->UpdateRuleStats("bool_xor: one active literal");
    return RemoveConstraint(ct);
  } 
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BigDataMLApplication

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

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

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

打赏作者

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

抵扣说明:

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

余额充值