Chapter 2

Random Walker


2.1 Phenomena

Attempting to capture the randomness we see around us in a finatry, computational world is of deep interest. This does not disclude motion simulation. From trying to parameterize microbial environments to understand motion of bacteria1, to creating a background scene of moths swarming around a light source in the night, the ability to produce randomness (or more precisely pseudo-randomness) is of vast use from the microscopic to the macroscopic world.

In motion phenomena, when one does not know of all the conditions that lead to a certain outcome, or one simply cannot model all the parameters - randomness is often introduced as a substitute. For example: modelling passing of genetics from parents. It is usually modelled as randomized selection of traits from either parent and changing the traits by slight perturbations - a substitute for evolution.

Therefore modelling and understanding how randomness is simulated is a crucial step in moving towards more complex natural phenomena. In this project, we will do this by trying to simulate different kinds of random walks - with varying underlying distributions and parameters.

2.2 Modelling

All things related to representation and motion will remain the same in terms of modelling for this chapter (and the remainder of the project). The only difference of a deterministic motion phenomena to a stochastic one, is that the position of our mover (or ‘walker’ in this chapter) will be updated randomly - according to some probability distribution, as compared to external forces in the environment.2 Therefore the update() method now will look like

update(){  
    applied quantity = rand_function(p1, p2,...)   
    // Do calculations to go from applied quantity to position  
      
   new pos = 𝑓( applied quantity [, old pos] )  
}

Here, rand_func is either a built-in function - in JS, the p5 library3 4, or a function made by ourselves that samples from particular probability distribution. The applied quantity, is either magnitude or direction of the next step which can be modelled as a velocity vector but not always done so.

Direction: then the angle is randomly generated and we derive velocity from the angle, with some set magnitude.
Magnitude: then the x-coordinates and y-coordinates are randomly generated and added to the current position. Or randomly generate a scalar value to be multiplied to a given velocity

Of course, affecting one parameter of motion usually affects the other and a random walk with constant direction but varying magnitude is not useful (unless in 1D). Hence, picking x and y coords would also usually ensure the direction changes as well. Hence, it is through combining them with different distributions and/or varying them separately (like constant magnitude and varying direction) we create different simulations of a random walk.

2.3 Implementation

2.3.1 Basic random walker

The basic walker is implemented without vectors, and hence, just adding changes to the current position each frame is how we implement it. The change is precisely what our applied quantity is and the random distribution is, uniform over the unit-square.

The rand_func here is random(a,b) returns a uniformly distributed sample from the range (a,b)(a,b)

/* A 2D random walk performed by a point object on a screen (Random Walker)
The Probability distribution taken for the RW is Uniform(-1,1) - separately for the x-value and y-value*/

class Walker {
  //This is the class for the RW
  constructor() {
    //we are placing the walker at the center of the screen
    this.x = width / 2;
    this.y = height / 2;
  }

  display() {
    //Displays the walker at the given location
    stroke(0);
    point(this.x, this.y);
  }

  update() {
    //Changes the location of the walker randomly within a unit square of the current location
    this.x += random(-1, 1);
    this.y += random(-1, 1);
  }
}

let w;

//Global functions inbuilt in p5.js, setup() runs once and draw() runs repeatedly
function setup() {
  createCanvas(700, 500);
  //Creating an instance 'w' of the of the class Walker
  w = new Walker();   
  background(255);
}

function draw() { 
  w.display();
  w.update();
}

And this is the output simulation

Although the implementation is different from expected from the previous section and chapter, the ideas are the same.
The main difference being the representation of our walker is a point - this is because it makes it easier to see the minute changes that happen to the walker each frame
Secondly, the other change is that we do not refresh our background is not refreshed every frame, instead we let the walker's previous position be visible throughout the sketch - all we are doing is just colouring a pixel black if the walker has passed through it. This is a similar adoption to how they are usually represented, although we will eventually switch to our usual motion representation (‘animated’ style) as we won’t be trying to model random walks, but walkers that possess random movements.5

2.3.2 Normal random walker

Rand_func = randomGaussian() - normally distributed with,
mean = current direction and SD = π/8π/8

Applied parameter = direction (through angle of velocity)

class WalkerNormal {
  constructor(x, y, r) {
    this.location = createVector(x, y);
    this.velocity = p5.Vector.random2D();  
    this.r = r;    
    this.D = this.r * 2;                       
    this.velAngle = this.velocity.heading();
		this.col = color(100)		// initialised with the grayscale value 100
  }
  
  display() {
		stroke(0);
    fill(this.col);
    // Displaying the step of the walker using a relatively thin line if step size is large
    // strokeWeight(this.r/3);
    // line(this.location.x - this.velocity.x, this.location.y - this.velocity.y,this.location.x, this.location.y);
    //Displaying the walker as a circle of radius r
    strokeWeight(1);
    circle(this.location.x, this.location.y, this.D);
  }
  
  update(vel_mag=1,chk_edges=false) {
    if (frameCount % 3 === 0) {
      // randomGaussian() returns a random sample from a N(0,1) 
      // Therefore by performing scaling and translation of the distribution we have 
      // the stepAangle is hence distributed according to N(current direction,π/8)
			
      this.velAngle = randomGaussian() * PI / 8 + this.velocity.heading();
			
      // Changing color if change in direction is more than 2 S.Ds (π/4 = 2σ)
      // color is set by choosing random RGB values
      if (abs(this.velAngle - this.velocity.heading()) >= QUARTER_PI) {        
        this.col = color(random(255), random(255), random(255));				
      }
    }
    this.velocity = createVector(cos(this.velAngle), sin(this.velAngle));
    this.velocity.mult(vel_mag);
    this.location.add(this.velocity);
		if (chk_edges){
			this.checkEdges();
		}
  }
  
  checkEdges() {
    //Function to check if the walker has crossed the canvas edges, if so - wrap around
    if (this.location.x > width) {
      this.location.x = 0;
    } else if (this.location.x < 0) {
      this.location.x = width;
    }
    if (this.location.y > height) {
      this.location.y = 0;
    } else if (this.location.y < 0) {
      this.location.y = height;
    }
  }
}

function windowResized(){
  //Function to resize canvas when window is resized - in other words, resize our sketch when windown is resized
  resizeCanvas(windowWidth,windowHeight);
  background(200);
}

let w;

function setup() {
  createCanvas(windowWidth,windowHeight);
	
  w = new WalkerNormal(width / 2, height / 2, 10);
  background(200);
}

function draw() {
  w.display();
  w.update(/*vel_mag*/ 3,/*chk_edgs*/ true); 
}

Extra functionality:

2.3.3 Square-distribution random walker

  class Walker{                                    
    constructor(x, y, r) {
      this.location = createVector(x, y);
      this.velocity = p5.Vector.random2D();  
      this.r = r; 
      this.D = r * 2 
      this.sa = random(0,TWO_PI);    // sa = velAngle
    }
    
    display() {
      stroke(0);
      fill(100);
      // Displaying the step of the walker using a relatively thin line
      strokeWeight(this.r/3);
      line(this.location.x - this.velocity.x, this.location.y - this.velocity.y,this.location.x, this.location.y);
      //Displaying the walker as a circle of radius r
      strokeWeight(1);
      circle(this.location.x, this.location.y, this.D);
    }
    
    
    update(vel_mag=1,chk_edges=false) {
      if (frameCount%3==0){
        this.sa = random(0,TWO_PI);
      }
      this.velocity.set(cos(this.sa), sin(this.sa));
      this.velocity.mult(vel_mag);
      this.location.add(this.velocity);
      if (chk_edges){
        this.checkEdges();
      }
    }
    
    checkEdges() {
      if (this.location.x > width+this.r) {
        this.location.x = this.location.x - (width + this.r);
      } else if (this.location.x < -this.r) {
        this.location.x = width+( this.location.x + this.r);
      }
      if (this.location.y > height+this.r) {
        this.location.y = this.location.y - (height + this.r);
      } else if (this.location.y < -this.r) {
        this.location.y = height + (this.location.y + this.r);
      }
    }
  }

  function windowResized(){
    resizeCanvas(windowWidth,windowHeight);
    background(200);
  }

  function randomSquared(){
    //Function to generate random numbers with a x^2 distribution - using accept reject method
    let x = random();    // random number to be accepted
    let y = random();    // qualifying random number

    // P(y < x^2) is the condition for acceptance
    while (y>x**2){
      x = y;
      y = random();
    }
    return x;
  }

  let w;

  function setup() {
      createCanvas(windowWidth,windowHeight);
      background(200);

      w = new Walker(width / 2, height / 2, 3);
  }

  function draw() {
    w.display();
    let vel_mag =(frameCount%180==0)?randomSquared()*50:3;
    w.move(vel_mag, true);
  }

2.3.4 A System of Random Walkers

Here is a sneek peek simulation into how systems of same entity types can be made. Similar implementation is made in the 5th chapter and breifly explained there.

Refer here for the implementation code

P.S: Drag your mouse across the canvas to spawn new walkers.

Notes

  1. One of many possible future works to pursue
  2. We could draw a correspondence between the two or have both at play in the same time - a varying wind force, is both an external Force and random
  3. In programming languages we first simulate a uniform distribution using algorithms that iterate from a given value (taken from some state variable) using operations (addition, multiplication, mod, bit operators) with large numbers. In p5 it is done through a regular LCG (Linear Congruence Generator).
  4. Then all other common distributions are sampled from the uniform distribution, using the Universality of Uniform distribution - which states that the cumulative distribution of any random variable is uniformly distributed.
  5. The implementation differences occur because it was made during the earlier stages of my thesis. We will address and unify most of our motion related code at 4.3.4 when we compare different types of random walkers.