Ising Model in C++ - c++

I'm writing a code in C++ for a 2D Ising model. Here's what the code should do:
Generate random NxN lattice, with each site either +1 or -1 value.
Select a site at random
If site when flipped (+1 to -1 or -1 to +1) is a state of lower energy, flip state ie. if dE < 0, flip state. If flipped state is of higher energy, flip with acceptance rate w = e^{-b(dE)}. Where dE is the change in energy if state is flipped.
4.Do this for all NxN sites, without repetition. This is considered one sweep.
Do like 100 sweeps.
I'm having trouble with steps 1, 2 and 3, would appreciate any help! For step 1, I managed to create and display a lattice, but I can't seem to extract the value of a site at location (x, y). Steps 2 and 3, how do I use a boolean expression of some sort to flip according to acceptance probability?
#include <cstdlib>
#include <ctime>
using namespace std;
#include <iostream>
int main() //random generation of spin configuration
{
int L; //Total number of spins L = NxN
int N = 30 //A square lattice of length 30
double B=1; //magnetic field
double M; //Total Magnetization = Sum Si
double E; //Total Energy
int T = 1.0;
int nsweeps = 100; //number of sweeps
int de; //change in energy when flipped
double Boltzmann; //Boltzmann factor
int x,y; //randomly chosen lattice site
int i,j,a,c; //counters
int ROWS = 5;
int COLS = 5;
int matrix[ROWS][COLS];
srand ( static_cast<unsigned> ( time ( 0 ) ) );
for ( int i = 0; i < ROWS; i++ )
{
for ( int j = 0; j < COLS; j++ )
{
matrix[i][j] = rand () % 2 *2-1;
}
}
// showing the matrix on the screen
for(int i=0;i<ROWS;i++) // loop 3 times for three lines
{
for(int j=0;j<COLS;j++) // loop for the three elements on the line
{
cout<<matrix[i][j]; // display the current element out of the array
}
cout<<endl; // when the inner loop is done, go to a new line
}
return 0; // return 0 to the OS.
//boundary conditions and range
if(x<0) x += N;
if(x>=L) x -= N;
if(y<0) y += N;
if(y>=L) y -= N;
//counting total energy of configuration
{ int neighbour = 0; // nearest neighbour count
for(int i=0; i<L; i++)
for(int j=0; j<L; j++)
{ if(spin(i,j)==spin(i+1, j)) // count from each spin to the right and above
neighbour++;
else
neighbour--;
if(spin(i, j)==spin(i, j+1))
neighbour++;
else
neighbour--;
}
E = -J*neighbour - B*M;
//flipping spin
int x = int(srand48()*L); //retrieves spin from randomly choosen site
int y = int(srand48()*L);
int delta_M = -2*spin(x, y); //calculate change in Magnetization M
int delta_neighbour = spin(spinx-1, y) + spin(x+1, y)+ spin(x, y-1) + spin(x, y+1);
int delta_neighbour = -2*spin(x,y)* int delta_neighbour;
double delta_E = -J*delta_neighbour -B*delta_M;
//flip or not
if (delta_E<=0)
{ (x, y) *= -1; // flip spin and update values
M += delta_M;
E += delta_E;
}
}

To follow up on my comment:
There are too many issues with your code for a single answer. Try to
build your program step by step. Use functions which perform one
thing, and this they do well. Test each function individually and if
necessary try to find out why it does not work. Then post specific
questions again.
To get you started:
Store your lattice as a std::vector<int> lattice(N*N)
Access element (x,y) with data[x+N*y].
Example:
#include <vector>
struct IsingModel
{
unsigned size_;
std::vector<int> lattice_;
// access element (x,y)
int& at(int x, int y) {
return lattice_[x + y*size_];
}
int at(int x, int y) const {
return lattice_[x + y*size_];
}
// generate size x size lattice
IsingModel(unsigned size)
: size_(size), lattice_(size*size, +1) {
}
static int BoolToSpin(bool v) {
return v ? +1 : -1;
}
// initialize spin randomly
void initializeRandom() {
for(int y=0; y<size_; y++) {
for(int x=0; x<size_; x++) {
at(x,y) = BoolToSpin(rand()%2);
}
}
}
static int Energy(int a, int b) {
return (a == b) ? +1 : -1;
}
// compute total energy
unsigned computeTotalEnergy() const {
unsigned energy = 0;
for(int y=1; y<size_-1; y++) {
for(int x=1; x<size_-1; x++) {
energy += Energy(at(x,y), at(x+1,y));
energy += Energy(at(x,y), at(x,y+1));
}
}
return energy ;
}
};
#include <iostream>
#include <cstdlib>
#include <ctime>
int main() {
srand(static_cast<unsigned>(time(0))); // intialize random number generator
IsingModel im(10);
im.initializeRandom();
unsigned energy = im.computeTotalEnergy();
std::cout << energy << std::endl; // print energy
}

Related

Finding heaviest path (biggest sum of weights) of an undirected weighted graph? Bellman Ford --

There's a matrix, each of its cell contains an integer value (both positive and negative). You're given an initial position in the matrix, now you have to find a path that the sum of all the cells you've crossed is the biggest. You can go up, down, right, left and only cross a cell once.
My solution is using Bellman Ford algorithm: Let's replace all the values by their opposite number, now we've just got a new matrix. Then, I create an undirected graph from the new matrix, each cell is a node, stepping on a cell costs that cell's value - it's the weight. So, I just need to find the shortest path of the graph using Bellman-Ford algorithm. That path will be the longest path of our initial matrix.
Well, there's a problem. The graph contains negative cycles, also has too many nodes and edges. The result, therefore, isn't correct.
This is my code:
Knowing that xd and yd is the initial coordinate of the robot.
void MatrixToEdgelist()
{
int k = 0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
int x = (i - 1) * n + j;
int y = x + 1;
int z = x + n;
if (j<n)
{
edges.push_back(make_tuple(x, y, a[i][j+1]));
}
if (i<n)
{
edges.push_back(make_tuple(x, z, a[i+1][j]));
}
}
}
void BellmanFord(Robot r){
int x = r.getXd();
int y = r.getYd();
int z = (x-1)*n + y;
int l = n*n;
int distance[100];
int previous[100]{};
int trace[100];
trace[1] = z;
for (int i = 1; i <= l; i++) {
distance[i] = INF;
}
distance[z] = a[x][y];
for (int i = 1; i <= l-1; i++) {
for (auto e : edges) {
int a, b, w;
tie(a, b, w) = e;
//distance[b] = min(distance[b], distance[a]+w);
if (distance[b] < distance[a] + w)// && previous[a] != b)
{
distance[b] = distance[a] + w;
previous[b] = a;
}
}
}
//print result
int Max=INF;
int node;
for (int i=2;i<=l;i++)
{
if (Max < distance[i])
{
Max = distance[i];
node = i;
}
}
if (Max<0)cout << Max << "\n";
else cout << Max << "\n";
vector<int> ans;
int i = node;
ans.push_back(i);
while (i != z)
{
i = previous[i];
ans.push_back(i);
}
for (int i=ans.size()-1;i>=0;i--)
{
int x, y;
if (ans[i] % n == 0)
{
x = ans[i] / n;
y = n;
}
else{
x = ans[i] / n + 1;
y = ans[i] - (( x - 1 ) * n);
}
cout << x << " " << y << "\n";
}
}
Example matrix
The result
Clearly that the distance should have continued to update, but it doesn't. It stops at the final node.
"Let's replace all the values by their opposite number"
Not sure what you mean by an opposite number. Anyway, that is incorrect.
If you have negative weights, then the usual solution is to add the absolute value of the most negative weight to EVERY weight.
Why Bellman-Ford? Dijkstra should be sufficient for this problem. ( By default Dijkstra finds the cheapest path. You find the most expensive by assigning the absolute value of ( the original weight minus the greatest ) to every link. )

How many squares can M bishops cannot go in an N*N chess board?

I am currently working on a program that calculates moves of an M amount of bishops on an N*N size chess grid. I have a program to find only the unaccessible squares for one bishop. Can anybody help me with this?
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int ways(int r,int c,int n)
{
int TL,TR,BL,BR;
TL = min(r,c) - 1;
TR = min(r,(n+1)-c) - 1;
BL = n - max(r,(n+1)-c);
BR = n - max(r,c);
return (TL+TR+BL+BR);
}
int main()
{
int r,c,n;
cin >> r >> c >> n;
cout << n*n - ways(r,c,n);
return 0;
}
If bishop's row and column is both 4 and the grid size is 8*8, then that bishop has 51 squares that it can't visit. But I can't figure out how I should approach this.
There are several ways to achieve this task but I'll give you a simple algorithm.
Number of squares those bishops cannot visit =
Total number of squares - Number of squares those bishops can visit
Since it's easy to calculate the total number of squares (n * n), the problem boils down to calculating the total number of squares that can be visited by those bishops from their given position.
In your code, you do not read the number of bishops and their initial coordinates. That should be the first step.
Since you are working in C++, you can use an std::set of std::pair to simplify your task. A set is used to store unique elements, so you can use that to store the locations that can be visited by the bishops and avoid duplicates. A pair can be used to store the location as coordinates (i, j).
Iterate through each bishop and calculate the squares they could visit and add it to the set. In the end, you have the total number of squares that the bishops can cover as the size of that set.
Then use the above formula to get your desired result.
Below is an implementation of that approach:
#include <iostream>
#include <set>
#include <utility>
using namespace std;
int main()
{
int n; // Size of the chess board
int m; // Number of bishops
int x, y; // Initial location of a bishop
int i, j; // Coordinates for iteration
// Read the limits
cin >> n >> m;
if (n < 0) n = 0;
// Read the location of the bishops
set<pair<int, int>> bishops;
for (i = 0; i < m; ++i)
{
cin >> x >> y;
if (x >= 0 && x < n && y >= 0 && y < n)
bishops.insert({x, y});
}
// This is the key
set<pair<int, int>> visitableLocations;
// Calculate the squares that could be visited by all the bishops
for (const auto& bishop : bishops)
{
x = bishop.first;
y = bishop.second;
/*
You don't need this after the edit made to your post
Because you don't consider its initial square as visitable
// Add the original location of the bishop
if (x >= 0 && x < n && y >= 0 && y < n)
visitableLocations.insert({x, y});
*/
// Check all the diagonal directions
// Within the boundaries of the board
// No other bishop should block the way
// auto debug = [&](){cout << i << ' ' << j << '\n';};
// north-east
i = x; j = y;
while (++i < n && ++j < n)
if (bishops.find({i, j}) == bishops.end())
visitableLocations.insert({i, j});//debug();}
// north-west
i = x; j = y;
while (--i >= 0 && ++j < n)
if (bishops.find({i, j}) == bishops.end())
visitableLocations.insert({i, j});//debug();}
// south-east
i = x; j = y;
while (++i < n && --j >= 0)
if (bishops.find({i, j}) == bishops.end())
visitableLocations.insert({i, j});//debug();}
// south-west
i = x; j = y;
while (--i >= 0 && --j >=0)
if (bishops.find({i, j}) == bishops.end())
visitableLocations.insert({i, j});//debug();}
}
// Now get the final answer
int ans = (n * n) - visitableLocations.size();
cout << ans;
return 0;
}

Finding minimum total length of line segments to connect 2N points

I'm trying to solve this problem by bruteforce, but it seems to run very slow when given 7 (which is 2*7 points).
Note: I only need to run it to maximum 2*8 points
Problem statement:
Given 2*N points in a 2d plane, connect them in pairs to form N line segments. Minimize the total length of all the line segments.
Example:
Input: 5 10 10 20 10 5 5 1 1 120 3 6 6 50 60 3 24 6 9 0 0
Output: 118.4
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iomanip>
using namespace std;
class point{
public:
double x, y;
};
double getLength(point a, point b){
return hypot((a.x - b.x), (a.y - b.y));
}
static double mini = INT_MAX;
void solve(vector <point> vec, double sum){
double prevSum = sum;
if(sum > mini){
return;
}
if(vec.size() == 2){
sum += getLength(vec[0], vec[1]);
mini = min(mini, sum);
return;
}
for(int i = 0; i < vec.size() - 1; i++){
for(int j = i + 1; j < vec.size(); j++){
sum = prevSum;
vector <point> temp = vec;
sum += getLength(temp[i], temp[j]);
temp.erase(temp.begin() + j);
temp.erase(temp.begin() + i);
solve(temp, sum);
}
}
}
int main(){
point temp;
int input;
double sum = 0;
cin >> input;
vector<point> vec;
for(int i = 0; i < 2 * input; i++){
cin >> temp.x >> temp.y;
vec.push_back(temp);
}
solve(vec, sum);
cout << fixed << setprecision(2) << mini << endl;
}
How can I speed up this code ?
I don't think this is what you are looking for but I mention it for completeness sake anyway. The problem can be formulated as a Mixed Integer Programming (MIP) problem.
We have distances:
d(i,j) = distance between point i and j (only needed for i<j)
and decision variables
x(i,j) = 1 if points i and j are connected (only needed for i<j)
0 otherwise
Then we can write:
Solving this problem can be done with widely available MIP solvers and leads to proven optimal solutions. A small example with 50 points:
You can solve this iteratively by using next_permutation() to go through all the permutations one by one. Apologies for the messy code, but this should show you how to do it:
struct Point {
Point(int x, int y) : x(x), y(y) {
}
bool operator< (const Point& rhs) {
const int key1 = y * 1000 + x;
const int key2 = rhs.y * 1000 + rhs.x;
return key1 < key2;
}
double dist(const Point& next) {
const double h = (double)(next.x - x);
const double v = (double)(next.y - y);
return sqrt(h*h + v*v);
}
int x, y;
};
You need the operator so you have some sort of sorting key for your points, so next_permutation can go through them in lexicographical increasing order.
double getShortestDist(std::vector p) {
double min = 200000;
std::sort(p.begin(), p.end());
while(std::next_permutation(p.begin(), p.end())) {
double sum = 0.0;
for (int i = 0; i < p.size(); i+= 2) {
sum += p[i].dist(p[i+1]);
}
if (sum < min) {
min = sum;
}
}
return min;
}
int main(int argc, char*argv[]) {
static const int arr[] = {
10, 10, 20, 10, 5, 5, 1, 1, 120, 3, 6, 6, 50, 60, 3, 24, 6, 9, 0, 0
};
std::vector<Point> test;
for (int i = 0; i < 20; i += 2) {
test.push_back(Point(arr[i], arr[i+1]));
printf("%d %d\n", arr[i], arr[i+1]);
}
printf("Output: %d, %f", test.size(), getShortestDist(test));
}

Gauss-Seidel method and coupled gradient methods for matrix

I have a matrix
and I should write code using Gauss-Seidel and coupled gradient methods taking the structure of the matrix.
Ax = e where A is matrix and e is vector with values of 1
I don't know how to write code using coupled gradient methods and my Gauss-Seidel algorithm don't have main part where I add this all thinks
//part of gauss-seidel method
const int N = 128; //size of array
const int no_of_iter = 128; //iterations
int main() {
double result[N]; //array for result
double result_pom[N]; //temporary result array
double sum = 0.0;
int x, y;
for (int i = 0; i < no_of_iter; i++) {
for (y = 0; y < N; y++) {
result_pom[y] = result[y]; //set values of result to result_pom
}
for (x = 0; x < N; x++) {
sum = 0.0;
//for functions where I add (x,y) el
result[x] = 0.25 * (1 - sum); //because 4 is dominant el of matrix and
//1 is value of vector e
}
}
}

Ising model simulation offset critical temperature

I'm writing a simulation of the Ising model in 2D. The model behaves as predicted except for one thing: the critical temperature is roughly 3.5 while it should be near 2/ln(2 + sqrt (2)).
The project is a C++ program that generates the data, and a shell script that exercises the program. The full code can be found here. Also here's lattice.cpp
#include <iostream>
#include "include/lattice.h"
using namespace std;
/*
Copy assignment operator, too long to include in the header.
*/
lattice &lattice::operator=(const lattice &other) {
size_ = other.size_;
spins_ = other.spins_;
J_ = other.J_;
H_ = other.H_;
delete spins_;
return *this;
}
void lattice::print() {
unsigned int area = size_ * size_;
for (unsigned int i = 0; i < area; i++) {
cout << to_symbol(spins_->at(i));
if (i % size_ == size_ - 1)
cout << endl;
}
cout << endl;
}
/*
Computes the energy associated with a spin at the given point.
It is explicitly float as that would allow the compiler to make use of multiple
registers instead of keeping track of unneeded precision. (typically J, H ~ 1).
*/
float lattice::compute_point_energy(int row, int col) {
int accumulator = get(row + 1, col) + get(row - 1, col) + get(row, col - 1) +
get(row, col + 1);
return -get(row, col) * (accumulator * J_ + H_);
}
/*
Computes total magnetisation in O(n^2). Thread safe
*/
int lattice::total_magnetisation() {
int sum = 0;
#pragma omp parallel for reduction(+ : sum)
for (unsigned int i = 0; i < size_ * size_; i++) {
sum += spins_->at(i);
}
return sum;
}
int inline to_periodic(int row, int col, int size) {
if (row < 0 || row >= size)
row = abs(size - abs(row));
if (col < 0 || col >= size)
col = abs(size - abs(col));
return row * size + col;
}
with lattice.h
#ifndef lattice_h
#define lattice_h
#include <cmath>
#include <vector>
/* Converts spin up/down to easily printable symbols. */
char inline to_symbol(int in) { return in == -1 ? '-' : '+'; }
/* Converts given pair of indices to those with periodic boundary conditions. */
int inline to_periodic(int row, int col, int size) {
if (row < 0 || row >= size)
row = abs(size - abs(row));
if (col < 0 || col >= size)
col = abs(size - abs(col));
return row * size + col;
}
class lattice {
private:
unsigned int size_;
// vector<bool> would be more space efficient, but it would not allow
// multithreading
std::vector<short> *spins_;
float J_;
float H_;
public:
lattice() noexcept : size_(0), spins_(NULL), J_(1.0), H_(0.0) {}
lattice(int new_size, double new_J, double new_H) noexcept
: size_(new_size), spins_(new std::vector<short>(size_ * size_, 1)),
J_(new_J), H_(new_H) {}
lattice(const lattice &other) noexcept
: lattice(other.size_, other.J_, other.H_) {
#pragma omp parallel for
for (unsigned int i = 0; i < size_ * size_; i++)
spins_->at(i) = other.spins_->at(i);
}
lattice &operator=(const lattice &);
~lattice() { delete spins_; }
void print();
short get(int row, int col) {
return spins_->at(to_periodic(row, col, size_));
}
unsigned int get_size() { return size_; }
void flip(int row, int col) { spins_->at(to_periodic(row, col, size_)) *= -1; }
int total_magnetisation();
float compute_point_energy(int row, int col);
};
#endif
and simulation.cpp
#include <iostream>
#include <math.h>
#include "include/simulation.h"
using namespace std;
/*
Advances the simulation a given number of steps, and updates/prints the statistics
into the given file pointer.
Defaults to stdout.
The number of time_steps is explcitly unsigned, so that linters/IDEs remind
the end user of the file that extra care needs to be taken, as well as to allow
advancing the simulation a larger number of times.
*/
void simulation::advance(unsigned int time_steps, FILE *output) {
unsigned int area = spin_lattice_.get_size() * spin_lattice_.get_size();
for (unsigned int i = 0; i < time_steps; i++) {
// If we don't update mean_energy_ every time, we might get incorrect
// thermodynamic behaviour.
total_energy_ = compute_energy(spin_lattice_);
double temperature_delta = total_energy_/area - mean_energy_;
if (abs(temperature_delta) < 1/area){
cerr<<temperature_delta<<"! Reached equilibrium "<<endl;
}
temperature_ += temperature_delta;
mean_energy_ = total_energy_ / area;
if (time_ % print_interval_ == 0) {
total_magnetisation_ = spin_lattice_.total_magnetisation();
mean_magnetisation_ = total_magnetisation_ / area;
print_status(output);
}
advance();
}
}
/*
Advances the simulation a single step.
DOES NOT KEEP TRACK OF STATISTICS. Hence private.
*/
void simulation::advance() {
#pragma omp parallel for collapse(2)
for (unsigned int row = 0; row < spin_lattice_.get_size(); row++) {
for (unsigned int col = 0; col < spin_lattice_.get_size(); col++) {
double dE = compute_dE(row, col);
double p = r_.random_uniform();
float rnd = rand() / (RAND_MAX + 1.);
if (exp(-dE / temperature_) > rnd) {
spin_lattice_.flip(row, col);
}
}
}
time_++;
}
/*
Computes change in energy due to flipping one single spin.
The function returns a single-precision floating-point number, as data cannot under
most circumstances make use of greater precision than that (save J is set to a
non-machine-representable value).
The code modifies the spin lattice, as an alternative (copying the neighborhood
of a given point), would make the code run slower by a factor of 2.25
*/
float simulation::compute_dE(int row, int col) {
float e_0 = spin_lattice_.compute_point_energy(row, col);
return -4*e_0;
}
/*
Computes the total energy associated with spins in the spin_lattice_.
I originally used this function to test the code that tracked energy as the lattice
itself was modified, but that code turned out to be only marginally faster, and
not thread-safe. This is due to a race condition: when one thread uses a neighborhood
of a point, while another thread was computing the energy of one such point in
the neighborhood of (row, col).
*/
double simulation::compute_energy(lattice &other) {
double energy_sum = 0;
unsigned int max = other.get_size();
#pragma omp parallel for reduction(+ : energy_sum)
for (unsigned int i = 0; i < max; i++) {
for (unsigned int j = 0; j < max; j++) {
energy_sum += other.compute_point_energy(i, j);
}
}
return energy_sum;
}
void simulation::set_to_chequerboard(int step){
if (time_ !=0){
return;
}else{
for (unsigned int i=0; i< spin_lattice_.get_size(); ++i){
for (unsigned int j=0; j<spin_lattice_.get_size(); ++j){
if ((i/step)%2-(j/step)%2==0){
spin_lattice_.flip(i, j);
}
}
}
}
}
with simulation.h
#ifndef simulation_h
#define simulation_h
#include "lattice.h"
#include "rng.h"
#include <gsl/gsl_rng.h>
/*
The logic of the entire simulation of the Ising model of magnetism.
This simulation will run and print statistics at a given time interval.
A simulation can be advanced a single time step, or many at a time,
*/
class simulation {
private:
unsigned int time_ = 0; // Current time of the simulation.
rng r_ = rng();
lattice spin_lattice_;
double temperature_;
double mean_magnetisation_ = 1;
double mean_energy_;
double total_magnetisation_;
double total_energy_;
unsigned int print_interval_ = 1;
void advance();
public:
void set_print_interval(unsigned int new_print_interval) { print_interval_ = new_print_interval; }
simulation(int new_size, double new_temp, double new_J, double new_H)
: time_(0), spin_lattice_(lattice(new_size, new_J, new_H)), temperature_(new_temp),
mean_energy_(new_J * (-4)), total_magnetisation_(new_size * new_size),
total_energy_(compute_energy(spin_lattice_)) {}
void print_status(FILE *f) {
f = f==NULL? stdout : f;
fprintf(f, "%4d\t%e \t%e\t%e\n", time_, mean_magnetisation_,
mean_energy_, temperature_);
}
void advance(unsigned int time_steps, FILE *output);
double compute_energy(lattice &other);
float compute_dE(int row, int col);
void set_to_chequerboard(int step);
void print_lattice(){
spin_lattice_.print();
};
// void load_custom(const lattice& custom);
};
#endif
The output right now looks something like this:
while it should be a step down near 2.26
I have found a few issues in your code:
The compute_dE method returns the wrong energy, as the factor of 2 shouldn't be there. The Hamiltonian of the Ising system is
While you are effectively using
The compute_energy method returns the wrong energy. The method should iterate over each spin pair only once. Something like this should do the trick:
for (unsigned int i = 0; i < max; i++) {
for (unsigned int j = i + 1; j < max; j++) {
energy_sum += other.compute_point_energy(i, j);
}
}
You use a temperature that is updated on the fly instead of using the target temperature. I do not really understand the purpose of that.