This is the first problem of the AT2, it goes through the very basics of what you will have to learn during this class. We will create a script, with an array, a loop and some basic plotting. Try and get used to writing down answers documented by figures in any of your favorite word processor. This is the most important part of the project reports grading you will have to hand in.
# /!\ I'm using Python 3
import plotly
import numpy as np
import plotly.plotly as py
import plotly.graph_objs as go
import matplotlib.pyplot as plt
import scipy.io as sio
from scipy import stats
%matplotlib inline
plotly.offline.init_notebook_mode(connected=True)
from IPython.core.display import display, HTML, Markdown
# The polling here is to ensure that plotly.js has already been loaded before
# setting display alignment in order to avoid a race condition.
display(HTML(
'<script>'
'var waitForPlotly = setInterval( function() {'
'if( typeof(window.Plotly) !== "undefined" ){'
'MathJax.Hub.Config({ SVG: { font: "STIX-Web" }, displayAlign: "center" });'
'MathJax.Hub.Queue(["setRenderer", MathJax.Hub, "SVG"]);'
'clearInterval(waitForPlotly);'
'}}, 250 );'
'</script>'
))
We will try to simulate a population of animals growing. We start with a fixed number of animals $N_0 = 2$, and we will model the growth yearly over a hundred years period. First, create a script.
Now for the growth model, let's name $p_n$ the population at year $n$. It is safe to assume that the population will grow by a factor of itself, that is, the bigger the population, the bigger the growth. So the number of new individuals born any given year should look something like $αp$, if $p$ is the population that year. Let us set $α$ to $0.1$ for now. Noting the current year $n - 1$, this means will try to model the following map:
$$p_n = p_{n-1} + 0.1 p_{n-1}$$
p
, meant to hold on to the population values every year.¶N_0 = 2
alpha = 0.1
# to be consistent with what follows,
# let's set the array size to 100
array_size = 101
p = np.zeros(array_size)
p[0]
to the initial population value.¶p[0] = N_0
p[1]
.¶def compute_value(n, p, alpha=alpha, N_0=N_0):
if n==0:
p[0] = N_0
else:
p[n] = 1.1*p[n-1]
compute_value(1, p)
print(p[1])
p[1]
that you did before, to compute the remaining p[2]
, . . . p[100]
values¶for i in range(2, 101):
compute_value(i, p)
data = go.Scatter(
x = list(range(1, len(p)+1)),
y = p,
mode = 'lines',
name = '$p_n$'
)
layout= go.Layout(
title= 'Evolution of the population',
hovermode= 'closest',
xaxis= dict(
title= '$\\text{Year } \, n$',
ticklen= 5,
zeroline= False,
gridwidth= 2,
),
yaxis=dict(
title= '$\\text{Number of animals } \; p_n$',
ticklen= 5,
gridwidth= 2,
),
showlegend= True
)
plotly.offline.iplot(go.Figure(data=[data], layout=layout))
It's diverging (the number of animals goes up to infinity). It was predictable, since the sequence $(p_n)_{n∈ℕ}$ is divergent:
$$p_n ≝ N_0 × 1.1^n \underset{n → +∞}{⟶} +∞$$
def compute_array(alpha=alpha, N_0=N_0, n_max=array_size):
p = [N_0]
for _ in range(array_size-1):
p.append(p[-1]*(1+alpha))
return np.array(p)
# Colorscales
def colorscale_list(cmap, number_colors, return_rgb_only=False):
cm = plt.get_cmap(cmap)
colors = [np.array(cm(i/number_colors)) for i in range(1, number_colors+1)]
rgb_colors_plotly = []
rgb_colors_only = []
for i, c in enumerate(colors):
col = 'rgb{}'.format(tuple(255*c[:-1]))
rgb_colors_only.append(col)
rgb_colors_plotly.append([i/number_colors, col])
rgb_colors_plotly.append([(i+1)/number_colors, col])
return rgb_colors_only if return_rgb_only else rgb_colors_plotly
display(Markdown("### For different values of `alpha`"))
number_lines = 10
legend_every = 1
value_min, value_max = .01, .1
colors = colorscale_list('Reds', number_lines+3, return_rgb_only=True)
# Plotting the evolution of the population
traces_alpha = []
for i, alpha in enumerate(np.linspace(value_min, value_max, number_lines)):
traces_alpha.append(
go.Scatter(
x = list(range(1, array_size+1)),
y = compute_array(alpha=alpha),
mode = 'lines',
name = 'alpha = {}'.format(alpha),
line = dict(
width = 3,
color = colors[i+2],
shape = 'spline',
dash = 'solid'
),
hoverlabel = dict(
namelength = -1
),
showlegend = (i % legend_every == 0)
)
)
plotly.offline.iplot(go.Figure(data=traces_alpha, layout=layout))
The population number diverges very quickly when alpha
increases. Nothing abnormal: the sequence $(p_n)_{n∈ℕ}$ is an exponential one, of ratio $1 + α$, so that:
$$p_n = N_0 × (1+α)^n$$
display(Markdown("### For different values of `N_0`, for `alpha = {}`".format(alpha)))
number_lines = 5
legend_every = 1
value_min, value_max = 1, 5
colors = colorscale_list('Greens', number_lines+3, return_rgb_only=True)
# Plotting the evolution of the population
traces_N_0 = []
for i, N_0 in enumerate(np.linspace(value_min, value_max, number_lines)):
traces_N_0.append(
go.Scatter(
x = list(range(1, array_size+1)),
y = compute_array(N_0=N_0),
mode = 'lines',
name = 'N_0 = {}'.format(N_0),
line = dict(
width = 3,
color = colors[i+2],
shape = 'spline',
dash = 'solid'
),
hoverlabel = dict(
namelength = -1
),
showlegend = (i % legend_every == 0)
)
)
plotly.offline.iplot(go.Figure(data=traces_N_0, layout=layout))
$p_n = N_0 × (1+α)^n$ increases at such an enormous rate with respect to $α$ that $N_0$ hardly has any impact, as compared to the exponential growth.
Of course our model is very naive, and it seems that we could refine it a little bit. Let's consider the general problem of resources, our population now lives in an environment where resources are limited. A good way of modeling that is simply to modulate the growth factor. This means that we will replace our $α$ by some function of the population. We choose to write $α = 200 − p$.
0
and 500
individuals.¶alpha_0 = 200
data_modulated = go.Scatter(
x = np.arange(501),
y = 200-np.arange(501),
mode = 'lines',
name = '$α$'
)
layout_modulated = go.Layout(
title= 'Evolution of $α ≝ 200 - p$',
hovermode= 'closest',
xaxis= dict(
title= 'Population number',
ticklen= 5,
zeroline= False,
gridwidth= 2,
),
yaxis=dict(
title= '$α$',
ticklen= 5,
gridwidth= 2,
),
showlegend= True
)
plotly.offline.iplot(go.Figure(data=[data_modulated], layout=layout_modulated))
As soon as $α < 0$, the population number will be decreasing, since the growth rate will be stricly lower than $1$.
$$p_n = p_{n-1} + \underbrace{0.001}_{≝ \, β} \, p_{n-1} (\underbrace{200}_{≝ \, γ} - p_{n-1})$$
def compute_new_behavior(beta=0.001, gamma=200, N_0=N_0, n_max=array_size):
p = [N_0]
for _ in range(array_size-1):
p.append(p[-1]*(1 + beta*(gamma - p[-1])))
return np.array(p)
data = go.Scatter(
x = list(range(1, array_size+1)),
y = compute_new_behavior(),
mode = 'lines',
name = '$p_n$'
)
plotly.offline.iplot(go.Figure(data=[data], layout=layout))
There is a plateau: the population number converges toward a maximum value, which is more realistic.
display(Markdown("## Parameter values leading to a monotonous evolution"))
number_lines = 10
legend_every = 1
value_min, value_max = .01, .1
parameters = {'beta': {'args': {'beta':0.001, 'gamma':200, 'N_0':2},
'nb_lines': 10, 'val_min':0.001, 'val_max':0.005,
'legend_every': 1, 'color': 'Reds'},
'gamma': {'args': {'beta':0.001, 'gamma':200, 'N_0':2},
'nb_lines': 10, 'val_min':200, 'val_max':1000,
'legend_every': 1, 'color': 'Blues'},
'N_0': {'args': {'beta':0.001, 'gamma':200, 'N_0':2},
'nb_lines': 10, 'val_min':1, 'val_max':10,
'legend_every': 1, 'color': 'Greens'}
}
traces = {}
for param in ['beta', 'gamma', 'N_0']:
display(Markdown("### For different values of {}".format(param)))
number_lines = parameters[param]['nb_lines']
legend_every = parameters[param]['legend_every']
value_min, value_max = parameters[param]['val_min'], parameters[param]['val_max']
colors = colorscale_list(parameters[param]['color'], number_lines+3, return_rgb_only=True)
args = parameters[param]['args']
# Plotting the evolution of the population
traces[param] = []
for i, par in enumerate(np.linspace(value_min, value_max, number_lines)):
args[param] = par
traces[param].append(
go.Scatter(
x = list(range(1, array_size+1)),
y = compute_new_behavior(**args),
mode = 'lines',
name = '{} = {}'.format(param, par),
line = dict(
width = 3,
color = colors[i+2],
shape = 'spline',
dash = 'solid'
),
hoverlabel = dict(
namelength = -1
),
showlegend = (i % legend_every == 0)
)
)
plotly.offline.iplot(go.Figure(data=traces[param], layout=layout))
display(Markdown("## Parameter values, some of which asymptotically lead to a sinusoidal evolution"))
number_lines = 10
legend_every = 1
value_min, value_max = .01, .1
parameters = {'beta': {'args': {'beta':0.001, 'gamma':200, 'N_0':2},
'nb_lines': 22, 'val_min':0.001, 'val_max':0.011,
'legend_every': 2, 'color': 'Reds'},
'gamma': {'args': {'beta':0.001, 'gamma':200, 'N_0':2},
'nb_lines': 20, 'val_min':200, 'val_max':2000,
'legend_every': 2, 'color': 'Blues'},
'N_0': {'args': {'beta':0.001, 'gamma':200, 'N_0':2},
'nb_lines': 20, 'val_min':1, 'val_max':20,
'legend_every': 2, 'color': 'Greens'}
}
traces = {}
for param in ['beta', 'gamma', 'N_0']:
display(Markdown("### For different values of {}".format(param)))
number_lines = parameters[param]['nb_lines']
legend_every = parameters[param]['legend_every']
value_min, value_max = parameters[param]['val_min'], parameters[param]['val_max']
colors = colorscale_list(parameters[param]['color'], number_lines+3, return_rgb_only=True)
args = parameters[param]['args']
# Plotting the evolution of the population
traces[param] = []
for i, par in enumerate(np.linspace(value_min, value_max, number_lines)):
args[param] = par
traces[param].append(
go.Scatter(
x = list(range(1, array_size+1)),
y = compute_new_behavior(**args),
mode = 'lines',
name = '{} = {}'.format(param, par),
line = dict(
width = 3,
color = colors[i+2],
shape = 'spline',
dash = 'solid'
),
hoverlabel = dict(
namelength = -1
),
showlegend = (i % legend_every == 0)
)
)
plotly.offline.iplot(go.Figure(data=traces[param], layout=layout))
It appears that:
for $β ≤ 0.005, γ ≤ 1000$ and for any $N_0 ∈ ℕ$: the population number functions $n \mapsto p_n$ increase when the parameter ($β, α$ or $N_0$) does so, and each of these functions is increasing and monotonous.
for $β > 0.005, γ >> 1000$: the geater the parameter, the bigger the oscillations of the population number functions, to such a point that it ends up being asymptotically sinusoidal (with ever-increasing oscillations, as $β$ and $γ$ increase).