From 929a0affa1ca877dce38ab86db5a7252e04dbc6a Mon Sep 17 00:00:00 2001 From: Quentin Cappart Date: Fri, 29 May 2020 16:21:22 -0400 Subject: [PATCH] adding baselines --- benchmarking/portfolio.bmk.sh | 97 +++++++++++++++ benchmarking/tsptw_bmk.sh | 75 ++++++++++++ .../iter_1900_model.pth.tar | Bin 88661 -> 0 bytes src/problem/portfolio/baseline/__init__.py | 0 src/problem/portfolio/baseline/dqn_solving.py | 74 ++++++++++++ src/problem/portfolio/baseline/ppo_solving.py | 114 ++++++++++++++++++ src/problem/portfolio/solving/solver.cpp | 84 ++++--------- src/problem/tsptw/baseline/__init__.py | 0 src/problem/tsptw/baseline/dqn_solving.py | 85 +++++++++++++ src/problem/tsptw/baseline/ppo_solving.py | 106 ++++++++++++++++ src/problem/tsptw/solving/solver.cpp | 86 +++---------- 11 files changed, 590 insertions(+), 131 deletions(-) create mode 100755 benchmarking/portfolio.bmk.sh create mode 100755 benchmarking/tsptw_bmk.sh delete mode 100644 selected-models/dqn/tsptw/n-city-20/grid-100-tw-10-100/iter_1900_model.pth.tar create mode 100644 src/problem/portfolio/baseline/__init__.py create mode 100644 src/problem/portfolio/baseline/dqn_solving.py create mode 100644 src/problem/portfolio/baseline/ppo_solving.py create mode 100644 src/problem/tsptw/baseline/__init__.py create mode 100644 src/problem/tsptw/baseline/dqn_solving.py create mode 100644 src/problem/tsptw/baseline/ppo_solving.py diff --git a/benchmarking/portfolio.bmk.sh b/benchmarking/portfolio.bmk.sh new file mode 100755 index 0000000..7f50008 --- /dev/null +++ b/benchmarking/portfolio.bmk.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +function timeout() { perl -e 'alarm shift; exec @ARGV' "$@"; } + +seed=$1 +size=$2 +time=$3 + +time_sec=$((time/1000)) + +capacity_ratio=0.5 +lambda_1=1 +lambda_2=5 +lambda_3=5 +lambda_4=5 +discrete_coeff=0 +beam_size=16 + +echo "[DQN]" +timeout $time_sec python src/problem/portfolio/baseline/dqn_solving.py --n_item=$size \ + --lambda_1=$lambda_1 \ + --lambda_2=$lambda_2 \ + --lambda_3=$lambda_3 \ + --lambda_4=$lambda_4 \ + --discrete_coeff=$discrete_coeff \ + --seed=$seed + +echo "------------------------------------------------------------------------" + +echo "[PPO]" +timeout $time_sec python src/problem/portfolio/baseline/ppo_solving.py --n_item=$size \ + --lambda_1=$lambda_1 \ + --lambda_2=$lambda_2 \ + --lambda_3=$lambda_3 \ + --lambda_4=$lambda_4 \ + --discrete_coeff=$discrete_coeff \ + --seed=$seed \ + --beam_size=$beam_size + + +echo "------------------------------------------------------------------------" + +echo "[BaB-DQN]" + +./solver_portfolio --model=rl-bab-dqn \ + --time=$time \ + --size=$size \ + --capacity_ratio=0.5 \ + --lambda_1=1 \ + --lambda_2=5 \ + --lambda_3=5 \ + --lambda_4=5 \ + --discrete_coeffs=0 \ + --cache=1 \ + --seed=$seed + + + +echo "------------------------------------------------------------------------" + +echo "[ILDS-DQN]" + +./solver_portfolio --model=rl-ilds-dqn \ + --time=$time \ + --size=$size \ + --capacity_ratio=0.5 \ + --lambda_1=1 \ + --lambda_2=5 \ + --lambda_3=5 \ + --lambda_4=5 \ + --discrete_coeffs=0 \ + --cache=1 \ + --d_l=5000 \ + --seed=$seed + + +echo "------------------------------------------------------------------------" + +echo "[RBS-PPO]" + +./solver_portfolio --model=rl-rbs-ppo \ + --time=$time \ + --size=$size \ + --capacity_ratio=0.5 \ + --lambda_1=1 \ + --lambda_2=5 \ + --lambda_3=5 \ + --lambda_4=5 \ + --discrete_coeffs=0 \ + --cache=1 \ + --luby=1 \ + --temperature=1 \ + --seed=$seed + + + + diff --git a/benchmarking/tsptw_bmk.sh b/benchmarking/tsptw_bmk.sh new file mode 100755 index 0000000..67049b1 --- /dev/null +++ b/benchmarking/tsptw_bmk.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +function timeout() { perl -e 'alarm shift; exec @ARGV' "$@"; } + +seed=$1 +size=$2 +time=$3 +time_sec=$((time/1000)) +grid_size=100 +max_tw_size=100 +max_tw_gap=10 +beam_size=16 + +echo "[DQN]" +timeout $time_sec python src/problem/tsptw/baseline/dqn_solving.py --n_city=$size \ + --grid_size=$grid_size \ + --max_tw_size=$max_tw_size \ + --max_tw_gap=$max_tw_gap \ + --seed=$seed +echo "------------------------------------------------------------------------" + +echo "[PPO]" +timeout $time_sec python src/problem/tsptw/baseline/ppo_solving.py --n_city=$size \ + --grid_size=$grid_size \ + --max_tw_size=$max_tw_size \ + --max_tw_gap=$max_tw_gap \ + --seed=$seed \ + --beam_size=$beam_size +echo "------------------------------------------------------------------------" + +echo "[CP-nearest]" +./solver_tsptw --model=nearest \ + --time=$time \ + --size=$size \ + --grid_size=$grid_size \ + --max_tw_size=$max_tw_size \ + --max_tw_gap=$max_tw_gap \ + --d_l=5000 \ + --cache=1 \ + --seed=$seed +echo "------------------------------------------------------------------------" +echo "[BaB-DQN]" +./solver_tsptw --model=rl-bab-dqn \ + --time=$time \ + --size=$size \ + --grid_size=$grid_size \ + --max_tw_size=$max_tw_size \ + --max_tw_gap=$max_tw_gap \ + --cache=1 \ + --seed=$seed +echo "------------------------------------------------------------------------" +echo "[ILDS-DQN]" +./solver_tsptw --model=rl-ilds-dqn \ + --time=$time \ + --size=$size \ + --grid_size=$grid_size \ + --max_tw_size=$max_tw_size \ + --max_tw_gap=$max_tw_gap \ + --d_l=5000 \ + --cache=1 \ + --seed=$seed +echo "------------------------------------------------------------------------" +echo "[RBS-PPO]" +./solver_tsptw --model=rl-rbs-ppo \ + --time=$time \ + --size=$size \ + --grid_size=$grid_size \ + --max_tw_size=$max_tw_size \ + --max_tw_gap=$max_tw_gap \ + --luby=128 \ + --temperature=20 \ + --cache=1 \ + --seed=$seed + + diff --git a/selected-models/dqn/tsptw/n-city-20/grid-100-tw-10-100/iter_1900_model.pth.tar b/selected-models/dqn/tsptw/n-city-20/grid-100-tw-10-100/iter_1900_model.pth.tar deleted file mode 100644 index 2c2a79ae9ed393195997bfe01499e740c46ce4c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88661 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%0DJR_0 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 diff --git a/src/problem/portfolio/baseline/__init__.py b/src/problem/portfolio/baseline/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/problem/portfolio/baseline/dqn_solving.py b/src/problem/portfolio/baseline/dqn_solving.py new file mode 100644 index 0000000..80d468a --- /dev/null +++ b/src/problem/portfolio/baseline/dqn_solving.py @@ -0,0 +1,74 @@ +import sys +import os +import numpy as np +import argparse +import torch + +sys.path.append(os.path.join(sys.path[0],'..','..','..','..')) + +from src.problem.portfolio.solving.solver_binding import SolverBinding +from src.problem.portfolio.environment.environment import Environment + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Instances parameters + parser.add_argument('--n_item', type=int, default=10) + parser.add_argument('--capacity_ratio', type=float, default=0.5) + parser.add_argument('--lambda_1', type=int, default=1) + parser.add_argument('--lambda_2', type=int, default=5) + parser.add_argument('--lambda_3', type=int, default=5) + parser.add_argument('--lambda_4', type=int, default=5) + parser.add_argument('--discrete_coeff', type=int, default=0) + parser.add_argument('--seed', type=int, default=1) + + return parser.parse_args() + +if __name__ == '__main__': + + args = parse_arguments() + + sys.stdout.flush() + rl_algorithm = "dqn" + + load_folder = "./selected-models/dqn/portfolio/n-item-%d/capacity-ratio-%.1f/moment-factors-%d-%d-%d-%d" % \ + (args.n_item, args.capacity_ratio, args.lambda_1, args.lambda_2, args.lambda_3, args.lambda_4) + + solver_binding = SolverBinding(load_folder, args.n_item, args.capacity_ratio, + args.lambda_1, args.lambda_2, args.lambda_3, args.lambda_4, + args.discrete_coeff, args.seed, rl_algorithm) + + + env = Environment(solver_binding.instance, solver_binding.n_feat, 1) + + cur_state = env.get_initial_environment() + + solution = [] + total_profit = 0 + + while True: + + nn_input = env.make_nn_input(cur_state, 'cpu') + + avail = env.get_valid_actions(cur_state) + + nn_input = nn_input.unsqueeze(0) + available = avail.astype(bool) + + with torch.no_grad(): + res = solver_binding.model(nn_input) + + out = res.cpu().numpy().squeeze(0) + + action_idx = np.argmax(out[available]) + + action = np.arange(len(out))[available][action_idx] + + cur_state, reward = env.get_next_state_with_reward(cur_state, action) + solution.append(action) + total_profit += reward + if cur_state.is_done(): + break + + print("ITEMS INSERTED:", solution) + print("BEST SOLUTION:", total_profit) \ No newline at end of file diff --git a/src/problem/portfolio/baseline/ppo_solving.py b/src/problem/portfolio/baseline/ppo_solving.py new file mode 100644 index 0000000..5a31f31 --- /dev/null +++ b/src/problem/portfolio/baseline/ppo_solving.py @@ -0,0 +1,114 @@ +import sys +import os +import numpy as np +import argparse +import torch + +sys.path.append(os.path.join(sys.path[0],'..','..','..','..')) + +from src.problem.portfolio.solving.solver_binding import SolverBinding +from src.problem.portfolio.environment.environment import Environment + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Instances parameters + parser.add_argument('--n_item', type=int, default=10) + parser.add_argument('--capacity_ratio', type=float, default=0.5) + parser.add_argument('--lambda_1', type=int, default=1) + parser.add_argument('--lambda_2', type=int, default=5) + parser.add_argument('--lambda_3', type=int, default=5) + parser.add_argument('--lambda_4', type=int, default=5) + parser.add_argument('--discrete_coeff', type=int, default=0) + parser.add_argument('--seed', type=int, default=1) + parser.add_argument('--beam_size', type=int, default=4) + return parser.parse_args() + +if __name__ == '__main__': + + args = parse_arguments() + + sys.stdout.flush() + rl_algorithm = "ppo" + + load_folder = "./selected-models/ppo/portfolio/n-item-%d/capacity-ratio-%.1f/moment-factors-%d-%d-%d-%d" % \ + (args.n_item, args.capacity_ratio, args.lambda_1, args.lambda_2, args.lambda_3, args.lambda_4) + + solver_binding = SolverBinding(load_folder, args.n_item, args.capacity_ratio, + args.lambda_1, args.lambda_2, args.lambda_3, args.lambda_4, + args.discrete_coeff, args.seed, rl_algorithm) + + + env = Environment(solver_binding.instance, solver_binding.n_feat, 1) + + cur_state = env.get_initial_environment() + + sequences = [[list(), cur_state, 1.0]] + + for _ in range(args.n_item): + + all_candidates = list() + for i in range(len(sequences)): + + seq, state, score = sequences[i] + + if state is not None: + state_feats = env.make_nn_input(state, "cpu") + avail = env.get_valid_actions(state) + available_tensor = torch.FloatTensor(avail) + with torch.no_grad(): + batched_set = state_feats.unsqueeze(0) + out = solver_binding.model(batched_set) + action_probs = out.squeeze(0) + + action_probs = action_probs + torch.abs(torch.min(action_probs)) + action_probs = action_probs - torch.max(action_probs * available_tensor) + action_probs = solver_binding.actor_critic_network.masked_softmax(action_probs, available_tensor, dim=0, + temperature=1) + action_probs = action_probs.detach() + + for j in range(2): + + if state is not None and action_probs[j] > 10e-30: + next_state, reward = env.get_next_state_with_reward(state, j) + candidate = [seq + [j], next_state, score * action_probs[j].detach()] + all_candidates.append(candidate) + else: + candidate = [seq + [-1], None, 0] + all_candidates.append(candidate) + # order all candidates by score + ordered = sorted(all_candidates, key=lambda tup: -tup[2]) + # select k best + sequences = ordered[:args.beam_size] + + bs_tour = [x for (x, y, z) in sequences] + bs_tour = filter(lambda x: not -1 in x, bs_tour) + bs_tour = list(bs_tour) + best_profit = -1000000 + best_sol = [] + for k in range(len(bs_tour)): + # print(bs_tour[k]) + + if not args.discrete_coeff: + tot_mean = sum([a * b for a, b in zip(bs_tour[k], env.instance.means)]) + tot_deviation = sum([a * b for a, b in zip(bs_tour[k], env.instance.deviations)]) ** (1. / 2) + tot_skewness = sum([a * b for a, b in zip(bs_tour[k], env.instance.skewnesses)]) ** (1. / 3) + tot_kurtosis = sum([a * b for a, b in zip(bs_tour[k], env.instance.kurtosis)]) ** (1. / 4) + + else: + tot_mean = int(sum([a * b for a, b in zip(bs_tour[k], env.instance.means)])) + tot_deviation = int(sum([a * b for a, b in zip(bs_tour[k], env.instance.deviations)]) ** (1. / 2)) + tot_skewness = int(sum([a * b for a, b in zip(bs_tour[k], env.instance.skewnesses)]) ** (1. / 3)) + tot_kurtosis = int(sum([a * b for a, b in zip(bs_tour[k], env.instance.kurtosis)]) ** (1. / 4)) + + tot_profit = args.lambda_1 * tot_mean - \ + args.lambda_2 * tot_deviation + \ + args.lambda_3 * tot_skewness - \ + args.lambda_4 * tot_kurtosis + + if tot_profit > best_profit: + best_sol = bs_tour[k] + best_profit = tot_profit + + print("ITEMS INSERTED:", best_sol) + print("BEST SOLUTION:", best_profit) \ No newline at end of file diff --git a/src/problem/portfolio/solving/solver.cpp b/src/problem/portfolio/solving/solver.cpp index 6001811..c655c09 100644 --- a/src/problem/portfolio/solving/solver.cpp +++ b/src/problem/portfolio/solving/solver.cpp @@ -2,7 +2,9 @@ #include #include #include - +#include +#include +#include #include #include @@ -27,6 +29,8 @@ std::chrono::time_point start; std::map, std::vector> predict_cache; + + Rnd r(1U); auto t = time(0); std::default_random_engine generator(0); @@ -105,8 +109,11 @@ class Portfolio_DP : public FloatMaximizeScript { mode = "ppo"; } + std::stringstream stream; + stream << std::fixed << std::setprecision(1) << opt.capacity_ratio; + string model_folder = "./selected-models/" + mode + "/portfolio/n-item-" + std::to_string(this->n_item) + - "/capacity-ratio-0.5" + //std::to_string(opt.capacity_ratio) + + "/capacity-ratio-" + stream.str() + "/moment-factors-" + std::to_string(opt.lambda_1) + "-" + std::to_string(opt.lambda_2) + "-" + std::to_string(opt.lambda_3)+ "-" + std::to_string(opt.lambda_4); @@ -400,47 +407,6 @@ class Portfolio_DP : public FloatMaximizeScript { }; -/* Class only used for getting search statistics */ -class SimpleSearchTracer : public SearchTracer { -protected: - static const char* t2s(EngineType et) { - switch (et) { - case EngineType::DFS: return "DFS"; - case EngineType::BAB: return "BAB"; - case EngineType::LDS: return "LDS"; - case EngineType::RBS: return "RBS"; - case EngineType::PBS: return "PBS"; - case EngineType::AOE: return "AOE"; - } - } -public: - - int n_node; - - SimpleSearchTracer(void) { - this->n_node = 0; - } - // init - virtual void init(void) { - } - // node - virtual void node(const EdgeInfo& ei, const NodeInfo& ni) { - this->n_node = this->n_node + 1; - } - // round - virtual void round(unsigned int eid) { - //std::cout << "accumulated number of nodes: " << this->n_node << std::endl; - } - // skip - virtual void skip(const EdgeInfo& ei) { - - } - // done - virtual void done(void) { - std::cout << "total node: " << this->n_node << std::endl; - } - virtual ~SimpleSearchTracer(void) {} -}; Portfolio_DP::ModelType stringToModel (std::string const& inString) { @@ -496,14 +462,12 @@ int main(int argc, char* argv[]) { opt.d_l(result["d_l"].as()); Portfolio_DP* p = new Portfolio_DP(opt); - SimpleSearchTracer* tracer = new SimpleSearchTracer(); if(opt.model() == Portfolio_DP::RL_DQN) { Search::Options o; Search::TimeStop ts(opt.time()); o.stop = &ts; o.d_l = opt.d_l(); - o.tracer = tracer; Search::Cutoff* c = Search::Cutoff::luby(result["luby"].as()); o.cutoff = c; @@ -522,16 +486,12 @@ int main(int argc, char* argv[]) { } delete p; } - std::chrono::time_point end = std::chrono::system_clock::now(); - int elapsed_seconds = std::chrono::duration_cast (end-start).count(); - cout << elapsed_seconds << "ms" << endl; - cout << "peak-depth:" << engine.statistics().depth << endl; - + cout << "BEST SOLUTION: " << best_cost << endl; if(engine.stopped()){ - cout << "TIMEOUT" << endl; + cout << "TIMEOUT - OPTIMALITY PROOF NOT REACHED" << endl; } else{ - cout << "FOUND OPTIMAL" << endl; + cout << "SEARCH COMPLETED - SOLUTION FOUND IS OPTIMAL" << endl; } } @@ -541,7 +501,7 @@ int main(int argc, char* argv[]) { Search::TimeStop ts(opt.time()); o.stop = &ts; o.d_l = opt.d_l(); - o.tracer = tracer; + Search::Cutoff* c = Search::Cutoff::luby(result["luby"].as()); o.cutoff = c; @@ -561,16 +521,12 @@ int main(int argc, char* argv[]) { } delete p; } - std::chrono::time_point end = std::chrono::system_clock::now(); - int elapsed_seconds = std::chrono::duration_cast (end-start).count(); - cout << elapsed_seconds << "ms" << endl; - cout << "peak-depth:" << engine.statistics().depth << endl; - + cout << "BEST SOLUTION: " << best_cost << endl; if(engine.stopped()){ - cout << "TIMEOUT" << endl; + cout << "TIMEOUT - OPTIMALITY PROOF NOT REACHED" << endl; } else{ - cout << "FOUND OPTIMAL" << endl; + cout << "SEARCH COMPLETED - SOLUTION FOUND IS OPTIMAL" << endl; } } @@ -581,7 +537,6 @@ int main(int argc, char* argv[]) { Search::Cutoff* c = Search::Cutoff::luby(result["luby"].as()); o.cutoff = c; - o.tracer = tracer; RBS engine(p,o); delete p; @@ -597,6 +552,13 @@ int main(int argc, char* argv[]) { } delete p; } + cout << "BEST SOLUTION: " << best_cost << endl; + if(engine.stopped()){ + cout << "TIMEOUT - OPTIMALITY PROOF NOT REACHED" << endl; + } + else{ + cout << "SEARCH COMPLETED - SOLUTION FOUND IS OPTIMAL" << endl; + } } else { diff --git a/src/problem/tsptw/baseline/__init__.py b/src/problem/tsptw/baseline/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/problem/tsptw/baseline/dqn_solving.py b/src/problem/tsptw/baseline/dqn_solving.py new file mode 100644 index 0000000..3cf90ad --- /dev/null +++ b/src/problem/tsptw/baseline/dqn_solving.py @@ -0,0 +1,85 @@ +import sys +import os +import numpy as np +import argparse +import dgl +import torch + +sys.path.append(os.path.join(sys.path[0],'..','..','..','..')) + +from src.problem.tsptw.solving.solver_binding import SolverBinding +from src.problem.tsptw.environment.environment import Environment + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Instances parameters + parser.add_argument('--n_city', type=int, default=20) + parser.add_argument('--grid_size', type=int, default=100) + parser.add_argument('--max_tw_gap', type=int, default=10) + parser.add_argument('--max_tw_size', type=int, default=100) + parser.add_argument('--seed', type=int, default=1) + + return parser.parse_args() + +if __name__ == '__main__': + + args = parse_arguments() + + sys.stdout.flush() + rl_algorithm = "dqn" + + load_folder = "./selected-models/dqn/tsptw/n-city-%d/grid-%d-tw-%d-%d" % \ + (args.n_city, args.grid_size, args.max_tw_gap, args.max_tw_size) + + solver_binding = SolverBinding(load_folder, args.n_city, args.grid_size, args.max_tw_gap, + args.max_tw_size, args.seed, rl_algorithm) + + + env = Environment(solver_binding.instance, solver_binding.n_node_feat, solver_binding.n_edge_feat, + 1, args.grid_size, args.max_tw_gap, args.max_tw_size) + + cur_state = env.get_initial_environment() + + tour = [0] + + while True: + + graph = env.make_nn_input(cur_state, 'cpu') + + avail = env.get_valid_actions(cur_state) + + bgraph = dgl.batch([graph]) + + available = avail.astype(bool) + + with torch.no_grad(): + res = solver_binding.model(bgraph, graph_pooling=False) + + res = dgl.unbatch(res) + + out = [r.ndata["n_feat"].data.cpu().numpy().flatten() for r in res] + out = out[0].reshape(-1) + + action_idx = np.argmax(out[available]) + + + action = np.arange(len(out))[available][action_idx] + + cur_state, reward = env.get_next_state_with_reward(cur_state, action) + tour.append(action) + if cur_state.is_done(): + tour.append(0) + break + + travel_time = 0 + + if len(tour) != args.n_city + 1: + print("TOUR:", tour) + print("NO COMPLETE TOUR FOUND") + + else: + for i in range(args.n_city): + travel_time += solver_binding.instance.travel_time[tour[i]][tour[i + 1]] + print("TOUR:", tour) + print("BEST SOLUTION:", travel_time) \ No newline at end of file diff --git a/src/problem/tsptw/baseline/ppo_solving.py b/src/problem/tsptw/baseline/ppo_solving.py new file mode 100644 index 0000000..8f81af1 --- /dev/null +++ b/src/problem/tsptw/baseline/ppo_solving.py @@ -0,0 +1,106 @@ +import sys +import os +import argparse +import dgl +import torch + +sys.path.append(os.path.join(sys.path[0],'..','..','..','..')) + +from src.problem.tsptw.solving.solver_binding import SolverBinding +from src.problem.tsptw.environment.environment import Environment + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Instances parameters + parser.add_argument('--n_city', type=int, default=20) + parser.add_argument('--grid_size', type=int, default=100) + parser.add_argument('--max_tw_gap', type=int, default=10) + parser.add_argument('--max_tw_size', type=int, default=100) + parser.add_argument('--seed', type=int, default=1) + parser.add_argument('--beam_size', type=int, default=4) + return parser.parse_args() + +if __name__ == '__main__': + + args = parse_arguments() + + sys.stdout.flush() + rl_algorithm = "ppo" + + + load_folder = "./selected-models/ppo/tsptw/n-city-%d/grid-%d-tw-%d-%d" % \ + (args.n_city, args.grid_size, args.max_tw_gap, args.max_tw_size) + + solver_binding = SolverBinding(load_folder, args.n_city, args.grid_size, args.max_tw_gap, + args.max_tw_size, args.seed, rl_algorithm) + + + env = Environment(solver_binding.instance, solver_binding.n_node_feat, solver_binding.n_edge_feat, + 1, args.grid_size, args.max_tw_gap, args.max_tw_size) + + cur_state = env.get_initial_environment() + + sequences = [[[0], cur_state, 1.0]] + + total_reward = 0 + + for _ in range(args.n_city - 1): + + all_candidates = list() + + for i in range(len(sequences)): + seq, state, score = sequences[i] + + if state is not None: + graph = env.make_nn_input(state, 'cpu') + avail = env.get_valid_actions(state) + + available_tensor = torch.FloatTensor(avail) + + bgraph = dgl.batch([graph, ]) + + res = solver_binding.model(bgraph, graph_pooling=False) + + out = dgl.unbatch(res)[0] + + action_probs = out.ndata["n_feat"].squeeze(-1) + + action_probs = action_probs + torch.abs(torch.min(action_probs)) + action_probs = action_probs - torch.max(action_probs * available_tensor) + + action_probs = solver_binding.actor_critic_network.masked_softmax(action_probs, available_tensor, dim=0, + temperature=1) + action_probs = action_probs.detach() + + for j in range(args.n_city): + + if state is not None and action_probs[j] > 10e-30: + next_state, reward = env.get_next_state_with_reward(state, j) + candidate = [seq + [j], next_state, score * action_probs[j].detach()] + all_candidates.append(candidate) + else: + candidate = [seq + [-1], None, 0] + all_candidates.append(candidate) + # order all candidates by score + ordered = sorted(all_candidates, key=lambda tup: -tup[2]) + # select k best + sequences = ordered[:args.beam_size] + + bs_tour = [x for (x, y, z) in sequences] + bs_tour = filter(lambda x: not -1 in x, bs_tour) + bs_tour = list(bs_tour) + + best_travel_time = 10000000 + best_seq = [] + for k in range(len(bs_tour)): + seq = bs_tour[k] + [0] + travel_time_acc = 0 + for i in range(args.n_city): + travel_time_acc += solver_binding.instance.travel_time[seq[i]][seq[i + 1]] + + if travel_time_acc < best_travel_time: + best_seq = seq + best_travel_time = travel_time_acc + print("TOUR:", seq) + print("BEST SOLUTION:", best_travel_time) \ No newline at end of file diff --git a/src/problem/tsptw/solving/solver.cpp b/src/problem/tsptw/solving/solver.cpp index 1f5a2b5..97bc4de 100644 --- a/src/problem/tsptw/solving/solver.cpp +++ b/src/problem/tsptw/solving/solver.cpp @@ -405,50 +405,13 @@ class TSPTW_DP : public IntMinimizeScript { std::chrono::time_point end = std::chrono::system_clock::now(); int elapsed_seconds = std::chrono::duration_cast (end-start).count(); std::stringstream output; - output << this->m << "," << this->seed << "," << elapsed_seconds << "," << tour_cost; + output << this->m << "," << this->seed << "," << elapsed_seconds << "," << this->tour_cost << "," << this->travel_to; return output.str(); } }; -/* Class only used for getting search statistics */ -class SimpleSearchTracer : public SearchTracer { -protected: - static const char* t2s(EngineType et) { - switch (et) { - case EngineType::DFS: return "DFS"; - case EngineType::BAB: return "BAB"; - case EngineType::LDS: return "LDS"; - case EngineType::RBS: return "RBS"; - case EngineType::PBS: return "PBS"; - case EngineType::AOE: return "AOE"; - } - } -public: - - int n_node; - - SimpleSearchTracer(void) { - this->n_node = 0; - } - virtual void init(void) { - } - virtual void node(const EdgeInfo& ei, const NodeInfo& ni) { - this->n_node = this->n_node + 1; - } - virtual void round(unsigned int eid) { - //std::cout << "accumulated number of nodes: " << this->n_node << std::endl; - } - virtual void skip(const EdgeInfo& ei) { - - } - virtual void done(void) { - std::cout << "total node: " << this->n_node << std::endl; - } - virtual ~SimpleSearchTracer(void) {} -}; - TSPTW_DP::ModelType stringToModel (std::string const& inString) { if (inString == "rl-bab-dqn") return TSPTW_DP::RL_BAB; @@ -500,7 +463,6 @@ int main(int argc, char* argv[]) { opt.time(result["time"].as()); opt.d_l(result["d_l"].as()); - SimpleSearchTracer* tracer = new SimpleSearchTracer(); if(opt.model() == TSPTW_DP::RL_DQN || opt.model() == TSPTW_DP::NN_HEURISTIC) { Search::Options o; @@ -508,30 +470,24 @@ int main(int argc, char* argv[]) { o.stop = &ts; TSPTW_DP* p = new TSPTW_DP(opt); o.d_l = opt.d_l(); - o.tracer = tracer; LDS engine(p, o); delete p; - cout << "nb_cities,seed,time,tour_cost,depth" << std::endl; + cout << "n_city,seed,time,tour_cost" << std::endl; while(TSPTW_DP* p = engine.next()) { int cur_cost = p->cost().val(); if(cur_cost < best_cost) { best_cost = cur_cost; int depth = engine.statistics().depth; - cout << p->to_string() << "," << depth << endl ; + cout << p->to_string() << endl ; } delete p; } - - std::chrono::time_point end = std::chrono::system_clock::now(); - int elapsed_seconds = std::chrono::duration_cast (end-start).count(); - cout << elapsed_seconds << "ms" << endl; - cout << "max-depth reached: " << engine.statistics().depth << endl; - + cout << "BEST SOLUTION: " << best_cost << endl; if(engine.stopped()){ - cout << "TIMEOUT" << endl; + cout << "TIMEOUT - OPTIMALITY PROOF NOT REACHED" << endl; } else{ - cout << "FOUND OPTIMAL" << endl; + cout << "SEARCH COMPLETED - SOLUTION FOUND IS OPTIMAL" << endl; } } @@ -542,30 +498,24 @@ int main(int argc, char* argv[]) { o.stop = &ts; TSPTW_DP* p = new TSPTW_DP(opt); o.d_l = opt.d_l(); - o.tracer = tracer; BAB engine(p, o); delete p; - cout << "nb_cities,seed,time,tour_cost,depth" << std::endl; + cout << "n_city,seed,time,tour_cost" << std::endl; while(TSPTW_DP* p = engine.next()) { int cur_cost = p->cost().val(); if(cur_cost < best_cost) { best_cost = cur_cost; int depth = engine.statistics().depth; - cout << p->to_string() << "," << depth << endl ; + cout << p->to_string() << endl ; } delete p; } - - std::chrono::time_point end = std::chrono::system_clock::now(); - int elapsed_seconds = std::chrono::duration_cast (end-start).count(); - cout << elapsed_seconds << "ms" << endl; - cout << "max-depth reached: " << engine.statistics().depth << endl; - + cout << "BEST SOLUTION: " << best_cost << endl; if(engine.stopped()){ - cout << "TIMEOUT" << endl; + cout << "TIMEOUT - OPTIMALITY PROOF NOT REACHED" << endl; } else{ - cout << "FOUND OPTIMAL" << endl; + cout << "SEARCH COMPLETED - SOLUTION FOUND IS OPTIMAL" << endl; } } @@ -579,30 +529,26 @@ int main(int argc, char* argv[]) { Search::Cutoff* c = Search::Cutoff::luby(result["luby"].as()); o.cutoff = c; - o.tracer = tracer; RBS engine(p,o); delete p; - cout << "nb_cities,seed,time,tour_cost,num_nodes,num_fails" << std::endl; + cout << "n_city,seed,time,tour_cost" << std::endl; while(TSPTW_DP* p = engine.next()) { int cur_cost = p->cost().val(); if(cur_cost < best_cost) { best_cost = cur_cost; int num_nodes = engine.statistics().node; int num_fail = engine.statistics().fail; - cout << p->to_string() << "," << num_nodes << "," << num_fail << endl ; + cout << p->to_string() << endl ; } delete p; } - std::chrono::time_point end = std::chrono::system_clock::now(); - int elapsed_seconds = std::chrono::duration_cast (end-start).count(); - cout << elapsed_seconds << "ms" << endl; - + cout << "BEST SOLUTION: " << best_cost << endl; if(engine.stopped()){ - cout << "TIMEOUT" << endl; + cout << "TIMEOUT - OPTIMALITY PROOF NOT REACHED" << endl; } else{ - cout << "FOUND OPTIMAL" << endl; + cout << "SEARCH COMPLETED - SOLUTION FOUND IS OPTIMAL" << endl; } }