This is an archival dump of old wiki content --- see scipy.org for current material.
Please see http://scipy-cookbook.readthedocs.org/

Attachment 'tutorial_lokta-voltera_v3.py'

Download

   1 # This example describe how to integrate ODEs with scipy.integrate module, and how
   2 # to use the matplotlib module to plot trajectories, directions field and others
   3 # useful informations.
   4 # 
   5 # == Presentation of the Lokta-Volterra Model ==
   6 # 
   7 # We will have a look at the Lokta-Volterra model, also known as the
   8 # predator-prey equations, which are a pair of first order, non-linear, differential
   9 # equations frequently used to describe the dynamics of biological systems in
  10 # which two species interact, one a predator and one its prey. They were proposed
  11 # independently by Alfred J. Lotka in 1925 and Vito Volterra in 1926 :
  12 # du/dt =  a*u -   b*u*v
  13 # dv/dt = -c*v + d*b*u*v 
  14 # 
  15 # with the following notations :
  16 # 
  17 # *  u : number of prey (for example rabbits)
  18 # 
  19 # *  v : number of predators (for example foxes)  
  20 #   
  21 # * a, b, c, d are constant parameters defining the behavior of the population :    
  22 # 
  23 #   + a is the natural growing rate of rabbits, when there's no foxes
  24 # 
  25 #   + b is the natural dying rate of rabbits, due to predation
  26 # 
  27 #   + c is the natural dying rate of foxes, when there's no rabbits
  28 # 
  29 #   + d is the factor descibing how many catched rabbits let create new rabbits
  30 # 
  31 # 
  32 # We will use X=[u, v] to describe the state of both populations.
  33 # 
  34 # Definition of the equations:
  35 # 
  36 from numpy import *
  37 import pylab as p
  38 
  39 # Definition of parameters 
  40 a = 1.
  41 b = 0.1
  42 c = 1.5
  43 d = 0.75
  44 
  45 def dX_dt(X, t=0):
  46     """ Return the growth rate of foxes and rabbits populations. """
  47     return array([ a*X[0] -   b*X[0]*X[1] ,  
  48                   -c*X[1] + d*b*X[0]*X[1] ])
  49 # 
  50 # === Population equilibrium ===
  51 # 
  52 # Before using scipy to integrate this system, we will have a closer look on 
  53 # position equilibrium. Equilibrium occurs when the growth rate is equal to 0.
  54 # This gives two fixed points:
  55 # 
  56 X_f0 = array([     0. ,  0.])
  57 X_f1 = array([ c/(d*b), a/b])
  58 all(dX_dt(X_f0) == zeros(2) ) and all(dX_dt(X_f1) == zeros(2)) # => True 
  59 # 
  60 # === Stability of the fixed points ===
  61 # Near theses two points, the system can be linearized :
  62 # dX_dt = A_f*X where A is the Jacobian matrix evaluated at the corresponding point.
  63 # We have to define the Jacobian matrix:
  64 # 
  65 def d2X_dt2(X, t=0):
  66     """ Return the jacobian matrix evaluated in X. """
  67     return array([[a -b*X[1],   -b*X[0]     ],
  68                   [b*d*X[1] ,   -c +b*d*X[0]] ])  
  69 # 
  70 # So, near X_f0, which represents the extinction of both species, we have:
  71 # A_f0 = d2X_dt2(X_f0)                    # >>> array([[ 1. , -0. ],
  72 #                                         #            [ 0. , -1.5]])
  73 # 
  74 # Near X_f0, the number of rabbits increase and the population of foxes decrease.
  75 # The origin is a [http://en.wikipedia.org/wiki/Saddle_point saddle point].
  76 # 
  77 # Near X_f1, we have :
  78 A_f1 = d2X_dt2(X_f1)                    # >>> array([[ 0.  , -2.  ],
  79                                         #            [ 0.75,  0.  ]])
  80 
  81 # whose eigenvalues are +/- sqrt(c*a).j :
  82 lambda1, lambda2 = linalg.eigvals(A_f1) # >>> (1.22474j, -1.22474j)
  83 
  84 # They are imaginary number, so the fox and rabbit populations are periodic and
  85 # their period is given by :
  86 T_f1 = 2*pi/abs(lambda1)                # >>> 5.130199
  87 #         
  88 # == Integrating the ODE using scipy.integate ==
  89 # 
  90 # Now we will use the scipy.integrate module to integrate the ODE.
  91 # This module offers a method named odeint, very easy to use to integrade ODE:
  92 # 
  93 from scipy import integrate
  94 
  95 t = linspace(0, 15,  1000)              # time
  96 X0 = array([10, 5])                     # initials conditions: 10 rabbits and 5 foxes  
  97 
  98 X, infodict = integrate.odeint(dX_dt, X0, t, full_output=True)
  99 infodict['message']                     # >>> 'Integration successful.'
 100 # 
 101 # `infodict` is optionnal, and you can omit the `full_output` argument if you don't want it.
 102 # type "info(odeint)" if you want more information about odeint inputs and outputs.
 103 # 
 104 # We can now use Matplotlib to plot the evolution of both populations:
 105 # 
 106 rabbits, foxes = X.T
 107 
 108 f1 = p.figure()
 109 p.plot(t, rabbits, 'r-', label='Rabbits')
 110 p.plot(t, foxes  , 'b-', label='Foxes')
 111 p.grid()
 112 p.legend(loc='best')
 113 p.xlabel('time')
 114 p.ylabel('population')
 115 p.title('Evolution of fox and rabbit populations')
 116 f1.savefig('rabbits_and_foxes_1.png')
 117 # 
 118 # 
 119 # The populations are indeed periodic, and their period is near to the T_f1 we calculated.
 120 # 
 121 # == Plotting directions field and trajectories in the phase plane ==
 122 # 
 123 # We will plot some trajectories in a phase plane for different starting
 124 # points between X__f0 and X_f1.
 125 # 
 126 # We will ue matplotlib's colormap to define colors for the trajectories.
 127 # These colormaps are very useful to make nice plots.
 128 # Have a look at [http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps ShowColormaps] if you want more information.
 129 # 
 130 values = linspace(0.3, 0.9, 5)                          # position of X0 between X_f0 and X_f1
 131 vcolors = p.cm.autumn_r(linspace(0.3, 1., len(values)))   # colors for each trajectory
 132 
 133 f2 = p.figure()
 134 
 135 #-------------------------------------------------------
 136 # plot trajectories
 137 for v, col in zip(values, vcolors): 
 138     X0 = v * X_f1                               # starting point
 139     X = integrate.odeint( dX_dt, X0, t)         # we don't need infodict here
 140     p.plot( X[:,0], X[:,1], lw=3.5*v, color=col, label='X0=(%.f, %.f)' % ( X0[0], X0[1]) )
 141 
 142 #-------------------------------------------------------
 143 # define a grid and compute direction at each point
 144 ymax = p.ylim(ymin=0)[1]                        # get axis limits
 145 xmax = p.xlim(xmin=0)[1] 
 146 nb_points   = 20                      
 147 
 148 x = linspace(0, xmax, nb_points)
 149 y = linspace(0, ymax, nb_points)
 150 
 151 X1 , Y1  = meshgrid(x, y)                       # create a grid
 152 DX1, DY1 = dX_dt([X1, Y1])                      # compute growth rate on the gridt
 153 M = (hypot(DX1, DY1))                           # Norm of the growth rate 
 154 M[ M == 0] = 1.                                 # Avoid zero division errors 
 155 DX1 /= M                                        # Normalize each arrows
 156 DY1 /= M                                  
 157 
 158 #-------------------------------------------------------
 159 # Drow direction fields, using matplotlib 's quiver function
 160 # I choose to plot normalized arrows and to use colors to give information on
 161 # the growth speed
 162 p.title('Trajectories and direction field')
 163 Q = p.quiver(X1, Y1, DX1, DY1, M, pivot='mid', cmap=p.cm.jet)
 164 p.xlabel('Number of Rabbits')
 165 p.ylabel('Number of Foxes')
 166 p.legend()
 167 p.grid()
 168 p.xlim(0, xmax)
 169 p.ylim(0, ymax)
 170 f2.savefig('rabbits_and_foxes_2.png')
 171 # 
 172 # 
 173 # We can see on this graph that an intervention on fox or rabbit populations can
 174 # have non intuitive effects. If, in order to decrease the number of rabbits,
 175 # we introduce foxes, this can lead to an increase of rabbits in the long run,
 176 # if that intervention happens at a bad moment.
 177 # 
 178 # 
 179 # == Plotting contours ==
 180 # 
 181 # We can verify that the fonction IF defined below remains constant along a trajectory:
 182 # 
 183 def IF(X):
 184     u, v = X
 185     return u**(c/a) * v * exp( -(b/a)*(d*u+v) )
 186 
 187 # We will verify that IF remains constant for differents trajectories
 188 for v in values: 
 189     X0 = v * X_f1                               # starting point
 190     X = integrate.odeint( dX_dt, X0, t)         
 191     I = IF(X.T)                                 # compute IF along the trajectory
 192     I_mean = I.mean()
 193     delta = 100 * (I.max()-I.min())/I_mean
 194     print 'X0=(%2.f,%2.f) => I ~ %.1f |delta = %.3G %%' % (X0[0], X0[1], I_mean, delta)
 195 
 196 # >>> X0=( 6, 3) => I ~ 20.8 |delta = 6.19E-05 %
 197 #     X0=( 9, 4) => I ~ 39.4 |delta = 2.67E-05 %
 198 #     X0=(12, 6) => I ~ 55.7 |delta = 1.82E-05 %
 199 #     X0=(15, 8) => I ~ 66.8 |delta = 1.12E-05 %
 200 #     X0=(18, 9) => I ~ 72.4 |delta = 4.68E-06 %
 201 # 
 202 # Potting iso-contours of IF can be a good representation of trajectories,
 203 # without having to integrate the ODE
 204 # 
 205 #-------------------------------------------------------
 206 # plot iso contours
 207 nb_points = 80                              # grid size 
 208 
 209 x = linspace(0, xmax, nb_points)    
 210 y = linspace(0, ymax, nb_points)
 211 
 212 X2 , Y2  = meshgrid(x, y)                   # create the grid
 213 Z2 = IF([X2, Y2])                           # compute IF on each point
 214 
 215 f3 = p.figure()
 216 CS = p.contourf(X2, Y2, Z2, cmap=p.cm.Purples_r, alpha=0.5)
 217 CS2 = p.contour(X2, Y2, Z2, colors='black', linewidths=2. )
 218 p.clabel(CS2, inline=1, fontsize=16, fmt='%.f')
 219 p.grid()
 220 p.xlabel('Number of Rabbits')
 221 p.ylabel('Number of Foxes')
 222 p.ylim(1, ymax)
 223 p.xlim(1, xmax)
 224 p.title('IF contours')
 225 f3.savefig('rabbits_and_foxes_3.png')
 226 p.show()
 227 # 
 228 # 
 229 # # vim: set et sts=4 sw=4:

New Attachment

File to upload
Rename to
Overwrite existing attachment of same name

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.