#include #include #include #include #include #include #include #include "pcg-cpp/include/pcg_random.hpp" #include "randutils/randutils.hpp" using Rng = randutils::random_generator; template using Vector = Eigen::Matrix; template using Matrix = Eigen::Matrix; const unsigned Empty = std::numeric_limits::max(); int iPow(int x, unsigned p) { if (p == 0) return 1; if (p == 1) return x; int tmp = iPow(x, p / 2); if (p % 2 == 0) return tmp * tmp; else return x * tmp * tmp; } unsigned mod(signed a, unsigned b) { return ((a < 0) ? (a + (1 - a / (signed)b) * b) : a) % b; } template Vector mod(const Eigen::MatrixBase& v, unsigned b) { Vector u; for (unsigned i = 0; i < D; i++) { u(i) = mod(v(i), b); } return u; } template void one_sequences(std::list>& sequences, unsigned level) { if (level > 0) { unsigned new_level = level - 1; for (std::array& sequence : sequences) { std::array new_sequence = sequence; new_sequence[new_level] = -1; sequences.push_front(new_sequence); } one_sequences(sequences, new_level); } } template std::vector> generateTorusMatrices() { std::vector> mats; std::array ini_sequence; ini_sequence.fill(1); std::list> sequences; sequences.push_back(ini_sequence); one_sequences(sequences, D); sequences.pop_back(); // don't want the identity matrix! for (std::array sequence : sequences) { Matrix m; for (unsigned i = 0; i < D; i++) { for (unsigned j = 0; j < D; j++) { if (i == j) { m(i, j) = sequence[i]; } else { m(i, j) = 0; } } } mats.push_back(m); } for (unsigned i = 0; i < D; i++) { for (unsigned j = 0; j < D; j++) { if (i != j) { Matrix m; for (unsigned k = 0; k < D; k++) { for (unsigned l = 0; l < D; l++) { if ((k == i && l == j) || (k == j && l == i)) { if (i < j) { m(k, l) = 1; } else { m(k, l) = -1; } } else if (k == l && (k != i && k != j)) { m(k, l) = 1; } else { m(k, l) = 0; } } } mats.push_back(m); } } } return mats; } template class Transformation { public: unsigned L; Matrix m; Vector v; Transformation(unsigned L) : L(L) { m.setIdentity(); v.setZero(); } Transformation(unsigned L, const Matrix& m, const Vector& v) : L(L), m(m), v(v) {} Transformation(unsigned L, const std::vector>& ms, Rng& r) : m(r.pick(ms)), L(L) { for (unsigned i = 0; i < D; i++) { v[i] = r.uniform((unsigned)0, L - 1); } v = v - m * v; } Transformation inverse() const { return Transformation(L, m.transpose(), -m.transpose() * v); } Vector apply(const Vector& x) const { return mod(v + m * x, L); } Transformation apply(const Transformation& t) const { Transformation tNew(L); tNew.m = m * t.m; tNew.v = apply(t.v); return tNew; } }; template class Vertex { public: Vector position; std::vector>> neighbors; unsigned occupiedNeighbors; unsigned maximumNeighbors; bool marked; Vertex() { occupiedNeighbors = 0; maximumNeighbors = Empty; marked = false; } bool empty() const { return maximumNeighbors == Empty; } bool frustrated() const { return occupiedNeighbors > maximumNeighbors; } bool valid() const { unsigned o = 0; for (const Vertex& v : neighbors) { if (!v.empty()) { o++; } } return o == occupiedNeighbors; } }; template class System { public: const unsigned L; std::vector N; std::vector> vertices; Transformation orientation; unsigned size() const { return vertices.size(); } unsigned types() const { return N.size() - 1; } unsigned occupancy() const { return size() - N[0]; } double density() const { return (double)occupancy() / size(); } bool compatible() const { for (const Vertex& v : vertices) { if (v.frustrated()) { return false; } } return true; } bool valid() const { for (const Vertex& v : vertices) { if (!v.valid()) { return false; } } return true; } unsigned vectorToIndex(const Vector& x) const { unsigned i = 0; for (unsigned d = 0; d < D; d++) { i += x[d] * iPow(L, d); } return i; } Vector indexToVector(unsigned i) const { Vector x; for (unsigned d = 0; d < D; d++) { x[d] = (i / iPow(L, d)) % L; } return x; } System(unsigned L, unsigned T) : L(L), N(T + 1, 0), vertices(iPow(L, D)), orientation(L) { N[0] = size(); for (unsigned i = 0; i < size(); i++) { vertices[i].position = indexToVector(i); vertices[i].neighbors.reserve(2 * D); } for (unsigned d = 0; d < D; d++) { for (signed i = 0; i < size(); i++) { unsigned j = iPow(L, d + 1) * (i / iPow(L, d + 1)) + mod(i + iPow(L, d), pow(L, d + 1)); vertices[i].neighbors.push_back(vertices[j]); vertices[j].neighbors.push_back(vertices[i]); } } } std::list>> overlaps(Vertex& v) { std::list>> o; if (v.empty()) { // an empty site cannot be frustrating anyone return o; } bool selfFrustrated = v.frustrated(); bool anyNeighborFrustrated = false; for (Vertex& vn : v.neighbors) { if (!vn.empty()) { bool thisNeighborFrustrated = vn.frustrated(); if (thisNeighborFrustrated) { anyNeighborFrustrated = true; } if (selfFrustrated || thisNeighborFrustrated) { o.push_back(vn); } } } if (selfFrustrated || anyNeighborFrustrated) { o.push_back(v); } return o; } bool canReplaceWith(const Vertex& v, unsigned t) const { if (t == Empty) { return true; } if (t < v.occupiedNeighbors) { return false; } if (v.empty()) { for (const Vertex& vn : v.neighbors) { if (vn.maximumNeighbors < vn.occupiedNeighbors + 1) { return false; } } } return true; } bool insert(Vertex& v, unsigned t, bool force = false) { if (force || (v.empty() && canReplaceWith(v, t))) { v.maximumNeighbors = t; for (Vertex& vn : v.neighbors) { vn.occupiedNeighbors++; } N[t]++; N[0]--; return true; } else { return false; } } bool remove(Vertex& v) { if (v.empty()) { return false; } else { N[v.maximumNeighbors]--; N[0]++; v.maximumNeighbors = Empty; for (Vertex& vn : v.neighbors) { vn.occupiedNeighbors--; } return true; } } bool randomLocalExchange(Rng& r) { Vertex& v = r.pick(vertices); return exchange(v, r.pick(v.neighbors)); } void swap(Vertex& v1, Vertex& v2) { if (v1.maximumNeighbors != v2.maximumNeighbors) { if (!v1.empty() && !v2.empty()) { std::swap(v1.maximumNeighbors, v2.maximumNeighbors); } else if (v1.empty()) { unsigned t = v2.maximumNeighbors; remove(v2); insert(v1, t, true); } else { unsigned t = v1.maximumNeighbors; remove(v1); insert(v2, t, true); } } } bool exchange(Vertex& v1, Vertex& v2) { if (canReplaceWith(v1, v2.maximumNeighbors) && canReplaceWith(v2, v1.maximumNeighbors)) { swap(v1, v2); return true; } else { return false; } } int overlap(const System& s) const { int o = 0; for (unsigned i = 0; i < size(); i++) { unsigned t2 = s.vertices[vectorToIndex(orientation.inverse().apply(indexToVector(i)))].maximumNeighbors; if (t2 == vertices[i].maximumNeighbors) { o++; } else { o--; } } return o; } bool randomExchange(Rng& r) { Vertex& v1 = r.pick(vertices); Vertex& v2 = r.pick(vertices); return exchange(v1, v2); } void sweepGrandCanonical(double z, Rng& r) { for (unsigned i = 0; i < size(); i++) { if (0.5 < r.uniform(0.0, 1.0)) { double pIns = size() * z / (occupancy() + 1); if (pIns > r.uniform(0.0, 1.0)) { while (true) { Vertex& v = r.pick(vertices); if (v.empty()) { insert(v, r.pick({1, 2, 2, 2, 2, 2, 3, 3, 3, 3})); break; } } } } else { double pDel = density() / z; if (pDel > r.uniform(0.0, 1.0)) { remove(r.pick(vertices)); } } randomLocalExchange(r); } } void sweepLocal(Rng& r) { for (unsigned i = 0; i < size(); i++) { randomLocalExchange(r); } } void sweepSwap(Rng& r) { for (unsigned i = 0; i < size(); i++) { randomExchange(r); } } unsigned flipCluster(const Transformation& R, Vertex& v0) { unsigned n = 0; Vertex& v0New = vertices[vectorToIndex(R.apply(v0.position))]; if (&v0New != &v0) { std::queue>> q; v0.marked = true; v0New.marked = true; swap(v0, v0New); for (Vertex& vn : overlaps(v0)) { if (!vn.marked) { q.push(vn); } } for (Vertex& vn : overlaps(v0New)) { if (!vn.marked) { q.push(vn); } } while (!q.empty()) { Vertex& v = q.front(); q.pop(); if (!v.marked && !overlaps(v).empty()) { v.marked = true; Vertex& vNew = vertices[vectorToIndex(R.apply(v.position))]; if (&vNew != &v) { vNew.marked = true; swap(v, vNew); for (Vertex& vn : overlaps(v)) { if (!vn.marked) { q.push(vn); } } for (Vertex& vn : overlaps(vNew)) { if (!vn.marked) { q.push(vn); } } n += 2; } else { n += 1; } } // For some reason, the process above misses frustrated states. This is an inelegant stopgap. if (q.empty()) { for (Vertex& vv : vertices) { if (!vv.marked && !overlaps(vv).empty()) { q.push(vv); } } } } } else { n = 1; } if (n > occupancy() / 2) { orientation = R.apply(orientation); } for (Vertex& v : vertices) { v.marked = false; } return n; } }; void print(const System<2>& s) { for (const Vertex<2>& v : s.vertices) { if (!v.empty()) { std::cerr << v.maximumNeighbors; } else { std::cerr << " "; } if (v.position(0) == s.L - 1) { std::cerr << std::endl; } } } int main() { const unsigned D = 3; unsigned L = 15; unsigned Nmin = 2e2; unsigned Nmax = 2e5; double Tmin = 0.04; double Tmax = 0.2; double δT = 0.02; System s(L, 3); Rng r; double z = exp(1 / 0.15); if (!s.compatible()) { std::cerr << "Storted incompatible!" << std::endl; return 1; } while (s.density() < 0.58) { s.sweepGrandCanonical(z, r); } if (!s.compatible()) { std::cerr << "Not compatible!" << std::endl; return 1; } std::cerr << "Found state with appropriate density." << std::endl; System s0 = s; std::vector> ms = generateTorusMatrices(); std::vector clusterDist(s.size() + 1); unsigned n = 1; unsigned i = 0; double nC = 0; while (nC / s.size() < 1e5) { if (n < 20 * log(i + 1)) { n++; std::cout << nC / s.size() << " " << (double)s.overlap(s0) / s.size() << std::endl; } unsigned nn = s.flipCluster(Transformation(L, ms, r), r.pick(s.vertices)); nC += nn; clusterDist[nn]++; /* s.sweepLocal(r); nC += s.size(); s.sweepSwap(r); */ i++; } if (!s.compatible()) { std::cerr << "Not compatible!" << std::endl; return 1; } if (!s.valid()) { std::cerr << "Not valid!" << std::endl; return 1; } std::ofstream file("dist.dat"); for (unsigned i : clusterDist) { file << i << " "; } file.close(); return 0; }