Lotka-Volterra equations
The Lotka-Volterra equations, also known as the predator-prey equations, are a model for the dynamics of two interacting species in an ecosystem. This model takes the form of two coupled non-linear differential equations.
The number of prey
and the number of predators
are functions of time. For mathematical convience (the model goes back to the early 20th century, before digital computers), they are represented by real numbers rather than integers, as this allows the use of differential equations instead of the clumsier difference equations.
The time evolution of prey and predator populations is given by
where
The term
The term
The term
The term
Numerical simulation: Euler method
A simple numerical simulation can be performed by applying the Euler method. It computes the values of approximations
Given initial values
Example
Note: this example is taken from the Wikipedia entry cited above.
Suppose there are two species of animals, a baboon (prey) and a cheetah (predator). If the initial conditions are
The Euler method yield two recurrence equations, RESequence
(see Solving recurrence equations):
sequence1 := RESequence context: (thisSnippet page lzSubcontext: #example) rules: #('lvx/euler' 'lvy/euler') parameters: { 'Δt ⇒ 1/10' } initialValues: (Dictionary with: 'x≅e' -> #(10.0) with: 'y≅e' -> #(10.0)) indexVariable: 't' stepSize: 'Δt'. sequence1 addSteps: 500.
Before trying to interpret those results, let's be cautious. These curves have sharp peaks, which are always suspicious in the context of numerical solutions. Is our time step small enough? Is our discretization (the Euler method) precise enough? If the answer to one of these questions is "no", then we could be looking at numerical artifacts. Let's try again with a smaller step size:
sequence2 := RESequence context: (thisSnippet page lzSubcontext: #example) rules: #('lvx/euler' 'lvy/euler') parameters: { 'Δt ⇒ 1/100' } initialValues: (Dictionary with: 'x≅e' -> #(10.0) with: 'y≅e' -> #(10.0)) indexVariable: 't' stepSize: 'Δt'. sequence2 addSteps: 5000.
This looks rather different! The increase of the population peaks from one cycle to the next looks like a numerical artifact. This is in fact a well-known issue with the Lotka-Volterra equations, as explained e.g. in this blog post.
Numerical simulation: the symplectic Euler method
A common strategy for stabilizing numerical solutions is to switch to an
implicit
integration scheme, in which the quantities on the right-hand side are evaluated at the
end
of the step rather than at the
beginning
. Doing this would result in the
implicit Euler method
. For the specific case of the Lotka-Volterra equations, which are a Hamiltonian system, the best approach is the semi-implicit Euler method, also known as the
symplectic
Euler method, which consists of treating one variable (we choose
We define the solutions obtained with the symplectic Euler method as
Back to the example
These equations yield the expected periodic cycles:
sequence3 := RESequence context: (thisSnippet page lzSubcontext: #example) rules: #('lvx/symplectic' 'lvy/symplectic') parameters: { 'Δt ⇒ 1/10' } initialValues: (Dictionary with: 'x≅s' -> #(10.0) with: 'y≅s' -> #(10.0)) indexVariable: 't' stepSize: 'Δt'. sequence3 addSteps: 500.
It is also interesting to plot
sequence3 parametricPlot: 'x≅s' with: 'y≅s'