#include "rogueviz.h" // SAG visualizer (e.g. Reddit roguelikes, GitHub languages) //----------------------------------------------------------- // see: https://www.youtube.com/watch?v=mDG3_f8R2Ns (SAG boardgames) // see: https://www.youtube.com/watch?v=WSyygk_3j9o (SAG roguelikes) // see: https://www.youtube.com/watch?v=HWQkDkeEUeM (SAG programming languages) #include "dhrg/dhrg.h" #include namespace rogueviz { namespace sag { int threads = 1; int informat; /* format ID */ bool turn(int delta); int sagpar = 0; int best_cost = 1000000000; enum eSagmode { sagOff, sagHC, sagSA }; eSagmode sagmode; // 0 - off, 1 - hillclimbing, 2 - SA const char *sagmodes[3] = {"off", "HC", "SA"}; ld temperature = -4; const char *loadfname; string auto_save; bool auto_visualize = true; int vizsa_start; int vizsa_len; /** all the SAG cells */ vector sagcells; /** table of distances between SAG cells */ vector> sagdist; /** what node is on sagcells[i] */ vector sagnode; /** node i is on sagcells[sagid[i]] */ vector sagid; /** sagcells[ids[c]]] == c */ map ids; /** if i in neighbors[j], sagcells[i] is a neighbor of sagcells[j] */ vector> neighbors; ld pdist(hyperpoint hi, hyperpoint hj); /** matrix for every sagcell */ vector cell_matrix; /** precision of geometric distances */ int gdist_prec; /** the maximum value in sagdist +1 */ int max_sag_dist; vector sagedges; vector> edges_yes, edges_no; int logistic_cost; /* 0 = disable, 1 = enable */ dhrg::logistic lgsag(1, 1); vector loglik_tab_y, loglik_tab_n; int ipturn = 100; int numiter = 0; int hightemp = 10; int lowtemp = -15; /* for the embedding method: */ bool embedding; dhrg::logistic lgemb(1, 1); vector placement; void optimize_sag_loglik(); void compute_dists() { int N = isize(sagcells); neighbors.clear(); neighbors.resize(N); for(int i=0; i visited; auto visit = [&] (int id, const transmatrix& T) { if(cell_matrix[id][0][0] != ERROR) return; cell_matrix[id] = T; visited.push_back(id); }; visit(0, Id); for(int i=0; itype; d++) if(ids.count(c0->move(d))) visit(ids[c0->move(d)], T0 * currentmap->adj(c0, d)); } if(gdist_prec) { for(int i=0; i q; auto visit = [&] (int j, int dist) { if(sdi[j] < N) return; sdi[j] = dist; q.push_back(j); }; visit(i, 0); for(int j=0; jallcells(); int N = isize(sagcells); ids.clear(); for(int i=0; i hubval; double costat(int vid, int sid) { if(vid < 0) return 0; double cost = 0; if(logistic_cost) { auto &s = sagdist[sid]; for(auto j: edges_yes[vid]) cost += loglik_tab_y[s[sagid[j]]]; for(auto j: edges_no[vid]) cost += loglik_tab_n[s[sagid[j]]]; return -cost; } vertexdata& vd = vdata[vid]; for(int j=0; jweight2; } if(!hubval.empty()) { for(auto sid2: neighbors[sid]) { int vid2 = sagnode[sid2]; if(vid2 >= 0 && (hubval[vid] & hubval[vid]) == 0) cost += hub_penalty; } } return cost; } // std::mt19937 los; double cost; vector chgs; edgetype *sag_edge; void forgetedges(int id) { for(int i=0; iorig = NULL; } bool chance(double p) { p *= double(hrngen.max()) + 1; auto l = hrngen(); auto pv = (decltype(l)) p; if(l < pv) return true; if(l == pv) return chance(p-pv); return false; } void saiter() { int DN = isize(sagid); int t1 = hrand(DN); int sid1 = sagid[t1]; int sid2; int s = hrand(4)+1; if(s == 4) sid2 = hrand(isize(sagcells)); else { sid2 = sid1; for(int ii=0; ii= 0) sagid[t2] = -1; double change = costat(t1,sid2) + costat(t2,sid1) - costat(t1,sid1) - costat(t2,sid2); sagnode[sid1] = t1; sagid[t1] = sid1; sagnode[sid2] = t2; if(t2 >= 0) sagid[t2] = sid2; if(change > 0 && (sagmode == sagHC || !chance(exp(-change * exp(-temperature))))) return; sagnode[sid1] = t2; sagnode[sid2] = t1; sagid[t1] = sid2; if(t2 >= 0) sagid[t2] = sid1; cost += change; } void prepare_graph() { int DN = isize(sagid); set> alledges; for(auto e: sagedges) { if(e.i == e.j) continue; alledges.emplace(e.i, e.j); alledges.emplace(e.j, e.i); } edges_yes.clear(); edges_yes.resize(DN); edges_no.clear(); edges_no.resize(DN); for(int i=0; ibase = sagcells[sag::sagid[i]]; forgetedges(i); } shmup::fixStorage(); } void load_sag_solution(const string& fname) { printf("Loading the sag from: %s\n", fname.c_str()); FILE *sf = fopen(fname.c_str(), "rt"); if(!sf) { printf("Failed to open file.\n"); exit(1); } int SN = isize(sagcells); if(sf) while(true) { string lab; while(true) { int c = fgetc(sf); if(c == EOF) goto afterload; else if(c == ',' || c == ';') break; else if(rv_ignore(c)) ; else lab += c; } int sid = -1; int err = fscanf(sf, "%d", &sid); if(sid < 0 || sid >= SN || err < 1) sid = -1; if(!labeler.count(lab)) { printf("unknown vertex: %s\n", lab.c_str()); } else { int id = getid(lab); sagid[id] = sid; } } afterload: if(sf) fclose(sf); prepare_graph(); reassign(); } void dofullsa(int satime) { sagmode = sagSA; int t1 = SDL_GetTicks(); int tl = -999999; while(true) { int t2 = SDL_GetTicks(); double d = (t2-t1) / (1000. * satime); if(d > 1) break; temperature = hightemp - (d*(hightemp-lowtemp)); for(int i=0; i<10000; i++) { numiter++; sag::saiter(); } if(t2 - tl > 980) { tl = t2; println(hlog, format("it %8d temp %6.4f [1/e at %13.6f] cost = %f ", numiter, double(sag::temperature), (double) exp(sag::temperature), double(sag::cost))); } } temperature = -5; sagmode = sagOff; reassign(); } void dofullsa_iterations(int saiter) { sagmode = sagSA; decltype(SDL_GetTicks()) t1 = -999999; for(int i=0; i 1000) { t1 = t2; println(hlog, format("it %8d temp %6.4f [1/e at %13.6f] cost = %f ", numiter, double(sag::temperature), (double) exp(sag::temperature), double(sag::cost))); } } } temperature = -5; sagmode = sagOff; reassign(); } void iterate() { if(!sagmode) return; int t1 = SDL_GetTicks(); for(int i=0; i 200) ipturn /= 2; else ipturn = ipturn * 100 / t; print(hlog, format("it %8d temp %6.4f [2:%8.6f,10:%8.6f,50:%8.6f] cost = %f\n", numiter, double(sag::temperature), (double) exp(-2 * exp(-sag::temperature)), (double) exp(-10 * exp(-sag::temperature)), (double) exp(-50 * exp(-sag::temperature)), (double) sag::cost)); if(auto_visualize) reassign(); } void save_sag_solution(const string& fname) { FILE *f = fopen(fname.c_str(), "wt"); for(int i=0; i indist(max_sag_dist, 0); const int mul = 1; int N = isize(sagid); for(int i=0; i pedge(max_sag_dist, 0); for(int i=0; i= sag_edge->visible_from) pedge[sagdist[sagid[ei.i]][sagid[ei.j]] * mul]++; } for(int d=0; d(f); int id; if(!labeler.count(lab)) { printf("unknown vertex: %s\n", lab.c_str()); continue; } else id = getid(lab); ld alpha, r; if(1) { dynamicval g(geometry, gNormal); hyperpoint h; for(int d=0; d(f); alpha = atan2(h); r = hdist0(h); println(hlog, "read ", lab, " as ", h, " which is ", tie(alpha, r)); } placement[id] = direct_exp(cspin(0, 2, alpha) * ctangent(0, r)); println(hlog, "dist = ", pdist(placement[id], C0), " expected: ", r); } } else if(informat == 3) { /* BFKL */ string ignore; if(!scan(f, ignore, ignore, ignore, ignore, ignore, ignore, ignore, ignore)) { printf("Error: incorrect format of the first line\n"); exit(1); } while(true) { string lab = scan(f); if(lab == "" || lab == "#ROGUEVIZ_ENDOFDATA") break; ld r, alpha; if(!scan(f, r, alpha)) { printf("Error: incorrect format of r/alpha\n"); exit(1); } hyperpoint h = spin(alpha * degree) * xpush0(r); if(!labeler.count(lab)) { printf("unknown vertex: %s\n", lab.c_str()); } else { int id = getid(lab); placement[id] = h; } } } else if(informat == 4) { while(true) { string lab = scan(f); if(lab == "") break; ld r, alpha; if(!scan(f, r, alpha)) { printf("Error: incorrect format of r/alpha\n"); exit(1); } hyperpoint h = spin(alpha) * xpush0(r); if(!labeler.count(lab)) { printf("unknown vertex: %s\n", lab.c_str()); } else { int id = getid(lab); placement[id] = h; } } } else { while(!feof(f.f)) { string lab = scan(f); int id; if(!labeler.count(lab)) { printf("unknown vertex: %s\n", lab.c_str()); continue; } else id = getid(lab); hyperpoint h; for(int d=0; d(f); placement[id] = h; } } reassign_embedding(); compute_loglik(); } void read_hubs(const string& fname) { hubval.resize(isize(vdata), -1); fhstream f(fname, "rt"); if(!f.f) { printf("Failed to open hub file: %s\n", fname.c_str()); exit(1); } println(hlog, "loading hubs: ", fname); while(!feof(f.f)) { string l1, l2; while(true) { int c = fgetc(f.f); if(c == EOF) return; else if(c == ';') break; else if(rv_ignore(c)) ; else l1 += c; } while(true) { int c = fgetc(f.f); if(c == EOF) return; else if(c == ';') return; else if(rv_ignore(c)) break; else l2 += c; } if(!id_known(l1)) { printf("label unknown: %s\n", l1.c_str()); exit(1); } hubval[getid(l1)] = atoi(l2.c_str()); } } void readsag(const char *fname) { maxweight = 0; sag_edge = add_edgetype("SAG edge"); fhstream f(fname, "rt"); if(!f.f) { printf("Failed to open SAG file: %s\n", fname); throw "failed to open SAG file"; } if(informat == 1) { scanline(f); set > edges; int all = 0, good = 0; while(!feof(f.f)) { string l1 = scan(f); string l2 = scan(f); if(l1 == "") continue; if(l2 == "") continue; edgeinfo ei(sag_edge); ei.i = getid(l1); ei.j = getid(l2); if(ei.i > ei.j) swap(ei.i, ei.j); all++; if(edges.count({ei.i, ei.j})) continue; good++; edges.emplace(ei.i, ei.j); ei.weight = 1; sagedges.push_back(ei); } println(hlog, "N = ", isize(vdata), " edges = ", good, "/", all); return; } while(!feof(f.f)) { string l1, l2; while(true) { int c = fgetc(f.f); if(c == EOF) return; else if(c == ';') break; else if(rv_ignore(c)) ; else l1 += c; } while(true) { int c = fgetc(f.f); if(c == EOF) return; else if(c == ';') break; else if(rv_ignore(c)) ; else l2 += c; } ld wei; if(!scan(f, wei)) continue; edgeinfo ei(sag_edge); ei.i = getid(l1); ei.j = getid(l2); ei.weight = wei; sagedges.push_back(ei); } } ld edgepower=1, edgemul=1; void read(string fn) { fname = fn; init(RV_GRAPH | RV_WHICHWEIGHT | RV_AUTO_MAXWEIGHT | RV_HAVE_WEIGHT); rv_hook(rogueviz::hooks_close, 100, [] { sag::sagedges.clear(); }); rv_hook(shmup::hooks_turn, 100, turn); rv_hook(rogueviz::hooks_rvmenu, 100, [] { dialog::addSelItem(XLAT("temperature"), fts(sag::temperature), 't'); dialog::add_action([] { dialog::editNumber(sag::temperature, sag::lowtemp, sag::hightemp, 1, 0, XLAT("temperature"), ""); }); dialog::addSelItem(XLAT("SAG mode"), sag::sagmodes[sag::sagmode], 'm'); dialog::add_action([] { sag::sagmode = sag::eSagmode( (1+sag::sagmode) % 3 ); }); dialog::addSelItem(XLAT("min temperature"), fts(sag::lowtemp), 'i'); dialog::add_action([] { dialog::editNumber(sag::lowtemp, -20, 20, 1, 0, XLAT("min temperature"), ""); }); dialog::addSelItem(XLAT("max temperature"), fts(sag::hightemp), 'i'); dialog::add_action([] { dialog::editNumber(sag::hightemp, -20, 20, 1, 0, XLAT("high temperature"), ""); }); dialog::addSelItem(XLAT("automatic cycle"), fts(sag::vizsa_len), 'c'); dialog::add_action([] { dialog::editNumber(sag::vizsa_len, 5, 1800, 1, 0, XLAT("automatic cycle"), ""); }); dialog::addBoolItem(XLAT("automatic"), sag::vizsa_start, 'a'); dialog::add_action([] { sag::vizsa_start = sag::vizsa_start ? 0 : SDL_GetTicks(); sag::sagmode = sagOff; }); dialog::addBoolItem_action(XLAT("auto-visualize"), sag::auto_visualize, 'b'); dialog::addBoolItem_action(XLAT("continuous embedding"), sag::embedding, 'e'); }); weight_label = "min weight"; temperature = 0; sagmode = sagOff; readsag(fname.c_str()); if(hub_filename != "") read_hubs(hub_filename); int DN = isize(vdata); for(int i=0; i alldist; for(int j=0; j else if(argis("-sagfull")) { shift(); sag::dofullsa(argi()); } else if(argis("-sagfulli")) { shift(); sag::dofullsa_iterations(argi()); } else if(argis("-sagviz")) { sag::vizsa_start = SDL_GetTicks(); shift(); sag::vizsa_len = argi(); } else if(argis("-sagstats")) { output_stats(); } else if(argis("-sagstats-logid")) { shift(); logid = argi(); } // (5) save the positioning else if(argis("-sagsave")) { PHASE(3); shift(); sag::save_sag_solution(args()); } else if(argis("-sagsave-auto")) { PHASE(3); shift(); auto_save = args(); } // (6) output loglikelihood else if(argis("-sagloglik")) { sag::optimize_sag_loglik(); } else if(argis("-sagmode")) { shift(); vizsa_start = 0; sagmode = (eSagmode) argi(); if(sagmode == sagSA) { shift(); temperature = argf(); } } else if(argis("-sagembed")) { sag::embedding = true; } else if(argis("-sagembedoff")) { sag::embedding = false; } else if(argis("-sagsavee")) { PHASE(3); shift(); sag::save_embedding(args()); } else if(argis("-sagloade")) { PHASE(3); shift(); sag::load_embedding(args()); } else return 1; #endif return 0; } bool turn(int delta) { if(vizsa_start) { auto t = ticks; double d = (t-vizsa_start) / (1000. * vizsa_len); if(d > 1 && logistic_cost == 2) { vizsa_start = ticks; optimize_sag_loglik(); output_stats(); } if(d > 1) sagmode = sagOff; else { temperature = hightemp - (d*(hightemp-lowtemp)); sagmode = sagSA; } } if(sagmode == sagOff && embedding) { embedding_iterate(); } iterate(); return false; // shmup::pc[0]->rebase(); } string cname() { if(euclid) return "coord-6.txt"; if(PURE) return "coord-7.txt"; return "coord-67.txt"; } int ah = addHook(hooks_args, 100, readArgs) + addHook_rvslides(120, [] (string s, vector& v) { if(s != "data") return; using namespace pres; string sagf = "SAG/"; v.push_back( slide{sagf+"Roguelikes", 63, LEGAL::UNLIMITED | QUICKGEO, "A visualization of roguelikes, based on discussion on /r/reddit. " "See: http://www.roguetemple.com/z/hyper/reddit.php", roguevizslide('0', [] () { rogueviz::dftcolor = 0x282828FF; rogueviz::showlabels = true; part(rogueviz::default_edgetype.color, 0) = 181; rogueviz::sag::edgepower = 1; rogueviz::sag::edgemul = 1; gmatrix.clear(); drawthemap(); gmatrix0 = gmatrix; slide_backup(rogueviz::sag::legacy, true); rogueviz::sag::read(RVPATH "roguelikes/edges.csv"); rogueviz::readcolor(RVPATH "roguelikes/color.csv"); rogueviz::sag::load_sag_solution(RVPATH "roguelikes/" + cname()); }) } ); v.push_back(slide {sagf+"Programming languages of GitHub", 64, LEGAL::UNLIMITED | QUICKGEO, "A visualization of programming languages.", roguevizslide('0', [] () { rogueviz::dftcolor = 0x282828FF; rogueviz::showlabels = true; part(rogueviz::default_edgetype.color, 0) = 128; rogueviz::sag::edgepower = .4; rogueviz::sag::edgemul = .02; gmatrix.clear(); drawthemap(); gmatrix0 = gmatrix; slide_backup(rogueviz::sag::legacy, true); rogueviz::sag::read(RVPATH "lang/edges.csv"); rogueviz::readcolor(RVPATH "lang/color.csv"); rogueviz::sag::load_sag_solution(RVPATH "lang/" + cname()); if(euclid) rogueviz::legend.clear(); }) }); v.push_back(slide {sagf+"Boardgames", 62, LEGAL::UNLIMITED | QUICKGEO, "A visualization of board games, based on discussions on Reddit.", roguevizslide('0', [] () { rogueviz::dftcolor = 0x282828FF; rogueviz::showlabels = true; part(rogueviz::default_edgetype.color, 0) = 157; rogueviz::sag::edgepower = 1; rogueviz::sag::edgemul = 1; gmatrix.clear(); drawthemap(); gmatrix0 = gmatrix; slide_backup(rogueviz::sag::legacy, true); rogueviz::sag::read(RVPATH "boardgames/edges.csv"); rogueviz::readcolor(RVPATH "boardgames/color.csv"); rogueviz::sag::load_sag_solution(RVPATH "boardgames/" + cname()); }) }); }); EX } }