add work in progress version of dobble solved with local search

This commit is contained in:
lperron@google.com
2011-03-18 11:23:15 +00:00
parent 5c3e755f2b
commit d0779c4ce0
2 changed files with 870 additions and 0 deletions

View File

@@ -370,6 +370,12 @@ objs/cvrptw.o: examples/cvrptw.cc
cvrptw: $(CPLIBS) $(BASE_LIBS) objs/cvrptw.o
$(CCC) $(CFLAGS) $(LDFLAGS) objs/cvrptw.o $(CPLIBS) $(BASE_LIBS) -o cvrptw
objs/dobble_ls.o:examples/dobble_ls.cc
$(CCC) $(CFLAGS) -c examples/dobble_ls.cc -o objs/dobble_ls.o
dobble_ls: $(CPLIBS) $(BASE_LIBS) objs/dobble_ls.o
$(CCC) $(CFLAGS) $(LDFLAGS) objs/dobble_ls.o $(CPLIBS) $(BASE_LIBS) -o dobble_ls
objs/flow_example.o:examples/flow_example.cc
$(CCC) $(CFLAGS) -c examples/flow_example.cc -o objs/flow_example.o

864
examples/dobble_ls.cc Normal file
View File

@@ -0,0 +1,864 @@
// Copyright 2010 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Dobble Generation problem:
// - We have 57 cards
// - 57 symbols
// - 8 symbols per card
// We want to assign symbols per card such that any two cards have exactly
// one symbol in common.
#include "base/commandlineflags.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/concise_iterator.h"
#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
#include "constraint_solver/constraint_solveri.h"
#include "util/bitset.h"
#include "base/random.h"
DEFINE_int32(lns_size, 10, "Size of the lns fragment.");
DEFINE_int32(lns_limit, 5, "Limit the number of failures of the lns loop.");
DEFINE_int32(lns_seed, 1, "Seed for the LNS random number generator.");
DEFINE_int32(fail_limit, 50000, "Fail limit for the global search.");
namespace operations_research {
class IntersectionCount : public Constraint {
public:
IntersectionCount(Solver* const solver,
const vector<IntVar*>& vars1,
const vector<IntVar*>& vars2,
IntVar* const count_var,
int count)
: Constraint(solver),
vars1_(new IntVar*[vars1.size()]),
vars2_(new IntVar*[vars2.size()]),
size_(vars1.size()),
count_var_(count_var),
count_(count) {
CHECK_EQ(vars1.size(), vars2.size());
memcpy(vars1_.get(), vars1.data(), vars1.size() * sizeof(vars1.data()));
memcpy(vars2_.get(), vars2.data(), vars2.size() * sizeof(vars2.data()));
for (int i = 0; i < size_; ++i) {
CHECK_GE(vars1_[i]->Min(), 0);
CHECK_GE(vars2_[i]->Min(), 0);
CHECK_LE(vars1_[i]->Max(), 1);
CHECK_LE(vars2_[i]->Max(), 1);
}
}
virtual void Post() {
Demon* const delayed =
solver()->MakeDelayedConstraintInitialPropagateCallback(this);
for (int i = 0; i < size_; ++i) {
vars1_[i]->WhenBound(delayed);
vars2_[i]->WhenBound(delayed);
}
}
virtual void InitialPropagate() {
int possible = 0;
int sure = 0;
int unbounded = 0;
for (int i = 0; i < size_; ++i) {
if (vars1_[i]->Min() == 1 && vars2_[i]->Min() == 1) {
sure++;
}
if (vars1_[i]->Max() == 1 && vars2_[i]->Max() == 1) {
possible++;
}
if (!vars1_[i]->Bound() || !vars2_[i]->Bound()) {
unbounded++;
}
}
count_var_->SetRange(sure, possible);
if (unbounded > 0) {
if (count_var_->Max() == sure) {
for (int i = 0; i < size_; ++i) {
if (vars1_[i]->Min() == 1 && vars2_[i]->Min() == 0) {
vars2_[i]->SetValue(0);
} else if (vars2_[i]->Min() == 1 && vars1_[i]->Min() == 0) {
vars1_[i]->SetValue(0);
}
}
} else if (count_var_->Min() == possible) {
for (int i = 0; i < size_; ++i) {
if (vars1_[i]->Max() == 1 && vars2_[i]->Max() == 1) {
vars1_[i]->SetValue(1);
vars2_[i]->SetValue(1);
}
}
}
}
}
private:
scoped_array<IntVar*> vars1_;
scoped_array<IntVar*> vars2_;
const int size_;
IntVar* const count_var_;
const int count_;
};
IntVar* AddIntersectionVar(Solver* const solver,
const vector<IntVar*>& vars1,
const vector<IntVar*>& vars2,
int count) {
IntVar* const cardinality = solver->MakeIntVar(0, count);
solver->AddConstraint(solver->RevAlloc(new IntersectionCount(solver,
vars1,
vars2,
cardinality,
count)));
return solver->MakeAbs(solver->MakeSum(cardinality, -1))->Var();
}
class CardLns : public BaseLNS {
public:
CardLns(const IntVar* const* vars,
int size,
int fragment_size,
int num_cards,
int num_symbols)
: BaseLNS(vars, size),
rand_(FLAGS_lns_seed),
fragment_size_(fragment_size),
num_cards_(num_cards),
num_symbols_(num_symbols) {}
virtual ~CardLns() {}
virtual bool NextFragment(vector<int>* fragment) {
if (rand_.Uniform(2)) { // Release Cards.
for (int i = 0; i < fragment_size_; ++i) {
const int card_index = rand_.Uniform(num_cards_);
for (int symbol_index = 0;
symbol_index < num_symbols_;
++symbol_index) {
fragment->push_back(card_index * num_symbols_ + symbol_index);
}
}
} else { // Release Symbols.
for (int i = 0; i < fragment_size_; ++i) {
const int symbol_index = rand_.Uniform(num_symbols_);
for (int card_index = 0; card_index < num_cards_; ++card_index) {
fragment->push_back(card_index * num_symbols_ + symbol_index);
}
}
}
return true;
}
private:
ACMRandom rand_;
const int fragment_size_;
const int num_cards_;
const int num_symbols_;
};
class CrossLns : public BaseLNS {
public:
CrossLns(const IntVar* const* vars,
int size,
int fragment_size,
int num_cards,
int num_symbols,
int num_symbols_per_card)
: BaseLNS(vars, size),
rand_(FLAGS_lns_seed),
fragment_size_(fragment_size),
num_cards_(num_cards),
num_symbols_(num_symbols),
num_symbols_per_card_(num_symbols_per_card),
symbols_per_card_(num_cards) {
for (int card = 0; card < num_cards_; ++card) {
symbols_per_card_[card].resize(num_symbols_per_card_, -1);
}
}
virtual ~CrossLns() {}
virtual void InitFragments() {
for (int card = 0; card < num_cards_; ++card) {
int found = 0;
for (int symbol = 0; symbol < num_symbols_; ++symbol) {
if (Value(Index(card, symbol)) == 1) {
symbols_per_card_[card][found++] = symbol;
}
}
DCHECK_EQ(num_symbols_per_card_, found);
}
}
virtual bool NextFragment(vector<int>* fragment) {
hash_set<int> cards_to_release;
const int max_cards = max(fragment_size_, num_cards_ / 2);
hash_set<int> symbols_to_release;
while (cards_to_release.size() < max_cards) {
const int card = rand_.Uniform(num_cards_);
const int symbol =
symbols_per_card_[card][rand_.Uniform(num_symbols_per_card_)];
cards_to_release.insert(card);
symbols_to_release.insert(symbol);
}
for (ConstIter<hash_set<int> > card_it(cards_to_release);
!card_it.at_end();
++card_it) {
for (ConstIter<hash_set<int> > symbol_it(symbols_to_release);
!symbol_it.at_end();
++symbol_it) {
fragment->push_back(Index(*card_it, *symbol_it));
}
}
return true;
}
private:
int Index(int card, int symbol) {
return card * num_symbols_ + symbol;
}
ACMRandom rand_;
const int fragment_size_;
const int num_cards_;
const int num_symbols_;
const int num_symbols_per_card_;
vector<vector<int> > symbols_per_card_;
};
class SwitchSymbols : public IntVarLocalSearchOperator {
public:
SwitchSymbols(const IntVar* const* vars,
int size,
int num_cards,
int num_symbols,
int num_symbols_per_card)
: IntVarLocalSearchOperator(vars, size),
num_cards_(num_cards),
num_symbols_(num_symbols),
num_symbols_per_card_(num_symbols_per_card),
current_card1_(-1),
current_card2_(-1),
current_symbol1_(-1),
current_symbol2_(-1),
symbols_per_card_(num_cards) {
for (int card = 0; card < num_cards; ++card) {
symbols_per_card_[card].resize(num_symbols_per_card, -1);
}
}
virtual ~SwitchSymbols() {}
virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) {
CHECK_NOTNULL(delta);
while (true) {
RevertChanges(true);
if (!Increment()) {
VLOG(1) << "finished nhood";
return false;
}
const int symbol1 = symbols_per_card_[current_card1_][current_symbol1_];
const int symbol2 = symbols_per_card_[current_card2_][current_symbol2_];
DCHECK(Value(Index(current_card1_, symbol1)));
DCHECK(Value(Index(current_card2_, symbol2)));
if (Value(Index(current_card1_, symbol2)) ||
Value(Index(current_card2_, symbol1))) {
VLOG(1) << "Will overwrite symbol, continuing";
continue;
}
SetValue(Index(current_card1_, symbol1), 0);
SetValue(Index(current_card2_, symbol2), 0);
SetValue(Index(current_card1_, symbol2), 1);
SetValue(Index(current_card2_, symbol1), 1);
if (ApplyChanges(delta, deltadelta)) {
VLOG(1) << "Delta = " << delta->DebugString();
return true;
}
}
return false;
}
protected:
virtual void OnStart() {
VLOG(1) << "start nhood";
for (int card = 0; card < num_cards_; ++card) {
int found = 0;
for (int symbol = 0; symbol < num_symbols_; ++symbol) {
if (Value(Index(card, symbol)) == 1) {
symbols_per_card_[card][found++] = symbol;
}
}
DCHECK_EQ(num_symbols_per_card_, found);
}
current_card1_ = 0;
current_card2_ = 1;
current_symbol1_ = 0;
current_symbol2_ = -1;
}
private:
int Index(int card, int symbol) {
return card * num_symbols_ + symbol;
}
bool Increment() {
current_symbol2_++;
if (current_symbol2_ == num_symbols_per_card_) {
current_symbol2_ = 0;
current_symbol1_++;
if (current_symbol1_ == num_symbols_per_card_) {
current_symbol1_ = 0;
current_card2_++;
if (current_card2_ == num_cards_) {
current_card1_++;
current_card2_ = current_card1_ + 1;
}
}
}
return current_card1_ < num_cards_ - 1;
}
const int num_cards_;
const int num_symbols_;
const int num_symbols_per_card_;
int current_card1_;
int current_card2_;
int current_symbol1_;
int current_symbol2_;
vector<vector<int> > symbols_per_card_;
};
class CycleSymbols : public IntVarLocalSearchOperator {
public:
CycleSymbols(const IntVar* const* vars,
int size,
int num_cards,
int num_symbols,
int num_symbols_per_card)
: IntVarLocalSearchOperator(vars, size),
num_cards_(num_cards),
num_symbols_(num_symbols),
num_symbols_per_card_(num_symbols_per_card),
current_card1_(-1),
current_card2_(-1),
current_symbol1_(-1),
current_symbol2_(-1),
symbols_per_card_(num_cards) {
for (int card = 0; card < num_cards; ++card) {
symbols_per_card_[card].resize(num_symbols_per_card, -1);
}
InitTriples();
}
virtual ~CycleSymbols() {}
virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) {
CHECK_NOTNULL(delta);
while (true) {
RevertChanges(true);
if (!Increment()) {
VLOG(1) << "finished nhood";
return false;
}
const int symbol1 = symbols_per_card_[current_card1_][current_symbol1_];
const int symbol2 = symbols_per_card_[current_card2_][current_symbol2_];
const int symbol3 = symbols_per_card_[current_card3_][current_symbol3_];
DCHECK(Value(Index(current_card1_, symbol1)));
DCHECK(Value(Index(current_card2_, symbol2)));
DCHECK(Value(Index(current_card3_, symbol3)));
if (Value(Index(current_card1_, symbol2)) ||
Value(Index(current_card2_, symbol3)) ||
Value(Index(current_card3_, symbol1))) {
VLOG(1) << "Will overwrite symbol, continuing";
continue;
}
SetValue(Index(current_card1_, symbol1), 0);
SetValue(Index(current_card2_, symbol2), 0);
SetValue(Index(current_card3_, symbol3), 0);
SetValue(Index(current_card1_, symbol2), 1);
SetValue(Index(current_card2_, symbol3), 1);
SetValue(Index(current_card3_, symbol1), 1);
if (ApplyChanges(delta, deltadelta)) {
VLOG(1) << "Delta = " << delta->DebugString();
return true;
}
}
return false;
}
protected:
virtual void OnStart() {
VLOG(1) << "start nhood";
for (int card = 0; card < num_cards_; ++card) {
int found = 0;
for (int symbol = 0; symbol < num_symbols_; ++symbol) {
if (Value(Index(card, symbol)) == 1) {
symbols_per_card_[card][found++] = symbol;
}
}
DCHECK_EQ(num_symbols_per_card_, found);
}
card_triple_index_ = 0;
current_card1_ = card_triples_[0].card1;
current_card2_ = card_triples_[0].card2;
current_card3_ = card_triples_[0].card3;
current_symbol1_ = 0;
current_symbol2_ = 0;
current_symbol3_ = -1;
}
private:
struct Triple {
Triple(int c1, int c2, int c3) : card1(c1), card2(c2), card3(c3) {}
int card1;
int card2;
int card3;
};
void InitTriples() {
for (int c1 = 0; c1 < num_cards_; ++c1) {
for (int c2 = 0; c2 < num_cards_; ++c2) {
for (int c3 = 0; c3 < num_cards_; ++c3) {
if (c1 != c2 && c1 != c3 && c2 != c3) {
card_triples_.push_back(Triple(c1, c2, c3));
}
}
}
}
}
int Index(int card, int symbol) {
return card * num_symbols_ + symbol;
}
void NextCardTriple() {
card_triple_index_++;
if (card_triple_index_ < card_triples_.size()) {
const Triple& triple = card_triples_[card_triple_index_];
current_card1_ = triple.card1;
current_card2_ = triple.card2;
current_card3_ = triple.card3;
}
}
bool Increment() {
current_symbol3_++;
if (current_symbol3_ == num_symbols_per_card_) {
current_symbol3_ = 0;
current_symbol2_++;
if (current_symbol2_ == num_symbols_per_card_) {
current_symbol2_ = 0;
current_symbol1_++;
if (current_symbol1_ == num_symbols_per_card_) {
current_symbol1_ = 0;
NextCardTriple();
}
}
}
return card_triple_index_ < card_triples_.size();
}
const int num_cards_;
const int num_symbols_;
const int num_symbols_per_card_;
int current_card1_;
int current_card2_;
int current_card3_;
int current_symbol1_;
int current_symbol2_;
int current_symbol3_;
vector<vector<int> > symbols_per_card_;
int card_triple_index_;
vector<Triple> card_triples_;
};
class CycleNeighborhood : public IntVarLocalSearchOperator {
public:
CycleNeighborhood(const IntVar* const* vars,
int size,
int max_size,
int num_cards,
int num_symbols,
int num_symbols_per_card)
: IntVarLocalSearchOperator(vars, size),
rand_(FLAGS_lns_seed),
max_size_(max_size),
num_cards_(num_cards),
num_symbols_(num_symbols),
num_symbols_per_card_(num_symbols_per_card),
symbols_per_card_(num_cards) {
for (int card = 0; card < num_cards_; ++card) {
symbols_per_card_[card].resize(num_symbols_per_card_, -1);
}
}
virtual ~CycleNeighborhood() {}
virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) {
CHECK_NOTNULL(delta);
while (true) {
RevertChanges(true);
const int num_cards_to_release = rand_.Uniform(max_size_ - 3) + 3;
hash_set<int> cards_set;
vector<ToSwap> to_swap;
hash_set<int> symbols_to_release;
while (cards_set.size() < num_cards_to_release) {
const int card = rand_.Uniform(num_cards_);
if (ContainsKey(cards_set, card)) {
continue;
}
while (true) {
const int symbol =
symbols_per_card_[card][rand_.Uniform(num_symbols_per_card_)];
if (!ContainsKey(symbols_to_release, symbol)) {
to_swap.push_back(ToSwap(card, symbol));
symbols_to_release.insert(symbol);
break;
}
}
}
std::random_shuffle(to_swap.begin(), to_swap.end());
for (int i = 0; i < num_cards_to_release; ++i) {
SetValue(Index(to_swap[i].card, to_swap[i].symbol), 0);
SetValue(Index(to_swap[i].card,
to_swap[(i + 1) % num_cards_to_release].symbol), 1);
}
if (ApplyChanges(delta, deltadelta)) {
VLOG(1) << "Delta = " << delta->DebugString();
return true;
}
}
return false;
}
protected:
virtual void OnStart() {
for (int card = 0; card < num_cards_; ++card) {
int found = 0;
for (int symbol = 0; symbol < num_symbols_; ++symbol) {
if (Value(Index(card, symbol)) == 1) {
symbols_per_card_[card][found++] = symbol;
}
}
DCHECK_EQ(num_symbols_per_card_, found);
}
}
private:
struct ToSwap {
ToSwap(int c, int s) : card(c), symbol(s) {}
int card;
int symbol;
};
int Index(int card, int symbol) {
return card * num_symbols_ + symbol;
}
ACMRandom rand_;
const int max_size_;
const int num_cards_;
const int num_symbols_;
const int num_symbols_per_card_;
vector<vector<int> > symbols_per_card_;
};
class DobbleFilter : public IntVarLocalSearchFilter {
public:
DobbleFilter(const IntVar* const* vars,
int size,
int num_cards,
int num_symbols,
int num_symbols_per_card)
: IntVarLocalSearchFilter(vars, size),
num_cards_(num_cards),
num_symbols_(num_symbols),
num_symbols_per_card_(num_symbols_per_card),
cards_(num_cards, 0ULL),
costs_(num_cards_) {
for (int card = 0; card < num_cards_; ++card) {
costs_[card].resize(num_cards_, 0);
}
}
virtual void OnSynchronize() {
memset(cards_.data(), 0, num_cards_ * sizeof(*cards_.data()));
for (int card = 0; card < num_cards_; ++card) {
for (int symbol = 0; symbol < num_symbols_; ++symbol) {
if (Value(card * num_symbols_ + symbol)) {
SetBit64(&cards_[card], symbol);
}
}
}
for (int card1 = 0; card1 < num_cards_; ++card1) {
for (int card2 = 0; card2 < num_cards_; ++card2) {
costs_[card1][card2] =
Cost(BitCount64(cards_[card1] & cards_[card2]));
}
}
DCHECK(CheckCards());
}
virtual bool Accept(const Assignment* delta, const Assignment* deltadelta) {
const Assignment::IntContainer& solution_delta = delta->IntVarContainer();
const int solution_delta_size = solution_delta.Size();
// First we check we are not using LNS.
for (int i = 0; i < solution_delta_size; ++i) {
const IntVarElement& element = solution_delta.Element(i);
if (!element.Activated()) {
// Doodle filters are not robust to LNS, so skip filters when
// some elements are not activated.
VLOG(1) << "LNS";
return true;
}
}
VLOG(1) << "No LNS, size = " << solution_delta_size;
backtrack_.clear();
hash_set<int> touched_cards;
for (int index = 0; index < solution_delta_size; ++index) {
int64 touched_var = -1;
FindIndex(solution_delta.Element(index).Var(), &touched_var);
const int card = touched_var / num_symbols_;
const int symbol = touched_var % num_symbols_;
const int new_value = solution_delta.Element(index).Value();
if (!ContainsKey(touched_cards, card)) {
Save(card);
touched_cards.insert(card);
}
if (new_value) {
SetBit64(&cards_[card], symbol);
} else {
ClearBit64(&cards_[card], symbol);
}
}
if (!CheckCards()) {
Backtrack();
DCHECK(CheckCards());
VLOG(1) << "reject by size";
return false;
}
hash_set<int> treated_cards;
int cost_delta = 0;
for (ConstIter<hash_set<int> > card_it(touched_cards);
!card_it.at_end();
++card_it) {
treated_cards.insert(*card_it);
const uint64 bitset = cards_[*card_it];
const vector<int>& row_cost = costs_[*card_it];
for (int card = 0; card < num_cards_; ++card) {
if (!ContainsKey(treated_cards, card)) {
cost_delta +=
Cost(BitCount64(bitset & cards_[card])) - row_cost[card];
}
}
}
Backtrack();
if (cost_delta >= 0) {
VLOG(1) << "reject";
}
return cost_delta < 0;
}
private:
struct Undo {
Undo(int c, uint64 b) : card(c), bitset(b) {}
int card;
uint64 bitset;
};
int Cost(int intersection_size) {
return abs(intersection_size - 1);
}
void Backtrack() {
for (int i = 0; i < backtrack_.size(); ++i) {
cards_[backtrack_[i].card] = backtrack_[i].bitset;
}
}
void Save(int card) {
backtrack_.push_back(Undo(card, cards_[card]));
}
bool CheckCards() {
for (int i = 0; i < num_cards_; ++i) {
if (num_symbols_per_card_ != BitCount64(cards_[i])) {
VLOG(1) << "card " << i << " has bitset of size "
<< BitCount64(cards_[i]);
return false;
}
}
return true;
}
const int num_cards_;
const int num_symbols_;
const int num_symbols_per_card_;
vector<uint64> cards_;
vector<vector<int> > costs_;
vector<Undo> backtrack_;
};
void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
LOG(INFO) << "Solving dobble assignment problem:";
LOG(INFO) << " - " << num_cards << " cards";
LOG(INFO) << " - " << num_symbols << " symbols";
LOG(INFO) << " - " << num_symbols_per_card << " symbols per card";
Solver solver("dobble");
vector<vector<IntVar*> > vars(num_cards);
vector<IntVar*> all_vars;
for (int card_index = 0; card_index < num_cards; ++card_index) {
solver.MakeBoolVarArray(num_symbols,
StringPrintf("card_%i_", card_index),
&vars[card_index]);
for (int symbol_index = 0;
symbol_index < num_symbols;
++symbol_index) {
all_vars.push_back(vars[card_index][symbol_index]);
}
}
vector<IntVar*> slack_vars;
for (int card1 = 0; card1 < num_cards; ++card1) {
for (int card2 = 0; card2 < num_cards; ++card2) {
if (card1 != card2) {
slack_vars.push_back(AddIntersectionVar(&solver,
vars[card1],
vars[card2],
num_symbols_per_card));
}
}
}
for (int card = 0; card < num_cards; ++card) {
solver.AddConstraint(solver.MakeSumEquality(vars[card],
num_symbols_per_card));
}
for (int symbol_index = 0; symbol_index < num_symbols; ++symbol_index) {
vector<IntVar*> tmp;
for (int card_index = 0; card_index < num_cards; ++card_index) {
tmp.push_back(vars[card_index][symbol_index]);
}
solver.AddConstraint(solver.MakeSumEquality(tmp, num_symbols_per_card));
}
LOG(INFO) << "Solving with LNS";
LOG(INFO) << " - lns_size = " << FLAGS_lns_size;
LOG(INFO) << " - lns_limit = " << FLAGS_lns_limit;
LOG(INFO) << " - fail_limit = " << FLAGS_fail_limit;
DecisionBuilder* const build_db = solver.MakePhase(all_vars,
Solver::CHOOSE_RANDOM,
Solver::ASSIGN_MAX_VALUE);
const int kNhoodLimit = 1000;
LocalSearchOperator* const switch_operator =
solver.RevAlloc(new SwitchSymbols(all_vars.data(),
all_vars.size(),
num_cards,
num_symbols,
num_symbols_per_card));
LocalSearchOperator* const cycle_operator =
solver.RevAlloc(new CycleSymbols(all_vars.data(),
all_vars.size(),
num_cards,
num_symbols,
num_symbols_per_card));
LocalSearchOperator* const long_cycle_operator_limited =
solver.MakeNeighborhoodLimit(
solver.RevAlloc(new CycleNeighborhood(all_vars.data(),
all_vars.size(),
FLAGS_lns_size,
num_cards,
num_symbols,
num_symbols_per_card)),
kNhoodLimit);
LocalSearchOperator* const long_cycle_operator_unlimited =
solver.RevAlloc(new CycleNeighborhood(all_vars.data(),
all_vars.size(),
FLAGS_lns_size,
num_cards,
num_symbols,
num_symbols_per_card));
// LocalSearchOperator* const cross_lns_operator_limited =
// solver.MakeNeighborhoodLimit(
// solver.RevAlloc(new CrossLns(all_vars.data(),
// all_vars.size(),
// FLAGS_lns_size,
// num_cards,
// num_symbols,
// num_symbols_per_card)),
// kNhoodLimit);
LocalSearchOperator* const card_lns_operator_limited =
solver.MakeNeighborhoodLimit(
solver.RevAlloc(new CardLns(all_vars.data(),
all_vars.size(),
FLAGS_lns_size,
num_cards,
num_symbols)),
kNhoodLimit);
LocalSearchOperator* const card_lns_operator_unlimited =
solver.RevAlloc(new CardLns(all_vars.data(),
all_vars.size(),
FLAGS_lns_size,
num_cards,
num_symbols));
vector<LocalSearchOperator*> operators;
operators.push_back(switch_operator);
operators.push_back(card_lns_operator_limited);
operators.push_back(long_cycle_operator_limited);
operators.push_back(cycle_operator);
// operators.push_back(card_lns_operator_unlimited);
operators.push_back(long_cycle_operator_unlimited);
LocalSearchOperator* const concat =
solver.ConcatenateOperators(operators, true);
SearchLimit* const lns_limit =
solver.MakeLimit(kint64max, kint64max, FLAGS_lns_limit, kint64max);
DecisionBuilder* const ls_db = solver.MakeSolveOnce(build_db, lns_limit);
vector<LocalSearchFilter*> filters;
filters.push_back(solver.RevAlloc(new DobbleFilter(all_vars.data(),
all_vars.size(),
num_cards,
num_symbols,
num_symbols_per_card)));
LocalSearchPhaseParameters* const parameters =
solver.MakeLocalSearchPhaseParameters(concat, ls_db, NULL, filters);
DecisionBuilder* const final_db =
solver.MakeLocalSearchPhase(all_vars, build_db, parameters);
IntVar* const objective_var = solver.MakeSum(slack_vars)->Var();
vector<SearchMonitor*> monitors;
OptimizeVar* const optimize = solver.MakeMinimize(objective_var, 1);
monitors.push_back(optimize);
SearchMonitor* const log = solver.MakeSearchLog(100000, optimize);
monitors.push_back(log);
SearchLimit* const fail_limit =
solver.MakeLimit(kint64max, kint64max, FLAGS_fail_limit, kint64max);
monitors.push_back(fail_limit);
solver.Solve(final_db, monitors);
}
}
int main(int argc, char **argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
const int kCards = 57;
const int kSymbols = 57;
const int kSymbolsPerCard = 8;
operations_research::SolveDobble(kCards,
kSymbols,
kSymbolsPerCard);
return 0;
}