├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── environment.yml ├── examples ├── README.md ├── ipynb │ ├── 02 │ │ └── main.ipynb │ ├── 03 │ │ └── main.ipynb │ ├── 04 │ │ └── main.ipynb │ ├── 05 │ │ └── main.ipynb │ ├── 06 │ │ └── main.ipynb │ ├── 07 │ │ └── main.ipynb │ └── 09 │ │ └── main.ipynb └── rmd │ ├── 02 │ └── main.rmd │ └── 03 │ └── main.rmd ├── known.py ├── proposal └── main.md ├── src ├── .DS_Store~c2ba64c... add stock and flow diagram ├── Alon.cls ├── README.md ├── assets │ ├── abm-diagram.tex │ ├── barber-shop-continuous-markov-process │ │ ├── README.md │ │ └── main.tex │ ├── barber-shop │ │ ├── README.md │ │ └── main.tex │ ├── bike-repair-shop.tex │ ├── clashes.tex │ ├── final-tsp-tours-with-R │ │ ├── main.pdf │ │ └── main.py │ ├── final-tsp-tours-with-python │ │ ├── main.pdf │ │ └── main.py │ ├── neighbourhood_search_flow_diagram │ │ ├── README.md │ │ ├── main.pdf │ │ └── main.tex │ ├── paint_LP.tex │ ├── process_based.tex │ ├── python_schelling_0.pdf │ ├── python_schelling_100.pdf │ ├── python_schelling_20.pdf │ ├── rsd-triangle │ │ ├── README.md │ │ └── main.tex │ ├── schelling_R_0.pdf │ ├── schelling_R_100.pdf │ ├── schelling_R_20.pdf │ ├── schelling_happy.tex │ ├── schelling_unhappy.tex │ ├── sd_vaccine_plots │ │ ├── main.py │ │ ├── plot_no_vaccine.pdf │ │ └── plot_with_vaccine.pdf │ ├── stock_flow_diagram.tex │ ├── taxi-firm-game │ │ ├── README.md │ │ ├── main.pdf │ │ └── main.tex │ ├── tsp-effect-of-neighbourhood-operators │ │ ├── README.md │ │ ├── main.pdf │ │ └── main.py │ └── tsp │ │ ├── README.md │ │ ├── main.pdf │ │ ├── main.py │ │ └── main.tex ├── bibliography.bib ├── chapters │ ├── 01 │ │ ├── README.md │ │ └── main.tex │ ├── 02 │ │ └── main.tex │ ├── 03 │ │ └── main.tex │ ├── 04 │ │ └── main.tex │ ├── 05 │ │ └── main.tex │ ├── 06 │ │ └── main.tex │ ├── 07 │ │ └── main.tex │ ├── 08 │ │ └── main.tex │ └── 09 │ │ └── main.tex ├── customenvironments.sty ├── epigrafica.sty ├── footnotes.bib ├── frontmatter │ ├── contributor │ │ └── main.tex │ ├── foreword │ │ └── main.tex │ ├── preamble │ │ └── main.tex │ └── preface │ │ └── main.tex ├── main.pdf └── main.tex └── tasks.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI-ubuntu 2 | 3 | 4 | on: 5 | push: 6 | pull_request: 7 | # Run weakly at 00:00 on Sunday. 8 | schedule: 9 | - cron: '0 0 * * 0' 10 | 11 | # This job installs dependencies, build the site, and pushes it to `gh-pages`` 12 | jobs: 13 | test: 14 | #runs-on: [ubuntu-latest, macOS-latest] # TODO Add check for macOS 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Install Conda 19 | uses: s-weigand/setup-conda@v1 20 | - name: Install environment 21 | run: | 22 | conda config --set always_yes yes --set changeps1 no 23 | conda update -q conda 24 | #Useful for debugging any issues with conda 25 | conda info -a 26 | conda env create -f environment.yml 27 | source activate ampwoss 28 | inv env # Finish install of environment 29 | sudo apt-get install aspell aspell-en #To check spelling 30 | 31 | - name: Doctest 32 | run: | 33 | source activate ampwoss 34 | inv doctest --style 35 | 36 | - name: Install LaTeX 37 | run: | 38 | sudo apt-get update 39 | #LaTeX packages 40 | sudo apt-get install -y texlive-latex-extra 41 | sudo apt-get install -y texlive-xetex 42 | sudo apt-get install texlive-bibtex-extra biber 43 | sudo apt-get install -y texlive-lang-greek 44 | sudo apt-get install -y latexmk 45 | sudo apt-get install lmodern 46 | sudo apt-get install texlive-fonts-extra 47 | - name: Check LaTeX compiles 48 | run: | 49 | source activate ampwoss 50 | cd src; xelatex -interaction=nonstopmode -shell-escape main.tex 51 | - name: Validate notebooks 52 | run: | 53 | source activate ampwoss 54 | python -m pip install nbval pytest 55 | inv validatenbs 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Core latex/pdflatex auxiliary files: 2 | *.aux 3 | *.lof 4 | *.log 5 | *.lot 6 | *.fls 7 | *.out 8 | *.toc 9 | *.fmt 10 | *.fot 11 | *.cb 12 | *.cb2 13 | .*.lb 14 | 15 | ## Intermediate documents: 16 | *.dvi 17 | *.xdv 18 | *-converted-to.* 19 | # these rules might exclude image files for figures etc. 20 | # *.ps 21 | # *.eps 22 | # *.pdf 23 | 24 | ## Generated if empty string is given at "Please type another file name for output:" 25 | .pdf 26 | 27 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 28 | *.bbl 29 | *.bcf 30 | *.blg 31 | *-blx.aux 32 | *-blx.bib 33 | *.run.xml 34 | 35 | ## Build tool auxiliary files: 36 | *.fdb_latexmk 37 | *.synctex 38 | *.synctex(busy) 39 | *.synctex.gz 40 | *.synctex.gz(busy) 41 | *.pdfsync 42 | 43 | ## Auxiliary and intermediate files from other packages: 44 | # algorithms 45 | *.alg 46 | *.loa 47 | 48 | # achemso 49 | acs-*.bib 50 | 51 | # amsthm 52 | *.thm 53 | 54 | # beamer 55 | *.nav 56 | *.pre 57 | *.snm 58 | *.vrb 59 | 60 | # changes 61 | *.soc 62 | 63 | # cprotect 64 | *.cpt 65 | 66 | # elsarticle (documentclass of Elsevier journals) 67 | *.spl 68 | 69 | # endnotes 70 | *.ent 71 | 72 | # fixme 73 | *.lox 74 | 75 | # feynmf/feynmp 76 | *.mf 77 | *.mp 78 | *.t[1-9] 79 | *.t[1-9][0-9] 80 | *.tfm 81 | 82 | #(r)(e)ledmac/(r)(e)ledpar 83 | *.end 84 | *.?end 85 | *.[1-9] 86 | *.[1-9][0-9] 87 | *.[1-9][0-9][0-9] 88 | *.[1-9]R 89 | *.[1-9][0-9]R 90 | *.[1-9][0-9][0-9]R 91 | *.eledsec[1-9] 92 | *.eledsec[1-9]R 93 | *.eledsec[1-9][0-9] 94 | *.eledsec[1-9][0-9]R 95 | *.eledsec[1-9][0-9][0-9] 96 | *.eledsec[1-9][0-9][0-9]R 97 | 98 | # glossaries 99 | *.acn 100 | *.acr 101 | *.glg 102 | *.glo 103 | *.gls 104 | *.glsdefs 105 | 106 | # gnuplottex 107 | *-gnuplottex-* 108 | 109 | # gregoriotex 110 | *.gaux 111 | *.gtex 112 | 113 | # htlatex 114 | *.4ct 115 | *.4tc 116 | *.idv 117 | *.lg 118 | *.trc 119 | *.xref 120 | 121 | # hyperref 122 | *.brf 123 | 124 | # knitr 125 | *-concordance.tex 126 | # TODO Comment the next line if you want to keep your tikz graphics files 127 | *.tikz 128 | *-tikzDictionary 129 | 130 | # listings 131 | *.lol 132 | 133 | # makeidx 134 | *.idx 135 | *.ilg 136 | *.ind 137 | *.ist 138 | 139 | # minitoc 140 | *.maf 141 | *.mlf 142 | *.mlt 143 | *.mtc[0-9]* 144 | *.slf[0-9]* 145 | *.slt[0-9]* 146 | *.stc[0-9]* 147 | 148 | # minted 149 | _minted* 150 | *.pyg 151 | 152 | # morewrites 153 | *.mw 154 | 155 | # nomencl 156 | *.nlg 157 | *.nlo 158 | *.nls 159 | 160 | # pax 161 | *.pax 162 | 163 | # pdfpcnotes 164 | *.pdfpc 165 | 166 | # sagetex 167 | *.sagetex.sage 168 | *.sagetex.py 169 | *.sagetex.scmd 170 | 171 | # scrwfile 172 | *.wrt 173 | 174 | # sympy 175 | *.sout 176 | *.sympy 177 | sympy-plots-for-*.tex/ 178 | 179 | # pdfcomment 180 | *.upa 181 | *.upb 182 | 183 | # pythontex 184 | *.pytxcode 185 | pythontex-files-*/ 186 | 187 | # thmtools 188 | *.loe 189 | 190 | # TikZ & PGF 191 | *.dpth 192 | *.md5 193 | *.auxlock 194 | 195 | # todonotes 196 | *.tdo 197 | 198 | # easy-todo 199 | *.lod 200 | 201 | # xmpincl 202 | *.xmpi 203 | 204 | # xindy 205 | *.xdy 206 | 207 | # xypic precompiled matrices 208 | *.xyc 209 | 210 | # endfloat 211 | *.ttt 212 | *.fff 213 | 214 | # Latexian 215 | TSWLatexianTemp* 216 | 217 | ## Editors: 218 | # WinEdt 219 | *.bak 220 | *.sav 221 | 222 | # Texpad 223 | .texpadtmp 224 | 225 | # Kile 226 | *.backup 227 | 228 | # KBibTeX 229 | *~[0-9]* 230 | 231 | # auto folder when using emacs and auctex 232 | ./auto/* 233 | *.el 234 | 235 | # expex forward references with \gathertags 236 | *-tags.tex 237 | 238 | # standalone packages 239 | *.sta 240 | 241 | # generated if using elsarticle.cls 242 | *.spl 243 | 244 | # minted output 245 | *.pygmentize 246 | 247 | # DS_Store 248 | *.DS_Store 249 | 250 | build/* 251 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Vince Knight 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ampwoss 2 | 3 | Applied mathematics problems with Open Source Software: Operational Research 4 | with Python and R. 5 | 6 | ## Back Cover 7 | 8 | **Applied Mathematics with Open-Source Software: Operational Research Problems 9 | with Python and R** is aimed at a broad segment of readers who wish to learn how 10 | to use open-source software to solve problems in applied mathematics. 11 | The book has an innovative structure with 4 sections of two chapters covering a 12 | large range of applied mathematical techniques: probabilistic modelling, 13 | dynamical systems, emergent behaviour and optimisation. The pairs of chapters in 14 | each section demonstrate different families of solution approaches. Each chapter 15 | starts with a problem, gives an overview of the relevant theory, shows a 16 | solution approach in R and in Python, and finally gives wider context by 17 | including a number of published references. This structure will allow for maximum 18 | accessibility, with minimal prerequisites in mathematics or programming as well 19 | as giving the right opportunities for a reader wanting to delve deeper in to a 20 | particular topic. 21 | 22 | Features 23 | 24 | - An excellent resource for scholars of applied mathematics and operational 25 | research, and indeed any academics who want to learn how to use open-source 26 | software. 27 | - Offers more general and accessible treatment of the subject than other texts, 28 | both in terms of programming language but also in terms of the subjects 29 | considered. 30 | - The R and Python sections purposefully mirror each other so that a reader can 31 | read only the section that interests them. 32 | - An accompanying open source repository with source files and further 33 | examples. 34 | 35 | ## List of chapters 36 | 37 | ### Chapter 1: Introduction 38 | 39 | This chapter aims to help a reader understand if this book is for them and what 40 | background they need to use it. It starts by answering the following questions: 41 | 42 | - Who is this book for? 43 | - What is meant by applied mathematics? 44 | - What is open source software? 45 | 46 | The chapter structure of the book is then discussed, apart from the 1st chapter 47 | all chapters have a similar structure and then some discussion is given to how 48 | the code is written and presented. 49 | 50 | This chapter can be skipped if a reader is confident in their use of programming 51 | and wants to skip ahead to a specific problem. 52 | 53 | ### Chapter 2: Markov chains 54 | 55 | This chapter models a barber shop as a queueing system using a continuous time 56 | Markov chain. The goal is to identify which particular reconfiguration of the 57 | barber shop would be most beneficial to customers. 58 | 59 | A theoretic overview of concepts related to stationary distributions of 60 | continuous Markov chains is given before solutions approaches are given in both 61 | R and Python. This is done using Numpy in Python and expm in R for all the 62 | underlying linear algebra. 63 | 64 | - [Jupyter notebook example](./examples/ipynb/02/main.ipynb) 65 | 66 | ### Chapter 3: Discrete event simulation 67 | 68 | This chapter models a bicycle repair shop as a network of two queueing 69 | processes. The goal is to identify the effect of two particular improvements to 70 | the service configuration. 71 | 72 | A theoretic overview of two types of discrete event simulation frameworks is 73 | given: event scheduling and process based simulation. As well as this the topics 74 | of random processes and pseudorandom number generation are also covered. 75 | 76 | The two types of simulation frameworks are addressed in the solution approaches: 77 | in Python Ciw is used which is an implementation of event scheduling. In R 78 | simmer is used which is an implementation of process based simulation. 79 | As a result, perhaps this chapter more than any other is worthwhile reading both 80 | the R and Python section (even if the reader is only a user of one language) as 81 | this gives a good contrast to the two frameworks. 82 | 83 | ### Chapter 4: Differential equations 84 | 85 | This chapter models the spread of a cold in a population as a single dimensional 86 | problem. The goal is to identify whether or not an investment in cold medicine 87 | should be made to offset a loss of productivity. 88 | 89 | A theoretic overview of what a differential equation is and what a solution of a 90 | differential equation represents is given. 91 | 92 | The solution offered in Python is done symbolically using the Sympy library 93 | whereas the solution offered in R is done through numerical integration using 94 | deSolve. 95 | 96 | ### Chapter 5: System dynamics 97 | 98 | This chapter models the spread of an infectious disease within a population as a 99 | compartmental model. The goal is to understand what interventions could be made 100 | to help minimise the long term effects of the disease. 101 | 102 | A theoretic overview of systems of ordinary differential equations is presented 103 | in terms of stocks and flows. The specific model used here: an SIR model is 104 | discussed in detail. This is all complemented with a brief discussion of some 105 | numerical techniques used to integrate systems of ordinary 106 | differential equations. 107 | 108 | The solution offered in Python makes use of Numpy and Scipy which is equivalent 109 | to the solution offered in R which makes use of deSolve. 110 | 111 | ## Chapter 6: Game theory 112 | 113 | This chapters models a policy decision that has implications on the actions of 114 | two companies in a duopoly modelled as a normal form game. The goal is to 115 | understand what financial incentives can be put in place to ensure competition 116 | between the companies that benefits the consumer. 117 | 118 | A theoretic overview of the definition of normal form games and Nash equilibria 119 | is given. 120 | 121 | The solution offered in Python makes use of Nashpy which is a specific library 122 | for the computation of Nash equilibria. The solution offered in R makes use of 123 | Recon which is a general purpose library with functionality for a few topics in 124 | economics. 125 | 126 | ## Chapter 7: Agent Based Simulation 127 | 128 | This chapters models a population of association football fans as a classic 129 | agent based model of segregation. The goal is to understand how the individual 130 | preferences of the population members affects the overall observed behaviour at 131 | the population level. 132 | 133 | A theoretic overview of agent based modelling is given specifically 134 | concentrating on the roles of agents and the environment. 135 | 136 | In Python the fully object oriented nature of the language is used to model 137 | agents and the environment as classes. The R solution makes use of the R6 library which allows 138 | for a similar implementation of classes with required functionality. 139 | 140 | ## Chapter 8: Linear Programming 141 | 142 | This chapter finds an exam schedule for a scenario where there are students that 143 | have a number of possible clashes. This is done by formulating and solving a 144 | linear programming problem. 145 | 146 | A theoretic description of linear programming is given. Intuitive ideas that lead 147 | to solutions approaches are described. Direct and linear algebraic formulations 148 | are explained. 149 | 150 | The Python solution makes use of the Pulp library which corresponds to the 151 | direct formulation of the problem. The R solution uses the ROI library which 152 | requires the linear algebraic formulation. 153 | 154 | ## Chapter 9: Heuristics 155 | 156 | This chapter identifies the best delivery route for a delivery company. This is 157 | a traditional travelling salesman problem which aims to find a route that minimises 158 | the total distance travelled. 159 | 160 | A theoretic description of neighbourhood search and a specific 161 | consideration of the "2-opt" algorithm are given. These are types 162 | of heuristic algorithms which may not 163 | guarantee optimality but can often perform better in practice than exact 164 | approaches. 165 | 166 | The standard Python library and base R are used to build these algorithms. 167 | 168 | # Biographies 169 | 170 | Geraint Palmer is a Welsh Medium Lecturer at Cardiff University in the School of 171 | Mathematics. He is a member of the operational research group where his research 172 | interests are in simulation and probabilistic modelling, in particular applying 173 | these to model public services such as healthcare systems. He uses open source 174 | software in all aspects of his research: he is a maintainer of Ciw, an open source 175 | Python library for discrete event simulation, and won the OR Society's Doctoral 176 | Award in 2018. Geraint is also a fellow of the Software Sustainability Institute 177 | and has presented at a number of international conferences on the subject of 178 | best practice of scientific computing, and regularly teaches programming and 179 | runs coding workshops for people of all ages. 180 | 181 | Vince Knight is a Senior Lecturer at Cardiff University in the School of 182 | Mathematics. His research interests are in emergent behaviour, probabilistic 183 | modelling, applications in healthcare and pedagogy. He maintains a number of 184 | open source research software projects, is a trustee of the UK Python 185 | association, is an editor for the Journal of Open Source Software, was awarded 186 | the 2017 John Pinner award for contribution to the Python community and is a 187 | fellow of the Sustainable Software Institute. He regularly wins awards for his 188 | teaching in the School of Mathematics. He does not only speak at conferences 189 | around the world but continues to organise conferences to bring the 190 | power of open source software to as many people as possible. 191 | 192 | # Source fils 193 | 194 | ## Create the Conda environment: 195 | 196 | Assuming you have anaconda on your machine, run: 197 | 198 | $ conda env create -f environment.yml 199 | 200 | There are some further dependencies that are needed. 201 | 202 | Activate the environment: 203 | 204 | $ conda activate ampwoss 205 | $ inv env 206 | 207 | To delete the environment: 208 | 209 | $ inv delenv 210 | 211 | ## Compilation instructions 212 | 213 | Run: 214 | 215 | $ inv compile 216 | 217 | This creates a `build/` directory which contains slightly modified source files 218 | (some annotations removed) as well as a `main.pdf`. This modifications are 219 | prescribed by a `substitions` variables in `tasks.py`. 220 | 221 | An intermediate step is to run: 222 | 223 | $ inv build 224 | 225 | Which creates the modified source files but does not compile the document. 226 | 227 | Note that as documented in 228 | [#72](https://github.com/drvinceknight/amwoss/issues/142) the 229 | `texlive-langgreek` latex package might be needed. 230 | 231 | ## Doctesting 232 | 233 | Run: 234 | 235 | $ inv doctest 236 | 237 | To doctest a specific chapter: 238 | 239 | $ inv doctest --path="src/chapters/06/main.tex" 240 | 241 | To also check the style with `black` on the python code and `lintr` on 242 | the R code: 243 | 244 | $ inv doctest --style 245 | 246 | ## Analysing the document 247 | 248 | Run: 249 | 250 | $ inv analyse 251 | 252 | This analyses the tex documents with `style` and `diction`: 253 | 254 | - `style`: analyses surface characteristics of a document, including sentence 255 | length and other readability measures. 256 | - `diction`: identifies wordy and commonly misused phrases. 257 | 258 | See 259 | http://wiki.christophchamp.com/index.php?title=Style_and_Diction#Lix 260 | for more information. 261 | 262 | To install these packages: 263 | 264 | Debian: 265 | 266 | apt install diction 267 | apt install style 268 | 269 | OS X (with the brew package manager): 270 | 271 | brew install diction 272 | brew install style 273 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: ampwoss 2 | channels: 3 | - defaults 4 | dependencies: 5 | - numpy=1.20.3 6 | - pandas=1.3.3 7 | - pip=21.2.2 8 | - scipy=1.7.1 9 | - sympy=1.8 10 | - matplotlib=3.5.0 11 | - r-desolve=1.24 12 | - r-essentials=3.6.0 13 | - r-lintr=1.0.3 14 | - r-pracma=2.2.5 15 | - r-rootsolve=1.7 16 | - pip: 17 | - click==8.0.2 # Possibly solves an upstream issue with click + black 18 | - black==21.9b0 19 | - ciw==2.2.0 20 | - docformatter==1.4 21 | - dwys==0.0.4 22 | - invoke==1.6.0 23 | - nashpy==0.0.25 24 | - pulp==2.5.1 25 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Accompanying example files 2 | 3 | This directory contains examples files: 4 | 5 | - [Jupyter notebooks](./ipynb) 6 | - [Markov Chains](./ipynb/02/main.ipynb) 7 | - [Discrete Event Simulation](./ipynb/03/main.ipynb) 8 | - [Differential Equations](./ipynb/04/main.ipynb) 9 | - [Systems Dynamics](./ipynb/05/main.ipynb) 10 | - [Game Theory](./ipynb/06/main.ipynb) 11 | - [Agent-Based Simulation](./ipynb/07/main.ipynb) 12 | - [Heuristics](./ipynb/09/main.ipynb) 13 | - [Rmd files](./rmd) 14 | - [Markov Chains](./rmd/02/main.ipynb) 15 | - [Discrete Event Simulation](./rmd/03/main.ipynb) 16 | -------------------------------------------------------------------------------- /examples/ipynb/02/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Markov chains\n", 8 | "\n", 9 | "This notebook contains code examples referring to the Markov chains chapter of \"Applied Mathematics with Open-Source Software: Operational Research Problems\n", 10 | "with Python and R\"." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First we define a function to get the transition rates between two given states:" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "def get_transition_rate( in_state,\n", 27 | " out_state,\n", 28 | " waiting_room=4,\n", 29 | " num_barbers=2,\n", 30 | "):\n", 31 | " \"\"\"Return the transition rate for 2 given states.\n", 32 | " Args:\n", 33 | " in_state: an integer\n", 34 | " out_state: an integer\n", 35 | " waiting_room: an integer (default: 4)\n", 36 | " num_barbers: an integer (default: 2)\n", 37 | " Returns:\n", 38 | " A real.\n", 39 | " \"\"\"\n", 40 | " arrival_rate = 10\n", 41 | " service_rate = 4\n", 42 | " capacity = waiting_room + num_barbers\n", 43 | " delta = out_state - in_state\n", 44 | " if delta == 1 and in_state < capacity: \n", 45 | " return arrival_rate\n", 46 | " if delta == -1:\n", 47 | " return min(in_state, num_barbers) * service_rate\n", 48 | " return 0" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "This can be used to create the transition matrix:" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 5, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "import itertools\n", 65 | "import numpy as np\n", 66 | "\n", 67 | "\n", 68 | "def get_transition_rate_matrix(waiting_room=4, num_barbers=2):\n", 69 | " \"\"\"Return the transition matrix Q.\n", 70 | "\n", 71 | " Args:\n", 72 | " waiting_room: an integer (default: 4)\n", 73 | " num_barbers: an integer (default: 2)\n", 74 | "\n", 75 | " Returns:\n", 76 | " A matrix.\n", 77 | " \"\"\"\n", 78 | " capacity = waiting_room + num_barbers\n", 79 | " state_pairs = itertools.product(range(capacity + 1), repeat=2)\n", 80 | " flat_transition_rates = [\n", 81 | " get_transition_rate(\n", 82 | " in_state=in_state,\n", 83 | " out_state=out_state,\n", 84 | " waiting_room=waiting_room,\n", 85 | " num_barbers=num_barbers,\n", 86 | " )\n", 87 | " for in_state, out_state in state_pairs\n", 88 | " ]\n", 89 | " transition_rates = np.reshape(\n", 90 | " flat_transition_rates, (capacity + 1, capacity + 1)\n", 91 | " )\n", 92 | " np.fill_diagonal(\n", 93 | " transition_rates, -transition_rates.sum(axis=1)\n", 94 | " )\n", 95 | "\n", 96 | " return transition_rates" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "For the problem from the chapter this is:" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 7, 109 | "metadata": {}, 110 | "outputs": [ 111 | { 112 | "name": "stdout", 113 | "output_type": "stream", 114 | "text": [ 115 | "[[-10 10 0 0 0 0 0]\n", 116 | " [ 4 -14 10 0 0 0 0]\n", 117 | " [ 0 8 -18 10 0 0 0]\n", 118 | " [ 0 0 8 -18 10 0 0]\n", 119 | " [ 0 0 0 8 -18 10 0]\n", 120 | " [ 0 0 0 0 8 -18 10]\n", 121 | " [ 0 0 0 0 0 8 -8]]\n" 122 | ] 123 | } 124 | ], 125 | "source": [ 126 | "Q = get_transition_rate_matrix()\n", 127 | "print(Q)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "Here is the state of the system after 0.5 time units:" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 8, 140 | "metadata": {}, 141 | "outputs": [ 142 | { 143 | "name": "stdout", 144 | "output_type": "stream", 145 | "text": [ 146 | "[[0.10492 0.21254 0.20377 0.17142 0.13021 0.09564 0.0815 ]\n", 147 | " [0.08501 0.18292 0.18666 0.1708 0.14377 0.1189 0.11194]\n", 148 | " [0.06521 0.14933 0.16338 0.16478 0.15633 0.14751 0.15346]\n", 149 | " [0.04388 0.10931 0.13183 0.15181 0.16777 0.18398 0.21142]\n", 150 | " [0.02667 0.07361 0.10005 0.13422 0.17393 0.2189 0.27262]\n", 151 | " [0.01567 0.0487 0.07552 0.11775 0.17512 0.24484 0.32239]\n", 152 | " [0.01068 0.03668 0.06286 0.10824 0.17448 0.25791 0.34914]]\n" 153 | ] 154 | } 155 | ], 156 | "source": [ 157 | "import scipy.linalg\n", 158 | "\n", 159 | "print(scipy.linalg.expm(Q * 0.5).round(5))" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "After 500 time units:" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 9, 172 | "metadata": {}, 173 | "outputs": [ 174 | { 175 | "name": "stdout", 176 | "output_type": "stream", 177 | "text": [ 178 | "[[0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n", 179 | " [0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n", 180 | " [0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n", 181 | " [0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n", 182 | " [0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n", 183 | " [0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n", 184 | " [0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]]\n" 185 | ] 186 | } 187 | ], 188 | "source": [ 189 | "print(scipy.linalg.expm(Q * 500).round(5))" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "We can obtain the steady state:" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 11, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "def get_steady_state_vector(Q):\n", 206 | " \"\"\"Return the steady state vector of any given continuous time\n", 207 | " transition rate matrix.\n", 208 | "\n", 209 | " Args:\n", 210 | " Q: a transition rate matrix\n", 211 | "\n", 212 | " Returns:\n", 213 | " A vector\n", 214 | " \"\"\"\n", 215 | " state_space_size, _ = Q.shape\n", 216 | " A = np.vstack((Q.T, np.ones(state_space_size)))\n", 217 | " b = np.append(np.zeros(state_space_size), 1)\n", 218 | " x, _, _, _ = np.linalg.lstsq(A, b, rcond=None)\n", 219 | " return x" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 12, 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "name": "stdout", 229 | "output_type": "stream", 230 | "text": [ 231 | "[0.03431 0.08577 0.10722 0.13402 0.16752 0.2094 0.26176]\n" 232 | ] 233 | } 234 | ], 235 | "source": [ 236 | "print(get_steady_state_vector(Q).round(5))" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": {}, 242 | "source": [ 243 | "The probability of a full shop" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 14, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [ 252 | "def get_probability_of_full_shop(waiting_room=4, num_barbers=2):\n", 253 | " \"\"\"Return the probability of the barber shop being full.\n", 254 | "\n", 255 | " Args:\n", 256 | " waiting_room: an integer (default: 4)\n", 257 | " num_barbers: an integer (default: 2)\n", 258 | "\n", 259 | " Returns:\n", 260 | " A real.\n", 261 | " \"\"\"\n", 262 | " Q = get_transition_rate_matrix(\n", 263 | " waiting_room=waiting_room,\n", 264 | " num_barbers=num_barbers,\n", 265 | " )\n", 266 | " pi = get_steady_state_vector(Q)\n", 267 | " return pi[-1]" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 15, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "name": "stdout", 277 | "output_type": "stream", 278 | "text": [ 279 | "0.261756\n" 280 | ] 281 | } 282 | ], 283 | "source": [ 284 | "print(round(get_probability_of_full_shop(), 6))" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": 16, 290 | "metadata": {}, 291 | "outputs": [ 292 | { 293 | "name": "stdout", 294 | "output_type": "stream", 295 | "text": [ 296 | "0.23557\n" 297 | ] 298 | } 299 | ], 300 | "source": [ 301 | "print(round(get_probability_of_full_shop(waiting_room=6), 6))" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 17, 307 | "metadata": {}, 308 | "outputs": [ 309 | { 310 | "name": "stdout", 311 | "output_type": "stream", 312 | "text": [ 313 | "0.078636\n" 314 | ] 315 | } 316 | ], 317 | "source": [ 318 | "print(round(get_probability_of_full_shop(num_barbers=3), 6))" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [] 327 | } 328 | ], 329 | "metadata": { 330 | "kernelspec": { 331 | "display_name": "Python 3", 332 | "language": "python", 333 | "name": "python3" 334 | }, 335 | "language_info": { 336 | "codemirror_mode": { 337 | "name": "ipython", 338 | "version": 3 339 | }, 340 | "file_extension": ".py", 341 | "mimetype": "text/x-python", 342 | "name": "python", 343 | "nbconvert_exporter": "python", 344 | "pygments_lexer": "ipython3", 345 | "version": "3.8.3" 346 | } 347 | }, 348 | "nbformat": 4, 349 | "nbformat_minor": 4 350 | } 351 | -------------------------------------------------------------------------------- /examples/ipynb/03/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Discrete Event Simulation\n", 8 | "\n", 9 | "This notebook contains code examples referring to the Discrete Event Simulation chapter of \"Applied Mathematics with Open-Source Software: Operational Research Problems\n", 10 | "with Python and R\"." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First we define a function that gives a Network object, containing the structure of the repair shop." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import ciw\n", 27 | "\n", 28 | "\n", 29 | "def build_network_object(\n", 30 | " num_inspectors=1,\n", 31 | " num_repairers=2,\n", 32 | "):\n", 33 | " \"\"\"Returns a Network object that defines the repair shop.\n", 34 | "\n", 35 | " Args:\n", 36 | " num_inspectors: a positive integer (default: 1)\n", 37 | " num_repairers: a positive integer (default: 2)\n", 38 | "\n", 39 | " Returns:\n", 40 | " a Ciw network object\n", 41 | " \"\"\"\n", 42 | " arrival_rate = 15\n", 43 | " inspection_rate = 20\n", 44 | " repair_rate = 10\n", 45 | " prob_need_repair = 0.8\n", 46 | " N = ciw.create_network(\n", 47 | " arrival_distributions=[\n", 48 | " ciw.dists.Exponential(arrival_rate),\n", 49 | " ciw.dists.NoArrivals(),\n", 50 | " ],\n", 51 | " service_distributions=[\n", 52 | " ciw.dists.Exponential(inspection_rate),\n", 53 | " ciw.dists.Exponential(repair_rate),\n", 54 | " ],\n", 55 | " number_of_servers=[num_inspectors, num_repairers],\n", 56 | " routing=[[0.0, prob_need_repair], [0.0, 0.0]],\n", 57 | " )\n", 58 | " return N" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "We can see information such as number of nodes in the network:" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 2, 71 | "metadata": {}, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | "2\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "N = build_network_object()\n", 83 | "print(N.number_of_nodes)" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "Then we define a function that runs one trial of the simulation." 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 3, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "def run_simulation(network, seed=0):\n", 100 | " \"\"\"Builds a simulation object and runs it for 8 time units.\n", 101 | "\n", 102 | " Args:\n", 103 | " network: a Ciw network object\n", 104 | " seed: a float (default: 0)\n", 105 | "\n", 106 | " Returns:\n", 107 | " a Ciw simulation object after a run of the simulation\n", 108 | " \"\"\"\n", 109 | " max_time = 8\n", 110 | " ciw.seed(seed)\n", 111 | " Q = ciw.Simulation(network)\n", 112 | " Q.simulate_until_max_time(max_time)\n", 113 | " return Q" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "From one trial we can obtain the proportion of bicycles taking over half an hour:" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 4, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "import pandas as pd\n", 130 | "\n", 131 | "\n", 132 | "def get_proportion(Q):\n", 133 | " \"\"\"Returns the proportion of bicycles spending over a given\n", 134 | " limit at the repair shop.\n", 135 | "\n", 136 | " Args:\n", 137 | " Q: a Ciw simulation object after a run of the\n", 138 | " simulation\n", 139 | "\n", 140 | " Returns:\n", 141 | " a real\n", 142 | " \"\"\"\n", 143 | " limit = 0.5\n", 144 | " inds = Q.nodes[-1].all_individuals\n", 145 | " recs = pd.DataFrame(\n", 146 | " dr for ind in inds for dr in ind.data_records\n", 147 | " )\n", 148 | " recs[\"total_time\"] = recs[\"exit_date\"] - recs[\"arrival_date\"]\n", 149 | " total_times = recs.groupby(\"id_number\")[\"total_time\"].sum()\n", 150 | " return (total_times > limit).mean()" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "Putting all this together for one trial" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 5, 163 | "metadata": {}, 164 | "outputs": [ 165 | { 166 | "name": "stdout", 167 | "output_type": "stream", 168 | "text": [ 169 | "0.261261\n" 170 | ] 171 | } 172 | ], 173 | "source": [ 174 | "N = build_network_object()\n", 175 | "Q = run_simulation(N)\n", 176 | "p = get_proportion(Q)\n", 177 | "print(round(p, 6))" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "A function to find the average proportion over a number of trials" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 6, 190 | "metadata": {}, 191 | "outputs": [], 192 | "source": [ 193 | "def get_average_proportion(num_inspectors=1, num_repairers=2):\n", 194 | " \"\"\"Returns the average proportion of bicycles spending over a\n", 195 | " given limit at the repair shop.\n", 196 | "\n", 197 | " Args:\n", 198 | " num_inspectors: a positive integer (default: 1)\n", 199 | " num_repairers: a positive integer (default: 2)\n", 200 | "\n", 201 | " Returns:\n", 202 | " a real\n", 203 | " \"\"\"\n", 204 | " num_trials = 100\n", 205 | " N = build_network_object(\n", 206 | " num_inspectors=num_inspectors,\n", 207 | " num_repairers=num_repairers,\n", 208 | " )\n", 209 | " proportions = []\n", 210 | " for trial in range(num_trials):\n", 211 | " Q = run_simulation(N, seed=trial)\n", 212 | " proportion = get_proportion(Q=Q)\n", 213 | " proportions.append(proportion)\n", 214 | " return sum(proportions) / num_trials\n" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "The proportion with current staff:" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 7, 227 | "metadata": {}, 228 | "outputs": [ 229 | { 230 | "name": "stdout", 231 | "output_type": "stream", 232 | "text": [ 233 | "0.159354\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "p = get_average_proportion(num_inspectors=1, num_repairers=2)\n", 239 | "print(round(p, 6))" 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "metadata": {}, 245 | "source": [ 246 | "The proportion with an extra inspector:" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 8, 252 | "metadata": {}, 253 | "outputs": [ 254 | { 255 | "name": "stdout", 256 | "output_type": "stream", 257 | "text": [ 258 | "0.038477\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "p = get_average_proportion(num_inspectors=2, num_repairers=2)\n", 264 | "print(round(p, 6))" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "The proportion with an extra repairer:" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 9, 277 | "metadata": {}, 278 | "outputs": [ 279 | { 280 | "name": "stdout", 281 | "output_type": "stream", 282 | "text": [ 283 | "0.103591\n" 284 | ] 285 | } 286 | ], 287 | "source": [ 288 | "p = get_average_proportion(num_inspectors=1, num_repairers=3)\n", 289 | "print(round(p, 6))" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [] 298 | } 299 | ], 300 | "metadata": { 301 | "kernelspec": { 302 | "display_name": "Python 3", 303 | "language": "python", 304 | "name": "python3" 305 | }, 306 | "language_info": { 307 | "codemirror_mode": { 308 | "name": "ipython", 309 | "version": 3 310 | }, 311 | "file_extension": ".py", 312 | "mimetype": "text/x-python", 313 | "name": "python", 314 | "nbconvert_exporter": "python", 315 | "pygments_lexer": "ipython3", 316 | "version": "3.7.3" 317 | } 318 | }, 319 | "nbformat": 4, 320 | "nbformat_minor": 2 321 | } 322 | -------------------------------------------------------------------------------- /examples/ipynb/04/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Differential Equations\n", 8 | "\n", 9 | "This notebook contains code examples referring to the Differential Equations chapter of \"Applied Mathematics with Open-Source Software: Operational Research Problems\n", 10 | "with Python and R\"." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First define some symbolic variables." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import sympy as sym\n", 27 | "\n", 28 | "t = sym.Symbol(\"t\")\n", 29 | "alpha = sym.Symbol(\"alpha\")\n", 30 | "I_0 = sym.Symbol(\"I_0\")\n", 31 | "I = sym.Function(\"I\")" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "A function to define the differential equation in the chapter." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 2, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "def get_equation(alpha=alpha):\n", 48 | " \"\"\"Return the differential equation.\n", 49 | "\n", 50 | " Args:\n", 51 | " alpha: a float (default: symbolic alpha)\n", 52 | "\n", 53 | " Returns:\n", 54 | " A symbolic equation\n", 55 | " \"\"\"\n", 56 | " return sym.Eq(sym.Derivative(I(t), t), -alpha * I(t))" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "Giving" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 3, 69 | "metadata": {}, 70 | "outputs": [ 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": [ 75 | "Eq(Derivative(I(t), t), -alpha*I(t))\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "eq = get_equation()\n", 81 | "print(eq)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Passing in a value for $\\alpha$" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 4, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "name": "stdout", 98 | "output_type": "stream", 99 | "text": [ 100 | "Eq(Derivative(I(t), t), -I(t))\n" 101 | ] 102 | } 103 | ], 104 | "source": [ 105 | "eq = get_equation(alpha=1)\n", 106 | "print(eq)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "Define a function that gives a solution to that differential equation." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "def get_solution(I_0=I_0, alpha=alpha):\n", 123 | " \"\"\"Return the solution to the differential equation.\n", 124 | "\n", 125 | " Args:\n", 126 | " I_0: a float (default: symbolic I_0)\n", 127 | " alpha: a float (default: symbolic alpha)\n", 128 | "\n", 129 | " Returns:\n", 130 | " A symbolic equation\n", 131 | " \"\"\"\n", 132 | " eq = get_equation(alpha=alpha)\n", 133 | " return sym.dsolve(eq, I(t), ics={I(0): I_0})" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "Giving" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 6, 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "Eq(I(t), I_0*exp(-alpha*t))\n" 153 | ] 154 | } 155 | ], 156 | "source": [ 157 | "sol = get_solution()\n", 158 | "print(sol)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "Verifying the result." 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 7, 171 | "metadata": {}, 172 | "outputs": [ 173 | { 174 | "name": "stdout", 175 | "output_type": "stream", 176 | "text": [ 177 | "True\n" 178 | ] 179 | } 180 | ], 181 | "source": [ 182 | "print(sym.diff(sol.rhs, t) == -alpha * sol.rhs)" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "Using the given parameters from the problem in the chapter." 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 8, 195 | "metadata": {}, 196 | "outputs": [ 197 | { 198 | "name": "stdout", 199 | "output_type": "stream", 200 | "text": [ 201 | "Eq(I(t), 100*exp(-2*t))\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "sol = get_solution(alpha=2, I_0=100)\n", 207 | "print(sol)" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "Define a function that gives the overall cost." 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 9, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "def get_cost(\n", 224 | " I_0=I_0,\n", 225 | " alpha=alpha,\n", 226 | " per_person_cost=10,\n", 227 | " cure_cost=0,\n", 228 | "):\n", 229 | " \"\"\"Return the cost.\n", 230 | "\n", 231 | " Args:\n", 232 | " I_0: a float (default: symbolic I_0)\n", 233 | " alpha: a float (default: symbolic alpha)\n", 234 | " per_person_cost: a float (default: 10)\n", 235 | " cure_cost: a float (default: 0)\n", 236 | "\n", 237 | " Returns:\n", 238 | " A symbolic expression\n", 239 | " \"\"\"\n", 240 | " I_sol = get_solution(I_0=I_0, alpha=alpha)\n", 241 | " area = sym.integrate(I_sol.rhs, (t, 0, sym.oo))\n", 242 | " productivity_cost = area * per_person_cost\n", 243 | " total_cost_of_cure = cure_cost * I_0\n", 244 | " return productivity_cost + total_cost_of_cure" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "The cost without purchasing the cure" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 10, 257 | "metadata": {}, 258 | "outputs": [ 259 | { 260 | "name": "stdout", 261 | "output_type": "stream", 262 | "text": [ 263 | "500\n" 264 | ] 265 | } 266 | ], 267 | "source": [ 268 | "alpha = 2\n", 269 | "cost_without_cure = get_cost(I_0=100, alpha=alpha)\n", 270 | "print(cost_without_cure)" 271 | ] 272 | }, 273 | { 274 | "cell_type": "markdown", 275 | "metadata": {}, 276 | "source": [ 277 | "The cost with purchasing the cure" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": 11, 283 | "metadata": {}, 284 | "outputs": [ 285 | { 286 | "name": "stdout", 287 | "output_type": "stream", 288 | "text": [ 289 | "750\n" 290 | ] 291 | } 292 | ], 293 | "source": [ 294 | "cost_with_cure = get_cost(I_0=100, alpha=2 * alpha, cure_cost=5)\n", 295 | "print(cost_with_cure)" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "metadata": {}, 302 | "outputs": [], 303 | "source": [] 304 | } 305 | ], 306 | "metadata": { 307 | "kernelspec": { 308 | "display_name": "Python 3", 309 | "language": "python", 310 | "name": "python3" 311 | }, 312 | "language_info": { 313 | "codemirror_mode": { 314 | "name": "ipython", 315 | "version": 3 316 | }, 317 | "file_extension": ".py", 318 | "mimetype": "text/x-python", 319 | "name": "python", 320 | "nbconvert_exporter": "python", 321 | "pygments_lexer": "ipython3", 322 | "version": "3.7.3" 323 | } 324 | }, 325 | "nbformat": 4, 326 | "nbformat_minor": 2 327 | } 328 | -------------------------------------------------------------------------------- /examples/ipynb/06/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Game Theory\n", 8 | "\n", 9 | "This notebook contains code examples referring to the Game Theory chapter of \"Applied Mathematics with Open-Source Software: Operational Research Problems\n", 10 | "with Python and R\"." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First we write a function that returns a game." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import nashpy as nash\n", 27 | "import numpy as np\n", 28 | "\n", 29 | "\n", 30 | "def get_game(profits, offset=0):\n", 31 | " \"\"\"Return the game object with a given offset when 3 taxis are\n", 32 | " provided.\n", 33 | "\n", 34 | " Args:\n", 35 | " profits: a matrix with expected profits\n", 36 | " offset: a float\n", 37 | "\n", 38 | " Returns:\n", 39 | " A nashpy game object\n", 40 | " \"\"\"\n", 41 | " new_profits = np.array(profits)\n", 42 | " new_profits[2] += offset\n", 43 | " return nash.Game(new_profits, new_profits.T)" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "The game for the problem in the chapter is given by:" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 2, 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "Bi matrix game with payoff matrices:\n", 63 | "\n", 64 | "Row player:\n", 65 | "[[1. 0.5 0.33333333]\n", 66 | " [1.5 0.95 0.5 ]\n", 67 | " [1.66666667 0.8 0.85 ]]\n", 68 | "\n", 69 | "Column player:\n", 70 | "[[1. 1.5 1.66666667]\n", 71 | " [0.5 0.95 0.8 ]\n", 72 | " [0.33333333 0.5 0.85 ]]\n" 73 | ] 74 | } 75 | ], 76 | "source": [ 77 | "profits = np.array(\n", 78 | " (\n", 79 | " (1, 1 / 2, 1 / 3),\n", 80 | " (3 / 2, 19 / 20, 1 / 2),\n", 81 | " (5 / 3, 4 / 5, 17 / 20),\n", 82 | " )\n", 83 | ")\n", 84 | "game = get_game(profits=profits)\n", 85 | "print(game)" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "Next we write a function that gives the Nash equilibria for a game." 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 3, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "def get_equilibria(profits, offset=0):\n", 102 | " \"\"\"Return the equilibria for a given offset when 3 taxis are\n", 103 | " provided.\n", 104 | "\n", 105 | " Args:\n", 106 | " profits: a matrix with expected profits\n", 107 | " offset: a float\n", 108 | "\n", 109 | " Returns:\n", 110 | " A tuple of Nash equilibria\n", 111 | " \"\"\"\n", 112 | " game = get_game(profits=profits, offset=offset)\n", 113 | " return tuple(game.support_enumeration())" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "So the equilibria for the considered game are:" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 4, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "name": "stdout", 130 | "output_type": "stream", 131 | "text": [ 132 | "(array([0., 1., 0.]), array([0., 1., 0.]))\n", 133 | "(array([0., 0., 1.]), array([0., 0., 1.]))\n", 134 | "(array([0. , 0.7, 0.3]), array([0. , 0.7, 0.3]))\n" 135 | ] 136 | } 137 | ], 138 | "source": [ 139 | "equilibria = get_equilibria(profits=profits)\n", 140 | "for eq in equilibria:\n", 141 | " print(eq)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "Finding the offset that gives only one Nash equilibrium." 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "offset = 0\n", 158 | "while len(get_equilibria(profits=profits, offset=offset)) > 1:\n", 159 | " offset += 0.01" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "gives" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 6, 172 | "metadata": {}, 173 | "outputs": [ 174 | { 175 | "name": "stdout", 176 | "output_type": "stream", 177 | "text": [ 178 | "0.15\n" 179 | ] 180 | } 181 | ], 182 | "source": [ 183 | "print(round(offset, 2))" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "with that equilibrium being:" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 7, 196 | "metadata": {}, 197 | "outputs": [ 198 | { 199 | "name": "stdout", 200 | "output_type": "stream", 201 | "text": [ 202 | "((array([0., 0., 1.]), array([0., 0., 1.])),)\n" 203 | ] 204 | } 205 | ], 206 | "source": [ 207 | "print(get_equilibria(profits=profits, offset=offset))" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [] 216 | } 217 | ], 218 | "metadata": { 219 | "kernelspec": { 220 | "display_name": "Python 3", 221 | "language": "python", 222 | "name": "python3" 223 | }, 224 | "language_info": { 225 | "codemirror_mode": { 226 | "name": "ipython", 227 | "version": 3 228 | }, 229 | "file_extension": ".py", 230 | "mimetype": "text/x-python", 231 | "name": "python", 232 | "nbconvert_exporter": "python", 233 | "pygments_lexer": "ipython3", 234 | "version": "3.7.3" 235 | } 236 | }, 237 | "nbformat": 4, 238 | "nbformat_minor": 2 239 | } 240 | -------------------------------------------------------------------------------- /examples/ipynb/07/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Agent-Based Simulation\n", 8 | "\n", 9 | "This notebook contains code examples referring to the Agent-Based Simulation chapter of \"Applied Mathematics with Open-Source Software: Operational Research Problems\n", 10 | "with Python and R\"." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First import the required libraries." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import random\n", 27 | "import itertools\n", 28 | "import numpy as np" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "Now define a class that defines a City." 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "class City:\n", 45 | " def __init__(self, size, threshold):\n", 46 | " \"\"\"Initialises the City object.\n", 47 | "\n", 48 | " Args:\n", 49 | " size: an integer number of rows and columns\n", 50 | " threshold: float between 0 and 1 representing the\n", 51 | " minimum acceptable proportion of similar neighbours\n", 52 | " \"\"\"\n", 53 | " self.size = size\n", 54 | " sides = range(size)\n", 55 | " self.coords = itertools.product(sides, sides)\n", 56 | " self.houses = {\n", 57 | " (x, y): House(x, y, threshold, self)\n", 58 | " for x, y in self.coords\n", 59 | " }\n", 60 | "\n", 61 | " def run(self, n_steps):\n", 62 | " \"\"\"Runs the simulation of a number of time steps.\n", 63 | "\n", 64 | " Args:\n", 65 | " n_steps: an integer number of steps\n", 66 | " \"\"\"\n", 67 | " for turn in range(n_steps):\n", 68 | " self.take_turn()\n", 69 | "\n", 70 | " def take_turn(self):\n", 71 | " \"\"\"Swaps all sad households.\"\"\"\n", 72 | " sad = [h for h in self.houses.values() if h.sad()]\n", 73 | " random.shuffle(sad)\n", 74 | " i = 0\n", 75 | " while i <= len(sad) / 2:\n", 76 | " sad[i].swap(sad[-i])\n", 77 | " i += 1\n", 78 | "\n", 79 | " def mean_satisfaction(self):\n", 80 | " \"\"\"Finds the average household satisfaction.\n", 81 | "\n", 82 | " Returns:\n", 83 | " The average city's household satisfaction\n", 84 | " \"\"\"\n", 85 | " return np.mean(\n", 86 | " [h.satisfaction() for h in self.houses.values()]\n", 87 | " )" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "Now define a class that defines a House." 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 3, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "class House:\n", 104 | " def __init__(self, x, y, threshold, city):\n", 105 | " \"\"\"Initialises the House object.\n", 106 | "\n", 107 | " Args:\n", 108 | " x: the integer x-coordinate\n", 109 | " y: the integer y-coordinate\n", 110 | " threshold: a number between 0 and 1 representing\n", 111 | " the minimum acceptable proportion of similar\n", 112 | " neighbours\n", 113 | " city: an instance of the City class\n", 114 | " \"\"\"\n", 115 | " self.x = x\n", 116 | " self.y = y\n", 117 | " self.threshold = threshold\n", 118 | " self.kind = random.choice([\"Cardiff\", \"Swansea\"])\n", 119 | " self.city = city\n", 120 | "\n", 121 | " def satisfaction(self):\n", 122 | " \"\"\"Determines the household's satisfaction level.\n", 123 | "\n", 124 | " Returns:\n", 125 | " A proportion\n", 126 | " \"\"\"\n", 127 | " same = 0\n", 128 | " for x, y in itertools.product([-1, 0, 1], [-1, 0, 1]):\n", 129 | " ax = (self.x + x) % self.city.size\n", 130 | " ay = (self.y + y) % self.city.size\n", 131 | " same += self.city.houses[ax, ay].kind == self.kind\n", 132 | " return (same - 1) / 8\n", 133 | "\n", 134 | " def sad(self):\n", 135 | " \"\"\"Determines if the household is sad.\n", 136 | "\n", 137 | " Returns:\n", 138 | " a Boolean\n", 139 | " \"\"\"\n", 140 | " return self.satisfaction() < self.threshold\n", 141 | "\n", 142 | " def swap(self, house):\n", 143 | " \"\"\"Swaps two households.\n", 144 | "\n", 145 | " Args:\n", 146 | " house: the house object to swap household with\n", 147 | " \"\"\"\n", 148 | " self.kind, house.kind = house.kind, self.kind" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "Now define a function that gives the resulting mean happiness for a given threshold." 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 4, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "def find_mean_happiness(seed, size, threshold, n_steps):\n", 165 | " \"\"\"Create and run an instance of the simulation.\n", 166 | "\n", 167 | " Args:\n", 168 | " seed: the random seed to use\n", 169 | " size: an integer number of rows and columns\n", 170 | " threshold: a number between 0 and 1 representing\n", 171 | " the minimum acceptable proportion of similar\n", 172 | " neighbours\n", 173 | " n_steps: an integer number of steps\n", 174 | "\n", 175 | " Returns:\n", 176 | " The average city's household satisfaction after\n", 177 | " n_steps\n", 178 | " \"\"\"\n", 179 | " random.seed(seed)\n", 180 | " C = City(size, threshold)\n", 181 | " C.run(n_steps)\n", 182 | " return C.mean_satisfaction()" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "Find the initial happiness after 0 steps:" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 5, 195 | "metadata": {}, 196 | "outputs": [ 197 | { 198 | "name": "stdout", 199 | "output_type": "stream", 200 | "text": [ 201 | "0.4998\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "initial_happiness = find_mean_happiness(\n", 207 | " seed=0, size=50, threshold=0.65, n_steps=0\n", 208 | ")\n", 209 | "print(initial_happiness)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "And the final happiness after 100 steps:" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 6, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "0.9078\n" 229 | ] 230 | } 231 | ], 232 | "source": [ 233 | "final_happiness = find_mean_happiness(\n", 234 | " seed=0, size=50, threshold=0.65, n_steps=100\n", 235 | ")\n", 236 | "print(final_happiness)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [] 245 | } 246 | ], 247 | "metadata": { 248 | "kernelspec": { 249 | "display_name": "Python 3", 250 | "language": "python", 251 | "name": "python3" 252 | }, 253 | "language_info": { 254 | "codemirror_mode": { 255 | "name": "ipython", 256 | "version": 3 257 | }, 258 | "file_extension": ".py", 259 | "mimetype": "text/x-python", 260 | "name": "python", 261 | "nbconvert_exporter": "python", 262 | "pygments_lexer": "ipython3", 263 | "version": "3.7.3" 264 | } 265 | }, 266 | "nbformat": 4, 267 | "nbformat_minor": 2 268 | } 269 | -------------------------------------------------------------------------------- /examples/ipynb/09/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Heuristics\n", 8 | "\n", 9 | "This notebook contains code examples referring to the Heuristics chapter of \"Applied Mathematics with Open-Source Software: Operational Research Problems\n", 10 | "with Python and R\"." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First we write a function to generate an initial random candidate solution." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import numpy as np\n", 27 | "\n", 28 | "\n", 29 | "def get_initial_candidate(number_of_stops, seed):\n", 30 | " \"\"\"Return an random initial tour.\n", 31 | "\n", 32 | " Args:\n", 33 | " number_of_stops: The number of stops\n", 34 | " seed: An integer seed.\n", 35 | "\n", 36 | " Returns:\n", 37 | " A tour starting an ending at stop with index 0.\n", 38 | " \"\"\"\n", 39 | " internal_stops = list(range(1, number_of_stops))\n", 40 | " np.random.seed(seed)\n", 41 | " np.random.shuffle(internal_stops)\n", 42 | " return [0] + internal_stops + [0]" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "An example with 13 stops." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "[0, 7, 12, 5, 11, 3, 9, 2, 8, 10, 4, 1, 6, 0]\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "number_of_stops = 13\n", 67 | "seed = 0\n", 68 | "initial_candidate = get_initial_candidate(\n", 69 | " number_of_stops=number_of_stops,\n", 70 | " seed=seed,\n", 71 | ")\n", 72 | "print(initial_candidate)" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "Next we write a function that calculates the cost of that solution." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 3, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "def get_cost(tour, distance_matrix):\n", 89 | " \"\"\"Return the cost of a tour.\n", 90 | "\n", 91 | " Args:\n", 92 | " tour: A given tuple of successive stops.\n", 93 | " distance_matrix: The distance matrix of the problem.\n", 94 | "\n", 95 | " Returns:\n", 96 | " The cost\n", 97 | " \"\"\"\n", 98 | " return sum(\n", 99 | " distance_matrix[current_stop, next_stop]\n", 100 | " for current_stop, next_stop in zip(tour[:-1], tour[1:])\n", 101 | " )" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "For the particular distance matrix given in the chapter, the cost of the initial solution is:" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 4, 114 | "metadata": {}, 115 | "outputs": [ 116 | { 117 | "name": "stdout", 118 | "output_type": "stream", 119 | "text": [ 120 | "827\n" 121 | ] 122 | } 123 | ], 124 | "source": [ 125 | "distance_matrix = np.array(\n", 126 | " (\n", 127 | " (0, 35, 35, 29, 70, 35, 42, 27, 24, 44, 58, 71, 69),\n", 128 | " (35, 0, 67, 32, 72, 40, 71, 56, 36, 11, 66, 70, 37),\n", 129 | " (35, 67, 0, 63, 64, 68, 11, 12, 56, 77, 48, 67, 94),\n", 130 | " (29, 32, 63, 0, 93, 8, 71, 56, 8, 33, 84, 93, 69),\n", 131 | " (70, 72, 64, 93, 0, 101, 56, 56, 92, 81, 16, 5, 69),\n", 132 | " (35, 40, 68, 8, 101, 0, 76, 62, 11, 39, 91, 101, 76),\n", 133 | " (42, 71, 11, 71, 56, 76, 0, 15, 65, 81, 40, 60, 94),\n", 134 | " (27, 56, 12, 56, 56, 62, 15, 0, 50, 66, 41, 58, 82),\n", 135 | " (24, 36, 56, 8, 92, 11, 65, 50, 0, 39, 81, 91, 74),\n", 136 | " (44, 11, 77, 33, 81, 39, 81, 66, 39, 0, 77, 79, 37),\n", 137 | " (58, 66, 48, 84, 16, 91, 40, 41, 81, 77, 0, 20, 73),\n", 138 | " (71, 70, 67, 93, 5, 101, 60, 58, 91, 79, 20, 0, 65),\n", 139 | " (69, 37, 94, 69, 69, 76, 94, 82, 74, 37, 73, 65, 0),\n", 140 | " )\n", 141 | ")\n", 142 | "cost = get_cost(\n", 143 | " tour=initial_candidate,\n", 144 | " distance_matrix=distance_matrix,\n", 145 | ")\n", 146 | "print(cost)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "We then write a function to act as a neighbourhood operator. This one swaps two random stops on the tour." 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 5, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "def swap_stops(tour):\n", 163 | " \"\"\"Return a new tour by swapping two stops.\n", 164 | "\n", 165 | " Args:\n", 166 | " tour: A given tuple of successive stops.\n", 167 | "\n", 168 | " Returns:\n", 169 | " A tour\n", 170 | " \"\"\"\n", 171 | " number_of_stops = len(tour) - 1\n", 172 | " i, j = np.random.choice(range(1, number_of_stops), 2)\n", 173 | " new_tour = list(tour)\n", 174 | " new_tour[i], new_tour[j] = tour[j], tour[i]\n", 175 | " return new_tour" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "Applying this to the initial solution:" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": 6, 188 | "metadata": {}, 189 | "outputs": [ 190 | { 191 | "name": "stdout", 192 | "output_type": "stream", 193 | "text": [ 194 | "[0, 7, 12, 5, 11, 3, 9, 2, 8, 1, 4, 10, 6, 0]\n" 195 | ] 196 | } 197 | ], 198 | "source": [ 199 | "print(swap_stops(initial_candidate))" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "Now we combine these and define a function that runs the neighbourhood search algorithm." 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 7, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "def run_neighbourhood_search(\n", 216 | " distance_matrix,\n", 217 | " iterations,\n", 218 | " seed,\n", 219 | " neighbourhood_operator=swap_stops,\n", 220 | "):\n", 221 | " \"\"\"Returns a tour by carrying out a neighbourhood search.\n", 222 | "\n", 223 | " Args:\n", 224 | " distance_matrix: the distance matrix\n", 225 | " iterations: the number of iterations for which to\n", 226 | " run the algorithm\n", 227 | " seed: a random seed\n", 228 | " neighbourhood_operator: the neighbourhood operator\n", 229 | " (default: swap_stops)\n", 230 | "\n", 231 | " Returns:\n", 232 | " A tour\n", 233 | " \"\"\"\n", 234 | " number_of_stops = len(distance_matrix)\n", 235 | " candidate = get_initial_candidate(\n", 236 | " number_of_stops=number_of_stops,\n", 237 | " seed=seed,\n", 238 | " )\n", 239 | " best_cost = get_cost(\n", 240 | " tour=candidate,\n", 241 | " distance_matrix=distance_matrix,\n", 242 | " )\n", 243 | " for _ in range(iterations):\n", 244 | " new_candidate = neighbourhood_operator(candidate)\n", 245 | " cost = get_cost(\n", 246 | " tour=new_candidate,\n", 247 | " distance_matrix=distance_matrix,\n", 248 | " )\n", 249 | " if cost <= best_cost:\n", 250 | " best_cost = cost\n", 251 | " candidate = new_candidate\n", 252 | "\n", 253 | " return candidate" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "Running this for 1000 iterations gives a better solution." 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 8, 266 | "metadata": {}, 267 | "outputs": [ 268 | { 269 | "name": "stdout", 270 | "output_type": "stream", 271 | "text": [ 272 | "[0, 7, 2, 8, 5, 3, 1, 9, 12, 11, 4, 10, 6, 0]\n" 273 | ] 274 | } 275 | ], 276 | "source": [ 277 | "number_of_iterations = 1000\n", 278 | "\n", 279 | "solution_with_swap_stops = run_neighbourhood_search(\n", 280 | " distance_matrix=distance_matrix,\n", 281 | " iterations=number_of_iterations,\n", 282 | " seed=seed,\n", 283 | " neighbourhood_operator=swap_stops,\n", 284 | ")\n", 285 | "print(solution_with_swap_stops)" 286 | ] 287 | }, 288 | { 289 | "cell_type": "markdown", 290 | "metadata": {}, 291 | "source": [ 292 | "with cost:" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 9, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "name": "stdout", 302 | "output_type": "stream", 303 | "text": [ 304 | "362\n" 305 | ] 306 | } 307 | ], 308 | "source": [ 309 | "cost = get_cost(\n", 310 | " tour=solution_with_swap_stops,\n", 311 | " distance_matrix=distance_matrix,\n", 312 | ")\n", 313 | "print(cost)" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": {}, 319 | "source": [ 320 | "We now define a different neighbourhood operator, reversing the tour between two stops." 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 10, 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "def reverse_path(tour):\n", 330 | " \"\"\"Return a new tour by reversing the path between two stops.\n", 331 | "\n", 332 | " Args:\n", 333 | " tour: A given tuple of successive stops.\n", 334 | "\n", 335 | " Returns:\n", 336 | " A tour\n", 337 | " \"\"\"\n", 338 | " number_of_stops = len(tour) - 1\n", 339 | " stops = np.random.choice(range(1, number_of_stops), 2)\n", 340 | " i, j = sorted(stops)\n", 341 | " new_tour = tour[:i] + tour[i : j + 1][::-1] + tour[j + 1 :]\n", 342 | " return new_tour" 343 | ] 344 | }, 345 | { 346 | "cell_type": "markdown", 347 | "metadata": {}, 348 | "source": [ 349 | "Applying this to the initial solution:" 350 | ] 351 | }, 352 | { 353 | "cell_type": "code", 354 | "execution_count": 11, 355 | "metadata": {}, 356 | "outputs": [ 357 | { 358 | "name": "stdout", 359 | "output_type": "stream", 360 | "text": [ 361 | "[0, 7, 4, 10, 8, 2, 9, 3, 11, 5, 12, 1, 6, 0]\n" 362 | ] 363 | } 364 | ], 365 | "source": [ 366 | "print(reverse_path(initial_candidate))" 367 | ] 368 | }, 369 | { 370 | "cell_type": "markdown", 371 | "metadata": {}, 372 | "source": [ 373 | "Using this neighbourhood operator in the neighbourhood search instead, gives an even better solution." 374 | ] 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": 12, 379 | "metadata": {}, 380 | "outputs": [ 381 | { 382 | "name": "stdout", 383 | "output_type": "stream", 384 | "text": [ 385 | "[0, 8, 5, 3, 1, 9, 12, 11, 4, 10, 6, 2, 7, 0]\n" 386 | ] 387 | } 388 | ], 389 | "source": [ 390 | "solution_with_reverse_path = run_neighbourhood_search(\n", 391 | " distance_matrix=distance_matrix,\n", 392 | " iterations=number_of_iterations,\n", 393 | " seed=seed,\n", 394 | " neighbourhood_operator=reverse_path,\n", 395 | ")\n", 396 | "print(solution_with_reverse_path)" 397 | ] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": {}, 402 | "source": [ 403 | "with cost:" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 13, 409 | "metadata": {}, 410 | "outputs": [ 411 | { 412 | "name": "stdout", 413 | "output_type": "stream", 414 | "text": [ 415 | "299\n" 416 | ] 417 | } 418 | ], 419 | "source": [ 420 | "cost = get_cost(\n", 421 | " tour=solution_with_reverse_path,\n", 422 | " distance_matrix=distance_matrix,\n", 423 | ")\n", 424 | "print(cost)" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": null, 430 | "metadata": {}, 431 | "outputs": [], 432 | "source": [] 433 | } 434 | ], 435 | "metadata": { 436 | "kernelspec": { 437 | "display_name": "Python 3", 438 | "language": "python", 439 | "name": "python3" 440 | }, 441 | "language_info": { 442 | "codemirror_mode": { 443 | "name": "ipython", 444 | "version": 3 445 | }, 446 | "file_extension": ".py", 447 | "mimetype": "text/x-python", 448 | "name": "python", 449 | "nbconvert_exporter": "python", 450 | "pygments_lexer": "ipython3", 451 | "version": "3.7.3" 452 | } 453 | }, 454 | "nbformat": 4, 455 | "nbformat_minor": 2 456 | } 457 | -------------------------------------------------------------------------------- /examples/rmd/02/main.rmd: -------------------------------------------------------------------------------- 1 | # Markov chains 2 | 3 | This notebook contains code examples referring to the Markov chains chapter of "Applied Mathematics with Open-Source Software: Operational Research Problems 4 | with Python and R". 5 | 6 | First we define a function to get the transition rates between two given states: 7 | 8 | ```{R} 9 | #' Return the transition rate for 2 given states. 10 | #' 11 | #' @param in_state an integer 12 | #' @param out_state an integer 13 | #' @param waiting_room an integer (default: 4) 14 | #' @param num_barbers an integer (default: 2) 15 | #' 16 | #' @return A real 17 | get_transition_rate <- function(in_state, 18 | out_state, 19 | waiting_room = 4, 20 | num_barbers = 2) { 21 | arrival_rate <- 10 22 | service_rate <- 4 23 | capacity <- waiting_room + num_barbers 24 | delta <- out_state - in_state 25 | 26 | if (delta == 1) { 27 | return(arrival_rate) 28 | } 29 | if (delta == -1) { 30 | return(min(in_state, num_barbers) * service_rate) 31 | } 32 | return(0) 33 | } 34 | ``` 35 | 36 | We can make the code slightly more efficient by creating a vectorized version of this function: 37 | 38 | ```{R} 39 | vectorized_get_transition_rate <- Vectorize( 40 | get_transition_rate, 41 | vectorize.args = c("in_state", "out_state") 42 | ) 43 | ``` 44 | 45 | This can be used to create the transition matrix: 46 | 47 | ```{R} 48 | #' Return the transition rate matrix Q 49 | #' 50 | #' @param waiting_room an integer (default: 4) 51 | #' @param num_barbers an integer (default: 2) 52 | #' 53 | #' @return A matrix 54 | get_transition_rate_matrix <- function(waiting_room = 4, 55 | num_barbers = 2){ 56 | max_state <- waiting_room + num_barbers 57 | 58 | Q <- outer( 59 | 0:max_state, 60 | 0:max_state, 61 | vectorized_get_transition_rate, 62 | waiting_room = waiting_room, 63 | num_barbers = num_barbers 64 | ) 65 | row_sums <- rowSums(Q) 66 | diag(Q) <- -row_sums 67 | Q 68 | } 69 | ``` 70 | 71 | For the problem from the chapter this is: 72 | 73 | ```{R} 74 | Q <- get_transition_rate_matrix() 75 | print(Q) 76 | ``` 77 | 78 | Here is the state of the system after 0.5 time units: 79 | 80 | ```{R} 81 | library(expm) 82 | library(magrittr) 83 | 84 | print( (Q * 0.5) %>% expm() %>% round(5)) 85 | ``` 86 | 87 | After 500 time units: 88 | 89 | 90 | ```{R} 91 | print( (Q * 500) %>% expm() %>% round(5)) 92 | ``` 93 | 94 | We can obtain the steady state: 95 | 96 | ```{R} 97 | library(pracma) 98 | 99 | #' Return the steady state vector of any given continuous time 100 | #' transition rate matrix 101 | #' 102 | #' @param Q a transition rate matrix 103 | #' 104 | #' @return A vector 105 | get_steady_state_vector <- function(Q){ 106 | state_space_size <- dim(Q)[1] 107 | A <- rbind(t(Q), 1) 108 | b <- c(integer(state_space_size), 1) 109 | mldivide(A, b) 110 | } 111 | 112 | ``` 113 | 114 | 115 | ```{R} 116 | print(get_steady_state_vector(Q)) 117 | ``` 118 | 119 | The probability of a full shop 120 | 121 | ```{R} 122 | #' Return the probability of the barber shop being full 123 | #' 124 | #' @param waiting_room (default: 4) 125 | #' @param num_barbers (default: 2) 126 | #' 127 | #' @return A real 128 | get_probability_of_full_shop <- function(waiting_room = 4, 129 | num_barbers = 2){ 130 | arrival_rate <- 10 131 | service_rate <- 4 132 | pi <- get_transition_rate_matrix( 133 | waiting_room = waiting_room, 134 | num_barbers = num_barbers 135 | ) %>% 136 | get_steady_state_vector() 137 | 138 | capacity <- waiting_room + num_barbers 139 | pi[capacity + 1] 140 | } 141 | 142 | ``` 143 | 144 | ```{R} 145 | print(get_probability_of_full_shop()) 146 | ``` 147 | 148 | ```{R} 149 | print(get_probability_of_full_shop(waiting_room = 6)) 150 | ``` 151 | 152 | ```{R} 153 | print(get_probability_of_full_shop(num_barbers = 3)) 154 | ``` 155 | -------------------------------------------------------------------------------- /examples/rmd/03/main.rmd: -------------------------------------------------------------------------------- 1 | # Discrete Event Simulation 2 | 3 | This notebook contains code examples referring to the Discrete Event Simulation chapter of "Applied Mathematics with Open-Source Software: Operational Research Problems 4 | with Python and R". 5 | 6 | First we define a function that gives a Network object, containing the structure of the repair shop. 7 | 8 | ```{R} 9 | library(simmer) 10 | 11 | #' Returns a simmer trajectory object outlining the bicycles 12 | #' path through the repair shop 13 | #' 14 | #' @return A simmer trajectory object 15 | define_bicycle_trajectories <- function() { 16 | inspection_rate <- 20 17 | repair_rate <- 10 18 | prob_need_repair <- 0.8 19 | bicycle <- 20 | trajectory("Inspection") %>% 21 | seize("Inspector") %>% 22 | timeout(function() { 23 | rexp(1, inspection_rate) 24 | }) %>% 25 | release("Inspector") %>% 26 | branch( 27 | function() (runif(1) < prob_need_repair), 28 | continue = c(F), 29 | trajectory("Repair") %>% 30 | seize("Repairer") %>% 31 | timeout(function() { 32 | rexp(1, repair_rate) 33 | }) %>% 34 | release("Repairer"), 35 | trajectory("Out") 36 | ) 37 | return(bicycle) 38 | } 39 | ``` 40 | 41 | 42 | ```{R} 43 | #' Runs one trial of the simulation. 44 | #' 45 | #' @param bicycle a simmer trajectory object 46 | #' @param num_inspectors positive integer (default: 1) 47 | #' @param num_repairers positive integer (default: 2) 48 | #' @param seed a float (default: 0) 49 | #' 50 | #' @return A simmer simulation object after one run of 51 | #' the simulation 52 | run_simulation <- function(bicycle, 53 | num_inspectors = 1, 54 | num_repairers = 2, 55 | seed = 0) { 56 | arrival_rate <- 15 57 | max_time <- 8 58 | repair_shop <- 59 | simmer("Repair Shop") %>% 60 | add_resource("Inspector", num_inspectors) %>% 61 | add_resource("Repairer", num_repairers) %>% 62 | add_generator( 63 | "Bicycle", bicycle, function() { 64 | rexp(1, arrival_rate) 65 | } 66 | ) 67 | set.seed(seed) 68 | repair_shop %>% run(until = max_time) 69 | return(repair_shop) 70 | } 71 | ``` 72 | 73 | ```{R} 74 | #' Returns the proportion of bicycles spending over 30 75 | #' minutes in the repair shop 76 | #' 77 | #' @param repair_shop a simmer simulation object 78 | #' 79 | #' @return a float between 0 and 1 80 | get_proportion <- function(repair_shop) { 81 | limit <- 0.5 82 | recs <- repair_shop %>% get_mon_arrivals() 83 | total_times <- recs$end_time - recs$start_time 84 | return(mean(total_times > limit)) 85 | } 86 | ``` 87 | 88 | The proportion of bicycles spending over 30 minutes 89 | with current staff: 90 | 91 | ```{R} 92 | bicycle <- define_bicycle_trajectories() 93 | repair_shop <- run_simulation(bicycle = bicycle) 94 | print(get_proportion(repair_shop = repair_shop)) 95 | ``` 96 | 97 | The following function will repeat this to get a reliable average: 98 | 99 | ```{R} 100 | #' Returns the average proportion of bicycles spending over 101 | #' a given limit at the repair shop. 102 | #' 103 | #' @param num_inspectors positive integer (default: 1) 104 | #' @param num_repairers positive integer (default: 2) 105 | 106 | #' @return a float between 0 and 1 107 | get_average_proportion <- function(num_inspectors = 1, 108 | num_repairers = 2) { 109 | num_trials <- 100 110 | bicycle <- define_bicycle_trajectories() 111 | proportions <- c() 112 | for (trial in 1:num_trials) { 113 | repair_shop <- run_simulation( 114 | bicycle = bicycle, 115 | num_inspectors = num_inspectors, 116 | num_repairers = num_repairers, 117 | seed = trial 118 | ) 119 | proportion <- get_proportion( 120 | repair_shop = repair_shop 121 | ) 122 | proportions[trial] <- proportion 123 | } 124 | return(mean(proportions)) 125 | } 126 | 127 | ``` 128 | 129 | The proportion with current staff: 130 | 131 | ```{R} 132 | print( 133 | get_average_proportion( 134 | num_inspectors = 1, 135 | num_repairers = 2) 136 | ) 137 | ``` 138 | 139 | 140 | With 2 inspectors: 141 | 142 | ```{R} 143 | print( 144 | get_average_proportion( 145 | num_inspectors = 2, 146 | num_repairers = 2 147 | ) 148 | ) 149 | ``` 150 | 151 | The proportion with an extra repairer: 152 | 153 | 154 | ```{R} 155 | print( 156 | get_average_proportion( 157 | num_inspectors = 1, 158 | num_repairers = 3 159 | ) 160 | ) 161 | ``` -------------------------------------------------------------------------------- /known.py: -------------------------------------------------------------------------------- 1 | words = { 2 | "AGH", 3 | "Abhinav", 4 | "Aftosmis", 5 | "Agarwal", 6 | "Alam", 7 | "Alamos", 8 | "Aln", 9 | "Alon", 10 | "Ames", 11 | "Ao", 12 | "Args", 13 | "Ashby", 14 | "Ax", 15 | "Axelrod", 16 | "Axelrod's", 17 | "Bader", 18 | "Bc", 19 | "Becskei", 20 | "Berzins", 21 | "Bhatele", 22 | "Bischof", 23 | "Biswas", 24 | "Bo", 25 | "Bohm", 26 | "Boids", 27 | "Booleans", 28 | "Bordner", 29 | "Bosilca", 30 | "Bubak", 31 | "COVID", 32 | "CRAN", 33 | "Center", 34 | "Champaign", 35 | "Ciw", 36 | "Crobak", 37 | "DES", 38 | "DataFrame", 39 | "Diaconescu", 40 | "Elowitz", 41 | "Eq", 42 | "Eval", 43 | "ExamScheduling", 44 | "Expon", 45 | "FC", 46 | "Federer", 47 | "Forrester", 48 | "Forrester's", 49 | "GBP", 50 | "Geraint", 51 | "Glynatsi", 52 | "Howson", 53 | "Itertools", 54 | "Jupyter", 55 | "Kerckhoffs's", 56 | "Kerckhoffs", 57 | "Krak", 58 | "Kutta", 59 | "LHS", 60 | "Lanchester's", 61 | "Lemke", 62 | "Livermore", 63 | "Los", 64 | "LpBinary", 65 | "LpBinary", 66 | "LpMinimize", 67 | "LpMinimize", 68 | "LpProblem", 69 | "LpProblem", 70 | "MILP", 71 | "MILP", 72 | "Macrobehaviours", 73 | "Matplotlib", 74 | "Mersenne", 75 | "Micha", 76 | "Micromotives", 77 | "Modularity", 78 | "modularity", 79 | "Moffett", 80 | "Mol", 81 | "NMeth", 82 | "Nashpy", 83 | "Nikoleta", 84 | "Numpy", 85 | "OSX", 86 | "Optimization", 87 | "Piscataway", 88 | "Pokémon", 89 | "Pratul", 90 | "Pseudorandom", 91 | "PuLP", 92 | "REPL", 93 | "REPLs", 94 | "RHS", 95 | "ROI", 96 | "RStudio", 97 | "RWTH", 98 | "Riggins", 99 | "Rosenfeld", 100 | "Roxana", 101 | "Rscript", 102 | "Rstudio", 103 | "Runge", 104 | "Rupak", 105 | "SIMD", 106 | "Sadaf", 107 | "Sandia", 108 | "Sandve", 109 | "Savageau", 110 | "SciPy", 111 | "SciPy's", 112 | "SimPy", 113 | "Simpy", 114 | "Simula", 115 | "SymPy", 116 | "Sympy", 117 | "Urbana", 118 | "Vectorize", 119 | "Yconstraints", 120 | "Ys", 121 | "Zizhong", 122 | "analytical", 123 | "ao", 124 | "arange", 125 | "argmin", 126 | "args", 127 | "automata", 128 | "autoregulation", 129 | "ax", 130 | "axelrod", 131 | "ay", 132 | "bN", 133 | "bache", 134 | "bc", 135 | "bo", 136 | "bool", 137 | "boolean", 138 | "booleans", 139 | "borchers", 140 | "bozorg", 141 | "braekers", 142 | "brailsford", 143 | "brailsford", 144 | "byrow", 145 | "calc", 146 | "capacitated", 147 | "cbc", 148 | "cbind", 149 | "ccc", 150 | "ccccccccccccc", 151 | "cccccccccccccccccc", 152 | "cccccccccccccccccccc", 153 | "cd", 154 | "center", 155 | "centered", 156 | "centralized", 157 | "ciw", 158 | "ciwpython", 159 | "ciwpython", 160 | "cmd", 161 | "coeffs", 162 | "combinatorial", 163 | "conda", 164 | "conforti", 165 | "coords", 166 | "coyle", 167 | "croes", 168 | "cryptographic", 169 | "cryptographie", 170 | "cryptosystem", 171 | "dI", 172 | "dI", 173 | "dIdt", 174 | "dR", 175 | "dRdt", 176 | "dS", 177 | "dSdt", 178 | "darkgreen", 179 | "darwin", 180 | "deSolve", 181 | "deo", 182 | "ders", 183 | "dev", 184 | "diag", 185 | "diaz", 186 | "dir", 187 | "dirs", 188 | "dplyr", 189 | "dr", 190 | "dsolve", 191 | "dt", 192 | "dt", 193 | "duran", 194 | "dv", 195 | "dx", 196 | "dy", 197 | "eg", 198 | "el", 199 | "elif", 200 | "eq", 201 | "eqs", 202 | "equilibria", 203 | "expm", 204 | "figueira", 205 | "flynn", 206 | "fontsize", 207 | "forrest", 208 | "forrester", 209 | "fudenberg", 210 | "func", 211 | "genous", 212 | "ggplot", 213 | "goulet", 214 | "gray", 215 | "griffiths", 216 | "groupby", 217 | "guerriero", 218 | "harris", 219 | "harris", 220 | "harris", 221 | "healthcare", 222 | "hifi", 223 | "ics", 224 | "ifelse", 225 | "ij", 226 | "inding", 227 | "inds", 228 | "inducible", 229 | "init", 230 | "init", 231 | "initialize", 232 | "iterable", 233 | "iteratively", 234 | "itertools", 235 | "ivp", 236 | "jing", 237 | "jupyter", 238 | "kerckhoffs", 239 | "lanchester", 240 | "len", 241 | "lewis", 242 | "linalg", 243 | "lll", 244 | "macrobehaviours", 245 | "magrittr", 246 | "markov", 247 | "maschler", 248 | "matplotlib", 249 | "matsumoto", 250 | "mckinney", 251 | "meds", 252 | "memoryless", 253 | "menger", 254 | "mersenne", 255 | "meurer", 256 | "michalewicz", 257 | "micromotives", 258 | "milp", 259 | "mitchell", 260 | "mldivide", 261 | "modeling", 262 | "modularisation", 263 | "moghdani", 264 | "moler", 265 | "mon", 266 | "multiagent", 267 | "na", 268 | "nasar", 269 | "nash", 270 | "nasheq", 271 | "nashpy", 272 | "nd", 273 | "neighborhood", 274 | "nh", 275 | "nolint", 276 | "np", 277 | "nrow", 278 | "num", 279 | "numpy", 280 | "odeint", 281 | "oliveira", 282 | "ometimes", 283 | "omplex", 284 | "oo", 285 | "operons", 286 | "optimality", 287 | "orge", 288 | "osorio", 289 | "ost", 290 | "outext", 291 | "palmer", 292 | "parallelisation", 293 | "param", 294 | "params", 295 | "parms", 296 | "pdf", 297 | "plt", 298 | "png", 299 | "pokemon", 300 | "pos", 301 | "postaction", 302 | "pracma", 303 | "pracma's", 304 | "proc", 305 | "pseudorandom", 306 | "pwd", 307 | "py", 308 | "pyplot", 309 | "queueing", 310 | "rI", 311 | "rSymPy", 312 | "rbind", 313 | "rcbc", 314 | "rcond", 315 | "reback", 316 | "reconfiguring", 317 | "recs", 318 | "reproducibility", 319 | "reqs", 320 | "rexp", 321 | "reynolds", 322 | "rhs", 323 | "rhss", 324 | "robinson", 325 | "robinson", 326 | "roi", 327 | "romanowska", 328 | "rostering", 329 | "rowSums", 330 | "runif", 331 | "runtime", 332 | "sapply", 333 | "sarkka", 334 | "savefig", 335 | "schelling", 336 | "schreuder", 337 | "scipy", 338 | "screenshots", 339 | "sep", 340 | "shoham", 341 | "shumacher", 342 | "simpy", 343 | "soetaert", 344 | "sprintf", 345 | "startarrowdecide", 346 | "startarrowobserve", 347 | "startarrowupdate", 348 | "stewart", 349 | "stopifnot", 350 | "str", 351 | "subdirectory", 352 | "sym", 353 | "sympy", 354 | "syms", 355 | "tabu", 356 | "tapIR", 357 | "tapSI", 358 | "tapinR", 359 | "tapinS", 360 | "tapoutI", 361 | "tapoutR", 362 | "tapoutS", 363 | "th", 364 | "theussl", 365 | "tidyverse", 366 | "tuple", 367 | "txss", 368 | "ucar", 369 | "vandergraft", 370 | "vazquez", 371 | "vbN", 372 | "vectorize", 373 | "vectorized", 374 | "vectorized", 375 | "von", 376 | "webpage", 377 | "wickham", 378 | "wilson", 379 | "workspace", 380 | "xshape", 381 | "ystems", 382 | } 383 | -------------------------------------------------------------------------------- /proposal/main.md: -------------------------------------------------------------------------------- 1 | # CRC Focus Proposal 2 | 3 | ## TITLE AND AUTHOR(S) 4 | 5 | ### 1. Provisional title of your book. 6 | 7 | "Applied mathematics with Open Source Software: Operational Research problems 8 | with Python and R." 9 | 10 | ### 2. Author(s)/editor(s), affiliations and contact information 11 | 12 | - Name: Vincent Knight and Geraint Palmer 13 | - Affiliation: Cardiff University School of Mathematics 14 | - Address: Mathematics Institute, Senghennydd Road, Cardiff, CF24 4AG 15 | - Telephone: +44 (0)29 2087 5548 16 | - Email: knightva@cardiff.ac.uk and palmergi1@cardiff.ac.uk 17 | 18 | ## Subject, aims and features 19 | 20 | ### 3. Please describe in detail the subject of your book and indicate its academic level. 21 | 22 | This book will cover an overview of Operational Research problems with a 23 | specific emphasis on solving them with open source software. The academic level 24 | will be from undergraduate to postgraduate students and researchers either 25 | aiming to learn about Operational Research and/or wanting to solve applied 26 | problems using open tools. 27 | 28 | 29 | The typical chapter structure will be: 30 | 31 | 1. Introduction - a brief overview of a given problem type. 32 | 2. Example problem. 33 | 3. Solving with Python. 34 | 4. Solving with R. 35 | 5. Brief theoretic background with pointers to reference texts. 36 | 6. Examples of research using these methods. 37 | 38 | The topics covered will span the common range of Operational Research problems: 39 | from the basics of probabilistic modelling to more niche areas such as linear 40 | programming and game theory. See additional materials for further details. 41 | 42 | 43 | ### 4. Please describe your motivation for writing the book; why it is important. 44 | 45 | 46 | There are numerous texts that cover the theoretic background of the field of 47 | Operational Research. Numerous undergraduate and postgraduate courses in these 48 | fields make use of these texts but also complement them with the use of 49 | commercial software. Students are often left with the misconception that a 50 | particular topic is equivalent to the particular commercial software product 51 | that was used during the course. This is unnecessary and also not inclusive of 52 | those unable to afford these products. Furthermore, it does not promote open 53 | research best practice. 54 | 55 | This book will offer a very important resource: showcasing how the numerous 56 | areas of applied mathematical problem solving can be done using free and open 57 | tools. 58 | 59 | 60 | ### 5. Please list up to 5 key features of your proposed book. 61 | 62 | 1. Full description and setup instructions for use of open source software. 63 | 2. Theoretic description of a number of mathematical problem solving tools. 64 | 3. Clear and detailed code examples in one, or when appropriate two open source 65 | programming languages. 66 | 4. Discussion of research carried out in each topic. 67 | 68 | 69 | ### 6. Will your book feature any supplementary material, e.g. code and datasets online? 70 | 71 | The source files for the book itself (the LaTeX files) and the programming 72 | examples will be open sourced. This will also ensure that usable code will be 73 | available. 74 | 75 | ## Audience and related books 76 | 77 | ### 7. Please discuss the intended audience for your book. Is it written primarily for scholars (if so, what disciplines), professionals (if so, which fields), or students (if so, what level)? Please be as specific and realistic as possible and remember that few, if any, books appeal to all of the above simultaneously. 78 | 79 | The intended audience will be scholars of applied mathematics and/or 80 | operational research. The intention of the book is to give an overview of the 81 | theoretic background of each type of problem considered but details of the 82 | actual implementation process will be the highlight of each chapter. Thus, this 83 | will be of interest to students (undergraduate and/or postgraduate) as well as 84 | academics who want to learn how to use open source software. 85 | 86 | 87 | ### 8. Please list below published books (or online resources) which one might consider as similar to your own: on the same topic, written at the same level, and intended for the same audience. If you feel there is no direct competition for your book, please list those titles that are more generally related to your book. 88 | 89 | 90 | - "Python for conducting operational research in healthcare": 91 | https://www.youtube.com/watch?v=CcEURL392-w The proposed text will differ from 92 | this talk by providing a text based resource but also by expanding to include 93 | further areas of operational research. 94 | - Allen Downey's "Think complexity" http://greenteapress.com/complexity/ and 95 | other similar titles. The proposed text will differ from this in its main 96 | message, whilst Allen Downey's books aim to use programming to teach concepts, 97 | the proposed text will concentrate on implementation of concepts. 98 | - Taha's "Operations Research: An Introduction, Global Edition" 99 | https://www.amazon.co.uk/Operations-Research-Introduction-Hamdy-Taha/dp/1292165545/ref=sr_1_1?ie=UTF8&qid=1543934254&sr=8-1&keywords=operational+research. 100 | The proposed text will actually complement these sorts of general texts quite 101 | well by showing implementation of the concepts using open source software. 102 | - Jones, Maillardet and Robinson's "Introduction to Scientific Programming and 103 | Simulation Using R (Chapman & Hall/CRC: The R Series)" 104 | https://www.amazon.co.uk/Introduction-Scientific-Programming-Simulation-Chapman/dp/1466569999/ref=sr_1_2?s=books&ie=UTF8&qid=1543934328&sr=1-2&keywords=Owen+Jones+R. 105 | The proposed text will differ from this as it will concentrate on two 106 | programming languages, thus hopefully showcasing the implementation as a 107 | general concept. Furthermore, the proposed text will be briefer in its form 108 | aiming to provide a handbook-like experience to readers. 109 | - "Python for healthcare analytics and modelling" https://pythonhealthcare.org/. 110 | The proposed text differs from this by offering a more general text, both in 111 | terms of programming language but also in terms of the subjects considered. 112 | 113 | 114 | ## FURTHER DETAILS 115 | 116 | ### 9. Roughly how many thousand words in length will your book be? CRC Focus books are 20,000 to 50,000 words long, including references and footnotes. 117 | 118 | 40,000 words. 119 | 120 | ### 10. When would you hope to be able to submit the final draft of the book to us? And in which format, LaTeX or Word? 121 | 122 | LaTeX 123 | 124 | ### 11. Please give the names and e-mail addresses of five people who would be qualified to give an opinion on your proposed book. (We will not necessarily contact these people). 125 | 126 | - Allen Downey: downey@allendowney.com 127 | - Thomas Monks: thomas.monks@soton.ac.uk 128 | - Paul Harper: harper@cardiff.ac.uk 129 | - Sally Brailsford: S.C.Brailsford@soton.ac.uk 130 | - Sally McClean: si.mcclean@ulster.ac.uk 131 | 132 | ### 12. Which scholarly/professional societies are applicable for marketing your book? 133 | 134 | Operational research societies such as: 135 | 136 | - INFORMS: https://www.informs.org/ 137 | - The OR Society: https://www.theorsociety.com/ 138 | 139 | More general mathematical societies: 140 | 141 | - IMA: https://ima.org.uk/ 142 | - SIAM: https://www.siam.org/ 143 | - AMS: https://www.ams.org/ 144 | 145 | Research software communities: 146 | 147 | - SSI: https://www.software.ac.uk/ 148 | 149 | ### 13. Please include a full table of contents, including chapter sub-headings and/or chapter abstracts. If the book is an edited collection, please also provide a tentative list of the expected authors and their affiliations (it is not necessary to have gained agreement from these people to contribute at this time). 150 | 151 | --- 152 | 153 | ###### Chapter 1. Introduction 154 | 155 | This chapter will introduce the book: describing what is to be considered an 156 | applied mathematical problem. This chapter 157 | will also describe the origins of open source software and give a number of 158 | examples of them. 159 | 160 | ###### Chapter 2. Basic programming and the command line 161 | 162 | This chapter will describe the installation and setup of Anaconda. It will also 163 | describe using the command line as an interface to your computer. Finally, some 164 | basics of Python and R (two open source languages) will also be covered. 165 | 166 | ##### Part 1: Probabilistic modelling 167 | 168 | ###### Chapter 3. Markov chains 169 | 170 | This chapter will describe probabilistic modelling with Markov chains: the 171 | basic theory will be covered. An example of modelling a queue will be used. 172 | Python/R will be used to illustrate building the continuous time transition 173 | matrix, discretizing it, looking at the long run time and finding the steady 174 | state vector. Some what if scenarios will be considered. Finally a discussion 175 | of some research using these notions will be used. 176 | 177 | 178 | ###### Chapter 4. Discrete event simulation 179 | 180 | In a similar fashion to Chapter 8: some queueing network problems cannot be 181 | easily computed as a Markov chain. This chapter will describe the theoretic 182 | background of discrete event simulation. It will illustrate the simulation of a 183 | complex queuing network using Python/R. Further examples of research in this 184 | area will be described. 185 | 186 | ##### Part 2: Dynamical systems 187 | 188 | ###### Chapter 5. Modelling with differential equations 189 | 190 | This chapter will describe using differential equations to model real world 191 | systems. A theoretic background will be included and symbolic computations (in 192 | Python/R) will be used to solve differential equations exactly. The example of 193 | Lanchester's battle equations will be used. A discussion of research on these 194 | topics will be included. 195 | 196 | ###### Chapter 6. Systems dynamics 197 | 198 | This chapter will describe the numerical solution of a dynamical system: for 199 | some systems of differential equations, obtaining exact solutions is too complex, 200 | in which case they can be solved numerically. An example of modelling the spread 201 | of disease in a complex format will be used. Python/R will be used to build the 202 | underlying equations and solve them numerically. Some what if scenarios will be 203 | considered. Finally a discussion of some research using these notions will be 204 | used. 205 | 206 | ##### Part 3: Emergent behaviour 207 | 208 | ###### Chapter 7. Game theory 209 | 210 | This will describe basics of game theory: theoretic background to concepts of 211 | equilibria will be described. A game modelling interactions between two 212 | service providers will be considered. Python/R will be used to 213 | obtain the Nash equilibria. A discussion of some research using these notions 214 | will be used. 215 | 216 | ###### Chapter 8. Agent based simulation 217 | 218 | Notions of agent based simulation will be used. As an example Schelling’s model 219 | of segregation will be built using Python/R. Research in this area will also be 220 | described. Graph theory libraries will be used to capture spacial relationships. 221 | 222 | 223 | ##### Part 4: Optimisation 224 | 225 | ###### Chapter 9. Linear programming 226 | 227 | This chapter will describe the optimisation of a linear program: the basic 228 | theory will be covered. An example of scheduling exams will be illustrated, 229 | Python/R will be used to build the LP and solve it. A discussion of some 230 | research using these notions will be used. 231 | 232 | 233 | ###### Chapter 10. Heuristics 234 | 235 | Some optimisation problems cannot be solved exactly: for example the travelling 236 | salesman problem. The 237 | basic theory will be covered. An example of solving the TSP using heuristic 238 | optimisation algorithms in Python/R will be covered. Further examples of this in 239 | research will be discussed. 240 | 241 | ##### Appendix: Data analysis, machine learning and statistics 242 | 243 | Numerous texts cover the Data analysis with Python/R, this chapter will give a 244 | very brief overview of these and give a description of good texts on the 245 | subject. 246 | 247 | --- 248 | 249 | #### 16. Please include a brief biography for each author/editor. (N.B. biographies are not required for contributors). 250 | 251 | 252 | Vince Knight is a senior lecturer (associate professor) of Mathematics at 253 | Cardiff University. He is a maintainer of open source libraries used in agent 254 | based modelling and game theoretic research. He uses open source software for 255 | all his teaching and research. He is a fellow of the 256 | sustainable software institute highlighting his interest in best practice when 257 | it comes to scientific programming. He has been teaching programming for more 258 | than 10 years and is regularly involved in organising Python conferences in the 259 | UK and Africa. 260 | 261 | Geraint Palmer is a teaching associate of Mathematics at Cardiff University. He 262 | is a maintainer of an open source library for discrete event simulation. He is 263 | also a fellow of the sustainable software institute and has presented at a 264 | number of international conferences on the subject of best practice of scientific 265 | computing. He has used open software tools in his own research and regularly 266 | teaches programming. 267 | -------------------------------------------------------------------------------- /src/.DS_Store~c2ba64c... add stock and flow diagram: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/.DS_Store~c2ba64c... add stock and flow diagram -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Editorial principles 2 | 3 | ## Who is this book for? 4 | This book is aimed at readers who want to use open source software to solve the 5 | considered applied mathematical problems. Some knowledge of mathematics is 6 | assumed but is not necessary. 7 | It is also assumed that the reader will have done any introductory tutorial in R 8 | or Python. 9 | TODO Add pointer to such tutorials 10 | 11 | By reading a particular chapter of the book, the reader will have: 12 | 13 | \begin{enumerate} 14 | \item the practical knowledge to solve problems using a computer, 15 | \item an overview of the higher level theoretic concepts, 16 | \item pointers to further reading to gain background understand and research 17 | undertaken using the concepts. 18 | \end{enumerate} 19 | 20 | ## Contents of sections 21 | 22 | ### Introduction 23 | 24 | > If I know nothing about the topic and read this paragraph I will know a 25 | > surface description of the problem type and the name of the solution approach. 26 | > Typically 2 or 3 sentences. 27 | 28 | ### Typical Problem 29 | 30 | > A brief (but comprehensive) description of a typical problem. If I do not know 31 | > anything about the subject matter I can understand the problem and the problem 32 | > parameters. If I **do** know the subject matter I should be able to solve the 33 | > problem. 34 | 35 | ### Theory 36 | 37 | - Typically there are two things to cover in this section (these might not 38 | necessarily be separate): 39 | 1. A comprehensive (but not in depth) overview of the theory. 40 | 2. How the theory applies to our typical problem 41 | - Can assume a secondary education in mathematics. 42 | - Concepts that are taught in a typical University mathematics course can be 43 | assumed but should include reference texts as **footnotes**. 44 | - Concepts that are **potentially** not taught in either of the above should 45 | be introduced with gentle language, including potential subtle points at 46 | programming section (eg "this can be done numerically") and reference 47 | texts included as **footnotes**. 48 | - Use a smaller example (different to the typical problem) that can be done 49 | by hand. 50 | - Some theoretic concepts might be illustrated using the typical problem. 51 | 52 | 53 | ### Code 54 | 55 | - Give a brief (blog post style) overview of using each langauge to solve the 56 | problem. Possibly including smaller examples of code output. 57 | - A functional approach is used: everything is embedded in functions that are 58 | called. Justification/explanation of this will be written in "about this book 59 | section" (documentation, modularity and testability are the justification). 60 | TODO Put in best practice triangle 61 | - The two R and Python sections are **purposefully** written as near clones of 62 | each other. The alternative would be to write a single section with text that 63 | describes both the R and Python: however in multiple places this would need to 64 | clarify differences and similarities of implementation. This is not the 65 | purpose of the book thus there are two separate and independent sections. They 66 | are not written differently unless necessary. 67 | 68 | 69 | #### Solving with Python 70 | 71 | #### Solving with R 72 | 73 | ## Language 74 | 75 | - Avoid we/I. 76 | - In DES chapter we used digits for numbers (1, 2, 3 ... etc instead of one, 77 | two, three). 78 | - Dice is plural, die is singular. 79 | - Itemized, enumerated lists have `,` (or `;` if long) at the end and `.` at the 80 | end of the last 81 | - In text: Names of libraries are in plain text and capitalized. 82 | 83 | ## Research highlights 84 | 85 | > If I was undertaking a research project in this field, reading these three 86 | > papers would be a good starting point to build the literature. 87 | -------------------------------------------------------------------------------- /src/assets/abm-diagram.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | \usetikzlibrary{shapes} 5 | \usetikzlibrary{arrows} 6 | 7 | \begin{document} 8 | 9 | \begin{tikzpicture} 10 | 11 | \newcommand\agent{} 12 | \def\agent(#1, #2){% 13 | \draw (#1, #2+0.575) circle (0.2); 14 | \draw (#1, #2-0.125) -- (#1, #2+0.375); 15 | \draw (#1-0.25, #2-0.575) -- (#1, #2-0.125) -- (#1+0.25, #2-0.575); 16 | \draw (#1-0.25, #2+0.375) -- (#1, #2+0.125) -- (#1+0.25, #2+0.375); 17 | } 18 | 19 | \node at (0, 7) {\textbf{Environment}}; 20 | \node [cloud, draw, cloud puffs=40, cloud puff arc=100, minimum width = 22cm, minimum height = 13cm] at (0, 0) {}; 21 | 22 | \node at (-6.5, 1.25) {\textbf{Agent}}; 23 | \draw (-8.75, -1) rectangle (-2.5, 1); 24 | \agent(-7, 0); 25 | \node at (-3.5, 0.6) {Action 1}; 26 | \node at (-3.5, 0.2) {Action 2}; 27 | \node at (-3.5, -0.2) {Action 3}; 28 | \node at (-3.5, -0.6) {$\vdots$}; 29 | \node (startarrowdecide) at (-6.75, 0) {}; 30 | \node (startarrowupdate) at (-7.25, 0.1) {}; 31 | \draw[-triangle 90] (startarrowdecide) edge[out=45, in=135] (-4.3, 0); 32 | \node at (-5.5, 0.25) {\small \textit{3. decide}}; 33 | \draw[-triangle 90] (startarrowupdate) edge[out=135, in=-135, looseness=7] (-7.25, -0.1); 34 | \node at (-8, -0.5) {\small \textit{2. update}}; 35 | 36 | 37 | \draw[dashed] (5.5, 0) circle (4.5); 38 | \node (startarrowobserve) at (-5, 1) {}; 39 | \draw[-triangle 90] (startarrowobserve) edge[out=45, in=150] (1.75, 2.75); 40 | \node at (-1, 3) {\small \textit{1. observe}}; 41 | 42 | 43 | \agent(3, -1); 44 | \agent(3.5, -3); 45 | \agent(5, -0.5); 46 | \agent(5.5, 2); 47 | \agent(8, 0); 48 | 49 | \agent(0.5, -4); 50 | \agent(-1, -3); 51 | \agent(-4, -2.5); 52 | \agent(-6.5, -3.5); 53 | \agent(-2.5, -5); 54 | \agent(-9.25, -1.5); 55 | \agent(-8.75, 2); 56 | \agent(-5, 4); 57 | \agent(0, 0.5); 58 | \agent(-0.5, 4.5); 59 | 60 | \end{tikzpicture} 61 | 62 | \end{document} 63 | -------------------------------------------------------------------------------- /src/assets/barber-shop-continuous-markov-process/README.md: -------------------------------------------------------------------------------- 1 | # Barber shop - birth death diagram rates 2 | 3 | A birth death markov process diagram with rates. 4 | 5 | 6 | Currently used in: 7 | 8 | - Chapter 3 9 | -------------------------------------------------------------------------------- /src/assets/barber-shop-continuous-markov-process/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | 5 | \begin{document} 6 | 7 | \begin{tikzpicture} 8 | 9 | \node (A) at (0, 0) [draw, circle] {0}; 10 | \node (B) at (1, 0) [draw, circle] {1}; 11 | \node (C) at (2, 0) [draw, circle] {2}; 12 | \node (D) at (3, 0) [draw, circle] {3}; 13 | \node (E) at (4, 0) [draw, circle] {4}; 14 | \node (F) at (5, 0) [draw, circle] {5}; 15 | \node (G) at (6, 0) [draw, circle] {6}; 16 | 17 | \draw [->] (A) to [out=45, in=135, edge node={node [above] {\small 10}}] (B); 18 | \draw [->] (B) to [out=45, in=135, edge node={node [above] {\small 10}}] (C); 19 | \draw [->] (C) to [out=45, in=135, edge node={node [above] {\small 10}}] (D); 20 | \draw [->] (D) to [out=45, in=135, edge node={node [above] {\small 10}}] (E); 21 | \draw [->] (E) to [out=45, in=135, edge node={node [above] {\small 10}}] (F); 22 | \draw [->] (F) to [out=45, in=135, edge node={node [above] {\small 10}}] (G); 23 | 24 | \draw [->] (B) to [out=-135, in=-45, edge node={node [below] {\small 4}}] (A); 25 | \draw [->] (C) to [out=-135, in=-45, edge node={node [below] {\small 8}}] (B); 26 | \draw [->] (D) to [out=-135, in=-45, edge node={node [below] {\small 8}}] (C); 27 | \draw [->] (E) to [out=-135, in=-45, edge node={node [below] {\small 8}}] (D); 28 | \draw [->] (F) to [out=-135, in=-45, edge node={node [below] {\small 8}}] (E); 29 | \draw [->] (G) to [out=-135, in=-45, edge node={node [below] {\small 8}}] (F); 30 | 31 | 32 | \end{tikzpicture} 33 | 34 | \end{document} 35 | -------------------------------------------------------------------------------- /src/assets/barber-shop/README.md: -------------------------------------------------------------------------------- 1 | # Barber shop 2 | 3 | A queueing diagram for the Barber shop. 4 | 5 | 6 | Currently used in: 7 | 8 | - Chapter 3 9 | -------------------------------------------------------------------------------- /src/assets/barber-shop/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | 5 | \begin{document} 6 | 7 | \begin{tikzpicture} 8 | 9 | \node at (4, 3.5) {\LARGE{Barber shop}}; 10 | % Queue inspection 11 | \draw (-0.5, 0) -- (-0.5, 3); 12 | \draw (-0.5, 0) -- (8, 0); 13 | \draw (8, 0) -- (8, 3); 14 | \draw (-0.5, 3) -- (8, 3); 15 | \draw (4, 0) -- (4, 3); 16 | \draw (5, 0) -- (5, 3); 17 | \draw (6, 0) -- (6, 3); 18 | \draw (7, 0) -- (7, 3); 19 | % Service center inspection 20 | \node at (10, -0.1) {\Large $\approx 4/\text{hour}$}; 21 | \draw (10, -0.1) circle (1.5); 22 | \node at (10, 3.1) {\Large $\approx 4/\text{hour}$}; 23 | \draw (10, 3.1) circle (1.5); 24 | 25 | \node at (1.75, 1.5) {Waiting room capacity: 4}; 26 | 27 | % Routing 28 | \draw[-triangle 90] (-4.5, 1.5) -- (-1, 1.5); 29 | \node at (-2.5, 2) {\Large $\approx 10/\text{hour}$}; 30 | \draw[-triangle 90] (11.5, 1.5) -- (15.5, 1.5); 31 | 32 | 33 | \end{tikzpicture} 34 | 35 | \end{document} 36 | -------------------------------------------------------------------------------- /src/assets/bike-repair-shop.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | 5 | \begin{document} 6 | 7 | \begin{tikzpicture} 8 | 9 | \draw[fill=none, draw=none] (-5, -3.5) rectangle (34, 3.5); % 'invisible' bounding box to not cut off arrows 10 | 11 | \node at (4, 3.5) {\LARGE{Inspection counter}}; 12 | % Queue inspection 13 | \draw (-0.5, 0) -- (8, 0); 14 | \draw (8, 0) -- (8, 3); 15 | \draw (-0.5, 3) -- (8, 3); 16 | \draw (4, 0) -- (4, 3); 17 | \draw (5, 0) -- (5, 3); 18 | \draw (6, 0) -- (6, 3); 19 | \draw (7, 0) -- (7, 3); 20 | % Service center inspection 21 | \draw (10, 1.5) circle (1.5); 22 | 23 | 24 | \node at (22, 3.5) {\LARGE{Repair workshop}}; 25 | % Queue repair 26 | \draw (17.5, 0) -- (26, 0); 27 | \draw (26, 0) -- (26, 3); 28 | \draw (17.5, 3) -- (26, 3); 29 | \draw (22, 0) -- (22, 3); 30 | \draw (23, 0) -- (23, 3); 31 | \draw (24, 0) -- (24, 3); 32 | \draw (25, 0) -- (25, 3); 33 | % Service center repair 34 | \draw (28, 3.1) circle (1.5); 35 | \draw (28, -0.1) circle (1.5); 36 | 37 | % Routing 38 | \draw[-triangle 90] (-4.5, 1.5) -- (0, 1.5); 39 | \draw[-triangle 90] (11.75, 1.5) -- (18, 1.5); 40 | \draw[-triangle 90] (29.5, 1.5) -- (33.5, 1.5); 41 | \draw[-triangle 90] (13.5, 1.5) -- (13.5, -3) -- (33.5, -3); 42 | \node at (15, 2) {\Large 80\%}; 43 | \node at (15, -2.5) {\Large 20\%}; 44 | \node at (-2.5, 2) {\Large $Poisson(15)$}; 45 | \node at (10, 1.5) {\Large $Expon(20)$}; 46 | \node at (28, 3.1) {\Large $Expon(10)$}; 47 | \node at (28, -0.1) {\Large $Expon(10)$}; 48 | 49 | \end{tikzpicture} 50 | 51 | \end{document} 52 | -------------------------------------------------------------------------------- /src/assets/clashes.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | \usepackage{tikz} 3 | \begin{document} 4 | \begin{tikzpicture} 5 | 6 | \node (cc) at (0, 0) {$C_c$}; 7 | \node (co) at (0, 1.5) {$C_o$}; 8 | \node (bo) at (1.5, 0) {$B_o$}; 9 | \node (bc) at (1.5, 1.5) {$B_c$}; 10 | \node (ao) at (3, 0) {$A_o$}; 11 | \node (ac) at (3, 1.5) {$A_c$}; 12 | \node (do) at (4.5, 0) {$D_o$}; 13 | \node (dc) at (4.5, 1.5) {$D_c$}; 14 | 15 | \draw[thick, draw=red] (co) -- (cc); 16 | \draw[thick, draw=blue] (co) -- (bc); 17 | \draw[thick, draw=red] (co) -- (bo); 18 | \draw[thick, dashed, draw=blue] (co) -- (bo); 19 | \draw[thick, draw=red] (cc) -- (bo); 20 | \draw[thick, draw=blue] (bc) -- (bo); 21 | \draw[thick, dashed, draw=green!80!black] (bc) -- (bo); 22 | \draw[thick, draw=green!80!black] (bc) -- (ac); 23 | \draw[thick, draw=green!80!black] (bo) -- (ac); 24 | \draw[thick, draw=orange] (ac) -- (ao); 25 | \draw[thick, draw=orange] (ac) -- (dc); 26 | \draw[thick, draw=orange] (ao) -- (dc); 27 | \draw[thick, draw=yellow!80!orange] (dc) -- (do); 28 | 29 | \end{tikzpicture} 30 | \end{document} 31 | -------------------------------------------------------------------------------- /src/assets/final-tsp-tours-with-R/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/final-tsp-tours-with-R/main.pdf -------------------------------------------------------------------------------- /src/assets/final-tsp-tours-with-R/main.py: -------------------------------------------------------------------------------- 1 | from matplotlib.lines import Line2D 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import sklearn.metrics.pairwise 5 | import sympy as sym 6 | 7 | def plot_tour(ax, x, y, tour): 8 | xs = x[tour] 9 | ys = y[tour] 10 | ax.quiver(xs[:-1], ys[:-1], xs[1:]-xs[:-1], ys[1:]-ys[:-1], scale_units='xy', angles='xy', scale=1.8, width=0.0001, headwidth=180, headlength=180, headaxislength=140) 11 | ax.plot(xs, ys, c='black', linewidth=1) 12 | ax.scatter(xs, ys, s=170, zorder=3, c='white', edgecolor='black') 13 | ax.tick_params(labelsize=6) 14 | 15 | for i, (x, y) in enumerate(zip(xs[:-1], ys[:-1])): 16 | ax.annotate(str(tour[i]), (x, y), fontsize='small', ha='center', va='center') 17 | 18 | seed = 1 19 | number_of_stops = 13 20 | np.random.seed(seed) 21 | xs = np.random.randint(0, 100, number_of_stops) 22 | ys = np.random.randint(0, 100, number_of_stops) 23 | 24 | initial_candidate = [0, 9, 4, 7, 1, 2, 5, 3, 8, 6, 11, 12, 10, 0] 25 | swap_stops_solution = [0, 11, 4, 10, 6, 2, 7, 12, 9, 1, 3, 5, 8, 0] 26 | swap_paths_solution = [0, 7, 2, 6, 10, 4, 11, 12, 9, 1, 3, 5, 8, 0] 27 | 28 | # Draw plot 29 | fig, axarr = plt.subplots(1, 3, figsize=(13, 4)) 30 | plot_tour(ax=axarr[0], x=xs, y=ys, tour=initial_candidate) 31 | axarr[0].set_title("Initial candidate") 32 | 33 | plot_tour(ax=axarr[1], x=xs, y=ys, tour=swap_stops_solution) 34 | axarr[1].set_title("Swapping stops after 1000 iterations") 35 | 36 | plot_tour(ax=axarr[2], x=xs, y=ys, tour=swap_paths_solution) 37 | axarr[2].set_title("Reversing path after 1000 iterations") 38 | 39 | plt.tight_layout() 40 | plt.savefig("main.pdf") 41 | -------------------------------------------------------------------------------- /src/assets/final-tsp-tours-with-python/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/final-tsp-tours-with-python/main.pdf -------------------------------------------------------------------------------- /src/assets/final-tsp-tours-with-python/main.py: -------------------------------------------------------------------------------- 1 | from matplotlib.lines import Line2D 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import sklearn.metrics.pairwise 5 | import sympy as sym 6 | 7 | def plot_tour(ax, x, y, tour): 8 | xs = x[tour] 9 | ys = y[tour] 10 | ax.quiver(xs[:-1], ys[:-1], xs[1:]-xs[:-1], ys[1:]-ys[:-1], scale_units='xy', angles='xy', scale=1.8, width=0.0001, headwidth=180, headlength=180, headaxislength=140) 11 | ax.plot(xs, ys, c='black', linewidth=1) 12 | ax.scatter(xs, ys, s=170, zorder=3, c='white', edgecolor='black') 13 | ax.tick_params(labelsize=6) 14 | 15 | for i, (x, y) in enumerate(zip(xs[:-1], ys[:-1])): 16 | ax.annotate(str(tour[i]), (x, y), fontsize='small', ha='center', va='center') 17 | 18 | seed = 1 19 | number_of_stops = 13 20 | np.random.seed(seed) 21 | xs = np.random.randint(0, 100, number_of_stops) 22 | ys = np.random.randint(0, 100, number_of_stops) 23 | 24 | initial_candidate = [0, 7, 12, 5, 11, 3, 9, 2, 8, 10, 4, 1, 6, 0] 25 | swap_stops_solution = [0, 7, 2, 8, 5, 3, 1, 9, 12, 11, 4, 10, 6, 0] 26 | swap_paths_solution = [0, 8, 5, 3, 1, 9, 12, 11, 4, 10, 6, 2, 7, 0] 27 | 28 | # Draw plot 29 | fig, axarr = plt.subplots(1, 3, figsize=(13, 4)) 30 | plot_tour(ax=axarr[0], x=xs, y=ys, tour=initial_candidate) 31 | axarr[0].set_title("Initial candidate") 32 | 33 | plot_tour(ax=axarr[1], x=xs, y=ys, tour=swap_stops_solution) 34 | axarr[1].set_title("Swapping stops after 1000 iterations") 35 | 36 | plot_tour(ax=axarr[2], x=xs, y=ys, tour=swap_paths_solution) 37 | axarr[2].set_title("Reversing path after 1000 iterations") 38 | 39 | plt.tight_layout() 40 | plt.savefig("main.pdf") 41 | -------------------------------------------------------------------------------- /src/assets/neighbourhood_search_flow_diagram/README.md: -------------------------------------------------------------------------------- 1 | # Neighbourhood search flow diagram 2 | 3 | A flow chart for neighbourhood search as applied to the TSP 4 | 5 | 6 | Currently used in: 7 | 8 | - Chapter 9 9 | -------------------------------------------------------------------------------- /src/assets/neighbourhood_search_flow_diagram/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/neighbourhood_search_flow_diagram/main.pdf -------------------------------------------------------------------------------- /src/assets/neighbourhood_search_flow_diagram/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{amsmath} 4 | \usepackage{tikz} 5 | \usetikzlibrary{shapes,arrows, calc} 6 | 7 | \begin{document} 8 | 9 | 10 | % Define block styles 11 | \tikzstyle{decision} = [diamond, draw, 12 | text width=4.5em, text badly centered, node distance=3cm, inner sep=0pt] 13 | \tikzstyle{block} = [rectangle, draw, 14 | text width=5em, text centered, rounded corners, minimum height=4em] 15 | \tikzstyle{line} = [draw, -latex'] 16 | \tikzstyle{cloud} = [draw, ellipse, node distance=4cm, 17 | minimum height=2em] 18 | 19 | \begin{tikzpicture}[node distance = 2cm, auto] 20 | % Place nodes 21 | \node [block] (init) {Obtain initial tour \(t\)}; 22 | \node [cloud, left of=init] (distance) {distance matrix}; 23 | \node [block, below of=init] (neighbour) {Obtain \(\tilde t\in N(t)\)}; 24 | \node [decision, below of=neighbour] (stop?) {Stop?}; 25 | \node [block, left of=neighbour, text width=3cm, node distance=3cm] (update) 26 | {\(t \leftarrow \text{argmin}_{t, \tilde t}(C)\)}; 27 | \node [block, below of=stop?, node distance=3cm] (stop) {Stop.}; 28 | % Draw edges 29 | \path [line] (stop?) -| node [near start] {no} (update); 30 | \path [line] (update) |- (neighbour); 31 | \path [line] (stop?) -- node [near start] {yes} (stop); 32 | 33 | \path [line] (init) -- (neighbour); 34 | \path [line] (neighbour) -- (stop?); 35 | \path [line,dashed] (distance) -- (init); 36 | \end{tikzpicture} 37 | 38 | 39 | \end{document} 40 | -------------------------------------------------------------------------------- /src/assets/paint_LP.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | \usepackage{tikz} 3 | \begin{document} 4 | \begin{tikzpicture} 5 | \draw[draw=none, fill=black!10] (0, 0) -- (0, 4) -- (12/7, 20/7) -- (4, 0) -- cycle; 6 | \draw[step=1cm,gray,very thin] (-1.9,-1.9) grid (7.9,7.9); 7 | \draw[ultra thick] (-1.9, 0) -- (7.9, 0); 8 | \draw[ultra thick] (0, -1.6) -- (0, 7.9); 9 | \node at (7, -0.5) {\LARGE A}; 10 | \node at (-0.5, 7) {\LARGE B}; 11 | \draw[ultra thick, black] (-0.5, 13/3) -- (27/4, -0.5); 12 | \draw[ultra thick, black] (-0.5, 45/8) -- (22/5, -0.5); 13 | \node[text=black] at (10, 5) {\LARGE $4A + 6B \leq 24$}; 14 | \node[text=black] at (10, 4) {\LARGE $5A + 4B \leq 20$}; 15 | \node[text=black] at (10, 3) {\LARGE $A \geq 0$}; 16 | \node[text=black] at (10, 2) {\LARGE $B \geq 0$}; 17 | 18 | \draw[ultra thick, black!40, dashed] (-0.5, 35/12) -- (18/5, -0.5); 19 | \draw[ultra thick, ->, black!40] (0.7, 0.7) -- (1.7, 1.9); 20 | \node[style={minimum size=0.2cm,draw=black!60,fill=black!40,shape=circle}] at (12/7, 20/7) {}; 21 | \end{tikzpicture} 22 | \end{document} 23 | -------------------------------------------------------------------------------- /src/assets/process_based.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | 5 | \begin{document} 6 | 7 | \begin{tikzpicture} 8 | 9 | \node[circle, draw=black, fill=black] (arrive) at (0, 0) {}; 10 | \node[rotate=30, anchor=north west] at (0, 0.65) {arrive}; 11 | \node[circle, draw=black, fill=black] (inspection_seize) at (1.5, 0) {}; 12 | \node[rotate=30, anchor=north west] at (1.5, 0.65) {seize inspection}; 13 | \node[circle, draw=black, fill=black] (inspection_delay) at (3, 0) {}; 14 | \node[rotate=30, anchor=north west] at (3., 0.65) {delay}; 15 | \node[circle, draw=black, fill=black] (inspection_release) at (4.5, 0) {}; 16 | \node[rotate=30, anchor=north west] at (4.5, 0.65) {release inspection}; 17 | \node[circle, draw=black, fill=black] (inspection_leave) at (10.5, 0) {}; 18 | \node[rotate=30, anchor=north west] at (10.5, 0.65) {leave}; 19 | \node[circle, draw=black, fill=black] (repair_seize) at (6, -1.5) {}; 20 | \node[rotate=-30, anchor=south west] at (6., -2.15) {seize repair}; 21 | \node[circle, draw=black, fill=black] (repair_delay) at (7.5, -1.5) {}; 22 | \node[rotate=-30, anchor=south west] at (7.5, -2.15) {delay}; 23 | \node[circle, draw=black, fill=black] (repair_release) at (9, -1.5) {}; 24 | \node[rotate=-30, anchor=south west] at (9., -2.15) {release repair}; 25 | \node[circle, draw=black, fill=black] (repair_leave) at (10.5, -1.5) {}; 26 | \node[rotate=-30, anchor=south west] at (10.5, -2.15) {leave}; 27 | 28 | \draw[thick] (arrive) -- (inspection_seize); 29 | \draw[thick] (inspection_seize) -- (inspection_delay); 30 | \draw[thick] (inspection_delay) -- (inspection_release); 31 | \draw[thick] (inspection_release) -- (inspection_leave); 32 | \draw[thick, rounded corners] (inspection_release) -- (4.5, -1.5) -- (repair_seize); 33 | \draw[thick] (repair_seize) -- (repair_delay); 34 | \draw[thick] (repair_delay) -- (repair_release); 35 | \draw[thick] (repair_release) -- (repair_leave); 36 | 37 | \end{tikzpicture} 38 | 39 | \end{document} 40 | -------------------------------------------------------------------------------- /src/assets/python_schelling_0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/python_schelling_0.pdf -------------------------------------------------------------------------------- /src/assets/python_schelling_100.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/python_schelling_100.pdf -------------------------------------------------------------------------------- /src/assets/python_schelling_20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/python_schelling_20.pdf -------------------------------------------------------------------------------- /src/assets/rsd-triangle/README.md: -------------------------------------------------------------------------------- 1 | A diagram showing the relationship between modularisation, documentation and 2 | testing. Used in the introductory chapter to describe how the code is written in 3 | the book. 4 | 5 | ## To compile 6 | 7 | To compile the LaTeX document: 8 | 9 | $ latexmk --xelatex main.tex 10 | 11 | To convert `main.pdf` to `png`: 12 | 13 | $ convert -density 300 main.pdf -quality 90 main.png 14 | -------------------------------------------------------------------------------- /src/assets/rsd-triangle/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[convert={density=1200,size=20000x20000,outext=.png}]{standalone} 2 | 3 | \usepackage{tikz} 4 | \usetikzlibrary{arrows} 5 | \usetikzlibrary{decorations.markings} 6 | \usetikzlibrary{calc} 7 | \usetikzlibrary{decorations.text} 8 | 9 | \begin{document} 10 | 11 | \begin{tikzpicture} 12 | 13 | \def\downshift#1{\raisebox{-2.5ex}} 14 | \def\upshift#1{\raisebox{2.5ex}} 15 | 16 | \draw[draw=none, fill=white] (-4.5, -4.5) rectangle (4.5, 4.5); 17 | 18 | \node[circle, draw=black, fill=white, minimum size=2.5cm] (M) at (0, 3.8) {Modularity}; 19 | \node[circle, draw=black, fill=white, minimum size=2.5cm] (D) at (-3.398075, -1.5) {Documentation}; 20 | \node[circle, draw=black, fill=white, minimum size=2.5cm] (T) at (3.398075, -1.5) {Testing}; 21 | 22 | \draw[-triangle 45] (M) -- (T) node[pos=0.5, sloped, align=center] {\tiny makes possible, \& test failures\\\tiny become more meaningful}; 23 | \draw[triangle 45-] (D) edge[out=105,in=195, postaction={decorate,decoration={text along path,text align=center,text={|\tiny\upshift|increases accuracy}}}] (M); 24 | \draw[-triangle 45] (D) edge[out=-45,in=-135, postaction={decorate,decoration={text along path,text align=center,text={|\tiny\downshift|clarifies purpose, and shows expected outcome}}}] (T); 25 | \draw[-triangle 45] (D) -- (M) node[pos=0.5, sloped, above, align=center] {\tiny helps identify components}; 26 | \draw[triangle 45-] (M) edge[out=-15,in=75, postaction={decorate,decoration={text along path,text align=center,text={|\tiny\upshift|checks modules individually and together}}}] (T); 27 | \draw[-triangle 45] (T) -- (D) node[pos=0.5, above] {\tiny confirms}; 28 | 29 | \end{tikzpicture} 30 | 31 | \end{document} 32 | -------------------------------------------------------------------------------- /src/assets/schelling_R_0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/schelling_R_0.pdf -------------------------------------------------------------------------------- /src/assets/schelling_R_100.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/schelling_R_100.pdf -------------------------------------------------------------------------------- /src/assets/schelling_R_20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/schelling_R_20.pdf -------------------------------------------------------------------------------- /src/assets/schelling_happy.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | 5 | \begin{document} 6 | 7 | \begin{tikzpicture} 8 | 9 | \draw (0, -0.1) -- (0, 3.1); 10 | \draw (1, -0.1) -- (1, 3.1); 11 | \draw (2, -0.1) -- (2, 3.1); 12 | \draw (3, -0.1) -- (3, 3.1); 13 | 14 | \draw (-0.1, 0) -- (3.1, 0); 15 | \draw (-0.1, 1) -- (3.1, 1); 16 | \draw (-0.1, 2) -- (3.1, 2); 17 | \draw (-0.1, 3) -- (3.1, 3); 18 | 19 | \draw[fill=black!20] (0.5, 0.5) circle (0.4); 20 | \draw[fill=black!20] (1.5, 0.5) circle (0.4); 21 | \draw[fill=black!20] (2.5, 0.5) circle (0.4); 22 | \draw (0.5, 1.5) circle (0.4); 23 | \draw[fill=black!20] (1.5, 1.5) circle (0.4); 24 | \draw[fill=black!20] (2.5, 1.5) circle (0.4); 25 | \draw[fill=black!20] (0.5, 2.5) circle (0.4); 26 | \draw[fill=black!20] (1.5, 2.5) circle (0.4); 27 | \draw (2.5, 2.5) circle (0.4); 28 | 29 | \draw[fill=black] (1.35, 1.65) circle (0.02); 30 | \draw[fill=black] (1.65, 1.65) circle (0.02); 31 | \draw (1.3, 1.45) edge[out=-90, in=-90, looseness=1.75] (1.7, 1.45); 32 | 33 | \end{tikzpicture} 34 | 35 | \end{document} 36 | -------------------------------------------------------------------------------- /src/assets/schelling_unhappy.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | 5 | \begin{document} 6 | 7 | \begin{tikzpicture} 8 | 9 | \draw (0, -0.1) -- (0, 3.1); 10 | \draw (1, -0.1) -- (1, 3.1); 11 | \draw (2, -0.1) -- (2, 3.1); 12 | \draw (3, -0.1) -- (3, 3.1); 13 | 14 | \draw (-0.1, 0) -- (3.1, 0); 15 | \draw (-0.1, 1) -- (3.1, 1); 16 | \draw (-0.1, 2) -- (3.1, 2); 17 | \draw (-0.1, 3) -- (3.1, 3); 18 | 19 | \draw (0.5, 0.5) circle (0.4); 20 | \draw (1.5, 0.5) circle (0.4); 21 | \draw (2.5, 0.5) circle (0.4); 22 | \draw (0.5, 1.5) circle (0.4); 23 | \draw[fill=black!20] (1.5, 1.5) circle (0.4); 24 | \draw (2.5, 1.5) circle (0.4); 25 | \draw (0.5, 2.5) circle (0.4); 26 | \draw[fill=black!20] (1.5, 2.5) circle (0.4); 27 | \draw[fill=black!20] (2.5, 2.5) circle (0.4); 28 | 29 | \draw[fill=black] (1.35, 1.65) circle (0.02); 30 | \draw[fill=black] (1.65, 1.65) circle (0.02); 31 | \draw (1.3, 1.25) edge[out=90, in=90, looseness=1.75] (1.7, 1.25); 32 | 33 | \end{tikzpicture} 34 | 35 | \end{document} 36 | -------------------------------------------------------------------------------- /src/assets/sd_vaccine_plots/main.py: -------------------------------------------------------------------------------- 1 | from scipy.integrate import solve_ivp 2 | import matplotlib.pyplot as plt 3 | plt.style.use('seaborn-whitegrid') 4 | 5 | def derivatives(t, y, vaccine_rate, birth_rate=0.01): 6 | """Defines the system of differential equations that 7 | describe the epidemiology model. 8 | 9 | Args: 10 | t: a positive float 11 | y: a tuple of three integers 12 | vaccine_rate: a positive float <= 1 13 | birth_rate: a positive float <= 1 14 | 15 | Returns: 16 | A tuple containing dS, dI, and dR 17 | """ 18 | infection_rate = 0.3 19 | recovery_rate = 0.02 20 | death_rate = 0.01 21 | S, I, R = y 22 | N = S + I + R 23 | dSdt = ( 24 | -((infection_rate * S * I) / N) 25 | + ((1 - vaccine_rate) * birth_rate * N) 26 | - (death_rate * S) 27 | ) 28 | dIdt = ( 29 | ((infection_rate * S * I) / N) 30 | - (recovery_rate * I) 31 | - (death_rate * I) 32 | ) 33 | dRdt = ( 34 | (recovery_rate * I) 35 | - (death_rate * R) 36 | + (vaccine_rate * birth_rate * N) 37 | ) 38 | return dSdt, dIdt, dRdt 39 | 40 | def integrate_ode( 41 | derivative_function, 42 | t_span, 43 | y0=(2999, 1, 0), 44 | vaccine_rate=0.85, 45 | birth_rate=0.01, 46 | ): 47 | """Numerically solve the system of differential equations. 48 | 49 | Args: 50 | derivative_function: a function returning a tuple 51 | of three floats 52 | t_span: endpoints oif the time range to integrate over 53 | y0: a tuple of three integers (default: (2999, 1, 0)) 54 | vaccine_rate: a positive float <= 1 (default: 0.85) 55 | birth_rate: a positive float <= 1 (default: 0.01) 56 | 57 | Returns: 58 | A tuple of three arrays 59 | """ 60 | sol = solve_ivp( 61 | derivative_function, 62 | t_span, 63 | y0, 64 | args=(vaccine_rate, birth_rate), 65 | ) 66 | ts, S, I, R = sol.t, sol.y[0], sol.y[1], sol.y[2] 67 | return ts, S, I, R 68 | 69 | t_span = [0, 730] 70 | t, S, I, R = integrate_ode(derivatives, t_span, vaccine_rate=0.0) 71 | 72 | fig, ax = plt.subplots(1, figsize=(10, 5)) 73 | ax.plot(t, S, label='Susceptible', c='black', linestyle='solid', linewidth=1.75) 74 | ax.plot(t, I, label='Infected', c='black', linestyle='dotted', linewidth=1.75) 75 | ax.plot(t, R, label='Recovered', c='black', linestyle='dashed', linewidth=1.75) 76 | ax.legend(fontsize=14, frameon=True, ncol=3, bbox_to_anchor=(0.85, 1.13)) 77 | ax.set_xlabel('Time', fontsize=14) 78 | ax.set_ylabel('People', fontsize=14) 79 | fig.savefig("plot_no_vaccine.pdf") 80 | 81 | t, S, I, R = integrate_ode(derivatives, t_span, vaccine_rate=0.85) 82 | 83 | fig, ax = plt.subplots(1, figsize=(10, 5)) 84 | ax.plot(t, S, label='Susceptible', c='black', linestyle='solid', linewidth=1.75) 85 | ax.plot(t, I, label='Infected', c='black', linestyle='dotted', linewidth=1.75) 86 | ax.plot(t, R, label='Recovered', c='black', linestyle='dashed', linewidth=1.75) 87 | ax.legend(fontsize=14, frameon=True, ncol=3, bbox_to_anchor=(0.85, 1.13)) 88 | ax.set_xlabel('Time', fontsize=14) 89 | ax.set_ylabel('People', fontsize=14) 90 | fig.savefig("plot_with_vaccine.pdf") -------------------------------------------------------------------------------- /src/assets/sd_vaccine_plots/plot_no_vaccine.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/sd_vaccine_plots/plot_no_vaccine.pdf -------------------------------------------------------------------------------- /src/assets/sd_vaccine_plots/plot_with_vaccine.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/sd_vaccine_plots/plot_with_vaccine.pdf -------------------------------------------------------------------------------- /src/assets/stock_flow_diagram.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{tikz} 4 | \usetikzlibrary{arrows} 5 | 6 | \begin{document} 7 | 8 | \begin{tikzpicture} 9 | 10 | % Stock of susceptibles 11 | \draw (0, 0) rectangle (3, 2); 12 | \node at (1.5, 1) {\LARGE $S$}; 13 | 14 | % Stock of Infecteds 15 | \draw (6, 0) rectangle (9, 2); 16 | \node at (7.5, 1) {\LARGE $I$}; 17 | 18 | % Stock of Recoverds 19 | \draw (12, 0) rectangle (15, 2); 20 | \node at (13.5, 1) {\LARGE $R$}; 21 | 22 | % Flow into S 23 | \draw (1.25, 2.25) -- (1.5, 2) -- (1.75, 2.25); % arrow 24 | \draw (1.425, 2.075) -- (1.425, 4.5); % pipe left 25 | \draw (1.575, 2.075) -- (1.575, 4.5); % pipe right 26 | \draw[fill=white] (1.3, 3.625) -- (1.7, 3.375) -- (1.7, 3.625) -- (1.3, 3.375) -- cycle; % tap 27 | 28 | % Flow into R 29 | \draw (13.25, 2.25) -- (13.5, 2) -- (13.75, 2.25); % arrow 30 | \draw (13.425, 2.075) -- (13.425, 4.5); % pipe left 31 | \draw (13.575, 2.075) -- (13.575, 4.5); % pipe right 32 | \draw[fill=white] (13.3, 3.625) -- (13.7, 3.375) -- (13.7, 3.625) -- (13.3, 3.375) -- cycle; % tap 33 | 34 | % Flow out of S 35 | \draw (1.25, -2.25) -- (1.5, -2.5) -- (1.75, -2.25); % arrow 36 | \draw (1.425, -2.425) -- (1.425, 0); % pipe left 37 | \draw (1.575, -2.425) -- (1.575, 0); % pipe right 38 | \draw[fill=white] (1.3, -0.875) -- (1.7, -1.125) -- (1.7, -0.875) -- (1.3, -1.125) -- cycle; % tap 39 | 40 | % Flow out of I 41 | \draw (7.25, -2.25) -- (7.5, -2.5) -- (7.75, -2.25); % arrow 42 | \draw (7.425, -2.425) -- (7.425, 0); % pipe left 43 | \draw (7.575, -2.425) -- (7.575, 0); % pipe right 44 | \draw[fill=white] (7.3, -0.875) -- (7.7, -1.125) -- (7.7, -0.875) -- (7.3, -1.125) -- cycle; % tap 45 | 46 | % Flow out of R 47 | \draw (13.25, -2.25) -- (13.5, -2.5) -- (13.75, -2.25); % arrow 48 | \draw (13.425, -2.425) -- (13.425, 0); % pipe left 49 | \draw (13.575, -2.425) -- (13.575, 0); % pipe right 50 | \draw[fill=white] (13.3, -0.875) -- (13.7, -1.125) -- (13.7, -0.875) -- (13.3, -1.125) -- cycle; % tap 51 | 52 | 53 | % Flow S-I 54 | \draw (5.75, 0.75) -- (6, 1) -- (5.75, 1.25); % arrow 55 | \draw (3, 0.925) -- (5.925, 0.925); % pipe bottom 56 | \draw (3, 1.075) -- (5.925, 1.075); % pipe top 57 | \draw[fill=white] (4.6, 1.2) -- (4.4, 1.2) -- (4.6, 0.8) -- (4.4, 0.8) -- cycle; % (4.5, 1) 58 | 59 | % Flow I-R 60 | \draw (11.75, 0.75) -- (12, 1) -- (11.75, 1.25); % arrow 61 | \draw (9, 0.925) -- (11.925, 0.925); % pipe bottom 62 | \draw (9, 1.075) -- (11.925, 1.075); % pipe top 63 | \draw[fill=white] (10.6, 1.2) -- (10.4, 1.2) -- (10.6, 0.8) -- (10.4, 0.8) -- cycle; % (4.5, 1) 64 | 65 | 66 | \node[inner sep=0.35cm] (b) at (7.5, 5) {\LARGE $b$}; 67 | \node[inner sep=0.35cm] (v) at (7.5, 6) {\LARGE $v$}; 68 | \node[inner sep=0.35cm] (d) at (6, -3.5) {\LARGE $d$}; 69 | \node[inner sep=0.35cm] (a) at (3.5, -1.5) {\LARGE $\alpha$}; 70 | \node[inner sep=0.35cm] (r) at (9.5, -1.5) {\LARGE $r$}; 71 | \node[inner sep=0.35cm] (tapinS) at (1.5, 3.5) {}; 72 | \node[inner sep=0.35cm] (tapinR) at (13.5, 3.5) {}; 73 | \node[inner sep=0.35cm] (tapoutS) at (1.5, -1) {}; 74 | \node[inner sep=0.35cm] (tapoutI) at (7.5, -1) {}; 75 | \node[inner sep=0.35cm] (tapoutR) at (13.5, -1) {}; 76 | \node[inner sep=0.35cm] (tapSI) at (4.5, 1) {}; 77 | \node[inner sep=0.35cm] (tapIR) at (10.5, 1) {}; 78 | \node[inner sep=0.35cm] (I) at (6.75, 0) {}; 79 | 80 | 81 | \draw[-triangle 90] (v) edge[out=180, in=45] (tapinS); 82 | \draw[-triangle 90] (v) edge[out=0, in=135] (tapinR); 83 | \draw[-triangle 90] (b) edge[out=-150, in=0] (tapinS); 84 | \draw[-triangle 90] (b) edge[out=-30, in=190] (tapinR); 85 | \draw[-triangle 90] (a) edge[out=100, in=-150] (tapSI); 86 | \draw[-triangle 90] (r) edge[out=30, in=-80] (tapIR); 87 | \draw[-triangle 90] (d) edge[out=180, in=-60] (tapoutS); 88 | \draw[-triangle 90] (d) edge[out=90, in=-150] (tapoutI); 89 | \draw[-triangle 90] (d) edge[out=0, in=-120] (tapoutR); 90 | \draw[-triangle 90] (I) edge[out=-150, in=-60] (tapSI); 91 | 92 | \node at (5, 6) {\large $-$}; 93 | \node at (5, 4.1) {\large $+$}; 94 | \node at (10, 6) {\large $+$}; 95 | \node at (10, 4.1) {\large $+$}; 96 | \node at (4.5, -3.1) {\large $+$}; 97 | \node at (9.5, -3.1) {\large $+$}; 98 | \node at (6.55, -2) {\large $+$}; 99 | \node at (3.75, 0) {\large $+$}; 100 | \node at (5.3, 0.25) {\large $+$}; 101 | \node at (10.25, -0.25) {\large $+$}; 102 | 103 | \end{tikzpicture} 104 | 105 | \end{document} 106 | -------------------------------------------------------------------------------- /src/assets/taxi-firm-game/README.md: -------------------------------------------------------------------------------- 1 | # Taxi firm game 2 | 3 | A diagram showing the various components of a game. 4 | 5 | 6 | Currently used in: 7 | 8 | - Chapter 6 9 | -------------------------------------------------------------------------------- /src/assets/taxi-firm-game/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/taxi-firm-game/main.pdf -------------------------------------------------------------------------------- /src/assets/taxi-firm-game/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass{standalone} 2 | 3 | \usepackage{amsmath} 4 | \usepackage{tikz} 5 | \usepackage{varwidth} 6 | \usetikzlibrary{calc} 7 | \usetikzlibrary{shapes} 8 | 9 | \begin{document} 10 | 11 | \begin{tikzpicture} 12 | 13 | \node (summary) at (0, 0) {2 players \(\implies\) 14 | {\begin{varwidth}{\linewidth}\begin{itemize} 15 | \item 2 action sets \({\color{blue!80}{A_1}}, \color{red!80}{A_2}\); 16 | \item 2 payoff functions, represented by matrices \(M, N\). 17 | \end{itemize}\end{varwidth}} 18 | }; 19 | 20 | 21 | \node (M) at ($(summary) + (-4, -2.5)$) { 22 | \(M= 23 | \begin{pmatrix} 24 | 1 & 1 / 2 & 1 / 3 \\ 25 | 3 / 2 & 19 / 20 & 1 / 2 \\ 26 | 5 / 3 & 4 / 5 & 17 / 20\\ 27 | \end{pmatrix}\) 28 | }; 29 | \node [draw=blue!80, align=left] (A1) at ($(M) + (-3.25, 0)$) {1 taxi \\ 2 taxis \\ 3 taxis}; 30 | \node [draw=red!80, align=left] (A2) at ($(M) + (0.4, 1)$) {1\hspace{.75cm} 2 \hspace{.75cm}3}; 31 | 32 | \node (N) at ($(summary) + (4, -2.5)$) { 33 | \(N= 34 | \begin{pmatrix} 35 | 1 & 3 / 2 & 5 / 3 \\ 36 | 1 / 2 & 19 / 20 & 4 / 5 \\ 37 | 1 / 3 & 1 / 2 & 17 / 20\\ 38 | \end{pmatrix} 39 | \)}; 40 | \node [draw=blue!80, align=left] (A1) at ($(N) + (-3.25, 0)$) {1 taxi \\ 2 taxis \\ 3 taxis}; 41 | \node [draw=red!80, align=left] (A2) at ($(N) + (0.4, 1)$) {1\hspace{.75cm} 2 \hspace{.75cm}3}; 42 | 43 | 44 | \node (outcome) [draw, thick] at ($(summary) + (0, -4)$) {Outcome of first firm choosing 1 and 45 | second firm choosing 3 taxis}; 46 | 47 | \draw [draw, very thick, ->] (outcome) -- ($(M) + (1.75, .3)$); 48 | \draw [draw, very thick, ->] (outcome) -- ($(N) + (1, .3)$); 49 | \end{tikzpicture} 50 | 51 | \end{document} 52 | -------------------------------------------------------------------------------- /src/assets/tsp-effect-of-neighbourhood-operators/README.md: -------------------------------------------------------------------------------- 1 | # Travelling salesman problem 2 | 3 | A diagram showing 3 collections of stops 4 | 5 | 6 | Currently used in: 7 | 8 | - Chapter 9 9 | -------------------------------------------------------------------------------- /src/assets/tsp-effect-of-neighbourhood-operators/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/tsp-effect-of-neighbourhood-operators/main.pdf -------------------------------------------------------------------------------- /src/assets/tsp-effect-of-neighbourhood-operators/main.py: -------------------------------------------------------------------------------- 1 | from matplotlib.lines import Line2D 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import sklearn.metrics.pairwise 5 | import sympy as sym 6 | 7 | def plot_tour(ax, x, y, tour): 8 | xs = x[tour] 9 | ys = y[tour] 10 | ax.quiver(xs[:-1], ys[:-1], xs[1:]-xs[:-1], ys[1:]-ys[:-1], scale_units='xy', angles='xy', scale=1.8, width=0.0001, headwidth=180, headlength=180, headaxislength=140) 11 | ax.plot(xs, ys, c='black', linewidth=1) 12 | ax.scatter(xs, ys, s=170, zorder=3, c='white', edgecolor='black') 13 | ax.tick_params(labelsize=6) 14 | 15 | for i, (x, y) in enumerate(zip(xs[:-1], ys[:-1])): 16 | ax.annotate(str(tour[i]), (x, y), fontsize='small', ha='center', va='center') 17 | 18 | xs = np.array([0, 1, 2, 4, 3, 2, 1]) 19 | ys = np.array([0, 0.5, 3, 2.75, 1, 2, 2.75]) 20 | 21 | initial_candidate = [0, 1, 2, 3, 4, 5, 6, 0] 22 | swap_stops_candidate = [0, 1, 5, 3, 4, 2, 6, 0] 23 | swap_paths_candidate = [0, 1, 5, 4, 3, 2, 6, 0] 24 | 25 | # Draw plot 26 | fig, axarr = plt.subplots(1, 3, figsize=(13, 4)) 27 | plot_tour(ax=axarr[0], x=xs, y=ys, tour=initial_candidate) 28 | axarr[0].set_title("Initial candidate") 29 | 30 | plot_tour(ax=axarr[1], x=xs, y=ys, tour=swap_stops_candidate) 31 | axarr[1].set_title("Swapping two stops") 32 | 33 | plot_tour(ax=axarr[2], x=xs, y=ys, tour=swap_paths_candidate) 34 | axarr[2].set_title("Reversing path between two stops") 35 | 36 | plt.tight_layout() 37 | plt.savefig("main.pdf") 38 | -------------------------------------------------------------------------------- /src/assets/tsp/README.md: -------------------------------------------------------------------------------- 1 | # Travelling salesman problem 2 | 3 | A diagram showing 3 collections of stops 4 | 5 | 6 | Currently used in: 7 | 8 | - Chapter 9 9 | -------------------------------------------------------------------------------- /src/assets/tsp/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/assets/tsp/main.pdf -------------------------------------------------------------------------------- /src/assets/tsp/main.py: -------------------------------------------------------------------------------- 1 | from matplotlib.lines import Line2D 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import sklearn.metrics.pairwise 5 | import sympy as sym 6 | 7 | seed = 1 8 | number_of_stops = 13 9 | np.random.seed(seed) 10 | xs = np.random.randint(0, 100, number_of_stops) 11 | ys = np.random.randint(0, 100, number_of_stops) 12 | 13 | 14 | # Draw plot 15 | fig, ax = plt.subplots(1, figsize=(5, 5)) 16 | ax.scatter(xs, ys, s=170, zorder=3, c='white', edgecolor='black') 17 | ax.tick_params(labelsize=6) 18 | ax.set_title("Map of all stops") 19 | plt.savefig("main.pdf") 20 | 21 | # Write distance matrices 22 | with open("main.tex", "w") as f: 23 | f.write(r"\begin{equation}") 24 | f.write(f"d=") 25 | distance_matrix = sklearn.metrics.pairwise.euclidean_distances(tuple(zip(xs, ys))) 26 | distance_matrix = np.round(a=distance_matrix, decimals=0).astype('int') 27 | latex = sym.latex(sym.Matrix(distance_matrix)) 28 | f.write(latex) 29 | f.write(f"\label{{eqn:tsp}}") 30 | f.write(r"\end{equation}") 31 | f.write("\n") 32 | -------------------------------------------------------------------------------- /src/assets/tsp/main.tex: -------------------------------------------------------------------------------- 1 | \begin{equation}d=\left[\begin{array}{ccccccccccccc}0 & 35 & 35 & 29 & 70 & 35 & 42 & 27 & 24 & 44 & 58 & 71 & 69\\35 & 0 & 67 & 32 & 72 & 40 & 71 & 56 & 36 & 11 & 66 & 70 & 37\\35 & 67 & 0 & 63 & 64 & 68 & 11 & 12 & 56 & 77 & 48 & 67 & 94\\29 & 32 & 63 & 0 & 93 & 8 & 71 & 56 & 8 & 33 & 84 & 93 & 69\\70 & 72 & 64 & 93 & 0 & 101 & 56 & 56 & 92 & 81 & 16 & 5 & 69\\35 & 40 & 68 & 8 & 101 & 0 & 76 & 62 & 11 & 39 & 91 & 101 & 76\\42 & 71 & 11 & 71 & 56 & 76 & 0 & 15 & 65 & 81 & 40 & 60 & 94\\27 & 56 & 12 & 56 & 56 & 62 & 15 & 0 & 50 & 66 & 41 & 58 & 82\\24 & 36 & 56 & 8 & 92 & 11 & 65 & 50 & 0 & 39 & 81 & 91 & 74\\44 & 11 & 77 & 33 & 81 & 39 & 81 & 66 & 39 & 0 & 77 & 79 & 37\\58 & 66 & 48 & 84 & 16 & 91 & 40 & 41 & 81 & 77 & 0 & 20 & 73\\71 & 70 & 67 & 93 & 5 & 101 & 60 & 58 & 91 & 79 & 20 & 0 & 65\\69 & 37 & 94 & 69 & 69 & 76 & 94 & 82 & 74 & 37 & 73 & 65 & 0\end{array}\right]\label{eqn:tsp}\end{equation} 2 | -------------------------------------------------------------------------------- /src/chapters/01/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 1. Introduction 2 | 3 | This chapter will introduce the book: describing what is to be considered an 4 | applied mathematical problem. This chapter will also describe the origins of 5 | open source software and give a number of examples of them. 6 | -------------------------------------------------------------------------------- /src/chapters/01/main.tex: -------------------------------------------------------------------------------- 1 | \chapter[Introduction]{Introduction} 2 | 3 | \chapterinitial{T}{hank} you for starting to read this book. This book aims to 4 | bring together two fascinating topics: 5 | 6 | \begin{itemize} 7 | \item 8 | Problems that can be solved using mathematics; 9 | \item 10 | Software that is free to use and change. 11 | \end{itemize} 12 | 13 | What we mean by both of those things will become clear through reading 14 | this chapter and the rest of the book. 15 | 16 | \section{Who is this book for?}\label{sec:who-is-this-book-for} 17 | 18 | This book is aimed at readers who want to use open-source software to solve the 19 | considered applied mathematical problems. 20 | 21 | If you are a student of a mathematical discipline, a graduate student of 22 | a subject like operational research, a hobbyist who enjoys solving the 23 | travelling salesman problem or even if you get paid to do this stuff: 24 | this book is for you. We will introduce you to the world of open-source 25 | software that allows you to do all these things freely. 26 | 27 | If you are a student learning to write code, a graduate student using 28 | databases for their research, an enthusiast who programs applications 29 | to help schedule weekly chores, or even if you get paid to 30 | write software: this book is for you. We will introduce you to a world 31 | of problems that can be solved using your skill set. 32 | 33 | It would be helpful for the reader of this book to: 34 | 35 | \begin{itemize} 36 | \item 37 | Have access to a computer and be able to connect to the internet 38 | to be able to download the relevant software; 39 | \item 40 | Have done any introductory tutorial in the languages they plan to use; 41 | \item 42 | Be prepared to read some mathematics. 43 | The topics covered use some algebra, calculus and probability. 44 | Technically you do not need to 45 | understand the specific mathematics to be able to use the tools in this book. 46 | \end{itemize} 47 | 48 | By reading a particular chapter of the book, the reader will have: 49 | 50 | \begin{enumerate} 51 | \item the practical knowledge to solve problems using a computer; 52 | \item an overview of the higher level theoretic concepts; 53 | \item pointers to further reading to gain background understand and research 54 | undertaken using the concepts. 55 | \end{enumerate} 56 | 57 | \section{What do we mean by applied mathematics?}\label{sec:what-do-we-mean-by-applied-mathematics} 58 | 59 | We consider this book to be a book on applied mathematics. This is not 60 | however a universal term, for some applied mathematics is the study of 61 | mechanics and involves things like modelling projectiles being fired out of canons. 62 | We will use the term a bit more freely here and mean any type of real 63 | world problem that can be tackled using mathematical tools. This is 64 | sometimes referred to as operational research\index{operational research}, 65 | operations research, mathematical modelling or indeed just mathematics. 66 | 67 | One of the authors, Vince, used mathematics to understand just how bad one of 68 | the so called ``worst plays in Super Bowl\index{Super Bowl} history was''. Using 69 | an area of mathematics called game theory\index{game theory} (seen in 70 | Chapter~\ref{chp:game_theory}) he showed that perhaps the strategic decision 71 | making was not as bad as it seemed, the outcome was just unlikely\footnote{At 72 | the time of writing this is available to read at: 73 | \url{https://vknight.org/unpeudemath/gametheory/2015/02/15/on-the-worst-play-in-superbowl-history.html}}. 74 | 75 | The other author, Geraint, used mathematics to find the best team of 76 | Pokémon\index{Pokémon}. Using an area of mathematics called linear 77 | programming\index{linear programming} (seen in 78 | Chapter~\ref{chp:linear_programming}) which is based on linear algebra he was 79 | able to find the best makeup of Pokémon\footnote{At the time of writing this is 80 | available to read at: 81 | \url{http://www.geraintianpalmer.org.uk/2018/05/29/pokemon-team-pulp/}}. 82 | 83 | Here, applied mathematics is the type of mathematics that helps us 84 | answer questions that the real world asks. 85 | 86 | \section{What is open-source software} 87 | \label{sec:what-is-open-source-software} 88 | 89 | Strictly speaking open-source software\index{open-source software} is software 90 | with source code that anyone can read, modify and improve. In practice this 91 | means that you do not need to pay to use it which is often one of the first 92 | attractions. 93 | This financial aspect can also be one of the reasons that someone will 94 | not use a particular piece of software due to a confusion between cost 95 | and value: if something is free is it really going to be any good? 96 | 97 | In practice open-source software is used all over the world and powers 98 | some of the most important infrastructure around. 99 | A good example of this is cryptographic software which should not rely on 100 | secrecy for security\footnote{This 101 | is also referred to as Kerckhoffs's\index{Kerckhoffs} principle which states 102 | that ``a cryptosystem should be secure, even if everything about the system, 103 | except the key, is public knowledge''~\cite{kerckhoffs1883cryptographie}}. This 104 | implies that cryptographic systems that do not require trust in a hidden system 105 | can exist. 106 | In practice these are all open-source. 107 | 108 | Today, open-source software is a lot more than a licensing agreement: 109 | it is a community of practice. Bugs are fixed faster, research is 110 | implemented immediately and knowledge is spread more widely thanks to 111 | open-source software. Bugs are fixed faster because anyone can read and 112 | inspect the source code. Most open-source software projects also have 113 | clear mechanisms for communicating with the developers and even 114 | reviewing and accepting code contributions from the general public. 115 | Research is implemented immediately because when new algorithms are 116 | discovered they are often added directly to the software by the 117 | researchers who found them. This all contributes to the spread of 118 | knowledge: open-source software is the modern shoulder of giants that 119 | we all stand on. 120 | 121 | Open source software is software that, like scientific knowledge is not 122 | restricted in its use. 123 | 124 | \section{How to get the most out of this 125 | book}\label{sec:how-to-get-the-most-out-of-this-book} 126 | 127 | The book itself is open-source. You can find the source files for this 128 | book online at \url{https://github.com/drvinceknight/ampwoss}. There will 129 | also find a number of \emph{Jupyter notebooks}\index{Jupyter} and \emph{R markdown 130 | files}\index{R markdown} that include code snippets that let you follow along. 131 | 132 | We feel that you can choose to read the book from cover to cover, 133 | writing out the code examples as you go; or it could also be used as a 134 | reference text when faced with a particular problem and wanting to know 135 | where to start. 136 | 137 | After this introductory chapter the book is split in to 4 sections. 138 | Each section corresponds to a broad problem type and contains 2 chapters that 139 | correspond to 2 solution approaches. The first chapter in a section is based on 140 | exact methodology whereas the second chapter is based on heuristic 141 | methodology. 142 | The structure of the book is: 143 | 144 | \begin{enumerate} 145 | \item Probabilistic modelling 146 | \begin{itemize} 147 | \item Markov chains 148 | \item Discrete event simulation 149 | \end{itemize} 150 | \item Dynamical systems 151 | \begin{itemize} 152 | \item Differential equations 153 | \item Systems dynamics 154 | \end{itemize} 155 | \item Emergent behaviour 156 | \begin{itemize} 157 | \item Game theory. 158 | \item Agent based simulation 159 | \end{itemize} 160 | \item Optimisation. 161 | \begin{itemize} 162 | \item Linear programming 163 | \item Heuristics 164 | \end{itemize} 165 | \end{enumerate} 166 | 167 | Every chapter has the following structure: 168 | 169 | \begin{enumerate} 170 | \item 171 | Introduction - a brief overview of a given problem type. Here we will 172 | describe the problem at hand in general terms. 173 | \item 174 | An example problem. This will provide a tangible example problem that 175 | offers the reader some intuition for the rest of the discussion. 176 | \item 177 | An overview of the theory as well as a discussion as to how the theory relates 178 | to the considered problem. Readers will also be presented with reference 179 | texts if they want to gain a more in depth understanding. 180 | \item 181 | Solving with Python. We will describe how to use tools available 182 | in Python to solve the problem. 183 | \item 184 | Solving with R. We will describe how to use tools available 185 | in R to solve the problem. 186 | \item 187 | The wider context. This section will include a few hand picked academic papers 188 | relevant to the covered topic. It is hoped that these few papers can be a good 189 | starting point for someone wanting to not only use the methodology described 190 | but also understand the broader field. 191 | \end{enumerate} 192 | 193 | For a given reader, not all sections of a chapter will be of interest. 194 | Perhaps a reader is only interested in R and finding out more about the 195 | research. 196 | The R and Python sections are \textbf{purposefully} written as near clones of 197 | each other so that a reader can read only the section that interests them. 198 | In places there are some minor differences in the text and this is due to 199 | differences of implementation in the respective languages. 200 | 201 | Note that the solution proposed to each problem in each chapter is not 202 | necessarily unique. For example, in Chapter~\ref{chp:discrete_event_simulation} 203 | the Python library Ciw\index{Ciw}~\cite{ciwpython, palmer2019ciw} 204 | is used whereas an alternative could be to use a Python 205 | library called SimPy\index{SimPy}~\cite{simpy}. 206 | 207 | Please do take from the book what you find useful. 208 | 209 | \section{How code is written in this book}\label{sec:how-code-is-written-in-this-book} 210 | 211 | Throughout this book, there are going to be various pieces of code written. Code 212 | is a series of instructions that usually give some sort of output when submitted 213 | to a computer. 214 | 215 | This book will show both the set of instructions (referred to as the input) and 216 | the output. 217 | 218 | You will see Python input as follows: 219 | 220 | \begin{pyin} 221 | print(2 + 2) 222 | \end{pyin} 223 | 224 | and you will see Python output as follows: 225 | 226 | \begin{pyout} 227 | 4 228 | \end{pyout} 229 | 230 | You will see R input as follows: 231 | 232 | \begin{Rin} 233 | print(2 + 2) 234 | \end{Rin} 235 | 236 | and you will see R output as follows: 237 | 238 | \begin{Rout} 239 | [1] 4 240 | \end{Rout} 241 | 242 | As well as this, a continuous line numbering across all code sections is used so 243 | that if the reader needs to refer to a given set of input or output this can be 244 | done. 245 | 246 | The code itself is written using 3 principles: 247 | 248 | \begin{itemize} 249 | \item Modularity\index{modularity}: code is written as a 250 | series of smaller sections of code. These correspond to smaller, 251 | simpler, individual tasks (modules) that can be used together to 252 | carry out a particular larger task. 253 | \item Documentation\index{documentation}: readable variable names as 254 | well as 255 | text describing the functionality of each module of code are used 256 | throughout. This ensures that code is not only usable but also 257 | understandable. 258 | \item Tests\index{tests}: there are places where each module of code is 259 | used independently to check the output. This can be thought of as a 260 | test of functionality which readers can use to check they are 261 | getting expected results. 262 | \end{itemize} 263 | 264 | These are best practice\index{best practice} principles in research software 265 | development that ensure usable, reproducible and reliable 266 | code~\cite{wilson2014best}. Interested readers might want to see 267 | Figure~\ref{fig:rsd-triangle} which shows how the 3 principles interact with 268 | each other. 269 | 270 | \begin{figure} 271 | \begin{center} 272 | \includestandalone[width=.8\textwidth]{./assets/rsd-triangle/main} 273 | \caption{The relationship between modularisation, documentation and 274 | testing. The authors thank Dr Nikoleta E. Glynatsi for their 275 | contribution to the drawing of this diagram.} 276 | \label{fig:rsd-triangle} 277 | \end{center} 278 | \end{figure} 279 | 280 | Thank you for picking up this book, we hope one if not all of the following 281 | chapters proves interesting or useful to you. 282 | -------------------------------------------------------------------------------- /src/chapters/04/main.tex: -------------------------------------------------------------------------------- 1 | \chapter[Differential Equations]{Differential Equations} 2 | 3 | % Introduction 4 | \chapterinitial{S}{ystems} often change in a way that depends on their current 5 | state. For example, the speed at which a cup of coffee cools down depends on its 6 | current temperature. These types of systems are called dynamical systems and are 7 | modelled mathematically using differential equations. This chapter will 8 | consider a direct solution approach using symbolic mathematics. 9 | 10 | \section{Problem}\label{sec:differential_equations_problem} 11 | 12 | Consider the following situation: the entire population of a small rural town 13 | has caught a cold. All 100 individuals will recover at an average rate of 2 per 14 | day. The town leadership have noticed that being ill costs approximately 15 | \(\pounds10\) per day, this is due to general lack of productivity, poorer mood and 16 | other intangible aspects. They need to decide whether or not to order cold 17 | medicine which would \textbf{double} the recovery rate. The cost of of the cold 18 | medicine is a one off cost of \(\pounds5\) per person. 19 | 20 | \section{Theory}\label{sec:differential_equations_theory} 21 | 22 | In the case of this town, the overall rate at which people get better is 23 | dependent on the number of people who are ill. This can be represented 24 | mathematically using a differential equation which is a way of relating the rate 25 | of change of a system to the state of the system itself. 26 | 27 | In general the objects of interest are the variable \(x\) over time \(t\), and 28 | the rate at which \(x\) changes with \(t\), its derivative\index{derivative} 29 | \(\frac{dx}{dt}\). 30 | The differential equation\index{differential equation} describing this will be 31 | of the form: 32 | 33 | \begin{equation} 34 | \frac{dx}{dt} = f(x) 35 | \end{equation} 36 | 37 | for some function \(f\). 38 | In this case, 39 | the number of infected individuals will be denoted as \(I\), which will 40 | implicitly mean that \(I\) is a function of time: \(I=I(t)\), and the rate at 41 | which individuals recover will be denoted by \(\alpha\), then the 42 | differential equation that describes the above situation is: 43 | 44 | \begin{equation} 45 | \frac{dI}{dt} = -\alpha I 46 | \end{equation} 47 | 48 | Finding a solution to this differential equation means finding an expression for 49 | \(I\) that when differentiated gives \(- \alpha I\). 50 | 51 | In this particular case, one such function is: 52 | 53 | \begin{equation} 54 | I(t) = e ^ {-\alpha t} 55 | \end{equation} 56 | 57 | This is a solution because: 58 | \(\frac{dI}{dt} = -\alpha e ^ {-\alpha y} = -\alpha I\). 59 | 60 | However here \(I(0) = 1\), whereas for this problem we know that at time \(t=0\) 61 | there are 100 infected individuals. In general there are many such functions 62 | that can satisfy a differential equation, known as a family of solutions. To 63 | know which particular solution is relevant to the situation, some sort of 64 | initial condition is required. Here this would 65 | be: 66 | 67 | \begin{equation} 68 | I(t) = 100e ^ {-\alpha t} 69 | \end{equation} 70 | 71 | To evaluate the cost the sum of the values of that function over time is needed. 72 | Integration\index{integration} gives exactly this, so the cost would be: 73 | 74 | \begin{equation} 75 | K \int_{0}^{\infty}I(t)dt 76 | \end{equation} 77 | 78 | where \(K\) is the cost per person per unit time. 79 | Therefore the overall cost would be the cost of being unproductive, plus the cost 80 | of the medicine, \(M\), and is given by: 81 | 82 | \begin{equation} 83 | K \int_{0}^{\infty}I(t)dt + M I(0) 84 | \end{equation} 85 | 86 | In the upcoming sections code will be used to confirm to carry out the above 87 | efficiently so as to answer the original question. 88 | 89 | \section{Solving with Python}\label{sec:differential_equations_solving-with-python} 90 | 91 | The first step is to define the symbolic variables that will be used. The 92 | Python library SymPy\index{SymPy}~\cite{meurer2017sympy} is used. 93 | 94 | \begin{pyin} 95 | import sympy as sym 96 | 97 | t = sym.Symbol("t") 98 | alpha = sym.Symbol("alpha") 99 | I_0 = sym.Symbol("I_0") 100 | I = sym.Function("I") 101 | \end{pyin} 102 | 103 | Now write a function to obtain the differential equation. 104 | 105 | \begin{pyin} 106 | def get_equation(alpha=alpha): 107 | """Return the differential equation. 108 | 109 | Args: 110 | alpha: a float (default: symbolic alpha) 111 | 112 | Returns: 113 | A symbolic equation 114 | """ 115 | return sym.Eq(sym.Derivative(I(t), t), -alpha * I(t)) 116 | \end{pyin} 117 | 118 | This gives an equation that defines the population change over time: 119 | 120 | \begin{pyin} 121 | eq = get_equation() 122 | print(eq) 123 | \end{pyin} 124 | 125 | which gives: 126 | 127 | \begin{pyout} 128 | Eq(Derivative(I(t), t), -alpha*I(t)) 129 | \end{pyout} 130 | 131 | Note that if Jupyter\index{Jupyter}~\cite{kluyver2016jupyter} notebooks are used 132 | then output will actually be a well rendered mathematical equation: 133 | 134 | \[ 135 | \frac{d}{d t} I{\left(t \right)} = - \alpha I{\left(t \right)} 136 | \] 137 | 138 | A value of \(\alpha\) can be passed if required: 139 | 140 | \begin{pyin} 141 | eq = get_equation(alpha=1) 142 | print(eq) 143 | \end{pyin} 144 | 145 | \begin{pyout} 146 | Eq(Derivative(I(t), t), -I(t)) 147 | \end{pyout} 148 | 149 | Now a function will be written to obtain the solution to this differential 150 | equation with initial condition \(I(0) = I_0\): 151 | 152 | \begin{pyin} 153 | def get_solution(I_0=I_0, alpha=alpha): 154 | """Return the solution to the differential equation. 155 | 156 | Args: 157 | I_0: a float (default: symbolic I_0) 158 | alpha: a float (default: symbolic alpha) 159 | 160 | Returns: 161 | A symbolic equation 162 | """ 163 | eq = get_equation(alpha=alpha) 164 | return sym.dsolve(eq, I(t), ics={I(0): I_0}) 165 | \end{pyin} 166 | 167 | This can verify the solution discussed previously: 168 | 169 | \begin{pyin} 170 | sol = get_solution() 171 | print(sol) 172 | \end{pyin} 173 | 174 | which gives: 175 | 176 | \begin{pyout} 177 | Eq(I(t), I_0*exp(-alpha*t)) 178 | \end{pyout} 179 | 180 | \[I(t) = I_0 e ^{-\alpha t}\] 181 | 182 | SymPy itself can be used to verify the result, by taking the derivative of the 183 | right hand side of our solution. 184 | 185 | \begin{pyin} 186 | print(sym.diff(sol.rhs, t) == -alpha * sol.rhs) 187 | \end{pyin} 188 | 189 | which gives: 190 | 191 | \begin{pyout} 192 | True 193 | \end{pyout} 194 | 195 | All of the above has given the general solution in terms of \(I(0)=I_0\) and 196 | \(\alpha\), however the code is written in such a way as we can pass the actual 197 | parameters: 198 | 199 | \begin{pyin} 200 | sol = get_solution(alpha=2, I_0=100) 201 | print(sol) 202 | \end{pyin} 203 | 204 | which gives: 205 | 206 | \begin{pyout} 207 | Eq(I(t), 100*exp(-2*t)) 208 | \end{pyout} 209 | 210 | Now, to calculate the cost write a function to integrate the result: 211 | 212 | \begin{pyin} 213 | def get_cost( 214 | I_0=I_0, 215 | alpha=alpha, 216 | per_person_cost=10, 217 | cure_cost=0, 218 | ): 219 | """Return the cost. 220 | 221 | Args: 222 | I_0: a float (default: symbolic I_0) 223 | alpha: a float (default: symbolic alpha) 224 | per_person_cost: a float (default: 10) 225 | cure_cost: a float (default: 0) 226 | 227 | Returns: 228 | A symbolic expression 229 | """ 230 | I_sol = get_solution(I_0=I_0, alpha=alpha) 231 | area = sym.integrate(I_sol.rhs, (t, 0, sym.oo)) 232 | productivity_cost = area * per_person_cost 233 | total_cost_of_cure = cure_cost * I_0 234 | return productivity_cost + total_cost_of_cure 235 | \end{pyin} 236 | 237 | The cost without purchasing the cure is: 238 | 239 | \begin{pyin} 240 | alpha = 2 241 | cost_without_cure = get_cost(I_0=100, alpha=alpha) 242 | print(cost_without_cure) 243 | \end{pyin} 244 | 245 | which gives: 246 | 247 | \begin{pyout} 248 | 500 249 | \end{pyout} 250 | 251 | 252 | The cost with cure can use the above with a modified \(\alpha\) and a non zero 253 | cost of the cure itself: 254 | 255 | \begin{pyin} 256 | cost_with_cure = get_cost(I_0=100, alpha=2 * alpha, cure_cost=5) 257 | print(cost_with_cure) 258 | \end{pyin} 259 | 260 | which gives: 261 | 262 | \begin{pyout} 263 | 750 264 | \end{pyout} 265 | 266 | So given the current parameters it is not worth purchasing the cure. 267 | 268 | \section{Solving with R}\label{sec:differential_equations_solving-with-R} 269 | 270 | R has some capability for symbolic mathematics, however at the time of writing 271 | the options available are somewhat limited and/or not reliable. As such, in R 272 | the problem will be solved using a numerical integration approach. For an 273 | outline of the theory behind this approach see Chapter 274 | \ref{chp:system_dynamics}. 275 | 276 | First a function to give the derivative for a given value of \(I\) is needed: 277 | 278 | \begin{Rin} 279 | #' Returns the numerical value of the derivative. 280 | #' 281 | #' @param t a set of time points 282 | #' @param y a function 283 | #' @param parameters the set of all parameters passed to y 284 | 285 | #' @return a float 286 | derivative <- function(t, y, parameters) { 287 | with( 288 | as.list(c(y, parameters)), { 289 | dIdt <- -alpha * I # nolint 290 | list(dIdt) # nolint 291 | } 292 | ) 293 | } 294 | \end{Rin} 295 | 296 | For example, to see the value of the derivative when \(I=0\): 297 | 298 | \begin{Rin} 299 | dv <- derivative(t = 0, y = c(I = 100), parameters = c(alpha = 2)) 300 | print(dv) 301 | \end{Rin} 302 | 303 | This gives: 304 | 305 | \begin{Rout} 306 | [[1]] 307 | [1] -200 308 | 309 | \end{Rout} 310 | 311 | Now the deSolve\index{deSolve}~\cite{soetaert2010solving} library will be used 312 | for solving differential equations numerically: 313 | 314 | \begin{Rin} 315 | library(deSolve) # nolint 316 | #' Return the solution to the differential equation. 317 | #' 318 | #' @param times: a vector of time points 319 | #' @param y_0: a float (default: 100) 320 | #' @param alpha: a float (default: 2) 321 | 322 | #' @return A vector of numerical values 323 | get_solution <- function(times, 324 | y0 = c(I = 100), 325 | alpha = 2) { 326 | params <- c(alpha = alpha) 327 | ode(y = y0, times = times, func = derivative, parms = params) 328 | } 329 | \end{Rin} 330 | 331 | This will return a sequence of time point and values of \(I\) at those time 332 | points. Using this we can compute the cost. 333 | 334 | \begin{Rin} 335 | #' Return the cost. 336 | #' 337 | #' @param I_0: a float (default: symbolic I_0) 338 | #' @param alpha: a float (default: symbolic alpha) 339 | #' @param per_person_cost: a float (default: 10) 340 | #' @param cure_cost: a float (default: 0) 341 | #' @param step_size: a float (default: 0.0001) 342 | #' @param max_time: an integer (default: 10) 343 | 344 | #' @return A numeric value 345 | get_cost <- function(I_0 = 100, 346 | alpha = 2, 347 | per_person_cost = 10, 348 | cure_cost = 0, 349 | step_size = 0.0001, 350 | max_time = 10) { 351 | times <- seq(0, max_time, by = step_size) 352 | out <- get_solution(times, y0 = c(I = I_0), alpha = alpha) 353 | number_of_observations <- length(out[, "I"]) 354 | time_intervals <- diff(out[, "time"]) 355 | area <- sum(time_intervals * out[-number_of_observations, "I"]) 356 | productivity_cost <- area * per_person_cost 357 | total_cost_of_cure <- cure_cost * I_0 358 | productivity_cost + total_cost_of_cure 359 | } 360 | \end{Rin} 361 | 362 | The cost without purchasing the cure is: 363 | 364 | \begin{Rin} 365 | alpha <- 2 366 | cost_without_cure <- get_cost(alpha = alpha) 367 | print(round(cost_without_cure)) 368 | \end{Rin} 369 | 370 | 371 | which gives: 372 | 373 | \begin{Rout} 374 | [1] 500 375 | \end{Rout} 376 | 377 | The cost with cure can use the above with a modified \(\alpha\) and a non zero 378 | cost of the cure itself: 379 | 380 | \begin{Rin} 381 | cost_with_cure <- get_cost(alpha = 2 * alpha, cure_cost = 5) 382 | print(round(cost_with_cure)) 383 | \end{Rin} 384 | 385 | which gives: 386 | 387 | \begin{Rout} 388 | [1] 750 389 | \end{Rout} 390 | 391 | So given the current parameters it is not worth purchasing the cure. 392 | 393 | \section{Wider context}\label{sec:differential_equations_wider_context} 394 | 395 | There are a number of further areas related to the study of as well as the use 396 | of differential equations. Topics omitted here include the actual solution 397 | approaches which in this chapter are taken 398 | care of using open-source software. Chapters 9, 14 and 16 399 | of~\cite{stewart2009calculus} provide a good introduction to some of 400 | these concepts as well as a general discussion of the area of mathematics in 401 | which they sit: \index{calculus}. 402 | 403 | Differential equations have been applied in many settings. 404 | In~\cite{lanchester1916aircraft} differential equations were used to model 405 | attrition in warfare\index{warfare}, the insights from these differential 406 | equations are referred to as Lanchester's square 407 | law\index{Lanchester's square law}. This has been historically fitted to a 408 | number of battles with varying levels of success. 409 | 410 | In~\cite{syms2015dynamic} differential equations are used to build a 411 | generic model of regime change. A detailed analysis of the 412 | stability\index{stability} of the system is included. The model offers some 413 | explanation of why oppressive regimes can follow an overthrow of a similarly 414 | oppressive regime: the underlying mathematical system is a stable cycle from 415 | which it is difficult to escape. 416 | 417 | \cite{vandergraft1983fluid} uses differential equations as a framework for 418 | modelling queueing\index{queueing} networks. This is interesting in its 419 | inception as differential equations are models for continuous quantities whereas 420 | queues are discrete type events (see Chapter~\ref{chp:markov_chains} 421 | and~\ref{chp:discrete_event_simulation} for more on this). The advantages of 422 | using differential equations are mainly in the computational efficiency. 423 | 424 | The model presented in this chapter is deterministic: there is a single solution 425 | that remains the same. This is not always a precise model of reality: often 426 | systems are stochastic so that the inputs are not constant parameters but follow 427 | some random distribution. This is where stochastic differential 428 | equations\index{stochastic differential equation} are applied, which is the 429 | subject of~\cite{sarkka2019applied}. 430 | -------------------------------------------------------------------------------- /src/chapters/06/main.tex: -------------------------------------------------------------------------------- 1 | \chapter[Game Theory]{Game Theory}\label{chp:game_theory} 2 | 3 | % Introduction 4 | \chapterinitial{M}{ost} of the time when modelling certain situations two 5 | approaches are valid: to make assumptions about the overall behaviour or to make 6 | assumptions about the detailed behaviour. The later can be thought of as 7 | measuring emergent behaviour. One tool used to do this is the study of 8 | interactive decision making: game theory. 9 | 10 | \section{Problem}\label{sec:game_theory_problem} 11 | 12 | Consider a city council. Two electric taxi companies, company A and company B, 13 | are going to move in to the city and the city wants to ensure that the customers 14 | are best served by this new duopoly. The two taxi firms will be deciding how 15 | many vehicles to deploy: one, two or three. The city wants to encourage them to 16 | both use three as this ensures the highest level of availability to the 17 | population. 18 | 19 | Some exploratory data analysis gives the following insights: 20 | 21 | \begin{itemize} 22 | \item if both companies use the same number of taxis then they make the same 23 | profit which will go down slightly as the number of taxis goes up; 24 | \item if one company uses more taxis than the other then they make more 25 | profit. 26 | \end{itemize} 27 | 28 | The expected profits for the companies are given in 29 | Table~\ref{tbl:profit-of-taxi-companies}. 30 | 31 | \newcolumntype{P}[1]{>{\centering\arraybackslash}p{#1}} 32 | \begin{table}[!hbtp] 33 | \begin{center} 34 | \begin{tabular}{cl|P{0.08\textwidth}P{0.08\textwidth}P{0.08\textwidth}} 35 | & & \multicolumn{3}{c}{Company B}\\ 36 | & & 1 & 2 & 3\\ 37 | \toprule 38 | \multirow{3}{*}{\rotatebox[origin=c]{90}{Company A}} & 1 & 1 & \(\frac{1}{2}\) & \(\frac{1}{3}\)\\[4.5mm] 39 | & 2 & \(\frac{3}{2}\) & \(\frac{19}{20}\) & \(\frac{1}{2}\)\\[4.5mm] 40 | & 3 & \(\frac{5}{3}\) & \(\frac{4}{5}\) & \(\frac{17}{20}\)\\[4.5mm] 41 | \end{tabular} 42 | \qquad 43 | \begin{tabular}{cl|P{0.08\textwidth}P{0.08\textwidth}P{0.08\textwidth}} 44 | & & 45 | \multicolumn{3}{c}{Company B}\\ 46 | & & 1 & 2 & 3\\ 47 | \toprule 48 | \multirow{3}{*}{\rotatebox[origin=c]{90}{Company A}} & 1 & 1 & \(\frac{3}{2}\) & \(\frac{5}{3}\)\\[4.5mm] 49 | & 2 & \(\frac{1}{2}\) & \(\frac{19}{20}\) & \(\frac{4}{5}\)\\[4.5mm] 50 | & 3 & \(\frac{1}{3}\) & \(\frac{1}{2}\) & \(\frac{17}{20}\)\\[4.5mm] 51 | \end{tabular} 52 | \end{center} 53 | \caption{Profits (in GBP per hour) of each taxi company based on 54 | the choice of vehicle number by all companies. The first table shows the profits for 55 | company A. The second table shows the profits for company B.} 56 | \label{tbl:profit-of-taxi-companies} 57 | \end{table} 58 | 59 | Given these expected profits, the council wants to understand what is likely to 60 | happen and potentially give a financial incentive to each company to ensure 61 | their behaviour is in the population's interest. 62 | This would take the form of a fixed increase to the companies' profits, 63 | \(\epsilon\), to be found, if they put on three taxis, shown in 64 | Table~\ref{tbl:profit-of-taxi-companies-offset} 65 | 66 | \begin{table}[!hbtp] 67 | \begin{center} 68 | \begin{tabular}{cl|P{0.08\textwidth}P{0.08\textwidth}P{0.08\textwidth}} 69 | & & \multicolumn{3}{c}{Company B}\\ 70 | & & 1 & 2 & 3\\ 71 | \toprule 72 | \multirow{3}{*}{\rotatebox[origin=c]{90}{Company A}} & 1 & 1 & \(\frac{1}{2}\) & \(\frac{1}{3}\)\\[4.5mm] 73 | & 2 & \(\frac{3}{2}\) & \(\frac{19}{20}\) & \(\frac{1}{2}\)\\[4.5mm] 74 | & 3 & \(\frac{5}{3} + \epsilon\) & \(\frac{4}{5} + \epsilon\) & \(\frac{17}{20} + \epsilon\)\\[4.5mm] 75 | \end{tabular} 76 | \qquad 77 | \begin{tabular}{cl|P{0.08\textwidth}P{0.08\textwidth}P{0.08\textwidth}} 78 | & & 79 | \multicolumn{3}{c}{Company B}\\ 80 | & & 1 & 2 & 3\\ 81 | \toprule 82 | \multirow{3}{*}{\rotatebox[origin=c]{90}{Company A}} & 1 & 1 & \(\frac{3}{2}\) & \(\frac{5}{3} + \epsilon\)\\[4.5mm] 83 | & 2 & \(\frac{1}{2}\) & \(\frac{19}{20}\) & \(\frac{4}{5} + \epsilon\)\\[4.5mm] 84 | & 3 & \(\frac{1}{3}\) & \(\frac{1}{2}\) & \(\frac{17}{20} + \epsilon\)\\[4.5mm] 85 | \end{tabular} 86 | \end{center} 87 | \caption{Profits (in GBP per hour) of each taxi company based on 88 | the choice of vehicle number by all companies. The first table shows the profits for 89 | company A. The second table shows the profits for company B. The 90 | council's financial incentive \(\epsilon\) is included.} 91 | \label{tbl:profit-of-taxi-companies-offset} 92 | \end{table} 93 | 94 | For example, from Table~\ref{tbl:profit-of-taxi-companies-offset} it can be seen 95 | that if Company B chooses to use 3 vehicles while Company A chooses to only use 96 | 2 then Company B would get \(\frac{17}{20} + \epsilon\) and Company A would get 97 | \(\frac{1}{2}\) profits per hour. The question is: what value of \(\epsilon\) 98 | ensures both companies use 3 vehicles? 99 | 100 | \section{Theory}\label{sec:game_theory_theory} 101 | 102 | In the case of this city, the interaction can be modelled using a mathematical 103 | object called a normal form game\index{normal form game}, which here requires: 104 | 105 | \begin{enumerate} 106 | \item a given collection of actors that make decisions (players); 107 | \item options available to each player (actions); 108 | \item a numerical value associated to each player for every possible 109 | choice of action made by all the players. This is the utility or reward. 110 | \end{enumerate} 111 | 112 | This is called a normal form game and is formally defined by: 113 | 114 | \begin{enumerate} 115 | \item a finite set of \(N\) players; 116 | \item action spaces for each player: \(\{A_1, A_2, A_3, \dots, A_N\}\); 117 | \item utility functions\index{utility} that for each player 118 | \(u_1, u_2, u_3, \dots, u_N\) where 119 | \(u_i:A_1\times A_2 \times A_3 \dots A_N \to \mathbb{R}\). 120 | \end{enumerate} 121 | 122 | When \(N=2\) the utility function is often represented by a pair of 123 | matrices\index{matrix} (1 for each player) with the same number of rows and 124 | columns. The rows correspond to the actions available to the first player and 125 | the columns to the actions available to the second player. 126 | 127 | Given a pair of actions (a row and column) we can read the utilities to both 128 | players by looking at the corresponding entry of the corresponding matrix. 129 | 130 | For this example, the two matrices would be: 131 | 132 | \[ 133 | M = 134 | \begin{pmatrix} 135 | 1 & 1 / 2 & 1 / 3 \\ 136 | 3 / 2 & 19 / 20 & 1 / 2 \\ 137 | 5 / 3 & 4 / 5 & 17 / 20\\ 138 | \end{pmatrix} 139 | \qquad 140 | N = M ^T = 141 | \begin{pmatrix} 142 | 1 & 3 / 2 & 5 / 3 \\ 143 | 1 / 2 & 19 / 20 & 4 / 5 \\ 144 | 1 / 3 & 1 / 2 & 17 / 20\\ 145 | \end{pmatrix} 146 | \] 147 | 148 | A diagram of the system is shown in Figure~\ref{fig:taxi-firm-game} 149 | 150 | \begin{figure} 151 | \begin{center} 152 | \includestandalone[width=.8\textwidth]{./assets/taxi-firm-game/main} 153 | \end{center} 154 | \caption{Diagrammatic representation of the action sets and payoff matrices for 155 | the game.} 156 | \label{fig:taxi-firm-game} 157 | \end{figure} 158 | 159 | A strategy corresponds to a way of choosing actions, this is represented by a 160 | probability\index{probability} vector\index{vector}. For the \(i\)th player, 161 | this vector \(v\) would be of size \(|A_i|\) (the size of the action space) and 162 | \(v_i\) corresponds to the probability of choosing the \(i\)th action. 163 | 164 | Both taxi firms always choosing to use 2 taxis (the second row/column) would 165 | correspond to both taxi firms choosing the strategy: \((0, 1, 0)\). 166 | If both companies use this strategy and the row player (who controls the 167 | rows) wants to improve their outcome it is evident by inspecting the second 168 | column that the highest number is \(19 / 20\): thus the row player has no reason 169 | to change what they are doing. 170 | 171 | This is called a Nash equilibrium\index{Nash equilibrium}: when both players are 172 | playing a strategy that is the best response\index{best response} against the 173 | other. 174 | 175 | An important fact is that a Nash equilibrium is guaranteed to exist. 176 | This was actually the theoretic result for which John Nash\index{John Nash} 177 | received a Nobel Prize\footnote{ 178 | John Nash proved the fact that any game has a Nash equilibrium 179 | in~\cite{nash1950equilibrium}. Discussions and proofs for particular cases 180 | can be found in good game theory text books such as~\cite{maschler2013game} 181 | }. 182 | There are various algorithms that can be used for finding Nash equilibria, they 183 | involve a search through the pairs of spaces of possible strategies until pairs 184 | of best responses are found. Mathematical insight allows this do be done 185 | somewhat efficiently using algorithms that can be thought of as modifications of 186 | the algorithms used in linear programming\index{linear programming} (see 187 | Chapter~\ref{chp:linear_programming}). 188 | One such example is called the Lemke-Howson\index{Lemke-Howson} algorithm. 189 | A Nash equilibrium is not necessarily guaranteed to be arrived at through 190 | dynamic decision making. However, any stable\index{stability} behaviour that 191 | does emerge will be a Nash equilibrium, such emergent processes are the topics 192 | of evolutionary game theory\index{evolutionary game theory}\footnote{ 193 | Evolutionary game theory was formalised in~\cite{smith1974theory} although 194 | some of the work of Robert Axelrod\index{Axelrod} is evolutionary in 195 | principle~\cite{axelrod1990evolution}. 196 | }, 197 | learning algorithms\index{learning algorithms}\footnote{ 198 | An excellent text on learning in games is~\cite{fudenberg1998theory}. 199 | } 200 | and/or agent based\index{agent based} modelling which will be covered in 201 | Chapter~\ref{chp:agent_based_simulation}. 202 | 203 | \section{Solving with Python}\label{sec:game_theory_solving-with-python} 204 | 205 | The first step is to write a function to create a game using the 206 | matrix of expected profits and any offset. The Nashpy\index{Nashpy}~\cite{knight2018nashpy} 207 | and Numpy\index{Numpy}~\cite{harris2020array} libraries will be used for this. 208 | 209 | \begin{pyin} 210 | import nashpy as nash 211 | import numpy as np 212 | 213 | 214 | def get_game(profits, offset=0): 215 | """Return the game object with a given offset when 3 taxis are 216 | provided. 217 | 218 | Args: 219 | profits: a matrix with expected profits 220 | offset: a float 221 | 222 | Returns: 223 | A nashpy game object 224 | """ 225 | new_profits = np.array(profits) 226 | new_profits[2] += offset 227 | return nash.Game(new_profits, new_profits.T) 228 | \end{pyin} 229 | 230 | 231 | This gives the game for the considered problem: 232 | 233 | \begin{pyin} 234 | import numpy as np 235 | 236 | profits = np.array( 237 | ( 238 | (1, 1 / 2, 1 / 3), 239 | (3 / 2, 19 / 20, 1 / 2), 240 | (5 / 3, 4 / 5, 17 / 20), 241 | ) 242 | ) 243 | game = get_game(profits=profits) 244 | print(game) 245 | \end{pyin} 246 | 247 | which gives: 248 | 249 | \begin{pyout} 250 | Bi matrix game with payoff matrices: 251 | 252 | Row player: 253 | [[1. 0.5 0.33333333] 254 | [1.5 0.95 0.5 ] 255 | [1.66666667 0.8 0.85 ]] 256 | 257 | Column player: 258 | [[1. 1.5 1.66666667] 259 | [0.5 0.95 0.8 ] 260 | [0.33333333 0.5 0.85 ]] 261 | \end{pyout} 262 | 263 | The function \mintinline{python}{get_equilibria} which will directly compute the 264 | equilibria: 265 | 266 | \begin{pyin} 267 | def get_equilibria(profits, offset=0): 268 | """Return the equilibria for a given offset when 3 taxis are 269 | provided. 270 | 271 | Args: 272 | profits: a matrix with expected profits 273 | offset: a float 274 | 275 | Returns: 276 | A tuple of Nash equilibria 277 | """ 278 | game = get_game(profits=profits, offset=offset) 279 | return tuple(game.support_enumeration()) 280 | \end{pyin} 281 | 282 | This can be used to obtain the equilibria in the game. 283 | 284 | \begin{pyin} 285 | equilibria = get_equilibria(profits=profits) 286 | \end{pyin} 287 | 288 | The equilibria are: 289 | 290 | \begin{pyin} 291 | for eq in equilibria: 292 | print(eq) 293 | \end{pyin} 294 | 295 | giving: 296 | 297 | \begin{pyout} 298 | (array([0., 1., 0.]), array([0., 1., 0.])) 299 | (array([0., 0., 1.]), array([0., 0., 1.])) 300 | (array([0. , 0.7, 0.3]), array([0. , 0.7, 0.3])) 301 | \end{pyout} 302 | 303 | There are 3 Nash equilibria: 3 possible pairs of behaviour that the 2 304 | companies could stabilise at: 305 | 306 | \begin{itemize} 307 | \item the first equilibrium \(((0, 1, 0), (0, 1, 0))\) corresponds to both 308 | firms always using 2 taxis; 309 | \item the second equilibrium \(((0, 0, 1), (0, 0, 1))\) corresponds to both 310 | firms always using 3 taxis; 311 | \item the third equilibrium \(((0, 0.7, 0.3), (0, 0.7, 0.3))\) corresponds to 312 | both firms using 2 taxis 70\% of the time and 3 taxis otherwise. 313 | \end{itemize} 314 | 315 | A good thing to note is that the two taxi companies will never only provide a 316 | single taxi (which would be harmful to the customers). 317 | 318 | The code below can be used to find the number of Nash equilibria for a given offset and 319 | stop when there is a single equilibrium: 320 | 321 | \begin{pyin} 322 | offset = 0 323 | while len(get_equilibria(profits=profits, offset=offset)) > 1: 324 | offset += 0.01 325 | \end{pyin} 326 | 327 | This gives a final offset value of: 328 | 329 | \begin{pyin} 330 | print(round(offset, 2)) 331 | \end{pyin} 332 | 333 | 334 | \begin{pyout} 335 | 0.15 336 | \end{pyout} 337 | 338 | and now confirm that the Nash equilibrium is where both taxi firms provide 339 | three vehicles: 340 | 341 | \begin{pyin} 342 | print(get_equilibria(profits=profits, offset=offset)) 343 | \end{pyin} 344 | 345 | giving: 346 | 347 | \begin{pyout} 348 | ((array([0., 0., 1.]), array([0., 0., 1.])),) 349 | \end{pyout} 350 | 351 | Therefore, in order to ensure that the maximum amount of taxis are used, the 352 | council should offer a \(\pounds 0.15\) per hour incentive to both taxi 353 | companies for putting on 3 taxis. 354 | 355 | \section{Solving with R}\label{sec:game_theory_solving-with-R} 356 | 357 | R does not have a uniquely appropriate library for the game considered here, we 358 | will choose to use Recon\index{Recon}~\cite{oliveira2019recon} which has 359 | functionality for finding the Nash equilibria for two player games when only 360 | considering pure strategies (where the players only choose to use a single 361 | action at a time). 362 | 363 | \begin{Rin} 364 | library(Recon) 365 | 366 | #' Returns the equilibria in pure strategies 367 | #' for a given offset 368 | #' 369 | #' @param profits: a matrix with expected profits 370 | #' @param offset: a float 371 | #' 372 | #' @return a list of equilibria 373 | get_equilibria <- function(profits, offset = 0){ 374 | new_profits <- rbind( 375 | profits[c(1, 2), ], 376 | profits[3, ] + offset 377 | ) 378 | sim_nasheq(new_profits, t(new_profits)) 379 | } 380 | \end{Rin} 381 | 382 | 383 | This gives the pure Nash equilibria: 384 | 385 | \begin{Rin} 386 | 387 | profits <- rbind( 388 | c(1, 1 / 2, 1 / 3), 389 | c(3 / 2, 19 / 20, 1 / 2), 390 | c(5 / 3, 4 / 5, 17 / 20) 391 | ) 392 | eqs <- get_equilibria(profits = profits) 393 | print(eqs) 394 | \end{Rin} 395 | 396 | which gives: 397 | 398 | \begin{Rout} 399 | $`Equilibrium 1` 400 | [1] "2" "2" 401 | 402 | $`Equilibrium 2` 403 | [1] "3" "3" 404 | 405 | \end{Rout} 406 | 407 | There are 2 pure Nash equilibria: 2 possible pairs of behaviour that the two 408 | companies might converge to. 409 | 410 | \begin{itemize} 411 | \item the first equilibrium \(((0, 1, 0), (0, 1, 0))\) corresponds to both 412 | firms always using 2 taxis; 413 | \item the second equilibrium \(((0, 0, 1), (0, 0, 1))\) corresponds to both 414 | firms always using 3 taxis. 415 | \end{itemize} 416 | 417 | There is in fact a third Nash equilibrium where both taxi firms use 2 taxis 70\% 418 | of the time and 3 taxis the rest of the time but Recon is unable 419 | to find Nash equilibria with mixed behaviour for games with more than two 420 | strategies. 421 | 422 | As discussed, the council would like to offset the cost of 3 423 | taxis so as to encourage the taxi company to provide a better service. 424 | 425 | The code below gives the number of equilibria for a given offset and stops when 426 | there is a single equilibrium: 427 | 428 | \begin{Rin} 429 | offset <- 0 430 | while (length( 431 | get_equilibria(profits = profits, offset = offset) 432 | ) > 1){ 433 | offset <- offset + 0.01 434 | } 435 | \end{Rin} 436 | 437 | This gives a final offset value of: 438 | 439 | \begin{Rin} 440 | print(round(offset, 2)) 441 | \end{Rin} 442 | 443 | \begin{Rout} 444 | [1] 0.15 445 | \end{Rout} 446 | 447 | Now confirm that the Nash equilibrium is where both taxi firms provide 448 | three vehicles: 449 | 450 | \begin{Rin} 451 | eqs <- get_equilibria(profits = profits, offset = offset) 452 | print(eqs) 453 | \end{Rin} 454 | 455 | giving: 456 | 457 | \begin{Rout} 458 | $`Equilibrium 1` 459 | [1] "3" "3" 460 | \end{Rout} 461 | 462 | Therefore, in order to ensure that the maximum amount of taxis are used, the 463 | council should offer a \(\pounds 0.15\) per hour incentive to both taxi 464 | companies for putting on 3 taxis. 465 | 466 | \section{Wider context}\label{sec:game_theory_wider_context} 467 | 468 | The definition of a normal form game here as well as the solution concept of 469 | Nash equilibrium are common starting points for the use of game theory as a 470 | study of emergent behaviour. Other topics include different forms of games, for 471 | example extensive form games\index{extensive form games} which are represented 472 | by trees and more explicitly model asynchronous decision making. Other solution 473 | concepts involve the specific study of the emergence mechanisms through models 474 | based on natural evolutionary process: Moran processes\index{Moran process} or 475 | replicator dynamics\index{replicator dynamics}. A good text book to read on 476 | these topics is~\cite{maschler2013game}. 477 | 478 | John Nash\index{John Nash} whose life was portrayed in the 2001 movie ``A 479 | Beautiful Mind'' (which is an adaptation of~\cite{nasar2011beautiful}) won the 480 | Nobel Prize for~\cite{nash1950equilibrium} in which he proved that a Nash 481 | equilibrium always exists. In~\cite{nash1951non} John Nash gives an 482 | application of game theory to a specific version of poker. 483 | 484 | Another application of the concept of Nash equilibrium 485 | is~\cite{deo2011centralized} where the authors identify worst case 486 | scenarios for ambulance diversion: a practice where an emergency room will 487 | declare itself too full to accept new patients. When there are multiple emergency 488 | units serving a same population strategic behaviour becomes relevant. The 489 | authors of this paper identify the effect of this decentralised decision making 490 | and also propose an approach that is socially optimal: similarly to the taxi 491 | problem considered in this chapter. 492 | 493 | A specific area of a lot of research in game theory is the study of cooperative 494 | behaviour. Axelrod\index{Axelrod}~\cite{axelrod1990evolution} started this work 495 | with computer tournaments where he invited code submissions of strategies to 496 | play a game called the Iterated Prisoner's 497 | Dilemma\index{Iterated Prisoner's Dilemma}. The outcome of this was an 498 | explanation for how cooperation can emerge in a complex system with a set of 5 499 | rules which included the need to be `nice'. The conclusions of Axelrod's work 500 | and these set of rules continue to be examined and often refuted. For example, 501 | more recent work involving so called Zero-Determinant\index{Zero-Determinant} 502 | strategies which considered extortion as a mathematical 503 | concept~\cite{press2012iterated} and also a possible outcome as opposed to 504 | cooperation. 505 | A review and systemic analysis of some of the research on behaviour, of which 506 | game theory is a subset, 507 | is given in~\cite{press2012iterated}. 508 | -------------------------------------------------------------------------------- /src/customenvironments.sty: -------------------------------------------------------------------------------- 1 | \usepackage{minted} 2 | \usepackage{tikz} 3 | \usepackage[framemethod=TikZ]{mdframed} 4 | \usepackage{xcolor} 5 | \usepackage{inconsolata} 6 | 7 | \definecolor{pyincol}{RGB}{51, 110, 159} 8 | \definecolor{pyoutcol}{RGB}{78, 169, 244} 9 | \definecolor{Rincol}{RGB}{55, 151, 59} 10 | \definecolor{Routcol}{RGB}{132, 203, 47} 11 | \definecolor{bg}{RGB}{248, 248, 248} 12 | 13 | %%% Test environment 14 | 15 | \newenvironment{explanation} 16 | {\begin{mdframed}[nobreak=true, roundcorner=10pt]} 17 | {\end{mdframed}} 18 | 19 | %%% Code environments 20 | 21 | \newenvironment{code}[3] 22 | % The basic code environment. 23 | % Used to create further environments. 24 | % 25 | % Take four arguments: 26 | % - a string of text to appear, the title 27 | % - a language for syntax 28 | % - the color of the box 29 | % 30 | % For example: 31 | % 32 | % \begin{code}{Python input}{python}{0.4pt}{cyan!5} 33 | % for i in range(5): 34 | % print(i) 35 | % end{code} 36 | {\begin{tcolorbox}[enhanced, 37 | colframe=#3, 38 | coltitle=white, 39 | fonttitle=\bfseries\ttfamily\Large, 40 | left=26pt, 41 | colback=bg, 42 | enlarge top by=8pt,% equivalent to mdframed 'skipabove' 43 | enlarge bottom by=5pt,% equivalent to mdframed 'skipbelow' 44 | title=#1, 45 | breakable=true, 46 | lines before break=6] 47 | \VerbatimEnvironment 48 | \begin{minted}[ 49 | linenos=true, 50 | style=friendly, 51 | firstnumber=last, 52 | ]{#2}} 53 | {\end{minted}\end{tcolorbox}} 54 | 55 | %%%%% Python code environments 56 | 57 | \newenvironment{pyin} 58 | % Python input environment 59 | % \begin{pyin} 60 | % for i in range(5): 61 | % print(i) 62 | % end{pyin} 63 | { 64 | \VerbatimEnvironment 65 | \begin{code}{Python input}{python}{pyincol}} 66 | {\end{code}} 67 | 68 | \newenvironment{pyout} 69 | % Python ouput environment 70 | % \begin{pyout} 71 | % 0 72 | % 1 73 | % 2 74 | % 3 75 | % 4 76 | % end{pyout} 77 | { 78 | \VerbatimEnvironment 79 | \begin{code}{Python output}{python}{pyoutcol}} 80 | {\end{code}} 81 | 82 | \newenvironment{pyin-no-test} 83 | % Python input environment that is not tested 84 | { 85 | \VerbatimEnvironment 86 | \begin{pyin}} 87 | {\end{pyin}} 88 | 89 | \newenvironment{pyout-no-test} 90 | % Python ouput environment that is not tested 91 | { 92 | \VerbatimEnvironment 93 | \begin{pyout}} 94 | {\end{pyout}} 95 | 96 | %%%%% R code environments 97 | 98 | \newenvironment{Rin} 99 | % R input environment 100 | % for (i in seq(0, 4)) { 101 | % print(i) 102 | % } 103 | % end{pyin} 104 | { 105 | \VerbatimEnvironment 106 | \begin{code}{R input}{R}{Rincol}} 107 | {\end{code}} 108 | 109 | 110 | \newenvironment{Rout} 111 | % R output environment 112 | % \begin{Rout} 113 | % 0 114 | % 1 115 | % 2 116 | % 3 117 | % 4 118 | % end{Rout} 119 | { 120 | \VerbatimEnvironment 121 | \begin{code}{R output}{R}{Routcol}} 122 | {\end{code}} 123 | 124 | \newenvironment{Rin-no-test} 125 | % R input environment that will be skipped by doctester 126 | { 127 | \VerbatimEnvironment 128 | \begin{Rin}} 129 | {\end{Rin}} 130 | 131 | \newenvironment{Rout-no-test} 132 | % R output environment that will be skipped by doctester 133 | { 134 | \VerbatimEnvironment 135 | \begin{Rout}} 136 | {\end{Rout}} 137 | 138 | %%%%% Cli code environments 139 | 140 | \newenvironment{cliin} 141 | { 142 | \VerbatimEnvironment 143 | \begin{code}{Cli input}{bash}{white}} 144 | {\end{code}} 145 | 146 | 147 | \newenvironment{cliout} 148 | { 149 | \VerbatimEnvironment 150 | \begin{code}{Cli output}{bash}{white}} 151 | {\end{code}} 152 | 153 | -------------------------------------------------------------------------------- /src/epigrafica.sty: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `epigrafica.sty', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% epigrafica.dtx (with options: `epigrafica') 8 | %% 9 | %% (c) copyright 2004-2009 10 | %% Antonis Tsolomitis 11 | %% Department of Mathematics, University of the Aegean 12 | %% 13 | %% This program can be redistributed and/or modified under the terms 14 | %% of the LaTeX Project Public License Distributed from CTAN 15 | %% archives in directory macros/latex/base/lppl.txt; either 16 | %% version 1 of the License, or any later version. 17 | %% 18 | %% Please report errors or suggestions for improvement to 19 | %% 20 | %% Dr. Antonis Tsolomitis 21 | %% Department of Mathematics 22 | %% University of the Aegean 23 | %% atsol@aegean.gr 24 | %% 25 | %% \CharacterTable 26 | %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z 27 | %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z 28 | %% Digits \0\1\2\3\4\5\6\7\8\9 29 | %% Exclamation \! Double quote \" Hash (number) \# 30 | %% Dollar \$ Percent \% Ampersand \& 31 | %% Acute accent \' Left paren \( Right paren \) 32 | %% Asterisk \* Plus \+ Comma \, 33 | %% Minus \- Point \. Solidus \/ 34 | %% Colon \: Semicolon \; Less than \< 35 | %% Equals \= Greater than \> Question mark \? 36 | %% Commercial at \@ Left bracket \[ Backslash \\ 37 | %% Right bracket \] Circumflex \^ Underscore \_ 38 | %% Grave accent \` Left brace \{ Vertical bar \| 39 | %% Right brace \} Tilde \~} 40 | \ProvidesFile{epigrafica.sty} 41 | [2009/10/03 v1.01 Package `epigrafica.sty'] 42 | \NeedsTeXFormat{LaTeX2e} 43 | \message{}\MessageBreak 44 | \message{(Epigrafica style file by A. Tsolomitis)}\MessageBreak 45 | \message{}\MessageBreak 46 | %%%\RequirePackage{pxfonts}%For Platino font 47 | 48 | \renewcommand{\sfdefault}{epigrafica} 49 | 50 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 51 | %\def\stigma{st} 52 | %\DeclareTextCommand{\anwtonos}{LGR}{\char'047\relax} 53 | %\let\katwtonos\relax 54 | %\let\qoppa\relax 55 | %\let\sampi\relax 56 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 57 | \renewcommand{\textbullet}{{\fontencoding{OT1}\selectfont\symbol{'225}}} 58 | \DeclareTextAccent{\`}{OT1}{30} 59 | \DeclareTextAccent{\'}{OT1}{180} 60 | \DeclareTextAccent{\^}{OT1}{25} 61 | \DeclareTextAccent{\~}{OT1}{152} 62 | \DeclareTextAccent{\"}{OT1}{168} 63 | \def\"#1{\accent168#1} 64 | \@ifpackagewith{fontenc}{T1}{}{\def\"#1{\accent168#1}} 65 | \DeclareTextAccent{\H}{OT1}{5} 66 | \DeclareTextAccent{\r}{OT1}{9} 67 | \DeclareTextAccent{\t}{OT1}{10} 68 | \DeclareTextAccent{\v}{OT1}{16} 69 | \DeclareTextAccent{\u}{OT1}{11} 70 | \DeclareTextAccent{\=}{OT1}{175} 71 | \DeclareTextAccent{\.}{OT1}{1} 72 | \DeclareTextCommand{\b}{OT1}[1] 73 | {{\o@lign{\relax#1\crcr\hidewidth\sh@ft{29}% 74 | \vbox to.2ex{\hbox{\char175}\vss}\hidewidth}}} %%% error 75 | \DeclareTextCommand{\c}{OT1}[1] 76 | {\leavevmode\setbox\z@\hbox{#1}\ifdim\ht\z@=1ex\accent11 #1% 77 | \else{\ooalign{\hidewidth\char184\hidewidth 78 | \crcr\unhbox\z@}}\fi} 79 | \DeclareTextCommand{\d}{OT1}[1] 80 | {{\o@lign{\relax#1\crcr\hidewidth\sh@ft{10}.\hidewidth}}} 81 | \DeclareTextCommand{\t}{OT1}[1] 82 | {\leavevmode\setbox\z@\hbox{#1}% 83 | {\ooalign{\hidewidth\char10\hidewidth 84 | \crcr\unhbox\z@}}} 85 | \DeclareTextCommand{\k}{OT1}[1] 86 | {\leavevmode\setbox\z@\hbox{#1}\ifdim\ht\z@=1ex\accent11 #1% 87 | \else{\ooalign{\hidewidth\char8\hidewidth 88 | \crcr\unhbox\z@}}\fi} 89 | \DeclareTextSymbol{\textperthousand}{OT1}{137} 90 | \DeclareTextSymbol{\textsterling}{OT1}{163} 91 | \DeclareTextSymbol{\L}{OT1}{6} 92 | \DeclareTextSymbol{\i}{OT1}{17} 93 | \DeclareTextSymbol{\j}{OT1}{18} 94 | \DeclareTextSymbol{\l}{OT1}{7} 95 | \DeclareTextSymbol{\textexclamdown}{OT1}{161} 96 | %\DeclareTextSymbol{\textgreater}{OT1}{`\>} 97 | %\DeclareTextSymbol{\textless}{OT1}{`\<} 98 | \DeclareTextSymbol{\textquestiondown}{OT1}{191} 99 | \DeclareTextSymbol{\textasciicircum}{OT1}{94} 100 | \DeclareTextComposite{\'}{OT1}{!}{161} 101 | \DeclareTextComposite{\'}{OT1}{?}{191} 102 | \DeclareTextComposite{\"}{OT1}{e}{235} 103 | \DeclareTextComposite{\"}{OT1}{o}{246} 104 | %\DeclareTextSymbol{\trademark}{OT1}{153} 105 | %\DeclareTextSymbol{\textregistered}{OT1}{174} 106 | \DeclareTextSymbol{\AA}{OT1}{197} 107 | \DeclareTextSymbol{\aa}{OT1}{229} 108 | \DeclareTextSymbol{\AE}{OT1}{198} 109 | \DeclareTextSymbol{\ae}{OT1}{230} 110 | \DeclareTextSymbol{\O}{OT1}{216} 111 | \DeclareTextSymbol{\o}{OT1}{248} 112 | \DeclareTextSymbol{\OE}{OT1}{140} 113 | \DeclareTextSymbol{\oe}{OT1}{156} 114 | \DeclareTextSymbol{\ss}{OT1}{223} 115 | \DeclareTextSymbol{\textcopyright}{OT1}{169} 116 | \DeclareTextSymbol{\textdagger}{OT1}{134} 117 | \DeclareTextSymbol{\textdaggerdbl}{OT1}{135} 118 | \DeclareTextSymbol{\textparagraph}{OT1}{182} 119 | \DeclareTextSymbol{\textsection}{OT1}{167} 120 | %\DeclareTextSymbolDefault{\textbackslash}{OT1} 121 | %\DeclareTextSymbol{\textbackslash}{OT1}{92} 122 | \DeclareTextCommandDefault{\textvisiblespace}{% 123 | \mbox{\kern.06em\vrule \@height.3ex}% 124 | \vbox{\hrule \@width.3em}% 125 | \hbox{\vrule \@height.3ex}} 126 | %\DeclareTextSymbolDefault{\textbraceleft}{OT1} 127 | %\DeclareTextSymbol{\textbraceleft}{OT1}{123} 128 | %\DeclareTextSymbolDefault{\textbraceright}{OT1} 129 | %\DeclareTextSymbol{\textbraceright}{OT1}{125} 130 | %\DeclareTextAccentDefault{\textcircled}{OT1} 131 | %\DeclareTextSymbolDefault{\textunderscore}{OT1} 132 | %\DeclareTextSymbol{\textunderscore}{OT1}{95} 133 | %\DeclareTextCommand{\textcircled}{OT1}[1]{{% 134 | % \ooalign{% 135 | % \hfil \raise .24ex\hbox {\upshape#1}\hfil \crcr 136 | % \char 136 % "0D 137 | % }% 138 | %}} 139 | 140 | 141 | %%%%%%%%%%% C-S %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 142 | \@ifpackagewith{inputenc}{iso-8859-7}{% 143 | \DeclareInputText{242}{c} 144 | }{} 145 | \@ifpackagewith{babel}{greek}{% 146 | %%%% from grsymb package %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 147 | \DeclareTextCommand{\Digamma}{LGR}{\char"C3\relax} 148 | \DeclareTextCommand{\ddigamma}{LGR}{\char"93\relax} 149 | \DeclareTextCommand{\tao}{LGR}{\char"01\relax} 150 | \DeclareTextCommand{\Qoppa}{LGR}{\char"14\relax} 151 | \DeclareTextCommand{\varqoppa}{LGR}{\char"13\relax} 152 | \DeclareTextCommand{\Sampi}{LGR}{\char"1A\relax} 153 | \DeclareTextCommand{\vardigamma}{LGR}{\char"07\relax} 154 | \DeclareTextCommand{\Stigma}{LGR}{\textlatin{\char"43\relax}} 155 | \DeclareTextCommand{\VarQoppa}{LGR}{\textlatin{\char"47\relax}} 156 | %\DeclareTextCommand{\euro}{LGR}{\char"18\relax} 157 | %\DeclareTextCommand{\permill}{LGR}{\char"19\relax} 158 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 159 | }{% 160 | \@ifpackagewith{babel}{polutonikogreek}{ 161 | %%%% from grsymb package %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 162 | \DeclareTextCommand{\Digamma}{LGR}{\char"C3\relax} 163 | \DeclareTextCommand{\ddigamma}{LGR}{\char"93\relax} 164 | \DeclareTextCommand{\tao}{LGR}{\char"01\relax} 165 | \DeclareTextCommand{\Qoppa}{LGR}{\char"14\relax} 166 | \DeclareTextCommand{\varqoppa}{LGR}{\char"13\relax} 167 | \DeclareTextCommand{\Sampi}{LGR}{\char"1A\relax} 168 | \DeclareTextCommand{\vardigamma}{LGR}{\char"07\relax} 169 | \DeclareTextCommand{\Stigma}{LGR}{\textlatin{\char"43\relax}} 170 | \DeclareTextCommand{\VarQoppa}{LGR}{\textlatin{\char"47\relax}} 171 | %\DeclareTextCommand{\euro}{LGR}{\char"18\relax} 172 | %\DeclareTextCommand{\permill}{LGR}{\char"19\relax} 173 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 174 | }{}} 175 | 176 | %%% Fix LaTeX logos %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 177 | %\DeclareRobustCommand{\LaTeX}{L\kern-.36em% 178 | % {\sbox\z@ T% 179 | % \vbox to\ht\z@{\hbox{\check@mathfonts 180 | % \fontsize\sf@size\z@ 181 | % \math@fontsfalse\selectfont 182 | % \kern.25em A\kern.15em}% 183 | % \vss}% 184 | % }% 185 | % \kern-.15em% 186 | % \TeX} 187 | \InputIfFileExists{lgrenc.def}{% 188 | \message{Loading the definitions for the Greek font encoding}}{% 189 | \errhelp{I can't find the lgrenc.def file for the Greek fonts}% 190 | \errmessage{Since I do not know what the LGR encoding means^^J 191 | I can't typeset Greek.^^J 192 | I stop here, while you get a suitable lgrenc.def file}\@@end 193 | } 194 | \DeclareRobustCommand{\LaTeX}{L\kern-.36em% 195 | \kern.15em\raise.5ex\hbox{% 196 | \check@mathfonts 197 | \fontsize\sf@size\z@ 198 | \math@fontsfalse\selectfont 199 | \kern.05em A\kern-.05em 200 | }% 201 | \TeX} 202 | \newsavebox{\theeinlatexlogo} 203 | \savebox{\theeinlatexlogo}{\hbox{\usefont{LGR}{epigrafica}{m}{it}e}} 204 | \DeclareRobustCommand{\LaTeXe}{\LaTeX\kern.06em2% 205 | \kern-.05em$_{\hbox{\fontencoding{LGR}\fontfamily{epigrafica}\fontshape{it}% 206 | \selectfont e}}$} 207 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 208 | 209 | \endinput 210 | %% 211 | %% End of file `epigrafica.sty'. 212 | -------------------------------------------------------------------------------- /src/footnotes.bib: -------------------------------------------------------------------------------- 1 | @book{van1996matrix, 2 | title={Matrix computations (Johns Hopkins studies in mathematical sciences)}, 3 | author={Van Loan, Charles F and Golub, G}, 4 | year={1996}, 5 | publisher={The Johns Hopkins University Press} 6 | } 7 | 8 | @article{moler1978nineteen, 9 | title={Nineteen dubious ways to compute the exponential of a matrix}, 10 | author={Moler, Cleve and Van Loan, Charles}, 11 | journal={SIAM review}, 12 | volume={20}, 13 | number={4}, 14 | pages={801--836}, 15 | year={1978}, 16 | publisher={SIAM} 17 | } 18 | 19 | @article{moler2003nineteen, 20 | title={Nineteen dubious ways to compute the exponential of a matrix, twenty-five years later}, 21 | author={Moler, Cleve and Van Loan, Charles}, 22 | journal={SIAM review}, 23 | volume={45}, 24 | number={1}, 25 | pages={3--49}, 26 | year={2003}, 27 | publisher={SIAM} 28 | } 29 | 30 | @article{flynn1966very, 31 | title={Very high-speed computing systems}, 32 | author={Flynn, Michael J}, 33 | journal={Proceedings of the IEEE}, 34 | volume={54}, 35 | number={12}, 36 | pages={1901--1909}, 37 | year={1966}, 38 | publisher={IEEE} 39 | } 40 | 41 | @article{matsumoto1998mersenne, 42 | title={Mersenne twister: a 623-dimensionally equidistributed uniform pseudo-random number generator}, 43 | author={Matsumoto, Makoto and Nishimura, Takuji}, 44 | journal={ACM Transactions on Modeling and Computer Simulation (TOMACS)}, 45 | volume={8}, 46 | number={1}, 47 | pages={3--30}, 48 | year={1998}, 49 | publisher={ACM New York, NY, USA} 50 | } 51 | 52 | @article{von195113, 53 | title={13. various techniques used in connection with random digits}, 54 | author={Von Neumann, John}, 55 | journal={Appl. Math Ser}, 56 | volume={12}, 57 | number={36-38}, 58 | pages={3}, 59 | year={1951} 60 | } 61 | 62 | @book{stewart2009probability, 63 | title={Probability, Markov chains, queues, and simulation}, 64 | author={Stewart, William J}, 65 | year={2009}, 66 | publisher={Princeton university press} 67 | } 68 | 69 | @article{nash1950equilibrium, 70 | title={Equilibrium points in n-person games}, 71 | author={Nash, John F and others}, 72 | journal={Proceedings of the national academy of sciences}, 73 | volume={36}, 74 | number={1}, 75 | pages={48--49}, 76 | year={1950}, 77 | publisher={USA} 78 | } 79 | 80 | @book{maschler2013game, 81 | title={Game theory}, 82 | author={Maschler, Michael and Solan, Eilon and Zamir, Shmuel}, 83 | journal={Cambridge University Press, Cambridge, pp. xxvi}, 84 | volume={979}, 85 | pages={4}, 86 | year={2013} 87 | } 88 | 89 | @article{smith1974theory, 90 | title={The theory of games and the evolution of animal conflicts}, 91 | author={Smith, J Maynard}, 92 | journal={Journal of theoretical biology}, 93 | volume={47}, 94 | number={1}, 95 | pages={209--221}, 96 | year={1974}, 97 | publisher={Elsevier} 98 | } 99 | 100 | @book{fudenberg1998theory, 101 | title={The theory of learning in games}, 102 | author={Fudenberg, Drew and Drew, Fudenberg and Levine, David K and Levine, David K}, 103 | volume={2}, 104 | year={1998}, 105 | publisher={MIT press} 106 | } 107 | 108 | @article{axelrod1981evolution, 109 | title={The evolution of cooperation}, 110 | author={Axelrod, Robert and Hamilton, William Donald}, 111 | journal={science}, 112 | volume={211}, 113 | number={4489}, 114 | pages={1390--1396}, 115 | year={1981}, 116 | publisher={American Association for the Advancement of Science} 117 | } 118 | 119 | @book{burden2001numerical, 120 | title={Numerical analysis}, 121 | author={Burden, Richard L and Faires, J Douglas and Reynolds, Albert C}, 122 | year={2001}, 123 | publisher={Brooks/cole Pacific Grove, CA} 124 | } 125 | 126 | @book{schelling2006micromotives, 127 | title={Micromotives and macrobehavior}, 128 | author={Schelling, Thomas C}, 129 | year={2006}, 130 | publisher={WW Norton \& Company} 131 | } 132 | 133 | @book{conforti2014integer, 134 | title={Integer programming}, 135 | author={Conforti, Michele and Cornu{\'e}jols, G{\'e}rard and Zambelli, Giacomo and others}, 136 | volume={271}, 137 | year={2014}, 138 | publisher={Springer} 139 | } 140 | 141 | @book{michalewicz2013solve, 142 | title={How to solve it: modern heuristics}, 143 | author={Michalewicz, Zbigniew and Fogel, David B}, 144 | year={2013}, 145 | publisher={Springer Science \& Business Media} 146 | } 147 | 148 | @article{croes1958method, 149 | title={A method for solving traveling-salesman problems}, 150 | author={Croes, Georges A}, 151 | journal={Operations research}, 152 | volume={6}, 153 | number={6}, 154 | pages={791--812}, 155 | year={1958}, 156 | publisher={INFORMS} 157 | } 158 | -------------------------------------------------------------------------------- /src/frontmatter/contributor/main.tex: -------------------------------------------------------------------------------- 1 | %%\twocolumn 2 | \chapter*{Contributors} 3 | 4 | \begin{multicols}{2} 5 | \contributor{Geraint Palmer}{Cardiff University School of Mathematics}{Cardiff, Wales, UK} 6 | 7 | \contributor{Vincent Knight}{Cardiff University School of Mathematics}{Cardiff, Wales, UK} 8 | \end{multicols} 9 | -------------------------------------------------------------------------------- /src/frontmatter/foreword/main.tex: -------------------------------------------------------------------------------- 1 | \chapter*{Foreword} 2 | 3 | This is the foreword 4 | -------------------------------------------------------------------------------- /src/frontmatter/preamble/main.tex: -------------------------------------------------------------------------------- 1 | \newtheorem{theorem}{Theorem}[chapter] \newtheorem{exercise}{Exercise}[chapter] \newtheorem{example}{Example}[chapter] \newtheorem{definition}{Definition}[chapter] \newtheorem{proof}{Proof}[chapter] -------------------------------------------------------------------------------- /src/frontmatter/preface/main.tex: -------------------------------------------------------------------------------- 1 | \chapter*{Preface} 2 | 3 | This is the preface. 4 | -------------------------------------------------------------------------------- /src/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drvinceknight/amwoss/eb22077e492fbce8979c6c35eb52427ad1e70112/src/main.pdf -------------------------------------------------------------------------------- /src/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[Alon2,singlecolor,11pt]{Alon} 2 | 3 | \usepackage{fixltx2e,fix-cm} 4 | \usepackage[english]{babel} 5 | \usepackage{epigrafica} 6 | \usepackage[LGR,OT1]{fontenc} 7 | % \usepackage{mathptmx,txfonts} % We are not sure why this is commented out. 8 | % The book does not compile with it loaded. 9 | \usepackage{amsmath} 10 | \usepackage{amssymb} 11 | \usepackage{booktabs} 12 | \usepackage{diagbox} 13 | \usepackage{graphicx} 14 | \usepackage{lmodern} 15 | \usepackage{makeidx} 16 | \usepackage{multicol} 17 | \usepackage{lscape} 18 | \usepackage{subfigure} 19 | \usepackage{booktabs} 20 | \usepackage{multirow} 21 | \usepackage[breakable,skins]{tcolorbox} 22 | 23 | \renewcommand{\sfdefault}{txss} 24 | 25 | \usepackage{changepage}% for text width to include margin 26 | 27 | \usepackage{customenvironments} 28 | 29 | % Adding our own packages 30 | \usepackage{standalone} 31 | \usepackage{tikz} 32 | \usepackage{varwidth} 33 | 34 | \usetikzlibrary{arrows} 35 | \usetikzlibrary{calc} 36 | \usetikzlibrary{decorations.markings} 37 | \usetikzlibrary{decorations.text} 38 | \usetikzlibrary{shapes} 39 | \usepackage{imakeidx} 40 | 41 | \usepackage[style=numeric-comp]{biblatex} 42 | \addbibresource{bibliography.bib} 43 | 44 | %--------------------- 45 | 46 | 47 | \frenchspacing 48 | \tolerance=5000 49 | 50 | \makeindex 51 | 52 | \include{frontmatter/preamble}%place custom commands and macros here 53 | \usepackage{version} 54 | 55 | % Non publisher libraries 56 | 57 | \title{Applied Mathematics Problems with Open-Source Software} 58 | % TODO The sub title is currently missing and needs to be present on the title 59 | % page. The above title is used for the running title. The full title is: 60 | % "Applied Mathematics Problems with Open-Source Software: Operational Research 61 | % with Python and R. 62 | \author{Vincent Knight and Geraint Palmer} 63 | 64 | \begin{document} 65 | 66 | \frontmatter 67 | 68 | %%%\maketitle%This is a placeholder titlepage, it will not be final. 69 | 70 | %%%Placeholder for front matter 71 | 72 | \halftitle 73 | 74 | \booktitle 75 | 76 | \locpage 77 | 78 | \cleardoublepage 79 | \setcounter{page}{7} %previous pages reserved for frontmatter to be added later 80 | \tableofcontents 81 | %%\listoffigures 82 | %%\listoftables 83 | 84 | % Geraint and Vince decided to comment this out for now. 85 | %\include{frontmatter/foreword/main} 86 | %\include{frontmatter/preface/main} 87 | \include{frontmatter/contributor/main} 88 | 89 | 90 | \mainmatter 91 | 92 | \part{Getting Started} 93 | \input{./chapters/01/main.tex} 94 | 95 | \part{Probabilistic Modelling} 96 | \input{./chapters/02/main.tex} 97 | \input{./chapters/03/main.tex} 98 | \part{Dynamical Systems} 99 | \input{./chapters/04/main.tex} 100 | \input{./chapters/05/main.tex} 101 | \part{Emergent Behaviour} 102 | \input{./chapters/06/main.tex} 103 | \input{./chapters/07/main.tex} 104 | \part{Optimisation} 105 | \input{./chapters/08/main.tex} 106 | \input{./chapters/09/main.tex} 107 | 108 | \printbibliography 109 | 110 | \printindex 111 | \cleardoublepage 112 | \end{document} 113 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import pathlib 3 | import re 4 | import subprocess 5 | import sys 6 | import tempfile 7 | 8 | import dwys 9 | 10 | from invoke import task 11 | 12 | import known 13 | 14 | substitutions = { 15 | "# nolint": "", 16 | "prob.solve(pulp.apis.PULP_CBC_CMD(msg=False))": "prob.solve()", 17 | ", warn.conflicts = FALSE, quietly = TRUE)": ")", 18 | } 19 | 20 | 21 | @task 22 | def delenv(c): 23 | """ 24 | Delete the conda environment created by `inv env`. 25 | """ 26 | c.run("conda remove --name ampwoss --all") 27 | 28 | @task 29 | def env(c): 30 | """ 31 | Finish the installation of any libraries and packages need for the 32 | environment. 33 | 34 | These are installation steps that are not able to be included in the 35 | anaconda environment file `environment.yml` (for example some R packages do 36 | not have anaconda binaries). 37 | """ 38 | c.run( 39 | """Rscript -e 'install.packages("simmer", repos="http://cran.us.r-project.org")'""" 40 | ) 41 | c.run( 42 | """Rscript -e 'install.packages("expm", repos="http://cran.us.r-project.org")'""" 43 | ) 44 | c.run( 45 | """Rscript -e 'install.packages("Recon", repos="http://cran.us.r-project.org")'""" 46 | ) 47 | # TODO Reintroduce this environment when aiming to test the R linear 48 | # programming chapter. 49 | # See: https://github.com/drvinceknight/amwoss/issues/60 50 | # if sys.platform == "darwin": 51 | # c.run("brew tap coin-or-tools/coinor") 52 | # c.run("brew install cbc") 53 | # else: 54 | # c.run("sudo apt-get install coinor-libcbc-dev coinor-libclp-dev") 55 | # c.run( 56 | # """Rscript -e 'install.packages("ROI", repos="http://cran.us.r-project.org")'""" 57 | # ) 58 | # c.run( 59 | # """Rscript -e 'remotes::install_github("dirkschumacher/rcbc")'""" 60 | # ) 61 | # c.run( 62 | # """Rscript -e 'remotes::install_github("dirkschumacher/ROI.plugin.cbc")'""" 63 | # ) 64 | 65 | @task 66 | def build(c, substitutions=substitutions): 67 | """ 68 | Copy the src directory in to build and then make all substitutions. It also 69 | ensures all once \index'd words are all \index'd. 70 | 71 | diff -u src/chapters/01/main.tex build/chapters/01/main.tex 72 | 73 | Should be used to carefully ensure this process has not created any unwanted 74 | scenarios. 75 | """ 76 | c.run("rm -rf build/") 77 | c.run("cp -r src build") 78 | for key, value in substitutions.items(): 79 | c.run(f"cd build; sed '-i.bak' 's/{key}/{value}/g' chapters/*/main.tex") 80 | 81 | @task 82 | def compile(c): 83 | """ 84 | Compile the LaTeX document. 85 | """ 86 | build(c) 87 | c.run("cd build; latexmk --xelatex -shell-escape main.tex") 88 | 89 | @task 90 | def analyse(c): 91 | """ 92 | Analyse the document style and wording. 93 | 94 | This makes use of the `diction` and `style` packages. 95 | 96 | Installation: 97 | 98 | Linux: 99 | 100 | apt install diction 101 | apt install style 102 | 103 | OS X: 104 | 105 | brew install diction 106 | brew install style 107 | """ 108 | book = list(pathlib.Path("./src/").glob("**/*.tex")) 109 | for path in book: 110 | print(path) 111 | c.run(f"detex {path} | diction -s -L en_gb") 112 | c.run(f"detex {path} | style -L en_gb") 113 | 114 | @task 115 | def doctest(c, style=False, path=None): 116 | """ 117 | Run doctests on all LaTeX documents 118 | 119 | - Checks code gives expected output using dwys 120 | - Check style of python code with black 121 | - Check style of R code with lintr (TODO Check that this works) 122 | """ 123 | max_column_length = 66 124 | pyin_pattern = re.compile(r"\\begin\{pyin\}\n(.*?)\\end\{pyin\}", re.DOTALL) 125 | pyout_pattern = re.compile(r"\\begin\{pyout\}\n(.*?)\n\\end\{pyout\}", re.DOTALL) 126 | Rin_pattern = re.compile(r"\\begin\{Rin\}\n(.*?)\\end\{Rin\}", re.DOTALL) 127 | Rout_pattern = re.compile(r"\\begin\{Rout\}\n(.*?)\n\\end\{Rout\}", re.DOTALL) 128 | files_to_ignore_style = () 129 | pyexecution_command = "python" 130 | Rexecution_command = "Rscript" 131 | 132 | if path is None: 133 | paths = list(pathlib.Path("./src/").glob("**/*.tex")) 134 | else: 135 | paths = [pathlib.Path(path)] 136 | dir_for_python_input_files = tempfile.mkdtemp() 137 | dir_for_R_input_files = tempfile.mkdtemp() 138 | 139 | exit_codes = [] 140 | temp_files_to_ignore_style = [] 141 | for i, p in enumerate(paths): 142 | print(f"Testing {p}") 143 | text = p.read_text() 144 | 145 | for in_pattern, out_pattern, execution_command, input_dir in ( 146 | ( 147 | pyin_pattern, 148 | pyout_pattern, 149 | pyexecution_command, 150 | dir_for_python_input_files, 151 | ), 152 | (Rin_pattern, Rout_pattern, Rexecution_command, dir_for_R_input_files), 153 | ): 154 | # Parse the code 155 | input_code, output_code = dwys.parse( 156 | string=text, in_pattern=in_pattern, out_pattern=out_pattern 157 | ) 158 | 159 | if execution_command == "python": 160 | input_filename = f"{dir_for_python_input_files}/{i}.py" 161 | else: 162 | input_filename = f"{dir_for_R_input_files}/{i}.R" 163 | 164 | if str(p) in files_to_ignore_style: 165 | temp_files_to_ignore_style.append(input_filename) 166 | 167 | try: 168 | diff, output, expected_output = dwys.diff( 169 | input_code=input_code, 170 | expected_output_code=output_code, 171 | execution_command=execution_command, 172 | input_filename=input_filename, 173 | ) 174 | diff = list(diff) 175 | 176 | try: 177 | assert diff == [] 178 | exit_codes.append(0) 179 | print(f"{execution_command}: ✅") 180 | except AssertionError: 181 | print(f"{execution_command}: ❌ Input does not match output in {p}") 182 | print(f"Obtained output:\n{output}") 183 | print(f"Expected output:\n{expected_output}") 184 | exit_codes.append(1) 185 | 186 | except AssertionError: 187 | print(f"{execution_command}: ❌ Syntax error in {p}") 188 | print(input_filename) 189 | print(subprocess.check_output([execution_command, input_filename])) 190 | exit_codes.append(1) 191 | 192 | print("Ensuring column lengths fit book") 193 | for path in itertools.chain( 194 | pathlib.Path(dir_for_R_input_files).glob("*"), 195 | pathlib.Path(dir_for_python_input_files).glob("*"), 196 | ): 197 | lines = path.read_text().split("\n") 198 | for line in lines: 199 | if len(line) > max_column_length: 200 | print( 201 | f"'{line[:max_column_length]}' is too long ({len(line)} > {max_column_length})" 202 | ) 203 | exit_codes.append(1) 204 | 205 | print("Check spelling") 206 | for path in paths: 207 | latex = path.read_text() 208 | aspell_output = subprocess.check_output( 209 | ["aspell", "-t", "--list", "--lang=en_GB"], input=latex, text=True 210 | ) 211 | incorrect_words = set(aspell_output.split("\n")) - {""} - known.words 212 | if len(incorrect_words) > 0: 213 | print(f"In {path} the following words are not known: ") 214 | for string in sorted(incorrect_words): 215 | print(string) 216 | exit_codes.append(1) 217 | 218 | if style is True: 219 | for file_path in temp_files_to_ignore_style: 220 | path = pathlib.Path(file_path) 221 | path.unlink() 222 | exit_codes += check_style(dir_for_python_input_files, 223 | dir_for_R_input_files, max_column_length) 224 | 225 | exit_code = max(exit_codes) 226 | if exit_code == 0: 227 | print("✅✅✅ ALL TESTS HAVE PASSED! ✅✅✅") 228 | else: 229 | print("❌❌❌ A test has failed. ❌❌❌") 230 | sys.exit(exit_code) 231 | 232 | def check_style(dir_for_python_input_files, 233 | dir_for_R_input_files, max_column_length): 234 | exit_codes = [] 235 | print("Running black") 236 | ec = subprocess.call( 237 | ["black", "--check", "--diff", f"-l {max_column_length}", dir_for_python_input_files] 238 | ) 239 | exit_codes.append(ec) 240 | 241 | print("Running docformatter") 242 | ec = subprocess.call( 243 | [ 244 | "docformatter", 245 | "--check", 246 | "--wrap-descriptions", 247 | f"{max_column_length}", 248 | "--wrap-summaries", 249 | f"{max_column_length}", 250 | "-r", 251 | dir_for_python_input_files, 252 | ] 253 | ) 254 | if ec > 0: 255 | diff = subprocess.check_output( 256 | [ 257 | "docformatter", 258 | "--wrap-descriptions", 259 | f"{max_column_length}", 260 | "--wrap-summaries", 261 | f"{max_column_length}", 262 | "-r", 263 | dir_for_python_input_files, 264 | ] 265 | ) 266 | print(diff.decode("utf-8")) 267 | else: 268 | print("Docstrings follow PEP 257 ✅") 269 | exit_codes.append(min(ec, 1)) 270 | 271 | print("Running lintr") 272 | # This excludes one specific lintr called 'object_usage_linter' as this is a 273 | # known issue with the lintr package in R. 274 | for path in pathlib.Path(dir_for_R_input_files).glob("*"): 275 | output = subprocess.check_output( 276 | [ 277 | "Rscript", 278 | "-e", 279 | f"lintr::lint('{path}', linters=lintr::default_linters[names(lintr::default_linters) != 'object_usage_linter'])", 280 | ] 281 | ) 282 | if len(output) > 0: 283 | print(output.decode("utf-8")) 284 | exit_codes.append(1) 285 | return exit_codes 286 | 287 | @task 288 | def validatenbs(c): 289 | c.run("python -m pip install nbval pytest") 290 | c.run("python -m pytest -vv --nbval --current-env") 291 | --------------------------------------------------------------------------------