PHY 411-506 Home Home  |  Course Outline  |  Lectures  |  Homework  |  Files

Lecture 32: April 9


Path Integral Quantum Monte Carlo

The following applet is based on a program PathIntegralMC.java is an implementation of the algorithm on pages 656-657 of the textbook.

The potential in this simulation is the simple harmonic oscillator V(x) = 0.5 x2.

Here is the code which generates this applet:

// PathIntegralMC.java

import comphys.graphics.*;
import comphys.Easy;

public class PathIntegralMC extends Animation {

    // Use path integral Monte Carlo to find ground state in a potential

    double V (double x) {       // potential energy function
        return 0.5 * x * x;     // harmonic oscillator
    }

    double dVdx (double x) {    // derivative of V(x) used in virial theorem
        return x;               // harmonic oscillator
    }

    int N = 10;                 // number of "atoms"
    double tau = 15;            // imaginary time = inverse temperature
    double deltaTau = tau / N;  // imaginary time step
    double T = 1 / tau;         // temperature

    double[] x;                 // displacements from equilibrium of "atoms"
    double E0;                  // ground state energy
    double xmin = -4;           // minimum value of x to bin
    double xmax = 4;            // maximum value of x to bin
    double dx = 0.1;            // binsize for probability array
    int nBins;                  // number of bins
    double[] P;                 // probability array
    double[] phi0;              // ground state wave function

    int nequil = 2000;          // number of thermalization steps
    int mcs;                    // Monte Carlo step number
    double delta = 1;           // Metropolis step size

    void initial () {
        deltaTau = tau / N;
        T = 1 / tau;
        if (x == null || x.length < N)
            x = new double[N];
        nBins = (int) Math.round((xmax - xmin) / dx);
        if (P == null || P.length < nBins) {
            P = new double[nBins];
            phi0 = new double[nBins];
        }
        zeroProbability();
        // set random displacements
        for (int i = 0; i < N; i++) {
            x[i] = (2 * Math.random() - 1) * delta;
            updateProbability(x[i]);
        }
        E0 = energySum() / N;
        computeVArray();        // for plotting
        computePhi0();
        mcs = 0;
    }

    double energySum () {
        double sum = 0;
        for (int bin = 0; bin < nBins; bin++) {
            double x = xmin + (bin + 0.5) * dx;
            // use virial theorem
            double kineticEnergy = 0.5 * x * dVdx(x);
            sum += P[bin] * (kineticEnergy + V(x));
        }
        return sum;
    }

    void updateProbability (double x) {
        int bin = (int) Math.round((x - xmin) / (xmax - xmin) * nBins);
        if (bin < 0)
            bin = 0;
        if (bin >= nBins)
            bin = nBins - 1;
        P[bin] += 1;
    }

    void zeroProbability () {
        for (int n = 0; n < nBins; n++)
            P[n] = 0;
    }

    double phi0Max;             // used for plotting phi0

    void computePhi0 () {
        phi0Max = 0;
        for (int i = 0; i < nBins; i++) {
            phi0[i] = Math.sqrt(P[i] / dx);
            if (phi0[i] > phi0Max)
                phi0Max = phi0[i];
        }
    }

    void MonteCarloStep () {    // one Monte Carlo step per atom
        for (int i = 0; i < N; i++) {
            // choose an atom j at random
            int j = (int) (N * Math.random());
            // trial displacement
            double xTrial = x[j] + (2 * Math.random() - 1) * delta;
            // periodic boundary conditions
            int jMinus1 = j - 1;
            if (jMinus1 == -1)
                jMinus1 = N - 1;
            int jPlus1 = j + 1;
            if (jPlus1 == N)
                jPlus1 = 0;
            // change in energy
            double deltaE =
                (x[jPlus1] - xTrial) * (x[jPlus1] - xTrial) +
                (xTrial - x[jMinus1]) * (xTrial - x[jMinus1]) -
                (x[jPlus1] - x[j]) * (x[jPlus1] - x[j]) -
                (x[jMinus1] - x[j]) * (x[jMinus1] - x[j]);
            deltaE /= 2 * deltaTau * deltaTau;
            deltaE += V(xTrial) - V(x[j]);
            if (deltaE < 0 || Math.random() <= Math.exp(-deltaTau * deltaE))
                x[j] = xTrial;
            updateProbability(x[j]);
        }
        ++mcs;
        // compute energy
        double norm = mcs;      // number of MC steps * number of atoms
        if (mcs > nequil)
            norm -= nequil;
        norm *= N;
        if (norm != 0)
            E0 = energySum() / norm;
        computePhi0();
    }

    int nPlot = 100;            // number of points for plotting V(x)
    double Vmin, Vmax;          // used for plotting
    double[] VArray;            // store values for plotting

    void computeVArray () {
        if (VArray == null || VArray.length < nPlot)
            VArray = new double[nPlot];
        Vmin = 1e10;
        Vmax = -1e10;
        double dx = (xmax - xmin) / (nPlot - 1);
        for (int i = 0; i < nPlot; i++) {
            double x = xmin + i * dx;
            VArray[i] = V(x);
            if (VArray[i] < Vmin)
                Vmin = VArray[i];
            if (VArray[i] > Vmax)
                Vmax = VArray[i];
        }
    }

    class Graph extends Plot {
        Graph () {
            setSize(500, 400);
        }

        public void paint () {
            clear();
            setColor("black");
            drawAxes(xmin, xmax, 0, N);
            plotStringCenter(Easy.format(xmin, 3), xmin, -0.07 * N);
            plotStringCenter("x", 0, -0.07 * N);
            plotStringCenter(Easy.format(xmax, 3), xmax, -0.07 * N);
            // draw potential
            double xOld = xmin;
            double yScale = N / (Vmax - Vmin);
            double yOld = (VArray[0] - Vmin) * yScale;
            double dx = (xmax - xmin) / (nPlot - 1);
            setColor("green");
            for (int i = 1; i < nPlot; i++) {
                double x = xOld + dx;
                double y = (VArray[i] - Vmin) * yScale;
                plotLine(xOld, yOld, x, y);
                xOld = x;
                yOld = y;
            }
            plotString("V(x) = 0.5 x^2", xmin, 1.04 * N);
            // draw path
            xOld = outer.x[0];
            yOld = 0;
            setColor("cyan");
            for (int i = 1; i < N; i++) {
                double x = outer.x[i];
                plotLine(xOld, yOld, x, yOld += 1);
                xOld = x;
            }
            plotLine(xOld, yOld, outer.x[0], N);
            // draw atoms
            double rx = 0.01 * (xmax - xmin);
            double ry = 0.01 * N * 5.0 / 4.0;
            setColor("red");
            for (int i = 0; i < N; i++) {
                floodCircle(outer.x[i] - rx, outer.x[i] + rx, i - ry, i + ry);
            }
            floodCircle(outer.x[0] - rx, outer.x[0] + rx, N - ry, N + ry);
            if (mcs == 0)
                return;
            // draw energy
            setColor("orange");
            double y0 = (E0 - Vmin) * yScale;
            plotLine(xmin, y0, xmax, y0);
            // draw probability
            double Pmax = 0;
            for (int i = 0; i < nBins; i++)
                if (Pmax < P[i])
                    Pmax = P[i];
            yScale = 0.8 / Pmax * N;
            xOld = xmin;
            dx = (xmax - xmin) / nBins;
            yOld = P[0] * yScale;
            setColor("magenta");
            for (int i = 1; i < nBins; i++) {
                double x = xOld + dx;
                double y = P[i] * yScale;
                plotLine(xOld, yOld, x, yOld);
                plotLine(x, yOld, x, y);
                xOld = x;
                yOld = y;
            }
            plotStringCenter("Probability", 0, 1.04 * N);
            // draw wave function
            xOld = xmin;
            yScale = N / (3 * phi0Max);
            yOld = y0 + phi0[0] * yScale;
            setColor("blue");
            for (int i = 1; i < nBins; i++) {
                double x = xOld + dx;
                double y = y0 + phi0[i] * yScale;
                plotLine(xOld, yOld, x, y);
                xOld = x;
                yOld = y;
            }
            plotStringLeft("Wave function", xmax, 1.04 * N);
        }
    }

    class Output extends Plot {
        Output () {
            setSize(500, 30);
        }

        public void paint () {
            clear();
            setWindow(0, 100, 0, 3);
            setColor("blue");
            boxArea(0, 100, 0, 3);
            setColor("white");
            if (mcs > nequil)
                plotString("MC step: " + (mcs - nequil), 5, 1);
            else
                plotString("Thermalizing " + mcs, 5, 1);
            plotStringCenter("E_0 = " + Easy.format(E0, 5), 50, 1);
            plotStringLeft("T = " + Easy.format(1 / tau, 3), 95, 1);
        }
    }

    Graph graph;
    Output output;
    Reader NReader, tauReader, xminReader, xmaxReader, dxReader;
    Reader deltaReader, nequilReader, skipReader;
    int skip;

    public void init () {
        initial();
        add(graph = new Graph());
        add(output = new Output());
        add(NReader = new Reader("N = ", N));
        add(tauReader = new Reader("tau = ", tau, 4));
        add(deltaReader = new Reader("delta = ", delta, 4));
        add(xminReader = new Reader("xmin = ", xmin, 4));
        add(xmaxReader = new Reader("xmax = ", xmax, 4));
        add(dxReader = new Reader("dx = ", dx, 4));
        add(nequilReader = new Reader("Therm steps", nequil));
        add(skipReader = new Reader("Skip steps", skip));
        addControlPanel();
    }

    public void step () {
        for (int s = 0; s <= skip; s++) {
            MonteCarloStep();
            if (mcs == nequil)
                zeroProbability();
        }
        graph.repaint();
        output.repaint();
    }

    public void reset () {
        N = NReader.readInt();
        tau = tauReader.readDouble();
        delta = deltaReader.readDouble();
        xmin = xminReader.readDouble();
        xmax = xmaxReader.readDouble();
        dx = dxReader.readDouble();
        nequil = nequilReader.readInt();
        skip = skipReader.readInt();
        initial();
        graph.repaint();
        output.repaint();
    }

    PathIntegralMC outer;       // provide access to inner classes
    public PathIntegralMC () {
        super();
        outer = this;
    }

    public static void main (String[] args) {
        PathIntegralMC pimc = new PathIntegralMC();
        pimc.frame("Path Integral Monte Carlo", 530, 650);
    }
}


UB Physics Home Questions or comments: phygons@acsu.buffalo.edu