1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2026-01-29 22:10:18 +00:00
Files
hyperrogue/rogueviz/embeddings/compare.cpp
2026-01-29 14:25:01 +01:00

349 lines
9.8 KiB
C++

#include "../som/kohonen.h"
namespace rogueviz {
namespace embeddings {
int count_directed_edges() {
int M = 0;
for(auto& v: directed_edges) M += isize(v);
return M;
}
void full_evaluation() {
println(hlog, "evaluating embedding: " + current->name());
try {
ld maxradius = 0;
for(int i=0; i<isize(rogueviz::vdata); i++) maxradius = max(maxradius, current->zero_distance(i));
println(hlog, "maximum radius = ", maxradius);
}
catch(hr_exception&) {}
bool symmetric = count_directed_edges() == 2 * isize(edgeinfos);
if(symmetric) full_routing();
continuous_ranks();
analyze_mdl_symmetric(symmetric);
}
struct abstract_embedding : embedding {
hyperpoint as_hyperpoint(int id) override { throw hr_exception("abstract embedding"); }
pair<cell*, hyperpoint> as_location(int id) override { throw hr_exception("abstract embedding"); }
};
/* convert a hyperbolic embedding into a landscape embedding */
using rogueviz::kohonen::kohvec;
using rogueviz::kohonen::columns;
int landscape_dim;
vector<kohvec> landscape_weights;
ld dist(const kohvec& a, const kohvec& b) {
ld res = 0;
for(int i=0; i<columns; i++) res += (a[i]-b[i]) * (a[i] - b[i]);
return res;
}
struct landscape_embedding : abstract_embedding {
virtual string name() override { return "landscape"; }
eGeometryClass get_geometry() override { return gcEuclid; }
int get_dimension() override { return isize(landscape_weights[0]); }
ld distance(int i, int j) override {
return dist(landscape_weights[i], landscape_weights[j]);
}
};
void landscape_build(int dim) {
rogueviz::embeddings::init_landscape(columns = landscape_dim = dim);
int N = isize(rogueviz::vdata);
landscape_weights.resize(N);
if(1) {
progressbar pb(N, "compute landscape");
for(int i=0; i<N; i++) {
landscape_weights[i] = rogueviz::embeddings::get_landscape_at(current->as_location(i).first);
pb++;
}
}
println(hlog, "delta size = ", isize(rogueviz::embeddings::delta_at));
enable_embedding(make_shared<landscape_embedding>());
}
void landscape_output(string shape, ld target_dist, string fname) {
ld max_dist = 0;
int N = isize(rogueviz::vdata);
for(int i=0; i<N; i++) {
ld tot = 0;
for(int d=0; d<columns; d++) tot += pow(landscape_weights[i][d], 2);
tot = sqrt(tot);
max_dist = max(max_dist, tot);
}
println(hlog, "max_dist = ", max_dist, " to ", target_dist);
fhstream f(fname, "w");
for(int i=0; i<N; i++) {
ld tot = 0;
println(f, rogueviz::vdata[i].name);
for(int d=0; d<columns; d++) {
auto w = landscape_weights[i][d] * target_dist / max_dist;
if(d) print(f, " ");
print(f, w);
tot += w * w;
}
if(shape == "euclid") println(f);
else if(shape == "poincare") println(f);
else if(shape == "lorentz") println(f, " ", sqrt(tot+1));
else if(shape == "horosphere") {
tot /= 2;
ld shift = -asinh(target_dist)/2;
println(f, " ", cosh(shift) * tot + sinh(shift) * (1+tot), " ", cosh(shift) * (1+tot) + sinh(shift) * tot);
}
}
}
/* read a distance table */
struct disttable_embedding : abstract_embedding {
vector<vector<ld>> disttable;
virtual string name() override { return "disttable"; }
eGeometryClass get_geometry() override { return gcHyperbolic; }
int get_dimension() override { return 1; }
ld zero_distance(int id) override { throw hr_exception("unknown"); }
ld distance(int i, int j) override { return disttable[i][j]; }
};
struct zdisttable_embedding : disttable_embedding {
vector<ld> dist0;
virtual string name() override { return "zdisttable"; }
ld zero_distance(int id) override { return dist0[id]; }
};
void read_disttable(string nodelist_fname, string disttable_fname) {
auto de = make_shared<disttable_embedding> ();
int N = isize(rogueviz::vdata);
fhstream g(nodelist_fname, "rt");
vector<int> our_index;
while(true) {
int i = rogueviz::readLabel(g);
if(i == -1) break;
our_index.push_back(i);
}
println(hlog, "read ", isize(our_index), " node labels from ", nodelist_fname);
if(isize(our_index) != N) { println(hlog, "wrong number of labels, ", N, " expected"); return; }
ld maxdist = 0;
de->disttable.resize(N);
for(int i=0; i<N; i++) de->disttable[i].resize(N, 0);
fhstream g1(disttable_fname, "rt");
println(hlog, "reading distance table from ", disttable_fname);
for(int i=0; i<N; i++)
for(int j=0; j<i; j++) {
ld dist = scan<ld>(g1);
maxdist = max(dist, maxdist);
de->disttable[our_index[i]][our_index[j]] = dist;
de->disttable[our_index[j]][our_index[i]] = dist;
}
println(hlog, "maximum distance = ", maxdist);
enable_embedding(de);
}
/* read a 3D Mercator embeding */
struct mercator_coord {
ld distance = -1;
array<ld, 3> h;
};
struct dmercator_embedding : abstract_embedding {
vector<mercator_coord> mcs;
virtual string name() override { return "dmercator"; }
eGeometryClass get_geometry() override { return gcHyperbolic; }
int get_dimension() override { return 3; }
hyperpoint as_hyperpoint(int id) {
auto& mc = mcs[id];
hyperpoint h;
for(int i=0; i<3; i++)
h[i] = sinh(mc.distance) * mc.h[i];
h[3] = cosh(mc.distance);
return h;
}
ld zero_distance(int id) override { return mcs[id].distance; }
ld distance(int a, int b) {
if(a == b) return ld(0);
ld da = mcs[a].distance;
ld db = mcs[b].distance;
ld cosphi = 0;
for(int i=0; i<3; i++) cosphi += mcs[a].h[i] * mcs[b].h[i];
ld co = sinh(da) * sinh(db) * (1 - cosphi);
ld v = cosh(da - db) + co;
if(v < 1) return ld(0);
return acosh(v);
};
};
void read_dmercator(string fname, bool only_read = false) {
auto me = make_shared<dmercator_embedding> ();
int N = isize(rogueviz::vdata);
me->mcs.resize(N);
fhstream g(fname, "rt");
while(true) {
string s = scan<string>(g);
if(s == "") break;
if(s == "#") { scanline_noblank(g); continue; }
int id = rogueviz::getid(s);
if(id == -1) { printf("got -1\n"); exit(1); }
ld kappa = scan<ld> (g); hr::ignore(kappa);
auto& mc = me->mcs[id];
mc.distance = scan<ld> (g);
for(int i=0; i<3; i++) mc.h[i] = scan<ld> (g);
ld radius = mc.h[0] * mc.h[0] + mc.h[1] * mc.h[1] + mc.h[2] * mc.h[2];
radius = sqrt(radius);
for(int i=0; i<3; i++) mc.h[i] /= radius;
}
for(auto& p: me->mcs) if(p.distance < 0) throw hr_exception("mercator read incorrectly");
enable_embedding(me);
}
/* read a high-dimensional Euclidean embeding */
struct euclid_embedding : zdisttable_embedding {
vector<vector<ld>> coords;
virtual string name() override { return "euclid"; }
eGeometryClass get_geometry() override { return gcEuclid; }
int get_dimension() override { return isize(coords[0]); }
};
void read_euclid(string fname, int dim) {
auto ee = make_shared<euclid_embedding> ();
int N = isize(rogueviz::vdata);
println(hlog, "Open file ", fname);
fhstream g(fname, "rt");
if(!g.f) return file_error(fname);
println(hlog, "Reading Euclid (dim = ", dim, ")...");
ee->coords.resize(N);
ee->dist0.resize(N);
while(true) {
int i = rogueviz::readLabel(g);
if(i == -1) break;
vector<ld> co(dim);
for(int i=0; i<dim; i++) co[i] = scan<ld>(g);
ld d = 0; for(int i=0; i<dim; i++) d += co[i] * co[i];
ee->dist0[i] = sqrt(d);
ee->coords[i] = std::move(co);
}
ee->disttable.resize(N);
for(int i=0; i<N; i++) ee->disttable[i].resize(N, 0);
for(int i=0; i<N; i++)
for(int j=0; j<=i; j++) {
ld d = 0; for(int k=0; k<dim; k++) d += pow(ee->coords[i][k]-ee->coords[j][k], 2);
ee->disttable[i][j] = ee->disttable[j][i] = sqrt(d);
}
enable_embedding(ee);
}
/* read a high-dimensional Poincare embeding */
struct poincare_embedding : zdisttable_embedding {
vector<vector<ld>> coords;
virtual string name() override { return "poincare"; }
eGeometryClass get_geometry() override { return gcHyperbolic; }
int get_dimension() override { return isize(coords[0])-1; }
};
void read_poincare(string fname, int dim) {
auto pe = make_shared<poincare_embedding>();
int N = isize(rogueviz::vdata);
println(hlog, "Open file ", fname);
fhstream g(fname, "rt");
if(!g.f) return file_error(fname);
println(hlog, "Reading Poincare (dim = ", dim, ")...");
pe->coords.resize(N);
pe->dist0.resize(N);
while(true) {
int i = rogueviz::readLabel(g);
if(i == -1) break;
vector<ld> co(dim+1);
for(int i=0; i<dim; i++) co[i] = scan<ld>(g);
ld d = 0; for(int i=0; i<dim; i++) d += co[i] * co[i];
hyperpoint h; h[0] = sqrt(d); h[1] = 0; auto h1 = perspective_to_space(h, 1);
for(int i=0; i<dim; i++) co[i] = co[i] * h1[0] / d; co[dim] = h1[2];
pe->dist0[i] = acos_auto_clamp(h1[2]);
pe->coords[i] = std::move(co);
}
pe->disttable.resize(N);
for(int i=0; i<N; i++) pe->disttable[i].resize(N, 0);
for(int i=0; i<N; i++)
for(int j=0; j<=i; j++) {
ld d = 0; for(int k=0; k<dim; k++) d += pow(pe->coords[i][k]-pe->coords[j][k], 2);
d -= pow(pe->coords[i][dim]-pe->coords[j][dim], 2);
pe->disttable[i][j] = pe->disttable[j][i] = acos_auto_clamp(d);
}
enable_embedding(pe);
}
int compare_args() {
using namespace arg;
if(argis("-fulleval")) {
full_evaluation();
}
else if(argis("-el-disttable")) {
shift(); string nodelist_fname = args();
shift(); string disttable_fname = args();
read_disttable(nodelist_fname, disttable_fname);
}
else if(argis("-el-dmercator")) {
shift(); read_dmercator(args());
}
else if(argis("-el-poincare")) {
shift(); string fname = args();
shift(); int dim = argi();
read_poincare(fname, dim);
}
else if(argis("-el-euclid")) {
shift(); string fname = args();
shift(); int dim = argi();
read_euclid(fname, dim);
}
else if(argis("-build-landscape")) {
shift(); landscape_build(argi());
}
else return 1;
return 0;
}
auto acmp = addHook(hooks_args, 50, compare_args);
}
}