field quotient discovery

This commit is contained in:
Zeno Rogue 2020-01-16 16:39:18 +01:00
parent a587a3c6a4
commit 5382038c4e
4 changed files with 203 additions and 19 deletions

View File

@ -11,6 +11,10 @@ namespace hr {
EX namespace fieldpattern { EX namespace fieldpattern {
int limitsq = 10;
int limitp = 10000;
int limitv = 100000;
#if HDR #if HDR
#define currfp fieldpattern::getcurrfp() #define currfp fieldpattern::getcurrfp()
@ -280,6 +284,7 @@ struct fpattern {
fpattern(int p) { fpattern(int p) {
force_hash = 0; force_hash = 0;
dis = nullptr;
if(!p) return; if(!p) return;
init(p); init(p);
} }
@ -299,8 +304,35 @@ struct fpattern {
void add1(const matrix& M, const transmatrix& Full); void add1(const matrix& M, const transmatrix& Full);
vector<matrix> generate_isometries3(); vector<matrix> generate_isometries3();
int solve3(); int solve3();
void generate_all3(); bool generate_all3();
// transmatrix full_R, full_P, full_X;
#if CAP_THREAD
struct discovery *dis;
#endif
}; };
#if CAP_THREAD
struct discovery {
fpattern experiment;
std::shared_ptr<std::thread> discoverer;
std::mutex lock, slock;
bool is_suspended;
bool stop_it;
map<unsigned, tuple<int, int, matrix, matrix, matrix, int> > hashes_found;
discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; }
void activate();
void suspend();
void check_suspend();
void schedule_destruction();
void discovered();
~discovery();
};
#endif
#endif #endif
bool fpattern::check_order(matrix M, int req) { bool fpattern::check_order(matrix M, int req) {
@ -365,7 +397,12 @@ vector<matrix> fpattern::generate_isometries3() {
for(T[2][1]=low; T[2][1]<Prime; T[2][1]++) for(T[2][1]=low; T[2][1]<Prime; T[2][1]++)
for(T[3][1]=low; T[3][1]<Prime; T[3][1]++) for(T[3][1]=low; T[3][1]<Prime; T[3][1]++)
if(colprod(1, 1) == 1) if(colprod(1, 1) == 1)
if(colprod(1, 0) == 0) if(colprod(1, 0) == 0) {
#if CAP_THREAD
if(dis) dis->check_suspend();
if(dis && dis->stop_it) return res;
#endif
for(T[0][2]=low; T[0][2]<Prime; T[0][2]++) for(T[0][2]=low; T[0][2]<Prime; T[0][2]++)
for(T[0][3]=low; T[0][3]<Prime; T[0][3]++) for(T[0][3]=low; T[0][3]<Prime; T[0][3]++)
if(rowcol(0, 0) == 1) if(rowcol(0, 0) == 1)
@ -394,6 +431,8 @@ vector<matrix> fpattern::generate_isometries3() {
if(rowcol(3, 1) == 0) if(rowcol(3, 1) == 0)
if(rowcol(3, 2) == 0) if(rowcol(3, 2) == 0)
res.push_back(T); res.push_back(T);
if(isize(res) > limitp) return res;
}
return res; return res;
} }
@ -426,7 +465,7 @@ unsigned fpattern::compute_hash() {
return hashv; return hashv;
} }
void fpattern::generate_all3() { bool fpattern::generate_all3() {
matrices.clear(); matrices.clear();
matcode.clear(); matcode.clear();
add1(Id); add1(Id);
@ -440,13 +479,15 @@ void fpattern::generate_all3() {
matrix E = mmul(matrices[i], P); matrix E = mmul(matrices[i], P);
if(!matcode.count(E)) if(!matcode.count(E))
for(int j=0; j<local_group; j++) add1(mmul(E, matrices[j])); for(int j=0; j<local_group; j++) add1(mmul(E, matrices[j]));
if(isize(matrices) >= limitv) { println(hlog, "limitv exceeded"); return false; }
} }
unsigned hashv = compute_hash(); unsigned hashv = compute_hash();
DEBB(DF_FIELD, ("all = ", isize(matrices), "/", local_group, " = ", isize(matrices) / local_group, " hash = ", hashv, " count = ", ++hash_found[hashv])); DEBB(DF_FIELD, ("all = ", isize(matrices), "/", local_group, " = ", isize(matrices) / local_group, " hash = ", hashv, " count = ", ++hash_found[hashv]));
return true;
} }
int fpattern::solve3() { int fpattern::solve3() {
if(!wsquare) return 0; reg3::generate_cellrotations();
reg3::construct_relations(); reg3::construct_relations();
DEBB(DF_FIELD, ("generating isometries for ", Field)); DEBB(DF_FIELD, ("generating isometries for ", Field));
@ -476,7 +517,11 @@ int fpattern::solve3() {
for(auto& xX: possible_X) for(auto& xX: possible_X)
for(auto& xP: possible_P) if(check_order(mmul(xP, xX), reg3::xp_order)) for(auto& xP: possible_P) if(check_order(mmul(xP, xX), reg3::xp_order))
for(auto& xR: possible_R) if(check_order(mmul(xR, xX), reg3::rx_order)) { // if(xR[0][0] == 1 && xR[0][1] == 0) { for(auto& xR: possible_R) if(check_order(mmul(xR, xX), reg3::rx_order)) { // if(xR[0][0] == 1 && xR[0][1] == 0)
#if CAP_THREAD
if(dis) dis->check_suspend();
if(dis && dis->stop_it) return 0;
#endif
auto by = [&] (char ch) -> matrix& { return ch == 'X' ? xX : ch == 'R' ? xR : xP; }; auto by = [&] (char ch) -> matrix& { return ch == 'X' ? xX : ch == 'R' ? xR : xP; };
for(int i=0; i<N; i++) { for(int i=0; i<N; i++) {
matrix ml = Id; matrix ml = Id;
@ -486,7 +531,10 @@ int fpattern::solve3() {
if(ml != mr) { fails[i]++; goto bad;} if(ml != mr) { fails[i]++; goto bad;}
} }
P = xP; R = xR; X = xX; P = xP; R = xR; X = xX;
generate_all3(); if(!generate_all3()) continue;
#if CAP_THREAD
if(dis) { dis->discovered(); continue; }
#endif
if(force_hash && compute_hash() != force_hash) continue; if(force_hash && compute_hash() != force_hash) continue;
cmb++; cmb++;
goto ok; goto ok;
@ -526,7 +574,7 @@ int fpattern::solve() {
} else wsquare = 0; } else wsquare = 0;
if(WDIM == 3) { if(WDIM == 3) {
if(dual == 0) { if(dual == 0 && (Prime <= limitsq || pw == 1)) {
int s = solve3(); int s = solve3();
if(s) return 0; if(s) return 0;
} }
@ -729,7 +777,6 @@ void fpattern::analyze() {
matrix M = matrices[i]; matrix M = matrices[i];
matrix M2 = mpow(M, N-1); matrix M2 = mpow(M, N-1);
inverses[i] = matcode[M2]; inverses[i] = matcode[M2];
println(hlog, mmul(M, M2));
} }
} }
@ -1164,9 +1211,67 @@ EX void field_from_current() {
gg.flags = go.flags | qANYQ | qFIELD | qBOUNDED; gg.flags = go.flags | qANYQ | qFIELD | qBOUNDED;
gg.g = go.g; gg.g = go.g;
gg.default_variation = go.default_variation; gg.default_variation = go.default_variation;
fieldpattern::quotient_field_changed = true; fieldpattern::quotient_field_changed = true;
} }
#if CAP_THREAD
EX map<string, discovery> discoveries;
void discovery::activate() {
if(!discoverer) {
discoverer = std::make_shared<std::thread> ( [this] {
for(int p=2; p<100; p++) {
experiment.Prime = p;
experiment.solve();
if(stop_it) break;
}
});
slock.lock();
}
if(is_suspended) {
is_suspended = false;
lock.unlock();
slock.unlock();
}
}
void discovery::discovered() {
slock.unlock();
lock.lock();
auto& e = experiment;
hashes_found[e.compute_hash()] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group);
lock.unlock();
slock.lock();
}
void discovery::suspend() { is_suspended = true; lock.lock(); slock.lock(); }
void discovery::check_suspend() { slock.unlock(); lock.lock(); lock.unlock(); slock.lock(); }
void discovery::schedule_destruction() { stop_it = true; }
discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); }
#endif
int hk =
#if CAP_THREAD
+ addHook(on_geometry_change, 100, [] { for(auto& d:discoveries) if(!d.second.is_suspended) d.second.suspend(); })
+ addHook(final_cleanup, 100, [] {
for(auto& d:discoveries) { d.second.schedule_destruction(); if(d.second.is_suspended) d.second.activate(); }
discoveries.clear();
})
#endif
+ addHook(hooks_args, 0, [] {
using namespace arg;
if(0) ;
else if(argis("-q3-limitsq")) { shift(); limitsq = argi(); }
else if(argis("-q3-limitp")) { shift(); limitp = argi(); }
else if(argis("-q3-limitv")) { shift(); limitv = argi(); }
else return 1;
return 0;
});
EX purehookset on_geometry_change;
EX } EX }
#define currfp fieldpattern::getcurrfp() #define currfp fieldpattern::getcurrfp()

View File

@ -404,6 +404,65 @@ EX string dim_name() {
return " (" + its(WDIM) + "D)"; return " (" + its(WDIM) + "D)";
} }
#if CAP_THREAD
EX void showQuotientConfig3() {
using namespace fieldpattern;
gamescreen(2);
dialog::init(XLAT("field quotient"));
auto& ds = discoveries[cginf.tiling_name];
if(!ds.discoverer) {
dialog::addItem("start discovery", 's');
dialog::add_action([&ds] { ds.activate(); });
}
else if(ds.is_suspended) {
dialog::addItem("resume discovery", 's');
dialog::add_action([&ds] { ds.activate(); });
}
else {
dialog::addItem("suspend discovery", 's');
dialog::add_action([&ds] { ds.suspend(); });
}
auto& e = ds.experiment;
if(!e.Prime)
dialog::addBreak(100);
else {
string s = its(e.Prime);
if(e.wsquare) s += "²";
dialog::addInfo(s);
}
dialog::addBreak(100);
if(!ds.is_suspended) ds.lock.lock();
auto&l = ds.hashes_found;
for(auto& v: l) {
char x = 'a';
string s = XLAT("#%1, cells: %2", itsh(v.first), its(get<5>(v.second)));
dialog::addItem(s, x++);
dialog::add_action([&v] {
stop_game();
int tmp;
tie(currfp.Prime, currfp.wsquare, currfp.R, currfp.P, currfp.X, tmp) = v.second;
currfp.Field = currfp.wsquare ? currfp.Prime * currfp.Prime : currfp.Prime;
currfp.generate_all3();
currfp.analyze();
start_game();
});
}
if(!ds.is_suspended) ds.lock.unlock();
dialog::addBreak(100);
dialog::addBack();
dialog::display();
}
#endif
EX string geometry_name() { EX string geometry_name() {
switch(ginf[geometry].cclass) { switch(ginf[geometry].cclass) {
case gcHyperbolic: case gcHyperbolic:
@ -459,12 +518,17 @@ EX void select_quotient_screen() {
g == geometry, key++); g == geometry, key++);
dialog::add_action([g] { dialog::add_action([g] {
if(g == gFieldQuotient && WDIM == 3) { if(g == gFieldQuotient && WDIM == 3) {
stop_game(); if(geometry != gFieldQuotient) {
fieldpattern::field_from_current(); stop_game();
set_geometry(gFieldQuotient); fieldpattern::field_from_current();
for(int p=2;; p++) { currfp.Prime = p; currfp.force_hash = 0; if(!currfp.solve()) break; } set_geometry(gFieldQuotient);
println(hlog, "set prime = ", currfp.Prime); for(int p=2;; p++) { currfp.Prime = p; currfp.force_hash = 0; if(!currfp.solve()) break; }
start_game(); println(hlog, "set prime = ", currfp.Prime);
start_game();
}
#if CAP_THREAD
pushScreen(showQuotientConfig3);
#endif
} }
else if(g == gFieldQuotient) else if(g == gFieldQuotient)
pushScreen(showQuotientConfig); pushScreen(showQuotientConfig);
@ -503,12 +567,12 @@ EX void select_quotient() {
println(hlog, "choices = ", choices); println(hlog, "choices = ", choices);
if(isize(choices) > 2) if(isize(choices) > 1)
pushScreen(select_quotient_screen); pushScreen(select_quotient_screen);
else if(isize(choices) > 1) { /* else if(isize(choices) > 1) {
set_geometry(choices[choices[0] == geometry ? 1 : 0]); set_geometry(choices[choices[0] == geometry ? 1 : 0]);
start_game(); start_game();
} } */
else else
addMessage("No quotient spaces avialable in the current tiling."); addMessage("No quotient spaces avialable in the current tiling.");
} }

View File

@ -73,6 +73,10 @@
#define CAP_XGD (ISANDROID || ISFAKEMOBILE) #define CAP_XGD (ISANDROID || ISFAKEMOBILE)
#endif #endif
#ifndef CAP_THREAD
#define CAP_THREAD (!ISMOBILE && !ISWEB)
#endif
#define CAP_FRAMELIMIT (!ISMOBWEB) #define CAP_FRAMELIMIT (!ISMOBWEB)
#if ISMOBILE #if ISMOBILE
@ -421,6 +425,11 @@ extern "C" {
#include <complex> #include <complex>
#include <new> #include <new>
#if CAP_THREAD
#include <thread>
#include <mutex>
#endif
#ifdef USE_UNORDERED_MAP #ifdef USE_UNORDERED_MAP
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>

View File

@ -1201,9 +1201,12 @@ eModel default_model() {
if(GDIM == 3) return mdPerspective; if(GDIM == 3) return mdPerspective;
return mdDisk; return mdDisk;
} }
EX purehookset on_geometry_change;
EX void set_geometry(eGeometry target) { EX void set_geometry(eGeometry target) {
bool was_default = pmodel == default_model(); bool was_default = pmodel == default_model();
callhooks(on_geometry_change);
if(geometry != target) { if(geometry != target) {
int old_DIM = GDIM; int old_DIM = GDIM;
stop_game(); stop_game();
@ -1492,6 +1495,8 @@ EX void initAll() {
polygonal::solve(); polygonal::solve();
} }
EX purehookset final_cleanup;
EX void finishAll() { EX void finishAll() {
achievement_final(!items[itOrbSafety]); achievement_final(!items[itOrbSafety]);
@ -1505,6 +1510,7 @@ EX void finishAll() {
#endif #endif
achievement_close(); achievement_close();
callhooks(final_cleanup);
} }