Search code examples
javascriptdifferential-equationsmath.js

Can't get Lotka-Volterra equations to oscillate stable with math.js


I'm trying to implement a simple Lotka-Volterra system in JavaScript, but get different result from what I see in academic papers and slides. This is my equations:

sim2.eval("dxdt(x, y) = (2 * x) - (x * y)");
sim2.eval("dydt(x, y) = (-0.25 * y) + (x * y)");

using coefficients a = 2, b = 1, c = 0.25 and d = 1. Yet, my result looks like this:

enter image description here

when I expected a stable oscillation as seen in these PDF slides:

enter image description here

Could it be the implementation of ndsolve that causes this? Or a machine error in JavaScript due to floating-point arithmetic?


Solution

  • For serious purposes use a higher order method, the minimum is fixed step classical Runge-Kutta. Then you can also use dt=0.1, it is stable for multiple periods, I tried tfinal=300 without problems. However you will see the step size in the graph as it is visibly piecewise linear. This is much reduced with half the step size, dt=0.05.

    function odesolveRK4(f, x0, dt, tmax) {
      var n = f.size()[0];  // Number of variables
      var x = x0.clone(),xh=[];   // Current values of variables
      var dxdt = [], k1=[], k2=[], k3=[], k4=[];        // Temporary variable to hold time-derivatives
      var result = [];      // Contains entire solution
    
      var nsteps = math.divide(tmax, dt);   // Number of time steps
      dt2 = math.divide(dt,2);
      dt6 = math.divide(dt,6);
      for(var i=0; i<nsteps; i++) {
        // compute the 4 stages if the classical order-4 Runge-Kutta method
        k1 = f.map(function(fj) {return fj.apply(null, x.toArray()); } );
        xh = math.add(x, math.multiply(k1, dt2));
        k2 = f.map(function(fj) {return fj.apply(null, xh.toArray()); } ); 
        xh = math.add(x, math.multiply(k2, dt2));
        k3 = f.map(function(fj) {return fj.apply(null, xh.toArray()); } ); 
        xh = math.add(x, math.multiply(k3, dt));
        k4 = f.map(function(fj) {return fj.apply(null, xh.toArray()); } ); 
        x = math.add(x, math.multiply(math.add(math.add(k1,k4), math.multiply(math.add(k2,k3),2)), dt6))
    
        if( 0==i%50) console.log("%3d %o %o",i,dt,x.toString());
        result.push(x.clone());
      }
    
      return math.matrix(result);
    }
    
    math.import({odesolveRK4:odesolveRK4});