sync with main
This commit is contained in:
@@ -474,8 +474,8 @@ ABSL_MUST_USE_RESULT Status RevisedSimplex::SolveInternal(
|
||||
// it is hard to claim we are really unbounded. This is a quick
|
||||
// heuristic to error on the side of optimality rather than
|
||||
// unboundedness.
|
||||
double max_magnitude = 0.0;
|
||||
double min_distance = kInfinity;
|
||||
Fractional max_magnitude = 0.0;
|
||||
Fractional min_distance = kInfinity;
|
||||
const DenseRow& lower_bounds = variables_info_.GetVariableLowerBounds();
|
||||
const DenseRow& upper_bounds = variables_info_.GetVariableUpperBounds();
|
||||
Fractional cost_delta = 0.0;
|
||||
|
||||
@@ -237,8 +237,8 @@ bool LPParser::ParseConstraint(StringPiece constraint) {
|
||||
|
||||
namespace {
|
||||
|
||||
template<class>
|
||||
constexpr bool dependent_false = false; // workaround before CWG2518/P2593R1
|
||||
template <class>
|
||||
constexpr bool dependent_false = false; // workaround before CWG2518/P2593R1
|
||||
|
||||
template <typename T>
|
||||
bool SimpleAtoFractional(absl::string_view str, T* value) {
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "ortools/sat/model.h"
|
||||
#include "ortools/sat/no_overlap_2d_helper.h"
|
||||
#include "ortools/sat/precedences.h"
|
||||
#include "ortools/sat/sat_base.h"
|
||||
#include "ortools/sat/scheduling_helpers.h"
|
||||
#include "ortools/sat/synchronization.h"
|
||||
|
||||
@@ -58,10 +57,10 @@ void Precedences2DPropagator::CollectPairsOfBoxesWithNonTrivialDistance() {
|
||||
for (int dim = 0; dim < 2; ++dim) {
|
||||
const SchedulingConstraintHelper& dim_helper =
|
||||
dim == 0 ? helper_.x_helper() : helper_.y_helper();
|
||||
for (int i = 0; i < helper_.NumBoxes(); ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
const absl::Span<const AffineExpression> interval_points =
|
||||
i == 0 ? dim_helper.Starts() : dim_helper.Ends();
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
j == 0 ? dim_helper.Starts() : dim_helper.Ends();
|
||||
for (int i = 0; i < helper_.NumBoxes(); ++i) {
|
||||
if (interval_points[i].var != kNoIntegerVariable) {
|
||||
var_to_box_and_coeffs[PositiveVariable(interval_points[i].var)]
|
||||
.boxes[dim][j]
|
||||
@@ -89,13 +88,14 @@ void Precedences2DPropagator::CollectPairsOfBoxesWithNonTrivialDistance() {
|
||||
dim == 0 ? helper_.x_helper() : helper_.y_helper();
|
||||
for (const int box1 : usage1.boxes[dim][0 /* start */]) {
|
||||
for (const int box2 : usage2.boxes[dim][1 /* end */]) {
|
||||
if (box1 == box2) continue;
|
||||
const AffineExpression& start = dim_helper.Starts()[box1];
|
||||
const AffineExpression& end = dim_helper.Ends()[box2];
|
||||
LinearExpression2 expr2;
|
||||
expr2.vars[0] = start.var;
|
||||
expr2.vars[1] = NegationOf(end.var);
|
||||
expr2.vars[1] = end.var;
|
||||
expr2.coeffs[0] = start.coeff;
|
||||
expr2.coeffs[1] = end.coeff;
|
||||
expr2.coeffs[1] = -end.coeff;
|
||||
expr2.SimpleCanonicalization();
|
||||
expr2.DivideByGcd();
|
||||
if (expr == expr2) {
|
||||
@@ -133,7 +133,8 @@ bool Precedences2DPropagator::Propagate() {
|
||||
for (const auto& [box1, box2] : non_trivial_pairs_) {
|
||||
DCHECK(box1 < helper_.NumBoxes());
|
||||
DCHECK(box2 < helper_.NumBoxes());
|
||||
if (!helper_.IsPresent(box1) && !helper_.IsPresent(box2)) {
|
||||
DCHECK_NE(box1, box2);
|
||||
if (!helper_.IsPresent(box1) || !helper_.IsPresent(box2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -148,9 +149,9 @@ bool Precedences2DPropagator::Propagate() {
|
||||
}
|
||||
LinearExpression2 expr;
|
||||
expr.vars[0] = helper->Starts()[b1].var;
|
||||
expr.vars[1] = NegationOf(helper->Ends()[b2].var);
|
||||
expr.vars[1] = helper->Ends()[b2].var;
|
||||
expr.coeffs[0] = helper->Starts()[b1].coeff;
|
||||
expr.coeffs[1] = helper->Ends()[b2].coeff;
|
||||
expr.coeffs[1] = -helper->Ends()[b2].coeff;
|
||||
const IntegerValue ub_of_start_minus_end_value =
|
||||
binary_relations_maps_->UpperBound(expr) +
|
||||
helper->Starts()[b1].constant - helper->Ends()[b2].constant;
|
||||
@@ -158,8 +159,8 @@ bool Precedences2DPropagator::Propagate() {
|
||||
is_unfeasible = false;
|
||||
break;
|
||||
}
|
||||
if (!is_unfeasible) break;
|
||||
}
|
||||
if (!is_unfeasible) break;
|
||||
}
|
||||
if (!is_unfeasible) continue;
|
||||
|
||||
@@ -167,8 +168,6 @@ bool Precedences2DPropagator::Propagate() {
|
||||
|
||||
helper_.ClearReason();
|
||||
num_conflicts_++;
|
||||
std::vector<IntegerLiteral> reason;
|
||||
std::vector<Literal> lit_reason;
|
||||
|
||||
for (int dim = 0; dim < 2; dim++) {
|
||||
SchedulingConstraintHelper* helper = helpers[dim];
|
||||
@@ -180,23 +179,18 @@ bool Precedences2DPropagator::Propagate() {
|
||||
}
|
||||
LinearExpression2 expr;
|
||||
expr.vars[0] = helper->Starts()[b1].var;
|
||||
expr.vars[1] = NegationOf(helper->Ends()[b2].var);
|
||||
expr.vars[1] = helper->Ends()[b2].var;
|
||||
expr.coeffs[0] = helper->Starts()[b1].coeff;
|
||||
expr.coeffs[1] = helper->Ends()[b2].coeff;
|
||||
expr.coeffs[1] = -helper->Ends()[b2].coeff;
|
||||
binary_relations_maps_->AddReasonForUpperBoundLowerThan(
|
||||
expr,
|
||||
-(helper->Starts()[b1].constant - helper->Ends()[b2].constant),
|
||||
&lit_reason, &reason);
|
||||
-(helper->Starts()[b1].constant - helper->Ends()[b2].constant) - 1,
|
||||
helper_.x_helper().MutableLiteralReason(),
|
||||
helper_.x_helper().MutableIntegerReason());
|
||||
}
|
||||
}
|
||||
helper_.AddPresenceReason(box1);
|
||||
helper_.AddPresenceReason(box2);
|
||||
helper_.x_helper().MutableIntegerReason()->insert(
|
||||
helper_.x_helper().MutableIntegerReason()->end(), reason.begin(),
|
||||
reason.end());
|
||||
helper_.x_helper().MutableLiteralReason()->insert(
|
||||
helper_.x_helper().MutableLiteralReason()->end(), lit_reason.begin(),
|
||||
lit_reason.end());
|
||||
return helper_.ReportConflict();
|
||||
}
|
||||
return true;
|
||||
@@ -205,7 +199,7 @@ bool Precedences2DPropagator::Propagate() {
|
||||
int Precedences2DPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
|
||||
const int id = watcher->Register(this);
|
||||
helper_.WatchAllBoxes(id);
|
||||
// TODO(user): add an API to BinaryRelationsMaps to watch linear2
|
||||
binary_relations_maps_->WatchAllLinearExpressions2(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@@ -1407,9 +1407,8 @@ void ProcessOneCompressedColumn(
|
||||
|
||||
// Simpler encoding for table constraints with 2 variables.
|
||||
void AddSizeTwoTable(
|
||||
const std::vector<int>& vars,
|
||||
const std::vector<std::vector<int64_t>>& tuples,
|
||||
const std::vector<absl::flat_hash_set<int64_t>>& values_per_var,
|
||||
absl::Span<const int> vars, absl::Span<const std::vector<int64_t>> tuples,
|
||||
absl::Span<const absl::flat_hash_set<int64_t>> values_per_var,
|
||||
PresolveContext* context) {
|
||||
CHECK_EQ(vars.size(), 2);
|
||||
const int left_var = vars[0];
|
||||
|
||||
@@ -188,7 +188,6 @@ void AddExtraSchedulingPropagators(SatParameters& new_params) {
|
||||
new_params.set_use_energetic_reasoning_in_no_overlap_2d(true);
|
||||
new_params.set_use_area_energetic_reasoning_in_no_overlap_2d(true);
|
||||
new_params.set_use_try_edge_reasoning_in_no_overlap_2d(true);
|
||||
new_params.set_no_overlap_2d_boolean_relations_limit(100);
|
||||
}
|
||||
|
||||
// We want a random tie breaking among variables with equivalent values.
|
||||
|
||||
@@ -3009,7 +3009,7 @@ CpSolverResponse SolveWithParameters(const CpModelProto& model_proto,
|
||||
}
|
||||
|
||||
CpSolverResponse SolveWithParameters(const CpModelProto& model_proto,
|
||||
const std::string& params) {
|
||||
absl::string_view params) {
|
||||
Model model;
|
||||
model.Add(NewSatParameters(params));
|
||||
return SolveCpModel(model_proto, &model);
|
||||
|
||||
@@ -71,7 +71,7 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model);
|
||||
* format, and returns an instance of CpSolverResponse.
|
||||
*/
|
||||
CpSolverResponse SolveWithParameters(const CpModelProto& model_proto,
|
||||
const std::string& params);
|
||||
absl::string_view params);
|
||||
#endif // !__PORTABLE_PLATFORM__
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "absl/log/vlog_is_on.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "absl/types/span.h"
|
||||
// #include "ortools/base/stl_util.h"
|
||||
#include "ortools/sat/2d_distances_propagator.h"
|
||||
#include "ortools/sat/2d_mandatory_overlap_propagator.h"
|
||||
#include "ortools/sat/2d_orthogonal_packing.h"
|
||||
@@ -277,11 +276,11 @@ void AddNonOverlappingRectangles(const std::vector<IntervalVariable>& x,
|
||||
DCHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
|
||||
|
||||
for (int i = 0; i < num_boxes; ++i) {
|
||||
if (repository->IsAbsent(x[i])) continue;
|
||||
if (repository->IsAbsent(y[i])) continue;
|
||||
if (repository->IsOptional(x[i])) continue;
|
||||
if (repository->IsOptional(y[i])) continue;
|
||||
for (int j = i + 1; j < num_boxes; ++j) {
|
||||
if (repository->IsAbsent(x[j])) continue;
|
||||
if (repository->IsAbsent(y[j])) continue;
|
||||
if (repository->IsOptional(x[j])) continue;
|
||||
if (repository->IsOptional(y[j])) continue;
|
||||
|
||||
// At most one of these two x options is true.
|
||||
const Literal x_ij = repository->GetOrCreatePrecedenceLiteral(
|
||||
@@ -308,21 +307,7 @@ void AddNonOverlappingRectangles(const std::vector<IntervalVariable>& x,
|
||||
}
|
||||
|
||||
// At least one of the 4 options is true.
|
||||
std::vector<Literal> clause = {x_ij, x_ji, y_ij, y_ji};
|
||||
if (repository->IsOptional(x[i])) {
|
||||
clause.push_back(repository->PresenceLiteral(x[i]).Negated());
|
||||
}
|
||||
if (repository->IsOptional(y[i])) {
|
||||
clause.push_back(repository->PresenceLiteral(y[i]).Negated());
|
||||
}
|
||||
if (repository->IsOptional(x[j])) {
|
||||
clause.push_back(repository->PresenceLiteral(x[j]).Negated());
|
||||
}
|
||||
if (repository->IsOptional(y[j])) {
|
||||
clause.push_back(repository->PresenceLiteral(y[j]).Negated());
|
||||
}
|
||||
gtl::STLSortAndRemoveDuplicates(&clause);
|
||||
if (!sat_solver->AddProblemClause(clause)) {
|
||||
if (!sat_solver->AddProblemClause({x_ij, x_ji, y_ij, y_ji})) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +389,8 @@ LinearPropagator::LinearPropagator(Model* model)
|
||||
random_(model->GetOrCreate<ModelRandomGenerator>()),
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()),
|
||||
watcher_id_(watcher_->Register(this)),
|
||||
order_(random_, [this](int id) { return GetVariables(infos_[id]); }) {
|
||||
order_(random_, time_limit_,
|
||||
[this](int id) { return GetVariables(infos_[id]); }) {
|
||||
// Note that we need this class always in sync.
|
||||
integer_trail_->RegisterWatcher(&modified_vars_);
|
||||
integer_trail_->RegisterReversibleClass(this);
|
||||
@@ -475,6 +476,8 @@ void LinearPropagator::SetPropagatedBy(IntegerVariable var, int id) {
|
||||
|
||||
void LinearPropagator::OnVariableChange(IntegerVariable var, IntegerValue lb,
|
||||
int id) {
|
||||
DCHECK_EQ(lb, integer_trail_->LowerBound(var));
|
||||
|
||||
// If no constraint use this var, we just ignore it.
|
||||
const int size = var_to_constraint_ids_[var].size();
|
||||
if (size == 0) return;
|
||||
@@ -1331,7 +1334,7 @@ bool LinearPropagator::DisassembleSubtree(int root_id, int num_tight) {
|
||||
if (next_increase > 0) {
|
||||
disassemble_queue_.push_back({id, next_var, next_increase});
|
||||
|
||||
// We know this will push later, so we register hit with a sentinel
|
||||
// We know this will push later, so we register it with a sentinel
|
||||
// value so that it do not block any earlier propagation. Hopefully,
|
||||
// adding this "dependency" should help find a better propagation
|
||||
// order.
|
||||
|
||||
@@ -149,16 +149,19 @@ class EnforcementPropagator : public SatPropagator {
|
||||
// Each constraint might push some variables which might in turn make other
|
||||
// constraint tighter. In general, it seems better to make sure we push first
|
||||
// constraints that are not affected by other variables and delay the
|
||||
// propagation of constraint that we know will become tigher.
|
||||
// propagation of constraint that we know will become tigher. This also likely
|
||||
// simplifies the reasons.
|
||||
//
|
||||
// Note that we can have cycle in this graph, and that this is not necessarily a
|
||||
// conflict.
|
||||
class ConstraintPropagationOrder {
|
||||
public:
|
||||
ConstraintPropagationOrder(
|
||||
ModelRandomGenerator* random,
|
||||
ModelRandomGenerator* random, TimeLimit* time_limit,
|
||||
std::function<absl::Span<const IntegerVariable>(int)> id_to_vars)
|
||||
: random_(random), id_to_vars_func_(std::move(id_to_vars)) {}
|
||||
: random_(random),
|
||||
time_limit_(time_limit),
|
||||
id_to_vars_func_(std::move(id_to_vars)) {}
|
||||
|
||||
void Resize(int num_vars, int num_ids) {
|
||||
var_has_entry_.Resize(IntegerVariable(num_vars));
|
||||
@@ -166,7 +169,7 @@ class ConstraintPropagationOrder {
|
||||
var_to_lb_.resize(num_vars);
|
||||
var_to_pos_.resize(num_vars);
|
||||
|
||||
in_ids_.Resize(num_ids);
|
||||
in_ids_.resize(num_ids);
|
||||
}
|
||||
|
||||
void Register(int id, IntegerVariable var, IntegerValue lb) {
|
||||
@@ -203,15 +206,17 @@ class ConstraintPropagationOrder {
|
||||
// Return -1 if there is none.
|
||||
// This returns a constraint with min degree.
|
||||
//
|
||||
// TODO(user): fix quadratic algo? We can use var_to_ids_func_() to maintain
|
||||
// the degree. But note that with the start_ optim and because we expect
|
||||
// mainly degree zero, this seems to be faster.
|
||||
// TODO(user): fix quadratic or even linear algo? We can use
|
||||
// var_to_ids_func_() to maintain the degree. But note that since we reorder
|
||||
// constraints and because we expect mainly degree zero, this seems to be
|
||||
// faster.
|
||||
int NextId() {
|
||||
if (ids_.empty()) return -1;
|
||||
|
||||
int best_id = 0;
|
||||
int best_num_vars = 0;
|
||||
int best_degree = std::numeric_limits<int>::max();
|
||||
int64_t work_done = 0;
|
||||
const int size = ids_.size();
|
||||
const auto var_has_entry = var_has_entry_.const_view();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
@@ -219,10 +224,28 @@ class ConstraintPropagationOrder {
|
||||
ids_.pop_front();
|
||||
DCHECK(in_ids_[id]);
|
||||
|
||||
// By degree, we mean the number of variables of the constraint that do
|
||||
// not have yet their lower bounds up to date; they will be pushed by
|
||||
// other constraints as we propagate them. If possible, we want to delay
|
||||
// the propagation of a constraint with positive degree until all involved
|
||||
// lower bounds are up to date (i.e. degree == 0).
|
||||
int degree = 0;
|
||||
absl::Span<const IntegerVariable> vars = id_to_vars_func_(id);
|
||||
work_done += vars.size();
|
||||
for (const IntegerVariable var : vars) {
|
||||
if (var_has_entry[var]) ++degree;
|
||||
if (var_has_entry[var]) {
|
||||
if (var_has_entry[NegationOf(var)] &&
|
||||
var_to_id_[NegationOf(var)] == id) {
|
||||
// We have two constraints, this one (id) push NegationOf(var), and
|
||||
// var_to_id_[var] push var. So whichever order we choose, the first
|
||||
// constraint will need to be scanned at least twice. Lets not count
|
||||
// this situation in the degree.
|
||||
continue;
|
||||
}
|
||||
|
||||
DCHECK_NE(var_to_id_[var], id);
|
||||
++degree;
|
||||
}
|
||||
}
|
||||
|
||||
// We select the min-degree and prefer lower constraint size.
|
||||
@@ -241,6 +264,11 @@ class ConstraintPropagationOrder {
|
||||
ids_.push_back(id);
|
||||
}
|
||||
|
||||
if (work_done > 100) {
|
||||
time_limit_->AdvanceDeterministicTime(static_cast<double>(work_done) *
|
||||
5e-9);
|
||||
}
|
||||
|
||||
// We didn't find any degree zero, we scanned the whole queue.
|
||||
// Extract best_id while keeping the order stable.
|
||||
//
|
||||
@@ -277,6 +305,7 @@ class ConstraintPropagationOrder {
|
||||
|
||||
public:
|
||||
ModelRandomGenerator* random_;
|
||||
TimeLimit* time_limit_;
|
||||
std::function<absl::Span<const IntegerVariable>(int)> id_to_vars_func_;
|
||||
|
||||
// For each variable we only keep the constraint id that pushes it further.
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/stl_util.h"
|
||||
@@ -274,7 +275,7 @@ class OpbReader {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ParseIndex(const std::string& word) {
|
||||
static int ParseIndex(absl::string_view word) {
|
||||
int index;
|
||||
CHECK(absl::SimpleAtoi(word, &index));
|
||||
return index;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Solves an assignment problem for given group of workers."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import io
|
||||
import pandas as pd
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Solves a simple assignment problem."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Solves a simple assignment problem."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# [START program]
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import io
|
||||
import pandas as pd
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ This problem has 72 different solutions in base 10.
|
||||
"""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Simple solve."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# [START import]
|
||||
import collections
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Solves a multiple knapsack problem using the CP-SAT solver."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import sys
|
||||
import time
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
@@ -54,6 +55,7 @@ class NQueenSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
print()
|
||||
print()
|
||||
|
||||
|
||||
# [END solution_printer]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Example of a simple nurse scheduling problem."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
from typing import Union
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Simple solve."""
|
||||
# [START import]
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -1337,21 +1337,19 @@ CompletionTimeExplorationStatus ComputeMinSumOfWeightedEndMins(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!helper.valid_permutation_iterator_.Init()) {
|
||||
return CompletionTimeExplorationStatus::NO_VALID_PERMUTATION;
|
||||
}
|
||||
|
||||
bool is_dag = false;
|
||||
int num_valid_permutations = 0;
|
||||
do {
|
||||
for (const auto& permutation : helper.valid_permutation_iterator_) {
|
||||
is_dag = true;
|
||||
if (--exploration_credit < 0) break;
|
||||
|
||||
IntegerValue sum_of_ends = 0;
|
||||
IntegerValue sum_of_weighted_ends = 0;
|
||||
|
||||
if (ComputeWeightedSumOfEndMinsOfOnePermutation(
|
||||
events, helper.valid_permutation_iterator_.permutation(),
|
||||
capacity_max, helper, sum_of_ends, sum_of_weighted_ends,
|
||||
cut_use_precedences)) {
|
||||
events, permutation, capacity_max, helper, sum_of_ends,
|
||||
sum_of_weighted_ends, cut_use_precedences)) {
|
||||
min_sum_of_ends = std::min(ToDouble(sum_of_ends), min_sum_of_ends);
|
||||
min_sum_of_weighted_ends =
|
||||
std::min(ToDouble(sum_of_weighted_ends), min_sum_of_weighted_ends);
|
||||
@@ -1362,7 +1360,10 @@ CompletionTimeExplorationStatus ComputeMinSumOfWeightedEndMins(
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (helper.valid_permutation_iterator_.Increase());
|
||||
}
|
||||
if (!is_dag) {
|
||||
return CompletionTimeExplorationStatus::NO_VALID_PERMUTATION;
|
||||
}
|
||||
const CompletionTimeExplorationStatus status =
|
||||
exploration_credit < 0 ? CompletionTimeExplorationStatus::ABORTED
|
||||
: num_valid_permutations > 0
|
||||
@@ -1383,7 +1384,7 @@ CompletionTimeExplorationStatus ComputeMinSumOfWeightedEndMins(
|
||||
// - detect disjoint tasks (no need to crossover to the second part)
|
||||
// - better caching of explored states
|
||||
ABSL_MUST_USE_RESULT bool GenerateShortCompletionTimeCutsWithExactBound(
|
||||
const std::string& cut_name, std::vector<CompletionTimeEvent> events,
|
||||
absl::string_view cut_name, std::vector<CompletionTimeEvent> events,
|
||||
IntegerValue capacity_max, CtExhaustiveHelper& helper, Model* model,
|
||||
LinearConstraintManager* manager) {
|
||||
TopNCuts top_n_cuts(5);
|
||||
@@ -1476,7 +1477,7 @@ ABSL_MUST_USE_RESULT bool GenerateShortCompletionTimeCutsWithExactBound(
|
||||
is_lifted |= event.lifted;
|
||||
cut.AddTerm(event.end, IntegerValue(1));
|
||||
}
|
||||
std::string full_name = cut_name;
|
||||
std::string full_name(cut_name);
|
||||
if (cut_use_precedences) full_name.append("_prec");
|
||||
if (is_lifted) full_name.append("_lifted");
|
||||
top_n_cuts.AddCut(cut.Build(), full_name, manager->LpValues());
|
||||
@@ -1493,7 +1494,7 @@ ABSL_MUST_USE_RESULT bool GenerateShortCompletionTimeCutsWithExactBound(
|
||||
is_lifted |= event.lifted;
|
||||
cut.AddTerm(event.end, event.energy_min);
|
||||
}
|
||||
std::string full_name = cut_name;
|
||||
std::string full_name(cut_name);
|
||||
if (is_lifted) full_name.append("_lifted");
|
||||
if (cut_use_precedences) full_name.append("_prec");
|
||||
full_name.append("_weighted");
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -984,192 +985,288 @@ inline void CompactVectorVector<K, V>::ResetFromTranspose(
|
||||
//
|
||||
// If the graph has no edges, it will generate all possible permutations.
|
||||
//
|
||||
// If the graph has edges, it will generate all possible permutations of the
|
||||
// dag that are a topological sorting of the graph.
|
||||
// If the graph has edges, it will generate all possible permutations of the dag
|
||||
// that are a topological sorting of the graph.
|
||||
//
|
||||
// The class maintains 5 fields:
|
||||
// - graph_: a vector of vectors, where graph_[i] contains the list of
|
||||
// elements that are adjacent to element i.
|
||||
// - size_: the size of the graph.
|
||||
// Typical usage:
|
||||
//
|
||||
// - missing_parent_numbers_: a vector of integers, where
|
||||
// missing_parent_numbers_[i] is the number of parents of element i that are
|
||||
// not yet in permutation_. Before Init() is called, no element is yet in
|
||||
// permutation_ so that it is the number of parents of i. After Init(), and
|
||||
// before Increase() returns true, it is always 0 (except during the
|
||||
// execution of Increase(), see below).
|
||||
// DagTopologicalSortIterator dag_topological_sort(5);
|
||||
//
|
||||
// - permutation_: a vector of integers, that after Init() is called, and
|
||||
// before Increase() returns false, it is a topological sorting of the graph
|
||||
// (except during the execution of Increase()).
|
||||
// - element_original_position_: a vector of integers, where
|
||||
// element_original_position_[i] is the original position of element i in the
|
||||
// permutation_. See the algorithm below for more details.
|
||||
// dag_topological_sort.AddArc(0, 1);
|
||||
// dag_topological_sort.AddArc(1, 2);
|
||||
// dag_topological_sort.AddArc(3, 4);
|
||||
//
|
||||
// for (const auto& permutation : dag_topological_sort) {
|
||||
// // Do something with each permutation.
|
||||
// }
|
||||
//
|
||||
// Note: to test if there are cycles, it is enough to check if at least one
|
||||
// iteration occurred in the above loop.
|
||||
//
|
||||
// Note 2: adding an arc during an iteration is not supported and the behavior
|
||||
// is undefined.
|
||||
|
||||
class DagTopologicalSortIterator {
|
||||
public:
|
||||
// Graph maps indices to their children. Any children must exist.
|
||||
DagTopologicalSortIterator() : size_(0) {}
|
||||
explicit DagTopologicalSortIterator(int size) : size_(size) { Reset(size); }
|
||||
DagTopologicalSortIterator() = default;
|
||||
|
||||
void Reset(int size) {
|
||||
size_ = size;
|
||||
graph_.assign(size, {});
|
||||
missing_parent_numbers_.assign(size, 0);
|
||||
permutation_.clear();
|
||||
element_original_position_.assign(size, 0);
|
||||
// Graph maps indices to their children. Any children must exist.
|
||||
explicit DagTopologicalSortIterator(int size)
|
||||
: graph_(size, std::vector<int>{}) {}
|
||||
|
||||
// An iterator class to generate all possible topological sorting of a dag.
|
||||
//
|
||||
// If the graph has no edges, it will generate all possible permutations.
|
||||
//
|
||||
// If the graph has edges, it will generate all possible permutations of the
|
||||
// dag that are a topological sorting of the graph.
|
||||
//
|
||||
// The class maintains 5 fields:
|
||||
// - graph_: a vector of vectors, where graph_[i] contains the list of
|
||||
// elements that are adjacent to element i. This is not owned.
|
||||
// - size_: the size of the graph.
|
||||
// - missing_parent_numbers_: a vector of integers, where
|
||||
// missing_parent_numbers_[i] is the number of parents of element i that
|
||||
// are not yet in permutation_. It is always 0 except during the
|
||||
// execution of operator++().
|
||||
// - permutation_: a vector of integers, that is a topological sorting of the
|
||||
// graph except during the execution of operator++().
|
||||
// - element_original_position_: a vector of integers, where
|
||||
// element_original_position_[i] is the original position of element i in
|
||||
// the permutation_. See the algorithm below for more details.
|
||||
|
||||
class Iterator {
|
||||
friend class DagTopologicalSortIterator;
|
||||
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = const std::vector<int>;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
Iterator& operator++();
|
||||
|
||||
friend bool operator==(const Iterator& a, const Iterator& b) {
|
||||
return &a.graph_ == &b.graph_ && a.ordering_index_ == b.ordering_index_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const Iterator& a, const Iterator& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
reference operator*() const { return permutation_; }
|
||||
|
||||
pointer operator->() const { return &permutation_; }
|
||||
|
||||
private:
|
||||
// End iterator.
|
||||
explicit Iterator(const std::vector<std::vector<int>>& graph
|
||||
ABSL_ATTRIBUTE_LIFETIME_BOUND,
|
||||
bool)
|
||||
: graph_(graph), ordering_index_(-1) {}
|
||||
|
||||
// Begin iterator.
|
||||
explicit Iterator(const std::vector<std::vector<int>>& graph
|
||||
ABSL_ATTRIBUTE_LIFETIME_BOUND);
|
||||
|
||||
// Unset the element at pos.
|
||||
void Unset(int pos);
|
||||
|
||||
// Set the element at pos to the element at k.
|
||||
void Set(int pos, int k);
|
||||
|
||||
// Graph maps indices to their children. Children must be in [0, size_).
|
||||
const std::vector<std::vector<int>>& graph_;
|
||||
// Number of elements in graph_.
|
||||
int size_;
|
||||
// For each element in graph_, the number of parents it has that are not yet
|
||||
// in permutation_. In particular, it is always 0 outside of operator++().
|
||||
std::vector<int> missing_parent_numbers_;
|
||||
// The current permutation. It is ensured to be a topological sorting of the
|
||||
// graph outside of operator++().
|
||||
std::vector<int> permutation_;
|
||||
// Keeps track of the original position of the element in permutation_[i].
|
||||
// See the comment above the class for the detailed algorithm.
|
||||
std::vector<int> element_original_position_;
|
||||
|
||||
// Index of the current ordering. Used to compare iterators. It is -1 if the
|
||||
// end has been reached.
|
||||
int64_t ordering_index_;
|
||||
};
|
||||
|
||||
Iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
||||
return Iterator(graph_);
|
||||
}
|
||||
Iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
||||
return Iterator(graph_, true);
|
||||
}
|
||||
|
||||
// Must be called before Init().
|
||||
void Reset(int size) { graph_.assign(size, {}); }
|
||||
|
||||
// Must be called before iteration starts or between iterations.
|
||||
void AddArc(int from, int to) {
|
||||
DCHECK_GE(from, 0);
|
||||
DCHECK_LT(from, size_);
|
||||
DCHECK_LT(from, graph_.size());
|
||||
DCHECK_GE(to, 0);
|
||||
DCHECK_LT(to, size_);
|
||||
DCHECK_LT(to, graph_.size());
|
||||
graph_[from].push_back(to);
|
||||
missing_parent_numbers_[to]++;
|
||||
}
|
||||
|
||||
// To describe the algorithm in Increase() and Init(), we consider the
|
||||
// following invariant, called Invariant(pos) for a position pos in [0,
|
||||
// size_):
|
||||
// 1. permutations_[0], ..., permutations_[pos] form a prefix of a
|
||||
// topological ordering of the graph;
|
||||
// 2. permutations_[pos + 1], ..., permutations_.back() are all other
|
||||
// elements that have all their parents in permutations_[0], ...,
|
||||
// permutations_[pos], ordered lexicographically by the index of their
|
||||
// last parent in permutations_[0], ... permutations_[pos] and then by
|
||||
// their index in the graph;
|
||||
// 3. missing_parent_numbers_[i] is the number of parents of element i that
|
||||
// are not in {permutations_[0], ..., permutations_[pos]}.
|
||||
// 4. element_original_position_[i] is the original position of element i of
|
||||
// the permutation following the order described in 2. In particular,
|
||||
// element_original_position_[i] = i for i > pos.
|
||||
// Set and Unset maintain these invariants.
|
||||
|
||||
// Precondition: Invariant(size_ - 1) holds.
|
||||
// Postcondition: Invariant(size_ - 1) holds if Increase() returns true.
|
||||
// If Increase() returns false, all topological orderings of the graph have
|
||||
// been generated and the state of permutation_ is not specified..
|
||||
bool Increase() {
|
||||
Unset(size_ - 1);
|
||||
for (int pos = size_ - 2; pos >= 0; --pos) {
|
||||
// Invariant(pos) holds.
|
||||
// Increasing logic: once permutation_[pos] has been put back to its
|
||||
// original position by Unset(pos), elements permutations_[pos], ...,
|
||||
// permutations_.back() are in their original ordering, in particular in
|
||||
// the same order as last time the iteration on permutation_[pos]
|
||||
// occurred (according to Invariant(pos).2, these are exactly the elements
|
||||
// that have to be tried at pos).
|
||||
// All possibilities in permutations_[pos], ...,
|
||||
// permutations_[element_original_position_[pos]] have been run through.
|
||||
// The next to test is permutations_[element_original_position_[pos] + 1].
|
||||
const int k = element_original_position_[pos] + 1;
|
||||
Unset(pos);
|
||||
// Invariant(pos - 1) holds.
|
||||
|
||||
// No more elements to iterate on at position pos. Go backwards one
|
||||
// position to increase that one.
|
||||
if (k == permutation_.size()) continue;
|
||||
Set(pos, k);
|
||||
// Invariant(pos) holds.
|
||||
for (++pos; pos < size_; ++pos) {
|
||||
// Invariant(pos - 1) holds.
|
||||
// According to Invariant(pos - 1).2, if pos >= permutation_.size(),
|
||||
// there are no more elements we can add to the permutation which means
|
||||
// that we detected a cycle. It would be a bug as we would have detected
|
||||
// it in Init().
|
||||
CHECK_LT(pos, permutation_.size()) << "Cycle detected";
|
||||
// According to Invariant(pos - 1).2, elements that can be used at pos
|
||||
// are permutations_[pos], ..., permutations_.back(). Starts the
|
||||
// iteration at permutations_[pos].
|
||||
Set(pos, pos);
|
||||
// Invariant(pos) holds.
|
||||
}
|
||||
// Invariant(size_ - 1) holds.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must be called before Increase().
|
||||
ABSL_MUST_USE_RESULT bool Init() {
|
||||
for (int i = 0; i < size_; ++i) {
|
||||
if (missing_parent_numbers_[i] == 0) {
|
||||
permutation_.push_back(i);
|
||||
}
|
||||
}
|
||||
for (int pos = 0; pos < size_; ++pos) {
|
||||
// Invariant(pos - 1) holds.
|
||||
// According to Invariant(pos - 1).2, if pos >= permutation_.size(),
|
||||
// there are no more elements we can add to the permutation.
|
||||
if (pos >= permutation_.size()) return false;
|
||||
// According to Invariant(pos - 1).2, elements that can be used at pos
|
||||
// are permutations_[pos], ..., permutations_.back(). Starts the
|
||||
// iteration at permutations_[pos].
|
||||
Set(pos, pos);
|
||||
// Invariant(pos) holds.
|
||||
}
|
||||
// Invariant(pos - 1) hold. We have a permutation.
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<int>& permutation() const { return permutation_; }
|
||||
|
||||
private:
|
||||
// Graph maps indices to their children. Children must be in [0, size_).
|
||||
std::vector<std::vector<int>> graph_;
|
||||
// Number of elements in graph_.
|
||||
int size_;
|
||||
// For each element in graph_, the number of parents it has that are not yet
|
||||
// in permutation_. In particular, it is always 0 when Init has been called
|
||||
// and when Increase is not in progress (and has not yet returned false).
|
||||
std::vector<int> missing_parent_numbers_;
|
||||
// The current permutation. It is ensured to be a topological sorting of the
|
||||
// graph once Init has been called and Increase has not yet returned false.
|
||||
std::vector<int> permutation_;
|
||||
// Keeps track of the original position of the element in permutation_[i]. See
|
||||
// the comment above the class for the detailed algorithm.
|
||||
std::vector<int> element_original_position_;
|
||||
|
||||
// Unset the element at pos.
|
||||
//
|
||||
// - Precondition: Invariant(pos) holds.
|
||||
// - Postcondition: Invariant(pos - 1) holds.
|
||||
void Unset(int pos) {
|
||||
const int n = permutation_[pos];
|
||||
// Before the loop: Invariant(pos).2 and Invariant(pos).3 hold.
|
||||
// After the swap below: Invariant(pos - 1).2 and Invariant(pos - 1).3 hold.
|
||||
for (const int c : graph_[n]) {
|
||||
if (missing_parent_numbers_[c] == 0) permutation_.pop_back();
|
||||
++missing_parent_numbers_[c];
|
||||
}
|
||||
std::swap(permutation_[element_original_position_[pos]], permutation_[pos]);
|
||||
// Invariant(pos).4 -> Invariant(pos - 1).4.
|
||||
element_original_position_[pos] = pos;
|
||||
}
|
||||
|
||||
// Set the element at pos to the element at k.
|
||||
//
|
||||
// - Precondition: Invariant(pos - 1) holds and k in [pos,
|
||||
// permutation_.size()).
|
||||
// - Postcondition: Invariant(pos) holds and permutation_[pos] has been
|
||||
// swapped with permutation_[k].
|
||||
void Set(int pos, int k) {
|
||||
int n = permutation_[k];
|
||||
// Before the loop: Invariant(pos - 1).2 and Invariant(pos - 1).3 hold.
|
||||
// After the loop: Invariant(pos).2 and Invariant(pos).3 hold.
|
||||
for (int c : graph_[n]) {
|
||||
--missing_parent_numbers_[c];
|
||||
if (missing_parent_numbers_[c] == 0) permutation_.push_back(c);
|
||||
}
|
||||
// Invariant(pos - 1).1 -> Invariant(pos).1.
|
||||
std::swap(permutation_[k], permutation_[pos]);
|
||||
// Invariant(pos - 1).4 -> Invariant(pos).4.
|
||||
element_original_position_[pos] = k;
|
||||
}
|
||||
};
|
||||
|
||||
// To describe the algorithm in operator++() and constructor(), we consider the
|
||||
// following invariant, called Invariant(pos) for a position pos in [0, size_):
|
||||
// 1. permutations_[0], ..., permutations_[pos] form a prefix of a topological
|
||||
// ordering of the graph;
|
||||
// 2. permutations_[pos + 1], ..., permutations_.back() are all other elements
|
||||
// that have all their parents in permutations_[0], ..., permutations_[pos],
|
||||
// ordered lexicographically by the index of their last parent in
|
||||
// permutations_[0], ... permutations_[pos] and then by their index in the
|
||||
// graph;
|
||||
// 3. missing_parent_numbers_[i] is the number of parents of element i that are
|
||||
// not in {permutations_[0], ..., permutations_[pos]}.
|
||||
// 4. element_original_position_[i] is the original position of element i of
|
||||
// the permutation following the order described in 2. In particular,
|
||||
// element_original_position_[i] = i for i > pos.
|
||||
// Set and Unset maintain these invariants.
|
||||
|
||||
// Precondition: Invariant(size_ - 1) holds.
|
||||
// Postcondition: Invariant(size_ - 1) holds if the end of the iteration is not
|
||||
// reached.
|
||||
inline DagTopologicalSortIterator::Iterator&
|
||||
DagTopologicalSortIterator::Iterator::operator++() {
|
||||
CHECK_GE(ordering_index_, 0) << "Iteration past end";
|
||||
if (size_ == 0) {
|
||||
// Special case: empty graph, only one topological ordering is
|
||||
// generated.
|
||||
ordering_index_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Unset(size_ - 1);
|
||||
for (int pos = size_ - 2; pos >= 0; --pos) {
|
||||
// Invariant(pos) holds.
|
||||
// Increasing logic: once permutation_[pos] has been put back to its
|
||||
// original position by Unset(pos), elements permutations_[pos], ...,
|
||||
// permutations_.back() are in their original ordering, in particular in
|
||||
// the same order as last time the iteration on permutation_[pos] occurred
|
||||
// (according to Invariant(pos).2, these are exactly the elements that have
|
||||
// to be tried at pos). All possibilities in permutations_[pos], ...,
|
||||
// permutations_[element_original_position_[pos]] have been run through.
|
||||
// The next to test is permutations_[element_original_position_[pos] + 1].
|
||||
const int k = element_original_position_[pos] + 1;
|
||||
Unset(pos);
|
||||
// Invariant(pos - 1) holds.
|
||||
|
||||
// No more elements to iterate on at position pos. Go backwards one position
|
||||
// to increase that one.
|
||||
if (k == permutation_.size()) continue;
|
||||
Set(pos, k);
|
||||
// Invariant(pos) holds.
|
||||
for (++pos; pos < size_; ++pos) {
|
||||
// Invariant(pos - 1) holds.
|
||||
// According to Invariant(pos - 1).2, if pos >= permutation_.size(), there
|
||||
// are no more elements we can add to the permutation which means that we
|
||||
// detected a cycle. It would be a bug as we would have detected it in
|
||||
// the constructor.
|
||||
CHECK_LT(pos, permutation_.size())
|
||||
<< "Unexpected cycle detected during iteration";
|
||||
// According to Invariant(pos - 1).2, elements that can be used at pos are
|
||||
// permutations_[pos], ..., permutations_.back(). Starts the iteration at
|
||||
// permutations_[pos].
|
||||
Set(pos, pos);
|
||||
// Invariant(pos) holds.
|
||||
}
|
||||
// Invariant(size_ - 1) holds.
|
||||
++ordering_index_;
|
||||
return *this;
|
||||
}
|
||||
ordering_index_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline DagTopologicalSortIterator::Iterator::Iterator(
|
||||
const std::vector<std::vector<int>>& graph)
|
||||
: graph_(graph),
|
||||
size_(graph.size()),
|
||||
missing_parent_numbers_(size_, 0),
|
||||
element_original_position_(size_, 0),
|
||||
ordering_index_(0) {
|
||||
if (size_ == 0) {
|
||||
// Special case: empty graph, only one topological ordering is generated,
|
||||
// which is the "empty" ordering.
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& children : graph_) {
|
||||
for (const int child : children) {
|
||||
missing_parent_numbers_[child]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < size_; ++i) {
|
||||
if (missing_parent_numbers_[i] == 0) {
|
||||
permutation_.push_back(i);
|
||||
}
|
||||
}
|
||||
for (int pos = 0; pos < size_; ++pos) {
|
||||
// Invariant(pos - 1) holds.
|
||||
// According to Invariant(pos - 1).2, if pos >= permutation_.size(), there
|
||||
// are no more elements we can add to the permutation.
|
||||
if (pos >= permutation_.size()) {
|
||||
ordering_index_ = -1;
|
||||
return;
|
||||
}
|
||||
// According to Invariant(pos - 1).2, elements that can be used at pos are
|
||||
// permutations_[pos], ..., permutations_.back(). Starts the iteration at
|
||||
// permutations_[pos].
|
||||
Set(pos, pos);
|
||||
// Invariant(pos) holds.
|
||||
}
|
||||
// Invariant(pos - 1) hold. We have a permutation.
|
||||
}
|
||||
|
||||
// Unset the element at pos.
|
||||
//
|
||||
// - Precondition: Invariant(pos) holds.
|
||||
// - Postcondition: Invariant(pos - 1) holds.
|
||||
inline void DagTopologicalSortIterator::Iterator::Unset(int pos) {
|
||||
const int n = permutation_[pos];
|
||||
// Before the loop: Invariant(pos).2 and Invariant(pos).3 hold.
|
||||
// After the swap below: Invariant(pos - 1).2 and Invariant(pos - 1).3 hold.
|
||||
for (const int c : graph_[n]) {
|
||||
if (missing_parent_numbers_[c] == 0) permutation_.pop_back();
|
||||
++missing_parent_numbers_[c];
|
||||
}
|
||||
std::swap(permutation_[element_original_position_[pos]], permutation_[pos]);
|
||||
// Invariant(pos).4 -> Invariant(pos - 1).4.
|
||||
element_original_position_[pos] = pos;
|
||||
}
|
||||
|
||||
// Set the element at pos to the element at k.
|
||||
//
|
||||
// - Precondition: Invariant(pos - 1) holds and k in [pos,
|
||||
// permutation_.size()).
|
||||
// - Postcondition: Invariant(pos) holds and permutation_[pos] has been swapped
|
||||
// with permutation_[k].
|
||||
inline void DagTopologicalSortIterator::Iterator::Set(int pos, int k) {
|
||||
int n = permutation_[k];
|
||||
// Before the loop: Invariant(pos - 1).2 and Invariant(pos - 1).3 hold.
|
||||
// After the loop: Invariant(pos).2 and Invariant(pos).3 hold.
|
||||
for (int c : graph_[n]) {
|
||||
--missing_parent_numbers_[c];
|
||||
if (missing_parent_numbers_[c] == 0) permutation_.push_back(c);
|
||||
}
|
||||
// Invariant(pos - 1).1 -> Invariant(pos).1.
|
||||
std::swap(permutation_[k], permutation_[pos]);
|
||||
// Invariant(pos - 1).4 -> Invariant(pos).4.
|
||||
element_original_position_[pos] = k;
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
|
||||
@@ -1075,43 +1075,39 @@ TEST(DagTopologicalSortIteratorTest, GenerateValidPermutations) {
|
||||
dag_iterator.AddArc(4, 1);
|
||||
dag_iterator.AddArc(2, 3);
|
||||
dag_iterator.AddArc(3, 1);
|
||||
EXPECT_TRUE(dag_iterator.Init());
|
||||
int count = 0;
|
||||
do {
|
||||
for ([[maybe_unused]] const auto& permutation : dag_iterator) {
|
||||
++count;
|
||||
} while (dag_iterator.Increase());
|
||||
}
|
||||
EXPECT_EQ(count, 13);
|
||||
}
|
||||
|
||||
TEST(DagTopologicalSortIteratorTest, GenerateAllPermutations) {
|
||||
DagTopologicalSortIterator dag_iterator(6);
|
||||
EXPECT_TRUE(dag_iterator.Init());
|
||||
int count = 0;
|
||||
do {
|
||||
for ([[maybe_unused]] const auto& permutation : dag_iterator) {
|
||||
++count;
|
||||
} while (dag_iterator.Increase());
|
||||
}
|
||||
EXPECT_EQ(count, 720);
|
||||
}
|
||||
|
||||
TEST(DagTopologicalSortIteratorTest, OnePrecedence) {
|
||||
DagTopologicalSortIterator dag_iterator(6);
|
||||
dag_iterator.AddArc(5, 2);
|
||||
EXPECT_TRUE(dag_iterator.Init());
|
||||
int count = 0;
|
||||
do {
|
||||
for ([[maybe_unused]] const auto& permutation : dag_iterator) {
|
||||
++count;
|
||||
} while (dag_iterator.Increase());
|
||||
}
|
||||
EXPECT_EQ(count, 360);
|
||||
}
|
||||
|
||||
TEST(DagTopologicalSortIteratorTest, ReversePrecedence) {
|
||||
DagTopologicalSortIterator dag_iterator(6);
|
||||
dag_iterator.AddArc(2, 5);
|
||||
EXPECT_TRUE(dag_iterator.Init());
|
||||
int count = 0;
|
||||
do {
|
||||
for ([[maybe_unused]] const auto& permutation : dag_iterator) {
|
||||
++count;
|
||||
} while (dag_iterator.Increase());
|
||||
}
|
||||
EXPECT_EQ(count, 360);
|
||||
}
|
||||
|
||||
@@ -1133,11 +1129,9 @@ TEST(DagTopologicalSortIteratorTest, RandomTest) {
|
||||
|
||||
absl::flat_hash_set<std::vector<int>> iterator_solutions;
|
||||
int count_iterator = 0;
|
||||
if (dag_iterator.Init()) {
|
||||
do {
|
||||
++count_iterator;
|
||||
iterator_solutions.insert(dag_iterator.permutation());
|
||||
} while (dag_iterator.Increase());
|
||||
for (const auto& permutation : dag_iterator) {
|
||||
++count_iterator;
|
||||
iterator_solutions.insert(permutation);
|
||||
}
|
||||
|
||||
std::vector<int> permutation = {0, 1, 2, 3, 4, 5};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#ifndef OR_TOOLS_UTIL_SORTED_INTERVAL_LIST_H_
|
||||
#define OR_TOOLS_UTIL_SORTED_INTERVAL_LIST_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <ostream>
|
||||
@@ -32,21 +33,41 @@ namespace operations_research {
|
||||
* Represents a closed interval [start, end]. We must have start <= end.
|
||||
*/
|
||||
struct ClosedInterval {
|
||||
#if !defined(SWIG)
|
||||
/**
|
||||
* An iterator over the values of a ClosedInterval object.
|
||||
*
|
||||
* To iterate over the values, you can use either a range for loop:
|
||||
*
|
||||
* ClosedInterval interval = {0, 100};
|
||||
* for (const int64_t value : interval) { Work(value); }
|
||||
*
|
||||
* or a classical for loop:
|
||||
* for (auto it = begin(interval); it != end(interval); ++it) { Work(*it); }
|
||||
*
|
||||
* The iterator is designed to be very efficient, using just a single counter.
|
||||
* It works correctly for any combination of `start` and `end` except the full
|
||||
* int64_t range (start == INT64_MIN && end == INT64_MAX).
|
||||
*/
|
||||
class Iterator;
|
||||
#endif // !defined(SWIG)
|
||||
|
||||
ClosedInterval() {}
|
||||
explicit ClosedInterval(int64_t v) : start(v), end(v) {}
|
||||
ClosedInterval(int64_t s, int64_t e) : start(s), end(e) {
|
||||
DLOG_IF(DFATAL, s > e) << "Invalid ClosedInterval(" << s << ", " << e
|
||||
<< ")";
|
||||
}
|
||||
|
||||
std::string DebugString() const;
|
||||
bool operator==(const ClosedInterval& other) const {
|
||||
constexpr bool operator==(const ClosedInterval& other) const {
|
||||
return start == other.start && end == other.end;
|
||||
}
|
||||
|
||||
// Because we mainly manipulate vector of disjoint intervals, we only need to
|
||||
// sort by the start. We do not care about the order in which interval with
|
||||
// the same start appear since they will always be merged into one interval.
|
||||
bool operator<(const ClosedInterval& other) const {
|
||||
constexpr bool operator<(const ClosedInterval& other) const {
|
||||
return start < other.start;
|
||||
}
|
||||
|
||||
@@ -59,6 +80,11 @@ struct ClosedInterval {
|
||||
int64_t end = 0; // Inclusive.
|
||||
};
|
||||
|
||||
#if !defined(SWIG)
|
||||
inline ClosedInterval::Iterator begin(ClosedInterval interval);
|
||||
inline ClosedInterval::Iterator end(ClosedInterval interval);
|
||||
#endif // !defined(SWIG)
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const ClosedInterval& interval);
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const std::vector<ClosedInterval>& intervals);
|
||||
@@ -648,6 +674,67 @@ class SortedDisjointIntervalList {
|
||||
IntervalSet intervals_;
|
||||
};
|
||||
|
||||
// Implementation details.
|
||||
|
||||
#if !defined(SWIG)
|
||||
class ClosedInterval::Iterator {
|
||||
public:
|
||||
using value_type = int64_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
Iterator(const Iterator&) = default;
|
||||
|
||||
int64_t operator*() const { return static_cast<int64_t>(current_); }
|
||||
|
||||
Iterator& operator++() {
|
||||
++current_;
|
||||
return *this;
|
||||
}
|
||||
void operator++(int) { ++current_; }
|
||||
|
||||
bool operator==(Iterator other) const { return current_ == other.current_; }
|
||||
bool operator!=(Iterator other) const { return current_ != other.current_; }
|
||||
|
||||
Iterator& operator=(const Iterator&) = default;
|
||||
|
||||
static Iterator Begin(ClosedInterval interval) {
|
||||
AssertNotFullInt64Range(interval);
|
||||
return Iterator(static_cast<uint64_t>(interval.start));
|
||||
}
|
||||
static Iterator End(ClosedInterval interval) {
|
||||
AssertNotFullInt64Range(interval);
|
||||
return Iterator(static_cast<uint64_t>(interval.end) + 1);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Iterator(uint64_t current) : current_(current) {}
|
||||
|
||||
// Triggers a DCHECK-failure when `interval` represents the full int64_t
|
||||
// range.
|
||||
static void AssertNotFullInt64Range(ClosedInterval interval) {
|
||||
DCHECK_NE(static_cast<uint64_t>(interval.start),
|
||||
static_cast<uint64_t>(interval.end) + 1)
|
||||
<< "Iteration over the full int64_t range is not supported.";
|
||||
}
|
||||
|
||||
// Implementation note: In C++, integer overflow is well-defined only for
|
||||
// unsigned integers. To avoid any compilation issues or UBSan failures, the
|
||||
// iterator uses uint64_t internally and relies on the fact that since C++20
|
||||
// unsigned->signed conversion is well-defined for all values using modulo
|
||||
// arithmetic.
|
||||
uint64_t current_;
|
||||
};
|
||||
|
||||
// begin()/end() are required for iteration over ClosedInterval in a range for
|
||||
// loop.
|
||||
inline ClosedInterval::Iterator begin(ClosedInterval interval) {
|
||||
return ClosedInterval::Iterator::Begin(interval);
|
||||
}
|
||||
inline ClosedInterval::Iterator end(ClosedInterval interval) {
|
||||
return ClosedInterval::Iterator::End(interval);
|
||||
}
|
||||
#endif // !defined(SWIG)
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_UTIL_SORTED_INTERVAL_LIST_H_
|
||||
|
||||
Reference in New Issue
Block a user