#include #include #include #include #include #include #include #include #include #include #include #include #include "cachepad.hpp" #include "mmalloc.hpp" #include "timer.hpp" using namespace std; using boost::optional; #ifndef BIT_WIDTH #define BIT_WIDTH 16 #endif const uint8_t logState = BIT_WIDTH; typedef typename boost::uint_t::least State; typedef typename boost::uint_t::least StateIter; const StateIter numState = (StateIter) 1 << logState; const StateIter nullState = ~((StateIter) 0); const State maxState = ~((State) 0); bitset<8> rule(110); State update(State s) { State r(0); bitset b(s); for (unsigned i=0; i Trans; typedef array pbitset; bool iterStateP(function f, optional msg = optional(), bool parallel = false, bool skipWorkTest = false) { PerfPrinter perfPrinter(msg); int numThreads = 1; if (parallel) numThreads = min(thread::hardware_concurrency(), numState); cache_pad *perThreadWorked = new cache_pad[numThreads]; list tasks; for (int t=0; tjoin(), delete tasks.front(), tasks.pop_front()); bool worked = skipWorkTest; for (int t=0; t f, optional msg = optional(), bool parallel = false) { return iterStateP([=](State s, bool &) { f(s); }, msg, parallel, true); } void iterTransP(int times, function f, optional msg = optional(), bool parallel = false, bool skipWorkTest = false) { PerfPrinter perfPrinter(msg); auto msg2 = [=,&msg] (int i) { return msg ? (*msg + string(" ") + to_string(times-i) + string("/") + to_string(times)) : msg; }; while (times--) if (not iterStateP(f, msg2(times), parallel, skipWorkTest)) return; } void iterTrans(int times, function f, optional msg = optional(), bool parallel = false) { iterTransP(times, [=](State s, bool &) { f(s); }, msg, parallel, true); } void init(Trans &t, Trans &c, pbitset &reachable) { iterState([&](State s) { t[s] = update(s); c[s] = s; reachable[t[s]] = 1; }, (string) "single step transition table", true); } void findCycle(Trans &t, Trans &c) { // forward to t=numState; now every state is in a cycle iterTransP(logState, [&](State s, bool &worked) { State n = t[s]; t[s] = t[n]; c[s] = min(c[s], c[n]); if (n != t[s]) worked = true; }, (string) "fwd time", true); // Transients may have a cycle id (minimum state) that is on the // transient (not in the cycle) and thus different from the cycle id // in the cycle. Thus we copy the cycle id from a state in the cycle // to all states in the basin. iterState([&](State s) { c[s] = c[t[s]]; }, (string) "fix transient cycle id", true); } State canonize(State s) { // TODO: test if a variant base on clz is faster State cs = s; for (int i=0; i(cs, (((s<>(logState-i))) & maxState)); return cs; }; void cycleStat(Trans &t, Trans &c, pbitset &reachable) { struct Stat { StateIter basin, len, eden, totalBasin; explicit Stat() : basin(0), len(0), eden(0), totalBasin(0) {} }; unordered_map cycStat; unordered_set redCycleCounted; // Is this cycle the canonical one? // How big is the basin of attraction? // How many garden of eden states does it contain? { PerfPrinter perfPrinter((string) "cycle count"); iterState([&](State s) { auto cyc = c[s]; auto canonCyc = canonize(cyc); auto &stat = cycStat[canonCyc]; stat.totalBasin++; if (cyc == canonCyc) { stat.basin++; if (!reachable[s]) stat.eden++; } }, (string) "basin & eden"); } // how long is the cycle, what is the actual minimal state { PerfPrinter perfPrinter((string) "cycle length"); for (auto &i : cycStat) { Stat &stat = i.second; State cur, start; cur = start = t[i.first]; do { stat.len++; cur = update(cur); } while (cur != start); // TODO: parallelize loop }} // print it for (auto &i : cycStat) { auto &s = i.second; assert(s.totalBasin % s.basin == 0); cout << bitset(i.first) << "\t" << (s.totalBasin / s.basin) << "\t" << s.len << "\t" << s.basin << "\t" << s.eden << "\t" << i.first << endl; } } void print(Trans &t) { for (auto s : t) cout << bitset(s) << endl; } void printTraj(State s, int count) { while (count--) { cout << bitset(s) << endl; s = update(s); } } int main(int argc, char **argv) { assert(argc >= 2); rule = atoi(argv[1]); if (!strcmp(argv[2], "traj")) { assert(argc == 5); printTraj(atoi(argv[3]), atoi(argv[4])); } if (!strcmp(argv[2], "cycle")) { Trans *t, *c; pbitset *r; { PerfPrinter perfPrinter((string) "allocating memory"); t = mmalloc(); c = mmalloc(); r = mmalloc(); } init(*t, *c, *r); findCycle(*t, *c); cycleStat(*t, *c, *r); } return 0; }