<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts | MAMOC</title><link>https://mamoc.netlify.app/post/</link><atom:link href="https://mamoc.netlify.app/post/index.xml" rel="self" type="application/rss+xml"/><description>Posts</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><image><url>https://mamoc.netlify.app/media/logo_hu5a202d189398ef4588a5475cdd17d46b_34335_300x300_fit_lanczos_3.png</url><title>Posts</title><link>https://mamoc.netlify.app/post/</link></image><item><title>The Spatial Ecology of Predator-Prey Systems.</title><link>https://mamoc.netlify.app/post/spatialecology/spatialecology/</link><pubDate>Sat, 18 Nov 2023 00:00:00 +0000</pubDate><guid>https://mamoc.netlify.app/post/spatialecology/spatialecology/</guid><description>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/media/spatialEcology/mamoc2a4_hu8b518cba082e8e642a0a92d33d5f236b_2220604_48e9ef209f6d9e085534155d0ac870d5.webp 400w,
/media/spatialEcology/mamoc2a4_hu8b518cba082e8e642a0a92d33d5f236b_2220604_5e804c6d4b232d107a2a3d7e47b77e32.webp 760w,
/media/spatialEcology/mamoc2a4_hu8b518cba082e8e642a0a92d33d5f236b_2220604_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/spatialEcology/mamoc2a4_hu8b518cba082e8e642a0a92d33d5f236b_2220604_48e9ef209f6d9e085534155d0ac870d5.webp"
width="536"
height="760"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;h2 id="abstract">Abstract&lt;/h2>
&lt;p>This study focuses on two methods of computationally modelling systems of many interacting particles of two species. The first method is the general idea of reaction-diffusion systems, which are mathematical differential constructs that describe how spatial and temporal patterns can emerge from the interactions between mathematical species and their environment. Also considered are agent based models, which instead only use hyper-local information and do not rely on macroscopic differential equations at all. In particular, the predator prey system is considered from both perspectives. By coupling local interaction rules with movement, these models elucidate the dynamics of population spread, predator-prey relationships, and the emergence of patterns in ecosystems.&lt;/p>
&lt;h2 id="introduction-to-reaction-diffusion-systems">Introduction to reaction-diffusion systems&lt;/h2>
&lt;p>Theoretical ecology is a mathematical framework for understanding how the interactions of individual organisms with each other and with the environment determine the distribution of populations and the structure of communities. Many different models are needed, each based on some set of hypotheses about the scale and structure of the spatial environment and the way that organisms disperse through it. When many agents interact in simple ways, sometimes emergent properties can be observed in their collective, such as the propagation of wavefronts or the formation of patterns. These patterns are glimpses of structure from chaos, and have prompted countless minds to become fascinated by the connections between maths and biology.&lt;/p>
&lt;p>Reaction-diffusion models are a way of translating the simple assumptions about ways agents can move and interact on the local level into global conclusions about the persistence or extinction of populations and the coexistence of interacting species. Born from the macroscopic observations of molecular diffusion, reaction-diffusion models now more widely refer to any event-driven system of interacting moving agents, and occur and many different scales.&lt;/p>
&lt;p>In biology, the great success of reaction-diffusion mathematics is the Turing model, which describes how homogeneous groups of cells in an embryo can spontaneously differentiate into patterns, like the spots and stripes on animal skins [&lt;a href="https://royalsocietypublishing.org/doi/10.1098/rstb.1952.0012" title="Turing, A. “The Chemical Basis of Morphogenesis.” Bulletin of Mathematical Biology, vol. 52, no. 1–2, Jan. 1990, pp. 153–97." target="_blank" rel="noopener">1&lt;/a>].&lt;/p>
&lt;figure id="figure-on-the-left-a-computationally-generated-turing-pattern-on-the-right-an-example-of-turing-patterns-found-organically-in-nature-on-the-beautiful-patterns-on-the-skin-of-fish-photo-credit-dennis-jacobsenshutterstock">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="On the left, a computationally generated Turing pattern. On the right, an example of Turing patterns found organically in nature, on the beautiful patterns on the skin of fish. Photo credit: Dennis Jacobsen/Shutterstock" srcset="
/media/spatialEcology/turingfishandpattern_hu7c7391cd83b1a8fe97fb9fc7a6a87e13_2025751_eb9f3473f276e6e7112aa6829c5571c5.webp 400w,
/media/spatialEcology/turingfishandpattern_hu7c7391cd83b1a8fe97fb9fc7a6a87e13_2025751_a487f9949391f68f59521839e0cfb951.webp 760w,
/media/spatialEcology/turingfishandpattern_hu7c7391cd83b1a8fe97fb9fc7a6a87e13_2025751_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/spatialEcology/turingfishandpattern_hu7c7391cd83b1a8fe97fb9fc7a6a87e13_2025751_eb9f3473f276e6e7112aa6829c5571c5.webp"
width="760"
height="254"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
On the left, a computationally generated Turing pattern. On the right, an example of Turing patterns found organically in nature, on the beautiful patterns on the skin of fish. Photo credit: Dennis Jacobsen/Shutterstock
&lt;/figcaption>&lt;/figure>
&lt;p>In chemistry, the Belousov-Zhabotinsky chemical reaction is an incredible display of dispersive concentric patterns. Further examples are as distributed as econometric information diffusion and as grievous as modelling the spread of forest fires.&lt;/p>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/6QESEa-00a0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;br>
These systems are linked, and the outcomes we see from their patterns are directly caused by similarities in the form of the differential equations which govern their spatio-temporal evolution. They share the two species reaction diffusion equations, which are given by&lt;/p>
&lt;p>$$
\begin{pmatrix} \dot q_{1} \\ \dot q_{2} \end{pmatrix} =
\begin{pmatrix} D_{1} \\ D_{2} \end{pmatrix} \cdot
\nabla^{2} \begin{pmatrix}q_{1} \\ q_{2} \end{pmatrix} +
\begin{pmatrix} R_{1}(q_{1}, q_{2}) \\ R_{2}(q_{1}, q_{2}) \end{pmatrix},$$&lt;/p>
&lt;p>where $q_{1,2}$ describes the concentration of either species, $D_{1,2}$ their diffusion coefficients, and $R_{1,2}(q_{1}, q_{2})$ are functions representing the agent&amp;rsquo;s interactive and local behaviour. Decomposing the equation, the first term on the right hand side can be recognised as Fick’s second law of diffusion, $ \dot q = D \nabla^{2} q$ for a concentration $q$ [&lt;a href="https://en.wikipedia.org/wiki/Fick%27s_laws_of_diffusion#Fick%27s_second_law" title="Wikipedia Contributors. “Fick’s Laws of Diffusion.” Wikipedia, Wikimedia Foundation, 14 Nov. 2023, en.wikipedia.org/wiki/Fick%27s_laws_of_diffusion#Fick. Accessed 18 Nov. 2023." target="_blank" rel="noopener">2&lt;/a>].&lt;/p>
&lt;p>The dynamics of our model is specified by the rates at which individuals move and die or reproduce. As such, a good place to start is to consider the local mechanics of our agent movement and reactions, supposing that agents may only move to a randomly chosen nearest neighbour of their location, and reproduce or die at rates which depend on the number of individuals at the same location. Using some simplifying assumptions along the way, there are creative ways to derive the reaction term, which is usually, hopefully, linear in $q_{1,2}$. Often, however, we are not so lucky. Spatial models often unavoidably invite non-linear terms, resulting in chaotic long-term behaviour.&lt;/p>
&lt;h2 id="lotka-volterra-equations">Lotka-Volterra equations.&lt;/h2>
&lt;h3 id="without-spatial-dependence">Without spatial dependence.&lt;/h3>
&lt;p>This example, the Lotka-Volterra equations, are a model for the population dynamics of a predator species and a prey species, perhaps foxes and rabbits, and is a convinient way to explore how reaction terms work. Choosing to completely ignore spatial dimensions, the mean field assumption can be adopted: that all agents interact with the average effect of all others. Letting $ x_1(t) $ denote the prey population and $x_2(t) $ denote the predator population at time $t $,&lt;/p>
&lt;p>$$
\dot x_1 = \alpha x_1 - \beta x_1 x_2,$$
$$\dot x_2 = \delta x_1 x_2 - \gamma x_2.
$$&lt;/p>
&lt;ul>
&lt;li>
&lt;p>$ \alpha $ is the natural birth rate of the prey in the absence of predators.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$ \beta $ is the death rate of the prey due to predation.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$ \gamma $ is the natural death rate of the predators in the absence of prey.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$\delta $ is the rate at which predators increase by consuming prey.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Reading off from the general form of reaction-diffusion equations, $D_1$ and $D_2$ have been set to zero, and the remaining terms are the reactive terms $R_{1,2}(q_{1}, q_{2})$. Below, linear stability analysis is used with the method outlined by Strogatz [&lt;a href="https://www.biodyn.ro/course/literatura/Nonlinear_Dynamics_and_Chaos_2018_Steven_H._Strogatz.pdf" title="Strogatz, Steven, author. Nonlinear Dynamics and Chaos : with Applications to Physics, Biology, Chemistry, and Engineering. Boulder, CO :Westview Press, a member of the Perseus Books Group, 2015." target="_blank" rel="noopener">3&lt;/a> pp.151-152]. Firstly, the steady states of this system of differential equations are found by setting the time derivatives to zero. The trivial solution $x_1 = x_2 = 0$ indicates mutual extinction, but otherwise, solving for constants, we get ${x_1, x_2} = {\gamma / \delta, \alpha / \beta}$. Secondarily, to analyse the stability of these steady state solutions, our set of differential equations are linearised so that we have&lt;/p>
&lt;p>$$J = \begin{pmatrix} \frac{\partial f_1}{\partial x_1} &amp;amp; \frac{\partial f_1}{\partial x_2} \\
\frac{\partial f_2}{\partial x_1} &amp;amp; \frac{\partial f_2}{\partial x_2} \end{pmatrix} = \begin{pmatrix} \alpha - \beta x_2 &amp;amp; -\beta x_1 \\
\delta x_2 &amp;amp; \delta x_1 - \gamma \end{pmatrix} = \begin{pmatrix} 0 &amp;amp; -\beta \frac{\gamma}{\delta} \\
\delta \frac{\alpha}{\beta} &amp;amp; 0 \end{pmatrix}.$$&lt;/p>
&lt;p>The trivial solution has eigenvalues $\alpha$ and $-\gamma$, indicating a saddle point [&lt;a href="https://www.biodyn.ro/course/literatura/Nonlinear_Dynamics_and_Chaos_2018_Steven_H._Strogatz.pdf" title="Strogatz, Steven, author. Nonlinear Dynamics and Chaos : with Applications to Physics, Biology, Chemistry, and Engineering. Boulder, CO :Westview Press, a member of the Perseus Books Group, 2015." target="_blank" rel="noopener">3&lt;/a>, p.130]. This is good, because saddle points are unstable, meaning the model does not predict spiralling uncontrollably towards extinction. The other matrix has eigenvalues $+i \sqrt{\alpha \gamma}$ and $- i \sqrt{\alpha \gamma}$, which indicates periodic trigonometric-esque solutions.&lt;/p>
&lt;figure id="figure-these-curves-represent-a-typical-periodic-steady-state-solution-to-the-lotka-volterra-equations">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="These curves represent a typical periodic steady state solution to the Lotka-Volterra equations" srcset="
/media/spatialEcology/LotkaVolterra_hud4524dcefe6802eae7038d96164df806_55194_0bb8c1a289ea9dfd20ee0aa8ac7a4826.webp 400w,
/media/spatialEcology/LotkaVolterra_hud4524dcefe6802eae7038d96164df806_55194_cd26b8c5e3577e094c22da779892ed35.webp 760w,
/media/spatialEcology/LotkaVolterra_hud4524dcefe6802eae7038d96164df806_55194_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/spatialEcology/LotkaVolterra_hud4524dcefe6802eae7038d96164df806_55194_0bb8c1a289ea9dfd20ee0aa8ac7a4826.webp"
width="760"
height="425"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
These curves represent a typical periodic steady state solution to the Lotka-Volterra equations
&lt;/figcaption>&lt;/figure>
&lt;p>The model elegantly captures the core ideas: prey populations grow naturally but are eaten by predators, and predator populations decline without food but grow when they eat prey. That said, the model is limited, as it never predicts extinction for nonzero initial populations. A better model would include a notion of self competition in the individual populations, perhaps stemming from a fixed supply of food. Such a model would be self-limiting. A better model,&lt;/p>
&lt;p>$$
\dot x_1 = \alpha x_1 (1 - x_1 / \eta_{1}) - \beta x_1 x_2,$$
$$\dot x_2 = \gamma x_2 (1 - x_2 / \eta_{2}) - \delta x_1 x_2
$$&lt;/p>
&lt;p>introduces the notion of independent growth limitation on the individual populations $x_1$ and $x_2$ to
the system through the Logistic model. The advantages is this allows one species to “win”, in addition to cases where both populations settle on fixed values. But there are limitations. The periodic/cyclic nature of the original Lotka-Volterra model is lost.&lt;/p>
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/spatialEcology/LVselflimiting.mp4" type="video/mp4">
&lt;/video>
&lt;h2 id="adding-spatial-dependence-to-the-lotka-volterra-model">Adding spatial dependence to the Lotka-Volterra model&lt;/h2>
&lt;p>The main ingredient missing from these models is the absence of spatial dependence in the model. Further, there is reason to be concerned with the mean field assumption that we adopted before, which states that all agents interact with the average effect of all the others agents. In reality, agents exist in 2D space and can only interact with their immediate spatial neighbours, and the model should reflect this.&lt;/p>
&lt;p>One option is to add in our our Fick’s law inspired diffusion terms, and so we define the spatial Lotka-Volterra equations&lt;/p>
&lt;p>$$\frac{\partial x_1}{\partial t} = \alpha x_1 - \beta x_1 x_2 + D_1 \nabla^2 x_1,$$&lt;/p>
&lt;p>$$\frac{\partial x_2}{\partial t} = \delta x_1 x_2 - \gamma x_2 + D_2 \nabla^2 x_2.$$&lt;/p>
&lt;p>This set of differential equations can be modelled computationally on a randomised initial population to show the progression of the reaction-diffusion system. We can use an Euler-esque method for executing this progression, by multiplying our differential equations by a timestep $dt$ to get a $dX$, and adding that to $X$ each iterative timestep $t$.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">steps&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">steps&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Lotka-Volterra equations with added diffusion Laplacians&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">laplacian_X1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">lapalcian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">X1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">laplacian_X2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">lapalcian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">X2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dX1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">alpha&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">X1&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">beta&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">X1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">X2&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">Dx&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">laplacian_X1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">dt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dX2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">gamma&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">X2&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">delta&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">X1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">X2&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">Dy&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">laplacian_X2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">dt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">X1&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">dX1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">X2&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">dX2&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Animated, we get our first look at the emergent patterns of spatial ecology. The background is black, and the areas that are more green have a higher density of prey, and the areas that are more red have a higher density of predators. Both populations are initialised with a random uniform distribution. The population densities are continuous variables, and do not represent discrete population sizes, as much the general spatial flux of species densities.&lt;/p>
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/spatialEcology/LV1.mp4" type="video/mp4">
&lt;/video>
&lt;p>The patterns of dots and stripes that form and then disappear are reminiscent of Turing patterns, and in the appendix, we will consider whether these are Turing patterns, how they form, and why they also seem to disperse over time.&lt;/p>
&lt;h2 id="agent-based-models-for-two-species-population-dynamics">Agent based models for two species population dynamics.&lt;/h2>
&lt;p>Spatial ecology can also be modelled computationally using agent-based models, and the benefit of this is that it did not assume the truth of a set of universal differential equations. As such, agent based models offer a completely different method for simulating systems of interacting particles by operating exclusively at the local level, with no macroscopic information shared to individual agents. Diffusion occurs via steps in random directions. Reactions occur based on only local information, such as if two interacting agents of the same species at the same place have enough energy to procreate. While these models are much closer approximations to real life, they still lack the macroscopic dynamics of flocking, herding, fleeing, or hunting. As such, it may make sense to imagine these agents as small groups of microorganisms, diffusing due to the random flow of Brownian motion, rather than intelligent autonomous animals.&lt;/p>
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/spatialEcology/Example1.mp4" type="video/mp4">
&lt;/video>
&lt;h3 id="program-overview">Program overview&lt;/h3>
&lt;p>The iterative time steps of my program is as simple as&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">TheGrid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ReactionDiffusionLibrary&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Grid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gridxsize&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">gridysize&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Prey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ReactionDiffusionLibrary&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Species&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Prey&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Prey_E0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Prey_EP&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Prey_N&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">TheGrid&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Pred&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ReactionDiffusionLibrary&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Species&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Predator&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Pred_E0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Pred_EP&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Pred_N&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">TheGrid&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">steps&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">step&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">steps&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Prey&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Move&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Pred&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Move&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TheGrid&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Interact&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Prey&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Pred&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>where the Prey, Pred, and TheGrid instances are C# classes incorporated into the python script using pythonnet clr. The two main mechanisms of reaction-diffusion are contained in the &lt;code>Move&lt;/code> method and the &lt;code>Interact&lt;/code> method. Here is &lt;code>Move&lt;/code>,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c#" data-lang="c#">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">Move&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">double&lt;/span>&lt;span class="p">[]&amp;gt;&lt;/span> &lt;span class="n">SpeciesCoords&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">double&lt;/span>&lt;span class="p">[]&amp;gt;(&lt;/span>&lt;span class="n">AgentsList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Count&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Random&lt;/span> &lt;span class="n">random&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Random&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">AgentsList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Count&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">++)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Move this agent to a random nearest neighbour square&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">agent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">AgentsList&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">dirIndex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Grid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Directions&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">dirIndex&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SpeciesCoords&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="kt">double&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Y&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Keep track of what agents are in what squares&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">char&lt;/span> &lt;span class="n">yOrDCondition&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">PredOrPrey&lt;/span> &lt;span class="p">==&lt;/span> &lt;span class="s">&amp;#34;Prey&amp;#34;&lt;/span> &lt;span class="p">?&lt;/span> &lt;span class="sc">&amp;#39;y&amp;#39;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="sc">&amp;#39;d&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AgentId&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">$&amp;#34;{yOrDCondition}{i}&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AgentIndex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Grid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AgentsInGrid&lt;/span>&lt;span class="p">[(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">][(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Y&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AgentId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Grid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteAgentsInGridToFile&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Each iteration, the coordinates of all the agents in the species are rewritten based on their movement in a random direction, and this also helped with dealing with the non constant number of agents due to births and deaths.&lt;/p>
&lt;p>The one alteration I made to the program to make it not a strictly agent based model is the &lt;code>Dying&lt;/code> boolean, which turns on when a species total number of agents dips below 1000 gridwide. This allowed agents to procreate on their own whenever the species has sufficiently low numbers rather than needing to randomly stumble upon another of their species on the grid. To justify this, we can imagine each dot as a small collection of microorganisms rather than an individual.&lt;/p>
&lt;p>The &lt;code>Interact&lt;/code> method iterates through each square in the grid, collecting pairs of agents. If two agents of the same species are in the same grid square, and they have above the minimum procreation energy, they will breed and produce another agent at that square. If a predator is in the same square as a prey, it will feed on the prey, killing it and absorbing its energy.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c#" data-lang="c#">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">Interact&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Species&lt;/span> &lt;span class="n">PreySpeciesObj&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Species&lt;/span> &lt;span class="n">PredSpeciesObj&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">ySize&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GridYSize&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">xSize&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GridXSize&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Check each Grid cell&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">y_i&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">y_i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">ySize&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">y_i&lt;/span>&lt;span class="p">++)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">x_i&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">x_i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">xSize&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">x_i&lt;/span>&lt;span class="p">++)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">agents&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">AgentsInGrid&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">y_i&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">x_i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">agents&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Count&lt;/span> &lt;span class="p">==&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">preys&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">preds&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Check each agent in a Grid cell, and sort into species&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">agentStr&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">agents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">agentStr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">StartsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sc">&amp;#39;y&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="n">preys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">agentStr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">agentStr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">StartsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sc">&amp;#39;d&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="n">preds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">agentStr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Predators breed and feed on prey&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Procreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">preds&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PredSpeciesObj&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">preds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Count&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">++)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">preys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Agent&lt;/span> &lt;span class="n">predator&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">PredSpeciesObj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AgentsList&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">preds&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Agent&lt;/span> &lt;span class="n">food&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">PreySpeciesObj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AgentsList&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">preys&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">predator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Energy&lt;/span> &lt;span class="p">+=&lt;/span> &lt;span class="n">food&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Energy&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">food&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddToDeathList&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Preys breed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Procreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">preys&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PreySpeciesObj&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// At the end of the turn, make babies into adults and handle deaths&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PreySpeciesObj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">NewDay&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PredSpeciesObj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">NewDay&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ClearAgentsInGrid&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="design-considerations">Design considerations&lt;/h3>
&lt;h5 id="energy">Energy&lt;/h5>
&lt;p>Prey generate energy stochastically but passively, and will do so as long as they are not eaten. Predators can only generate energy by feeding on prey. Energy on average is gained rather than lost throughout the simulation. However, existing in the same gridsquare as another agent of your own species incurs an energy cost directly proportional to the number of agents there. For example, if $40$ agents occupy the same gridspace, they will each suffer an energy cost of $40$ energy that iteration. This is meant to replicate an energy cost due to limited food supply at any one area, and makes the populations display self-limiting behaviour.&lt;/p>
&lt;h5 id="procreation">Procreation&lt;/h5>
&lt;p>To procreate, agents must bump into each other on the same gridspace, and if both parents have above a minium energetic threshold, they will procreate and suffer an energy cost. There is no limitation on procreating with your own offspring. To avoid extinction in my simulations&lt;/p>
&lt;h5 id="death">Death&lt;/h5>
&lt;p>Agents can die in two ways, running out of energy or being eaten. In this model, only prey may be eaten. While prey passively generate energy, they may still die from running out of energy if they live too long in areas with a high density of prey. Predators more usually die from running out of energy, by not eating enough prey.&lt;/p>
&lt;h3 id="learnings-from-animating-the-agent-based-model">Learnings from animating the agent based model&lt;/h3>
&lt;p>The results are different from the Lotka-Volterra animation. Instead of smoothly fading between areas of high prey density and high predator density, we see vast areas of blank space. But still, some similar emergent properties can be seen, in particular the collections of agents evolving in wavefronts.&lt;/p>
&lt;p>Explosive pockets of mass generations of new agents can be seen, but why do they occur? The spatial and energetic restrictions on when procreation can occur impede local procreation rates, which can lead to huge gains in average agent energy. If enough energy is stored up and a species begins to bump into itself, a chain reaction of procreation is set-off. The limiting factor of this is usually the rate of diffusion, which leads to most agents in the chain reaction dying due to the energy cost of occupying the same gridsquare as many others before they can move away to safer areas.&lt;/p>
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/spatialEcology/Example2.mp4" type="video/mp4">
&lt;/video>
&lt;p>Expanding the size of the grid, the emergence of wavefronts becomes more obvious. We can begin to see expanding florets of prey, chased down by a secondary wave of predators. These expanding wavefronts sometimes stochastically form similar concentric strucures to the patterns formed by the Belousov-Zhabotinsky chemical reaction.&lt;/p>
&lt;p>Each time the model is run, different patterns are generated. A glance at the population curves below for a selection of runs shows that in the early stages the curves appear self-similar. The long term behaviour is more unpredictable, and includes large spikes when the species has amassed a high energy and begins to collide into itself, provoking an uncontrolled chain reaction of procreation.&lt;/p>
&lt;figure id="figure-an-overview-of-the-population-curves-for-nine-runs-of-the-agent-based-model-as-throughout-the-green-line-represents-the-prey-population-and-red-represents-the-predator-population">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="An overview of the population curves for nine runs of the agent based model. As throughout, the green line represents the prey population and red represents the predator population" srcset="
/media/spatialEcology/PopulationCurves_hudc36992af8aa5337ab9543474733b9af_275924_ff838a9e2bb93d2cfec8499e30f1d022.webp 400w,
/media/spatialEcology/PopulationCurves_hudc36992af8aa5337ab9543474733b9af_275924_b70d816156bd1176a51dee3f65420046.webp 760w,
/media/spatialEcology/PopulationCurves_hudc36992af8aa5337ab9543474733b9af_275924_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/spatialEcology/PopulationCurves_hudc36992af8aa5337ab9543474733b9af_275924_ff838a9e2bb93d2cfec8499e30f1d022.webp"
width="336"
height="760"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
An overview of the population curves for nine runs of the agent based model. As throughout, the green line represents the prey population and red represents the predator population
&lt;/figcaption>&lt;/figure>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>Two models were examined in this article. &lt;a href="https://github.com/cameron-michie/ReactionDiffusion" target="_blank" rel="noopener">Feel free to check out my github.&lt;/a>.&lt;/p>
&lt;p>The agent based model was the most enjoyable, the most challenging, and took the most time. For performance reasons, the backend had to be rewritten in C#, but this was a fun challenge. The main interface and graphing remained in python. This model could easily be used for further research or study. For instance, agent based models have recently become hugely important in predicting the mature structure of bacterial biofilms, particularly to mirror how biofilms react to stimuli in lab scenarios.&lt;/p>
&lt;h1 id="appendix">Appendix&lt;/h1>
&lt;p>&lt;strong>Derivation of how Turing patterns form and why they disperse in the spatial Lotka Volterra equations&lt;/strong>&lt;/p>
&lt;p>Patterns seem to form in the spatial Lotka reaction-diffusion simulation for predators and prey in a 2D grid. These patterns seem similar to Turing patterns commonly seen in nature. The equations I have called the spatial Lotka-Volterra equations were formed by adding in diffusive Laplacian terms to the classic Lotka-Volterra equations.&lt;/p>
&lt;p>$$\frac{\partial x_1}{\partial t} = \alpha x_1 - \beta x_1 x_2 + D_1 \nabla^2 x_1 $$&lt;/p>
&lt;p>$$\frac{\partial x_2}{\partial t} = \delta x_1 x_2 - \gamma x_2 + D_2 \nabla^2 x_2 $$&lt;/p>
&lt;p>To prove that the given system of equations produces Turing patterns [&lt;a href="http://be150.caltech.edu/2019/handouts/21_turing.html#:~:text=21_turing.%20Reaction,is%20a%20stable" title="Bois, J and Elowitz, M (2019) &amp;#39;Turing patterns&amp;#39; CalTech" target="_blank" rel="noopener">4&lt;/a>], we need to perform a linear stability analysis around a homogeneous steady state solution, and look for conditions under which perturbations grow in a spatially inhomogeneous way. We will follow the method here [&lt;a href="https://link.springer.com/chapter/10.1007/978-3-319-19500-1_7#:~:text=In%20his%20seminal%20paper%2C%20A,also%20called%20%E2%80%98diffusion%20driven%20instabilities%E2%80%99" title="Perthame, B. 2015. Linear Instability, Turing Instability and Pattern Formation. Lecture Notes on Mathematical Modelling in the Life Sciences, pp. 117–143. Available at: http://dx.doi.org/10.1007/978-3-319-19500-1_7." target="_blank" rel="noopener">5&lt;/a>]. First, let&amp;rsquo;s find the homogeneous steady state $(x_1^{*}, x_2^{*})$ for a spatially uniform state by setting&lt;/p>
&lt;p>$$ \frac{\partial x_1}{\partial t} = \frac{\partial x_2}{\partial t} = 0, \nabla^2 x_1 = \nabla^2 x_2 = 0.$$&lt;/p>
&lt;p>Subbing this into the above spatial Lotka Volterras, we get
$$
0 = \alpha x_1^* - \beta x_1^* x_2^*,$$
$$0 = \delta x_1^* x_2^* - \gamma x_2^*.
$$&lt;/p>
&lt;p>Solving for $ x_1^{*} $ and $ x_2^{*} $,
$ x_2^{*} = \frac{\alpha}{\beta}$ and
$ x_1^* = \frac{\gamma}{\delta x_2^{*}} = \frac{\gamma \beta}{\alpha \delta}$&lt;/p>
&lt;p>Next, we linearize the system around the steady state by letting $ x_1 = x_1^* + u $ and $ x_2 = x_2^* + v $, where $ u $ and $ v $ are small perturbations. We then substitute these into our system and keep only linear terms in $ u $ and $ v $. After linearization, the system becomes:
$$
\partial_t u = \alpha u - \beta (x_1^* v + x_2^* u) + D_1 \nabla^2 u$$
$$
\partial_t v = \delta (x_1^* v + x_2^* u) - \gamma v + D_2 \nabla^2 v
$$&lt;/p>
&lt;p>Substituting the steady-state values and simplifying we get
$$
\partial_t u = (\alpha - \beta x_2^*) u - \beta x_1^* v + D_1 \nabla^2 uu = - \frac{\beta \gamma}{\delta} v + D_1 \nabla^2 u $$
$$\partial_t v = \delta x_2^* u + (\delta x_1^* - \gamma) v + D_2 \nabla^2 v = \delta x_2^* u + (\delta x_1^* - \gamma) v + D_2 \nabla^2 .$$&lt;/p>
&lt;p>So, we have our fully linearized system which we can write in matrix form as:&lt;/p>
&lt;p>$$
\begin{pmatrix} \partial_t u, \\
\partial_t v \end{pmatrix} =\begin{pmatrix} D_1 \nabla^2 &amp;amp; -\frac{\beta \gamma}{\delta} \\
\frac{\alpha \delta}{\beta} &amp;amp; D_2 \nabla^2 - \gamma
\end{pmatrix}
\begin{pmatrix}
u \\
v
\end{pmatrix}
$$&lt;/p>
&lt;p>Now we look for solutions in the form of $ e^{\lambda t + ik\cdot r} $, where $ \lambda $ is the growth rate of the perturbation, $ k $ is the wave number, and $ r $ is the position vector.&lt;/p>
&lt;p>We substitute this form into the linearized system and solve for $ \lambda $. The dispersion relation is obtained by setting the determinant of the matrix minus $ \lambda I $ to zero:&lt;/p>
&lt;p>$$
\det
\begin{pmatrix}
D_1 k^2 - \lambda &amp;amp; -\frac{\beta \gamma}{\delta} \\
\frac{\alpha \delta}{\beta} &amp;amp; D_2 k^2 - \gamma - \lambda
\end{pmatrix}
= 0
$$&lt;/p>
&lt;p>Solving the characteristic equation, we get:
$$
(D_1 k^2 - \lambda)(D_2 k^2 - \gamma - \lambda) - \left(-\frac{\beta \gamma}{\delta}\right)\left(\frac{\alpha \delta}{\beta}\right) = 0
$$&lt;/p>
&lt;p>Expanding and simplifying gives us a quadratic equation in $\lambda $,
$$
\lambda^2 + \lambda(D_1 k^2 + D_2 k^2 - \gamma) + h(k^2) = 0.
$$
where $ h(k^2) = D_1 D_2 k^4 - \gamma D_1 k^2 - \alpha \gamma$.&lt;/p>
&lt;p>For Turing instability, we need [&lt;a href="Haas,%20P.A.%20and%20Goldstein,%20R.E.,%202021.%20Turing%e2%80%99s%20diffusive%20threshold%20in%20random%20reaction-diffusion%20systems.%20Physical%20Review%20Letters,%20126%2823%29,%20p.238101." title="https://www.condmatjclub.org/uploads/2021/01/JCCM_January_2021_01.pdf">6&lt;/a>]:&lt;/p>
&lt;ul>
&lt;li>the homogeneous system to be stable without diffusion, which we proved in the linear stability analysis in the previous section,&lt;/li>
&lt;li>and the diffusion terms to destabilize the homogeneous steady state, meaning the real part of $ \lambda $ must be positive for some range of $k^2$.&lt;/li>
&lt;/ul>
&lt;p>And so, one way to find evidence of Turing instability would be to look for any sign change in the real part of $\lambda (k^2)$, which would indicate there exists a range of wavenumbers for which patterns will emerge.&lt;/p>
&lt;p>Instead, we will reason that if we have $h(k^2) &amp;lt; 0$ for some $k^2$, it must be true that $ \lambda(k^2)$ is positive for that same interval of $k^2$, because of the positive $\lambda$ coefficients in $\lambda^2 - \lambda(D_1 k^2 + D_2 k^2 - \gamma) + h(k^2) = 0.$&lt;/p>
&lt;p>Next, performing a quick change of variables $z = k^2$,&lt;/p>
&lt;p>$$\frac{dh(z=k^2)}{dz}=2 D_1 D_2 z - \gamma D_1,$$&lt;/p>
&lt;p>we aim to find the stationary point so set $\frac{h(z^{*})}{dz}$ to zero, and prove its minimum is negative. We get $z^{*}=\frac{\gamma}{2 D_2}$, and plugging this into $h(z)$ gives a value for $h_{min}$ (since $h(z)$ is clearly concave),&lt;/p>
&lt;p>$$h_{min}(z^{*}=\frac{\gamma}{2 D_2}) = \frac{D_1 D_2 \gamma^2}{4 D_2} - \frac{D_1 \gamma^2}{2 D_2} - \alpha \gamma = - \frac{D_1 \gamma^2}{D_2} - \alpha \gamma$$&lt;/p>
&lt;p>Since $ \alpha, \gamma, D_1, D_2 $ are necessarily $&amp;gt;0$, we have proved that for $h(k^2)$ is necessarily negative for some range of $k^2$, and that $\lambda $ is positive for that same range of $k^2$. Why this leads to Turing instabilities is explained in more detail here [&lt;a href="Haas,%20P.A.%20and%20Goldstein,%20R.E.,%202021.%20Turing%e2%80%99s%20diffusive%20threshold%20in%20random%20reaction-diffusion%20systems.%20Physical%20Review%20Letters,%20126%2823%29,%20p.238101." title="https://www.condmatjclub.org/uploads/2021/01/JCCM_January_2021_01.pdf">6&lt;/a>].&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;a href="https://royalsocietypublishing.org/doi/10.1098/rstb.1952.0012" target="_blank" rel="noopener">Turing, A. “The Chemical Basis of Morphogenesis.” Bulletin of Mathematical Biology, vol. 52, no. 1–2, Jan. 1990, pp. 153–97.&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Fick%27s_laws_of_diffusion#Fick%27s_second_law" target="_blank" rel="noopener">Wikipedia Contributors. “Fick’s Laws of Diffusion.” Wikipedia, Wikimedia Foundation, 14 Nov. 2023, en.wikipedia.org/wiki/Fick%27s_laws_of_diffusion#Fick. Accessed 18 Nov. 2023.&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.biodyn.ro/course/literatura/Nonlinear_Dynamics_and_Chaos_2018_Steven_H._Strogatz.pdf" target="_blank" rel="noopener">Strogatz, Steven, author. Nonlinear Dynamics and Chaos : with Applications to Physics, Biology, Chemistry, and Engineering. Boulder, CO :Westview Press, a member of the Perseus Books Group, 2015.&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="http://be150.caltech.edu/2019/handouts/21_turing.html#:~:text=21_turing.%20Reaction,is%20a%20stable" target="_blank" rel="noopener">Bois, J and Elowitz, M (2019) &amp;lsquo;Turing patterns&amp;rsquo; CalTech&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://link.springer.com/chapter/10.1007/978-3-319-19500-1_7#:~:text=In%20his%20seminal%20paper%2C%20A,also%20called%20%E2%80%98diffusion%20driven%20instabilities%E2%80%99" target="_blank" rel="noopener">Perthame, B. 2015. Linear Instability, Turing Instability and Pattern Formation. Lecture Notes on Mathematical Modelling in the Life Sciences, pp. 117–143.&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.condmatjclub.org/uploads/2021/01/JCCM_January_2021_01.pdf" target="_blank" rel="noopener">Haas, P.A. and Goldstein, R.E., 2021. Turing’s diffusive threshold in random reaction-diffusion systems. Physical Review Letters, 126(23), p.238101.&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Learning to walk with Novelty Search</title><link>https://mamoc.netlify.app/post/neuroev/</link><pubDate>Mon, 30 Oct 2023 00:00:00 +0000</pubDate><guid>https://mamoc.netlify.app/post/neuroev/</guid><description>&lt;h2 id="neuroevolution-of-augmenting-topologies">NeuroEvolution of Augmenting Topologies&lt;/h2>
&lt;p>NeuroEvolution of Augmenting Topologies (NEAT) is an evolutionary algorithm for generating intelligent behaviour in one layer neural networks. NEAT works by evaluating neural networks against a fitness function (performance metric) and breeding those networks that perform best. NEAT does not require the backpropogation algorithm or labelled examples to learn, therefore allowing it to be used in much broader settings than deep learning methods. Invented in 2002 by Stanley and Miikkulainen&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> the state of the art adaptations to NEAT have applications in game design&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>, reinforcement learning and control problems&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>. This article will explore experiments regarding bipedal walking and the NEAT algorithm including its Novelty Search adaptation.&lt;/p>
&lt;p>&lt;a href="#learning-to-walk">Skip to the experiments&lt;/a>&lt;/p>
&lt;h2 id="the-algorithm">The Algorithm&lt;/h2>
&lt;p>As NEAT is an evolutionary algorithm which obeys the high level process for optimisation shown in the diagram below.
&lt;figure id="figure-high-level-evolutionary-process-source-baeldung">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="High Level Evolutionary Process source Baeldung" srcset="
/media/NEUROEV/EV_PROC_hu57e3a4069b2866dc5384458af090d488_545545_e1efdd193c549551707d89c88d660253.webp 400w,
/media/NEUROEV/EV_PROC_hu57e3a4069b2866dc5384458af090d488_545545_9ead92e2e885d740a375a6b16e0b2fc7.webp 760w,
/media/NEUROEV/EV_PROC_hu57e3a4069b2866dc5384458af090d488_545545_1200x1200_fit_q75_h2_lanczos.webp 1200w"
src="https://mamoc.netlify.app/media/NEUROEV/EV_PROC_hu57e3a4069b2866dc5384458af090d488_545545_e1efdd193c549551707d89c88d660253.webp"
width="760"
height="343"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
High Level Evolutionary Process source Baeldung
&lt;/figcaption>&lt;/figure>
A more detailed description of each step in the evolution process:&lt;/p>
&lt;ol>
&lt;li>Initialisation: The first step is to generate a pool of candidate solutions. This can be done by randomly generating neural networks or by loading a set of pre-existing networks.&lt;/li>
&lt;li>Selection: Next, each candidate solution is assessed on the problem. The best performing solutions are then selected to be parents for the next generation.&lt;/li>
&lt;li>Crossover: Once the parents have been selected, they are combined to create new solutions. This is done by randomly selecting genes from each parent and combining them to create a new offspring.&lt;/li>
&lt;li>Mutation: After crossover, the offspring are randomly mutated. This is done by making small changes to the genes of the offspring. Mutation helps to introduce new genetic diversity into the population, which can help to improve the performance of the algorithm.&lt;/li>
&lt;li>Solution: The NEAT process continues to generate new solutions and assess them on the problem until a solution of sufficient quality is found. Once a satisfactory solution is found, the evolutionary process is stopped and the solution is returned&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>.
&lt;figure id="figure-population-diagram-source-paul-pauls">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Population diagram source Paul Pauls" srcset="
/media/NEUROEV/POP_SPECIES_hu780caca97ebf17e93702b8d4a01b4578_695923_a67cf1fb4110c25002a1a904b0b20d50.webp 400w,
/media/NEUROEV/POP_SPECIES_hu780caca97ebf17e93702b8d4a01b4578_695923_346da7c11e54e5dc294f138f73d81acc.webp 760w,
/media/NEUROEV/POP_SPECIES_hu780caca97ebf17e93702b8d4a01b4578_695923_1200x1200_fit_q75_h2_lanczos.webp 1200w"
src="https://mamoc.netlify.app/media/NEUROEV/POP_SPECIES_hu780caca97ebf17e93702b8d4a01b4578_695923_a67cf1fb4110c25002a1a904b0b20d50.webp"
width="760"
height="341"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Population diagram source Paul Pauls
&lt;/figcaption>&lt;/figure>
&lt;/li>
&lt;/ol>
&lt;h3 id="genetic-encoding">Genetic Encoding&lt;/h3>
&lt;p>As stated as a key problem in Stanley&amp;rsquo;s paper, a major challenge of NEAT is the genetic representation of the neural networks. It is important to ensure that dissimilar network toplogies can crossover in a meaningful way; new networks are given time to innovate (stopping them being removed from the population prematurely) and finally, how to incorporate Occam&amp;rsquo;s Razor into the problem without a contrived fitness function. These problems are solved in NEAT with the following solutions:&lt;/p>
&lt;ul>
&lt;li>Utilising historical markings in the genetic encoding to identify topologies with the same origin,&lt;/li>
&lt;li>Separate newly innovated groups into new species,&lt;/li>
&lt;li>Instantiate the network with a minimal structure and only grow when necessary.&lt;/li>
&lt;/ul>
&lt;p>As such, the genetic encoding(genome) for an individual in the NEAT algorithm is as follows:&lt;/p>
&lt;ol>
&lt;li>List of node genes defining its activation function and whether it is an input,output or hidden node,&lt;/li>
&lt;li>List of connection genes where each connection gene specifies the in-node, the out-node, the weight of the connection, an enabled bit and an innovation number (used for crossover).
&lt;figure id="figure-example-genomephenotype-encoding-source-k-stanley">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Example Genome/Phenotype encoding source K Stanley" srcset="
/media/NEUROEV/ex_genome_hu6084c2f9fdaf781eff80542965790dbc_209793_111e9c00feb9f0474850b32572e30477.webp 400w,
/media/NEUROEV/ex_genome_hu6084c2f9fdaf781eff80542965790dbc_209793_63a41a61731d5d69927ce091f852ee75.webp 760w,
/media/NEUROEV/ex_genome_hu6084c2f9fdaf781eff80542965790dbc_209793_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/NEUROEV/ex_genome_hu6084c2f9fdaf781eff80542965790dbc_209793_111e9c00feb9f0474850b32572e30477.webp"
width="760"
height="498"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Example Genome/Phenotype encoding source K Stanley
&lt;/figcaption>&lt;/figure>
&lt;/li>
&lt;/ol>
&lt;h3 id="mutation">Mutation&lt;/h3>
&lt;p>Mutation in NEAT is conducted with the following rules (for each rule it is also possible for nothing to happen to a connection, weight or node):&lt;/p>
&lt;ul>
&lt;li>Connection weights are perturbed by a random amount,&lt;/li>
&lt;li>A connection is added between two previously unconnected nodes,&lt;/li>
&lt;li>A node is added where a connection was. The old connection is disabled and two new connections are added.&lt;/li>
&lt;/ul>
&lt;h3 id="crossover">Crossover&lt;/h3>
&lt;p>Crossover in NEAT is performed with historical markings, this means whenever a new gene appears (node/connection) a global &amp;lsquo;innovation&amp;rsquo; number is incremented and assigned to that gene. This means that genes with matching innovation numbers can be lined up. Genes that do not match are inherited from the more fit parent or, if they are equally fit, chosen randomly. This method is an incredibly low complexity way to align structure in the parents that is suitable for crossover. An example of crossover is provided below:
&lt;figure id="figure-matching-up-genomes-for-different-network-topologies-using-innovation-numbers-source-k-stanley">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Matching up genomes for different network topologies using innovation numbers source K Stanley" srcset="
/media/NEUROEV/ex_crossover_hu269931ccd4dbcaa7fbfb9ca00ada3704_249956_a67244a1d9c753596adc70169fb8ddd9.webp 400w,
/media/NEUROEV/ex_crossover_hu269931ccd4dbcaa7fbfb9ca00ada3704_249956_3311114b4f908de24001e910f9e89a57.webp 760w,
/media/NEUROEV/ex_crossover_hu269931ccd4dbcaa7fbfb9ca00ada3704_249956_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/NEUROEV/ex_crossover_hu269931ccd4dbcaa7fbfb9ca00ada3704_249956_a67244a1d9c753596adc70169fb8ddd9.webp"
width="628"
height="760"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Matching up genomes for different network topologies using innovation numbers source K Stanley
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h3 id="protecting-innovation">Protecting Innovation&lt;/h3>
&lt;p>Finally, in order to combat the aforementioned problem of the innovations being wiped out before they have time to be optimised we utilise speciation. A distance measure must therefore be created in order to allocated species to individuals in the population. The distance measure ($\delta$) is then defined as a linear combination of excess (E, copies of the same gene) and disjoint(D, genes with not match in the corresponding parent) genes plus the average weight difference of matching genes ($\bar{W}$).&lt;/p>
&lt;p>$$
\delta = \frac{c_1 E}{N} + \frac{c_2 D}{N} + c_3\bar{W}
$$&lt;/p>
&lt;p>Where $c_i$ is the importance of each factor and N is the number of genes. A compatibility threshold $\delta_t$ is then defined and each individual is placed into the first species for which its distance is less than the threshold to avoid overlap.
Finally, in order to reduce one species dominating the population, NEAT utilises explicit fitness sharing mitigate this. Explicit fitness sharing works by taking every individuals fitness and dividing it by the number of individuals in the species, so we are saying a large species has a better chance to do better and this should be averaged out. Species then grow or shrink depending on their performance relative to the population average.
$$
N\\_{j}^{'} = \frac{\sum\\_{i=1}^{N_j}f\\_{ij}}{f}
$$
Where:&lt;/p>
&lt;ul>
&lt;li>$N_{j}$ and $N^{&amp;rsquo;}_{j}$ are the old updated species size for species $j$,&lt;/li>
&lt;li>$f_{ij}$ is the species size divided individual fitness of individual $i$,&lt;/li>
&lt;li>$f$ is the mean adjusted fitness of the whole population.&lt;/li>
&lt;/ul>
&lt;h2 id="strengths-and-weaknesses">Strengths and Weaknesses&lt;/h2>
&lt;p>NEAT based methods are particularly suited to problems where:&lt;/p>
&lt;ul>
&lt;li>The problem is dynamic and therefore the neural network needs to be able to fundamentally change throughout its lifetime,&lt;/li>
&lt;li>There is little or no knowledge of the optimal network structure for the problem,&lt;/li>
&lt;li>The gradient of the objective function is degenerate or there are multiple objectives to be simultaneously optimised.&lt;/li>
&lt;/ul>
&lt;p>However NEAT performs poorly however in situations where:&lt;/p>
&lt;ul>
&lt;li>The problem is too expensive to evaluate. NEAT requires evaluating each candidate solution on the problem. If the problem is too expensive to evaluate, then NEAT may not be practical,&lt;/li>
&lt;li>The problem is inexpressible through a one layer neural network, however the adapation to NEAT, HYPER-NEAT&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup> addresses this issue,&lt;/li>
&lt;li>The default encoding scheme in NEAT is not suitable for the network topology that is desired to be developed,&lt;/li>
&lt;li>Limited computational resources because Neuroevolution is incredibly computationally expensive, requiring significant processing power and time, especially for complex tasks.&lt;/li>
&lt;/ul>
&lt;p>In particular this article will discuss the fitness and novelty based approaches to NEAT and their accompanying performances on the OPENAI gym bipedal walker environment&lt;sup id="fnref:6">&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref">6&lt;/a>&lt;/sup>.&lt;/p>
&lt;h2 id="learning-to-walk">Learning to Walk&lt;/h2>
&lt;p>The first experiment I attempted was the OPENAI Gym Bipedal Walker environment, the aim was to utilise the NEAT algorithm and learn walking behaviour within a 24 hour training window or by completing the environment (whichever came first). The walker environment is deemed &amp;lsquo;completed&amp;rsquo; if the model can complete 300 units of distance in 1600 time steps and the reward function gives an agent a reward for every unit of distance it travels and penalises an agent for the following:&lt;/p>
&lt;ul>
&lt;li>The use of any motor joints incurs a small energy cost,&lt;/li>
&lt;li>The walker falling over incurs a -100 score penalty.&lt;/li>
&lt;/ul>
&lt;p>Implicitly, this means the environment is telling us a &amp;lsquo;good&amp;rsquo; model is one that travels lots of distance, with minimal energy expenditure and does not fall over. This is a good starting heuristic for &amp;lsquo;walking&amp;rsquo; behaviour, but as will be discussed later, embedding more knowledge into the fitness function improves performance. This leads to my earliest implementation of the function that evaluates a genomes performance as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">eval_genome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">genome&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">config&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># setup the model and the environment&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">make&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;BipedalWalker-v3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">hardcore&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">net&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">neat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">FeedForwardNetwork&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">genome&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitnesses&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># run the model in the environment &amp;#39;runs_per_net&amp;#39; times&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">runs&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">runs_per_net&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">reset&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Run the simulation until environment termination.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">action&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">net&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">activate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">observation&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">reward&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">terminated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">truncated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">step&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">reward&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">terminated&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">truncated&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitnesses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitness&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># The genome&amp;#39;s fitness is its mean performance across all runs.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">statistics&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitnesses&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>However, after running NEAT in this setup for various hyperparameter settings and a total of 40+ hours of training time, the model would get consistently stuck in a lunging behaviour that gained 6-8 units of distance and then stood still, as demonstrated in the clip below. This behaviour meant that because the walker did not fall over &lt;strong>another, more promising, model that travels less than 106 units but falls over is considered worse (once the -100 penalty for falling over is applied) despite have more promising emergent walking behaviour&lt;/strong>. This problem meant the fitness function is not incentivising walking behaviour well and had to be adapted to avoid this local minima.
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/NEUROEV/step_and_stop.mp4" type="video/mp4">
&lt;/video>
&lt;/p>
&lt;h3 id="rolling-window-approach">Rolling Window Approach&lt;/h3>
&lt;p>In order to fix the aforementioned problem with the fitness function and a long ignored problem with slow training times I decided to implement a further constraint on the model to address both these issues. These issues were solved by what I called the &amp;lsquo;rolling window&amp;rsquo; approach. This approach gives the model a distance it must attain in a certain amount of time, penalising and ending the simulation run if the model does not attain these criterion. The process is outlined in Figure 5 below.
&lt;figure id="figure-flow-chart-of-rolling-window-approach">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Flow Chart of Rolling Window Approach" srcset="
/media/NEUROEV/dark_flow.drawio_hu6b988a1d720cd372098467a21a2b889e_41279_18b8cfab93e52a0169c12484f5194d48.webp 400w,
/media/NEUROEV/dark_flow.drawio_hu6b988a1d720cd372098467a21a2b889e_41279_8768f5e48f4dac5ff4ea191cb69dc1bd.webp 760w,
/media/NEUROEV/dark_flow.drawio_hu6b988a1d720cd372098467a21a2b889e_41279_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/NEUROEV/dark_flow.drawio_hu6b988a1d720cd372098467a21a2b889e_41279_18b8cfab93e52a0169c12484f5194d48.webp"
width="394"
height="511"
loading="lazy" data-zoomable class=" img-dark" />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Flow Chart of Rolling Window Approach
&lt;/figcaption>&lt;/figure>
The rolling window approach improves the training process in the following ways:&lt;/p>
&lt;ul>
&lt;li>The adaptation to the loss function terminates any model that doesnt make it through the next window thereby improving performance (this caused time per generation to go from 20s to 1.2s in my training).&lt;/li>
&lt;li>The adapation penalises any model that doesnt make it through a window(see &amp;lsquo;fitness = fitness - 50 + c&amp;rsquo;) thereby incentivising models to move $df$ in $dt$ time and hence alleviating the lunging problem.&lt;/li>
&lt;li>&amp;lsquo;-50&amp;rsquo; is selected as the penalty for failing the first window, this is half the penalty for falling over but still sizeable enough to incentivise the walking behaviour desired.&lt;/li>
&lt;li>Finally, in order to ensure that the penalty for failing at window $x$ is less than window $x+1$, $c\,$ is incremented after every window to make sure the penalty reduces the more windows the model makes it through. After picking 320 and 60 as good candidates for $dt$ and $df$ (when the fifth window is complete the model should have traversed 300 units) the model should complete the environment after making it through five windows.&lt;/li>
&lt;/ul>
&lt;p>The code for this approach is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">eval_genome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">genome&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">config&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># setup the model and the environment&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">make&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;BipedalWalker-v3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">hardcore&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">net&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">neat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">FeedForwardNetwork&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">genome&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitnesses&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># run the model in the environment &amp;#39;runs_per_net&amp;#39; times&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">runs&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">runs_per_net&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">reset&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Run the simulation until environment termination or window failure.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">dt&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">df&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">320&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">action&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">net&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">activate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">observation&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">reward&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">terminated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">truncated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">step&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">df&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">reward&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">reward&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">terminated&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">truncated&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># if the model does not make it through a window&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dt&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">319&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">df&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">59&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n">fitness&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">50&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># if the model makes it through a window&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">dt&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">319&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">df&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">59&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitnesses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitness&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># The genome&amp;#39;s fitness is its mean performance across all runs.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">statistics&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitnesses&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="results">Results&lt;/h3>
&lt;p>After running the NEAT algorithm with the rolling window setup, convergent walking or &amp;lsquo;shuffling&amp;rsquo; behaviour was achieved after 35000 generations and stagnated until 40000 generations. The training process and final model are displayed in the videos below. Although the model is now moving, albeit inefficiently, the greedy nature of the fitness function means the environment &lt;strong>was not completed&lt;/strong> (the maximum distance attained was 154 units of distance). In order to motivate NEAT to generate more complex walking behaviour, that will not necessarily show dividends straight away, a new fitness function should be considered.
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/NEUROEV/fit_Walker.mp4" type="video/mp4">
&lt;/video>
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/NEUROEV/fit-128-1600.mp4" type="video/mp4">
&lt;/video>
&lt;/p>
&lt;h2 id="novelty-search">Novelty Search&lt;/h2>
&lt;p>After listening to the TWiML podcast &lt;sup id="fnref:7">&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref">7&lt;/a>&lt;/sup> with Kenneth Stanley (the co-author of the original NEAT paper) I discovered novelty search, a supposedly better parameterisation of the fitness function to generate complex behaviour. This new search algorithm is the basis of the second walker experiment. The idea of Novelty Search is to define a fit model as a &amp;rsquo;novel&amp;rsquo; model, this means rewarding solutions that are sparse in the environment space. For example, in a maze (the scenario the paper&lt;sup id="fnref:8">&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref">8&lt;/a>&lt;/sup> uses) a &amp;rsquo;novel&amp;rsquo; solution is one which reaches a new area of the maze rather than minimising something like the distance to the end (which dramatically increases the chance of getting stuck in a local minima).
&lt;figure id="figure-example-of-novelty-search-outperforming-euclidean-distance-for-maze-solving-source-lehman-and-stanley">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Example of Novelty Search outperforming euclidean distance for maze solving source: Lehman and Stanley" srcset="
/media/NEUROEV/deception_hu118c79a3b89c72d235fc59339dc20510_118277_144ed92a773a8a2895eccd730635b234.webp 400w,
/media/NEUROEV/deception_hu118c79a3b89c72d235fc59339dc20510_118277_eb3c291655afb31f7af1135f0aedaa37.webp 760w,
/media/NEUROEV/deception_hu118c79a3b89c72d235fc59339dc20510_118277_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/NEUROEV/deception_hu118c79a3b89c72d235fc59339dc20510_118277_144ed92a773a8a2895eccd730635b234.webp"
width="668"
height="475"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Example of Novelty Search outperforming euclidean distance for maze solving source: Lehman and Stanley
&lt;/figcaption>&lt;/figure>
In the example above from Lehman and Stanley&amp;rsquo;s paper on Novelty Search&lt;sup id="fnref1:8">&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref">8&lt;/a>&lt;/sup> we see that novelty search dramatically outperforms a fitness based approach to maze solving. This setup is applied to the walker environment to see the performance of novelty search. The setup for the algorithm is as follows:&lt;/p>
&lt;ul>
&lt;li>Novelty is defined as reaching a novel forward distance in the environment i.e the model has walked forwards to a spot no model has reached before,&lt;/li>
&lt;li>A model is not deemed novel if it is within 1 unit of distance to the nearest three models,&lt;/li>
&lt;li>An archive of previous solutions is initially filled with 200 models and then added to whenever a new model is deemed novel,&lt;/li>
&lt;li>The environment is deemed completed if a model completes the 300 units in 1600 time steps.&lt;/li>
&lt;/ul>
&lt;p>Therefore, the code for implementing the novelty search is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">eval_genome&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">genome&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">config&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">archive&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">make&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;BipedalWalker-v3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">hardcore&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">net&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">neat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">FeedForwardNetwork&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">genome&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitnesses&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">runs&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">runs_per_net&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">novelty&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">archive&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">copy&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">reset&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dt&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">400&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># act on environment&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">action&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">net&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">activate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">observation&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">reward&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">terminated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">truncated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">step&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">df&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">reward&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">reward&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># early termination heuristic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dt&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">399&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">df&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">terminated&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">terminated&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">truncated&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">archive&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">archive&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitness&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">novel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># determine the models novelty&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">novelty&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">abs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitness&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">novelty&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">novelty&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sort&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">novel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">statistics&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">novelty&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># if the model is novel add it to the archive&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">novel&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">fitness&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">archive&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitness&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># if the environment is completed end simultation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">fitness&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="mi">300&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">novel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># heuristic to state a non-novel positive model is better&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># than one that falls over&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">fitness&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">99&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitness&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fitnesses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitness&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># The genome&amp;#39;s fitness is its mean performance across all runs.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">statistics&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fitnesses&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="results-1">Results&lt;/h3>
&lt;p>After running NEAT on the new Novelty based fitness function a solution is found to the problem after just 4284 generations (shown in the video below). Although the computational cost is slightly higher than the previous rolling windows approach ($\approx 5s\,$ vs $\approx 1s\,$), Novelty search took almost 10 times less generations to converge to a solution that travels double the distance of the rolling window approach. &lt;strong>Thefore, Novelty Search dramatically outperformed the basic NEAT implementation&lt;/strong> Below, it can be seen that the final model&amp;rsquo;s behaviour is considerably more complex, utilising more motor joints meaning the algorithm has incentivised learning more complex but meaningful behaviour as desired. Furthermore, in comparison to the distance based approach previously we can see a much more diverse set of optimal architectures being explored by the algorithm.
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/NEUROEV/Walker.mp4" type="video/mp4">
&lt;/video>
&lt;video controls >
&lt;source src="https://mamoc.netlify.app/media/NEUROEV/nov-236-1600.mp4" type="video/mp4">
&lt;/video>
&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>In conclusion I have learnd a lot about NeuroEvolution in the process of writing this article. In particular, I am glad that my motivation to learn about gradient free learning has led to me discovering these algorithms and I am pleased at the results I have gained. I have learnt about the basic NEAT algorithm and how it can be used in reinforcement learning situations alongside its Novelty Search adaptation. If i was to continue learning more in the area I would look into HYPERNEAT and the more recent developments in the NeuroEvolution space.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Kenneth O. Stanley and Risto Miikkulainen ,
&lt;a href="https://nn.cs.utexas.edu/downloads/papers/stanley.cec02.pdf" target="_blank" rel="noopener">Efficient Evolution of Neural Network Topologies&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://nn.cs.utexas.edu/downloads/papers/stanley.ieeetec05.pdf" target="_blank" rel="noopener">Real-Time Neuroevolution in the NERO Video Game&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>Kenneth O. Stanley and Risto Miikkulainen ,&lt;a href="https://nn.cs.utexas.edu/downloads/papers/stanley.gecco02_1.pdf" target="_blank" rel="noopener">Efficient Reinforcement Learning through Evolving Neural Network Topologies&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>Paul Pauls, &lt;a href="https://towardsdatascience.com/a-primer-on-the-fundamental-concepts-of-neuroevolution-9068f532f7f7" target="_blank" rel="noopener">A Primer on the Fundamental Concepts of Neuroevolution&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>Kenneth O. Stanley, David D&amp;rsquo;ambrosio, Jason Gauci &lt;a href="https://axon.cs.byu.edu/~dan/778/papers/NeuroEvolution/stanley3**.pdf" target="_blank" rel="noopener">A Hypercube-Based Indirect Encoding for Evolving Large-Scale
Neural Networks&lt;/a>&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6">
&lt;p>OPEN AI gym &lt;a href="https://gymnasium.farama.org/environments/box2d/" target="_blank" rel="noopener">Bipedal Walker Environment&lt;/a>&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7">
&lt;p>NeuroEvolution: Evolving Novel Neural Netowrk Architectures with Kenneth Stanley - &lt;a href="https://open.spotify.com/episode/2QZa0NdO2RUKaBffwNISdG?si=TFC3HJlaQV6PiA05fs2ppA" target="_blank" rel="noopener">TWiML Talk 94&lt;/a>&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:8">
&lt;p>Joel Lehman and Kenneth O. Stanley, &lt;a href="https://www.cs.swarthmore.edu/~meeden/DevelopmentalRobotics/lehman_ecj11.pdf" target="_blank" rel="noopener">Abandoning Objectives: Evolution through the
Search for Novelty Alone&lt;/a>&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:8" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>PINNs, Not the Spiky Kind.</title><link>https://mamoc.netlify.app/post/pinns_article/</link><pubDate>Fri, 28 Jul 2023 00:00:00 +0000</pubDate><guid>https://mamoc.netlify.app/post/pinns_article/</guid><description>&lt;h2 id="physics-informed-neural-networks-pinns">Physics Informed Neural Networks (PINNs)&lt;/h2>
&lt;p>Physics-informed neural networks (PINNs) are a deep learning framework for solving partial differential equations (PDEs). PINNs work by adding a specified PDE to the loss function of a neural network and utilising automatic differentiation to train the network. This allows PINNs to solve PDEs that are difficult or impossible to solve with traditional numerical methods, such as finite element methods (FEM). Invented in 2017 by M. Raisse et al. in two papers &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> for the forward/inverse problems, which are defined as follows:&lt;/p>
&lt;ul>
&lt;li>Foward Problem: The underlying equation and the boundary conditions are known, and the goal is to find the solution to the equation.&lt;/li>
&lt;li>Inverse Problem: The underlying equation and the boundary conditions are known, but the values of some of the parameters in the equation are unknown. The goal is to find the values of these parameters and the solution, given experimental data.&lt;/li>
&lt;/ul>
&lt;p>In both forward and inverse problems, PINNs are trained on a dataset of known solutions to the underlying equation. This dataset can be generated using numerical methods, such as the finite element method, finite difference method or from physical experiments. PINNs are particularly well-suited for solving problems where:&lt;/p>
&lt;ul>
&lt;li>The local consistency of FEM is broken like in integro-differential equations,&lt;/li>
&lt;li>High-dimensional input domain settings, in which classical techniques (such as FEM) are prohibitively expensive&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>,&lt;/li>
&lt;li>Parameter inference is occuring simultaneously (inverse problems).&lt;/li>
&lt;/ul>
&lt;p>Furthermore, they can be trained to generate solutions for multiple physical parameter values, by adding them as outputs from the model, e.g., multiple kinematic viscosity values in the Navier-Stokes equations. The PINN can also be probed for any continuous point in time/space unlike FEMs. In these restricted use cases, PINNs achieve performance on par or better than FEMs&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>. &lt;strong>However&lt;/strong> PINNs are currently perform poorly at solving stiff equations, where stiffness is the difficulty to solve numerically, as &amp;lsquo;Neural Networks are biased in their frequency domain to drop off lower frequency terms which is what causes the issues with stiffness&amp;rsquo;&lt;sup id="fnref1:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> and in the vast majority of cases PDEs do not satisfy the conditions for which FEMs are considerably faster or more accurate. This means that in industry the most popular use case of PINNs is an out the box model that has been trained with standard boundary conditions, which allows the customer to get useful solutions out.&lt;/p>
&lt;p>This article explores PINNs by generating a solution for the Sprott attractor (forward problem) and the solution to an inverse problem involving the Navier-Stokes equations. These problems are chosen to benefit a PINN as the Sprott attractor exhbits sensitivity to initial conditions which make it hard for the FEM to match. Furthermore inverse problems, as mentioned before, are faster for PINNs as they can learn the parameters while training unlike FEMs.&lt;/p>
&lt;h2 id="deep-neural-networks">Deep Neural Networks&lt;/h2>
&lt;p>In PINNS, the neural network (NN) is approximating the solution to the PDE. The PDE is defined on the domain $\Omega \subset \mathbb{R}^d$ with a solution $u(\mathbf{x})$ approximated by the neural network $f$ parameterised by $\lambda$. The input vector is $\mathbf{x}=(x_{1},&amp;hellip;,x_{d})^T$.
$$
f \left( \mathbf{x};\frac{\partial u}{\partial x\\_{1}},...,\frac{\partial u}{\partial x\\_{d}};\frac{\partial^2 u}{\partial x\\_1^2},...,\frac{\partial^2 u}{\partial x\\_1\partial x\\_d},...;\lambda \right).
$$&lt;/p>
&lt;p>Alongside suitable boundary conditions $\mathcal{B}(u,\mathbf{x})=0 \text{ on } \delta \Omega$. Although many NN architectures exist, the multi-layer perceptron is suitable for solving most PDEs and as such for the remainder of the article the architecture will be assumed to be a fully connected feed-forward network.&lt;/p>
&lt;h2 id="adapted-loss-function">Adapted Loss Function&lt;/h2>
&lt;p>It was stated in the first paragraph that PINNs operate by $\textcolor{red}{\text{incorporating the PDE into the loss function}}$. So, in the usual manner, the model, loss and training dataset are denoted as $f$, $V$ and $\mathcal{T}$ respectively. Then $\mathbf{\theta}$ is defined as the weights and biases in the NN and $\mathcal{T}=\mathcal{T}_f \cup \mathcal{T}_b$ are two training sets where $\mathcal{T}_f \subset \Omega$ are the points in the domain and $\mathcal{T}_b \subset \Omega$ are the problem boundary conditions. Then the loss function can be setup as
$$
\ell(\mathcal{T},f(x;\mathbf{\theta}),R) = \omega\\_f \textcolor{red}{R(f(\mathbf{x};\mathbf{\theta}),\mathcal{T}\\_f)} + \omega\\_b V(f(\mathbf{x};\mathbf{\theta}),\mathcal{T}\\_b)
$$
where, for PINNs,
$$
\textcolor{red}{R(f(\mathbf{x};\mathbf{\theta}),\mathcal{T}\\_f)}=\frac{1}{|\mathcal{T}\\_f|} \underset{x \in \mathcal{T}_f}{\sum} \left\lVert f(\mathbf{x};\frac{\partial u}{\partial x\\_{1}},...,\frac{\partial u}{\partial x\\_{d}};\frac{\partial^2 u}{\partial x\\_1^2},...,\frac{\partial^2 u}{\partial x\\_1\partial x\\_d},...;\lambda) \right\rVert\\_2^2
$$
$$
V(f(\mathbf{x};\mathbf{\theta}),\mathcal{T}\\_b)=\frac{1}{|\mathcal{T}\\_b|} \underset{x\\_i \in \mathcal{T}_b}{\sum} \lVert f(x\\_i;\mathbf{\theta}) - \mathcal{B}(x\\_i) \rVert\\_2^2
$$
There are two terms for the loss, induced on the boundary conditions and on the domain, that are simultaneously backpropogated to train the model. Observed or experimental data can be added into the boundary condition loss set as $(x_i,\mathcal{B}(x_i))$ pairs. Building upon the classic misty hill analogy for gradient descent&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>, the $\textcolor{red}{R(f(\mathbf{x};\mathbf{\theta}),\mathcal{T}_f)}$ (the loss induced by the governing PDE) provides a set of steps down the hill that act as a good heuristic to improve the chances of getting to the bottom (global minima and solution to the PDE). For example if the aim is to approximate a solution to the diffusion equation $\frac{\partial^2 f}{\partial x^2}= \frac{1}{\kappa} \frac{\partial f}{\partial t}$ then the corresponding PDE loss would be $\underset{f}{\text{argmin}}\,\left( \frac{\partial^2 f}{\partial x^2}- \frac{1}{\kappa} \frac{\partial f}{\partial t}\right)$ as the second expression is the minimisation task that corresponds to the NN obeying the PDE.
&lt;figure id="figure-gradient-descent-as-steps-on-a-misty-hill">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Gradient descent as steps on a misty hill" srcset="
/media/PINNS/grad_desc_hu53f230db81fceccf5a4ec70f80aca5d0_10973762_334ed7e0bd8e731d62068ae7a002a94a.webp 400w,
/media/PINNS/grad_desc_hu53f230db81fceccf5a4ec70f80aca5d0_10973762_85bf2b5125d3c683dc56a37ecebd79f3.webp 760w,
/media/PINNS/grad_desc_hu53f230db81fceccf5a4ec70f80aca5d0_10973762_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/PINNS/grad_desc_hu53f230db81fceccf5a4ec70f80aca5d0_10973762_334ed7e0bd8e731d62068ae7a002a94a.webp"
width="760"
height="380"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Gradient descent as steps on a misty hill
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h2 id="automatic-differentiation">Automatic Differentiation&lt;/h2>
&lt;p>During the backpropogation process the traditional aim is to minimise the derivatives $\frac{\partial \ell(f)}{\partial x_1},&amp;hellip;,\frac{\partial \ell(f) }{\partial x_d}$ ,i.e., the loss of the model with respect to the inputs. In the process of computing these derivatives we also end up computing the loss with respect to each model parameter and can then utilise a weight update algorithm such as ADAM&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup>, L-BFGS&lt;sup id="fnref:6">&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref">6&lt;/a>&lt;/sup> or the traditional $w_{t+1}=w_t + \eta \frac{\partial \ell(f)}{\partial w}$. In PINNS &lt;strong>we now have the boundary conditions of the PDE to enforce on the NN through the addition of the PDE error to the loss function&lt;/strong>. Furthermore, NNs only require one pass of the data through the approximating function however FEM require the computation of both $ \, f(x_1,&amp;hellip;x_i,&amp;hellip;,x_d)$ and $f(x_1,&amp;hellip;x_i+ \Delta x_i,&amp;hellip;,x_d) $ for all $i$. For large $\text{dim}(\mathbf{x})$, automatic differentiation is much more efficient.&lt;/p>
&lt;h2 id="the-pinn">The PINN&lt;/h2>
&lt;p>We now formalise the PINN algorithm and NN structure, copying from Lu Lu and DEEPXDE&lt;sup id="fnref:7">&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref">7&lt;/a>&lt;/sup>.&lt;/p>
&lt;ol>
&lt;li>Construct a Neural Network with accompanying parameters,&lt;/li>
&lt;li>Create the two training datasets $\mathcal{T}_f,\mathcal{T}_b$,&lt;/li>
&lt;li>Specify the loss function and weights associated to $\omega_f, \omega_b$,&lt;/li>
&lt;li>Train the Neural Network to find find the best parameters that minimise the loss function.&lt;/li>
&lt;/ol>
&lt;figure id="figure-schematic-of-a-pinn-for-solving-the-diffusion-equation-credit-to-lu-lu-et-al-and-deepxde">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Schematic of a PINN for solving the diffusion equation credit to Lu Lu et al. and DEEPXDE" srcset="
/media/PINNS/PINN_schematic_hua8a4a35974e8387c5e20c3afa3459564_64304_a043966212037368b648242d18f35916.webp 400w,
/media/PINNS/PINN_schematic_hua8a4a35974e8387c5e20c3afa3459564_64304_5fa9102219d9af36e79ce2fedc340ae3.webp 760w,
/media/PINNS/PINN_schematic_hua8a4a35974e8387c5e20c3afa3459564_64304_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://mamoc.netlify.app/media/PINNS/PINN_schematic_hua8a4a35974e8387c5e20c3afa3459564_64304_a043966212037368b648242d18f35916.webp"
width="747"
height="321"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Schematic of a PINN for solving the diffusion equation credit to Lu Lu et al. and DEEPXDE
&lt;/figcaption>&lt;/figure>
&lt;h2 id="forward-problem-sprott-attractor">Forward Problem: Sprott Attractor&lt;/h2>
&lt;p>The Sprott Attractor&lt;sup id="fnref:8">&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref">8&lt;/a>&lt;/sup> is a chaotic dynamical system proposed in a 2014 paper that exhibits sensitivity to initial conditions and exhibits complex structure. It will be treated as a forward problem and therefore that we know in advance the parameters $\alpha, \beta$ and select $t \in [0,20]$ as the domain to learn on.
$$
f(t)=\begin{cases}
\frac{dx}{dt} = y +\alpha xy + xz \\
\frac{dy}{dt} = 1 - \beta x^2 + yz \\
\frac{dz}{dt} = x - x^2 - y^2
\end{cases},\quad \alpha=2.07, \beta = 1.79
$$
&lt;/p>
&lt;h3 id="datapde-setup">Data/PDE Setup&lt;/h3>
&lt;p>The code in this section defines the geometric domain(time domain $(0,20)$), solver settings, and ODE system. The ODE system is a three-dimensional system of ordinary differential equations (ODEs) where $\texttt{x, y and z}$ are the three state variables of the system and $\texttt{o}$ is now the output of the model.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">deepxde&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">dde&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">geom&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TimeDomain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_default_float&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;float64&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">2.07&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1.79&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">ode_system&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">o&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># https://www.dynamicmath.xyz/strange-attractors/ Sprott attractor&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">o&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">o&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">z&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">o&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dx_t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dy_t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dz_t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dx_t&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">z&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dy_t&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">z&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dz_t&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">X&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="neural-network-setup">Neural Network Setup&lt;/h3>
&lt;p>The code in this section sets up the neural network for solving the ODE system. The network architecture is 4 hidden layers, each with 20 neurons. The activation function for all layers is $tanh$ (notable for PINNS due its non-zero derivatives mitigating the &amp;ldquo;dying ReLU&amp;rdquo; problem&lt;sup id="fnref:9">&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref">9&lt;/a>&lt;/sup>). The weights of the neural network are initialized using the Glorot normal initializer.&lt;/p>
&lt;p>The code also defines a function called input_transform. This function takes a time $\texttt{t}$ as input and returns a vector of sinusoidal features that will be used as input to the neural network. As we know in advance that the solution contains sinusoidal like behaviour this feature layer will boost learning performance. The code then calls the $\texttt{apply_feature_transform}$ method on the neural network.&lt;/p>
&lt;p>The code in this section is used to set up the neural network for solving the ODE system.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">x_true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y_true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">z_true&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gen_truedata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">linspace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">reshape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">observe_x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">icbc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PointSetBC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x_true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">component&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">observe_y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">icbc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PointSetBC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y_true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">component&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">observe_z&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">icbc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PointSetBC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">z_true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">component&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PDE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">geom&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">ode_system&lt;/span>&lt;span class="p">,[&lt;/span>&lt;span class="n">observe_x&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">observe_y&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">observe_z&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">anchors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">num_test&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">layer_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">activation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;tanh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">initializer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Glorot normal&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">net&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">FNN&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">layer_size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">activation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">initializer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">input_transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">6&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">axis&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">net&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">apply_feature_transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">input_transform&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="train">Train&lt;/h3>
&lt;p>The code in this section trains the neural network to solve the ODE system. The training process uses the Adam optimizer with a learning rate of 1e-3. The training is run for 200 iterations, and the loss history is monitored every iteration. The model is saved to a file after training is complete for later inference.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">net&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;adam&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lr&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1e-3&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">losshistory&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">train_state&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iterations&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">display_every&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disregard_previous_best&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">save_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./strange_attract-10t/model&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="results">Results&lt;/h3>
&lt;p>The results show that the Sprott Attractor model can be accurately solved using a neural network with only 50 pieces of data. The model converges after 80 epochs, and the final plot of the three-dimensional attractor matches the true solution with a testing error of approximately 1e-4. This is a significant result, as it shows that PINNS can be used to solve complex ODE systems with relatively little data. The attractor is a three-dimensional object that has a complex and unpredictable structure. The neural network is able to reproduce this structure accurately.
&lt;figure id="figure-training-animation-for-sprott-attractor">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Training animation for Sprott Attractor"
src="https://mamoc.netlify.app/media/PINNS/training.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Training animation for Sprott Attractor
&lt;/figcaption>&lt;/figure>
&lt;figure id="figure-solution-plot">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Solution Plot"
src="https://mamoc.netlify.app/media/PINNS/solution.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Solution Plot
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h2 id="inverse-problem-inletoutlet-cavity">Inverse Problem: Inlet/Outlet Cavity&lt;/h2>
&lt;p>For the inverse problem, we consider a cavity filled with liquid that has an inlet port in the bottom left and outlet port in the top right, we aim to model the flow of liquid in this cavity.&lt;/p>
&lt;p>The problem considered is one created by John Burkardt in &amp;lsquo;Centroidal voronoi tesselation-based reduced-order modelling of complex systems&amp;rsquo;&lt;sup id="fnref:10">&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref">10&lt;/a>&lt;/sup> as the problem considered has an accompanying dataset&lt;sup id="fnref:11">&lt;a href="#fn:11" class="footnote-ref" role="doc-noteref">11&lt;/a>&lt;/sup> of $(u,v)$ pairs for the domain that can be used for $\mathcal{T}_b$.
The spatial domain is defined as $\Omega = (0,1)\times(0,1)$, time domain $(0,T)$ and use the 2d Navier-Stokes Equations with the following boundary conditions:
$$
\begin{align}
\frac{\partial \mathbf{u}}{\partial t} - \nu \Delta \mathbf{u} + \mathbf{u} \cdot \nabla \mathbf{u} + \nabla p = 0 \quad \text{in} \quad (0,T)\times\Omega \\
\nabla \cdot \mathbf{u} = 0 \quad \text{in} \quad (0,T)\times\Omega \\
\mathbf{u}(0,x)=\mathbf{u}_0(x) \quad \text{in} \quad \Omega \\
\\
\mathbf{u}=5(1-(y-0.1)^2/0.01) \quad \text{on} \quad (0,T)\times\Gamma_i \\
(\mathbf{n}\cdot\nabla)\mathbf{u}=0 \quad \text{on} \quad (0,T)\times\Gamma_o \\
\mathbf{u}=0\quad \text{on} \quad (0,T)\times\Gamma_d
\end{align}
$$
&lt;figure id="figure-spatial-domain-omega">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Spatial Domain $\Omega$" srcset="
/media/PINNS/cavity_hubfa96c8b587b58c120351d76d00bc006_41273_91f00b1f03b11fc757b01e09bf4e7539.webp 400w,
/media/PINNS/cavity_hubfa96c8b587b58c120351d76d00bc006_41273_8d07d55422dff48f8251434b2b977c28.webp 760w,
/media/PINNS/cavity_hubfa96c8b587b58c120351d76d00bc006_41273_1200x1200_fit_q75_h2_lanczos.webp 1200w"
src="https://mamoc.netlify.app/media/PINNS/cavity_hubfa96c8b587b58c120351d76d00bc006_41273_91f00b1f03b11fc757b01e09bf4e7539.webp"
width="760"
height="494"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Spatial Domain $\Omega$
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h3 id="datapde-setup-1">Data/PDE Setup&lt;/h3>
&lt;p>The code in this section defines the Navier-Stokes equations that will be solved. The equations are defined in terms of the velocity field $(u,v)$, the pressure $p$, and the streamfunction $\psi$.We make the assumption that $u = \psi_y \text{ and } v = −\psi_ x$ for some latent function $\psi(t, x, y)$. Under this assumption, the continuity equation $u_x +v_y=0$ will be automatically satisfied.&lt;/p>
&lt;p>The free parameters $[C_1,C_2]$ to be learned, that make the problem inverse, are also defined. The code then defines the spatial and temporal domains for the problem. The spatial domain is a rectangle with dimensions $(0,0) \text{ to } (1,1)$. The temporal domain is the interval [0,6]. The geometry is a combination of the spatial and temporal domains. In the code below we manufacture adherance to the continuity equation $\nabla \cdot \mathbf{u}=0$ by using the stream function&lt;sup id="fnref:12">&lt;a href="#fn:12" class="footnote-ref" role="doc-noteref">12&lt;/a>&lt;/sup> $\psi(x,y)$ which only needs continuous first and second partial derivatives (implying the order of differentiation does not matter) and obeys $u(x,y)=\frac{\partial \psi}{\partial y}\, \text{ and }\,v(x,y)=-\frac{\partial \psi}{\partial x}$. Then, the continuity condition holds as follows:
$$
\nabla (u,v) \equiv \frac{\partial u}{\partial x} + \frac{\partial v}{\partial y} = \frac{\partial \psi}{\partial y \partial x} - \frac{\partial \psi}{\partial x \partial y} = 0.
$$
So the model has four outputs $(\psi,p,u,v)$ where $u,v$ are linked to $\psi$ with the PDE equations &lt;code> u_diff = u_real - u&lt;/code> and &lt;code>v_diff = v_real - v&lt;/code>. This allows the point set boundary condition to be applied in the next section.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">deepxde&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">dde&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">create_datasets&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">create_dataset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Parameters to be identified&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">C1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Variable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">C2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Variable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">300&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">Navier_Stokes_Equation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> System of PDEs to be minimized: incompressible Navier-Stokes equation in the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> continuum-mechanics based setup.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">psi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">u_real&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v_real&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">[:,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p_x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p_y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">u&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">psi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">psi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">u_x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">u_y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">u_t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v_x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v_y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v_t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jacobian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">du_xx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hessian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">du_yy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hessian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dv_xx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hessian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dv_yy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">grad&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hessian&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">continuity&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">u_x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">v_y&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x_momentum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">u_t&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">C1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">u_x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">v&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">u_y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">p_x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">C2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">du_xx&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">du_yy&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">y_momentum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">v_t&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">C1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">v_x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">v&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">v_y&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">p_y&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">C2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dv_xx&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">dv_yy&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">u_diff&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">u_real&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">u&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v_diff&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">v_real&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">v&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">continuity&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x_momentum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y_momentum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">u_diff&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v_diff&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">space_domain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Rectangle&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">time_domain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TimeDomain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">geomtime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GeometryXTime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">space_domain&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">time_domain&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="neural-network-setup-1">Neural Network Setup&lt;/h3>
&lt;p>The code in this section sets up the neural network for solving the Navier-Stokes equations. The neural architecture has 6 hidden layers, each with 50 neurons. As before, the activation function for all layers is $tanh$ and the weights of the neural network are initialized using the Glorot uniform initializer.&lt;/p>
&lt;p>The code also creates a dataset of training data. The dataset is created by sampling the Navier-Stokes equations on a fine grid. The dataset includes 700 domain points, 200 boundary points, 100 initial points for $\mathcal{T}_f$ and 7000 for $\mathcal{T}_b$, and 3000 test points.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="n">ob_x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_v&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data_full.csv&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">5000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ob_xyt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hstack&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">ob_x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_t&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">observe_u&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">icbc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PointSetBC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ob_xyt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">component&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">observe_v&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">icbc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PointSetBC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ob_xyt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ob_v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">component&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TimePDE&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">geomtime&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Navier_Stokes_Equation&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="n">observe_u&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">observe_v&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">num_domain&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">700&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">num_boundary&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">num_initial&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">anchors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ob_xyt&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">num_test&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">layer_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">6&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">activation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;tanh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">initializer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Glorot uniform&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">net&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">FNN&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">layer_size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">activation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">initializer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dde&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">net&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="train-1">Train&lt;/h3>
&lt;p>The code in this section trains the neural network to solve the ODE system. The training process uses the Adam optimizer with a learning rate of 1e-3. The training is run for 10000 iterations and the loss history is monitored every 10 iterations. The model is saved to a file after training is complete for later inference.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
This model required hours of time to train with a GPU accelerated tensorflow backend, so we highly recommend doing the same if you try to replicate the experiment.
&lt;/div>
&lt;/div>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;adam&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lr&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">1e-3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">external_trainable_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">C1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">C2&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">loss_history&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">train_state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">iterations&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">callbacks&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">variable&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">display_every&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disregard_previous_best&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">save_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./psi_pinn_nt/model&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="results-1">Results&lt;/h3>
&lt;p>The results show that the PINN is able to accurately predict the flow of fluid in the domain. The predicted flow matches the true flow over the domain the NN was trained on, with non-degenerative performance on out of domain data. The testing error was on the order of 1e-3 across the six objective functions, which is a good indication that the neural network is accurately predicting the flow.
&lt;figure id="figure-solution-plot">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Solution Plot"
src="https://mamoc.netlify.app/media/PINNS/ns.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Solution Plot
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>I learned a lot about PINNs in the process of writing this article, specifically, testing my patience after spending one week debugging the PINN to discover that my visualization function was malformed. I learned about the basic structure of a PINN, how they are trained, and how they can be used to solve PDEs. I also learned about the advantages of PINNs over traditional numerical methods, such as FEM. I am excited to learn more about PINNs and their potential applications in the future.&lt;/p>
&lt;p>Here are some specific things I learned:&lt;/p>
&lt;ul>
&lt;li>PINNs are a relatively new research area, but they have already shown great promise for solving PDEs.&lt;/li>
&lt;li>PINNs can be used to solve PDEs that are difficult or impossible to solve with traditional numerical methods.&lt;/li>
&lt;li>PINNs can be trained on relatively little data, how little data was required to train the Sprott attractor was very impressive.&lt;/li>
&lt;li>PINNs can often be more efficient than traditional numerical methods for high dimensional problems.&lt;/li>
&lt;/ul>
&lt;p>Thank you to Cam, Nathan, Rick and Ed for their support and putting up with my whining at spending one week debugging a single line of code and to Ben and James for their exhaustive grammar checking.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>M. Raissi, P. Perdikaris, G.E. Karniadakis,
&lt;a href="https://doi.org/10.1016/j.jcp.2018.10.045" target="_blank" rel="noopener">Physics-informed neural networks: A deep learning framework for solving forward and inverse problems involving nonlinear partial differential equations&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://arxiv.org/pdf/1907.06729.pdf" target="_blank" rel="noopener">Christian Beck et al, Overcoming the curse of dimensionality in the numerical approximation of Allen–Cahn partial differential equations via truncated full-history recursive multilevel Picard approximations&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://open.spotify.com/episode/3fYcufW1xQsNh5qkOW6EMW?si=VwtK2hPxTDaTSLP5KoSlKg&amp;amp;context=spotify%3Ashow%3A4Y9L0uykSmk7sgskLjBQ1k" target="_blank" rel="noopener">Physics-Informed Neural Networks (PINNs) - Chris
Rackauckas | Podcast #42&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>&lt;a href="https://ayush-98282.medium.com/what-can-we-learn-from-gradient-descent-8ef0827902e1" target="_blank" rel="noopener">What can we learn from gradient descent?&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>&lt;a href="https://arxiv.org/abs/1412.6980" target="_blank" rel="noopener">Kingma, D. P., &amp;amp; Ba, J. L. (2015). Adam: A method for stochastic optimization.&lt;/a>&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6">
&lt;p>&lt;a href="https://link.springer.com/article/10.1007/bf01589116" target="_blank" rel="noopener">Liu, D. C., &amp;amp; Nocedal, J. (1989). On the limited memory BFGS method for large scale optimization.&lt;/a>&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7">
&lt;p>&lt;a href="https://arxiv.org/pdf/1907.04502.pdf" target="_blank" rel="noopener">LU LU, XUHUI MENG, ZHIPING MAO, AND GEORGE EM KARNIADAKIS, DEEPXDE: A DEEP LEARNING LIBRARY FOR SOLVING DIFFERENTIAL EQUATIONS&lt;/a>&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:8">
&lt;p>&lt;a href="https://sprott.physics.wisc.edu/pubs/paper423.pdf" target="_blank" rel="noopener">J.C. Sprott, A dynamical system with a strange attractor and invariant tori&lt;/a>&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:9">
&lt;p>&lt;a href="https://towardsdatascience.com/the-dying-relu-problem-clearly-explained-42d0c54e0d24" target="_blank" rel="noopener">Kenneth Leung,The Dying ReLU Problem, Clearly Explained&lt;/a>&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:10">
&lt;p>&lt;a href="https://people.sc.fsu.edu/~jburkardt/publications/MR2231716.pdf" target="_blank" rel="noopener">JOHN BURKARDT, MAX GUNZBURGER, AND HYUNG-CHUN LEE, CENTROIDAL VORONOI TESSELLATION-BASEDREDUCED-ORDER MODELING OF COMPLEX SYSTEMS&lt;/a>&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:11">
&lt;p>&lt;a href="https://people.sc.fsu.edu/~jburkardt/datasets/inout_flow2/inout_flow2.html" target="_blank" rel="noopener">INOUT_FLOW2 INOUT Flow Problem Solution Datasets&lt;/a>&amp;#160;&lt;a href="#fnref:11" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:12">
&lt;p>&lt;a href="https://people.sc.fsu.edu/~jburkardt/classes/math1091_2020/stream/stream.pdf" target="_blank" rel="noopener">JOHN BURKARDT, The Stream Function, MATH1091: ODE methods for a reaction diffusion equation&lt;/a>&amp;#160;&lt;a href="#fnref:12" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item></channel></rss>