I would like to create a function which initializes a vector or array of size width * height, but which also creates a border around these values.
The values around the outside also need to be initialized to a different value from the ones in the center.
The objects I am storing do not have a default constructor, so I cannot rely on that for initialization.
This is the code I have so far, but it feels like there should be a simpler or more idiomatic way of doing this.
I can use any features up to and including C++1z.
#include <iostream>
#include <vector>
void fill_values(const unsigned width, const unsigned height, std::vector<int> &values) {
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
if(x==0 || x==width+1 || y==0 || y==height+1) {
values.push_back(1);
} else {
values.push_back(0);
}
}
}
}
int main(int argc, char *argv[]) {
const unsigned width = 4;
const unsigned height = 3;
std::vector<int> values;
fill_values(width, height, values);
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
std::cout << values[y * (width+2) + x];
}
std::cout << '\n';
}
return 0;
}
Output : -
111111
100001
100001
100001
111111
Honestly, your code is fine. I pretty easily understood what it does.
But in the spirit of proposing alternate complex implementations, I'd propose the following. A different way to fill the matrix is to add a full row of 1s, then height rows of 1000...001, then another full row of 1s. We can make that a bit more explicit. Also, would suggest returning a vector instead of filling it:
std::vector<int> fill_values(const unsigned width, const unsigned height) {
std::vector<int> m;
m.reserve((width + 2) * (height + 2));
// add row of 1s
m.insert(m.end(), width + 2, 1);
// add height middle rows
for (int i = 0; i < height; ++i) {
m.push_back(1);
m.insert(m.end(), width, 0);
m.push_back(1);
}
// and a final row of 1s
m.insert(m.end(), width + 2, 1);
return m;
}
As #Fedorico said in the comments, using a vector of vectors is a better representation for your values variable. Rather than pass the values by reference as a parameter, it would be better to depend on copy elision for the return value. I also found it easier to just use the set height and width to be the total number of rows and cols in the data so that there's no need to add two.
The following code depends on c++11 or newer:
#include <iostream>
#include <vector>
using namespace std;
// Fills the 2D matrix with 1s on the border and 0s in the middle.
vector<vector<int>> generate_matrix(int rows, int cols);
void print_matrix(const vector<vector<int>>& matrix);
int main()
{
// Don't sync I/O with C stdio.
ios_base::sync_with_stdio(false);
// Height and Width of the entire 2D matrix.
const int rows = 6;
const int cols = 5;
vector<vector<int>> matrix = generate_matrix(rows, cols);
print_matrix(matrix);
return 0;
}
vector<vector<int>> generate_matrix(int rows, int cols)
{
// fill a rows x cols 2D vector with 0s.
vector<vector<int>> matrix(rows, vector<int>(cols, 0));
// fill in 1s on top and bottom rows.
if (rows > 0)
{
for (int i = 0; i < cols; ++i)
{
matrix[0][i] = 1;
matrix[rows-1][i] = 1;
}
}
// fill in 1s on the left and right columns.
if (cols > 0)
{
for (int i = 0; i < rows; ++i)
{
matrix[i][0] = 1;
matrix[i][cols-1] = 1;
}
}
return matrix;
}
void print_matrix(const vector<vector<int>>& matrix)
{
// Use a reference for the row iterator to prevent a vector copy.
for (auto& row : matrix)
{
for (auto element : row)
{
cout << element;
}
cout << '\n';
}
}
Not a great difference, but you can use std::generate_n() (starting from c++11) with a lambda function.
The following is a full working example
#include <vector>
#include <iostream>
#include <algorithm>
int main ()
{
constexpr std::size_t width { 4U };
constexpr std::size_t height { 3U };
constexpr std::size_t w2 { width + 2U };
constexpr std::size_t h2 { height + 2U };
std::vector<int> values;
values.resize ( w2 * h2 );
std::generate_n(values.begin(), w2 * h2, [=]() -> int
{
static std::size_t i = -1;
++i;
return ( 0U == i / w2 ) || ( h2 - 1U == i / w2 )
|| ( 0U == i % w2 ) || ( w2 - 1U == i % w2 );
});
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
std::cout << values[y * (width+2) + x] << ' ';
}
std::cout << '\n';
}
return 0;
}
If width and heigth are known at compile time, you can initialize the std::vector (or the std::array?) with initializer list, using a little template work (give me some time and I'll show an example).
Related
I'm trying to do task in C++. I need create this function:
void fillArray(std::array<std::array<int, maxColumns>, maxRows> array, size_t rows, size_t columns) {
}
Right now my example code looks like this:
#include <iostream>
#include <array>
constexpr int maxColumns = 42;
constexpr int maxRows = 334;
void fillArray(std::array<std::array<int, maxColumns>, maxRows> array, size_t rows, size_t columns) {
}
int main()
{
}
I need to fill the array with numbers from 1 to rows*columns starting from [0][0] and diagonally. How to declare and initialize the function with array in this example and then fill it diagonally? Any help would be greatly appreciated!
It should be
template <std::size_t maxColumns, std::size_t maxRows>
void fillArray(std::array<std::array<int, maxColumns>, maxRows>& array) {
// ...
}
Demo
Let's suppose you use a simple one-dimensional valarray (or array if you insist) of the size width * height wrapped in a class:
class Matrix
{
private:
std::valarray<int> _data;
int _width, _height;
public:
Matrix(int width, int height) : _width(width), _height(height), _data(width * height)
{
}
}
Then you can add a member function that maps x, y coordinates to an item reference:
int& item(int x, int y) { return _data[x + _width * y]; }
... and another one for filling it diagonally like this:
void fillDiagonally(int value = 0, int step = 1)
{
for (int i = 0; i < _height + _width; ++i) {
// calculate starting coordinates (along left edge and then bottom edge)
int row = i < _height ? i : _height - 1;
int col = i < _height ? 0 : i - _height + 1;
// move diagonally up and right until you reach margins, while filling-in values
for (int j = 0; j < _width - col && j <= row; ++j) {
item(col + j, row - j) = value;
value += step;
}
}
}
and use it like this:
int main()
{
Matrix m(8, 5);
m.fillDiagonally(1);
}
This way, you don't need to pass the array as an argument, because it's a part of the class. Otherwise you would have to pass it by reference, like you were suggested above.
The question is quite straightforward. After some trials, here is the most efficient code I found:
//For the sake of the example, I initialize every entry as zero.
vector<float> vector1D(1024 * 768, 0);
vector<vector<float>> vector2D(768, vector<float>(1024,0));
int counter = 0;
for (int i = 0; i < 768; i++) {
for (int j = 0; j < 1024; j++) {
vector2D[i][j] = vector1D[counter++];
}
}
Is there a faster way?
Yes.
You can remap the way you access the elements without needing to copy them. You can create a "view" class to achieve that:
template<typename T>
class two_dee_view
{
public:
two_dee_view(std::vector<T>& v, std::size_t row, std::size_t col)
: v(v), stride(col) { if(v.size() < row * col) v.resize(row * col); }
T& operator()(std::size_t row, std::size_t col)
{ return v[(row * stride) + col]; }
T const& operator()(std::size_t row, std::size_t col) const
{ return v[(row * stride) + col]; }
std::size_t col_size() const { return stride; }
std::size_t row_size() const { return v.size() / stride; }
private:
std::vector<T>& v;
std::size_t stride;
};
int main()
{
std::vector<double> v {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
two_dee_view<double> v2d(v, 2, 3);
for(auto row = 0U; row < v2d.row_size(); ++row)
for(auto col = 0U; col < v2d.col_size(); ++col)
std::cout << row << ", " << col << ": " << v2d(row, col) << '\n';
}
Output:
0, 0: 1
0, 1: 2
0, 2: 3
1, 0: 4
1, 1: 5
1, 2: 6
The class simply maintains a reference to the std::vector you pass in to the constructor. You should only use the two_dee_view as long as the original std::vector lives but no longer.
It might be faster by using memcpy, as that is the lowest possible level of an API for copying memory and is likely that there are compiler optimizations which may use specific instructions, etc. and make if faster:
for (int i = 0; i < 768; i++) {
memcpy(vector2D[i].data(), &vector1D[i * 1024], sizeof(float) * 1024);
}
Keep in mind that you shouldn't be using memcpy for anything but trivially-copiable data. That is, it will work fine for float and int but not for classes as the copy constructor will not be called.
If you have to use a vector of vectors for some reason, using memcpy or memmove is faster (because it's a single step, as described in another reply). But you should use the STL instead of doing it by yourself.
vector<float> vector1D(1024 * 768, 0);
vector<vector<float>> vector2D(768, vector<float>(1024, 0));
for (int i = 0; i < 768; i++) {
vector2D[i].assign(next(vector1D.cbegin(), 1024 * i),
next(vector1D.cbegin(), 1024 * (i + 1)));
}
This results in a straight memmove (depending on the STL implementation) but is much more safe, optimized and (possibly) readable.
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.
I am working with a dynamic square 2D array that I sometimes need to enlarge for my needs. The enlarging part consist in adding a new case on each border of the array, like this:
To achieve this, I first copy the content of my actual 2D array in a temporary other 2D array of the same size. Then I create the new 2D array with the good size, and copy the original content of the array in the middle of the new one.
Is there any quick way to copy the content of the old array in the middle of my new array? The only way I have found so far is only by using two for sections:
for(int i = 1; i < arraySize-1; i++)
{
for(int j = 1; j < arraySize-1; j++)
{
array[i][j] = oldArray[i-1][j-1];
}
}
But I'm wondering if there is no quicker way to achieve this. I thought about using std::fill, but I don't see how it would be possible to use it in this particular case.
My EnlargeArray function:
template< typename T >
void MyClass<T>::EnlargeArray()
{
const int oldArraySize = tabSize;
// Create temporary array
T** oldArray = new T*[oldArraySize];
for(int i = 0; i < oldArraySize; i++)
{
oldArray[i] = new T[oldArraySize];
}
// Copy old array content in the temporary array
for(int i = 0; i < arraySize; i++)
{
for(int j = 0; j < arraySize; j++)
{
oldArray[i][j] = array[i][j];
}
}
tabSize+=2;
const int newArraySize = arraySize;
// Enlarge the array
array= new T*[newArraySize];
for(int i = 0; i < newArraySize; i++)
{
array[i] = new T[newArraySize] {0};
}
// Copy of the old array in the center of the new array
for(int i = 1; i < arraySize-1; i++)
{
for(int j = 1; j < arraySize-1; j++)
{
array[i][j] = oldArray[i-1][j-1];
}
}
for(int i = 0; i < oldArraySize; i++)
{
delete [] oldArray[i];
}
delete [] oldArray;
}
Is there any quick way to copy the content of the old array in the middle of my new array?
(Assuming the question is "can I do better than a 2D for-loop?".)
Short answer: no - if your array has R rows and C columns you will have to iterate over all of them, performing R*C operations.
std::fill and similar algorithms still have to go through every element internally.
Alternative answer: if your array is huge and you make sure to avoid
false sharing, splitting the copy operation in multiple threads that deal with a independent subset of the array could be beneficial (this depends on many factors and on the hardware - research/experimentation/profiling would be required).
First, you can use std::make_unique<T[]> to manage the lifetime of your arrays. You can make your array contiguous if you allocate a single array of size row_count * col_count and perform some simple arithmetic to convert (col, row) pairs into array indices. Then, assuming row-major order:
Use std::fill to fill the first and last rows with zeros.
Use std::copy to copy the old rows into the middle of the middle rows.
Fill the cells at the start and end of the middle rows with zero using simple assignment.
Do not enlarge the array. Keep it as it is and allocate new memory only for the borders. Then, in the public interface of your class, adapt the calculation of the offets.
To the client of the class, it will appear as if the array had been enlarged, when in fact it wasn't really touched by the supposed enlargement. The drawback is that the storage for the array contents is no longer contiguous.
Here is a toy example, using std::vector because I cannot see any reason to use new[] and delete[]:
#include <vector>
#include <iostream>
#include <cassert>
template <class T>
class MyClass
{
public:
MyClass(int width, int height) :
inner_data(width * height),
border_data(),
width(width),
height(height)
{
}
void Enlarge()
{
assert(border_data.empty()); // enlarge only once
border_data.resize((width + 2) * 2 + (height * 2));
width += 2;
height += 2;
}
int Width() const
{
return width;
}
int Height() const
{
return height;
}
T& operator()(int x, int y)
{
assert(x >= 0);
assert(y >= 0);
assert(x < width);
assert(y < height);
if (border_data.empty())
{
return inner_data[y * width + x];
}
else
{
if (y == 0)
{
return border_data[x]; // top border
}
else if (y == height - 1)
{
return border_data[width + x]; // bottom border
}
else if (x == 0)
{
return border_data[width + height + y]; // left border
}
else if (x == width - 1)
{
return border_data[width * 2 + height * 2 + y]; // right border
}
else
{
return inner_data[(y - 1) * (width - 2) + (x - 1)]; // inner matrix
}
}
}
private:
std::vector<T> inner_data;
std::vector<T> border_data;
int width;
int height;
};
int main()
{
MyClass<int> test(2, 2);
test(0, 0) = 10;
test(1, 0) = 20;
test(0, 1) = 30;
test(1, 1) = 40;
for (auto y = 0; y < test.Height(); ++y)
{
for (auto x = 0; x < test.Width(); ++x)
{
std::cout << test(x, y) << '\t';
}
std::cout << '\n';
}
std::cout << '\n';
test.Enlarge();
test(2, 0) = 50;
test(1, 1) += 1;
test(3, 3) = 60;
for (auto y = 0; y < test.Height(); ++y)
{
for (auto x = 0; x < test.Width(); ++x)
{
std::cout << test(x, y) << '\t';
}
std::cout << '\n';
}
}
Output:
10 20
30 40
0 0 50 0
0 11 20 0
0 30 40 0
0 0 0 60
The key point is that the physical representation of the enlarged "array" no longer matches the logical one.
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
}