diff --git a/rogueviz/dhrg/betweenness.cpp b/rogueviz/dhrg/betweenness.cpp new file mode 100644 index 00000000..251f7427 --- /dev/null +++ b/rogueviz/dhrg/betweenness.cpp @@ -0,0 +1,406 @@ +#include +#include + +// pseudo-betweenness + +namespace dhrg { + +int out(int x, string cmt="") { + println(hlog, "result = ", x, " ", cmt); + return x; + } + +ld outNZ(ld x, string cmt="") { + return x; + } + +int segmentlen(segment *s) { + auto l = s->left; + auto r = s->right; + int i = 1; + while(l != r) l = l->grightsibling(), i++; + return i; + } + +int get0(qtybox& b) { return b.minv == 0 ? b[0] : 0; } + +int tallybox_total(qtybox& box) { + int tt = 0; + for(int i=box.minv; ileft).c_str(), get_path(s->right).c_str(), segmentlen(s), get0(s->qty), tallybox_total(s->qty)); + } + + +int quickdist(segment *p1, segment *p2) { + int d = 0; + int d1 = p1->left->lev; + int d2 = p2->left->lev; + + while(d1>d2) { p1 = p1->parent; d1--; d++; } + while(d2>d1) { p2 = p2->parent; d2--; d++; } + + return segmentdist(p1, p2, d); + } + +pair betweenness3(mycell *c, int setid = 0) { + + vector neighbors; + neighbors = allchildren(c); + if(c->lev) { + neighbors.push_back(c->grightsibling()); + neighbors.push_back(c->rightparent); + if(c->leftparent != c->rightparent) + neighbors.push_back(c->leftparent); + neighbors.push_back(c->gleftsibling()); + } + + build_ack(c, setid); + for(auto c1: neighbors) build_ack(c1, setid); + + std::unordered_map info; + + for(segment* p: acknowledged) { + + int tot = tallybox_total(p->qty); + + info[p] += tot; + + segment *p2 = p->parent; + while(p2) { + if(p2->seen != -1) { + info[p2] -= tot; + break; + } + p2=p2->parent; + } + } + + for(segment* p: acknowledged) { + p->seen = -1; + } + + acknowledged.clear(); + + vector > info_v; + for(auto p: info) if(p.second) info_v.emplace_back(p); + + int NN = isize(neighbors); + int NN2 = 2 * NN; + + vector bydir(NN2, 0); + + int bestdist; + vector bestdir; + + vector neighbor_segs; + for(auto n: neighbors) neighbor_segs.push_back(getsegment(n, n, setid, true)); + + for(auto p: info_v) { + if(p.first->left == c && p.first->right == c) continue; + bestdist = 1000; + bestdir.clear(); + for(int k=0; k 2) { + println(hlog, "many best dirs\n"); + throw hr_exception("many best dirs"); + } + else if(bestdir[0] + 1 == bestdir[1]) + bydir[bestdir[0] + bestdir[1]] += p.second; + else if(bestdir[0] == 0 && bestdir[1] == NN-1) + bydir.back() += p.second; + else { + println(hlog, "non-adjacent best dirs\n"); + for(int k=0; kqty[0]; + + ll elsewhere = 0; + for(int a=0; aleft, p2) || is_insegment(p1->right, p2) || is_insegment(p2->left, p1) || is_insegment(p2->right, p1)) + return true; + + mycell *mright; + + mright = p1->right; + for(int u=0; u<=cgi.expansion->sibling_limit; u++) { + mright->build(); + if(mright == p2->left) return true; + mright = mright->grightsibling(); + } + + mright = p2->right; + for(int u=0; u<=cgi.expansion->sibling_limit; u++) { + mright->build(); + if(mright == p1->left) return true; + mright = mright->grightsibling(); + } + + return false; + } + +template void children(segment *s, const T& f) { + s=s->firstchild; + while(s) { + f(s); + s = s->nextchild; + } + } + +typedef long double betweenness_type; + +betweenness_type ack(int d01, int d02, int d12) { + return pow(cgi.expansion->get_growth(), -(d01 + d02 - d12)); + // if(d01 + d02 == d12+2) return 1; + // return 0; + } + +int sd(segment *s1, segment *s2) { + /* + for(auto l = s1->left;; l = l->grightsibling()) { + if(is_insegment(l, s2)) return 0; + if(l == s1->right) break; + } + + for(auto l = s2->left;; l = l->grightsibling()) { + if(is_insegment(l, s1)) return 0; + if(l == s2->right) break; + } + */ + + return quickdist(s1, s2); + } + +// di0: distance from Hidden to 0 +// di0: distance from Hidden to 1 +betweenness_type brec_fix_other(int di0, const vector& lst, int pos, int di1, segment *s1) { + // indent_measure im("brec-fo " + its(pos) + " in s1: " + segdesc(s1) + " di0= " + its(di0) + " di1= " + its(di1) ); + segment *s0 = lst[pos]; + if(!dependent(s0, s1) || pos == 0) + return outNZ(tallybox_total(s1->qty) * ack(di0+pos, sd(s0, s1)+pos, di1)); + + betweenness_type total = 0; + if(get0(s1->qty)) + total += get0(s1->qty) * ack(di0+pos, sd(s0,s1)+pos, di1); + + for(segment *c1 = s1->firstchild; c1; c1 = c1->nextchild) + total += brec_fix_other(di0+1, lst, pos-1, di1+1, c1); + + return outNZ(total); + } + +map, betweenness_type> memo[BOXSIZE]; + +betweenness_type brec_fix_main(int d1, segment *s1, int d2, segment *s2); + +betweenness_type brec_fix_main_actual(int d1, segment *s1, int d2, segment *s2) { + betweenness_type total = 0; + if(get0(s1->qty)) + total += get0(s1->qty) * tallybox_total(s2->qty) * ack(d1, d2, sd(s1, s2)); + if(get0(s2->qty)) + total += get0(s2->qty) * tallybox_total(s1->qty) * ack(d1, d2, sd(s1, s2)); + if(get0(s1->qty) && get0(s2->qty)) + total -= get0(s1->qty) * get0(s2->qty) * ack(d1, d2, sd(s1, s2)); + + for(segment *c1 = s1->firstchild; c1; c1 = c1->nextchild) + for(segment *c2 = s2->firstchild; c2; c2 = c2->nextchild) + total += brec_fix_main(d1+1, c1, d2+1, c2); + + return total; + } + +betweenness_type brec_fix_main(int d1, segment *s1, int d2, segment *s2) { + // indent_measure im("brec-main " + its(d1) + " in s1: " + segdesc(s1) + " " + its(d2) + " in s2: " + segdesc(s2)); + if(!dependent(s1, s2)) + return outNZ(tallybox_total(s1->qty) * tallybox_total(s2->qty) * ack(d1, d2, sd(s1, s2))); + + // if(s1->left->lev >= 10) return brec_fix_main_actual(d1, s1, d2, s2); + + auto& mem = memo[s1->left->lev][make_tuple(d1+d2, s1, s2)]; + if(mem) return mem-1; + + auto total = brec_fix_main_actual(d1, s1, d2, s2); + mem = total + 1; + return total; + } + +betweenness_type brec(const vector& lst, int pos, segment *s1, segment *s2) { + segment *s0 = lst[pos]; + // indent_measure im("brec " + its(pos) + " in s1: " + segdesc(s1) + " in s2: " + segdesc(s2) + " s0 = " + segdesc(s0) ); + bool id01 = !dependent(s0, s1); + bool id02 = !dependent(s0, s2); + if(id01 && id02) { + int di01 = sd(s0, s1); + int di02 = sd(s0, s2); + return outNZ(brec_fix_main(di01+pos, s1, di02+pos, s2)); + } + else { + bool id12 = (id01 || id02) && !dependent(s1, s2); + if(id12 && id02) { + //01 not + int di12 = sd(s1, s2); + int di02 = sd(s0, s2); + return outNZ(tallybox_total(s2->qty) * brec_fix_other(di02, lst, pos, di12, s1)); + } + else if(id12 && id01) { + int di12 = sd(s1, s2); + int di01 = sd(s0, s1); + return outNZ(tallybox_total(s1->qty) * brec_fix_other(di01, lst, pos, di12, s2)); + } + else if(pos == 0) + return outNZ(brec_fix_main(sd(s0, s1), s1, sd(s0, s2), s2)); + else { + betweenness_type total = 0; + if(get0(s1->qty)) + total += get0(s1->qty) * brec_fix_other(sd(s0, s1), lst, pos, sd(s1, s2), s2); + if(get0(s2->qty)) + total += get0(s2->qty) * brec_fix_other(sd(s0, s2), lst, pos, sd(s1, s2), s1); + if(get0(s1->qty) && get0(s2->qty)) + total -= get0(s1->qty) * get0(s2->qty) * ack(pos + sd(s0, s1), pos + sd(s0, s2), sd(s1, s2)); + for(segment *c1 = s1->firstchild; c1; c1 = c1->nextchild) + for(segment *c2 = s2->firstchild; c2; c2 = c2->nextchild) + total += brec(lst, pos-1, c1, c2); + return outNZ(total); + } + } + } + +betweenness_type betweenness4(mycell *c, int setid = 0) { + segment *s = getsegment(c, c, setid, true); + vector lst; + while(s) { lst.push_back(s); s = s->parent; } + segment *rs = getsegment(mroot, mroot, setid, true); + return brec(lst, isize(lst)-1, rs, rs); + } + +bool neq(betweenness_type a, betweenness_type b) { + return abs(a-b) > 1e-6; + } + +void compute_betweenness(bool verify) { + progressbar pb(N, "compute_betweenness"); + int errorcount = 0, errorcount2 = 0; + for(int i=0; i= 8000000000ll / 64) { mm.clear(); } + else { x_total_cache += s; clean_level++; } + } + if(total_cache != x_total_cache) printf("cleanup from %lld to %lld cache items, at level %d\n", total_cache, x_total_cache, clean_level); + // if(i != 1) continue; + mycell *c1 = vertices[i]; + // add_to_set(c1, -1, 0); + auto b = betweenness3(c1); + // add_to_set(c1, 1, 0); + auto b4 = betweenness4(c1); + print(hlog, format("B;%10Ld;%10Ld;%20.10Lf;%3d;%-40s", b.first, b.second, b4, vertices[i]->lev, rogueviz::vdata[i].name.c_str())); + if(verify) { + /* + betweenness_type a = b.first; + betweenness_type b = 0; + for(int j=0; j0) errorcount += (b-a); + if(b-a<0) errorcount2 += (a-b); + if(neq(a, b)) exit(1); + */ + } + else printf("\n"); + pb++; + } + if(verify) println(hlog, format("errorcount = %d/%d\n", errorcount, errorcount2)); + } + +void build(mycell *c, int lev, string s) { + int id = 0; + if(lev) for(mycell *c1: allchildren(c)) { build(c1, lev-1, s + std::to_string(id)); id++; } + printf("* %s\n", s.c_str()); + vertices.push_back(c); + rogueviz::vdata.emplace_back(); + rogueviz::vdata.back().name = s; + /* + vertices.push_back(c); + rogueviz::vdata.emplace_back(); + rogueviz::vdata.back().name = s; + */ + } + +void build_all(int d) { + build(mroot, d, ""); + N = isize(vertices); + counttallies(); + } + +void load_test() { + string s; + while(getline(std::cin, s)) { + mycell *mc = mroot; + if(s[0] == '#') continue; + for(char c: s) if(c >= '0' && c <= '9') mc = allchildren(mc) [c - '0']; + vertices.push_back(mc); + rogueviz::vdata.emplace_back(); + rogueviz::vdata.back().name = "PATH:" + s; + } + // build(mroot, 5, ""); + N = isize(vertices); + counttallies(); + // add_to_set(vertices[0], -1, 0); + } + +} diff --git a/rogueviz/dhrg/dhrg-128.cpp b/rogueviz/dhrg/dhrg-128.cpp new file mode 100644 index 00000000..50bdaf6c --- /dev/null +++ b/rogueviz/dhrg/dhrg-128.cpp @@ -0,0 +1,2 @@ +#define BOXSIZE 128 +#include "dhrg.cpp" diff --git a/rogueviz/dhrg/dhrg-64.cpp b/rogueviz/dhrg/dhrg-64.cpp new file mode 100644 index 00000000..f1fb5fc1 --- /dev/null +++ b/rogueviz/dhrg/dhrg-64.cpp @@ -0,0 +1,2 @@ +#define BOXSIZE 64 +#include "dhrg.cpp" diff --git a/rogueviz/dhrg/dhrg-utils.cpp b/rogueviz/dhrg/dhrg-utils.cpp new file mode 100644 index 00000000..3b996de2 --- /dev/null +++ b/rogueviz/dhrg/dhrg-utils.cpp @@ -0,0 +1,33 @@ +// general utilities +#include + +namespace dhrg { + +typedef long long ll; + +struct progressbar : indenter_finish { + string name; + static const int PBSIZE = 64; + int step = -1, total, drawat = 0, count = -1; + + void operator ++ (int) { + step++; + while(step >= drawat && total) { + count++; + drawat = (total * (count+1)) / PBSIZE; + fprintf(stderr, "%s [", get_stamp().c_str()); + for(int k=0; kgamestart(); } + +int M; +vector vertices; + +vector disttable0, disttable1; + +void memoryInfo(); + +void cellcoords(); +void origcoords(); +void build_disttable(); + +void dhrg_init(); +bool dhrg_animate(int sym, int uni); +} + +#include "readgraph.cpp" +#include "dhrg-utils.cpp" +#include "regular.cpp" +#include "gridmapping.cpp" +#include "mycell.cpp" +#include "segment.cpp" +#include "dynamic.cpp" +#include "loglik.cpp" +#include "paths.cpp" +#include "embedder.cpp" +#include "tests.cpp" +#include "betweenness.cpp" +#include "groundtruth.cpp" +#include "routing.cpp" + +namespace dhrg { + +void memoryInfo() { + string s = ""; + + ll totalmemory = 0; + + for(int it=0; it<2; it++) { + auto pct = [&] (auto x, auto y) { + if(it == 0) totalmemory += x*y; + else s += " " + its(x) + "x" + its(y) + "B=" + its((x*y*100)/totalmemory) + "%"; + }; + pct(cellcount, sizeof(cell)); + pct(heptacount, sizeof(heptagon)); + pct(mycellcount, sizeof(mycell)); + pct(segmentcount, sizeof(segment)); + } + println(hlog, "Memory info: ", s, " (", int(totalmemory/1048576), " MB)"); + fflush(stdout); + } + +void debugtally() { + print(hlog, "T"); for(int i=0; igamestart())); + using namespace rogueviz; +/* for(int i=0; ibase = currentmap->gamestart(); + for(int i=0; iat = In * shmup::ggmatrix(vertices[i]->ascell()); */ + for(int i=0; ibase = vertices[i]->ascell(); + for(int i=0; iat = Id; + fixedges(); + for(int i=N; istore(); + } + +int destroy; + +void clear() { + coords.clear(); + // destroytallies(); + // todo: correct cleanup! +#ifdef BUILD_ON_HR + mymap.clear(); +#else + delete mroot; + mroot = NULL; +#endif + } + +// actual octiles of normal are roughly: -1.15 -.674 -.318 0 .318 .674 1.15 + +void dhrg_init() { + if(!mroot) { + println(hlog, "DHRG version " DHRGVER "\n"); + rogueviz::init(0); + rogueviz::rv_hook(hooks_handleKey, 100, dhrg_animate); + regular_info(); + generate_root(); + } + } + +int next_timestamp; +int ts_rogueviz; +int ts_rbase; +int ts_coords; +int ts_vertices; +bool stored; + +int dhrgArgs() { + using namespace arg; + + if(argis("-dhrg")) { + PHASE(3); shift(); dhrg_init(); read_graph_full(args()); + next_timestamp++; + ts_rogueviz = next_timestamp; + ts_vertices = next_timestamp; + } + + else if(argis("-graph")) { + PHASE(3); shift(); dhrg_init(); read_graph(args(), false, false, false); + next_timestamp++; + ts_rogueviz = next_timestamp; + // stored = true; + } + + else if(argis("-graphv")) { + PHASE(3); shift(); dhrg_init(); read_graph(args(), true, true, true); + next_timestamp++; + ts_rogueviz = next_timestamp; + ts_rbase = next_timestamp; + stored = true; + } + + else if(argis("-analyze_grid")) { + PHASE(3); shift(); dhrg_init(); do_analyze_grid(argi()); + } + + else if(argis("-analyze_dists")) { + PHASE(3); dhrg_init(); shift(); do_analyze_dists(argi()); + } + + else if(argis("-test_paths")) { + PHASE(3); dhrg_init(); shift(); test_paths(argi()); + } + + else if(argis("-contll")) { + if(ts_rogueviz >= ts_rbase && ts_rogueviz >= ts_vertices && ts_rogueviz > ts_coords) { + origcoords(); + build_disttable_approx(); + ts_coords = ts_rogueviz; + } + else if(ts_rbase >= ts_vertices && ts_rbase > ts_coords) { + ts_coords = ts_rbase; + rvcoords(); + build_disttable_approx(); + } + else if(ts_vertices > ts_coords) { + ts_coords = ts_vertices; + cellcoords(); + build_disttable_approx(); + } + logistic cont; + cont.setRT(graph_R, graph_T); + fast_loglik_cont(cont, loglik_cont_approx, "lcont", 1, 1e-6); + // return loglik_cont(); + } + + else if(argis("-dhrgview")) { + if(ts_vertices > ts_rbase) { + place_rogueviz_vertices(); + ts_rbase = ts_vertices; + } + if(!stored) rogueviz::storeall(), stored = true; + else shmup::fixStorage(); + mainloop(); quitmainloop = false; + } + + else if(argis("-iterate")) { + + if(ts_rbase > ts_vertices) { + PHASE(3); dhrg_init(); graph_from_rv(); + ts_vertices = ts_rbase; + } + if(!ts_vertices) { + printf("Error: read vertices with -dhrg or -graph\n"); + exit(1); + } + shift(); + embedder_loop(argi()); + next_timestamp++; + ts_vertices = next_timestamp; + } + + else if(argis("-dorestart")) { + dorestart = true; + } + + else if(argis("-dontrestart")) { + dorestart = false; + } + + else if(argis("-lctype")) { + shift(); lc_type = args()[0]; + } + + else if(argis("-loadtest")) { + dhrg_init(); load_test(); + } + + else if(argis("-buildtest")) { + shift(); dhrg_init(); build_all(argi()); + } + + else if(argis("-pbv")) { + compute_betweenness(true); + } + + else if(argis("-pb")) { + compute_betweenness(false); + } + + else if(argis("-gtt")) { + shift(); ground_truth_test(args()); + } + + else if(argis("-routing")) { + shift(); routing_test(args()); + } + + else if(argis("-esaveas")) { + shift(); save_embedding(args()); + } + + else if(argis("-esave")) { + save_embedding(rogueviz::fname + "-dhrg.txt"); + } + + else if(argis("-eload")) { + PHASE(3); shift(); dhrg_init(); load_embedded(args()); + next_timestamp++; + ts_rogueviz = next_timestamp; + ts_vertices = next_timestamp; + } + + else return 1; + + return 0; + } + +auto hook = + addHook(hooks_args, 50, dhrgArgs) + + addHook(hooks_clearmemory, 200, clear); + +#if CAP_SDL +#include "visualize.cpp" +#endif + +} diff --git a/rogueviz/dhrg/dynamic.cpp b/rogueviz/dhrg/dynamic.cpp new file mode 100644 index 00000000..25998004 --- /dev/null +++ b/rogueviz/dhrg/dynamic.cpp @@ -0,0 +1,146 @@ +// computing pairs of vertices in each distance using dynamic programming (as described in the paper) + +namespace dhrg { + +bool segmentValid(mycell *cl, mycell *cr) { + if(cl == cr) return true; + mycell *c1 = cl; + int d = 0; + for(; d<7 && c1 != cr; d++) { + c1->build(); c1 = c1->grightsibling(); + } + if(d == 7) return false; + cr->build(); + cl = cl->grightsibling()->gleftchild(); + cr = cr->gleftchild(); + return segmentValid(cl, cr); + } + +vector all_child_segments(segment *s) { + vector res; + for(auto m1: allchildren(s->left, -1)) + for(auto m2: allchildren(s->right, +1)) + if(segmentValid(m1, m2)) + res.push_back(getsegment(m1,m2,0,true)); + return res; + } + +// returns 0 if not in segment, 1-based index if in segment +int insegment(mycell *mc, segment *s1) { + mycell *l = s1->left; + int i = 1; + while(true) { + if(l == mc) return i; + if(l == s1->right) return 0; + l->build(); l = l->grightsibling(); + i++; + } + } + +int segmentcode(segment *s) { + mycell *l = s->left; + int code = 0; + while(true) { + code += l->gettype(); + if(l == s->right) return code; + l->build(); l = l->grightsibling(); + code *= 8; + } + } + +int compute_descendants(segment *s, int d) { + + auto memokey = make_tuple(segmentcode(s), d); + static map mem; + if(mem.count(memokey)) return mem[memokey]; + + if(d == 0) return s->left == s->right ? 1 : 0; + int total = 0; + for(auto s1: all_child_segments(s)) + total += compute_descendants(s1, d-1); + return mem[memokey] = total; + } + +// returns 0 if segments are not crossing, positive number if crossing +// for segments with equal codes, equal numbers = the same way of crossing +int segmentcross(segment *s1, segment *s2) { + int i1; + i1 = insegment(s1->left, s2); + if(i1) return 4*i1+1; + i1 = insegment(s1->right, s2); + if(i1) return 4*i1+2; + i1 = insegment(s2->left, s1); + if(i1) return 4*i1+3; + i1 = insegment(s2->right, s1); + if(i1) return 4*i1+4; + return 0; + } + +set allsegments; + +int compute_in_dist(segment *s1, segment *s2, int d1, int d2, int dex) { + // if(d1 + d2 + 4 < dex) return 0; + int d = -segmentcross(s1,s2); + if(!d) d = segmentdist(s1, s2, 0); + + if(d > 2 || d1 == 0 || d2 == 0) { + if(d < 0) d = 0; + return d1+d2+d == dex ? compute_descendants(s1,d1) * compute_descendants(s2,d2) : 0; + } + else { + mycell *ss1 = s1->right, *ss2 = s2->right; + if(d > 0) { + int side = 0; + while(true) { + ss1->build(); ss1 = ss1->grightsibling(); if(ss1 == s2->left) { side=1; break; } + ss2->build(); ss2 = ss2->grightsibling(); if(ss2 == s1->left) { side=2; break; } + } + d += 100 * side; + } + allsegments.insert(segmentcode(s1)); + allsegments.insert(segmentcode(s2)); + auto memokey = make_tuple(segmentcode(s1), segmentcode(s2), d, d1, d2, dex); + static map mem; + if(mem.count(memokey)) return mem[memokey]; + int total = 0; + for(auto s3: all_child_segments(s1)) + for(auto s4: all_child_segments(s2)) + total += compute_in_dist(s3, s4, d1-1, d2-1, dex); + if(0) if(mem.count(memokey) && mem[memokey] != total) { + printf("%d vs %d :: %x %x d=%d %d,%d,%d\n", mem[memokey], total, segmentcode(s1), segmentcode(s2), d, d1, d2, dex); + return mem[memokey]; + } + return mem[memokey] = total; + } + } + +void do_analyze_dists(int rad) { + + println(hlog, "do_analyze_dists (", rad, ")"); + indenter_finish indf; + auto m = mroot; + + auto seg = getsegment(m, m, 0, true); + + // compute the correct answer, but not if this requires creating more than 1500 cells + celllister cl(croot(), rad, 1500, NULL); + vector correct(2*rad+4, 0); + + for(cell *c1: cl.lst) if(celldist(c1) == rad) + for(cell *c2: cl.lst) if(celldist(c2) == rad) + correct[celldistance(c1,c2)]++; + + int total = 0; + for(int a=0; a<2*rad+4; a++) { + int cd = compute_in_dist(seg, seg, rad, rad, a); + printf("%2d: %d/%d\n", a, cd, correct[a]); + total += cd; + } + + printf("total = %d (%d)\n", total, cgi.expansion->get_descendants(5).approx_int() * cgi.expansion->get_descendants(5).approx_int()); + printf("all segments = %d\n", isize(allsegments)); + + // printf("descendants = %d (%d)\n", compute_descendants(seg, 5), int(.1+expansion.get_descendants(5).approx_int())); + } + +} diff --git a/rogueviz/dhrg/embedder.cpp b/rogueviz/dhrg/embedder.cpp new file mode 100644 index 00000000..47b0b155 --- /dev/null +++ b/rogueviz/dhrg/embedder.cpp @@ -0,0 +1,340 @@ +// local search + +namespace dhrg { + +int newmoves = 0; +int iterations = 0; +int distlimit; + +void dispnewmoves() { + if(newmoves == 0) printf("."); + else if(newmoves < 10) printf("%c", '0'+newmoves); + else printf("%c", 'a' + newmoves/10); + newmoves = 0; + } + +vector tomove; + +bool smartmove = false; +bool dorestart = false; + +int lastmoves; + +int movearound() { + indenter_finish im("movearound"); + int total = 0; + if(smartmove) for(bool b: tomove) if(b) total++; + if(total == 0) { + tomove.resize(0), tomove.resize(N, true); + } + int moves = 0; + ld llo = loglik_chosen(); +// int im = 0; + + {progressbar pb(N, "tomove: " + its(total) + " (last: " + its(lastmoves) + ")"); + for(int i=0; ilev >= distlimit) continue; + tallyedgesof(i, 1, mc2[d]); + add_to_tally(mc2[d], 1, 0); + if(lc_type == 'C') + add_to_set(mc2[d], 1, 0); + llo2[d] = loglik_chosen(); + if(lc_type == 'C') + add_to_set(mc2[d], -1, 0); + if(llo2[d] > bestllo) bestd = d, bestllo = llo2[d]; + + add_to_tally(mc2[d], -1, 0); + tallyedgesof(i, -1, mc2[d]); + } + if(bestd >= 0) { + moves++; newmoves++; + vertices[i] = mc = mc2[bestd]; + llo = llo2[bestd]; + tomove[i] = true; + for(auto p: rogueviz::vdata[i].edges) { + int j = p.second->i ^ p.second->j ^ i; + tomove[j] = true; + } + } + tallyedgesof(i, 1, mc); + add_to_tally(mc, 1, 0); + add_to_set(mc, 1, 0); + pb++; + }} + // dispnewmoves(); + println(hlog, " moves = ", moves); + return lastmoves = moves; + } + +int move_restart() { + indenter_finish im("move_restart"); + ld llo = loglik_chosen(); + array, 2> distances_map = {0}; + int moves = 0; +// int im = 0; + + {progressbar pb(N, "move_restart"); + for(int i=0; i bestllo) + bestllo = newllo, mc2 = mcn, changed = true; + } + if(changed) whereto = mc2; + } + bool better = bestllo > llo; + distances_map[better][quickdist(whereto, mc)]++; + if(better) { + llo = bestllo; + vertices[i] = mc = whereto; + moves++; + } + tallyedgesof(i, 1, mc); + add_to_tally(mc, 1, 0); + add_to_set(mc, 1, 0); + pb++; + }} + // dispnewmoves(); + println(hlog, " moves = ", moves); + print(hlog, " stats:"); + for(int a=0; a<2; a++) for(int b=0; b<128; b++) { + int d = distances_map[a][b]; + if(d) print(hlog, format(" %d/%d:%d", a,b, d)); + } + println(hlog, "\n"); + return lastmoves = moves; + } + +// 7: 12694350 3847975716 +// 6: 39472959 11969080911 + +void verifycs() { + long long edgecs = 0, totalcs = 0; + for(int u=0; uget_growth()); + current_logistic.setRT(factor * graph_R, factor * graph_T); + saved_logistic = current_logistic; + + // for(int u=0; uqty[j]) + distlimit = min(j+2, BOXSIZE-1); + + println(hlog, "Using distlimit = ", distlimit); + } + +void read_graph_full(const string& fname) { + using namespace rogueviz; + + memoryInfo(); + + if(true) { + indenter_finish im("Read graph"); + + // N = isize(vdata); + + read_graph(fname, false, false, false); + vertices.resize(N); + progressbar pb(N, "Translating to cells"); + + for(int i=0; igamestart(); + hyperpoint T0 = vdata[i].m->at * C0; + virtualRebase2(vdata[i].m->base, T0, true); + vertices[i] = find_mycell(vdata[i].m->base); +#else + vertices[i] = find_mycell_by_path(computePath(vdata[i].m->at)); +#endif + vdata[i].m->at = Id; + pb++; + // printf("%s\n", computePath(vdata[i].m->base).c_str()); + } + } + + recycle_compute_map(); + preparegraph(); + } + +void graph_from_rv() { + using namespace rogueviz; + + memoryInfo(); + + vertices.resize(N); + progressbar pb(N, "converting RogueViz to DHRG"); + + for(int i=0; ibase); +#else + auto path1 = computePath(vdata[i].m->base); + vertices[i] = find_mycell_by_path(path1); +#endif + vdata[i].m->at = Id; + pb++; + } + + preparegraph(); + } + +bool iteration() { + iterations++; + indenter_finish im("Iteration #" + its(iterations)); + int m = movearound(); + if(!m && dorestart) m = move_restart(); + if(!m) return false; + fix_logistic_parameters(current_logistic, loglik_logistic, "logistic", 1e-6); + writestats(); + fflush(stdout); + return true; + } + +void embedder_loop(int max) { + indenter_finish im("embedder_loop"); + ld lastopt = loglik_chosen(); + + while(max-- && iteration()) { + ld curopt = loglik_chosen(); + println(hlog, "current = %", curopt); + if(curopt <= lastopt) { + println(hlog, "Not enough improvement -- breaking"); + break; + } + lastopt = curopt; + } + } + +void save_embedding(const string s) { + FILE *f = fopen(s.c_str(), "wt"); + for(int i=0; i ids; + for(int i=0; i distances[128]; + + for(cell *c: cl.lst) { + hyperpoint h = celltopoint(c); + ld dd = hdist0(h); + int d = celldist(c); + stats[d][0] ++; + stats[d][1] += dd; + stats[d][2] += dd*dd; + distances[d].push_back(dd); + + if(d>0) { + ld alpha[2]; + int qalpha = 0; + forCellCM(c2, c) if(celldist(c2) == d) { + hyperpoint h1 = celltopoint(c2); + alpha[qalpha++] = atan2(h1[0], h1[1]); + } + if(qalpha != 2) printf("Error: qalpha = %d\n", qalpha); + ld df = alpha[0] - alpha[1]; + if(df<0) df = -df; + while(df > 2*M_PI) df -= 2*M_PI; + while(df > M_PI) df = 2*M_PI - df; + df /= 4*M_PI; + wstats[d][0] += df; + if(d==2) printf("df == %" PLDF " dd = %" PLDF "\n", df, dd); + wstats[d][1] += df*dd; + wstats[d][2] += df*dd*dd; + } + } + + println(hlog, "log(gamma) = ", log(cgi.expansion->get_growth())); + + ld lE, dif, lwE; + for(int d=0; d<32; d++) if(stats[d][0]) { + int q = stats[d][0]; + if(q != cgi.expansion->get_descendants(d).approx_int()) continue; + ld E = stats[d][1] / q; + ld E2 = stats[d][2] / q; + ld Vr = E2 - E * E; + if(Vr < 0) Vr = 0; + dif = E- lE; lE = E; + ld Vd = d > 1 ? Vr/(d-1) : 0; + + ld wE = wstats[d][1]; + ld wE2 = wstats[d][2]; + ld wVr = wE2 - wE * wE; + + print(hlog, format("d=%2d: q = %8d E = %12.8" PLDF " dif = %12.8" PLDF " Vr = %12.8" PLDF " Vr/(d-1)=%12.8" PLDF, + d, q, E, dif, Vr, Vd)); + + if(0) print(hlog, format(" | <%" PLDF "> ex = %12.8" PLDF " d.ex = %12.8" PLDF " Vr = %12.8" PLDF, wstats[d][0], wE, wE - lwE, wVr)); + + ld Sigma = sqrt(Vr); + sort(distances[d].begin(), distances[d].end()); + if(Sigma) for(int u=1; u<8; u++) + print(hlog, format(" %8.5" PLDF, (distances[d][u * isize(distances[d]) / 8] - E) / Sigma)); + + println(hlog); + lwE = wE; + } + } + +} diff --git a/rogueviz/dhrg/groundtruth.cpp b/rogueviz/dhrg/groundtruth.cpp new file mode 100644 index 00000000..01a7e40e --- /dev/null +++ b/rogueviz/dhrg/groundtruth.cpp @@ -0,0 +1,121 @@ + +// $(VARIANT) -nogui -dhrg embedded-graphs/facebook_combined_result -contll -iterate 99 -contll -esave > $@ + +namespace dhrg { + +bool is(char *where, const char *what) { + while(*where && *what && *where == *what) where++, what++; + return !*what; + } + +void ground_truth_test(string s) { + + logistic cont; + + vector logliks; + vector reps; + + auto report = [&] (string s, ld val) { + logliks.push_back(val); + reps.push_back(s); + println(hlog, "REPORT ", s, " = ", val); + }; + + if(1) { + FILE *f = fopen(("embout/" + s).c_str(), "rb"); + char buf[999999]; + int siz = fread(buf, 1, 999999, f); + + for(int i=0; iget_growth()); + } + +} diff --git a/rogueviz/dhrg/loglik.cpp b/rogueviz/dhrg/loglik.cpp new file mode 100644 index 00000000..3b3a1156 --- /dev/null +++ b/rogueviz/dhrg/loglik.cpp @@ -0,0 +1,434 @@ +// log-likelihood computation + +#include +#define USE_THREADS +int threads = 32; + +namespace dhrg { + +ld llcont_approx_prec = 10000; + +// tally edges of the given vertex at the given index + +int edgetally[MAXDIST]; + +void tallyedgesof(int i, int delta, mycell *mc) { + using namespace rogueviz; + for(auto p: vdata[i].edges) { + int j = p.second->i ^ p.second->j ^ i; + if(j==i) printf("LOOP!\n"); + edgetally[quickdist(mc, vertices[j], 0)] += delta; + } + } + +// --- count all edge tallies + +void counttallies() { + using namespace rogueviz; + + { + progressbar pb(N, "Tallying pairs"); + for(int i=0; ii ^ p.second->j ^ i; + if(j < i) { edgetally[quickdist(vertices[i], vertices[j], 0)]++; pb++; } + } + } + + } + +void destroytallies() { + progressbar pb(N, "Destroying tallies"); + for(int i=0; i void fix_logistic_parameters(logistic& l, const T& f, const char *name, ld eps) { + indenter_finish im("fix_logistic_parameters"); + ld cur = f(l); + println(hlog, format("%s = %20.10" PLDF " (R=%10.5" PLDF " T=%" PLDF ")", name, cur, l.R, l.T)); + + for(ld step=1; step>eps; step /= 2) { + + while(true) { l.R += step; ld t = f(l); if(t <= cur) break; cur = t; } + l.R -= step; + + while(true) { l.R -= step; ld t = f(l); if(t <= cur) break; cur = t; } + l.R += step; + + while(true) { l.T += step; ld t = f(l); if(t <= cur) break; cur = t; } + l.T -= step; + + while(true) { l.T -= step; ld t = f(l); if(t <= cur) break; cur = t; } + l.T += step; + + println(hlog, format("%s = %20.10" PLDF " (R=%10.5" PLDF " T=%10.5" PLDF ")", name, cur, l.R, l.T)); + fflush(stdout); + } + } + +logistic current_logistic; +logistic saved_logistic; +logistic cont_logistic; + +// --- continuous logistic loglikelihood +// -------------------------------------- + +vector vertexcoords; + +// compute vertexcoords from the original embedding data +void origcoords() { + indenter_finish im("origcoords"); + using namespace rogueviz; + vertexcoords.resize(N); + for(int i=0; ibase, currentmap->gamestart(), C0) * rogueviz::vdata[i].m->at * C0; + } + +// compute vertexcoords from vertices +void cellcoords() { + indenter_finish im("cellcoords"); + vertexcoords.resize(N); + for(int i=0; iascell()); // calc_relative_matrix(vertices[i]->ascell(), currentmap->gamestart(), C0) * C0; + if(isnan(vertexcoords[i][0])) println(hlog, "got NAN for i=", i, " in lev= ", vertices[i]->lev); + } + } + +// needs cellcoords/rvcoords/origcoords +void build_disttable() { + indenter_finish im("build_disttable"); + int tab[N]; + for(int i=0; ii ^ p.second->j ^ i; + if(j -1e-10) break; + } + + return llh; + } + +// --- placement loglikelihood + +ld loglik_placement() { + mycell *root = mroot; + ld placement_loglik = 0; + auto seg = getsegment(root,root,0); + for(int j=0; jqty[j]; + if(!qj) continue; + placement_loglik += qj * (log(qj*1./N) - cgi.expansion->get_descendants(j).log_approx()); + } + return placement_loglik; + } + +// --- logistic loglikelihood + +ld loglik_logistic(logistic& l = current_logistic) { + ld result = 0; + for(int u=0; u > pairs; + ld result = 0; + for(int u=0; u (edgetally[u], tally[u]); + if(p.second == 0) continue; + while(isize(pairs)) { + auto pb = pairs.back(); + if(p.first / p.second > pb.first / pb.second) + p.first += pb.first, p.second += pb.second, pairs.pop_back(); + else break; + } + pairs.push_back(p); + } + for(auto p: pairs) + result += bestll2(p.first, p.second); + return result; + } + +// --- compute loglikelihood according to current method + +char lc_type = 'R'; + +ld loglik_chosen() { + switch(lc_type) { + case 'O': + return loglikopt(); + case 'R': + return loglik_logistic(); + case 'M': + return loglikopt_mono(); + case 'C': + return loglikopt() + loglik_placement(); + case 'D': + return loglikopt_mono() + loglik_placement(); + default: + return loglikopt(); + } + } + +// 1e-3 (cont), 1e-6 (normal) + +// statistics + +void writestats() { + indenter_finish im("writestats"); + memoryInfo(); + println(hlog, "Vertices by distance (N = ", N, "):"); + mycell *root = mroot; + for(int j=0; jqty[j]; + if(!qj) continue; + print(hlog, " ", j, ":", qj); + } + println(hlog); + ld placement_loglik = loglik_placement(); + + for(int u=0; u auto parallelize(long long N, T action) -> decltype(action(0,0)) { +#ifndef USE_THREADS + return action(0,N); +#else + if(threads == 1) return action(0,N); + std::vector v; + typedef decltype(action(0,0)) Res; + std::vector results(threads); + for(int k=0; k> disttable_approx; + +ld precise_hdist(hyperpoint vi, hyperpoint vj) { + ld da = acosh(vi[2]); + ld db = acosh(vj[2]); + + ld phia = atan2(vi[0], vi[1]); + ld phib = atan2(vj[0], vj[1]); + + ld co = sinh(da) * sinh(db) * (1 - cos(phia-phib)); + // - (vi[0]*vj[0] + vi[1]*vj[1]); + + ld v = cosh(da - db) + co; + if(v < 1) return 0; + + return acosh(v); + } + +void build_disttable_approx() { + indenter_finish im("build_disttable_approx"); + + array zero = {0, 0}; + + using namespace rogueviz; + + std::vector>> results(threads); + std::vector v; + for(int k=0; ki ^ p.second->j ^ i; + if(j void fast_loglik_cont(logistic& l, const T& f, const char *name, ld start, ld eps) { + + indenter_finish im("fix_logistic_parameters"); + ld cur = f(l); + println(hlog, format("%s = %20.10" PLDF " (R=%10.5" PLDF " T=%" PLDF ")\n", name, cur, l.R, l.T)); + + map, double> memo; + auto ff = [&] () { + if(memo.count(make_pair(l.R, l.T))) + return memo[make_pair(l.R, l.T)]; + return memo[make_pair(l.R, l.T)] = f(l); + }; + + for(ld step=start; step>eps; step /= 2) { + + loop: + bool changed = false; + + while(true) { l.R += step; ld t = ff(); if(t <= cur) break; cur = t; changed = true; } + l.R -= step; + + while(true) { l.R -= step; ld t = ff(); if(t <= cur) break; cur = t; changed = true; } + l.R += step; + + while(true) { l.T += step; ld t = ff(); if(t <= cur) break; cur = t; changed = true; } + l.T -= step; + + while(true) { l.T -= step; ld t = ff(); if(t <= cur) break; cur = t; changed = true; } + l.T += step; + + if(changed) goto loop; + + println(hlog, format("%s = %20.10" PLDF " (R=%10.5" PLDF " T=%10.5" PLDF ")\n", name, cur, l.R, l.T)); + fflush(stdout); + } + } + + +} diff --git a/rogueviz/dhrg/mycell.cpp b/rogueviz/dhrg/mycell.cpp new file mode 100644 index 00000000..4438276f --- /dev/null +++ b/rogueviz/dhrg/mycell.cpp @@ -0,0 +1,284 @@ +// mycell -- information about the given vertex of a triangulation +// cell is the relevant struct from HyperRogue; we do not use cell directly to conserve memory + +namespace dhrg { + +int mycellcount; + +struct segmentlist { + segment *s; + segmentlist *next; + }; + +struct mycell { +#ifdef BUILD_ON_HR + cell *c; +#else + int type; +#endif + int lev; + mycell *leftparent, *rightparent; + mycell *leftsibling, *rightsibling; + mycell *leftchild; +#ifdef LONG_BRACKETS + segmentlist* bracketing; +#endif + +#ifdef BUILD_ON_HR + mycell(cell *_c) : c(_c) { + leftparent = rightparent = + leftsibling = rightsibling = + NULL; +#ifdef LONG_BRACKETS + bracketing = NULL; +#endif + mycellcount++; + for(int i=0; i mymap; + +mycell *find_mycell(cell *c) { + mycell*& mc = mymap[c]; + if(mc == NULL) mc = new mycell(c); + return mc; + } + +void mycell::build() { + const auto m = this; + if(m->leftsibling) return; // already computed + cell *c2[MAX_EDGE+1]; + int dist[MAX_EDGE+1]; + int t = m->c->type; + int d = celldist(m->c); + m->lev = d; + if(d == 0) { + m->leftsibling = m->rightsibling = m; + m->leftchild = find_mycell(createMov(m->c,0)); + forCellCM(c2, croot()) find_mycell(c2)->build(); + } + else { + for(int i=0; ic, i); + for(int i=0; ileftparent = find_mycell(c2[i]); + m->leftsibling = find_mycell(c2[i+1]); + m->leftchild = find_mycell(c2[(i+2)%t]); + } + if(dist[i] == d && dist[i+1] < d) { + m->rightparent = find_mycell(c2[i+1]); + m->rightsibling = find_mycell(c2[i]); + } + } + } + } +#endif + +mycell *mroot; + +void generate_root() { +#if BUILD_ON_HR + mroot = find_mycell(croot()); +#else + int origtype = cgi.expansion->rootid; + + mroot = new mycell(); + mroot->lev = 0; + mroot->type = origtype; + mroot->leftsibling = mroot->rightsibling = mroot; + mroot->leftparent = mroot->rightparent = NULL; + + mycell *child; + bool first = true; + for(int c: cgi.expansion->children[origtype]) { + if(first) { + first = false; + mroot->leftchild = child = new mycell(); + } + else { + child->rightsibling = new mycell(); + child->rightsibling->leftsibling = child; + child = child->rightsibling; + } + + child->leftparent = child->rightparent = mroot; + child->type = c; + child->lev = 1; + } + child->rightsibling = mroot->leftchild; + mroot->leftchild->leftsibling = child; +#endif + } + +#ifndef BUILD_ON_HR + +/* mycell *find_mycell(cell *c) { + printf("find_mycell not implemented\n"); + exit(1); + } */ + +mycell* mycell::gleftsibling() { + if(leftsibling) return leftsibling; + leftparent->gchildren(); + if(!leftsibling) { + printf("error: no left sibling found\n"); + exit(1); + } + return leftsibling; + } + +mycell* mycell::grightsibling() { + if(rightsibling) return rightsibling; + rightparent->gchildren(); + if(!rightsibling) { + printf("error: no right sibling found\n"); + } + return rightsibling; + } + +mycell* mycell::gleftchild() { + if(leftchild) return leftchild; + leftchild = new mycell(); + leftchild->leftparent = gleftsibling(); + leftchild->rightparent = this; + leftchild->lev = lev+1; + leftchild->type = cgi.expansion->children[type][0]; + return leftchild; + } + +void mycell::gchildren() { + mycell *child = gleftchild(); + if(child->rightsibling) return; + bool first = true; + for(int c: cgi.expansion->children[type]) { + if(first) { + first = false; + continue; + } + child->rightsibling = new mycell(); + child->rightsibling->leftsibling = child; + child = child->rightsibling; + child->leftparent = child->rightparent = this; + child->type = c; + child->lev = lev + 1; + } + child->rightsibling = grightsibling()->gleftchild(); + child->rightsibling->leftsibling = child; + } +#endif + +vector allchildren(mycell *m, int dir=0) { + m->build(); + vector res; + if(m->lev == 0) { + mycell *f = mroot->leftchild; + int origtype = cgi.expansion->rootid; + for(int i: cgi.expansion->children[origtype]) { + ignore(i); + res.push_back(f); + f = f->rightsibling; + } + return res; + } + auto m1 = m->gleftchild(); + while(true) { + m1->build(); + bool isright = m1->rightparent == m; + bool isleft = m1->leftparent == m; + if(!isright && !isleft) return res; + if(dir > 0 && !isright) ; + else if(dir < 0 && !isleft) ; + else res.push_back(m1); + m1 = m1->grightsibling(); + } + } + +vector allneighbors(mycell *m) { + auto ret = allchildren(m); + if(m->gleftsibling() != m) { + ret.push_back(m->leftsibling); + ret.push_back(m->grightsibling()); + } + if(m->leftparent) { + ret.push_back(m->leftparent); + if(m->rightparent != m->leftparent) + ret.push_back(m->rightparent); + } + return ret; + } + +mycell *find_mycell_by_path(const string& s) { + mycell *at = mroot; + for(char c: s) { + at = at->gleftchild(); + while(c > '0') c--, at = at->grightsibling(); + } + return at; + } + +int childindex(mycell *c) { + mycell *p = c->rightparent->leftchild; + int i = 0; + while(p != c) p = p->grightsibling(), i++; + return i; + } + +string get_path(mycell *c) { + string s; + while(c != mroot) s += '0' + childindex(c), c = c->rightparent; + reverse(s.begin(), s.end()); + return s; + } + +#ifndef BUILD_ON_HR +cell *mycell::ascell() { + if(lev == 0) return croot(); + auto m = this; m->build(); + auto c = rightparent->ascell(); + int childid = 0; + while(m != m->rightparent->leftchild) + childid++, m = m->gleftsibling(); + if(lev == 1) return createMov(croot(), childid); + cell *c2 = ts::child_number(c, childid, celldist); + + return c2; + } +#endif + +} diff --git a/rogueviz/dhrg/paths.cpp b/rogueviz/dhrg/paths.cpp new file mode 100644 index 00000000..a74e06d7 --- /dev/null +++ b/rogueviz/dhrg/paths.cpp @@ -0,0 +1,55 @@ +// computePath to convert HyperRogue's cell to DHRG's mycell (mycell.cpp) + +namespace dhrg { + +// compute the path to the given cell + +hrmap *mapptr; + +string computePath(cell *c) { + string s = ""; + int d = celldist(c); + while(d > 0) { + char z = '0'; + cell *c1 = ts::left_parent(c, celldist); + int id = neighborId(c1, c); + d--; + if(d == 0) z = '0'+id; + else while(true) { + id--; + if(id<0) id += c1->type; + if(celldist(createMov(c1, id)) <= d) break; + if(z >= 'a') { printf("<%d> ", celldist(createMov(c1, id))); } + z++; + } + s += z; c = c1; + } + reverse(s.begin(), s.end()); + return s; + } + +int recycle_counter = 0; + +void recycle_compute_map() { + if(mapptr) { + delete mapptr; + mapptr = NULL; + } + } + +string computePath(const transmatrix& T) { + if(recycle_counter >= 1000) + recycle_compute_map(); + if(!mapptr) mapptr = bt::in() ? bt::new_map() : new hrmap_hyperbolic; + recycle_counter++; + dynamicval dv (currentmap, mapptr); + cell *c = mapptr->gamestart(); + hyperpoint T0 = T * C0; + // call HyperRogue's function virtualRebase: + // a point in the hyperbolic plane is given by cell c and point T0 relative to c + // change c and T0 so that the same point is specified, but with minimal T0 + virtualRebase(c, T0); + return computePath(c); + } + +} diff --git a/rogueviz/dhrg/readgraph.cpp b/rogueviz/dhrg/readgraph.cpp new file mode 100644 index 00000000..1092e54d --- /dev/null +++ b/rogueviz/dhrg/readgraph.cpp @@ -0,0 +1,81 @@ +namespace dhrg { + double graph_R, graph_alpha, graph_T; + vector > coords; + + rogueviz::edgetype *any; + + int N; + + void fixedges() { + using namespace rogueviz; + for(int i=N; idead = true; + for(int i=0; iorig = NULL; + addedge(e->i, e->j, e); + } + } + + void tst() {} + + void read_graph(string fn, bool subdiv, bool doRebase, bool doStore) { + + any = rogueviz::add_edgetype("embedded edges"); + rogueviz::fname = fn; + fhstream f(fn + "-coordinates.txt", "rt"); + if(!f.f) { + printf("Missing file: %s-coordinates.txt\n", rogueviz::fname.c_str()); + exit(1); + } + printf("Reading coordinates...\n"); + string ignore; + if(!scan(f, ignore, ignore, ignore, ignore, N, graph_R, graph_alpha, graph_T)) { + printf("Error: incorrect format of the first line\n"); exit(1); + } + rogueviz::vdata.reserve(N); + while(true) { + string s = scan(f); + if(s == "D11.11") tst(); + if(s == "" || s == "#ROGUEVIZ_ENDOFDATA") break; + int id = rogueviz::getid(s); + rogueviz::vertexdata& vd(rogueviz::vdata[id]); + vd.name = s; + vd.cp = rogueviz::colorpair(rogueviz::dftcolor); + + double r, alpha; + if(!scan(f, r, alpha)) { printf("Error: incorrect format of r/alpha\n"); exit(1); } + coords.push_back(make_pair(r, alpha)); + + transmatrix h = spin(alpha * degree) * xpush(r); + + rogueviz::createViz(id, currentmap->gamestart(), h); + } + + fhstream g(fn + "-links.txt", "rt"); + if(!g.f) { + println(hlog, "Missing file: ", rogueviz::fname, "-links.txt"); + exit(1); + } + println(hlog, "Reading links..."); + int qlink = 0; + while(true) { + int i = rogueviz::readLabel(g), j = rogueviz::readLabel(g); + if(i == -1 || j == -1) break; + addedge(i, j, 1, subdiv, any); + qlink++; + } + + if(doRebase) { + printf("Rebasing...\n"); + for(int i=0; i= at_least) return at_least; + c1 = ts::right_parent(c1, celldist); + c2 = ts::left_parent(c2, celldist); + int ndist = steps + cycle_minus(c2, c1); + if(ndist < at_least) at_least = ndist; + } + } + +int gettypeof(cell *c) { return type_in_reduced(*(cgi.expansion), c, celldist); } + +vector grow_forever; + +set> checked; + +bool err = false; + +int my_sibling_limit; + +void find_sibling_limit(cell *c2, cell *c1) { + if(err) return; + if(celldist(c2) != celldist(c1)) { + printf("not the same ring %d/%d\n", celldist(c1), celldist(c2)); + c1->item = itSilver; + c2->item = itGold; + err = true; + return; + } + vector signature; + cell *cx = c1; + cell *cy = c1; + bool gf = false; + while(cx != c2) { + int t = gettypeof(cx); + if(cx != c1 && grow_forever[t]) gf = true; + signature.push_back(t); cy = cx; cx = ts::right_of(cx, celldist); + } + signature.push_back(gettypeof(cx)); signature.push_back(unlimited_distance(cy, c1) - unlimited_distance(c2, c1)); + if(checked.count(signature)) return; + checked.insert(signature); + // for(int v: signature) printf("%d ", v); + int cm = cycle_minus(c2, c1); + int ud = c1 == c2 ? -1 : 2 + unlimited_distance(ts::left_parent(c2, celldist), ts::right_parent(c1, celldist)); + // printf(": %d/%d {%p/%p} [%d]\n", cm, ud, c1, c2, my_sibling_limit); + if(cm < ud && cm > my_sibling_limit) { my_sibling_limit = cm; } + if(gf) return; + int t1 = gettypeof(c1); + int t2 = gettypeof(c2); + for(int i1=0; i1children[t1]); i1++) + for(int i2=0; i2children[t2]); i2++) + if(c1 != c2 || i1 <= i2+1) + find_sibling_limit(ts::child_number(c2, i2+1, celldist), ts::child_number(c1, i1, celldist)); + } + +void correct_sibling_limit() { + my_sibling_limit = 0; + if(S3 < 4) { + grow_forever.clear(); + grow_forever.resize(cgi.expansion->N, true); + + while(true) { + bool changed = false; + for(int i=0; iN; i++) if(grow_forever[i]) { + grow_forever[i] = false; + if(isize(cgi.expansion->children[i]) == 0) + throw hr_exception("Error: our algorithm does not work if some vertices have no tree children"); + if(isize(cgi.expansion->children[i]) > 1) + for(int c: cgi.expansion->children[i]) + if(grow_forever[c] || c == i) grow_forever[i] = true; + if(!grow_forever[i]) changed = true; + } + if(!changed) break; + } + + print(hlog, "The following grow forever:"); for(int i=0; iN; i++) if(grow_forever[i]) print(hlog, " ", i); println(hlog); + + cell *root = currentmap->gamestart(); + my_sibling_limit = 0; + forCellCM(c1, root) forCellCM(c2, root) find_sibling_limit(c2, c1); + } + println(hlog, "The correct value of sibling_limit is ", my_sibling_limit); + cgi.expansion->sibling_limit = my_sibling_limit; + } + +void regular_info() { + indenter_finish im("regular_info"); + + cgi.expansion->get_descendants(0); + println(hlog, "growth = ", cgi.expansion->get_growth()); + + // int typecount = expansion.N; + + correct_sibling_limit(); + } + +} diff --git a/rogueviz/dhrg/routing.cpp b/rogueviz/dhrg/routing.cpp new file mode 100644 index 00000000..9bb72b3b --- /dev/null +++ b/rogueviz/dhrg/routing.cpp @@ -0,0 +1,150 @@ + +// $(VARIANT) -nogui -dhrg embedded-graphs/facebook_combined_result -contll -iterate 99 -contll -esave > $@ + +namespace dhrg { + +struct pairdata { ld success, route_length; }; + +#define NOYET 127 + +vector > actual; + +vector pairs; + +vector last_goal; + +void prepare_pairs() { + pairs.resize(N); + actual.resize(N); + for(int i=0; i bfsqueue; + auto& p = actual[i]; + auto visit = [&] (int j, int d) { + if(p[j] == NOYET) { + p[j] = d; + bfsqueue.push_back(j); + } + }; + visit(i, 0); + for(int k=0; ki ^ ed.second->j ^ a; + visit(b, p[a] + 1); + } + } + } + last_goal.clear(); + last_goal.resize(N, -1); + } + +void route_from(int src, int goal, const vector& distances_from_goal) { + if(last_goal[src] == goal) return; + if(src == goal) { + pairs[src].success = 1; + pairs[src].route_length = 0; + } + else { + ld bestd = distances_from_goal[src] - 1e-5; + + /* iprintf("route_from goal=%d a=%d, bestd = %f\n", goal, src, bestd + 1e-5); + indent += 2; */ + + vector candidates; + for(auto ed: rogueviz::vdata[src].edges) { + int e = ed.second->i ^ ed.second->j ^ src; + ld d = e == goal ? -1 : distances_from_goal[e]; + if(d < bestd) bestd = d, candidates.clear(); + if(d == bestd) candidates.push_back(e); + } + pairs[src].success = pairs[src].route_length = 0; + for(int c: candidates) { + route_from(c, goal, distances_from_goal); + // iprintf("candidate = %d\n", c); + pairs[src].success += pairs[c].success / isize(candidates); + pairs[src].route_length += (1 + pairs[c].route_length) / isize(candidates); + } + // iprintf("success = %f, route = %f\n", pairs[src].success, pairs[src].route_length); + // indent -= 2; + } + last_goal[src] = goal; + } + +struct iddata { + ld tot, suc, routedist, bestdist; + iddata() { tot = suc = routedist = bestdist = 0; } + } datas[6]; + +template void greedy_routing(int id, const T& distance_function) { + for(int goal=0; goal distances_from_goal(N); + for(int src=0; src reps; + vector values; + + auto report = [&] (string s, ld val) { + values.push_back(val); + reps.push_back(s); + println(hlog, "REPORT ", s, " = ", val); + }; + + if(1) { + FILE *f = fopen(("embout/" + s).c_str(), "rb"); + char buf[999999]; + int siz = fread(buf, 1, 999999, f); + + for(int i=0; iget_growth()); + } + +} diff --git a/rogueviz/dhrg/segment.cpp b/rogueviz/dhrg/segment.cpp new file mode 100644 index 00000000..f4e908b2 --- /dev/null +++ b/rogueviz/dhrg/segment.cpp @@ -0,0 +1,261 @@ +// algorithms to compute distances between mycells, and an implementation of tally counter + +namespace dhrg { + +int segmentcount; + +struct qtybox { + int minv, maxv; + int qty[BOXSIZE]; + int& operator [] (int i) { + if(i>=BOXSIZE) + throw hr_exception("not enough memory reserved -- increase BOXSIZE constant"); + return qty[i]; + } + qtybox() { for(int i=0; ibuild(); pright->build(); + segment *c1 = pleft->byleft[setid]; + while(c1) { + if(c1->right == pright) { + return c1; + } + c1 = c1 -> nextleft; + } + if(!docreate) return c1; + segment *p = new segment; + p->left = pleft; + p->right = pright; + p->nextleft = pleft->byleft[setid]; pleft->byleft[setid] = p; + p->nextright = pright->byright[setid]; pright->byright[setid] = p; + p->nextchild = NULL; p->firstchild = NULL; + if(pleft->leftparent) { + p->parent = getsegment(pleft->leftparent, pright->rightparent, setid); + p->nextchild = p->parent->firstchild; + p->parent->firstchild = p; + } + else + p->parent = NULL; + #ifdef LONG_BRACKETS + if(pleft != pright) { + pleft = pleft->grightsibling(); + int slen = 0; + while(pleft != pright) { + segmentlist *n = new segmentlist; + slen++; + n->s = p; + n->next = pleft->bracketing; + pleft->bracketing = n; + pleft = pleft->grightsibling(); + } + if(slen > 10) throw hr_exception("bad bracket"); + } + #endif + return p; + } + +ll tally[MAXDIST]; + +ll *whichtally = tally; + +vector acknowledged; + +void tallybox(qtybox& box, int d, int mul) { + for(int i=box.minv; iseen == -1) { + p->seen = d; + acknowledged.emplace_back(p); + } + else if(p->seen > d) + p->seen = d; + } + +void acknowledgments(int mul) { + for(segment* p: acknowledged) { + tallybox(p->qty, p->seen, mul); + segment *p2 = p->parent; + int dist = 1; + while(p2) { + if(p2->seen != -1) { + tallybox(p->qty, p2->seen+dist, -mul); + break; + } + p2=p2->parent; dist++; + } + p->seen = -1; + } + acknowledged.clear(); + } + +void trim(qtybox& b) { + while(b.minv < b.maxv && !b[b.minv]) b.minv++; + if(b.minv == b.maxv) b.minv = BOXSIZE, b.maxv = 0; + else while(!b[b.maxv-1]) b.maxv--; + } + +void add_to_set(mycell *c, int mul, int setid) { + // assume mul does not equal 0 ! + segment *p = getsegment(c, c, setid); + int d = 0; + while(p) { + p->qty[d] += mul; + if(p->qty[d]) { + p->qty.minv = min(p->qty.minv, d); + p->qty.maxv = max(p->qty.maxv, d+1); + } + else trim(p->qty); + d++; p = p->parent; + } + } + +int segmentdist(segment *p1, segment *p2, int d); + +void build_ack(mycell *c, int setid) { + segment *p = getsegment(c, c, setid); + int d = 0; + + #ifdef LONG_BRACKETS + segmentlist *brackets = c->bracketing; + while(brackets) { + acknowledge(brackets->s, 0); + brackets = brackets->next; + } + #else + acknowledge(getsegment(c->gleftsibling(), c->grightsibling(), setid, false), 0); // larger! + #endif + + while(p) { + if(p->left != p->right) { + mycell *cc = p->left->grightsibling(); + while(cc != p->right) { + acknowledge(getsegment(cc, cc, setid, false), d); + cc = cc->grightsibling(); + } + } + + int sl = cgi.expansion->sibling_limit; + + mycell *mright = p->right; + mycell *mright0 = mright; + for(int u=0; u<=sl; u++) { + mright->build(); + segment *mrightbyleft = mright->byleft[setid]; + while(mrightbyleft) { + if(u == 3 && p->right->rightparent == mright->leftparent) + acknowledge(mrightbyleft, d+2); + else if(u > 3) + acknowledge(mrightbyleft, segmentdist(getsegment(mright0, mright0, setid), mrightbyleft, d)); + else + acknowledge(mrightbyleft, d+u); + mrightbyleft = mrightbyleft->nextleft; + } + mright=mright->grightsibling(); + } + + mycell *mleft = p->left; + mycell *mleft0 = mleft; + for(int u=0; u<=sl; u++) { + mleft->build(); + segment *mleftbyright = mleft->byright[setid]; + while(mleftbyright) { + if(u == 3 && p->left -> leftparent == mleft->rightparent) + acknowledge(mleftbyright, d+2); + else if(u > 3) + acknowledge(mleftbyright, segmentdist(mleftbyright, getsegment(mleft0, mleft0, setid), d)); + else + acknowledge(mleftbyright, d+u); + mleftbyright = mleftbyright->nextright; + } + mleft=mleft->gleftsibling(); + } + + // go forth + d++; p = p->parent; + } + } + +void add_to_tally(mycell *c, int mul, int setid) { + build_ack(c, setid); + acknowledgments(mul); + } + +bool is_insegment(mycell *c, segment *p) { + mycell *p1 = p->left; + mycell *p2 = p->right; + while(true) { + if(c == p1) return true; + if(p1 == p2) return false; + p1 = p1->grightsibling(); + } + } + +int segmentdist(segment *p1, segment *p2, int d) { + + if(p1->left == p1->right && is_insegment(p1->left, p2)) return d; + if(p2->left == p2->right && is_insegment(p2->left, p1)) return d; + + int best = 1000000; + + while(true) { + if(d >= best) return best; + mycell *mright; + + int sl = cgi.expansion->sibling_limit; + + mright = p1->right; + for(int u=0; u<=sl; u++) { + mright->build(); + if(mright == p2->left) best = min(best, d+u); + mright = mright->grightsibling(); + } + + mright = p2->right; + for(int u=0; u<=sl; u++) { + mright->build(); + if(mright == p1->left) best = min(best, d+u); + mright = mright->grightsibling(); + } + + p1 = p1->parent; + p2 = p2->parent; + d += 2; + } + } + +int quickdist(mycell *c1, mycell *c2, int setid=0) { + int d = 0; + c1->build(); + c2->build(); + int d1 = c1->lev; + int d2 = c2->lev; + segment *p1 = getsegment(c1, c1, setid); + segment *p2 = getsegment(c2, c2, setid); + + while(d1>d2) { p1 = p1->parent; d1--; d++; } + while(d2>d1) { p2 = p2->parent; d2--; d++; } + + return segmentdist(p1, p2, d); + } + +} diff --git a/rogueviz/dhrg/tests.cpp b/rogueviz/dhrg/tests.cpp new file mode 100644 index 00000000..2edbc56f --- /dev/null +++ b/rogueviz/dhrg/tests.cpp @@ -0,0 +1,91 @@ +// check whether our distance and tallycounter algorithms work correctly + +namespace dhrg { + +void test_paths(int radius) { + celllister cl(croot(), radius, 1000000, NULL); + int N = isize(cl.lst); + printf("N = %d\n", N); + vector mycells; + for(cell *c: cl.lst) + mycells.push_back(find_mycell_by_path(computePath(c))); + + int ctable[MAXDIST]; + + for(int u=0; uascell(); + if(c != c1) { + printf("ascell error %s / %s\n", computePath(c).c_str(), computePath(c1).c_str()); + c->item = itEmerald; + c1->item = itRuby; + errorcount++; + return; + } + } + + for(int i=0; iitem = itDiamond; + cl.lst[j]->item = itSilver; + errorcount++; + return; + } + else ctable[a]++; + } + + for(mycell* mc1: mycells) for(mycell* mc2: mycells) { + add_to_set(mc1, 1, 0); + add_to_tally(mc2, 1, 0); + // int v = 0; + if(tally[quickdist(mc1, mc2)] != 1) { + printf("[%p] [%p]\n", mc1, mc2); + printf("quickdist = %d\n", quickdist(mc1, mc2)); + for(int i=0; iascell()->item = itDiamond; + mc2->ascell()->item = itSilver; + errorcount++; + add_to_tally(mc2, -1, 0); + add_to_set(mc1, -1, 0); + return; + } + add_to_tally(mc2, -1, 0); + add_to_set(mc1, -1, 0); + } + + for(mycell* mc: mycells) + add_to_set(mc, 1, 0); + + for(mycell* mc: mycells) + add_to_tally(mc, 1, 0); + + for(int u=0; u= 0) { + for(int i=0; i= 0) { + if(!mousepressed) held_id = -1; + else if(mouseover) { + rogueviz::vdata[held_id].m->base = mouseover; + shmup::fixStorage(); + dhrg::fixedges(); + + auto& mc = vertices[held_id]; + tallyedgesof(held_id, -1, mc); + add_to_set(mc, -1, 0); + add_to_tally(mc, -1, 0); + + mc = find_mycell_by_path(computePath(rogueviz::vdata[held_id].m->base)); + + tallyedgesof(held_id, 1, mc); + add_to_tally(mc, 1, 0); + add_to_set(mc, 1, 0); + } + } + + keyhandler = [] (int sym, int uni) { + + if(uni == '-' && held_id == -1) { + for(int i=0; ibase == mouseover) + held_id = i; + return; + } + + dialog::handleNavigation(sym, uni); + }; + } + +bool dhrg_animate(int sym, int uni) { + if((cmode & sm::NORMAL) && uni == '/') { + clearMessages(); + + if(ts_rbase > ts_vertices) { + dhrg_init(); graph_from_rv(); + ts_vertices = ts_rbase; + place_rogueviz_vertices(); + if(!stored) rogueviz::storeall(), stored = true; + else shmup::fixStorage(); + } + pushScreen(show_likelihood); + + return true; + } + return false; + } diff --git a/rogueviz/rogueviz-all.cpp b/rogueviz/rogueviz-all.cpp index f61d0524..795cae0e 100644 --- a/rogueviz/rogueviz-all.cpp +++ b/rogueviz/rogueviz-all.cpp @@ -76,6 +76,7 @@ // the following comments are read by mymake so that it knows that the files include other files there: // hidden dependencies: rogueviz/nilrider/ +// hidden dependencies: rogueviz/dhrg/ //#endif