ALPS Project: scheduler library

Implementing a Monte Carlo simulation

Here we discuss how to implement a Monte Carlo simulation based on the scheduler library. The full source code is provided in the example directory.

The classes

Two classes need to be provided: a simulation class derived from MCRun and an object factory derived from Factory. If only a single simulation object is needed, such as here, the templated single-object Factory SimpleMCFactory can be used.
class IsingSimulation : public alps::scheduler::MCRun
{
public:
  IsingSimulation(const alps::ProcessList&,const alps::Parameters&,int);
  void save(alps::ODump&) const;
  void load(alps::IDump&);
  void dostep();
  bool is_thermalized() const;
  double work_done() const;
  bool change_parameter(const std::string& name, const alps::StringValue& value);
  
private:
  uint32_t length;                  // the system size
  double beta;                      // the inverse temperatre
  uint32_t sweeps;                  // the number of sweeps done
  uint32_t thermalization_sweeps;   // the number of sweeps to be done for equilibration
  uint32_t total_sweeps;            // the total number of sweeps to be done after equilibration
  std::vector<int> spins;        // the vector to store the spins
}; 

typedef alps::scheduler::SimpleMCFactory<IsingSimulation> IsingFactory;

The main program

just starts a scheduler and catches exceptions:
int main(int argc, char** argv)
{
  try {
   return alps::scheduler::start(argc,argv,IsingFactory());
  }
  catch (std::exception& exc) {
    return -1;
  }
}

Implementation of the functions

The constructor

initializes the data members from the parameters. It then starts from a random configuration and creates two objects to measure energy and magnetization.
IsingSimulation::IsingSimulation(const alps::ProcessList& w,const alps::Parameters& p,int node)
  : scheduler::MCRun(0,w,p,node),
    length(static_cast<uint32_t>(parms["L"])),
    beta(1./static_cast<double>(parms["T"])),
    thermalization_sweeps(static_cast<uint32_t>(parms["THERMALIZATION"])),
    total_sweeps(static_cast<uint32_t>(parms["SWEEPS"])),
    sweeps(0),
    spins(where.empty() ? 0 : length)
{
  // initialize random spin configuration
  for(int i=0;i<spins.size();i++)   
    spins[i]=(random_real() < 0.5 ? 1 : -1);

  // create measurement objects
  measurements << alea::RealObservable("Energy");
  measurements << alea::RealObservable("Magnetization");
}
If the object is just created to evaluate the simulation but not to perform actual simulations, the where data member of the simulation, specifying the processes on which a simulation will performed, will be empty. In that case the spins vectoring, storing the spins of the simulation can be resized to zero.

The member functions

void IsingSimulation::save(alps::ODump& dump) const
{
  dump << sweeps << spins;
}
saves the simulation data into a checkpoint.
void IsingSimulation::load(alps::IDump& dump)
{
  dump >> sweeps;
  if(!where.empty()) // skip reading the spins if we are just evaluating
    dump >> spins;
}
This loads the simulation data from a checkpoint. If the object is just loaded to evaluate the simulation but not to perform actual simulations, the where data member of the simulation, specifying the processes on which a simulation will performed, will be empty. In that case reading the spin configuration can be omitted to save memory.
bool IsingSimulation::change_parameter(const std::string& name, const alps::StringValue& value)
{
  if(name=="SWEEPS") {
    total_sweeps=static_cast<uint32_t>(value);
    return true; // can do it.
  }
  else
    return false; // cannot do it.
}
is called when the user tries to change a parameter while the simulation is running. In this example we only allow changes to the total number of sweeps.

Note:The implementation of this function is optional.

bool IsingSimulation::is_thermalized() const
{
  return (sweeps >= thermalization_sweeps);
}
determines thermalization. Here the simulation is thermalized when a certain number of sweeps, given by the user has been performed.
double IsingSimulation::work_done() const
{
  return (is_thermalized() ? (sweeps-thermalization_sweeps)/double(total_sweeps) :0.);
}
returns the fraction of total work performed.
void IsingSimulation::dostep()
{
  // increment sweep count
  sweeps++;
  
  // perform updates
  for (int j=0;j<length;j++)  {
    // choose a random site and determine the neighbors
    int i = int(double(length)*random_real());
    int right=(i+1 < length ? i+1 : 0);
    int left=( i-1 < 0 ? length-1 : i-1);

    // calculate change in the weight of the configuration
    double p=exp(2.*beta*spins[i]*(spins[right]+spins[left]));

    // Metropolis updating: accept if random number is smaller than p
    if (p>=1. || random_real() < p)
      spins[i]=-spins[i];
  }
  
  // perform measurements
  int tmag=0;
  int ten=0;
  for (int i=0;i<length;i++) {
    int right=(i +1 < length ? i+1 : 0);
    tmag += spins[i];
    ten += -spins[i]*spins[right];
  }
  
  // normalize measurements and add them to the observables
  measurements.get<alea::RealObservable>("Energy") << double(ten)/length;
  measurements.get<alea::RealObservable>("Magnetization") << double(tmag)/length;
}
performs the actual simulations and measurements.

Further member functions

The member functions
void MCRun::start() const;
void MCRun::halt() const;
can be overridden to perform extra tasks when the simulation is started and halted (e.g. opening and closing of files or (de)allocation of extra resources.

copyright (c) 1994-2010 by Matthias Troyer

Distributed under the Boost Software License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)