Skip to content

Instantly share code, notes, and snippets.

@genkuroki
Created December 10, 2017 11:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save genkuroki/e1b67ede17207881e3b0204cb0522cca to your computer and use it in GitHub Desktop.
Save genkuroki/e1b67ede17207881e3b0204cb0522cca to your computer and use it in GitHub Desktop.
turing-patterns-creating-animated-gifs.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "### Warning\nThis is a long post. It wasn't meant to be this way, but reaction diffusion equations proved to be too damn interesting not to write a lot about. To try and understand the system, I do some maths and some programming. Be warned, the maths isn't rigorous and the code isn't the most efficient. This is a deliberate choice. My goal is to try and understand the system, and along the way learn to create images like this\n\n![title](http://www.degeneratestate.org/static/turing-patterns/title.png)\n\n\n# Turing Patterns\n\nIn 1952, [Turing](https://en.wikipedia.org/wiki/Alan_Turing) published a paper called [\"The Chemical Basis of Morphogenesis\"](http://www.dna.caltech.edu/courses/cs191/paperscs191/turing.pdf) suggesting a possible mechanism for how a simple set of chemical reactions could lead to the formation of stripes, spots and other patterns we see on animals. It is a surprisingly readable paper that covers some maths, some chemistry, some numerical modelling and ties it all together to try and answer a biological question. \n\nAt the core of the paper are a set of partial differential equations known as [reaction-diffusion equations](https://en.wikipedia.org/wiki/Reaction%E2%80%93diffusion_system). Turing's analysis shows that in certain regimes systems that obey these equations are unstable to small perturbations, leading to the growth of large scale patterns.\n\nIf you have only a basic understanding of chemistry (like I do) this might be surprising. Most chemical reactions I've seen involve homogeneous concentrations of reagents, reacting to create the final products, often described by a reaction equation like\n\n$A + B \\rightarrow C + D$\n\nIn this simplified model there is no room for pattern formation, because haven't included any description about how concentrations of the different chemicals vary in space. Once we take into account these differences, a much richer set of behaviours is possible.\n\nIn this post, I am going explore the system described in the paper."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Warning\nThis is a long post. It wasn't meant to be this way, but reaction diffusion equations proved to be too damn interesting not to write a lot about. To try and understand the system, I do some maths and some programming. Be warned, the maths isn't rigorous and the code isn't the most efficient. This is a deliberate choice. My goal is to try and understand the system, and along the way learn to create images like this\n\n![title](title.png)\n\n\n# Turing Patterns\n\nIn 1952, [Turing](https://en.wikipedia.org/wiki/Alan_Turing) published a paper called [\"The Chemical Basis of Morphogenesis\"](http://www.dna.caltech.edu/courses/cs191/paperscs191/turing.pdf) suggesting a possible mechanism for how a simple set of chemical reactions could lead to the formation of stripes, spots and other patterns we see on animals. It is a surprisingly readable paper that covers some maths, some chemistry, some numerical modelling and ties it all together to try and answer a biological question. \n\nAt the core of the paper are a set of partial differential equations known as [reaction-diffusion equations](https://en.wikipedia.org/wiki/Reaction%E2%80%93diffusion_system). Turing's analysis shows that in certain regimes systems that obey these equations are unstable to small perturbations, leading to the growth of large scale patterns.\n\nIf you have only a basic understanding of chemistry (like I do) this might be surprising. Most chemical reactions I've seen involve homogeneous concentrations of reagents, reacting to create the final products, often described by a reaction equation like\n\n$A + B \\rightarrow C + D$\n\nIn this simplified model there is no room for pattern formation, because haven't included any description about how concentrations of the different chemicals vary in space. Once we take into account these differences, a much richer set of behaviours is possible.\n\nIn this post, I am going explore the system described in the paper."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "\n## Reaction Diffusion Equations\n\nReaction-Diffusion equations are a class of partial differential equations whose dynamics are governed by two terms: a diffusion part and a reaction part. We will be looking at the two component case, which takes the form\n\n$\\frac{\\partial a(x,t)}{\\partial t} = D_{a}\\frac{\\partial^{2} a(x,t)}{\\partial x^{2}} + R_{a}(a(x,t),b(x,t))$\n\n$\\frac{\\partial b(x,t)}{\\partial t} = D_{b}\\frac{\\partial^{2} b(x,t)}{\\partial x^{2}} + R_{b}(a(x,t),b(x,t))$\n\nWhere the $a(x,t)$ and $b(x,t)$ describe the concentration of chemicals $a$ and $b$ at position $x$ and time $t$. The functions $R_{a}$ and $R_{b}$ determine how the concentrations change due to interspecies reactions and $D_{a}$ and $D_{b}$ are the diffusion coefficients. "
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Diffusion\n\nThe diffusion part of the equations causes areas of high concentration to spread out to areas of low concentration, while conserving the total amount of the chemical. To get a feel for what's happening, let's focus the equation\n\n$ \\frac{\\partial a(x,t)}{\\partial t} = D_{a}\\frac{\\partial^{2} a(x,t)}{\\partial x^{2}} $\n\nIf this looks familiar, it is because it appears in a number of different areas of science and maths. A few are: the [diffusion equation](https://en.wikipedia.org/wiki/Diffusion_equation), the [heat equation](https://en.wikipedia.org/wiki/Heat_equation) and [Brownian motion](https://en.wikipedia.org/wiki/Brownian_motion). \n\nIt has an analytic solution, which for initial Gaussian distribution is\n\n$ a(x,t) = \\frac{a_{0}}{\\sqrt{2\\pi(\\sigma_{0}^{2} + 2 D_{a} t)}} \\exp\\left(-\\frac{x^{2}}{2(\\sigma_{0}^{2} + 2 D_{a} t)}\\right)$\n\nA \"spreading out\", or diffusion over time, as the name suggests."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Simulating equations\n\nIf you've got this far, you might have noticed that this is written in a [jupyter notebook](http://jupyter.org/). This allows us to mix code with writing, so we don't just have to look at an equation to understand how it behaves - we can simulate it directly. \n\nTo simulate the PDEs, I'm going to use the [explicit finite-difference method](https://en.wikipedia.org/wiki/Finite_difference_method#Example:_The_heat_equation). It is not the best numerical method to use, but it is easy to code, and provided we keep the time step small enough it will be stable. \n\nUnder this scheme, we approximate the time derivative as\n\n$$\n\\frac{\\partial a(x,t)}{\\partial t} \\approx \\frac{1}{dt}(a_{x,t+1} - a_{x,t})\n$$\n\nAnd the spatial part of the derivative (which is usually know as the [Laplacian](https://en.wikipedia.org/wiki/Laplace_operator)) as\n\n$$\n\\frac{\\partial^{2} a(x,t)}{\\partial x^{2}} \\approx \\frac{1}{dx^{2}}(a_{x+1,t} + a_{x-1,t} - 2a_{x,t})\n$$\n\nPutting it all together, for the diffusion part of the equation we get.\n\n$$\na_{x,t+1} = a_{x,t} + dt\\left( \\frac{D_{a}}{dx^{2}}(a_{x+1,t} + a_{x-1,t} - 2a_{x,t}) \\right)\n$$\n\nLet's start by defining some functions to take care of the spatial derivatives. We will be using periodic boundary conditions throughout our exploration, which are easy to implement in numpy using its [roll](https://docs.scipy.org/doc/numpy-1.12.0/reference/generated/numpy.roll.html) function."
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "import matplotlib.pyplot as plt\nimport numpy as np\n\n# I'm using seaborn for it's fantastic default styles\nimport seaborn as sns\nsns.set_style(\"whitegrid\")\n\n%matplotlib inline\n%load_ext autoreload\n%autoreload 2\n\nfrom tutils import BaseStateSystem",
"execution_count": 1,
"outputs": []
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "def laplacian1D(a, dx):\n return (\n - 2 * a\n + np.roll(a,1,axis=0) \n + np.roll(a,-1,axis=0)\n ) / (dx ** 2)\n\ndef laplacian2D(a, dx):\n return (\n - 4 * a\n + np.roll(a,1,axis=0) \n + np.roll(a,-1,axis=0)\n + np.roll(a,+1,axis=1)\n + np.roll(a,-1,axis=1)\n ) / (dx ** 2)",
"execution_count": 2,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "To aid in generating visualisations, I've written a base class which updates and plots a system based on some notion of state. There's nothing specific about the reaction diffusion equations encoded in it, so I'm not going to go into any detail about it. You can find the full code for it, along with this notebook on github [here](https://github.com/ijmbarr/turing-patterns). It uses [matplotlib's animation](http://matplotlib.org/api/animation_api.html) functionality to plot the output as a gif.\n\nAll together we can simulate the effect of the diffusion equation:"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "class OneDimensionalDiffusionEquation(BaseStateSystem):\n def __init__(self, D):\n self.D = D\n self.width = 1000\n self.dx = 10 / self.width\n self.dt = 0.9 * (self.dx ** 2) / (2 * D)\n self.steps = int(0.1 / self.dt)\n \n def initialise(self):\n self.t = 0\n self.X = np.linspace(-5,5,self.width)\n self.a = np.exp(-self.X**2)\n \n def update(self):\n for _ in range(self.steps):\n self.t += self.dt\n self._update()\n\n def _update(self): \n La = laplacian1D(self.a, self.dx)\n delta_a = self.dt * (self.D * La) \n self.a += delta_a\n \n def draw(self, ax):\n ax.clear()\n ax.plot(self.X,self.a, color=\"r\")\n ax.set_ylim(0,1)\n ax.set_xlim(-5,5)\n ax.set_title(\"t = {:.2f}\".format(self.t))\n \none_d_diffusion = OneDimensionalDiffusionEquation(D=1)\n\none_d_diffusion.plot_time_evolution(\"diffusion.gif\")",
"execution_count": 3,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![diffusion](http://www.degeneratestate.org/static/turing-patterns/diffusion.gif)\n\nAs expected - the diffusion equation causes the the concentration to \"spread out\"."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![diffusion](diffusion.gif)\n\nAs expected - the diffusion equation causes the the concentration to \"spread out\"."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Reaction\n\nInteractions between the two chemical components are introduced via the functions $R_{a}$ and $R_{b}$. These functions only depend on the local concentration of each of the chemicals. Their exact form will depend on the chemicals involved, but it is possible to show that Turing patterns are observed for a whole class of different equations.\n\nThe one thing we will require from these equations is that they reach a stable equilibrium when the concentrations of the chemicals involved are completely homogeneous. This means that there exists concentrations of $a$ and $b$ such that\n\n$R_a(a_{0}, b_{0}) = 0$\n\n$R_{b}(a_{0}, b_{0}) = 0$\n\nThe fact that we require this to hold will make the later instability more suprising.\n\nFor the reaction equations, I'm going to use the [FitzHugh–Nagumo equation](https://en.wikipedia.org/wiki/FitzHugh%E2%80%93Nagumo_model)\n\n$R_a(a, b) = a - a^{3} - b + \\alpha$\n\n$R_{b}(a, b) = \\beta (a - b)$\n\nWhere $\\alpha$ and $\\beta$ are constants.\n\nLet's see how it behaves"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "class ReactionEquation(BaseStateSystem):\n def __init__(self, Ra, Rb):\n self.Ra = Ra\n self.Rb = Rb\n self.dt = 0.01\n self.steps = int(0.1 / self.dt)\n \n def initialise(self):\n self.t = 0\n self.a = 0.1\n self.b = 0.7\n self.Ya = []\n self.Yb = []\n self.X = []\n \n def update(self):\n for _ in range(self.steps):\n self.t += self.dt\n self._update()\n\n def _update(self): \n delta_a = self.dt * self.Ra(self.a,self.b) \n delta_b = self.dt * self.Rb(self.a,self.b) \n\n self.a += delta_a\n self.b += delta_b\n \n def draw(self, ax):\n ax.clear()\n \n self.X.append(self.t)\n self.Ya.append(self.a)\n self.Yb.append(self.b)\n\n ax.plot(self.X,self.Ya, color=\"r\", label=\"A\")\n ax.plot(self.X,self.Yb, color=\"b\", label=\"B\")\n ax.legend()\n \n ax.set_ylim(0,1)\n ax.set_xlim(0,5)\n ax.set_xlabel(\"t\")\n ax.set_ylabel(\"Concentrations\")\n \nalpha, beta = 0.2, 5\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n \none_d_reaction = ReactionEquation(Ra, Rb)\none_d_reaction.plot_time_evolution(\"reaction.gif\", n_steps=50)",
"execution_count": 4,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](http://www.degeneratestate.org/static/turing-patterns/reaction.gif)\n\nThe system is stable, and stabilises to $a = b = \\sqrt[3]{\\alpha}$."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](reaction.gif)\n\nThe system is stable, and stabilises to $a = b = \\sqrt[3]{\\alpha}$."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Full Model\n\nWe now have two parts to the reaction diffusion equation: a diffusion term that \"spreads\" out concentration and a reaction part the equalises the two concentrations. It feels like these two together should create a stable system, so it is surprising that we can end up with patterns.\n\nBut enough with the preliminaries, let's take a look at some of the patterns formed. "
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "def random_initialiser(shape):\n return(\n np.random.normal(loc=0, scale=0.05, size=shape),\n np.random.normal(loc=0, scale=0.05, size=shape)\n )\n\nclass OneDimensionalRDEquations(BaseStateSystem):\n def __init__(self, Da, Db, Ra, Rb,\n initialiser=random_initialiser,\n width=1000, dx=1, \n dt=0.1, steps=1):\n \n self.Da = Da\n self.Db = Db\n self.Ra = Ra\n self.Rb = Rb\n \n self.initialiser = initialiser\n self.width = width\n self.dx = dx\n self.dt = dt\n self.steps = steps\n \n def initialise(self):\n self.t = 0\n self.a, self.b = self.initialiser(self.width)\n \n def update(self):\n for _ in range(self.steps):\n self.t += self.dt\n self._update()\n\n def _update(self):\n \n # unpack so we don't have to keep writing \"self\"\n a,b,Da,Db,Ra,Rb,dt,dx = (\n self.a, self.b,\n self.Da, self.Db,\n self.Ra, self.Rb,\n self.dt, self.dx\n )\n \n La = laplacian1D(a, dx)\n Lb = laplacian1D(b, dx)\n \n delta_a = dt * (Da * La + Ra(a,b))\n delta_b = dt * (Db * Lb + Rb(a,b))\n \n self.a += delta_a\n self.b += delta_b\n \n def draw(self, ax):\n ax.clear()\n ax.plot(self.a, color=\"r\", label=\"A\")\n ax.plot(self.b, color=\"b\", label=\"B\")\n ax.legend()\n ax.set_ylim(-1,1)\n ax.set_title(\"t = {:.2f}\".format(self.t))\n \nDa, Db, alpha, beta = 1, 100, -0.005, 10\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 100\ndx = 1\ndt = 0.001\n\nOneDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, dx=dx, dt=dt, \n steps=100\n).plot_time_evolution(\"1dRD.gif\", n_steps=150)",
"execution_count": 5,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](http://www.degeneratestate.org/static/turing-patterns/1dRD.gif)\n\nA pattern of stripes.\n\nWe can see that same behavior in two dimensions:"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](1dRD.gif)\n\nA pattern of stripes.\n\nWe can see that same behavior in two dimensions:"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "class TwoDimensionalRDEquations(BaseStateSystem):\n def __init__(self, Da, Db, Ra, Rb,\n initialiser=random_initialiser,\n width=1000, height=1000,\n dx=1, dt=0.1, steps=1):\n \n self.Da = Da\n self.Db = Db\n self.Ra = Ra\n self.Rb = Rb\n\n self.initialiser = initialiser\n self.width = width\n self.height = height\n self.shape = (width, height)\n self.dx = dx\n self.dt = dt\n self.steps = steps\n \n def initialise(self):\n self.t = 0\n self.a, self.b = self.initialiser(self.shape)\n \n def update(self):\n for _ in range(self.steps):\n self.t += self.dt\n self._update()\n\n def _update(self):\n \n # unpack so we don't have to keep writing \"self\"\n a,b,Da,Db,Ra,Rb,dt,dx = (\n self.a, self.b,\n self.Da, self.Db,\n self.Ra, self.Rb,\n self.dt, self.dx\n )\n \n La = laplacian2D(a, dx)\n Lb = laplacian2D(b, dx)\n \n delta_a = dt * (Da * La + Ra(a,b))\n delta_b = dt * (Db * Lb + Rb(a,b))\n \n self.a += delta_a\n self.b += delta_b\n \n def draw(self, ax):\n ax[0].clear()\n ax[1].clear()\n\n ax[0].imshow(self.a, cmap='jet')\n ax[1].imshow(self.b, cmap='brg')\n \n ax[0].grid(b=False)\n ax[1].grid(b=False)\n \n ax[0].set_title(\"A, t = {:.2f}\".format(self.t))\n ax[1].set_title(\"B, t = {:.2f}\".format(self.t))\n \n def initialise_figure(self):\n fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(12,6))\n return fig, ax\n \nDa, Db, alpha, beta = 1, 100, -0.005, 10\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 100\ndx = 1\ndt = 0.001\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, height=width, \n dx=dx, dt=dt, steps=100\n).plot_evolution_outcome(\"2dRD.png\", n_steps=150)",
"execution_count": 6,
"outputs": []
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "class TwoDimensionalRDEquations(BaseStateSystem):\n def __init__(self, Da, Db, Ra, Rb,\n initialiser=random_initialiser,\n width=1000, height=1000,\n dx=1, dt=0.1, steps=1):\n \n self.Da = Da\n self.Db = Db\n self.Ra = Ra\n self.Rb = Rb\n\n self.initialiser = initialiser\n self.width = width\n self.height = height\n self.shape = (width, height)\n self.dx = dx\n self.dt = dt\n self.steps = steps\n \n def initialise(self):\n self.t = 0\n self.a, self.b = self.initialiser(self.shape)\n \n def update(self):\n for _ in range(self.steps):\n self.t += self.dt\n self._update()\n\n def _update(self):\n \n # unpack so we don't have to keep writing \"self\"\n a,b,Da,Db,Ra,Rb,dt,dx = (\n self.a, self.b,\n self.Da, self.Db,\n self.Ra, self.Rb,\n self.dt, self.dx\n )\n \n La = laplacian2D(a, dx)\n Lb = laplacian2D(b, dx)\n \n delta_a = dt * (Da * La + Ra(a,b))\n delta_b = dt * (Db * Lb + Rb(a,b))\n \n self.a += delta_a\n self.b += delta_b\n \n def draw(self, ax):\n ax[0].clear()\n ax[1].clear()\n\n ax[0].imshow(self.a, cmap='jet')\n ax[1].imshow(self.b, cmap='brg')\n \n ax[0].grid(b=False)\n ax[1].grid(b=False)\n \n ax[0].set_title(\"A, t = {:.2f}\".format(self.t))\n ax[1].set_title(\"B, t = {:.2f}\".format(self.t))\n \n def initialise_figure(self):\n fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(12,6))\n return fig, ax\n \nDa, Db, alpha, beta = 1, 100, -0.005, 10\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 100\ndx = 1\ndt = 0.001\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, height=width, \n dx=dx, dt=dt, steps=100\n).plot_time_evolution(\"2dRD.gif\", n_steps=300)",
"execution_count": 7,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "[![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD.png)](http://www.degeneratestate.org/static/turing-patterns/2dRD.gif)\n\n(It is possible to animate the formation of these patterns using a similar method call as in the one-dimensional case above. The resulting gifs are quite impressive, but also large, so they are not directly included here. However, if you click on the image it will take you to the animation)\n\nIt turns out this behaviour is common to a lot of reaction-diffusion equations, not just the ones presented here. \n\nSo how does this happen? To understand, we are going to need to do some maths. "
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD.gif)\n\n(It is possible to animate the formation of these patterns using a similar method call as in the one-dimensional case above. The resulting gifs are quite impressive, but also large, so they are not directly included here. However, if you click on the image it will take you to the animation)\n\nIt turns out this behaviour is common to a lot of reaction-diffusion equations, not just the ones presented here. \n\nSo how does this happen? To understand, we are going to need to do some maths. "
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](2dRD.gif)\n\n(It is possible to animate the formation of these patterns using a similar method call as in the one-dimensional case above. The resulting gifs are quite impressive, but also large, so they are not directly included here. However, if you click on the image it will take you to the animation)\n\nIt turns out this behaviour is common to a lot of reaction-diffusion equations, not just the ones presented here. \n\nSo how does this happen? To understand, we are going to need to do some maths. "
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Stability Analysis\n\nLet's return to the original equations. We've seen that for certain conditions, pattern formations occurs.\n\nUnfortunately, solving the equations directly for non-linear reaction functions is often not possible. Instead we can look at what happens when the the system is perturbed slightly from equilibrium.\n\n### Linearising the Equations\n\nWe start by assuming there is some concentrations, $a_{0}$ and $b_{0}$, for which the system is stable. This means that \n - $R_{a}(a_{0},b_{0}) = 0$\n - $R_{b}(a_{0},b_{0}) = 0$\n \nAround these solutions, we look at the time dependence of small perturbations around these values\n - $x = a - a_{0}$\n - $y = b - b_{0}$\n \nAnd linearise the reaction equations\n - $R_{a}(a,b) \\approx r_{aa}x + r_{ab}y$\n - $R_{b}(a,b) \\approx r_{ba}x + r_{bb}y$\n \nwhere $r_{ij} = \\frac{\\partial R_{i}}{\\partial j}$.\n\nThese approximations give us a set of linear equations, written in vector form as\n\n$\\dot{\\mathbf{x}} = D\\nabla^{2} \\mathbf{x} + R \\mathbf{x}$\n\nWhere $R = \\left(\\begin{matrix} r_{11} & r_{12} \\\\ r_{21} & r_{22}\\end{matrix}\\right)$ and $D = \\left(\\begin{matrix} D_{a} & 0 \\\\ 0 & D_{b}\\end{matrix}\\right)$\n\n### Fourier Transform\nIf we impose periodic boundary conditions to this equation, a natural solution can be found by applying a [Fourier Transformation](https://acko.net/files/gltalks/toolsforthought/) to $\\mathbf{x}$. If we call the reciprocal coordinate $k$, and the Fourier transform of $\\mathbf{x}$ as $\\tilde{\\mathbf{x}}$, then the transformed equation is \n\n$\\dot{\\tilde{\\mathbf{x}}} = (R - k^{2}D) \\tilde{\\mathbf{x}}$\n\nWhich, has solutions of the form\n\n$\\tilde{\\mathbf{x}}(t) = \\tilde{\\mathbf{x}}(0) e^{\\omega t}$\n\nTo find $\\omega$ we plug this solution back into our transformed equation to get\n\n$\\omega \\tilde{\\mathbf{x}} = (R - k^{2}D) \\tilde{\\mathbf{x}}$\n\nShowing that $\\omega$ is just the eigenvalue of $(R - k^{2}D)$. \n\n### Stability\n\nWe now have an equation for the time dependence of our system in the Fourier domain. Using it, we can now discuss what we mean by _stability_. Our system is considered stable if small perturbations around the homogeneous do not cause it to move further away from the stable solutions. \n\nIn terms of our solution, $\\tilde{\\mathbf{x}}(0) e^{\\omega t}$, stability means that the values of $\\omega$ does not have positive real parts for all values of $k$. If $\\omega$ is negative, the perturbation will decay away. If $\\omega$ is imaginary, it will oscillate around the stable state. However, if it is positive and real _any_ small perturbation will grow exponentially, until a high order term of the reaction equation becomes important.\n\nTo find $\\omega$, we need to solve the equation\n\n$\\hbox{Det}(R - k^{2}D - \\omega I) = 0 $\n\nWriting $J = R - k^{2}D$, this equitation takes the form\n\n$\\omega^{2} - \\omega\\hbox{Tr}(J) + \\hbox{Det}(J) = 0$\n\nSolving for $\\omega$, we get\n\n$\\omega = \\frac{1}{2}(\\hbox{Tr}(J) \\pm \\sqrt{\\hbox{Tr}(J)^{2} - 4 \\hbox{Det}(J) })$\n\n### Conditions for (in)Stability\n\nFrom our initial assumption that there was a stable homogeneous state, we require that $\\omega$ has negative real parts where $k = 0$, which corresponds to the spatially homogeneous solution. For this to be true, we require that \n\n - $\\hbox{Tr}(R) < 0$\n - $\\hbox{det}(R) > 0$\n \nOr, in terms of the components of $R$:\n - $r_{aa} + r_{bb} < 0$\n - $r_{aa}r_{bb} - r_{ab}r_{ba} > 0$\n\nFor an instability to now occur at finite wavelength, we need one of the following conditions to hold:\n - $\\hbox{Tr}(J) > 0$\n - $\\hbox{det}(J) < 0$\n\nBecause $\\hbox{Tr}(J) = \\hbox{Tr}(R) - k^{2}(d_{a} + d_{b})$, the first condition cannot hold for any real $k$. This means the we require the second to hold, or, after a bit of algebra\n\n$k^{4}d_{a}d_{b} - k^{2}(d_{a}r_{bb} + d_{b}r_{aa}) + (r_{aa}r_{bb} - r_{ab}r_{ba}) < 0$\n\nfor some real value of $k$. Once again, we need to solve a quadratic equation. To do this we note that because $k$ needs to be real, $k^{2}$ must be positive. This means that at least one root of quadratic equation in $k^{2}$ needs to be positive and real. This condition is only met when \n\n$d_{b}r_{aa} + d_{a}r_{bb} > 2\\sqrt{d_{a}d_{b}(r_{aa}r_{bb} - r_{ab}r_{ba})} > 0$\n\nAnd that's it. We've derived that conditions for the diffusion-reaction equations to be stable to small perturbations. \n\nWe can write the complete requirements as\n\n1. $r_{aa} + r_{bb} < 0$\n2. $r_{aa}r_{bb} - r_{ab}r_{ba} > 0$\n3. $d_{b}r_{aa} + d_{a}r_{bb} > 2\\sqrt{d_{a}d_{b}(r_{aa}r_{bb} - r_{ab}r_{ba})} > 0$"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Discussion\nWe've waded through a lot of maths to reach this point. Let's take stock of what we can make of the conditions for instability that we have derived. \n\nGiven the diffusion coefficients are positive, from the first and third equation above, we know that $r_{aa}$ and $r_{bb}$ have to have different signs, and the component with the negative sign has to be larger. Combining this we the second equation we know that either $r_{ab}$ or $r_{ba}$ has to be negative, and the other has to be positive. This two facts show why some systems like this are called activator-inhibitor system: the presence of one component increase the production of itself - the other inhibits the production of both.\n\nWith this information, the third equation shows us that the diffusion coefficients need to be different. Specifically, the diffusion coefficient of the inhibitor has to be larger then that of the activator.\n\nTogether, this gives us a hand-waving explanation of what might be going on: Consider starting with a small increase in the activator at some point. This in turn creates an increase in the inhibitor at that point. Both of these chemical diffuse to nearby points, but the inhibitor diffuses faster, lowering the activator concentration of nearby points. This lowering of the activator concentration at nearby points, which in turn lowers the inhibitor concentration. The disturbance spreads out like a wave. \n\nIt is a lot easier to get a feel for what's going on once we visualise the this perturbation:"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "Da, Db, alpha, beta = 1, 100, -0.005, 10\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\ndef initalise_bump(shape):\n a = np.zeros(shape)\n if len(a.shape) == 1:\n a[int(a.shape[0] / 2)] = 0.3\n elif len(a.shape) == 2:\n a[int(a.shape[0] / 2), int(a.shape[1] / 2)] = 0.3\n\n return(\n a,\n np.zeros(shape)\n )\n\nwidth = 100\ndx = 1\ndt = 0.001\n\nOneDimensionalRDEquations(\n Da, Db, Ra, Rb, \n initialiser=initalise_bump, \n width=width, dx=dx, dt=dt, \n steps=250\n).plot_time_evolution(\"1dRD_initial_bump.gif\", n_steps=300)",
"execution_count": 8,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](http://www.degeneratestate.org/static/turing-patterns/1dRD_initial_bump.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](1dRD_initial_bump.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Exploring the Parameter Space\n\nIt is interesting to ask how the parameters of the equations affect the pattern formation. To get a feel for what this dependence is, we can investigate the amplitude of the instability for different frequencies. Close to equilibrium, we expect the frequency with the largest real component to dominate the dynamics of the system.\n\nCalculating this value isn't mathematically difficult: we have an equation of the amplitude $\\omega$ in terms of the frequency $k$ above. Differentiating an equation of this size by hand can become tedious due to its size, and it is easy to introduce errors. And why carry out simple replacement steps when we have a computer to hand?\n\nIn the following, I use the computer algebra system [sympy](http://www.sympy.org/en/index.html) to find the largest real mode of the system"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "import sympy as sp\nsp.init_printing()\n\n# define symbols\na,b,alpha,beta = sp.symbols(\"a,b,alpha,beta\")\nRaa, Rab, Rba, Rbb = sp.symbols(\"Raa, Rab, Rba, Rbb\")\nDa, Db = sp.symbols(\"Da, Db\")\nk2 = sp.Symbol(\"k2\")\n\n# create matricies\nR = sp.Matrix(\n [[Raa, Rab],\n [Rba, Rbb]]\n)\n\nD = sp.Matrix(\n [[Da, 0],\n [0, Db]]\n)\n\nJ = (R - k2 * D)\n\n# define our solution for omega\nomega = sp.Matrix.trace(J) + sp.sqrt(sp.Matrix.trace(J) ** 2 - 4 * sp.Matrix.det(J))\nomega",
"execution_count": 9,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 9,
"data": {
"text/plain": " ______________________________________________\n ╱ \n-Da⋅k₂ - Db⋅k₂ + Raa + Rbb + ╲╱ 4⋅Rab⋅Rba - 4⋅(-Da⋅k₂ + Raa)⋅(-Db⋅k₂ + Rbb) +\n\n_______________________________\n 2 \n (-Da⋅k₂ - Db⋅k₂ + Raa + Rbb) ",
"image/png": "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\n",
"text/latex": "$$- Da k_{2} - Db k_{2} + Raa + Rbb + \\sqrt{4 Rab Rba - 4 \\left(- Da k_{2} + Raa\\right) \\left(- Db k_{2} + Rbb\\right) + \\left(- Da k_{2} - Db k_{2} + Raa + Rbb\\right)^{2}}$$"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "# find the maximum value of omega for k^{2}\nomega_prime = sp.diff(omega, k2)\nsols = sp.solvers.solve(omega_prime, k2)\nmax_k2 = sols[0]\n\nmax_k2",
"execution_count": 10,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 10,
"data": {
"text/plain": " ________________ ________________\nDa⋅Db⋅Raa - Da⋅Db⋅Rbb - Da⋅╲╱ -Da⋅Db⋅Rab⋅Rba - Db⋅╲╱ -Da⋅Db⋅Rab⋅Rba \n─────────────────────────────────────────────────────────────────────\n Da⋅Db⋅(Da - Db) ",
"image/png": "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\n",
"text/latex": "$$\\frac{1}{Da Db \\left(Da - Db\\right)} \\left(Da Db Raa - Da Db Rbb - Da \\sqrt{- Da Db Rab Rba} - Db \\sqrt{- Da Db Rab Rba}\\right)$$"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "def get_length_scale(al, be, da, db):\n\n # rewritng in terms of Da, Db, alpha, beta\n Ra = a - a**3 - b + alpha\n Rb = beta * (a - b)\n\n # we expand around the homogenius steady state\n zero_subs = [\n (a, sp.sign(alpha) * sp.cbrt(sp.Abs(alpha))),\n (b, sp.sign(alpha) * sp.cbrt(sp.Abs(alpha)))\n ]\n\n # and linearise\n R_prime_subs = {\n (Raa, sp.diff(Ra, a)),\n (Rab, sp.diff(Ra, b)),\n (Rba, sp.diff(Rb, a)),\n (Rbb, sp.diff(Rb, b))\n }\n\n # putting in our numeric values we get\n vals = [\n (alpha, al),\n (beta, be),\n (Da, da),\n (Db, db)\n ]\n\n # substitute it all in to find the maximum k\n max_k2 = [sp.N(\n sol\n .subs(R_prime_subs)\n .subs(zero_subs)\n .subs(vals)\n ) for sol in sols]\n \n k2 = max(sp.re(sym) for sym in max_k2)\n \n # if k2 is negative, no real solution exists\n if k2 <= 0:\n return -1\n \n # convert k2 into length units\n return sp.N(2 * sp.pi / sp.sqrt(k2))\n\nget_length_scale(-0.005, 10, 1, 100)",
"execution_count": 11,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 11,
"data": {
"text/plain": "13.6336345738685",
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAJ8AAAAPBAMAAAAIUwCQAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAVO8Qq5l2zWYiibvd\nRDIcHY1cAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACqUlEQVQ4Ea1UTUgUYRh+9reZXXedg4du688h\nIoQhwiCE3URLgmorJTIkIQry4oKXIIo5REgXlyAqT0ukyBq6FR2iIi8R1MEhiC6Be6hLBKZCq1hM\nz/t+s4fufTDvzPPzPft+33yzgAy7uuDoQ2pu0YCQqY7XMVN9Ito3YNfF7Cn8GKhWK4bsGR+lIJ7Y\n5VnfyECkC7iLyJZMil3HWQMMc9BPlOwich61D0A0CCqYDoKgYMgi4g7EgwRw2Mg4MLQJTDj4LYHp\nIl4aYJjnSNeS62hdBpL72OGjK8A5IA4lIx7sEsSD3cA9I9PJwI9+rCGBq2UWBVoy24SZbuRLwP1r\nEsRRARYNmaohtqweLAC9RjaBXLgu+anMCAGZdLtCXXKlGcifGBM652V23JRnPKvPMP9vYG9FTI3v\nl3zeFLC03piTXY+9B6yyBE5dqhOneSl5cms+9MSD215TliVj5q0rpoaHjhAIk7+KqAP78RmgBwxM\nu/Yf2uZ5KZkKJkMPlhpuU9ZApLolMHBxmskKWPKbSPAM4KHPnWMgxyu6fuoTybYvG77xRI6vSIDI\n+lJ465QW+aqn/SbodPl2LWkp2hWph4EXykiWSAlpFTDRZTw3kdyQAMoa+AB44xC/Y6CnQEtLCdaW\nXUZ6/RAk8Ciw5KOlxhULGS0ju64e7AXWHCNrIJeqgX3SoQIt8XZ22LrJuW39/RtHsIctcGcLgJJ5\ntjCoHtmFuGdkDeShXWGzWOMeQoGWLPewPVpDyzY1MkVdwxpvSrJDvFCPdJguG1kDpxD5Jcc3Xol1\nQIEWHMN+x/KQG2PgjnyhFmfmOFHJzCSsMfXgs4tboZzoawwiM3yee1MAZofqBiiD1PAJ4NPwa+Yt\nBIOwRwZc4I5DqOTXkVEeCfFkB/jnYGSK/3X8BZ4IAQbPKa1vAAAAAElFTkSuQmCC\n",
"text/latex": "$$13.6336345738685$$"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Which looks about right when we compare it to our simulations above - the spatial variation is about 14 length units.\n\nLet's use it to see how the length scale of the system changes as a function of $\\alpha$ and $\\beta$:"
},
{
"metadata": {
"scrolled": false,
"trusted": true
},
"cell_type": "code",
"source": "betas = [(1 + i) * 0.125 for i in range(0,40)]\nalphas = [i * 0.05 for i in range(-20,20)]\n\noutcomes = np.zeros((len(betas), len(alphas)))\n\nfor x,al in enumerate(alphas):\n for y,be in enumerate(betas):\n outcomes[y,x] = get_length_scale(al, be, 1, 100)\n\n\nplt.figure(figsize=(8,8))\n\no = outcomes\no[outcomes == 0] = -1\n\nplt.imshow(o, cmap=\"hsv\", interpolation=\"none\")\nplt.colorbar()\n\nplt.xticks(*zip(*[(n,l) for n,l in enumerate(alphas) if n % 5 == 0]), rotation=90)\nplt.yticks(*zip(*[(n,l) for n,l in enumerate(betas) if n % 5 == 0]), rotation=0)\n\nplt.title(\"Lengthscale\", fontsize=20)\nplt.xlabel(r\"$\\alpha$\", fontsize=18)\nplt.ylabel(r\"$\\beta$\", fontsize=18);",
"execution_count": 12,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": "<matplotlib.figure.Figure at 0x381c41e2e8>",
"image/png": "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\n"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "And take a look at the long length scale we should see when we use a low value of $\\beta$:"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "Da, Db, alpha, beta = 1, 100, 0.01, 0.25\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 200\ndx = 1\ndt = 0.001\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, height=width, \n dx=dx, dt=dt, steps=150\n).plot_evolution_outcome(\"2dRD_small_beta.png\", n_steps=100)",
"execution_count": 13,
"outputs": []
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "Da, Db, alpha, beta = 1, 100, 0.01, 0.25\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 200\ndx = 1\ndt = 0.001\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, height=width, \n dx=dx, dt=dt, steps=150\n).plot_time_evolution(\"2dRD_small_beta.gif\", n_steps=200)",
"execution_count": 14,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "[![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD_small_beta.png)](http://www.degeneratestate.org/static/turing-patterns/2dRD_small_beta.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD_small_beta.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](2dRD_small_beta.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Initial Conditions\n\nAdjusting the parameters gives us control over the length scale of the system. We can also gain some control over the system by adjusting the initial conditions of the system. For example, we can impose symmetries on the system by making the initial conditions symmetric:"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "from scipy.ndimage.interpolation import rotate\n\ndef average_rotate(a, degree):\n \"\"\"\n Takes a 2d array a, and produces the average arrays,\n having rotated it degree times. The resulting shape\n has approximate degree-fold rotational symmetry.\n \"\"\"\n theta = 360 / degree\n\n a = np.mean([rotate(a, theta * i, reshape=False) for i in range(degree)], axis=0)\n\n return a\n\n\ndef random_symmetric_initialiser(shape, degree):\n \"\"\"\n Random initialiser with degree-fold symmetry.\n \"\"\"\n \n a = np.random.normal(loc=0, scale=0.05, size=shape)\n b = np.random.normal(loc=0, scale=0.05, size=shape)\n\n return (\n average_rotate(a, degree), \n average_rotate(b, degree)\n )\n\nDa, Db, alpha, beta = 1, 100, 0.01, 1\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 200\ndx = 1\ndt = 0.001\n\n# three fold\nthree_fold_initialiser = lambda shape: random_symmetric_initialiser(shape, 3)\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n initialiser=three_fold_initialiser, \n width=width, height=width, \n dx=dx, dt=dt, steps=250\n).plot_evolution_outcome(\"2dRD_3_fold_sym.png\", n_steps=150)\n\n# five fold\nfive_fold_initialiser = lambda shape: random_symmetric_initialiser(shape, 5)\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n initialiser=five_fold_initialiser, \n width=width, height=width, \n dx=dx, dt=dt, steps=250\n).plot_evolution_outcome(\"2dRD_5_fold_sym.png\", n_steps=150)",
"execution_count": 15,
"outputs": []
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "from scipy.ndimage.interpolation import rotate\n\ndef average_rotate(a, degree):\n \"\"\"\n Takes a 2d array a, and produces the average arrays,\n having rotated it degree times. The resulting shape\n has approximate degree-fold rotational symmetry.\n \"\"\"\n theta = 360 / degree\n\n a = np.mean([rotate(a, theta * i, reshape=False) for i in range(degree)], axis=0)\n\n return a\n\n\ndef random_symmetric_initialiser(shape, degree):\n \"\"\"\n Random initialiser with degree-fold symmetry.\n \"\"\"\n \n a = np.random.normal(loc=0, scale=0.05, size=shape)\n b = np.random.normal(loc=0, scale=0.05, size=shape)\n\n return (\n average_rotate(a, degree), \n average_rotate(b, degree)\n )\n\nDa, Db, alpha, beta = 1, 100, 0.01, 1\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nwidth = 200\ndx = 1\ndt = 0.001\n\n# three fold\nthree_fold_initialiser = lambda shape: random_symmetric_initialiser(shape, 3)\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n initialiser=three_fold_initialiser, \n width=width, height=width, \n dx=dx, dt=dt, steps=250\n).plot_time_evolution(\"2dRD_3_fold_sym.gif\", n_steps=300)\n\n# five fold\nfive_fold_initialiser = lambda shape: random_symmetric_initialiser(shape, 5)\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n initialiser=five_fold_initialiser, \n width=width, height=width, \n dx=dx, dt=dt, steps=250\n).plot_time_evolution(\"2dRD_5_fold_sym.gif\", n_steps=300)",
"execution_count": 16,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Three fold rotational symmetry:\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD_3_fold_sym.png)\n\nFive fold rotational symmetry:\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD_5_fold_sym.png)\n\nThe symmetry isn't perfect due to implementing it one a square grid, but it works quite well."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Three fold rotational symmetry:\n\n![reaction](2dRD_3_fold_sym.gif)\n\nFive fold rotational symmetry:\n\n![reaction](2dRD_5_fold_sym.gif)\n\nThe symmetry isn't perfect due to implementing it one a square grid, but it works quite well."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Spatial Effects\n\nAnother interesting extension to these systems is to see what happens when we allow the parameters of the system to vary in space. In our implementation, this is just a matter of making the parameters a grid as well:"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "Da, Db, alpha, beta = 1, 100, 0.01, 10\n\nwidth = 200\ndx = 1\ndt = 0.001\n\nx,y = np.mgrid[0:width,0:width]\nbeta = 0.1 + 5 * (1 + np.sin(2 * np.pi * y / 50)) * (1 + np.sin(2 * np.pi * x / 50))\n\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, height=width, \n dx=dx, dt=dt, steps=100\n).plot_evolution_outcome(\"2dRD_spatial.png\", n_steps=200)",
"execution_count": 17,
"outputs": []
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "Da, Db, alpha, beta = 1, 100, 0.01, 10\n\nwidth = 200\ndx = 1\ndt = 0.001\n\nx,y = np.mgrid[0:width,0:width]\nbeta = 0.1 + 5 * (1 + np.sin(2 * np.pi * y / 50)) * (1 + np.sin(2 * np.pi * x / 50))\n\n\ndef Ra(a,b): return a - a ** 3 - b + alpha\ndef Rb(a,b): return (a - b) * beta\n\nTwoDimensionalRDEquations(\n Da, Db, Ra, Rb, \n width=width, height=width, \n dx=dx, dt=dt, steps=100\n).plot_time_evolution(\"2dRD_spatial.gif\", n_steps=400)",
"execution_count": 18,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "[![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD_spatial.png)](http://www.degeneratestate.org/static/turing-patterns/2dRD_spatial.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](http://www.degeneratestate.org/static/turing-patterns/2dRD_spatial.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "![reaction](2dRD_spatial.gif)"
},
{
"metadata": {
"collapsed": true
},
"cell_type": "markdown",
"source": "## Similar Systems \n\nTuring patterns are only one part of pattern generation from non-linear PDEs. Given we have the tools to explore these systems, it is worth looking at another simple system in which pattern formation occurs: [the Gray-Scott equations](https://groups.csail.mit.edu/mac/projects/amorphous/GrayScott/). As described in the paper [Complex Patterns in a Simple System](http://www.staff.science.uu.nl/~frank011/Classes/complexity/Literature/Pearson.pdf), for an interesting set of initial conditions the Gray-Scott equations result in a range complex patterns being formed. \n\nWhile these equations _are_ reaction-diffusion equations, the patterns that end up being formed are not from Turing instabilities. Instead they come from the initial conditions of the system: a small region in the centre of the region is perturbed. As this perturbation spreads out, strange patterns are formed. \n\nTo visualise it, we just need to pass another initialisation function of our previous class, and put in new reaction equations. The parameters for the interesting systems are taken from [here](http://www.aliensaint.com/uo/java/rd/). I'm not currently aware of any theory that gives a full explanation of these patterns. "
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "def gs_initialiser(shape):\n a = np.ones(shape)\n b = np.zeros(shape)\n centre = int(shape[0] / 2)\n \n a[centre-20:centre+20,centre-20:centre+20] = 0.5\n b[centre-20:centre+20,centre-20:centre+20] = 0.25\n \n a += np.random.normal(scale=0.05, size=shape)\n b += np.random.normal(scale=0.05, size=shape)\n \n return a,b \n\n# interesting parameters taken from http://www.aliensaint.com/uo/java/rd/\nparams = [\n [0.16, 0.08, 0.035, 0.065], \n [0.14, 0.06, 0.035, 0.065], \n [0.16, 0.08, 0.06, 0.062], \n [0.19, 0.05, 0.06, 0.062], \n [0.16, 0.08, 0.02, 0.055], \n [0.16, 0.08, 0.05, 0.065], \n [0.16, 0.08, 0.054, 0.063], \n [0.16, 0.08, 0.035, 0.06]\n]\n\nfor i, param in enumerate(params):\n \n Da, Db, f, k = param\n \n def Ra(a,b): return - a*b*b + f*(1-a)\n def Rb(a,b): return + a*b*b - (f+k)*b\n\n width = 200\n dx = 1\n dt = 1\n\n TwoDimensionalRDEquations(\n Da, Db, Ra, Rb,\n initialiser=gs_initialiser,\n width=width, height=width, \n dx=dx, dt=dt, steps=200\n ).plot_evolution_outcome(\"gs_{}.png\".format(i), n_steps=100)",
"execution_count": 19,
"outputs": []
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "def gs_initialiser(shape):\n a = np.ones(shape)\n b = np.zeros(shape)\n centre = int(shape[0] / 2)\n \n a[centre-20:centre+20,centre-20:centre+20] = 0.5\n b[centre-20:centre+20,centre-20:centre+20] = 0.25\n \n a += np.random.normal(scale=0.05, size=shape)\n b += np.random.normal(scale=0.05, size=shape)\n \n return a,b \n\n# interesting parameters taken from http://www.aliensaint.com/uo/java/rd/\nparams = [\n [0.16, 0.08, 0.035, 0.065], \n [0.14, 0.06, 0.035, 0.065], \n [0.16, 0.08, 0.06, 0.062], \n [0.19, 0.05, 0.06, 0.062], \n [0.16, 0.08, 0.02, 0.055], \n [0.16, 0.08, 0.05, 0.065], \n [0.16, 0.08, 0.054, 0.063], \n [0.16, 0.08, 0.035, 0.06]\n]\n\nfor i, param in enumerate(params):\n \n Da, Db, f, k = param\n \n def Ra(a,b): return - a*b*b + f*(1-a)\n def Rb(a,b): return + a*b*b - (f+k)*b\n\n width = 200\n dx = 1\n dt = 1\n\n TwoDimensionalRDEquations(\n Da, Db, Ra, Rb,\n initialiser=gs_initialiser,\n width=width, height=width, \n dx=dx, dt=dt, steps=200\n ).plot_time_evolution(\"gs_{}.gif\".format(i), n_steps=200)",
"execution_count": 20,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_0.png)](http://www.degeneratestate.org/static/turing-patterns/gs_0.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_1.png)](http://www.degeneratestate.org/static/turing-patterns/gs_1.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_2.png)](http://www.degeneratestate.org/static/turing-patterns/gs_2.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_3.png)](http://www.degeneratestate.org/static/turing-patterns/gs_3.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_4.png)](http://www.degeneratestate.org/static/turing-patterns/gs_4.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_5.png)](http://www.degeneratestate.org/static/turing-patterns/gs_5.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_6.png)](http://www.degeneratestate.org/static/turing-patterns/gs_6.gif)\n\n[![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_7.png)](http://www.degeneratestate.org/static/turing-patterns/gs_7.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_0.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_1.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_2.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_3.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_4.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_5.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_6.gif)\n\n![reaction](http://www.degeneratestate.org/static/turing-patterns/gs_7.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "\n![reaction](gs_0.gif)\n\n![reaction](gs_1.gif)\n\n![reaction](gs_2.gif)\n\n![reaction](gs_3.gif)\n\n![reaction](gs_4.gif)\n\n![reaction](gs_5.gif)\n\n![reaction](gs_6.gif)\n\n![reaction](gs_7.gif)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Conclusion\n\nIt's been interesting to play with these non-linear systems. One of the main challenges I've found with looking at them is how you find \"interesting\" parameters. I have some ideas about how we might search for these automatically, but that will have to wait for another post. \n\nThe complexity that can arise from these simple systems surprised me at fist, but it [has been shown](http://schulmanlab.jhu.edu/papers/reaction-diffusion-ca-journal-version.pdf) that it is possible to encode cellular automata in reaction diffusion systems. This suggests that the patterns formed by reaction-diffusion systems can be as complex as those produced by any Turing-complete system, and that any attempt to provide a quantitative explanation for their full behaviour will eventually run into some undecidable statement. \n\nAnd with that connection between Turing's work on reaction-diffusion equations and his more well known work on computability, I think it's time to end this post.\n\n## Code\nThis post was created from a jupyter notebook. You can find it [here](https://github.com/ijmbarr/turing-patterns)."
}
],
"metadata": {
"kernelspec": {
"name": "python3",
"display_name": "Python 3",
"language": "python"
},
"language_info": {
"name": "python",
"version": "3.6.2",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
},
"toc": {
"threshold": 4,
"number_sections": true,
"toc_cell": false,
"toc_window_display": false,
"toc_section_display": "block",
"sideBar": true,
"navigate_menu": true,
"moveMenuLeft": true,
"widenNotebook": false,
"colors": {
"hover_highlight": "#DAA520",
"selected_highlight": "#FFD700",
"running_highlight": "#FF0000",
"wrapper_background": "#FFFFFF",
"sidebar_border": "#EEEEEE",
"navigate_text": "#333333",
"navigate_num": "#000000"
},
"nav_menu": {
"height": "381px",
"width": "252px"
}
},
"gist": {
"id": "",
"data": {
"description": "turing-patterns-creating-animated-gifs.ipynb",
"public": true
}
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment