k_perm_solver.h
#pragma once
#include <bits/stdc++.h>
#include "Instance.h"
using namespace std;
namespace k_perm_solver {
struct node {
pt r; // robot (-1 means obstacle, 0 means empty, i means i-1th robot
int t; // time of the graph node
node(pt r, int time = 0) : r(r), t(time) {}
const bool operator<(const node& o) const {
if (t != o.t)
return t > o.t;
return r < o.r;
}
bool operator==(const node& o) const { return tie(r, t) == tie(o.r, o.t); }
};
} // namespace k_perm_solver
namespace std {
template <> struct hash<k_perm_solver::node> {
size_t operator()(const k_perm_solver::node& n) const {
return (hash<pt>()(n.r) << 20) ^ n.t;
}
};
} // namespace std
namespace k_perm_solver {
struct k_perm_solver {
using state = pair<pt, int>;
static constexpr int GRID = 500;
static constexpr int OFF = 200;
template <class V>
struct arrmap {
vector<array<array<V, GRID>, GRID>> arr;
arrmap(int t): arr(t+1) {
for (auto& vv : arr) {
for (auto& v : vv) {
v.fill((V) -1);
}
}
}
V& operator[](const state& s) {
return arr[s.second][s.first.x+OFF][s.first.y+OFF];
}
V operator[](const state& s) const {
return arr[s.second][s.first.x+OFF][s.first.y+OFF];
}
bool count(const state& s) const {
return (*this)[s] != -1;
}
void erase(const state& s) {
(*this)[s] = -1;
}
};
instance& ins;
vector<vector<int>> moves; // first index is robot (transpose of move matrix)
vector<vector<int>> closeness; // 2d array of robot-robot path closeness
arrmap<char> blocked_f;
arrmap<short> blocked_r;
arrmap<char> blocked_b;
vector<bitset<GRID>> blocked_s;
// set<pt> blocked_s;
bool makespan;
bool spam_stdio;
/*
k_perm_solver(instance& _ins, bool _makespan)
: ins(_ins), blocked_s(1000), makespan(_makespan), spam_stdio(false) {
if (!verify(ins)) {
cerr << "Requires valid initial sequence" << endl;
assert(false);
}
moves.resize(ins.n);
// convert dense moves into per-robot moves
for (int t = 0; t < ins.time; t++) {
for (int j = 0; j < ins.n; j++) {
moves[j].push_back(ins.moves[t][j]);
}
}
// for each robot, add moves as obstacles
for (int i = 0; i < ins.n; i++) {
add_path(i);
}
// block the obstacles
for (pt p : ins.obstacle) {
blocked_s[p.x+OFF][p.y+OFF] = 1;
}
}*/
k_perm_solver(instance& _ins, bool _makespan, bool _spam_stdio)
: ins(_ins), blocked_f(2*ins.time), blocked_r(2*ins.time), blocked_b(2*ins.time), blocked_s(GRID), makespan(_makespan),
spam_stdio(_spam_stdio) {
if (!verify(ins)) {
cerr << "Requires valid initial sequence" << endl;
assert(false);
}
// allow for increasing makespan
ins.time *= 1.1;
ins.moves.resize(ins.time, vector<int>(ins.n));
moves.resize(ins.n);
// convert dense moves into per-robot moves
for (int t = 0; t < ins.time; t++) {
for (int j = 0; j < ins.n; j++) {
moves[j].push_back(ins.moves[t][j]);
}
}
// make closeness array
closeness.resize(ins.n);
for (int i = 0; i < ins.n; i++) {
closeness[i].resize(ins.n);
}
// for each robot, add moves as obstacles
for (int i = 0; i < ins.n; i++) {
add_path(i);
}
// block the obstacles
for (pt p : ins.obstacle) {
assert(0 <= p.x + OFF && p.x + OFF < GRID && 0 <= p.y + OFF &&
p.y + OFF < GRID);
blocked_s[p.x + OFF][p.y + OFF] = 1;
}
}
void add_closeness(int r, pt p, int t) {
for (int d = 0; d <= 4; d++) {
pt op = p + dxy[d];
if (blocked_f.count(state(op, t))) {
int other_r = blocked_r[state(op, t)];
if (r < 0 || r >= ins.n) {
cerr << "BAD r " << r << " vs " << ins.n << endl;
}
if (other_r < 0 || other_r >= ins.n) {
cerr << "BAD other_r " << other_r << " vs " << ins.n << endl;
}
assert(r != -1);
assert(r < ins.n);
assert(other_r != -1);
assert(other_r < ins.n);
if (other_r == r)
continue;
closeness[other_r][r]++;
closeness[r][other_r]++;
}
}
}
void remove_closeness(int r, pt p, int t) {
for (int d = 0; d <= 4; d++) {
pt op = p + dxy[d];
if (blocked_f.count(state(op, t))) {
int other_r = blocked_r[state(op, t)];
if (other_r == r)
continue;
closeness[other_r][r]--;
closeness[r][other_r]--;
}
}
}
void add_path(int r) {
pt p = ins.start[r];
int L = max((int)moves[r].size(), ins.time);
for (int t = 0; t <= L; t++) {
if (t < (int)moves[r].size()) {
blocked_f[state(p, t)] = moves[r][t];
blocked_r[state(p, t)] = r;
add_closeness(r, p, t);
p = p + dxy[moves[r][t]];
blocked_b[state(p, t)] = moves[r][t];
} else {
blocked_f[state(p, t)] = 0;
}
}
assert(p == ins.target[r]);
}
// removes the path for a robot
void remove_path(int r) {
pt p = ins.start[r];
int L = max((int)moves[r].size(), ins.time);
for (int t = 0; t <= L; t++) {
if (t < (int)moves[r].size()) {
blocked_f.erase(state(p, t));
blocked_r.erase(state(p, t));
remove_closeness(r, p, t);
p = p + dxy[moves[r][t]];
blocked_b.erase(state(p, t));
} else {
blocked_f.erase(state(p, t));
}
}
assert(p == ins.target[r]);
}
bool allowed(node s, int d) {
node ns(s.r + dxy[d], s.t + 1);
// Obstacles
if (blocked_s[ns.r.x + OFF][ns.r.y + OFF])
return false;
// Some robot is already there
if (blocked_f.count(state(ns.r, ns.t)))
return false;
// Some robot is there right now and not moving away in the same dir
if (blocked_f.count(state(ns.r, s.t)) && blocked_f[state(ns.r, s.t)] != d)
return false;
// Some robot is moving into our spot and we're not moving away
if (blocked_b.count(state(s.r, s.t)) && blocked_b[state(s.r, s.t)] != d)
return false;
return true;
}
int actual_time(const vector<int>& moveset) {
if (makespan) {
int T = moveset.size();
while (T > 0 && moveset[T - 1] == 0)
T--;
return T;
} else {
int d = 0;
for (int x : moveset) {
d += (x != 0);
}
return d;
}
}
// Find path thats near the old path
// (must evaluate to true on "in_bounds")
int num_paths = 0;
vector<int> find_path(int r, int maxMS, double time_left,
const pair<int, vector<vector<int>>>& in_bounds_p) {
num_paths++;
const auto& [mx_id, in_bounds] = in_bounds_p;
constexpr static int INF = 0x3f3f3f3f;
vector dist(ins.time + 1, vector(mx_id + 1, INF));
vector par(ins.time + 1, vector(mx_id + 1, -1));
auto get = [&](const pt& p) { return in_bounds[p.x + OFF][p.y + OFF]; };
// unordered_map<node, int> dist;
// unordered_map<node, int> par;
auto t = ins.target[r];
/*
auto comp = [&](const node& u, const node& v) {
int s0 = dist[u] + abs(u.r - t);
int s1 = dist[v] + abs(v.r - t);
if (s0 != s1)
return s1 < s0;
return v < u;
};*/
node s(ins.start[r], 0);
vector<vector<node>> q(ins.time + 1);
// priority_queue<node, vector<node>, decltype(comp)> q(comp);
dist[s.t][get(s.r)] = 0;
q[abs(s.r - t)].push_back(s);
// Max time for search is remaining time
auto start_time = clock();
int iter_count = 0;
bool found = false;
for (int curdist = abs(s.r - t); curdist <= ins.time; curdist++) {
random_shuffle(q[curdist].begin(), q[curdist].end());
while (!q[curdist].empty()) {
if (iter_count++ % 1024 == 0 &&
clock() - start_time >= CLOCKS_PER_SEC * time_left)
goto end;
s = q[curdist].back();
q[curdist].pop_back();
if (s.r == t && s.t >= ins.time) {
found = true;
// cerr << "Nodes expanded: " << dist.size() << endl;
goto end;
}
if (s.t >= ins.time) {
continue;
}
int cur = dist[s.t][get(s.r)];
if (cur + abs(s.r - t) != curdist)
continue;
// cerr << s.r << " " << s.t << endl;
for (int d = 4; d >= 0; d--) {
if (allowed(s, d)) {
node ns(s.r + dxy[d], s.t + 1);
if (get(ns.r) == -1)
continue;
int ndist = cur + (makespan || d != 0);
int pri = ndist + abs(ns.r - t);
if (pri <= ins.time && (ndist < dist[ns.t][get(ns.r)])) {
dist[ns.t][get(ns.r)] = ndist;
par[ns.t][get(ns.r)] = d;
q[pri].push_back(ns);
}
}
}
}
}
end:
if (!found) {
// cerr << "Path not found!" << endl;
return vector<int>();
}
// cerr << "Found path!" << endl;
vector<int> res;
while (s.t != 0) {
auto move = par[s.t][get(s.r)];
res.push_back(move);
s.r = s.r - dxy[move];
s.t--;
}
reverse(res.begin(), res.end());
// if (spam_stdio)
// cerr << "Actual time: " << actual_time(res) << endl;
if (makespan && actual_time(res) >= maxMS)
return vector<int>();
return res;
}
pair<int, vector<vector<int>>> create_bounds(pt p, const vector<int>& moveset,
int R = 8) {
pair res(0, vector(GRID, vector<int>(GRID, -1)));
auto& id = res.first;
for (int i = -1; i < (int)moveset.size(); i++) {
if (i >= 0)
p = p + dxy[moveset[i]];
for (int x = p.x - R / 2; x <= p.x + R / 2; x++) {
for (int y = p.y - R / 2; y <= p.y + R / 2; y++) {
if (res.second[x + OFF][y + OFF] == -1) {
res.second[x + OFF][y + OFF] = id++;
}
}
}
}
return res;
}
// Tries to swap the order the two paths go in
// i.e. freezes the first path and then puts down the second
// or freezes the second and then puts down the first.
bool solve_k(vector<int> r, double time_left, int R) {
vector<pair<int, int>> L(r.size());
int prevMS = 0;
for (int i = 0; i < (int)r.size(); i++) {
L[i].first = actual_time(moves[r[i]]);
L[i].second = r[i];
if (makespan) {
prevMS = max(prevMS, L[i].first);
} else {
prevMS += L[i].first;
}
}
sort(L.rbegin(), L.rend());
for (int i = 0; i < (int)r.size(); i++) {
r[i] = L[i].second;
}
// Save the previous moves
vector<vector<int>> pmove(r.size());
for (int i = 0; i < (int)r.size(); i++) {
pmove[i] = moves[r[i]];
remove_path(r[i]);
}
int span = -1;
int dist = 0;
int ndone = 0;
while (ndone < (int)r.size()) {
auto moveset =
find_path(r[ndone], prevMS, time_left,
create_bounds(ins.start[r[ndone]], pmove[ndone], R));
if (moveset.empty())
break;
moves[r[ndone]] = moveset;
add_path(r[ndone]);
ndone++;
}
// If not feasible, reverse things
if (ndone < (int)r.size()) {
fail:
for (int i = 0; i < ndone; i++) {
remove_path(r[i]);
}
for (int i = 0; i < (int)r.size(); i++) {
moves[r[i]] = pmove[i];
add_path(r[i]);
}
// if (spam_stdio)
// cerr << "Failed to find better span. Swap produced: " << span << endl;
return false;
} else {
// New pair-span
if (makespan) {
for (int i = 0; i < (int)r.size(); i++) {
span = max(span, actual_time(moves[r[i]]));
}
} else {
for (int i = 0; i < (int)r.size(); i++) {
dist = dist + actual_time(moves[r[i]]);
}
if (dist >= prevMS) {
goto fail;
}
}
if (spam_stdio)
cerr << "Better span found: " << prevMS << "->" << span << endl;
return true;
}
}
bool optimize(int seconds, int k, int R) {
if (spam_stdio)
cerr << "Given a compute budget of " << seconds
<< " seconds. k, R = " << k << ", " << R << endl;
auto ti = clock();
srand(time(NULL));
// recompute distances every time because moves changes
vector<int> dist, dist_orig;
vector<pair<int, int>> ranked;
int last = 0;
for (int i = 0; i < ins.n; i++) {
int wt = actual_time(moves[i]);
dist.push_back(wt * wt + last);
last = dist.back();
ranked.push_back({wt, i});
}
dist_orig = dist;
sort(ranked.rbegin(), ranked.rend());
int lol = 0;
while (clock() - ti < seconds * CLOCKS_PER_SEC) {
vector<int> samples;
dist = dist_orig;
lol %= ins.n;
bool sample_wrt_closeness = true;
if (sample_wrt_closeness) {
// have some nonzero probability to pick other entities
int SCALING = sqrt(ins.n) + 1;
vector<int> weights(ins.n, 1);
// first sample is based on dist
while ((int)samples.size() < k) {
int id;
if (false && samples.empty()) {
id = ranked[lol++].second;
} else {
id = lower_bound(dist.begin(), dist.end(), rand() % dist.back()) -
dist.begin();
}
if (find(samples.begin(), samples.end(), id) == samples.end()) {
samples.push_back(id);
// update weight array
for (int i = 0; i < ins.n; i++) {
weights[i] += closeness[id][i] * SCALING;
}
// make sure we never sample things we sampled
for (int i : samples) {
weights[i] = 0;
}
// update distribution we draw from
int last = 0;
for (int i = 0; i < ins.n; i++) {
dist[i] = weights[i] + last;
last = dist[i];
}
}
}
} else {
while ((int)samples.size() < k) {
int id = lower_bound(dist.begin(), dist.end(), rand() % dist.back()) -
dist.begin();
if (find(samples.begin(), samples.end(), id) == samples.end()) {
samples.push_back(id);
}
}
}
// reoptimize wrt to two robots
double curr_time = (clock() - ti) / double(CLOCKS_PER_SEC);
solve_k(samples, seconds - curr_time, R);
}
// Clean-up moves
if (spam_stdio)
cerr << "Old makespan: " << ins.time/2 << endl;
ins.time = 0;
for (int i = 0; i < ins.n; i++) {
ins.time = max(ins.time, (int)moves[i].size());
}
ins.moves.resize(ins.time);
// Transpose moves back into time-major
for (int i = 0; i < ins.n; i++) {
for (int t = 0; t < ins.time; t++) {
if (ins.moves[t].empty())
ins.moves[t].resize(ins.n);
if (t >= (int)moves[i].size()) {
ins.moves[t][i] = 0;
} else {
ins.moves[t][i] = moves[i][t];
}
}
}
bool cont;
do {
cont = false;
if (accumulate(ins.moves.back().begin(), ins.moves.back().end(), 0) ==
0) {
ins.moves.pop_back();
ins.time--;
cont = true;
}
} while (cont);
if (spam_stdio)
cerr << "New makespan: " << ins.time << endl;
return true;
}
};
// returns if improvement found
bool run(instance& ins, int seconds, bool makespan, int k = 3, int R = 8,
bool spam_stdio = false) {
k_perm_solver gd(ins, makespan, spam_stdio);
bool improve = gd.optimize(seconds, k, R);
//cerr << gd.num_paths << " paths\n";
return improve;
}
} // namespace k_perm_solver