Python

Discrete Lotka-Volterra mean-field model

Python

: fish, : sharks.

A fish avoids one shark with probability . Assuming independent encounters, avoiding all sharks has probability .

So the probability of being eaten is

and the expected number eaten is .

: fish growth per step. : shark death rate. : conversion from eaten fish to new sharks.

Both updates use the populations at time .

For small , , giving the usual interaction term.

Mean-field model: fractional populations, no random draws or carrying capacity. It gives the expected change from a given state, not necessarily the average of full stochastic trajectories.

Python
Python
Python

Computing the mean field

A sum of independent Bernoulli trials with success probability is binomial, with expectation . Given the current populations , write . The event counts have conditional means

The nested expectation matters: new sharks are drawn from the number actually eaten, , not directly from all fish.

Substitute these means into and :

These are the exact expected increments from a fixed state. Iterating them gives the deterministic mean-field curve. It is not generally the average stochastic trajectory because

in general. The nonlinear predation term depends on the joint distribution of fish and sharks. The stochastic interpretation also requires .

Python
Python
Python

Discrete Lotka-Volterra stochastic simulation

Same initial conditions and parameters as above. Now we go over the animals individually.

For each generation: start with , .

For each fish currently in the sea:

  • have a child? random.uniform(0, 1) < alpha → add 1 to
  • get eaten? another draw, smaller than → subtract 1 from
  • if eaten, does this produce a new shark? draw smaller than → add 1 to

For each shark:

  • die? draw smaller than → subtract 1 from

Then update:

We loop over the populations at time . Newborns wait until the next generation. A fish can have a child and then get eaten in the same step.

The predation draw combines the independent shark encounters: is the probability of avoiding all sharks.

Integer populations, probabilities in . Same seed → same random sequence.

Python
Python
Python
Python
Python

Issue with this simulation is how often we are drawing a random number. Instead of for each fish, drawing a random number as their probability of child, just draw a single random number and apply as % of all fish that succeed. Sum of bernouli trials? binomial distribution!

Counting successes directly

Each fish has a child with probability . For one fish, this is a Bernoulli trial:

How many fish have children in total?

So we can replace the whole birth loop with rng.binomial(fish, alpha). This returns a count, not a percentage.

A uniform percentage would give the wrong distribution. For , , we expect 5 births, with variance . Most draws should be near 5, not spread evenly from 0 to 100.

Same idea for the other events:

Draw the number eaten before drawing new sharks. The shark birth trial count is , not .

Four binomial draws per generation. Same transition probabilities as the individual loops, fewer Python calls. The same seed won’t give the same path across the two implementations: they consume random numbers differently.

Python
Python
Python
Python