# Logistic Map
The Logistic Map is an iterated function that is described by a non-linear difference equation:
xt+1 = rxt(1 - xt)
In the difference equation above, x represents the population size at a point in time, t, and r
represents the growth rate.
The Logistic Map is often studied in the context of Chaos Theory, and indeed, it is an archetype for a chaotic system.
It is also an example of a system that exhibits complex behaviour despite consisting of simple rules.
The Logistic Map can be placed into the Network Automaton framework by considering a network with a single node, with a
single self-connection. The node's state represents the population size, x (typically, a number between 0 and 1,
which represents the fraction of maximum population size attainable). The self-connection possesses a value, which is
the growth rate, r. Finally, the rule operating on the network is simply the difference equation given above.
In the following demo, we simulate a Logistic Map with Netomaton. To illustrate how the population changes in response
to different growth rates, we set up a network with a number of disconnected nodes so that we can evolve the system
with different growth rates simultaneously.
```python
import netomaton as ntm
import numpy as np
timesteps = 20
growth_rates = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5]
network = ntm.topology.disconnected(len(growth_rates))
[network.add_edge(i, i, growth_rate=growth_rate) for i, growth_rate in enumerate(growth_rates)]
# all nodes have the same population value of 0.5 at the start
initial_conditions = [0.5]*len(growth_rates)
def activity_rule(ctx):
return ctx.edge_data(ctx.node_label, "growth_rate") * ctx.current_activity * (1 - ctx.current_activity)
trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
activity_rule=activity_rule, timesteps=timesteps)
activities = ntm.get_activities_over_time_as_list(trajectory)
# plot the trajectories for each growth rate
activities = np.array(activities)
ntm.plot1D(x=[n for n in range(1, timesteps + 1)],
y=[activities[:, i] for i in range(len(growth_rates))],
label=growth_rates, xlabel="t", ylabel="x(t)",
legend={"title": "Growth rate", "bbox_to_anchor": (1.04, 1)},
tight_layout={"rect": [0, 0, 0.95, 1]})
```
The source code for the example above can be found [here](logistic_map_demo.py).
Netomaton supports plotting bifurcation plots, which are useful for understanding the Logistic Map.
```python
import netomaton as ntm
import numpy as np
timesteps = 200
num_nodes = 1000
growth_rates = np.linspace(0.0, 4.0, num_nodes)
network = ntm.topology.disconnected(num_nodes)
[network.add_edge(i, i, growth_rate=growth_rate) for i, growth_rate in enumerate(growth_rates)]
initial_conditions = [0.5]*num_nodes
def activity_rule(ctx):
return ctx.edge_data(ctx.node_label, "growth_rate") * ctx.current_activity * (1 - ctx.current_activity)
trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
activity_rule=activity_rule, timesteps=timesteps)
activities = ntm.get_activities_over_time_as_list(trajectory)
# create the bifurcation plot
activities = np.array(activities)
ntm.bifurcation_plot(x=growth_rates, timesteps=int((timesteps/2)),
trajectories=[activities[:, i] for i in range(num_nodes)],
xlabel="Growth rate", ylabel="Population")
```
The source code for the example above can be found [here](logistic_map_demo2.py).
Finally, Netomaton also supports the creation of a Poincaré plot for the Logistic Map:
```python
import netomaton as ntm
import numpy as np
timesteps = 1000
num_nodes = 50
growth_rates = np.linspace(3.6, 4.0, num_nodes)
network = ntm.topology.disconnected(num_nodes)
[network.add_edge(i, i, growth_rate=growth_rate) for i, growth_rate in enumerate(growth_rates)]
initial_conditions = [0.5]*num_nodes
def activity_rule(ctx):
return ctx.edge_data(ctx.node_label, "growth_rate") * ctx.current_activity * (1 - ctx.current_activity)
trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
activity_rule=activity_rule, timesteps=timesteps)
activities = ntm.get_activities_over_time_as_list(trajectory)
# create the Poincaré plot
activities = np.array(activities)
ntm.poincare_plot(activities=[activities[:, i] for i in range(num_nodes)],
timesteps=int((timesteps/2)),
xlabel="Population", ylabel="Population(t + 1)",
xlim=(0.25, 0.75), ylim=(0.8, 1.01))
```
The source code for the example above can be found [here](logistic_map_demo3.py).
For more information, see:
> May, R. M. (1976). Special mathematical models with very complicated dynamics. Nature.
> https://geoffboeing.com/2015/03/chaos-theory-logistic-map/
> https://en.wikipedia.org/wiki/Logistic_map