Interactive PowerBasic Forum

IT-Consultant: Charles Pegge => OxygenBasic Examples => Topic started by: Theo Gottwald on April 13, 2024, 09:38:43 AM

Title: Metropolis-Hastings algorithm for estimating pi that includes graphics
Post by: Theo Gottwald on April 13, 2024, 09:38:43 AM
Here's an updated version of the Metropolis-Hastings algorithm for estimating pi that includes graphics:

' Metropolis-Hastings Algorithm for Pi Estimation with Graphics
' Oxygen Basic, ConsoleG, OpenGL

#compact
%filename "metropolis_pi_graphics.exe"
'uses RTL64
% Title "Metropolis-Hastings Algorithm for Pi Estimation with Graphics"

'% WindowStyle WS_OVERLAPPEDWINDOW
'% Animated
'% ScaleUp
% PlaceCentral
% AnchorCentral

% shaders

uses consoleG

BeginScript

const int NUM_STEPS = 1000000;
const float RADIUS = 1.0;
const float PROPOSAL_SD = 0.1;

int acceptCount = 0;

procedure metropolisHastings() {
    float x = 0.0;
    float y = 0.0;
    float proposalX, proposalY, distSq, proposalDistSq, acceptProb;

    for (int i = 0; i < NUM_STEPS; i++) {
        // Generate a proposal point
        proposalX = x + rndn() * PROPOSAL_SD;
        proposalY = y + rndn() * PROPOSAL_SD;
        proposalDistSq = proposalX*proposalX + proposalY*proposalY;

        // Calculate the acceptance probability
        distSq = x*x + y*y;
        if (distSq > RADIUS*RADIUS && proposalDistSq <= RADIUS*RADIUS) {
            acceptProb = 1.0;
        } else if (distSq <= RADIUS*RADIUS && proposalDistSq > RADIUS*RADIUS) {
            acceptProb = (RADIUS*RADIUS / proposalDistSq);
        } else {
            acceptProb = 1.0;
        }

        // Accept or reject the proposal point
        if (rnd < acceptProb) {
            x = proposalX;
            y = proposalY;
            acceptCount++;
        }

        // Draw the current point
        pushstate();
        translate(x, y, 0);
        scale(0.01, 0.01, 0.01);
        color(1, 0, 0);
        go sphere();
        popstate();
    }
}

procedure drawCircle() {
    glBegin(GL_LINE_LOOP);
    for (int i = 0; i <= 360; i++) {
        float angle = i * PI / 180.0;
        float x = RADIUS * cos(angle);
        float y = RADIUS * sin(angle);
        glVertex2f(x, y);
    }
    glEnd();
}

procedure main() {
    cls();
    shading();

    drawCircle();

    metropolisHastings();

    print("Pi estimate: ");
    println(4.0 * acceptCount / NUM_STEPS);

    waitkey();
}

EndScript

In this version, we've added a `drawCircle` procedure that draws a circle with radius `RADIUS` centered at the origin. We've also added code to draw a small red sphere at each accepted point in the `metropolisHastings` procedure.

When you run this program, you should see a circle centered at the origin and a cloud of red points inside the circle. The cloud of points represents the accepted proposals in the Metropolis-Hastings algorithm, and should be roughly uniformly distributed inside the circle. The estimate of pi is calculated as `4.0 * acceptCount / NUM_STEPS`, where `acceptCount` is the number of accepted proposals and `NUM_STEPS` is the total number of steps in the random walk.

You can experiment with different values of `NUM_STEPS` and `PROPOSAL_SD` to see how they affect the accuracy of the estimate and the appearance of the graph. Note that larger values of `NUM_STEPS` will result in a more accurate estimate, but will also take longer to run.