summaryrefslogtreecommitdiff
path: root/lib/wolff.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/wolff.hpp')
-rw-r--r--lib/wolff.hpp320
1 files changed, 320 insertions, 0 deletions
diff --git a/lib/wolff.hpp b/lib/wolff.hpp
new file mode 100644
index 0000000..d21d788
--- /dev/null
+++ b/lib/wolff.hpp
@@ -0,0 +1,320 @@
+
+#ifndef WOLFF_H
+#define WOLFF_H
+
+#include <functional>
+#include <vector>
+#include <random>
+#include <cmath>
+#include <iterator>
+#include <list>
+#include <tuple>
+#include <queue>
+
+namespace wolff{
+
+ template <class vertex_prop = std::tuple<>, class edge_prop = std::tuple<>>
+ class graph {
+ /* the graph class describes a lattice on which wolff is run. for most
+ * purposes, using the default square lattice constructor is sufficient,
+ * but arbitrary lattices can be constructed
+ */
+ public:
+ unsigned D; // dimension of space
+ unsigned L; // linear size
+ unsigned ne; // number of edges
+ unsigned nv; // number of vertices
+
+ struct _vertex;
+
+ typedef struct _halfedge {
+ struct _vertex &self; // reference to the vertex this edge comes from
+ struct _vertex &neighbor; // reference to the vertex this edge goes to
+ edge_prop prop; // optional properties
+
+ _halfedge(struct _vertex &v1, struct _vertex &v2) : self(v1), neighbor(v2) {};
+ } halfedge;
+
+ typedef struct _vertex {
+ unsigned ind; // index of the vertex
+ std::list<halfedge> edges; // list of edges incident on this vertex
+ vertex_prop prop; // optional properties
+ } vertex;
+
+ std::vector<vertex> vertices; /* vector of vertices, length nv, with
+ * vertices[i].ind = i for all 0 <= i < nv
+ */
+ graph() {
+ // default constructor for empty graph. use it to build your own!
+ D = 0;
+ L = 0;
+ nv = 0;
+ ne = 0;
+ };
+
+ graph(unsigned D, unsigned L) : D(D), L(L) {
+ // default constructor for square lattice graph
+ nv = pow(L, D);
+ ne = D * nv;
+
+ vertices.resize(nv);
+
+ for (unsigned i = 0; i < nv; i++) {
+ vertices[i].ind = i;
+ for (unsigned j = 0; j < D; j++) {
+ unsigned n1 = pow(L, j + 1) * (i / ((unsigned)pow(L, j + 1))) +
+ fmod(i + pow(L, j), pow(L, j + 1));
+ unsigned n2 = pow(L, j + 1) * (i / ((unsigned)pow(L, j + 1))) +
+ fmod(pow(L, j + 1) + i - pow(L, j), pow(L, j + 1));
+
+ halfedge f(vertices[i], vertices[n1]);
+ halfedge b(vertices[i], vertices[n2]);
+
+ vertices[i].edges.push_back(f);
+ vertices[i].edges.push_back(b);
+ }
+ }
+ };
+
+ void add_ghost() {
+ // adds a ghost site to any graph
+ vertices.resize(nv + 1);
+
+ for (auto it = vertices.begin(); it != std::prev(vertices.end()); it++) {
+ halfedge e1(*it, vertices[nv]);
+ it->edges.push_back(e1);
+
+ halfedge e2(vertices[nv], *it);
+ vertices[nv].edges.push_back(e2);
+ }
+
+ vertices[nv].ind = nv;
+
+ ne += nv;
+ nv++;
+ };
+ };
+
+ template <class R_t, class X_t, class G_t = graph<>>
+ class measurement;
+
+ template <class R_t, class X_t, class G_t = graph<>>
+ class system {
+ public:
+ unsigned nv; // number of vertices
+ unsigned ne; // number of edges
+ G_t G; // the graph defining the lattice with ghost
+ double T; // the temperature
+ std::vector<X_t> s; // the state of the ordinary spins
+
+#ifdef WOLFF_BOND_DEPENDENCE
+ std::function <double(const typename G_t::halfedge&, const X_t&, const X_t&)> Z; // coupling between sites
+#else
+ std::function <double(const X_t&, const X_t&)> Z; // coupling between sites
+#endif
+
+#ifndef WOLFF_NO_FIELD
+ R_t s0; // the current state of the ghost site
+#ifdef WOLFF_SITE_DEPENDENCE
+ std::function <double(const typename G_t::vertex&, const X_t&)> B; // coupling with the external field
+#else
+ std::function <double(const X_t&)> B; // coupling with the external field
+#endif
+#endif
+
+#ifdef WOLFF_USE_FINITE_STATES
+ std::array<std::array<std::array<double, WOLFF_FINITE_STATES_N>, WOLFF_FINITE_STATES_N>, WOLFF_FINITE_STATES_N> Zp;
+#ifndef WOLFF_NO_FIELD
+ std::array<std::array<double, WOLFF_FINITE_STATES_N>, WOLFF_FINITE_STATES_N> Bp;
+#endif
+#endif
+
+ system(G_t g, double T,
+#ifdef WOLFF_BOND_DEPENDENCE
+ std::function <double(const typename G_t::halfedge&, const X_t&, const X_t&)> Z
+#else
+ std::function <double(const X_t&, const X_t&)> Z
+#endif
+#ifndef WOLFF_NO_FIELD
+#ifdef WOLFF_SITE_DEPENDENCE
+ , std::function <double(const typename G_t::vertex&, const X_t&)> B
+#else
+ , std::function <double(const X_t&)> B
+#endif
+#endif
+ ) : G(g), T(T), Z(Z)
+#ifndef WOLFF_NO_FIELD
+ , s0(), B(B)
+#endif
+ {
+ nv = G.nv;
+ ne = G.ne;
+ s.resize(nv);
+#ifndef WOLFF_NO_FIELD
+ G.add_ghost();
+#endif
+#ifdef WOLFF_USE_FINITE_STATES
+ this->finite_states_init();
+#endif
+ }
+
+ void flip_cluster(typename G_t::vertex& v0, const R_t& r, std::mt19937& rng,
+ measurement<R_t, X_t, G_t>& A) {
+ std::uniform_real_distribution<double> dist(0.0, 1.0);
+
+ std::queue<unsigned> queue;
+ queue.push(v0.ind);
+
+ std::vector<bool> visited(G.nv, false);
+
+ while (!queue.empty()) {
+ unsigned i = queue.front();
+ queue.pop();
+
+ if (!visited[i]) { // don't reprocess anyone we've already visited!
+ visited[i] = true;
+
+ X_t si_new;
+#ifndef WOLFF_NO_FIELD
+ R_t s0_new;
+
+ bool we_are_ghost = (i == nv);
+
+ if (we_are_ghost) {
+ s0_new = r.act(s0);
+ } else
+#endif
+ {
+ si_new = r.act(s[i]);
+ }
+
+ for (const typename G_t::halfedge &e : G.vertices[i].edges) {
+ double dE, p;
+ unsigned j = e.neighbor.ind;
+
+#ifndef WOLFF_NO_FIELD
+ bool neighbor_is_ghost = (j == nv);
+
+ if (we_are_ghost || neighbor_is_ghost) {
+ X_t s0s_old, s0s_new;
+ unsigned non_ghost;
+
+ if (neighbor_is_ghost) {
+ non_ghost = i;
+ s0s_old = s0.act_inverse(s[i]);
+ s0s_new = s0.act_inverse(si_new);
+ } else {
+ non_ghost = j;
+ s0s_old = s0.act_inverse(s[j]);
+ s0s_new = s0_new.act_inverse(s[j]);
+ }
+
+#ifdef WOLFF_SITE_DEPENDENCE
+ dE = B(G.vertices[non_ghost], s0s_old) - B(G.vertices[non_ghost], s0s_new);
+#else
+ dE = B(s0s_old) - B(s0s_new);
+#endif
+
+#ifdef WOLFF_USE_FINITE_STATES
+ p = Bp[s0s_old.enumerate()][s0s_new.enumerate()];
+#endif
+
+ // run measurement hooks for encountering a ghost bond
+ A.ghost_bond_visited(*this, G.vertices[non_ghost], s0s_old, s0s_new, dE);
+ } else // this is a perfectly normal bond!
+#endif
+ {
+#ifdef WOLFF_BOND_DEPENDENCE
+ dE = Z(e, s[i], s[j]) - Z(e, si_new, s[j]);
+#else
+ dE = Z(s[i], s[j]) - Z(si_new, s[j]);
+#endif
+
+#ifdef WOLFF_USE_FINITE_STATES
+ p = Zp[s[i].enumerate()][si_new.enumerate()][s[j].enumerate()];
+#endif
+
+ // run measurement hooks for encountering a plain bond
+ A.plain_bond_visited(*this, e, si_new, dE);
+ }
+
+#ifndef WOLFF_USE_FINITE_STATES
+ p = 1.0 - exp(-dE / T);
+#endif
+
+ if (dist(rng) < p) {
+ queue.push(j); // push the neighboring vertex to the queue
+ }
+ }
+
+#ifndef WOLFF_NO_FIELD
+ if (we_are_ghost) {
+ A.ghost_site_transformed(*this, s0_new);
+ s0 = s0_new;
+ } else
+#endif
+ {
+ A.plain_site_transformed(*this, G.vertices[i], si_new);
+ s[i] = si_new;
+ }
+ }
+ }
+ }
+
+ void run_wolff(unsigned N,
+ std::function <R_t(std::mt19937&, const system<R_t, X_t, G_t>&, const typename G_t::vertex&)> r_gen, measurement<R_t, X_t, G_t>& A, std::mt19937& rng) {
+ std::uniform_int_distribution<unsigned> dist(0, nv - 1);
+
+ for (unsigned n = 0; n < N; n++) {
+ unsigned i0 = dist(rng);
+ R_t r = r_gen(rng, *this, G.vertices[i0]);
+
+ A.pre_cluster(n, N, *this, G.vertices[i0], r);
+
+ this->flip_cluster(G.vertices[i0], r, rng, A);
+
+ A.post_cluster(n, N, *this);
+ }
+ }
+
+#ifdef WOLFF_USE_FINITE_STATES
+ void finite_states_init() {
+#ifndef WOLFF_NO_FIELD
+ for (unsigned i = 0; i < WOLFF_FINITE_STATES_N; i++) {
+ for (unsigned j = 0; j < WOLFF_FINITE_STATES_N; j++) {
+ Bp[i][j] = 1.0 - exp(-(B(X_t(i)) - B(X_t(j))) / T);
+ }
+ }
+#endif
+ for (unsigned i = 0; i < WOLFF_FINITE_STATES_N; i++) {
+ for (unsigned j = 0; j < WOLFF_FINITE_STATES_N; j++) {
+ for (unsigned k = 0; k < WOLFF_FINITE_STATES_N; k++) {
+ Zp[i][j][k] = 1.0 - exp(-(Z(X_t(i), X_t(k)) - Z(X_t(j), X_t(k))) / T);
+ }
+ }
+ }
+ }
+#endif
+
+ };
+
+ template <class R_t, class X_t, class G_t>
+ class measurement {
+ public:
+ virtual void pre_cluster(unsigned, unsigned, const system<R_t, X_t, G_t>&, const typename G_t::vertex& v, const R_t&) {};
+
+ virtual void plain_bond_visited(const system<R_t, X_t, G_t>&, const typename G_t::halfedge& e, const X_t&, double) {};
+ virtual void plain_site_transformed(const system<R_t, X_t, G_t>&, const typename G_t::vertex& v, const X_t&) {};
+
+#ifndef WOLFF_NO_FIELD
+ virtual void ghost_bond_visited(const system<R_t, X_t, G_t>&, const typename G_t::vertex& v, const X_t&, const X_t&, double) {};
+ virtual void ghost_site_transformed(const system<R_t, X_t, G_t>&, const R_t&) {};
+#endif
+
+ virtual void post_cluster(unsigned, unsigned, const system<R_t, X_t, G_t>&) {};
+ };
+
+}
+
+#endif
+