Commit 606be908 authored by Valentin Antuori's avatar Valentin Antuori
Browse files

dfs integration

parent 6d36aa17
......@@ -17,6 +17,7 @@ set(SRCS
src/mcts.cpp
src/heuristic.cpp
src/SparseSet.cpp
src/DFSRollout.cpp
)
......@@ -26,12 +27,14 @@ AUX_SOURCE_DIRECTORY(src/tclap TCLAP)
set(HEADERS
#src/policy-gradient.h
src/test.h
src/instance.h
src/solution.h
src/options.h
src/mcts.h
src/heuristic.h
src/SparseSet.hpp
src/DFSRollout.h
${TCLAP}
)
......
......@@ -2,12 +2,17 @@
#include <assert.h>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include "DFSRollout.h"
bool debug_dfs = false;
using namespace std;
int FAIL_LIMIT = 10000;
double ALPHA = 0.90;
int DFSRollout::component_ect(const int op) const
{
auto comp{data.component(op)};
......@@ -43,7 +48,7 @@ double DFSRollout::fitness_func(const int action) const
return 1 - (THETA_1 * lambda1 + THETA_2 * lambda2 + THETA_3 * lambda3 + THETA_4 * lambda4);
}
DFSRollout::DFSRollout(Instance& data) : data(data) {
DFSRollout::DFSRollout(const Instance& data) : data(data) {
period.resize(data.nb_components, 0);
prev_operation.resize(data.nb_components, -1);
......@@ -72,10 +77,18 @@ void DFSRollout::clear() {
maximum_tardiness.clear();
maximum_tardiness.push_back(0);
tardi_depth = 0;
best_sequence.clear();
best_seq_max_tardiness.clear();
_period.clear();
_num_operation.clear();
_prev_operation.clear();
_tardi_depth = 0;
tour_length = 0;
train_length = 0;
}
......@@ -101,6 +114,10 @@ void DFSRollout::verify(const char* msg, const int offset) {
if(start[y] < start[x] + data.duration(x) + data.distance(x,y)) {
cout << msg << ", overlap: " << start[y] << " < " << (start[x] + data.duration(x) + data.distance(x,y)) << endl;
// std::cout << y << "-> " << x << std::endl;
// for(int j{1}; j<sequence.size(); ++j) {
// std::cout << sequence[j] << " " << start[sequence[j]] << "("<<data.duration(sequence[j])<<") -> " << data.distance(sequence[j], sequence[j+1]) << std::endl;
// }
exit(1);
}
......@@ -116,7 +133,6 @@ void DFSRollout::verify(const char* msg, const int offset) {
}
}
bool DFSRollout::trainOk(const int i) const {
return train_length + data.trolley_length(i) <= data.T_max;
}
......@@ -137,6 +153,13 @@ void DFSRollout::get_operations() {
} else if(data.is_pickup(prev_operation[i])) {
actions.push_back(data.get_delivery(prev_operation[i]));
} else {
if(num_operation[i] != 2){
std::cout << num_operation[i] <<"/" << period[i] << std::endl;
std::cout << "prev = " << prev_operation[i] << std::endl;
std::cout << "compo = " << i << std::endl;
for(auto elt : sequence)
std::cout << elt << std::endl;
}
assert(num_operation[i] == 2);
if(data.is_full_operation(prev_operation[i])) {
p = data.get_pickempty_idx(i, period[i]);
......@@ -177,7 +200,7 @@ void DFSRollout::get_distribution(const double temperature)
double tp{0};
for(auto i{0}; i < actions.size(); ++i)
{
proba[actions[i]] = static_cast<long int>((exp_fitness[i] / sum_exp_fitness) * precision);
proba[actions[i]] = static_cast<long int>((exp_fitness[i] / sum_exp_fitness) * precision);
tp += proba[actions[i]];
}
......@@ -189,10 +212,8 @@ void DFSRollout::get_distribution(const double temperature)
++proba[actions[--i]];
++tp;
}
}
int DFSRollout::get_previous_operations(const int op) const {
int c{data.component(op)};
if(num_operation[c] == 1) {
......@@ -312,13 +333,75 @@ void DFSRollout::greedy_best() {
}
}
void DFSRollout::greedy_stochastic() {
void DFSRollout::greedy_stochastic(int upper_bound) {
int timeout;
bool can_stop = false;
while(sequence.size() < data.nb_tasks) {
get_operations();
compute_distances();
if(maximum_tardiness.back() == 0)
tardi_depth = sequence.size();
if(!can_stop && get_tardiness() > 0)
{
timeout = sequence.size() + 1000;
can_stop = true;
}else if(can_stop && sequence.size() > timeout && get_tardiness() > upper_bound)
{
break;
}
get_distribution(0.1);
get_distribution(temperature);
auto act{actions.rbegin()};
auto r{random_generator() % precision};
while(act != actions.rend()) {
if(r < proba[*act])
break;
else {
r -= proba[*act];
++act;
}
}
commit(*act);
// print_step();
}
}
void DFSRollout::extend_rollout() {
int timeout;
bool can_stop = false;
restore_state();
if(get_tardiness() > 0)
{
timeout = tardi_depth + 1000;
can_stop = true;
}
while(sequence.size() < data.nb_tasks) {
get_operations();
compute_distances();
if(!can_stop && get_tardiness() > 0)
{
timeout = sequence.size() + 1000;
can_stop = true;
}else if(can_stop && sequence.size() > timeout)
{
break;
}
get_distribution(temperature);
auto act{actions.rbegin()};
......@@ -351,14 +434,31 @@ void DFSRollout::do_op(const int op) {
// domain[i].clear();
// domain[i].push_back(op);
// action[i] = domain[i].begin();
// #ifdef DEBUG_DFS_ROLLOUT
// std::cout << "Before do_op" << std::endl;
// #endif
// if(debug_dfs){
// std::cout << "From do_op : " << std::endl;
// }
commit(op);
// #ifdef DEBUG_DFS_ROLLOUT
// std::cout << "After do_op" << std::endl;
// #endif
}
void DFSRollout::commit(const int op) {
#ifdef DEBUG
verify("before commit", 1);
#ifdef DEBUG_DFS_ROLLOUT
// if(debug_dfs){
// verbose = true;
// verified = true;
// std::cout << "Op = " << op << std::endl;
// std::cout <<"start : " << start[op] << std::endl;
// for(auto act : actions)
// std::cout << act << "(" << start[act] << ")";
// std::cout << std::endl;
verify("before commit", 1);
// }
#endif
sequence.push_back(op);
......@@ -374,7 +474,7 @@ void DFSRollout::commit(const int op) {
++period[c];
}
#ifdef DEBUG
#ifdef DEBUG_DFS_ROLLOUT
verify("after commit");
#endif
......@@ -382,7 +482,7 @@ void DFSRollout::commit(const int op) {
void DFSRollout::undo() {
#ifdef DEBUG
#ifdef DEBUG_DFS_ROLLOUT
verify("before undo");
#endif
......@@ -411,41 +511,92 @@ void DFSRollout::undo() {
--period[c];
}
#ifdef DEBUG
#ifdef DEBUG_DFS_ROLLOUT
verify("after undo");
#endif
}
void DFSRollout::save_state()
{
#ifdef DEBUG_DFS_ROLLOUT
int offset = 1;
if(sequence.size() == data.nb_tasks)
{
offset = 0;
}
verify("Saving state", offset);
#endif
best_sequence = sequence;
best_seq_max_tardiness = maximum_tardiness;
// remove the last tardiness computation, prevent bug when completing the rollout
if(best_sequence.size() != data.nb_tasks)
best_seq_max_tardiness.pop_back();
best_tardiness = get_tardiness();
_period = period;
_num_operation = num_operation;
_prev_operation = prev_operation;
_tour_length = tour_length;
_train_length = train_length;
_tardi_depth = tardi_depth;
_start = start;
}
void DFSRollout::restore_state()
{
sequence = std::move(best_sequence);
maximum_tardiness = std::move(best_seq_max_tardiness);
period = std::move(_period);
num_operation = std::move(_num_operation);
prev_operation = std::move(_prev_operation);
start = std::move(_start);
tour_length = _tour_length;
train_length = _train_length;
tardi_depth = _tardi_depth;
// std::cout << "Restore solution, tard= " << get_tardiness() << ", size = " << sequence.size() << std::endl;
}
void DFSRollout::search(const int ub) {
///*
int DFSRollout::search(int ub, int ub_depth) {
double sum_domain_size = 0;
int nb_dom = 0;
const auto start_level{sequence.size()};
best_tardiness=ub;
best_tardiness = numeric_limits<int>::max(); // best sol for this run may be worst than the UB
tardi_depth = 0;
best_sequence.clear();
int iter{0};
bool first_fail = true;
int tardiness_at_fail;
max_iter = 1;
while(true) {
++iter;
// branch
while(sequence.size() < data.nb_tasks) {
get_operations();
compute_distances();
if(sequence.size() > tardi_depth && get_tardiness() == 0){
tardi_depth = sequence.size();
}
// fail because of the upper bound
if(get_tardiness() >= best_tardiness) {
if(best_tardiness == 1 and sequence.size() > best_depth())
if(get_tardiness() >= ub) {
if(sequence.size() > best_sequence.size())
{
best_sequence = sequence;
if(verbose)
cout << "d depth=" << best_depth() << " iter=" << iter << endl;
save_state();
}
if(first_fail)
tardiness_at_fail = get_tardiness();
maximum_tardiness.pop_back();
break;
}
......@@ -458,6 +609,9 @@ void DFSRollout::search(const int ub) {
if(proba[*a] > 0)
domain[i].push_back(*a);
}
// std::cout << actions.size() << " / " << domain[i].size() << std::endl;
sum_domain_size += domain[i].size();
nb_dom++;
// if randomized, swap the first action with one chosen at random given the distribution of the heuristic
if(randomized) {
......@@ -475,14 +629,41 @@ void DFSRollout::search(const int ub) {
}
// branch left
action[i] = domain[i].begin();
action[i] = domain[i].begin();
// if(debug_dfs){
// std::cout << "branch left on " << *action[i] << std::endl;
// std::cout << "Domain : " << std::endl;
// for(auto elt : domain[i]){
// std::cout << elt << " ";
// }
// std::cout << "\nIterator at position : " << (action[i] - domain[i].begin()) << std::endl;
// }
commit(*action[i]);
}
if(first_fail)
{
if(sequence.size() == data.nb_tasks)
tardiness_at_fail = get_tardiness();
first_fail = false;
if(tardiness_at_fail < ub || tardi_depth > ub_depth)
{
max_iter = FAIL_LIMIT;
}else if(tardi_depth > ALPHA * ub_depth)
{
double term = (1 - (ub_depth - tardi_depth)/(ub_depth - ALPHA*ub_depth));
max_iter = FAIL_LIMIT * term * term;
// std::cout << max_iter << " : "<< tardiness_at_fail <<"/"<<ub << std::endl;
}
}
// the branch is full
if(sequence.size() == data.nb_tasks) {
best_tardiness = get_tardiness();
best_sequence = sequence;
save_state();
ub = best_tardiness;
if(verbose)
cout << "d lmax=" << best_tardiness << " iter=" << iter << endl;
if(verified)
......@@ -494,45 +675,233 @@ void DFSRollout::search(const int ub) {
break;
}
}
// backtrack
while(sequence.size() >= start_level) {
// if(debug_dfs){
// std::cout << "Backtrack" << std::endl;
// }
while(sequence.size() > start_level) {
// undo until the lower bound is lower than the upper bound
int i;
do {
undo();
i = sequence.size();
++action[i];
} while(sequence.size() >= start_level and get_tardiness() >= best_tardiness);
if(sequence.empty())
} while(sequence.size() > start_level and get_tardiness() >= ub);
if(sequence.empty() || get_tardiness() >= ub)
break;
// if there is an action that we haven't tried yet, try it, otherwise backtrack again
if(action[i] != domain[i].end()) {
maximum_tardiness.push_back(std::max(get_tardiness(), compute_distance(*action[i])));
// if(debug_dfs){
// std::cout << "branch right on " << *action[i] << std::endl;
// std::cout << "Domain : " << std::endl;
// for(auto elt : domain[i]){
// std::cout << elt << " ";
// }
// std::cout << "\nIterator at position : " << (action[i] - domain[i].begin()) << std::endl;
// }
commit(*action[i]);
break;
}
}
// if no tardiness after backtrack, then update tardi_depth
if(get_tardiness() == 0)
{
tardi_depth = sequence.size();
}
// check if the algorithm should stop
if(sequence.empty() and action[0] == domain[0].end()) {
if(verbose)
// if(verbose)
cout << "o search tree exhausted: " << best_tardiness << " at temperature " << temperature << " \n" ;
} else if(best_tardiness == 0) {
if(verbose)
cout << "o solution found\n";
} else if(iter >= max_iter) {
if(verbose)
cout << "o time out\n";
if(verbose && max_iter > 1)
cout << "o time out ( "<< max_iter << ")" <<"\n";
} else
continue;
break;
}
// std::cout << "End - Saved = " << best_tardiness << ", " << _tardi_depth << ", size = " << best_sequence.size() <<std::endl;
// std::cout << sum_domain_size/nb_dom << std::endl;
// std::cout << tardiness_at_fail << " - " << sequence.size() << std::endl;
// std::cout << "Saved = " << best_tardiness << ", " << _tardi_depth <<", " << best_sequence.size() << std::endl;
return max_iter;
}
//*/
/*
int DFSRollout::search(int ub) { //depth ub for computing iter_max
double sum_domain_size = 0;
int nb_dom = 0;
const auto start_level{sequence.size()};
best_tardiness = numeric_limits<int>::max(); // best sol for this run may be worst than the UB
_tardi_depth = 0; // best depth for this search
tardi_depth = 0;
best_sequence.clear();
int iter{0};
bool first_fail = true;
int tardiness_at_fail;
max_iter = 1;
while(true) {
++iter;
// branch
while(sequence.size() < data.nb_tasks) {
get_operations();
compute_distances();
if(sequence.size() > tardi_depth && get_tardiness() == 0){
tardi_depth = sequence.size();
}
// fail because of the upper bound
if(get_tardiness() > 0) {
if(tardi_depth > _tardi_depth || tardi_depth == _tardi_depth && get_tardiness() < best_tardiness){
save_state();
}
maximum_tardiness.pop_back();
break;
}
// get the probability distribution on the actions, and create the domain from that
get_distribution(temperature);
auto i{sequence.size()};
domain[i].clear();
for(auto a{actions.rbegin()}; a!=actions.rend(); ++a) {
if(proba[*a] > 0)
domain[i].push_back(*a);
}
sum_domain_size += domain[i].size();
nb_dom++;
// if randomized, swap the first action with one chosen at random given the distribution of the heuristic
if(randomized) {
auto act{domain[i].begin()};
auto r{random_generator() % precision};
while(act != domain[i].end()) {
if(r < proba[*act])
break;
else {
r -= proba[*act];
++act;
}
}
std::swap(*act, *domain[i].begin());
}
// branch left
action[i] = domain[i].begin();
// if(debug_dfs){
// std::cout << "branch left on " << *action[i] << std::endl;
// std::cout << "Domain : " << std::endl;
// for(auto elt : domain[i]){
// std::cout << elt << " ";
// }
// std::cout << "\nIterator at position : " << (action[i] - domain[i].begin()) << std::endl;
// }
commit(*action[i]);
}
if(first_fail) // Fail limit
{
first_fail = false;
if(tardi_depth > ub)
{
max_iter = FAIL_LIMIT;
}else if(tardi_depth > ALPHA * ub)
{
double term = 1-(ub - tardi_depth)/(ub - ub*ALPHA);
max_iter = FAIL_LIMIT * term * term;
// std::cout << max_iter << " : "<< tardi_depth <<"/"<<ub << std::endl;
}else{
// std::cout << max_iter << " : "<< tardi_depth <<"<"<<ALPHA*ub << std::endl;
}
}
// the branch is full
if(sequence.size() == data.nb_tasks) {
save_state();
ub = best_tardiness;
if(verbose)
cout << "d lmax=" << best_tardiness << " iter=" << iter << endl;
if(verified)
verify("solution");
if(best_tardiness == 0) {
if(verbose)
cout << "o solution found\n";
break;
}
}
// if(debug_dfs){
// std::cout << "Backtrack" << std::endl;
// }
while(sequence.size() > start_level) {
int i;
do {
undo();
i = sequence.size();
++action[i];
} while(sequence.size() > start_level and get_tardiness() > 0);
if(sequence.empty() || get_tardiness() > 0)
break;
// if there is an action that we haven't tried yet, try it, otherwise backtrack again
if(action[i] != domain[i].end()) {
maximum_tardiness.push_back(std::max(get_tardiness(), compute_distance(*action[i])));
// if(debug_dfs){
// std::cout << "branch right on " << *action[i] << std::endl;
// std::cout << "Domain : " << std::endl;
// for(auto elt : domain[i]){
// std::cout << elt << " ";
// }
// std::cout << "\nIterator at position : " << (action[i] - domain[i].begin()) << std::endl;
// }
commit(*action[i]);
break;
}
}
// if no tardiness after backtrack, then update tardi_depth
if(get_tardiness() == 0)
{
tardi_depth = sequence.size();
}
// check if the algorithm should stop
if(sequence.empty() and action[0] == domain[0].end()) {
// if(verbose)
cout << "o search tree exhausted: " << best_tardiness << " at temperature " << temperature << " \n" ;
} else if(best_tardiness == 0) {
if(verbose)
cout << "o solution found\n";
} else if(iter >= max_iter) {
if(verbose)
cout << "o time out ( "<< max_iter << ")" <<"\n";
} else