Last active
October 29, 2016 08:17
-
-
Save benjamin-rood/0e027147158731921e6bce8e5b4a33e6 to your computer and use it in GitHub Desktop.
Towards a Massive Agent-Based Model in Go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <br><br><br> | |
| [TOC] | |
| #Preface & Acknowledgements | |
| ###github.com/benjamin-rood/abm-cp | |
|  | |
| ###Context | |
| Software for computing a Predator-Prey Agent Based Model of prey colour polymorphism (CP) in Go. | |
| Written specifically to assist the research by Dr. James Dale in evaluating hypotheses for the evolutionary maintenance of extreme colour polymorphism. | |
| ###Goal | |
| Server-side computation, client-side GUI controls and visualisation of the running ABM in a web browser. | |
| Generalise system so any `abm` package with local model context, agent design, etc can be hot-swapped in (linked via a command line flag) and run. | |
| ###How to use: | |
| Install Go from [here](https://golang.org/dl/). | |
| Download my repo: `go get -u github.com/benjamin-rood/abm-cp` | |
| Download external dependencies: | |
| `go get -u golang.org/x/net/websocket` | |
| `go get -u github.com/benjamin-rood/gobr` | |
| `go get -u github.com/pquerna/ffjson` | |
| `go get -u github.com/davecgh/go-spew/spew` | |
| `go get -u github.com/spf13/cobra` | |
| *(All dependecies will be vendored into the `abm-cp` package from v1.0.0 onwards)* | |
| Change current directory to `$GOPATH/src/github.com/benjamin-rood/abm-cp` and run `go install`. | |
| From there, calling `abm-cp run` from the shell prompt will start the websocket server. | |
| Point web browser at `localhost:8000` | |
| Current version only tested on Safari/Chrome on OS X and Chrome on Windows 7/8.1. | |
| ###Acknowledgements | |
| *I would like to acknowledge the generous technical assistance that members of the* **gophers** *Slack community provided to my many Go language questions. Being able to publicly iterate custom interface and concurrent structure implementations accelerated my adeptness significantly. I mention the following individuals in particular for assisting with significant stumbling blocks in the development process of this project:* | |
| Christine Dodrill *helped enormously by taking the code sketch of what I was wanting to do and really filled the gap in my understanding by showing how to defining a custom* `x/net/websocket` *interface implementation around my other architectural requirements.* | |
| **Christine Dodril** | |
| https://christine.website | |
| [email protected] | |
| https://github.com/Xe | |
| Luna Duclos *for too many things to list, including the possibilities of the* `gopher-js` *library + educating me on generic JSON encoding/decoding in Go using the* `json.RawMessage` *type.* | |
| **Luna Duclos** | |
| Palm Stone Games | |
| [email protected] | |
| https://github.com/PSG-Luna | |
| *Finally,* William Kennedy *for his excellent book Go in Action, as well as his patience and personal encouragement.* | |
| **William Kennedy** | |
| Arden Labs | |
| https://www.ardanlabs.com | |
| [email protected] | |
| <br> | |
| > This report was written entirely with [StackEdit](https://stackedit.io/). | |
| <br> | |
|  | |
| <br> | |
| ---------- | |
| # Research Context and Key Concepts # | |
| ###Agent-Based Model (ABM) | |
| >Agent-based modeling is a rule-based, computational modeling methodology that focuses on rules and interactions among the individual components or the agents of the system.... (In biological models) the goal of this modeling method is to generate (artificial) populations of the system components of interest and simulate their interactions in a virtual world. Agent-based models start with rules for behaviour and seek to reconstruct, through computational instantiation of those behavioural rules, the observed patterns of behavior[^AnMiDutta-MoscatoVodovotz2009]. | |
| The emphasis here is on the *artificiality* of the system. We are **not** simulating real-world biological environments, but rather constructing an abstracted *slice* of "the system components of interest", which serves to model a relation (or dynamic) arising from the interaction of the agents in the model – which agents are patterned on types of fauna in the natural world. | |
| See *[Agent-based model in biology](https://en.wikipedia.org/wiki/Agent-based_model_in_biology)* for more. | |
| ###Colour Polymorphism (CP)#### | |
| From *[The Evolution of Color Polymorphism: Crypticity, Searching Images, and Apostatic Selection](http://digitalcommons.unl.edu/bioscifacpub/52/)* (Bond AB. 2007): | |
| > Polymorphism... (has been) viewed from the outset as an adaptive response to the behavior of visual predators. (...) The argument has three main premises: (*a*) Visual search is subject to processing limitations, in that it is more difficult to search for two or more dissimilar, cryptic prey types simultaneously than to search for only one. (*b*) Predators consequently tend to focus on the most rewarding prey type and effectively overlook the others. (*c*) The result is frequency-dependent, apostatic selection, a higher relative mortality among more abundant prey types that serves to stabilize prey polymorphism. | |
| > (...) | |
| > Polymorphic species can be separated into two broad categories that appear to reflect distinguishable evolutionary strategies. One grouping consists of species that occur in many disparate forms, all of which bear a general resemblance to the same background. The number of morphs can range from perhaps three or four to dozens, as in grouse locusts (*Acrydium arenosum*) (Nabours et al. 1933), land snails (*Cepaea nemoralis*) (Cain & Sheppard 1954), or underwing moths (*Catocala micronympha*) (Barnes & McDunnough 1918)... (the) second group of polymorphic species are associated with heterogeneous environments that are divided into large, disparate substrate patches (Bond & Kamil 2006, Merilaita et al. 2001). In such coarse-grained habitats (Levins 1968), an individual can occupy only one substrate type at a time and, because of the disparity in patch appearance, cannot match all substrates equally well. The result is selection for maximum crypticity on only one of the available substrates... (b)ecause each morph specializes on a particular substrate type, these species may be highly selective in choosing a resting location, actively avoiding nonmatching backgrounds (Edmunds 1976, Owen 1980, Sargent 1981). | |
| ###Context: CP in *Isocladus armatus* | |
| The ABM of this project is designed as a companion for the research by Dr. James Dale : | |
| > We will use *Isocladus armatus* as a model system to evaluate... hypotheses for the evolutionary maintenance of extreme colour polymorphism. | |
| >The function of colouration in *I. armatus* is unknown[^Jansen1971]. However in other species of isopods[^Merilaita2001] [^JormalainenMerilaitaTuomi1995] [^Moreira1974] colouration is typically argued to be a defence against predation: it functions as camouflage[^StevensMerilaita2009]. Here we putatively assign three different forms[^StevensMerilaita2009] of visual camouflage adopted by *I. armatus*: background matching, disruptive coloration and masquerade... the *a priori* expectation is that stabilising selection from predation should fix colouration onto the single most effectively cryptic phenotype. The key question is therefore: **why do multiple camouflage strategies co-occur throughout the entire geographic range[^Jansen1971] [^Jansen1968] of this species?** | |
| Indeed, CP is widely distributed among cryptic prey species[^Bond2007]. In some cases, polymorphism involves cryptic prey which specialise on distinct substrate types that occur within heterogeneous environments divided into large, disparate patches[^Merilaita2001] [^FaralloForstner2012]. However, in many other cases, cryptic prey seem almost unrestricted in their morph diversity[^Bond2007] [^WhiteleyOwenSmith1997]. The evolutionary processes that maintain CP in such species is an enduring and unsolved evolutionary problem[^Bond2007]. | |
| The functional hypothesis this ABM is in support of is **Apostatic Selection**: | |
| >**Apostatic selection** views prey polymorphism as the outcome of the perceptual and cognitive characteristics of their predators. **Because it is more difficult to search simultaneously for multiple cryptic prey types, visual predators are expected to focus on the more common forms and overlook the others**[^Bond2007]. Predator specialization on common morphs results in negatively frequency dependent (apostatic) selection favouring rare morphs within a single population[^Bond2007] [^Dale2006] [^Endler1998]. Indeed, the properties of colour variability expressed by *I. armatus* are consistent with a negatively frequency dependent selective mechanism[^DaleLankReeve2001] [^Dale2006], i.e. high degrees of polymodal variation with multiple component parts that appear to co-vary independently. Although commonly invoked to explain the maintenance of CP in many species... validating the apostatic hypothesis in a natural wild system has proven difficult[^Bond2007] [^GrayMcKinnon2007] [^Dale2006]. | |
| ###Predator – Prey System | |
| This ABM should describe a dynamic between populations of two types of agent: **Visual Predators** (VP) and **Colour-Polymorphic Prey** (CP Prey). To distinguish this particular Agent-Based Model ("the model") from the concept of ABM, we shall denote it as $\text{ABM-CP}$. | |
| The principal factor will be in the *relation* between the two population sets defined by the colour variability of CP Prey and the perceptual and cognitive characteristics of VP, resulting in **apostatic selection**. | |
| Generally: | |
| >Predators and their prey evolve together. Over time, prey animals develop adaptations to help them avoid being eaten and predators develop strategies to make them more effective at catching their prey. These strategies and adaptations can take many forms including camouflage, mimicry, defensive mechanisms, agility, speed, behaviors and even tool usage that make their job easier. | |
| In nature a balance tends to exist between the predators and prey within an environment. There are a number of factors that can affect it but part of it is the birth and death rates of the predators and prey species. It is logical to expect the two populations to fluctuate in response to the density of one another. | |
| When the prey species is numerous, the number of predators will increase because there is more food to feed them and a higher population can be supported with available resources. As the number of predators begins to increase, the density of the prey population will decrease in response to increased rates of predation. That results in a decrease in the number of predators as the food resource becomes smaller which in turn decreases the rate of predation, allowing the prey species population to flourish again. | |
| See *[Predator-Prey Relationships](https://explorable.com/predator-prey-relationships)* for more. | |
| ---------- | |
| #Implementation Overview & Requirements | |
|  | |
| <br> | |
| After consideration and discussion with Assoc. Prof. Scogings, the following conclusions emerged: | |
| * The relationship between the PREDATOR and PREY agents can be modeled exclusively in two dimensions. | |
| * To increase flexibility, and allow for spatial interactions with greater nuance, agents should operate in a vector subspace over $\mathbb{R}^{2}$, rather than a fixed integer field using matrices to determine agent positioning. | |
| * Focus on concurrent ABM **modelling/computation** (rule-based agent behaviour), **visualisation**, and data **logging** for statistical analysis. | |
| * The surface area to parallelise computation of the domain is very narrow, as agent actions are largely interdependent with other neighbouring agents at each moment in time. Instead, implementation requires *concurrent software design* from the beginning. | |
| * Visualisation must be in "Live" viewport | |
| * Must be GUI application, with interactive controls to set constant value parameters which are received and then used to either update or start/restart modelling/computation. | |
| * Software must be designed for modularity: need to support extensibility of agent behaviours and parameters per J. Dale specifications post-development to ensure software correctly meets research needs. | |
| * The best endpoint for the user is a Web App: | |
| * Platform agnostic | |
| * Every possible user has a web browser on their device | |
| * GUI stable and low-overhead, programmable by CSS, HTML, JS | |
| * Allows for local and remote execution — both cases, same UI & UX | |
| Based on prior experimentation it was clear that, for the purposes of determining what needs to be rendered on screen, treating each agent purely as a *vector* from some origin and translating that from "model world" space to screen space would be the easiest way decoupling the modelling computation from its presentation. Despite prior experience with **[Processing](www.processing.org)**, and with **[Cinder](www.libcinder.org)** – *a graphics library for C++* – I knew that I needed to work in the language and frameworks best suited to delivering a concurrent *Web App* rather than worrying exclusively about the presentation layer.[^frontend-mistake] | |
| To that end, application software was developed in [**Go**](golang.org), a language designed by Rob Pike, Ken Thompson, and Robert Griesemer at Google[^golang.org - history] and released to the public in 2009. It was designed for composing concurrent software, with a concurrency model based on *Communicating Sequential Processes* (CSP) by Tony Hoare.[^golang.org - ancestors] [^Hoare] In particular, the "canonical" Go program is a web server and a significant part of the standard library is for implementing web/cloud applications which made it an ideal choice for this project. | |
| <br> | |
| ---------- | |
| <br> | |
| #Definition of the ABM Environment | |
| Consider the set of all real numbers within $[\,-1, \,+1\,]$.$\ $ Let $\,F\ $ be the cartesian product of such a set with itself, *s.t.* individual elements in $\,F\,$ are two-dimensional points, or a cartesian *pair* within a subset of $R^2$. | |
| $$\therefore \ F\ = \ \{\ (x,y) \in\, \mathbb{R}^{2} \, \lvert\ (\,-1 \leq x \leq 1 \,)\ \wedge\,(\,-1 \leq y \leq 1 \,)\ \}$$ | |
| Let $\ E\ $ be the symbolic representation of our modelling environment, such that $E$ encompasses the following: | |
| * A vector space $\ V\ $ over field $\ F$. | |
| * Variable extensions on the field $F$ which act as "environmental" describers *(to define the properties of the medium the agents exist in)* and modifiers *(to be used to model changes to the world state)*.[^env-mod] | |
| $ $ | |
| *Note: For the purposes of the initial research goals, it is assumed that all agents exist in a perfect, static environment, with no variations. Improvement or regression of agent abilities or characteristics are entirely independent of environmental factors.* | |
| The range of $F$ within $E$ will be sufficient to act as a looping "petrie dish" to observe the behaviour of the two populations of agents, with the origin $\,(0,0)\,$ as the absolute centre of the projection of the environment. | |
| Rather than agents 'bouncing' or 'avoiding' the boundaries of $E$, values $(x,y) \in E\,$ **wrap** when exceeding the limits of the surface area, *s.t.* the field behaves spherical surface. | |
| $\ \forall \ a \in [-1, +1]\,$ which defines the range of $\,F$,$\,$ we have three cases: | |
| Case $\ 1 < a \ :$ | |
| : $ a \longmapsto (a-2)$ | |
| <br> | |
| Case $\ -1 < a < 1 \ :$ | |
| : $a = a$ | |
| <br> | |
| Case $\ a < -1 :$ | |
| : $a \longmapsto (a+2)$ | |
| *For example, if an agent moves from position $\,(-0.9, 0.2)\,$ down by 0.3 it would map from $\,(-1.2, 0.2)\,$ to $\,(0.8, 0.2)$; similarly, if an agent moves from position $\,(1.0, 0.95)$ to $(1.13, 1.025)\,$ the resulting position would be $\,(-0.87,-0.975)$* | |
|  | |
| <CENTER><small>*Planar projection drawing of $E$ with various Colour Polymorphic Prey agents*</small></CENTER><br> | |
|  | |
| <CENTER><small>*Simplistic partitioning of $E$ into $S$ sectors to reduce search space*</small></CENTER><br> | |
| [^env-mod]: <small> $ $For example, shifts in the body of water/medium of $E$ like cyclic waves coming in and out according to tidal patterns, which would dynamically affect agent movement as the body of water shifted; or temperature variations in the water which could affect 'food' abundance. | |
| <br> | |
| #Model Time | |
| Inside our Agent-Based Model, our time scale can be broken up into three metrics, each one greater than the next. Practically, the `timeframe` structure holds the representation of time metrics for the unique model instance . | |
| - **Turn:** The cycle length for all agents in $E\,$ to perform a single Action. | |
| - **Phase:** Division of a Turn, between agent sets, environmental effects/factors, and updates to populations and model conditions (via | |
| external). A Phase is complete when all members of a set have | |
| performed an Action or all requirements for the model's continuation | |
| have been fulfilled. | |
| - **Action:** An individual 'step' in the model. All Actions have a cost, in both energy (measured in how *hungry* an agent is at a given moment), as well as the period (number of turns) before that specific Action can be performed again. For most actions this is zero. Some Actions could also *stop* any other behaviour by that agent for a period. | |
| Creating a discrete delineation of *Model Time* provides the ability to consider model events in their own context with enough granularity to analyse an individual agent's actions at any moment in time.[^agent-actions] | |
| ---------- | |
| <br> | |
| #Visual Representation | |
| The running $\text{ABM-CP}$ instance is presented as a dynamically sized 2D *viewport*, with the image frame representing a planar projection of the model environment $E$. $\ $While the vector space of $E$ is fixed, the projection fits to the dimensions of the browser window of the user: | |
|  | |
| Only the raw position data of the agents and the background are sent from the $\text{ABM-CP}$ instance; $\,$ the translation to pixels is handled in the browser-side code using the **P5.js** library. This way, the space between agents changes depending on the projection to the *viewport*, but their shape and the way they move is standardised for all users. | |
|  | |
| <CENTER><small>*CP Prey agents move randomly in the environment* | |
| <br> | |
| All agents are depicted using simple, primitive shapes. The requirement is to visualise the dynamic activity of agents individually and at large, while avoiding visual 'seduction' which might obscure analysis. | |
| Colour Polymorphic Prey agents are represented as small circles reflecting the individual agent colouration. | |
|  | |
| <CENTER><small>*Spawning of Colour Polymorphic Prey progeny with varying colour mutation* | |
| <br> | |
| Visual Predators are represented by an isosceles triangle – this efficiently communicates the `heading` angle of the agent, and the path it will take each turn. | |
|  | |
|  | |
| The colouration of VP agents communicates their individual **colour targeting** parameter. $\ $When VP agents successfully attack *(consume)* a CP Prey agent, the colour of the agent triangle 'blinks' black for one frame, and then the colour of the updates to reflect the updated target colour as a result of attack success. | |
|  | |
| To make the predator agents easier to read, we add a white tip to the triangle "nose". | |
| <br> | |
| ### SCREENSHOTS | |
|  | |
|  | |
|  | |
| <br> | |
| ---------- | |
| <br> | |
| #Agent Types | |
| As mentioned earlier, our agents 'live' in world which we represent as a 2D vector subspace enveloped *("looped")* at the perimeter. The substance of each agent, requires greater detail: while both agent types in our *ABM* have the same generalised process of movement, each has an accompanying *state* which defines the individual agent characteristics – how fast it will move the next *turn*, how far it can see, its colouration – and stores its internal metrics: $\ $for example, in the case of agent gravidity, we need to keep track of the remaining number of turns until parturition. [^paturition-disambiguation] | |
| Meanwhile, the *action* that an agent takes at any moment$\,$[^action-disambiguation] is determined by the correspondence of the agent's current *state* to the rule-based algorithm for the agent type. These algorithms are detailed following the CP Prey and VP agent sections respectively. | |
| <br> | |
| ##Colour-Polymorphic Prey | |
|  | |
| <CENTER><small>*Random movement of CP Prey agent, modelling acquisition of abundant food in environment.* | |
| <br> | |
| <pre class="prettyprint"><code class="golang"><small>type ColourPolymorphicPrey struct { | |
| uuid string | |
| description AgentDescription | |
| pos geometry.Vector /* position in the environment */ | |
| pos geometry.Vector /* position in the environment */ | |
| movS float64 /* speed / movement range per turn */ | |
| movA float64 /* acceleration */ | |
| tr float64 /* turn rate / range (in radians) */ | |
| dir geometry.Vector /* heading as unit vector */ | |
| ϴ float64 /* heading angle */ | |
| lifespan int | |
| hunger int /* counter for food (energy) reqt */ | |
| fertility int /* counter for interval between birth and sex */ | |
| gravid bool | |
| colouration colour.RGB | |
| }</code></pre> | |
| <br> | |
| Colour-Polymorphic Prey *(CP Prey)* agents are asexually replicating creatures, distinguished by their inherited **colouration**, small size, and random movement. In our model, we assume that food for *CP Prey* agents is omnipresent and abundant in the environment $E\,$ *(hence random movement to model generic consumption)*. The **'mutation factor'** parameter $mf$ defines the maximum possible degree of variation in agent colouration between generations, $s.t.$ the exact mutation from parent to individual *progeny* colouration is a uniform random value $x$ within $\,[-mf,\, mf]\,$ which is added to the parental agent's colouration value before being inherited by its new child. | |
| <br> | |
|  | |
| <CENTER><small>*Variable rate of colour mutation over a single generation, based on the value of $\ mf$* | |
| <br> | |
| By removing all factors other than colouration inheritance from our *CP Prey* agents, the intent is to focus on modelling genetic success through our artificial *morphs* colouration. Chance of asexual replication as the *action* by a given *CP Prey* agent in any turn is determined by sufficient interval since last attempt – *i.e. enough turns simply moving/absorbing food from environment* – and a uniform random chance within $[\,0,1)$ being greater than an external Contextual parameter. | |
| ---------- | |
| #### Rule-Based Behaviour: Colour-Polymorphic Prey | |
| #####1. Begin | |
| <pre><small>IF PREY AGEING is true: | |
| decrement lifetime | |
| IF lifetime ≤ 0: Jump to DEATH | |
| Increment fertility | |
| IF fertility = 0: | |
| gravid ⟵ false | |
| Jump to SPAWN | |
| IF fertility ≥ period: Jump to MATE SEARCH | |
| ELSE: Jump to EXPLORE</pre> | |
| #####2. Mate Search (local) | |
| <pre><small>SEARCH PREY population of MICRO SECTORS within range | |
| IF FOUND MATE: | |
| position ⟵ MATE(position) | |
| Jump to ATTEMPT REPRODUCE | |
| ELSE: Jump to EXPLORE</pre> | |
| #####3. Attempt Reproduce | |
| <pre><small>Let ω be a random real number in [0, 1) | |
| IF ω ≤ sReproduceChance: | |
| fertility ⟵ -gestation | |
| gravid ⟵ true | |
| Jump to END</pre> | |
| #####4. Explore | |
| <pre><small>MOVE to random position within radius (movS*movA) | |
| Jump to END</pre> | |
| #####5. Spawn | |
| <pre><small>Let n be a random integer in [1, b] | |
| Add n children inheriting colour to the PREY population at a random proximate position. | |
| Jump to END</pre> | |
| #####6. Death | |
| <pre><small>Remove cpp object from PREY population and MICRO SECTOR location | |
| Jump to END</pre> | |
| #####7. End | |
| <pre><small>Increment action counter for model time</small> | |
| EXIT </pre> | |
| <br> | |
| ---------- | |
| <br> | |
| ##Visual Predators | |
| <pre class="prettyprint"><code class="golang"><small>type VisualPredator struct { | |
| uuid string | |
| description AgentDescription | |
| pos geometry.Vector /* position in the environment */ | |
| movS float64 /* speed / movement range per turn */ | |
| movA float64 /* acceleration */ | |
| tr float64 /* turn rate / range (in radians) */ | |
| dir geometry.Vector /* heading as unit vector */ | |
| ϴ float64 /* heading angle */ | |
| lifespan int | |
| hunger int /* counter for food (energy) reqt */ | |
| attackSuccess bool | |
| fertility int /* counter for interval between birth and sex */ | |
| gravid bool | |
| vsr float64 /* visual search range */ | |
| γ float64 /* visual seach (colour) tolerance */ | |
| τ colour.RGB /* search target / colour specialisation */ | |
| ετ float64 /* colour specialisation strength */ | |
| }</code></pre> | |
| <br> | |
| Visual Predator agents are sexually reproducing, solo hunters whose food source is Colour-Polymorphic Prey. Defined by fast movement and hunting prey agents which match the predator's visual search *identifier*. In the case of our model, this identifier is a **target** colour which is compared to the **colouration** of any *CP Prey* agents within the visual range of the predator. Like the Colour-Polymorphic Prey agents, the behaviour of Visual Predator agents are prescribed, with some random variation at crucial points of interaction with other agents – *attacking CP Prey agents, mating with other VP agents* – to better imitate a natural population. | |
| *Illustration: VP agent searches for mate.* | |
| Successful VP agents are those whose ability to seek out prey is efficient, so that the predator has energy sufficient for sexual activity with a partner.[^vp-sexuality] $\ $The efficacy of a Visual Predator is enhanced by specialisation, which is represented by the strength of the *imprinting* of the predator's **target colour**: $\ $the stronger the **colour specialisation**, the less energy is required for visual search. This specialisation comes at the cost of 'blindness' to prey whose colouration is similar to the target value, where the strength of *colour specialisation* is inversely proportional to search tolerance in colour difference from target colour. | |
| ---------- | |
| #### Rule-Based Behaviour: Visual Predators | |
| #####1. Begin | |
| <pre><small>Increment agent hunger, fertility | |
| IF PREDATOR STARVATION is true: | |
| IF hunger causes PANIC: | |
| Increase range of γ (visual acuity) | |
| Decrease ετ (colour specialisation) | |
| IF PREDATOR AGEING is true: | |
| Decrement agent lifespan | |
| IF lifetime ≤ zero: Jump to DEATH | |
| IF PREDATOR STARVATION ⋀ hunger > limit: Jump to DEATH | |
| IF fertility is zero: Jump to SPAWN | |
| IF fertility ⋀ hunger > PREDATOR ENERGY REQUIREMENT: Jump to MATE SEARCH | |
| ELSE: Jump to PREY SEARCH</pre> | |
| #####2. Prey (Visual) Search | |
| <pre><small>SEARCH PREY population of SECTORS within vsr | |
| A PREY agent is SEEN based on the formula:</pre> | |
| <small>$$f (\chi) = ce^{-c\chi} + \ell$$ | |
| <small>*Where $\chi$ is the colour difference between PREY agent colouration and PREDATOR agent colour target $\, \tau \,$ , $\,\ c\, $ is the colour specialisation strength $\epsilon\tau \,$ , $\,$ and $\,\ \ell = \, \frac{1}{c}\, \, ∀\, \ c\, >\, 1\ $ or otherwise $\ \ell = (1-c)$* | |
| <pre><small>IF ƒ(χ) > γ for some agent in PREY population: | |
| Jump to INTERCEPT | |
| ELSE: Jump to PATROL</pre> | |
| #####3. Intercept | |
| <pre><small>Let Ψ be the difference in angle the current relative position of the target. | |
| Calculate the distance from the current agent position and the target. | |
| IF dist < movS: | |
| move agent to target position | |
| Jump to ATTACK | |
| ELSE: | |
| TURN agent heading by Ψ radians | |
| MOVE forward according to new heading, at distance (movS·movA) | |
| from current position. | |
| Jump to END</pre> | |
| #####4. Attack | |
| <pre><small>Let α be a random real value in [0, 1] | |
| IF α > (1 - vpAttackChance): | |
| Attack Success ⟵ true | |
| τ ⟵ (prey colouration - τ)·CAF | |
| Decrement hunger by ƒ(χ)·bg | |
| Increment ετ | |
| Remove prey agent from population. | |
| Jump to END</pre> | |
| #####5. Mate Search (local) | |
| <pre><small>Search neighbouring PREDATOR agents | |
| IF neighbour fertility > PREDATOR ENERGY REQUIREMENT: | |
| INTERCEPT target | |
| Attempt to COPULATE, decrement neighbour fertility by SEXUAL COST | |
| IF copulation successful: | |
| agent gravid ⟵ true | |
| agent fertility ⟵ -GestationCost | |
| ELSE: | |
| agent fertility ⟵ 1</pre> | |
| #####6. Patrol | |
| <pre><small>Let ϴ be a random angle in (-turnRate, +turnRate) | |
| IF ϴ > zero: agent turns left by ϴ radians | |
| If ϴ < zero: agent turns right by ϴ radians | |
| TURN agent heading by ϴ radians | |
| MOVE forward according to new heading, at distance (movS·movA) from current position. | |
| Jump to END</pre> | |
| #####7. Death | |
| <pre><small>Remove agent from PREDATOR population and current sector record. | |
| Jump to END</pre> | |
| #####8. End | |
| <pre><small>Increment action counter for model time</small> | |
| EXIT </pre> | |
| <br> | |
| ---------- | |
| <br> | |
| #Prey Colouration as a Relation to Predator Specialisation | |
| From the abstract to *The Evolution of Color Polymorphism: Crypticity, Searching Images, and Apostatic Selection*: | |
| >The development and maintenance of color polymorphism in cryptic prey species is a source of enduring fascination, in part because it appears to result from selective processes operating across multiple levels of analysis, ranging from cognitive psychology to population ecology. Since the 1960s, **prey species with diverse phenotypes have been viewed as the evolved reflection of the perceptual and cognitive characteristics of their predators.** [^Bond] | |
| Following the respective descriptions and rule-based behaviour algorithms for our two agent types, it would be remiss not to address the greater complexity that Visual Predator agents are endowed with in respect to their *CP Prey*. This disparity grows when we see the greater detail of the programmed methods for *VP* agents, especially for hunting. $\ $Whilst it is not incongruent to the predator-prey dynamic in the natural world that predating organisms target those of overwhelming less capability to avoid becoming prey, too much prescription and detail in the definition of an agent type risks running counter, at least in spirit, to the goals of an *Agent-Based Model*.$\,$[^ABM-methodology] $\ $With respect to our modelling goals, however, the detailed definition of Visual Predator agents is not in conflict: what we are trying to model and see 'emerge' is **cryptic variation in Colour-Polymorphic Prey**, not the evolution of specialisation in visual predation. | |
| In order to reflect the rationale for this approach, let us begin by formalising the bolded quote from Bond above into a statement describing the *relation* between prey colouration and specialisation of visual predation in our software model: [^relation-disambiguation] | |
| >*Diverse phenotypes shall reflect the characteristics of their predation.* | |
| In order to convey this relation in functional terms, we must describe precisely how visual search and successful predation is implemented in the $\,\text{ABM-CP}$ software model. | |
| ###Attention as a Searchlight | |
| > Studies of spatial attention suggest to some psychologists that visual attention can profitably be compared to a searchlight beam than can "shine" anywhere in the visual field. The "beam" marks the region of space for which one is prepared, so inputs within the beam and processed more efficiently and more swiftly. The beam can be wide or narrowly focused, and it can be moved about at will as one explores (attends to) one aspect of the visual field or another. [^attention-quote] | |
| <abr> | |
| >Visual search is subject to processing limitations, in that it is more difficult to search for two or more dissimilar, cryptic prey types simultaneously than to search for only one... [So,] Predators consequently tend to focus on the most rewarding prey type and effectively overlook the others. [^Bond] | |
| The key methodological question which needed to be answered was: *how to define visual predator specialisation in such a way that models a competitive advantage?* | |
| This was formalised, as it related to the model design, by defining **specialisation imprinting with respect to colour targeting** as that which results in a Visual Predator favouring those attack vectors with a higher efficacy and optimal efficiency: $\,$ The greater the colour specialisation of a predator, the lower the energy expenditure to successfully attack a prey agent of the target colouration $\,\therefore\,$ the greater total energy reward when consuming prey for those with a higher specialisation or narrow *focus* on a single target. | |
| Let us define comparatively "Generalist" and "Specialist" Visual Predator agents within $\,\text{ABM-CP}$: | |
| - A *Generalist* is an agent who maintains a low positive level of **colour specialisation strength** $\mathcal{E}_\tau$ over its lifetime, and correspondingly has a high tolerance $(\gamma)$ for variations in prey colouration *("wider" searchlight beam with lower illumination)*. | |
| - A *Specialist* is an agent that maintains a high positive level of **colour specialisation strength** over its lifetime, and correspondingly has a low tolerance for variations in prey colouration *("narrow" searchlight beam with much stronger illumination)*. | |
| <small>*Nb. Currently, the degree of specialisation is wholly* adaptive *within the lifetime of a VP agent, but it is trivial to define inherited specialisation properties.* | |
| $\text{Let :}$ | |
| * $q_p$ be the member parameter $\,p\,$ of some agent $\,q:\ $then, for example $\ q_{(x,y)}\ $ is the positional vector of $\,q\,$ | |
| * $\ \delta_{\text{v}u}\ $ or $\ \text{v}\frown u\ $ denote the vector distance between a Visual Predator $\,\text{v}_{(x_0,y_0)}$ and a CP Prey agent $\ u_{(x_1,y_1)}$ | |
| * $\ \chi_{\text{v}u}\ $ or $\ \text{v}\, \chi\, u\ $ denote the colour difference *(colour distance)* between the target colour of a Visual Predator $\,\text{v}_{\tau}\,$ and the colouration of a CP Prey agent $\,u_{\text{col}}\,$ | |
| * $c\ $ denote $\ \text{v}_{\varepsilon\tau}\,$ the **colour specialisation / imprinting strength** of Visual Predator $\,\text{v}$. $\ $*Nb. this is guaranteed to be a positive real number.* | |
| * $f\,$ denote the function for determining **colour recognition intensity** of $\ \text{v}\, \chi\, u\ $, with input $\chi$ and constant $\,c\,$ such that: | |
| $$\chi \,\longmapsto c e^{-c\chi}$$ | |
| **where the image of $f$ is denoted $f (\chi)$ or $f_\chi = \,$ the energy reward for consuming a prey agent.** | |
| If we consider a *Generalist* with a specialisation strength of $\,2\,$ and a *Specialist* with a specialisation strength of $\,16\,$, we get the following plots of $\,f\,$: | |
|  | |
| Let $\,\kappa\, $ denote the solution between the two at $\,\approx 0.148532$ $\ \ $ *i.e.* where the difference between the **target colour** and **prey colouration** $\, = \chi \approx 0.148532$ or about $15\%$ | |
| The energy gain for all values of $\ \chi < \kappa\, $ overwhelmingly favours the *Specialist*. | |
|  | |
| For all values of $\chi > \kappa\,$ the *Generalist* actually outperforms the *Specialist* because the focus/specialisation on such a narrow colour range comes at a cost to flexibility. | |
| We should therefore expect a global optimum value for $\mathcal{E}_\tau$, where too great a specialisation makes it impossible for a Visual Predator to adapt to changes in the Colour-Polymorphic Prey population. | |
| <br> | |
| ### Example | |
| Consider a section of a world state, with CP Prey agents $\ a,\,b,\, \dotsc,\,h\ $ and Visual Predator $\,v$ : | |
|  | |
| The illustration above is consistent with the language of the $\text{ABM-CP}$ visualisation: circles represent CP Prey agents with colouration, while the triangle shows the Visual Predator $\text{v}$ with its blue target colour $\,\text{v}_\tau$. The dashed circular line denotes the perimeter of visual search radius $\ \text{v}_{sr}\ $. | |
| So, let $\,S\,$ be the set of CP Prey agents which are within the visual search range $\,vsr\,$ of Visual Predator agent $\,v$ $\ \ \therefore \, S = \{a, b, c, d, e, g\}$ | |
| Observe that of the prey agents that are within visual range of $\,\text{v}\,$, only $\ a, b, g \ $ will be *seen* by the Visual Predator. This is because the difference in colour between what $\,\text{v}\,$ is looking for and the agents $\,c, d, e\,$ is too great – i.e. the difference $\,\chi\,$ is greater than the tolerance $\,\text{v}_\gamma$ : | |
| $\therefore\ $Let $\,T\,$ be a subset of $\,S\,$ containing only those agents $\,s \in S\,$ s.t. $\ \chi_{\text{v}s} \leq \ \text{v}_\gamma$ | |
| $\therefore \,T = \{a,b,g\}$ | |
| Precisely which prey agent $\,\text{v}\,$ targets is determined by the difference of $\,f(\chi)$ and vector distance $\,\delta\,.$ | |
|  | |
| Accordingly, let $\,\prec\ $ be an ordering *relation* between any two distinct CP prey agents $\,x,\, y\,$ with respect to a Visual Predator agent $\,\text{v,}\,$ where $\ x\prec\ y\ \equiv \ [\,f(\chi_{\text{v}x}) - \,\delta_{\text{v}x}\,] \, < \, [\,f(\chi_{\text{v}y}) - \,\delta_{\text{v}y}\,]$ | |
| Suppose the colour specialisation strength of $\,\text{v}\, = \text{v}_{\varepsilon\tau} = 5.0\ $, giving us the following plot of $f$: | |
|  | |
| Let us apply $\,\prec\,$ to $\,T\,$ such that $\,(\,T,\prec\,)\,$ is a *linearly ordered set*. | |
| $\therefore \ (\,T,\prec\,) = \{g,b,a\}\ $ and $\,\text{v}\,$ intercepts $\,g\,$, consuming the prey agent. | |
| As a result of successfully attacking a prey agent, $\,\text{v}\,$ increases its specialisation strength $\text{v}_{\varepsilon\tau}$ and adjusts its target colour by $\Delta\tau$ [^delta-tau] | |
| Compare this result with that of $\,\text{v}\, = \text{v}_{\varepsilon\tau} = 32.0\ $: | |
|  | |
|  | |
| In this case, even the minor colour differences of agents $\,a,b,g\,$ are too great for $\,\text{v}\,$: the predator would be entirely blind to *all* prey agents in our scene, and $\,\text{v}\,$ would continue to hunt for a compatible match until it began to starve, forcing it to attempt to adapt by lowering its specialisation value. $\ $By then, however, any prey agents might be too far away, and $\,\text{v}\,$ would be at risk of death. | |
| ###Summary | |
| The *relation* between *Prey Colouration* and *Predator Specialisation*, such that **diverse Colour-Polymorphic Prey morphs reflect the characteristics of their Visual Predation**, should be the "evolutionary" paradigm observable between the two agent types. Over-specialisation of predator morphs to one colour type pf prey agent will inevitably be an evolutionary dead-end, while under-specialisation should result in lower breeding rates which is a direct disadvantage. | |
| <br> | |
| ---------- | |
| <br> | |
| #Condition Parameters | |
| A Condition refers to the unique collection of parameters that provide the *constant* dependent conditions that the `abm` package will use to construct a **Model**: how fast agents move in a given action, how likely they are to successfully reproduce, how long their lifespans are, etc. Loaded as a JSON-encoded text block (either from the browser-based GUI or via command-line input), until some Condition is defined for the required Agent-Based Model, computation cannot proceed. Each unique modelling session, therefore, is initialised by *injecting* the conditions specified by the user, which effectively *seed* the model. | |
| Thus, all user control in the $\text{ABM-CP}$ Application lies in the definition of those condition parameters which specify variables for agent types and their environment. In addition there are optional "flags" which toggle the contents of the **Model**'s $\,$*engine set* $\,$ (e.g. visualisation and logging). | |
| For the sake of complete flexibility, there are approximately fifty parameters. However, between individual sessions it is unlikely that we should need to change most of them session after session; as a solution, there needs to be *preset* Conditions to load and inherit from where one can specify overriding values as needed. | |
| ###Population Controls | |
| Amongst the users controls are conditional parameters which define starting values and caps on agent populations. Given the "perfect" environment, the population of CP Prey would grow exponentially and beyond the bounds of reasonable computation. If the initial population of VP agents is too large in relation to that of the CP Prey, the prey agents would be decimated and the predators quickly lose their food source and die out. We therefore use the population parameters both as a way to set different population conditions and to control the scope of the model. | |
| <font size=3>`abm-cp-prey-pop-start` | |
| `abm-cp-prey-pop-cap` | |
| </font> | |
| <font size=3>`abm-vp-pop-start` | |
| `abm-vp-pop-cap` | |
| </font> | |
| ###Agent Characteristics | |
| Includes how fast an agent moves per turn, lifespan, limit on possible number of progeny that an agent type can spawn, etc. | |
| <font size=3>`abm-cp-prey-ageing` | |
| `abm-cp-prey-lifespan` | |
| `abm-cp-prey-speed` | |
| `abm-cp-prey-acceleration` | |
| `abm-cp-prey-turn` | |
| `abm-cp-prey-gestation` | |
| `abm-cp-prey-sexual-cost` | |
| `abm-cp-prey-reproduction-chance` | |
| `abm-cp-prey-spawn-size` | |
| `abm-cp-prey-mf` | |
| </font> | |
| <font size=3>`abm-vp-ageing` | |
| `abm-vp-lifespan` | |
| `abm-vp-starvation-point` | |
| `abm-vp-panic-point` | |
| `abm-vp-gestation` | |
| `abm-vp-sex-req` | |
| `abm-vp-speed` | |
| `abm-vp-acceleration` | |
| `abm-vp-turn` | |
| `abm-vp-vsr` | |
| `abm-vp-visual-acuity` | |
| `abm-vp-visual-acuity-bump` | |
| `abm-vp-baseline-col-sig-strength` | |
| `abm-vp-max-col-sig-strength` | |
| `abm-vp-reproduction-chance` | |
| `abm-vp-spawn-size` | |
| `abm-vp-attack-chance` | |
| `abm-vp-baseline-attack-gain` | |
| `abm-vp-col-adaptation-factor` | |
| `abm-vp-starvation`</font> | |
| ###Randomness and Frequency | |
| <font size=3>`abm-random-ages` | |
| `abm-rng-random-seed` | |
| `abm-rng-seedval` | |
| `abm-rng-fuzziness` | |
| </font> | |
| ###Application Flags | |
| <font size=3>`abm-logging-flag` | |
| `abm-log-frequency` | |
| `abm-use-custom-log-filepath` | |
| `abm-custom-log-filepath` | |
| `abm-log-filepath` | |
| `abm-visualise-flag` | |
| `abm-visualise-freq` | |
| `abm-limit-duration-flag` | |
| `abm-fixed-duration` | |
| `abm-session-identifier` </font> | |
| <br> | |
| ---------- | |
| <br><br> | |
| #Software Construction | |
|  | |
| From the beginning, given that modularity was a known requirement, the development process needed to be as organised and orderly as possible: building complete, foundational components which can reliably inter-operate and be used as cogs in the *'engine'* for the ABM. | |
| One of my personal requirements was that the project be *open-source* for two reasons: | |
| 1. When developing a program to test a research hypothesis it would seem essential that anybody should have free access interrogate the code base as a means of verifying the resultant data used in a publication. | |
| 2. By using **[git](https://en.wikipedia.org/wiki/Git_%28software%29)** with the source code hosted by **[GitHub](www.github.com)** I would have free, standardised source code management. This is particularly a natural route by the fact that the command `go get` to copy and install a Go project uses **git**. | |
| <br> | |
| ###Dealing with Vector Geometries | |
| The first consideration was to decide how a *vector* is to be represented programmatically before we can implement the functions necessary for computing movement and relational actions by agents in our 2D vector space. In **Go** the most important data structure for convenient and efficient means of working with sequences of typed data is a *slice*, a dynamic-sized array of elements stored in contiguous memory[^GoBlog-Slices]. By taking advantage of *slices* we can write general vector operations *(cross/dot product, scalar multiplication, vector distance, rotation, etc)* which are suitable for *n-dimensional* vectors. | |
| By defining a customised slice `type Vector []float64` we can pass a `Vector` of any length *(dimensionality)* to a function which expects a `Vector`, and then test for its length inside the function itself if needed. *(For example, to calculate the cross product of any two vectors, we cannot perform the operation on vectors of two different lengths, nor on vectors that are not three dimensional, so the cross product function must test for `Vector` length equality.)* | |
| The necessary vector operations were constructed as a distinct Go *package*[^GoBlog-packages] named **`geometry`** made up of small, reliable functions, covering the gamut of operations I would need, and which I could easily add to later. | |
| <pre class="prettyprint"><code class="go"> | |
| // Vector : Any sized dimension representation of a point of vector space. | |
| type Vector []float64 | |
| // VecScalarMultiply - performs scalar multiplication on a vector v, s.t. scalar * v = [scalar*e1, scalar*e2, scalar*e3] | |
| func VecScalarMultiply(v Vector, scalar float64) (Vector, error) { | |
| if len(v) == 0 { | |
| return nil, errors.New("v is an empty vector") | |
| } | |
| var sm Vector | |
| for i := range v { | |
| sm = append(sm, (v[i] * scalar)) | |
| } | |
| return sm, nil | |
| } | |
| <br></font></code></pre> | |
| <center><small>*Example function from geometry package*</font> | |
| <br> | |
| Unit tests were created for each function, providing static and random inputs to test against when making any changes to the codebase. | |
|  | |
| <CENTER><small>*Agent movement is accomplished in two steps: first, rotation, then a scalar on the unit directional vector.* | |
| <br> | |
| ###Colour | |
| As colouration would be the key aspect in defining our *CP Prey* agents and their relation to the *VP* agents which are hunting them, I needed to create simple structures and methods which encapsulated the use of "Colour" and allow for it be used independently of implementation details. Although small, I decided to create `colour` as a separate package as I had done with `geometry`. | |
| Programmatically, a Colour instance `colour.RGB` is a three-channel (Red, Green, Blue) identity represented as floating-point values within $[0, 1]\,$; where $\text{colour(}0,0,0)=\,$ `colour.RGB{0.0, 0.0, 0.0}` is black, and $\text{ colour(}1,1,1)=\,$ `colour.RGB{1.0, 1.0, 1.0}` is white. This is consistent with the $\text{ABM-CP}$ metric standards, which means values from a `colour.RGB` instance do not need to be converted/cast when interacting with any others in the computation. | |
| <pre class="prettyprint"><code class="golang">type RGB struct { | |
| Red float64 `json:"red"` | |
| Green float64 `json:"green"` | |
| Blue float64 `json:"blue"` | |
| }</code></pre> | |
| <CENTER><font size="3">*colour.RGB struct type with JSON encoding fields*</font></CENTER> | |
| <pre class="prettyprint"><code class="golang">func RGBDistance(c1 RGB, c2 RGB) float64 { | |
| red := math.Abs(c1.Red - c2.Red) | |
| green := math.Abs(c1.Green - c2.Green) | |
| blue := math.Abs(c1.Blue - c2.Blue) | |
| return calc.ToFixed(((red + blue + green) / 3.0), 3) | |
| // returns to 3 d.p. only | |
| }</code></pre> | |
| The above function quantifies the value difference between two `colour.RGB`s, returning a positive or negative floating-point ratio from 0.0 to 1.0. [^rgbDist-Signed] | |
| <br> | |
| ##Modelling | |
| The `abm` package is effectively an entire program without a direct executable interface. Each time a **Model** is created by the user of the package, that is logically equivalent to the creation of a subprogram by the caller software, designed to be run concurrent with the caller which acts as the *supervisor* to the `abm` process. | |
| <br> | |
| <pre class="prettyprint"><code class="golang">type Model struct { | |
| timestamp string // instance inception time | |
| running bool | |
| Timeframe // embedded Model clock | |
| Environment // embedded environment attributes | |
| ConditionParams // embedded local model conditions and constraints | |
| AgentPopulations // embedded slices of each agent type | |
| Om chan gobr.OutMsg // Outgoing comm channel | |
| Im chan gobr.InMsg // Incoming comm channel | |
| e chan error // error message channel | |
| Quit chan struct{} // client monitor signal | |
| halt chan struct{} // execution engine signal | |
| render chan render.AgentRender // VIS message channel | |
| turnSync *gobr.SignalHub // synchronisation | |
| Stats // embedded global agent population statistics | |
| DatBuf // embedded buffer of agent pop record for LOG | |
| }</code></pre> | |
| <br> | |
| The data and execution of each $\,\text{ABM-CP}\,$ session is held in the **Model** structure, with *each instance acting as the working encapsulation of the modelling session*, collecting the necessary substructures with the accompanying operations as methods on the `struct` type. | |
| ###Client Wrapper | |
|  | |
| <CENTER><font size="3">*A User establishes a connection to the supervisor process, which creates a new Model instance.*</font></CENTER> | |
| As a **Model** executes 'blind' of the User connection type *(e.g. Web App GUI, remote/local command line, etc)*, it is embedded in the **Client** structure which facilitates external I/O and controls to the User with the corresponding interface *(e.g. WebSockets, SSH)*. This allows for multiple, concurrent modelling instances on a single host machine or server that runs the program, with the correct **Client** type enclosing the **Model** created by the *supervisor*. | |
|  | |
| <CENTER><font size="3">*The server encloses each Model instance in the correct Client implementation for the connection to the user.*</font></CENTER> | |
| <pre class="prettyprint"><code class="golang">package abm | |
| // Client is the wrapper interface between the different comm protocols with the 'user' / 'caller' and the Model. | |
| type Client interface { | |
| Monitor(chan struct{}) | |
| Dead() bool | |
| TimeStamp() time.Time | |
| }</code></pre> | |
| <CENTER><font size="3">*The abm.Client interface.*</font></CENTER><br> | |
| <pre class="prettyprint"><code class="golang">package web | |
| // SocketClient wraps the Model, bridging it to the WebSocket user connection. | |
| type SocketClient struct { | |
| *websocket.Conn | |
| UUID string | |
| Name string | |
| *abm.Model | |
| active bool | |
| timestamp time.Time | |
| } | |
| // Monitor keeps the client's connection alive, | |
| // and responds to any internal running model signaling. | |
| // Implements abm.Client interface method for SocketClient. | |
| func (c *SocketClient) Monitor(ch chan struct{}) { | |
| defer func() { | |
| c.active = false | |
| c.timestamp = time.Now() // record when closed. | |
| }() | |
| for { | |
| select { | |
| case ←c.Quit: // internal signal from Model | |
| close(ch) // exit websocket connection. | |
| // send final statistics | |
| // clean up | |
| return | |
| case ←ch: | |
| c.Suspend() // websocket connection dead, suspend model operation. | |
| return | |
| default: | |
| time.Sleep(time.Millisecond * 100) | |
| } | |
| } | |
| }</code></pre> | |
| <CENTER><font size="3">*The Web User's Client implementation using WebSockets*</font></CENTER><br> | |
| **The abm.Client interface is defined by three methods: Monitor, Dead, and TimeStamp**. Any user-defined type which implements those three methods is automatically a **Client** to the **Model**. | |
| In the Monitor implementation above, it keeps the connection alive and responds to any internal **Model** instance signalling. For example, if there is a fault in the running abm, or if the population of the CP Prey agents reaches zero, then the model will invoke `Stop()` and the `Quit` channel will close, automatically signalling to any listening processes, which permits us to clean up and disconnect the Client. <br><br> | |
| ###Model Substructures | |
| The substructures of each **Model** instance are the data representation of each modelling construct: | |
| - **Timeframe** $\ $structure to represent in-model clock as `TURNS :: PHASES :: ACTIONS` | |
| - **ConditionParams** $\ $structure is the collection of the static (constant) modelling parameters set by the user at inception. | |
| - **Environment** $\ $structure which specifies the boundary / dimensions of the working model. | |
| - **AgentPopulations** $\ $are the sets of active Prey and Predator agents in the running model for the current *turn*, stored as *slices* of individual agents. | |
| <br> | |
| In addition, each **Model** stores statistical data (**Stats**) and holds a single-turn buffer of all agent data (**DatBuf**) in memory to be written to log files concurrently to the main $\ \text{ABM-CP}\ $ computation. | |
| ####**Agent Methods** | |
| Each **Agent** type – `ColourPolymorphicPrey` and `VisualPredator` – has its own set of methods defining movement/reproduction/etc, as well as the generation of entire populations, the serialisation of description data to JSON-encoded strings, and the export of the minimal data sufficient to represent the individual agent on screen in the `agentRender` struct to be sent to visualisation. | |
| ####**Agent Description** | |
| <pre class="prettyprint"><code class="golang">type AgentDescription struct { | |
| AgentType string `json:"agent-type"` | |
| AgentNum int `json:"agent-num"` | |
| ParentUUID string `json:"parent"` | |
| CreatedMT int `json:"creation-turn"` | |
| CreatedAT string `json:"creation-date"` | |
| } | |
| </code></pre> | |
| Defined at time of agent creation to facilitate detailed statistical logging of each agent's lifetime, both agent types embed their own **Agent Description**, essentially analogous to a private record in a database. In the future this can be held in the table of a database, using the Agent's UUID as a foreign key identifier. | |
| ####**Managing Agent Populations** | |
| As mentioned above, a population, *or set*, of a given agent type is stored as a slice. When necessary for agents to be quickly partitioned into subsets or added into new collections, pointers to agents within the master populations were used, therefore reducing the need to copy agent states unnecessarily. Using slices to manage fluid groups of agents combines the advantages of linked-lists with direct/random element access of an array, and without the need to change any notation as Go automatically dereferences pointers by default. | |
| Using pointers to subsets of a Population allows for the dynamic sorting necessary for Visual Predators to search for nearby prey – in particular it meant that search parameters and methodologies could be swapped ad-hoc and safely tested against. If a *VP* successfully attacks *(consumes)* a *CP Prey*, rather than directly interfering with the Population, we just 'flag' the now-dead agent for removal by setting its lifespan to zero. | |
| This works because the slices which store the current agent populations of each agent type are 'refreshed' every turn as a necessary condition of the dynamic expansion and contraction of agents. | |
| We begin with an empty set which will become the Population of agents for the next turn. Each agent from the Population of the current turn individually runs through the defined **Rule-Based Behaviour** algorithm for that type, which is implemented as a function returning a set *(as a slice)* of agents. If the agent is still alive during the turn then the agent is copied to the set that will be returned – If the agent 'spawns' during the turn, then its progeny are also added to the returning set. However, if the lifespan of an agent is $\,0$, then the agent which called the function in the first place is *not* copied to the returning set, and the memory resource for that particular agent instance will be garbage collected. After each **RBB** function return, the new set/*slice* of agents is appended to the Population for the next turn. Finally, once the entire population has run through the **RBB** algorithm, the new population set for the next turn replaces the current one. By using the lifespan *(or hunger level of an individual agent)* as a 'flag' for removal we end up with a high-level *mark-during-sweep* algorithm inside the running of an $\text{ABM-CP}$ turn without any extra code. Also worth mentioning is that as the new *CP Prey* Population is contiguous in memory, it is ready to be searched by *VP* agents in the next phase of the current turn. Coupled with the efficiency and speed of Go's GC, this method has proven to be sufficient without requiring any premature optimisation. | |
| ##Model Orchestration | |
| >Concurrency is the *composition* of independently executing processes, while parallelism is the simultaneous *execution* of (possibly related) computations. Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once. | |
| > | |
| >*Concurrency* is a way to structure a program by breaking it into pieces that can be executed *independently*. | |
| > | |
| >*Communication* is the means to *coordinate* the independent executions. $\ $[^Pike-ConcurrencyNotParallelism] [^GoBlog-ConcurrencyNotParallelism] | |
| As mentioned earlier, Go's concurrency model is based on *Communicating Sequential Processes* (CSP), but it can also be seen as a type-safe generalisation of Unix pipes. [^EffectiveGo-Concurrency1] The elements of this model are **goroutines**, **channels**, and the **select** statement. | |
| In Go, independent, concurrent processes[^processes-disambiguation] are created as **goroutines**, while **channels** serve to orchestrate their operation and communicate between them: when a resource needs to be shared between goroutines, channels act as a conduit between the goroutines and provide a mechanism that guarantees a synchronous exchange (when used with unbuffered channels). [^GoInAction-Channels1] | |
| **Select** is the concurrent control structure, similar to a *switch* statement. | |
| <pre class="prettyprint"><code class="golang">select { | |
| // blocks until one of the cases can proceed | |
| case ch1 ← x: // sending data over a channel | |
| // ... | |
| case y := ←ch2: // receiving data from a channel | |
| g(y) | |
| ch4 ← struct{}{} // sending a signal | |
| case ←ch3: // receiving a signal | |
| // ... | |
| } | |
| </code></pre> | |
| Each case specifies a communication (a send of receive operation on some channel). A **select** waits (blocks) until a communication is ready to proceed – it then performs that communication and executes the case's associated statements[^select-gopl] then exits. | |
| In order to *orchestrate* between the concurrent processes of each modelling session, a **Model** uses **channels** which *share memory by communicating*, allowing the passing of data between **goroutines** to simultaneously coordinate activity through the **select** statement. [^Concurrency-appendix] | |
| As with all member variables inside user `struct` types in Go, publicly accessible channels are those whose names begin with a capital letter: $\ $The `Om` and `Im` channels provide I/O between the **Model** instance and the User controls *(via the supervisor process)*, while the `Quit` channel is used to safely end the $\,\text{ABM-CP}\,$ session. The internal channels communicate between concurrent methods of the **Model** instance. As channels are first-class types in Go, they are easily created and passed as parameters to functions which are called concurrently. | |
| Each **Model** instance also holds an embedded pointer to a concurrent `SignalHub`[^SignalHub] which is used to broadcast *turn synchronisation* messages (using a `map` of channels) to registered **goroutines** operating concurrently to one other: | |
|  | |
| The `Broadcast()` method *blocks* the broadcasting goroutine until all registered goroutines retrieve the signal message – thereby forcing Model synchronisation between every Turn. [^turn-sync-blocking] | |
| Further, by using channels to communicate between the different sections of the program operation, we can more easily "see" and reason about the connections between the concurrent surface of a Model's execution. | |
| ##Composing for Concurrent Model Execution | |
| From the outset this project required concurrent **modelling computation**, **visualisation**, and data **logging**. The composition of the many concurrent procedures orchestrated by channels ended up producing something inherently cyclic, orbital, and reasonable: a single **goroutine** could spawn many others which would spin within the 'gravity' of their parent, which in turn operated in orbit of a larger orchestration. | |
| The resulting structure of each $\,\text{ABM-CP}\,$ instance is an extendable geared "Engine" such that any number of processes could be attached – forming the "engine set" – and execute concurrently and inter-operate using Go's channels. | |
|  | |
| <br> | |
| <pre class="prettyprint"><code class="golang">func (m *Model) Controller() { | |
| signature := "CONTROLLER_" + m.SessionIdentifier | |
| for { | |
| select { | |
| case msg := ←m.Im: | |
| switch msg.Type { | |
| case "conditions": // if conditions params msg is recieved, (re)start | |
| if m.running { | |
| register: | |
| clash := gobr.WaitForSignalOnce(signature, m.turnSync) | |
| m.e ← m.Stop() | |
| } | |
| err := json.Unmarshal(msg.Data, &m.ConditionParams) | |
| (...) | |
| m.Timeframe.Reset() | |
| m.e ← m.Start() | |
| case "pause": | |
| m.e ← m.Suspend() | |
| } | |
| case ←m.Quit: | |
| gobr.WaitForSignalOnce(signature, m.turnSync) // blocks | |
| return | |
| } | |
| } | |
| }</code></pre> | |
| Central to the Model Execution is the **Controller** which processes the instruction messages received by the **Client** and executes the corresponding controls, switching between operating states of the Model computation: | |
| - *START*$\ $ initialises the agent-based model according to the loaded ConditionParams, creating the agent populations and starting the requisite *Engine Set* (*e.g.* $\{$ `RUN`, `VIS`, `LOG` $\}$ ) specified by the user. | |
| - *SUSPEND*$\ $ closes the `halt` channel, thus signalling to all concurrent processes to exit at the end of the current **TURN** and halting the current progress of the agent-based model. This effectively pauses the execution of the Model instance, holding the agent-based model state in memory for 24 hours (or saved to a record) to be continued later. The default action taken on the loss of **Client** connection. | |
| - *RESUME*$\ $ is the inverse action to $\ $*SUSPEND*$\ $ – it restarts the engine set and continues the agent-based modelling. | |
| - *STOP*$\ $ closes the `halt` channel, and clears the active data of the agent-based model from memory. Once stopped, the state of the agent-based model cannot be recovered and resumed; the Model execution can only be reset and start over, either with the same Conditional Parameters or new ones. | |
| The **Controller** goroutine is kept alive for the life of the Model instance itself, waiting to receive a signal and switch the program operation. While not part of the *Engine Set* a Model's Controller acts as the orchestrator and link between the *Engine Set* and the rest of the application. | |
| ###Engine Set = $\{$ RUN (Modelling) , VIS (Visualisation) , LOG (Stats) $\}$ | |
|  | |
| With respect to the current state of the $\text{ABM-CP}$ repository, let us look at the *Engine Set* as implemented, using an artificial starting scenario to provide context and assuming that the user has flagged *Visualisation* and *Logging* in the contextual parameters. | |
| <pre class="prettyprint"><code class="golang">func (m *Model) Start() error { | |
| (...) | |
| timestamp := fmt.Sprintf("%s", time.Now()) | |
| (...) | |
| if m.Logging { | |
| go m.log(m.e) | |
| } | |
| if m.Visualise { | |
| go m.vis(m.e) | |
| } | |
| time.Sleep(pause) | |
| go m.run(m.e) | |
| return nil | |
| } | |
| </code></pre> | |
| When the **Controller** receives the **Contextual Parameters** to "seed" the Model, it calls the `Start` instruction which launches the goroutines making up the *Engine Set* that the user has specified. | |
|  | |
| <CENTER><font size="3">*In the structure of a Model Turn, the **Colour-Polymorphic Prey Phase** occurs first, with all agents in the population either spawning progeny or moving randomly. For clarity of illustration, Prey agents are shown in red, and Predators in blue. For each red dot at the start of the turn, a distinct goroutine is launched to carry out the computation necessary concurrently to its fellow agents.*</font></CENTER><br> | |
| ####RUN (Modelling) | |
| The `run` goroutine executes the control logic and computation successive **turns** of modelling until such time as it receives the signal to end via `halt` channel. If receiving on the `Quit` channel, also passes on the `Stop` instruction. | |
| <pre class="prettyprint"><code class="golang">func (m *Model) run(ec chan← error) { | |
| signature := "RUN_" + m.SessionIdentifier | |
| for { | |
| select { | |
| case ←m.halt: | |
| // block while waiting for turn to end. | |
| gobr.WaitForSignalOnce(signature, m.turnSync) | |
| time.Sleep(pause) | |
| return | |
| case ←m.Quit: | |
| // block while waiting for turn to end. | |
| gobr.WaitForSignalOnce(signature, m.turnSync) | |
| ec ← m.Stop() // stop model execution | |
| time.Sleep(pause) | |
| return | |
| default: | |
| if m.LimitDuration && m.Turn ≥ m.FixedDuration { | |
| ec ← m.Stop() | |
| return | |
| } | |
| if (len(m.popCpPrey) == 0) || | |
| (len(m.popVisualPredator) == 0) { | |
| ec ← m.Stop() | |
| return | |
| } | |
| // PROCEED WITH TURN | |
| m.turn(ec) | |
| } | |
| } | |
| }</code></pre> | |
| Whilst the code for the `TURN` execution is split into a separate function, that is largely for code parsing and management: each call to `turn()` is part of the **RUN** goroutine which runs concurrent with **ERR**, **VIS**, and **LOG** processes (where enabled). | |
| The turn code is split into two halves, first for the *CP Prey* `PHASE` and then the *Visual Predator* `PHASE`. Both phases have a similar structure so it is sufficient to look at the entirety of one and touch on the differences in the other: | |
| <pre class="prettyprint"><code class="golang">func (m *Model) cpPreyPhase(errCh chan← error) []ColourPolymorphicPrey { | |
| var mutex sync.Mutex | |
| var Wg sync.WaitGroup | |
| var agentsUpdate []ColourPolymorphicPrey | |
| for i := range m.popCpPrey { | |
| Wg.Add(1) | |
| go func(agent ColourPolymorphicPrey) { | |
| defer func() { | |
| Wg.Done() | |
| if m.Logging { | |
| errCh ← m.cpPreyRecordAssignValue(agent.UUID(), agent) | |
| } | |
| }() | |
| result := agent.Action(m.ConditionParams, len(m.popCpPrey)) | |
| if m.Visualise { | |
| m.render ← agent.GetDrawInfo() | |
| } | |
| mutex.Lock() | |
| agentsUpdate = append(agentsUpdate, result...) | |
| mutex.Unlock() | |
| m.Action++ | |
| }(m.popCpPrey[i]) | |
| } | |
| Wg.Wait() | |
| return agentsUpdate | |
| } | |
| </code></pre> | |
| The `Action()` method called on each `agent` in the *CP Prey* population constitutes the individual agent's `ACTION` for this turn according to the *Rule-Based Behaviour* defined for the agent type. The `result` of each is the slice constituting the calling agent (if still living) and any progeny it has spawned this turn. | |
|  | |
| <CENTER><font size="3">***Visual Predator Phase*** –*the potential movement and turn range for the Visual Predators in the current turn is represented by a pink arc. Here we see two Visual Predators intercepting and attacking from below the closest Prey agents to them. The successful attack on these Prey agents will mark them for deletion and they will be skipped over in the next Turn.*</font></CENTER><br> | |
| <pre class="prettyprint"><code class="golang">func (c *ColourPolymorphicPrey) Action(conditions ConditionParams, popSize int) (newpop []ColourPolymorphicPrey) { | |
| newkids := []ColourPolymorphicPrey{} | |
| jump := "" | |
| // BEGIN | |
| jump = c.Age(conditions) | |
| switch jump { | |
| case "DEATH": | |
| goto End | |
| case "SPAWN": | |
| progeny := c.Birth(conditions) // max spawn size, mutation factor | |
| newkids = append(newkids, progeny...) | |
| case "FERTILE": | |
| if popSize ≤ conditions.CpPreyPopulationCap { | |
| c.Reproduction(conditions.CpPreyReproductionChance, conditions.CpPreyGestation) | |
| } | |
| fallthrough | |
| case "EXPLORE": | |
| Θ := calc.RandFloatIn(-conditions.CpPreyTurn, conditions.CpPreyTurn) | |
| c.Turn(Θ) | |
| c.Move() | |
| } | |
| newpop = append(newpop, *c) | |
| End: | |
| newpop = append(newpop, newkids...) // add the newly created children to the returning population | |
| return | |
| }</code></pre> | |
| Depending on control flags in the Model **Condition Parameters**, extra steps for dispatching agent data to **LOG** and **VIS** goroutines are carried out for each CP Prey agent in the population at the moment of the current turn. | |
| As the action of each *CP Prey* agent is independent of any other in the population, the execution for each *CP Prey* agent's *Rule-Based Behaviour* can be executed concurrently in a goroutine, using lower-level control structures to ensure safe operation: Mutexes to serialise operation and avoid data races, WaitGroups to make sure all *CP Prey* goroutines finish before returning the update to the Model population via it's caller: | |
| <pre class="prettyprint"><code class="golang">func (m *Model) turn(errCh chan← error) { | |
| m.popCpPrey = m.cpPreyPhase(errCh) | |
| m.Phase++ | |
| m.Action = 0 // reset at phase end | |
| m.popVisualPredator = m.visualPredatorPhase(errCh) | |
| m.Phase++ | |
| m.Action = 0 // reset at phase end | |
| m.Phase = 0 // reset at Turn end | |
| m.turnSync.Broadcast(blocking) // sync with Engine Set goroutines | |
| m.Turn++ | |
| } | |
| </code></pre> | |
| The only significant difference in the execution of the *Visual Predator* phase from the *CP Prey* one is that each *VP* agent's `ACTION` must be (at this stage) executed *sequentially* to avoid potential data races when the *VP agents* interact with each other and the *CP Prey* population. | |
| Finally, `turnSync.Broadcast` is called with *blocking* so that the broadcast ensures synchronisation with the other processes in the active Engine Set. | |
| ###VIS (Visualisation) and LOG (Statistical Logging) | |
|  | |
| <CENTER><font size="3">*The Model state at the end of the turn is what will be rendered in the client viewport. ` VIS` sends on the minimum set required over the wire – just the agent's position coordinates, heading, and colouration.*</font></CENTER><br> | |
| If `VIS` is in the active Engine Set, then all agents in a given population prior to exiting their run through of their respected phase, send the minimal data required to draw an agent in the viewport along the Model's **render** channel. This gets received by the `VIS` process and appended to the *DrawList* to be sent to the User. As we have seen above, at each `turn` end, a signal is broadcast to all other processes in the Engine Set. At the moment `VIS` receives the signal it sends along the *DrawList* to the *Client* handler via the `Om` (Outgoing Message) channel, then clears it for the next turn. | |
| <pre class="prettyprint"><code class="golang">func (m *Model) vis(ec chan← error) { | |
| signature := "VIS_" + m.SessionIdentifier | |
| turnEnd := m.turnSync.Register(signature) | |
| (...) | |
| for { | |
| select { // blocks until one of the cases is true. | |
| case job := ←m.render: | |
| switch job.Type { | |
| case "cpPrey": | |
| dl.CPP = append(dl.CPP, job) | |
| case "vp": | |
| dl.VP = append(dl.VP, job) | |
| } | |
| case ←turnEnd: | |
| (...) | |
| msg.Data = dl | |
| m.Om ← msg | |
| msg = gobr.OutMsg{Type: "render", Data: nil} // reset msg contents | |
| dl = render.DrawList{} // reset draw instructions | |
| case ←m.halt: | |
| return | |
| } | |
| } | |
| } | |
| </code></pre> | |
| The `LOG` process follows a similar pattern, although far less efficiently. Currently each agent's data is duplicated to a map in **DatBuf** indexed by the agent's UUID after it completes it's **Action** in that Phase. When the signal for the turn end is received, the records are copied a second time and then written to seperate log files to disk. | |
| <pre class="prettyprint"><code class="golang">func (m *Model) log(ec chan← error) { | |
| signature := "LOG_" + m.SessionIdentifier | |
| turnEnd, clash := m.turnSync.Register(signature) | |
| (...) | |
| for { | |
| select { | |
| case ←m.halt: // RUN halted as channel closed – therefore we end LOG | |
| return | |
| case ←turnEnd: | |
| func() { | |
| cpr := m.cpPreyRecordCopy() | |
| vpr := m.vpRecordCopy() | |
| go func(record map[string]ColourPolymorphicPrey, errCh chan← error) { | |
| // write map as json to file. | |
| (...) | |
| }(cpr, ec) | |
| go func(record map[string]VisualPredator, errCh chan← error) { | |
| // write map as json to file. | |
| (...) | |
| }(vpr, ec) | |
| }() | |
| } | |
| } | |
| } | |
| </code></pre> | |
| Whilst the implementation of the logging is entirely impractical, it demonstrates that the structure of the Model execution can be extended, and an arbitrary number of additional processes can be mocked up and added to the *Engine Set* at will. | |
|  | |
| <CENTER><font size="3">*In the phases of subsequent Turns, the populations of both Prey and Predator agents carry out their Rule-Based Behaviours, with the state at the end of every* $n$*-turn being dispatched to be visualised or written to log file.*</font></CENTER> | |
|  | |
| ###Summary | |
| The concurrent composition of the *Engine Set* leans heavily on the *Pipelines* pattern[^GoBlog-Pipelines] of linking goroutines by channels: | |
| > [A] pipeline is a series of stages connected by channels, where each stage is a group of goroutines running the same function. In each stage, the goroutines will receive values from *upstream* via *inbound* channels, perform some function on that data (...) [then] send values *downstream* via *outbound* channels | |
| Breaking the program into many small, logical sections which could execute concurrently not only made the operation of the program easier to reason about as it grew in scale, it also meant that changes or insertions to the pipeline did not break the rest of the code – that is, code which makes up the potential *Engine Set* can be 'plugged in' to the existing codebase with one or two lines of code. | |
| <br><br> | |
| ---------- | |
| <br> | |
| #Conclusion | |
| ###Educational Development | |
| Prior to this project I had effectively zero experience and knowledge of the reality of modern web development and its fundamental concepts, my experience was mostly limited to a smattering of HTML and Javascript. (I did, however, have experience with graphics programming and the Processing language, and as a result the *rendering* in a viewport inside a web browser using the **P5.js** library was a swift and straightforward.) | |
| Thankfully, the decision to use **Go** proved to be a very successful decision – not only did it force me to really learn correct approaches and methodologies, rather than take a shortcuts, but I also learnt to implement them in a modern way directly, rather than through some framework, in a language which was principally designed for such *web* and *cloud* applications and to be turned towards practically any possible output (or, at least, the same ones you might use the noble *C* for). So, not only was the concurrency model of the language exactly what I was looking for with respect to my opinions and ideas for the modelling of agents and their interactions, Go was also the language I needed to connect it with the web UI and deal with the concurrent requirements of such an approach. | |
| Unfortunately, despite the joys and the generosities afforded to me in support of my learning, the balance of development time was so significantly taken up by the requirements of the web server and accompanying WebSockets implementation (both in the front and back ends) and the research for it, while going from zero prior experience with Go to making something functional as a Web App. In fact, such effort was required that it took up roughly 70% of my time I would estimate. Conversely, the construction and code for the agent-based modelling took about 20% of the time, but it was a creative pleasure and I worked very fast, and about 80% of the code was written for the agent-based model! | |
| The costs of the slow and consuming research and struggles to successfully stand up a working use of WebSockets cost dearly, and there was an entirely inappropriate amount of time left to work with Dr. Dale to make sure that the Model correctly addressed his research needs and adjust to his suggestions and requirements. I still have hopes that this can continue *ex post facto*, but I cannot help reflect on what a missed opportunity this was. Agent-based modelling is an incredibly stimulating research domain, and the chance to assist however slightly with Dr. Dale's work was and is very important to me. Whilst the scope of the paper for which this project was undertaken does not require or realistically allow for such collaboration, this is of little comfort – in the final analysis, offering the software and development time towards something productive for the research goals was actually the most important thing, and what all this effort should be in aid of. To be frank, I failed in this regard – not for lack of effort, or desire, but overwhelmingly from lack of time. To give some context, the section *Prey Colouration as a Relation to Predator Specialisation* in this report, although partially a reflection of my thinking and conception throughout the project, was the required response to important questioning by Dr. Dale of my approach and how I had overlooked part of what the modelling requirements were – the results and the argument I present in this section occurred in the last two weeks of the semester, and yet without this section there would be absolutely no rationale for my approach. This is what makes the shortcomings so disappointing, as the paper ended and the project came to a halt as a result of competing time commitments and occupations, the truly rich vein from an intellectual and research perspective was only just being discovered. So, whilst it is entirely outside the remit of this report, I shall offer my suggestion of how to best improve this paper and what both students and supervisor might best get from it: offer a secondary, accompanying 300-level paper as well, a 'Part B' so to speak, entirely optional, not to extend the scale of the projects that students can take on, but solely to implement requirements and recommendations from academic supervision so that the project can meet further (or the main) research goals, should the progress in 'Part A' have been successful and merit further investment. | |
| ###Successes | |
| The base requirement, to construct a "live" visualised *Predator-Prey* agent-based model was fully accomplished. | |
| The pattern of a (potentially distributed) computational "server" with an Web UI which allows for complete portability and zero overhead for the user was successfully implemented at a basic level, using WebSockets as the transfer protocol. | |
| The Visualisation efficiently and effectively communicates the Model state and individual and collective agent activity over time. It doesn't hurt that the visualisation of thousands of agents is also quite beautiful, as it not only gives one rapid feedback necessary for iterating on the design of *Contextual Parameters* but makes the labour required more enjoyable through visual engagement. | |
| The concurrency primitives and model of the language have been well exploited, and custom concurrency structures e.g. `SignalHub` were successfully implemented. | |
| The design and construction of the *Engine Set* to be extensible and customisable at will, the mechanics of turn synchronisation and concurrent program execution were successfully implemented. | |
| ###Shortcomings & Mistakes | |
| Despite "accomplishing" the primary goal of standing up and visualising an agent-based model over WebSockets, **there is a glaring flaw with the modelling process in its current form**. The model as designed is predicated on directly trying to observe evolutionary adaptation in *Colour-Polymorphic Prey* (in the form of *colour-polymorphism*) as a reflection of *Visual Predator* specialisation *vis-à-vis* the costs of visual search in their environment. Unfortunately, with the current requirement of population "caps"[^why-need-population-caps] defined in a Model's *Contextual Parameters*, the result is that the desired paradigm between *Prey colouration* and *Predator Specialisation* is not allowed to occur as the populations rapidly grow and hit the practical limit for a remote session and thus the Model is shaped by the arbitrary constraint instead. Throughout this process there seemed to be an implicit requirement, from my perspective, that the scale of the Model should be determined organically by an emergent balance or equilibrium between the *Prey* and *Predator* populations acting on each other alone. The exigencies of time and the development process unconsciously funnelled my self-criticality towards the expedient path[^expediency], needing to validate the *Engine Set* design and meet the minimum requirements to prove the viability of the project by roughly implementing the **LOG** "service". | |
| **The immediate thing to address must be a means to handle an unconstrained number of agents in the Model.** Options: only dispatch to **VIS** one "keyframe" per $x$ turns, compress the dataset before sending it over WebSocket, or send complete frames as images (will always have a fixed upper-limit size then). | |
| Once that is done, the parameters which provide a natural equilibrium between the populations can be discovered. | |
| **Hand coding GUI inputs: Should have used generated HTML front-end inputs based on type and name of parameters** in `ConditionParams` structure (i.e. by using reflection). This wasted *days* of productivity which meant I couldn't freely iterate on the Web App user-interface, and would have been significantly more effective if the front-end was generated from the configuration of the back-end – this also means there is less code to manage and the project is not split across multiple languages.[^gopherjs] | |
| **JSON serialisation is much too expensive, it is the single largest computational cost** by a significant margin and the resulting message size being sent "over-the-wire" or to log files is impractical. This may require a custom solution. At a minimum, there needs to be an attempt to economise by switching the serialisation format to *Protocol Buffers*[^protobuf] or something similar. | |
| As mentioned at the end of the *Software Construction* section, the logging system (**LOG**) is entirely impractical and only sufficient as a proof of concept to demonstrate the viability in the *Engine Set* design and concept – one (relatively short) modelling session would produce several GB of logging files. **Writing log files in JSON format is valuable for prototyping and debugging (by being human-readable), but the storage and computational requirements make it entirely impractical.** A fast, disk-based binary record system is needed,[^diskv] and compressed log outputs required. | |
| Further, whilst the dispatch of `DrawInfo` from each `agent` goroutine over channel to **VIS** in the appropriate phase makes sense, the copying of all agents individually over a channel (in fact, currently it ends up happening twice) is entirely unnecessary. A rethink is needed given that the logging process can happen entirely independent of turn synchronisation altogether, and any reading from any records/databases can be designed to be entirely seperate from the writes (which need only happen once per turn) and therefore be setup to be concurrent-safe. | |
| ###Implemented & Incomplete Features (Roadmap) | |
| >####**0.1.0** | |
| > | |
| >*Base requirements completed.* | |
| > | |
| >* A simple interface for the CP Prey ABM, with the visualisation on the left, with conditional parameter controls on the right. ✔ | |
| >* Use P5js for render viewport. ✔ | |
| > | |
| >* Browser recieves drawing instructions from ABM which get loaded into the P5 instance's draw loop. ✔ | |
| >* Responsive design, visualisation (render viewport) scales to the dimensions of available browser real estate. ✔ | |
| >* Server handles concurrent bi-directional connections for concurrent ABM modelling individual to each user, with data sent between client and server using Web Sockets. ✔ | |
| >* Server cleans up after user disconnections. ✔ | |
| >* Server receives new submitted conditionsual parameters as signal to start/restart ABM. ✔ | |
| >* Serialisation of data messages using JSON (prohibatively slow for anything approaching 10,000 agents). ✔ | |
| <br> | |
| >* CP Prey agents implementation: | |
| * Rule-Based-Behaviour. ✔ | |
| * Asexual Reproduction. ✔ | |
| * Mutation (colouration). ✔ | |
| <br> | |
| >* Visual Predator implementation: | |
| * Rule-Based-Behaviour. ✔ | |
| * Exhaustive Prey Search (very slow). ✔ | |
| * Colour Imprinting (needs tweaking, no baseline yet established). ✔ | |
| * Simple concurrent execution of Predator/Prey Action. ✔ | |
| <br> | |
| >* Unit tests for geometry, calc, render packages. ✔ | |
| >####**0.2.0** | |
| >* Dispatch errors along channels, use external handlers who receive the errors and process/print them. ✔ | |
| > | |
| >* Essential unit tests for `abm` package ✔ | |
| >* Show live population and timeline statistics inside P5js viewport. ✔ | |
| <br> | |
| >* Visual Predator implemenation: | |
| * Find baseline params for Colour Imprinting. ✔ | |
| * Adaptation in response to hunger. ✔ | |
| * Starvation $\Longrightarrow$ Death. ✔ | |
| * Sexual Reproduction. ✔ | |
| <br> | |
| >* General modelling and interactions between agent types in place, with all baseline parameters set for end-use. ✔ | |
| >####**0.3.0** | |
| >* Toggle Visualisation on/off ✔ | |
| >* Record data of a running model to log files as JSON for statistical analysis as a concurrent process, independent of Visualisation or model computation. Toggleable. ✔ | |
| >* User determined frequency of data logging on client side. ✔ | |
| >* Better model for Predator specialisation through colour-imprinting which directly gives search/attack advantage, rather than being decided randomly. ✔ | |
| >* Complete tests for `abm` package ✔ | |
| >####**0.4.0** | |
| >* Use `ffjson`–generated custom Marshal/Unmarshal JSON methods for ~2X speedup when serialising render messages to client ✔ | |
| >* Import JSON-formatted text files as pre-defined modelling ConditionParams via browser upload. | |
| >* Automatically gzip JSON-formatted logging files. | |
| >* Use k-dimensional tree for spatial partitioning of model environment, permitting optimal search. | |
| (General implementation in Go already done using trees connected to pointers to elements in slices) | |
| >* Web-side input validation for web contextual parameters | |
| >**0.5.0** | |
| >* Have complete control over ABM computation, logging, visualisation from command-line, rather than just starting up a web server and controlling through the the browser. | |
| >* Use uncompressed JSON-formatted logging for debug only. | |
| >* Switch to a compressed binary encoding for log files – or try FlatBuffers? | |
| >**0.6.0** | |
| >* Switch data serialisation to Protocol Buffers (protobuf) ~10X speedup. Marshalling drawing instructions to JSON is currently the single most expensive action! | |
| >**1.0.0 – Late 2016?** | |
| >* **Use Amazon Web Services and switch to a model of cloud (distributed) computation and storage** for all log files, thus entirely taking the burden off the user for all hardware costs in the modelling. Whilst the need for CPU and memory optimisation along with data compression over the wire remains essential, the scale of the model environment and populations could become entirely unrestricted. | |
| >* Switch all public html file to templated/generated ones based on conditions parameters etc. | |
| >* Switch to `gopherjs` for all front-end code? | |
| >* Allow end-user to switch between different browser layouts: Visualisation only, Standard and Statistical? $\Leftarrow$ Could use *Jupyter* to present graphing in browser? | |
| >* Start ABM computation remotely and keep running after disconnection? i.e. start the model running, leave it, reconnect based on session UUID at a later tim to check up or review results. | |
| >* Batch processing. | |
| >* Email user when model session finishes. | |
| >* Enable use in a distributed environment. | |
| >* Complete testing suite including integration tests. | |
| >* Allow hot-swapping of different `abm` variant packages. | |
| >* Store modelling sessions to server database along with statistical data for later retrieval. | |
| >* Fluid ABM timescale controls? Doable, but probably not without switching to gopherjs so I can integrate it all within the same codebase. | |
| >* Optional recording of Visualisation to SVG frame sequence using `ajstarks/svgo` package. | |
| <br><br> | |
| ---------- | |
| <br><br> | |
| [^why-need-population-caps]: <sub>The size of the messages being sent over the wire means that the gaps between drawn frames are so great that the program will slow to a crawl when the agent populations get too high. | |
| [^expediency]: <sub>Such expediency is a common flaw in the management of development time, but it would be counter-productive to not recognise and frankly analyse the circumstances of when and how it happens on a given project. | |
| [^gopherjs]: <sub>There is also the potential to swap out the seperate JS code for the viewport by embedding it in the generated Go package and/or switching to the `gopherjs` (www.gopherjs.org) package so that any javascript can be written in Go also. | |
| [^protobuf]: <sub>https://developers.google.com/protocol-buffers/ | |
| [^flatbuffers]: <sub>https://google.github.io/flatbuffers/ | |
| [^diskv]: <sub>Diskv – `peterbourgon/diskv` – a disk-backed key-value store looks like an appropriate package to begin experimenting with for this purpose. | |
| http://godoc.org/github.com/peterbourgon/diskv | |
| [^GoBlog-Slices]: <sub>$\ $Gerrand, Andrew (2011). *The Go Blog, Go Slices: usage and internals.* | |
| http://blog.golang.org/go-slices-usage-and-internals</font> | |
| $ $ | |
| [^GoBlog-packages]: <sub>$\ $Ajmani, Sameer (2015). *The Go Blog: Package Names.* | |
| https://blog.golang.org/package-names</font> | |
| ><sub>Go code is organized into packages. Within a package, code can refer to any identifier (name) defined within, while clients of the package may only reference the package's exported types, functions, constants, and variables. Such references always include the package name as a prefix: foo.Bar refers to the exported name Bar in the imported package named foo. Good package names make code better. A package's name provides context for its contents, making it easier for clients to understand what the package is for and how to use it. The name also helps package maintainers determine what does and does not belong in the package as it evolves. Well-named packages make it easier to find the code you need. | |
| [^Concurrency-appendix]: <sub>*See* | |
| $ $ | |
| [^rgbDist-signed]: <sub>The signed value returned by `RGBDistance` is necessary to differentiate between sets of colours which might have the same *magnitude* between values. | |
| $ $ | |
| [^SignalHub]: <sub>The `SignalHub` is of my own design, used for one sender to broadcast a signal either to multiple receivers, or to arbitrary ones individually. | |
| $ $ | |
| [^turn-sync-blocking]: <sub>The performance impact of such blocking is negligible as certain processes only commence after a turn's end. | |
| $ $ | |
| [^Pike-ConcurrencyNotParallelism]: <sub>$\ $Pike, R. (2012, February). *Concurrency is not Parallelism.* Presented at Heroku's Waza 2012 Conference, San Francisco, CA, USA. | |
| *Slides:* http://talks.golang.org/2012/waza.slide | |
| *Video:* http://vimeo.com/49718712 | |
| $ $ | |
| [^GoBlog-ConcurrencyNotParallelism]: <sub>$\ $Gerrand, A (2013). *The Go Blog, Concurrency is Not Parallelism.* | |
| https://blog.golang.org/concurrency-is-not-parallelism | |
| $ $ | |
| [^EffectiveGo-Concurrency1]: <sub>$\ $*Effective Go: Concurrency – communicate by sharing.* | |
| https://golang.org/doc/effective_go.html#sharing | |
| $ $ | |
| [^processes-disambiguation]: <sub>$\ $Meaning a *process* in the general sense: $ $ *a series of actions or steps taken in order to achieve a particular end.* | |
| $ $ | |
| [^GoInAction-Channels1]: <sub>$\ $Kennedy, W., Ketelsen, B., & St. Martin, E. (2016). *Go In Action*. Shelter Island, NY : Manning Publications Co. $\ $p.147 | |
| $ $ | |
| [^select-gopl]: <sub>$\ $Donovan, A. & Kernighan, B. (2016). *The Go Programming Language.* New York : Addison-Wesley. $\ $p.245 | |
| $ $ | |
| [^GoBlog-Pipelines]: <sub>$\ $Ajmani, S. (2014). *The Go Blog, Go Concurrency Patterns: Pipelines and cancellation.* <br>https://blog.golang.org/pipelines | |
| [^Bond]: <sub>Bond AB. 2007. *The Evolution of Color Polymorphism: Crypticity, Searching Images, and Apostatic Selection.* Annual Review of Ecology, Evolution and Systematics 38, 489-514. | |
| <font color=white>. | |
| [^ABM-methodology]: <sub>*i.e.* Simple agents, simple rules, producing complex behaviours/results at scale. | |
| <font color=white>. | |
| [^relation-disambiguation]: <sub> Meaning a *Binary Relation* between arbitrary sets $X$ and $Y$ specified by its graph $G$, which is a subset of the Cartesian product $X \times Y\ $ *s.t.* $\ xR\,y\,$ $(\ x \text{ is related to } \,y \text{ by }R \ )$ | |
| <font color=white>. | |
| [^attention-quote]: <sub>Reisberg, Daniel. 2010. *Cognition: Exploring the Science of the Mind. 4E*. $\,$WW Norton & Company : New York / London. p.111 | |
| <font color=white>. | |
| [^ctxt-bg]: <sub> $\,\text{b}_\text{g}$ is set as an external contextual parameter as part of model initialisation. | |
| <font color=white>. | |
| [^delta-tau]: <sub>$\,\Delta\tau = \text{v}_\tau - a_{\text{col}}\ $ for any VP $\ \text{v}\,$ and CP Prey $\,a$ | |
| <font color=white>. | |
| [^alpha-attackchance]: <sub>$\,\alpha$ is a random value within $\,[0,1)\,$ and $\,\text{VpAttackChance}$ is a context parameter defined at initialisation within $\,[0,1]\,$. | |
| <font color=white>. | |
| [^action-disambiguation]: <sub>Note that we use the term *action* as defined by a discrete Model Time step – that is, to denote the activity that an individual agent engages in within the minimum time representation as defined by our Agent–Based Model. | |
| [^paturition-disambiguation]: <sub>Moment of 'biological' delivery of offspring, *i.e.* Birth. | |
| [^vp-sexuality]: <sub>For the sake of simplicity, Visual Predators have no gender, and either party in a successful mating process can be impregnated and carry their offspring to term. | |
| [^agent-actions]:<sub>Via the processing of detailed logging. This allows for the possibility to entirely reconstruct and represent the modelling as an animation in any arbitrary fashion based on the log files. | |
| [^golang.org - history]: <sub> https://golang.org/doc/faq#history | |
| [^golang.org -ancestors]: <sub> https://golang.org/doc/faq#ancestors | |
| [^Hoare]: <sub> Hoare, C. A. R.(Charles Anthony Richard) (1985). *Communicating Sequential Processes*. Englewood Cliffs, N.J. : Prentice/Hall International | |
| [^frontend-mistake]: <sub>The GUI was written in HTML, CSS, and Javascript – specifically, the [**P5js**](http://p5js.org/) library was chosen for its simplicity and emphasis on 2D drawing, in order to render the visualisation viewport inside the browser window. | |
| [^AnMiDutta-MoscatoVodovotz2009]: <sub>An G., Mi Q., Dutta-Moscato J., Vodovotz Y. (2009). Agent-based models in translational systems biology. *Systems Biology and Medicine*, [1(September/October), 159-171](https://dx.doi.org/10.1002/wsbm.45).</sub> | |
| [^Jansen1971]:<sub> Jansen KP. 1971. Ecological studies on intertidal New Zealand Sphaeromatidae (Isopoda-Flabellifera). *Marine Biology* 11, 262-285.</sub> | |
| [^Merilaita2001]: <sub>Merilaita S. 2001. Habitat heterogeneity, predation and gene flow: Colour polymorphism in the isopod, *Idotea baltica*. *Evolutionary Ecology* 15, 103-116.</sub> | |
| [^JormalainenMerilaitaTuomi1995]: <sub>Jormalainen V, Merilaita S. & Tuomi J. 1995. Differential predation on sexes affects colour polymorphism of the isopod *Idotea baltica* (Pallas). *Biological Journal of the Linnean Society* 55, 45-68.</sub> | |
| [^Moreira1974]: <sub>Moreira PS. 1974. Cryptic protective coloration in *Serolis laevis* and *Serolis polaris* (Isopoda: Flabellifera). *Crustaceana (Leiden)* 27, 1-4.</sub> | |
| [^StevensMerilaita2009]: <sub>Stevens M & Merilaita S. 2009. Animal camouflage: current issues and new perspectives. *Philosophical Transactions of the Royal Society B: Biological Sciences* 364, 423-427.</sub> | |
| [^Jansen1968]: <sub>Jansen, KP. 1968. *A Comparative Study of Intertidal Species of Sphaeromidae (Isopoda Flabellifera)*. PhD Thesis: University of Canterbury, New Zealand.</sub> | |
| [^Bond2007]: <sub>Bond AB. 2007. The Evolution of Color Polymorphism: Crypticity, Searching Images, and Apostatic Selection. *Annual Review of Ecology, Evolution and Systematics* 38, 489-514.</sub> | |
| [^FaralloForstner2012]: <sub>Farallo VR & Forstner MRJ. 2012. Predation and the maintenance of color polymorphism in a habitat specialist squamate. *PLoS ONE* 7, e30316.</sub> | |
| [^WhiteleyOwenSmith1997]: <sub>Whiteley DA, Owen DF & Smith DA. 1997. Massive polymorphism and natural selection in *Donacilla cornea* (Poli, 1791) (Bivalve: Mesodesmatidae). *Biological Journal of the Linnean Society* 62, 475-494.</sub> | |
| [^Dale2006]: <sub>Dale J. 2006. Intraspecific variation in coloration. In *Bird Coloration, Vol 2: Function and Evolution* (eds. Hill GE & McGraw KJ). Harvard University Press: Cambridge, MA. 36-86.</sub> | |
| [^Endler1998]: <sub>Endler JA. 1988. Frequency-dependent predation, crypsis and aposematic coloration. *Philosophical Transactions of the Royal Society of London Series B: Biological Sciences* 319, 505-523.</sub> | |
| [^DaleLankReeve2001]: <sub>Dale J, Lank DB & Reeve HK. Signaling individual identity versus quality: A model and case studies with ruffs, queleas, and house finches. *American Naturalist* 158, 75-86 (2001).</sub> | |
| [^GrayMcKinnon2007]: <sub>Gray SM & McKinnon JS. 2007. Linking color polymorphism maintenance and speciation. *Trends in Ecology & Evolution* 22, 71-79.</sub> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment