1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2026-01-08 20:39:03 +00:00
Files
hyperrogue/rogueviz/sag/data.cpp

477 lines
12 KiB
C++

// RogueViz -- SAG embedder: data manager
// Copyright (C) 2011-24 Zeno Rogue, see 'hyper.cpp' for details
#include "../rogueviz.h"
#include "../embeddings/embeddings.h"
namespace rogueviz {
namespace sag {
using namespace cells;
edgetype *sag_edge;
/** if this is true, no nodes are allowed to be on the same subcell */
bool allow_doubles = false;
/** node i is on sagcells[sagid[i]] */
vector<int> sagid;
/** what node is on sagcells[i] (need loglik_repeat to be off) */
vector<int> sagnode;
/* separate hubs -- only for smClosest */
ld hub_penalty;
string hub_filename;
vector<int> hubval;
vector<vector<int>> edges_yes, edges_no;
vector<vector<pair<int, double>>> edge_weights;
vector<bool> fixed_position;
ld edgepower=1, edgemul=1;
void init();
void compute_cost();
bool colorpartite;
bool take(int i, int j) {
if(colorpartite) return vdata[i].cp != vdata[j].cp;
return i != j;
}
edgetype *ensure_sag_edge() {
if(!sag_edge) sag_edge = add_edgetype("SAG edge");
return sag_edge;
}
vector<int> qon, qsf;
void save_sag_solution(const fhstream& f);
struct sag_embedding : public rogueviz::embeddings::tiled_embedding {
virtual string name() { return "SAG"; }
pair<cell*, hyperpoint> as_location(int id) override {
ld rad = .25 * cgi.scalefactor;
if(isize(subcell_points) > 1) rad /= pow(isize(subcell_points), WDIM);
int ci = sag::sagid[id];
hyperpoint h = C0;
if(allow_doubles && qon.size() && qon[ci] > 1) h =
spin(TAU*qsf[ci] / qon[ci]) * xpush0(rad * (qon[ci]-1) / qon[ci]);
if(isize(subcell_points) > 1)
h = rgpushxto0(subcell_points[sagcells[ci].second]) * h;
return { sagcells[ci].first, h };
}
ld distance(int i, int j) override {
return sagdist[sagid[i]][sagid[j]];
}
ld zero_distance(int i) override {
return sagdist[sagid[i]][0];
}
void save(fhstream& f) {
if(!(state & SS_DATA)) throw hr_exception("save_sag_solution with no data");
for(int i=0; i<isize(sagid); i++)
println(f, vdata[i].name, ";", sagid[i]);
}
};
void prepare_graph() {
int DN = isize(sagid);
DEBBI(debug_init_sag, ("prepare_graph with DN = ", DN));
set<pair<int, int>> alledges;
for(auto e: edgeinfos) {
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);
fixed_position.clear(); fixed_position.resize(DN);
for(int i=0; i<DN; i++) for(int j=0; j<DN; j++) if(take(i, j)) {
if(alledges.count({i, j}))
edges_yes[i].push_back(j);
else
edges_no[i].push_back(j);
}
edge_weights.clear(); edge_weights.resize(DN);
for(auto& e: edgeinfos) {
if(e->i == e->j) continue;
e->weight2 = pow((double) e->weight, (double) edgepower) * edgemul;
edge_weights[e->i].emplace_back(e->j, e->weight2);
edge_weights[e->j].emplace_back(e->i, e->weight2);
}
sagnode.clear();
sagnode.resize(isize(sagcells), -1);
for(int i=0; i<DN; i++)
sagnode[sagid[i]] = i;
compute_cost();
}
void set_inverse();
void place_correctly() {
int DN = isize(sagid);
qon.clear(); qon.resize(isize(sagcells), 0);
qsf.clear(); qsf.resize(isize(sagcells), 0);
for(int i=0; i<DN; i++) {
qsf[i] = qon[sagid[i]];
qon[sagid[i]]++;
}
enable_embedding(make_shared<sag_embedding>());
}
bool visualization_active;
void forgetedges(int id) {
for(int i=0; i<isize(vdata[id].edges); i++)
vdata[id].edges[i].second->orig = NULL;
}
void create_viz() {
if(distance_only) return;
int DN = isize(sagid);
bool vact = state & SS_GRAPH;
state |= SS_GRAPH;
if(!vact) for(int i=0; i<DN; i++) vdata[i].data = 0;
if(sagcells[0].first == nullptr) return;
if(vact) for(int i=0; i<DN; i++) forgetedges(i);
if(!vact) for(int i=0; i<DN; i++) {
vertexdata& vd = vdata[i];
vd.cp = colorpair(dftcolor);
vd.be(sagcells[sagid[i]].first, Id);
}
place_correctly();
set_inverse();
vact = true;
}
/** save the SAG solution (sagid) */
void save_sag_solution(const string& fname) {
DEBBI(debug_init_sag, ("Saving the sag solution to: ", fname));
fhstream f(fname, "wt");
if(!f.f) return file_error(fname);
sag_embedding e;
e.save(f);
}
/** load the SAG solution (sagid) */
void load_sag_solution(const string& fname) {
if(!(state & SS_DATA)) throw hr_exception("load_sag_solution with no data");
if(fname == "-") throw hr_exception("load_sag_solution from RogueViz not implemented");
DEBBI(debug_init_sag, ("Loading the sag solution from: ", fname));
FILE *sf = fopen(fname.c_str(), "rt");
if(!sf) return file_error(fname);
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();
create_viz();
}
void load_sag_solution_basic(const string& fname) {
if(!(state & SS_DATA)) throw hr_exception("load_sag_solution_basic with no data");
DEBBI(debug_init_sag, ("Loading the sag solution (basic) from: ", fname));
FILE *f = fopen(fname.c_str(), "rt");
if(!f) return file_error(fname);
for(auto& i: sagid) if(fscanf(f, "%d", &i) < 1) return file_format_error(fname);
fclose(f);
if(debug_init_sag) println(hlog, "loaded sagid = ", sagid);
prepare_graph();
create_viz();
}
void after_data() {
state |= SS_DATA;
init_snake_if_needed();
int DN = isize(vdata);
int SN = isize(sagcells);
if(SN < DN) {
println(hlog, "SN = ", SN, " DN = ", DN);
throw hr_exception("not enough cells for SAG");
}
sagid.resize(DN);
for(int i=0; i<DN; i++) sagid[i] = i;
prepare_graph();
create_viz();
}
/** load all the edges */
void read_weighted(const char *fname) {
if(state & SS_DATA) return;
DEBBI(debug_init_sag, ("Loading the weighted daga for sag from: ", fname));
state |= SS_WEIGHTED;
rogueviz::init(RV_GRAPH | RV_WHICHWEIGHT | RV_AUTO_MAXWEIGHT | RV_HAVE_WEIGHT);
init_cells();
maxweight = 0;
fhstream f(fname, "rt");
if(!f.f) return file_error(fname);
while(!feof(f.f)) {
string l1, l2;
while(true) {
int c = fgetc(f.f);
if(c == EOF) goto after;
else if(c == ';') break;
else if(rv_ignore(c)) ;
else l1 += c;
}
while(true) {
int c = fgetc(f.f);
if(c == EOF) goto after;
else if(c == ';') break;
else if(rv_ignore(c)) ;
else l2 += c;
}
ld wei;
if(!scan(f, wei)) continue;
addedge(getid(l1), getid(l2), wei, ensure_sag_edge());
}
after:
after_data();
}
void read_hubs(const string& fname) {
if(!(state & SS_DATA)) throw hr_exception("read_hubs with no data");
DEBBI(debug_init_sag, ("Loading the hub daga for sag from: ", fname));
hubval.resize(isize(vdata), -1);
fhstream f(fname, "rt");
if(!f.f) return file_error(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());
throw hr_exception("unknown label in read_hubs");
}
hubval[getid(l1)] = atoi(l2.c_str());
}
}
void generate_fake_data(int n, int m) {
if(state & SS_DATA) return;
DEBBI(debug_init_sag, ("Generating fake data ", tie(n, m)));
rogueviz::init(RV_GRAPH | RV_WHICHWEIGHT | RV_AUTO_MAXWEIGHT | RV_HAVE_WEIGHT);
init_cells();
state |= SS_WEIGHTED;
sagid.resize(n);
for(int i=0; i<n; i++) sagid[i] = i;
hrandom_shuffle(sagid);
if(m > n || m < 0) throw hr_exception("generate_fake_data parameters incorrect");
sagid.resize(m);
int DN = isize(sagid);
resize_vertices(DN);
for(int i=0; i<DN; i++)
vdata[i].name = its(i) + "@" + its(sagid[i]);
for(int i=0; i<DN; i++)
for(int j=i+1; j<DN; j++)
addedge(i, j, 1. / sagdist[sagid[i]][sagid[j]], ensure_sag_edge());
after_data();
for(int i=0; i<DN; i++) {
color_t col = ccolor::formula(sagcells[sagid[i]].first);
col <<= 8;
col |= 0xFF;
vdata[i].cp.color1 = col;
vdata[i].cp.color2 = 0;
vdata[i].cp.shade = 0;
}
}
/** Generate an unweighted graph, with edges determined by logistic. Prepare with -sagrt <R> <T> first */
void generate_unweighted(int DN) {
if(state & SS_DATA) return;
init_cells();
int N = isize(sagcells);
sagid.resize(DN);
for(int i=0; i<DN; i++) sagid[i] = hrand(N);
// todo : what if not allow_doubles?
resize_vertices(DN);
for(int i=0; i<DN; i++)
vdata[i].name = its(i) + "@" + its(sagid[i]);
vector<int> colors;
if(colorpartite) {
colors.resize(DN);
for(int i=0; i<DN; i++) colors[i] = hrand(2);
}
for(int i=0; i<DN; i++)
for(int j=i+1; j<DN; j++) {
if(colorpartite && colors[i] == colors[j]) continue;
ld d = sagdist[sagid[i]][sagid[j]];
ld prob = yes_for(d);
if(chance(prob)) addedge(i, j, 1, ensure_sag_edge());
}
create_viz();
for(int i=0; i<DN; i++) {
color_t col =
colorpartite
? rainbow_color(.5, colors[i] * 1./2)
: ccolor::formula(sagcells[sagid[i]].first);
col <<= 8;
col |= 0xFF;
vdata[i].cp.color1 = vdata[i].cp.color2 = col;
}
prepare_graph();
}
int data_read_args() {
#if CAP_COMMANDLINE
using namespace arg;
if(0) ;
else if(argis("-sagmin")) {
auto& ed = sag_edge ? *sag_edge : default_edgetype;
shift_arg_formula(ed.visible_from);
ed.visible_from_hi = ed.visible_from;
}
else if(argis("-sagminhi")) {
auto& ed = sag_edge ? *sag_edge : default_edgetype;
shift_arg_formula(ed.visible_from_hi);
}
else if(argis("-sag-edgepower")) {
shift_arg_formula(sag::edgepower);
shift_arg_formula(sag::edgemul);
}
else if(argis("-sag-weighted")) {
PHASE(3);
shift(); sag::read_weighted(argcs());
}
else if(argis("-sag-init")) {
PHASE(3);
init_cells();
after_data();
}
else if(argis("-sag-generate-unweighted")) {
PHASE(3);
shift(); sag::generate_unweighted(argi());
}
else if(argis("-saghubs")) {
PHASE(3);
shift_arg_formula(sag::hub_penalty);
shift(); sag::read_hubs(argcs());
}
else if(argis("-sag-generate")) {
PHASE(3);
shift(); int n = argi();
shift(); int m = argi();
sag::generate_fake_data(n, m);
}
// (3) load the initial positioning
else if(argis("-sag-load-sol")) {
PHASE(3); shift(); sag::load_sag_solution(args());
}
else if(argis("-sag-load-solution")) {
PHASE(3); shift(); sag::load_sag_solution_basic(args());
}
else if(argis("-sag-save-sol")) {
PHASE(3); shift(); sag::save_sag_solution(args());
}
else if(argis("-sag-fix")) {
shift(); int id = getid(args());
if(id >= isize(sagid)) throw hr_exception("bad id in -sag-fix");
fixed_position[id] = true;
}
else if(argis("-sag-move-to")) {
shift(); int sid1 = getid(args());
if(sid1 < 0 || sid1 >= isize(sagid)) throw hr_exception("bad id in -sag-move-to");
shift(); int t2 = argi();
if(t2 < 0 || t2 >= isize(sagnode)) throw hr_exception("bad id in -sag-move-to");
int sid2 = sagid[t2];
int t1 = allow_doubles ? -1 : sagnode[sid1];
sagnode[sid1] = t2; sagid[t2] = sid1;
if(sid2 >= 0) sagnode[sid2] = t1; sagid[t1] = sid2;
compute_cost();
create_viz();
}
else if(argis("-sag-colorpartite")) {
colorpartite = true;
if(state & SS_DATA) prepare_graph();
}
else return 1;
#endif
return 0;
}
int ahdata = addHook(hooks_args, 100, data_read_args);
}
}