From e626b52b11930426cbbc06c34b407c13da96bfd4 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 16:48:51 +0200 Subject: [PATCH 1/9] examples/boid: Update viz Update the visualisation with sliders sliders for coloured boids. --- examples/basic/boid_flockers/app.py | 47 ++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/examples/basic/boid_flockers/app.py b/examples/basic/boid_flockers/app.py index 205cb21858c..a38c23fd44d 100644 --- a/examples/basic/boid_flockers/app.py +++ b/examples/basic/boid_flockers/app.py @@ -1,26 +1,57 @@ from boid_flockers.model import BoidFlockers from mesa.visualization import SolaraViz, make_space_matplotlib - +from mesa.visualization import Slider def boid_draw(agent): - return {"color": "tab:red"} + if not agent.neighbors: # Only for the first Frame + neighbors = len(agent.model.space.get_neighbors(agent.pos, agent.vision, False)) + else: + neighbors = len(agent.neighbors) + if neighbors <= 1: + return {"color": "red", "size": 20} + elif neighbors >= 2: + return {"color": "green", "size": 20} model_params = { - "population": 100, + "population": Slider( + label="Number of boids", + value=100, + min=10, + max=200, + step=10, + ), "width": 100, "height": 100, - "speed": 5, - "vision": 10, - "separation": 2, + "speed": Slider( + label="Speed of Boids", + value=5, + min=1, + max=20, + step=1, + ), + "vision": Slider( + label="Vision of Bird (radius)", + value=10, + min=1, + max=50, + step=1, + ), + "separation": Slider( + label="Minimum Separation", + value=2, + min=1, + max=20, + step=1, + ), } -model = BoidFlockers(100, 100, 100, 5, 10, 2) +model = BoidFlockers() page = SolaraViz( model, [make_space_matplotlib(agent_portrayal=boid_draw)], model_params=model_params, - name="BoidFlockers", + name="Boid Flocking Model", ) page # noqa From f3cae9a84cbac9665001283a23ba6d801f8dd1a6 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 16:54:40 +0200 Subject: [PATCH 2/9] examples/boid: Remove old stuff --- .../basic/boid_flockers/Flocker Test.ipynb | 113 ------------------ examples/basic/boid_flockers/Readme.md | 6 +- .../boid_flockers/SimpleContinuousModule.py | 29 ----- .../boid_flockers/boid_flockers/server.py | 64 ---------- .../boid_flockers/simple_continuous_canvas.js | 78 ------------ examples/basic/boid_flockers/requirements.txt | 3 - examples/basic/boid_flockers/run.py | 3 - 7 files changed, 1 insertion(+), 295 deletions(-) delete mode 100644 examples/basic/boid_flockers/Flocker Test.ipynb delete mode 100644 examples/basic/boid_flockers/boid_flockers/SimpleContinuousModule.py delete mode 100644 examples/basic/boid_flockers/boid_flockers/server.py delete mode 100644 examples/basic/boid_flockers/boid_flockers/simple_continuous_canvas.js delete mode 100644 examples/basic/boid_flockers/requirements.txt delete mode 100644 examples/basic/boid_flockers/run.py diff --git a/examples/basic/boid_flockers/Flocker Test.ipynb b/examples/basic/boid_flockers/Flocker Test.ipynb deleted file mode 100644 index c757f3a88ed..00000000000 --- a/examples/basic/boid_flockers/Flocker Test.ipynb +++ /dev/null @@ -1,113 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "from boid_flockers.model import BoidFlockers\n", - "\n", - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def draw_boids(model):\n", - " x_vals = []\n", - " y_vals = []\n", - " for boid in model.agents:\n", - " x, y = boid.pos\n", - " x_vals.append(x)\n", - " y_vals.append(y)\n", - " fig = plt.figure(figsize=(10, 10))\n", - " ax = fig.add_subplot(111)\n", - " ax.scatter(x_vals, y_vals)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "model = BoidFlockers(100, 100, 100, speed=5, vision=5, separation=1)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "for i in range(50):\n", - " model.step()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlwAAAJPCAYAAACpXgqFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3W+snNd9H/jvT1LMUnEVmQwg+Y9iB22MxEbqVt1N04Kt\nuGtLVI3WirCA0wAu1LTJInAXN1rSrSUnqPUi68ZuyPVqF4bRJnaJoPZWTaPYKdwV2TRMs9ggzsZx\n7Ur22img1rIhuiHtMHEU1TbPvpih7tXVveS9d+bcZ56ZzwcYaJ5n5rlz9PDOne+c8zvnqdZaAADo\n57qhGwAAsOwELgCAzgQuAIDOBC4AgM4ELgCAzgQuAIDOdhS4quoDVXW+qj69Yd8/qqrPVNW/r6pf\nrKpv2/DYg1X1+ar6bFXd1aPhAABjsdMerg8muXvTvjNJXttae12SzyV5MEmq6jVJfjDJa6bHvK+q\n9KQBACtrR0GotfbrSb6yad/Z1trl6eZvJnnF9P49ST7cWvt6a+3JJL+b5Pvm01wAgPGZV8/T307y\nsen9lyV5asNjTyV5+ZxeBwBgdGYOXFX1E0n+a2vtQ1d5musHAQAr64ZZDq6qv5XkjUlev2H3F5Pc\ntmH7FdN9m48VwgCA0Wit1V6P3XPgqqq7k/y9JHe01v54w0MfTfKhqjqVyVDidyX5+FY/Y5aGr7qq\neqi19tDQ7Rgr5282zt/eOXezcf5m4/zt3awdRTsKXFX14SR3JPn2qvpCkndmMivxRUnOVlWS/EZr\n7a2ttSeq6pEkTyT5RpK3ttb0ZgEAK2tHgau19kNb7P7AVZ7/riTv2mujAACWifWxxuvc0A0YuXND\nN2Dkzg3dgBE7N3QDRu7c0A0YuXNDN2BV1VCjfVXV1HABAGMwa27RwwUA0JnABQDQmcAFANCZwAUA\n0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZ\nwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAF\nANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQ\nmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnA\nBQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA\n0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANDZjgJXVX2gqs5X1ac37DtU\nVWer6nNVdaaqbt7w2INV9fmq+mxV3dWj4QAAY7HTHq4PJrl7074Hkpxtrb06ya9Mt1NVr0nyg0le\nMz3mfVWlJw0AWFk7CkKttV9P8pVNu9+U5PT0/ukkPzC9f0+SD7fWvt5aezLJ7yb5vtmbCgAwTrP0\nPN3SWjs/vX8+yS3T+y9L8tSG5z2V5OUzvA4AwKjNZaivtdaStKs9ZR6vAwAwRjfMcOz5qrq1tfZ0\nVb00yZen+7+Y5LYNz3vFdN8LVNVDGzbPtdbOzdAedqiqjiWHTky2Lp5srT02bIsAYLFU1dEkR+f2\n8yadUzt64Vcl+eXW2vdOt9+T5EJr7d1V9UCSm1trD0yL5j+USd3Wy5P8myR/um16oapqrbWa1/8I\nOzMJWzc9mjx8cLJn7XLyzU8mX3uH4AUAW5s1t+yoh6uqPpzkjiTfXlVfSPIPkvx0kkeq6u8keTLJ\nm5OktfZEVT2S5Ikk30jy1s1hiyEdOpGcOpjcd2XHdcn7b08+9ZGqlzyeXHdBrxcAzNeOAldr7Ye2\neegN2zz/XUnetddGsd+uT3LjgeRnbp9srx2pqnuFLgCYj1lquBiliyeTtSNJpkOKb0/y3Ul+Jht6\nvQ4mx08kEbgAYA4sSLpiJr1Wl+5N7v9Ecv/l5C1Jnh26WQCw1HZcND/3F1Y0P7j12YrPHk6uf23y\n8IHJI2vPJJcMKQLA1Ky5ReAiiaUiAObJ39TlI3CxLW94gP23xfI7Rg2WwL4sC8H4rL/hT115w5t5\nCLAvXrD8jolICFzLyxseABaFwAUAc7V5+Z21Z5JLJwdtEoNTw7WkJkOKN34k+TPTmYefejb5o3sM\nKQL0p4Z2+ajh4ipuSPJj0/trQzYEYKVMA5aQxXMEriXy/G9UNx9O3ntgQw3XATVcADAMgWtJvHBW\n4v2Xh20RAHCFwLU0Ns9K/PR1ydrlPHf5JkWbADAUgWtpfW+Sb34yOX5hsn1J0SYADMQsxSWx15WN\nJ8fd/K7kulcmz/6n5GvvmDxidg0AXOHSPjxnt9OQpyHtI+sXrX5bkj/4enLgsgtZA8A6y0LwnN1P\nQz50Ijm1cSZjkp/8luSnYoV6AJif64ZuAADAstPDtdIunkzW/kqSTUOKa5fX95ndCACzUsO1gjbV\nep1Lbv4fFM0DwPYUzbMre53NCACrbNbcooZrhKrqWNXhM5NbHdvd0YdOTMLWfZncHj643psFAPSg\nhmtkXngJn7UjVbWLHqrLh/u1DgDYisA1Opsv4bPzZRsmYe3G106K469Ye1ZRPAD0JXCtlCvrbt2a\n5B8n+VKSbz6ufgsA+hK4RufiyWTtSJKNRe+77KE6Nr2dzvq1FgGAXsxSHKHdXsLn+ceZoQgAu2VZ\nCHZlr2ENAFaZwAUA0Jl1uAAAFpzAtQJmWygVAJiVIcUlp1AeAGY3a26xLMTS2/tCqQDAfBhSBADo\nTA/X0pvHQqkAwCzUcK0Aa28BwGyswwUA0Jl1uAAAFpzABQDQmcAFANCZwAUA0JnABQDQmcAFANCZ\nwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAF\nANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQ\nmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0NnMgauqHqyqx6vq01X1oao6UFWHqupsVX2uqs5U1c3z\naCwAzENVHas6fGZyq2NDt4flV621vR9c9aok/zbJ97TWnq2qf57kY0lem+T3Wmvvqaq3J3lJa+2B\nTce21lrt+cUBWGmToHToxGTr4snW2mM7P+6mR5OHD072rD2TXLp3p8ezmmbNLTfM+PqXknw9yY1V\n9c0kNyb5UpIHk9wxfc7pJOeSPLDVDwCA3VoPTaeuhKYjVbXD0HToxOS4+67sOJgcP5FE4KKbmYYU\nW2sXk5xM8p8zCVpfba2dTXJLa+389Gnnk9wyUysB4HkOnZj0UN2Xye3hg+u9XbB4Zurhqqo/leT+\nJK9K8vtJ/kVVvWXjc1prraq2HLesqoc2bJ5rrZ2bpT0AcG0XTyZrR5JsHFI8OWiTWDhVdTTJ0bn9\nvBlruH4wyZ2ttR+Zbv/NJN+f5L9P8t+11p6uqpcm+dXW2ndvOlYNFwB7Mmsd1l7rv1hds+aWWQPX\n65L8syT/bZI/TvJPk3w8ySuTXGitvbuqHkhys6J5AOZJaGI/DRq4pg34+5kMoF9O8okkP5LkTyZ5\n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\nMinr9tba9ya5PsnfiHN3NR/M5LNhoy3Pl8/cLW11/uaWWfb95FowdWbfl+R3W2tPtta+nuT/zOTc\nsYXW2tOttU9O7/9hks9kMpnjTUlOT592OskPDNPCxVdVr0jyxiQ/m+TKDB3nbwem34b/cmvtA0nS\nWvtGa+334/ztxKVMvjDdWFU3JLkxyZfi3G2rtfbrSb6yafd258tn7iZbnb95Zpah06wFU3fv5Um+\nsGHbedqh6TfmP5fJm+aW1tr56UPnk9wyULPG4H9N8veSXN6wz/nbme9M8l+q6oNV9Ymq+idV9a1x\n/q6ptXYxk5nw/zmToPXV1trZOHe7td358pm7ezNlli6Bazpe/Oktbn99w3MsmLo3zskeVNWLk/zL\nJD/eWvuDjY9NZ9E6r1uoqr+W5Muttd/Jeu/W8zh/V3VDktuTvK+1dnuSr2XTEJjzt7Wq+lNJ7k/y\nqkw+3F5cVW/Z+Bznbnd2cL6cy23MI7PMsizE9q/Y2p1Xe3y6YOobk7x+w+4vJrltw/Yrpvt4vs3n\n6bY8P2WzSVV9SyZh6+dba7803X2+qm5trT1dVS9N8uXhWrjQ/lKSN1XVG5P8iSQ3VdXPx/nbqaeS\nPNVa+63p9i9kUgPytPN3Tf9Nkv+ntXYhSarqF5P8xTh3u7Xde9Vn7g7NK7MMMUvx7kyGJ+65sjr9\n1EeT/I2qelFVfWeS70ry8f1u3wj8v0m+q6peVVUvyqRo76MDt2lhVVUl+bkkT7TW3rvhoY8muW96\n/74kv7T5WJLW2jtaa7e11r4zk4Llf9ta+5tx/naktfZ0ki9U1aunu96Q5PEkvxzn71o+m+T7q+rg\n9H38hkwmbjh3u7Pde9Vn7g7MM7Ps+zpcVfX5TBZMvTjd9RuttbdOH3tHJmOk38hk6OexfW3cSFTV\nX03y3kxm7fxca+0fDtykhVVVR5L8uySfynp374OZvDEeSfIdSZ5M8ubW2leHaONYVNUdSU601t5U\nVYfi/O1IVb0ukwkHL0ryH5P8cCbvXefvGqrq72cSEi4n+USSH0nyJ+PcbamqPpzkjiTfnkm91j9I\n8pFsc7585j7fFufvnZl8Xswls1j4FACgs6FnKQIALD2BCwCgM4ELAKAzgQsAoDOBCwCgM4ELAKAz\ngQsAoDOBCwCgs/8fICoqGcqtXKgAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "draw_boids(model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.4.2" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/examples/basic/boid_flockers/Readme.md b/examples/basic/boid_flockers/Readme.md index d1f4a987399..da78e40d71a 100644 --- a/examples/basic/boid_flockers/Readme.md +++ b/examples/basic/boid_flockers/Readme.md @@ -35,11 +35,7 @@ Directly run the file ``run.py`` in the terminal. e.g. ## Files * [boid_flockers/model.py](boid_flockers/model.py): Core model file; contains the Boid Model and Boid Agent class. -* [boid_flockers/SimpleContinuousModule.py](boid_flockers/SimpleContinuousModule.py): Defines ``SimpleCanvas``, the Python side of a custom visualization module for drawing agents with continuous positions. -* [boid_flockers/simple_continuous_canvas.js](boid_flockers/simple_continuous_canvas.js): JavaScript side of the ``SimpleCanvas`` visualization module; takes the output generated by the Python ``SimpleCanvas`` element and draws it in the browser window via HTML5 canvas. -* [boid_flockers/server.py](boid_flockers/server.py): Sets up the visualization; uses the SimpleCanvas element defined above -* [run.py](run.py) Launches the visualization. -* [Flocker_Test.ipynb](Flocker_Test.ipynb): Tests the model in a Jupyter notebook. +* [app.py](app.py): Visualization code. ## Further Reading diff --git a/examples/basic/boid_flockers/boid_flockers/SimpleContinuousModule.py b/examples/basic/boid_flockers/boid_flockers/SimpleContinuousModule.py deleted file mode 100644 index ec670d7af1c..00000000000 --- a/examples/basic/boid_flockers/boid_flockers/SimpleContinuousModule.py +++ /dev/null @@ -1,29 +0,0 @@ -import mesa - - -class SimpleCanvas(mesa.visualization.VisualizationElement): - local_includes = ["boid_flockers/simple_continuous_canvas.js"] - - def __init__(self, portrayal_method=None, canvas_height=500, canvas_width=500): - """ - Instantiate a new SimpleCanvas - """ - self.portrayal_method = portrayal_method - self.canvas_height = canvas_height - self.canvas_width = canvas_width - new_element = ( - f"new Simple_Continuous_Module({self.canvas_width}, {self.canvas_height})" - ) - self.js_code = "elements.push(" + new_element + ");" - - def render(self, model): - space_state = [] - for obj in model.agents: - portrayal = self.portrayal_method(obj) - x, y = obj.pos - x = (x - model.space.x_min) / (model.space.x_max - model.space.x_min) - y = (y - model.space.y_min) / (model.space.y_max - model.space.y_min) - portrayal["x"] = x - portrayal["y"] = y - space_state.append(portrayal) - return space_state diff --git a/examples/basic/boid_flockers/boid_flockers/server.py b/examples/basic/boid_flockers/boid_flockers/server.py deleted file mode 100644 index 190c6533abb..00000000000 --- a/examples/basic/boid_flockers/boid_flockers/server.py +++ /dev/null @@ -1,64 +0,0 @@ -import mesa - -from .model import BoidFlockers -from .SimpleContinuousModule import SimpleCanvas - - -def boid_draw(agent): - if not agent.neighbors: # Only for the first Frame - neighbors = len(agent.model.space.get_neighbors(agent.pos, agent.vision, False)) - else: - neighbors = len(agent.neighbors) - - if neighbors <= 1: - return {"Shape": "circle", "r": 2, "Filled": "true", "Color": "Red"} - elif neighbors >= 2: - return {"Shape": "circle", "r": 2, "Filled": "true", "Color": "Green"} - - -boid_canvas = SimpleCanvas( - portrayal_method=boid_draw, canvas_height=500, canvas_width=500 -) -model_params = { - "population": mesa.visualization.Slider( - name="Number of boids", - value=100, - min_value=10, - max_value=200, - step=10, - description="Choose how many agents to include in the model", - ), - "width": 100, - "height": 100, - "speed": mesa.visualization.Slider( - name="Speed of Boids", - value=5, - min_value=1, - max_value=20, - step=1, - description="How fast should the Boids move", - ), - "vision": mesa.visualization.Slider( - name="Vision of Bird (radius)", - value=10, - min_value=1, - max_value=50, - step=1, - description="How far around should each Boid look for its neighbors", - ), - "separation": mesa.visualization.Slider( - name="Minimum Separation", - value=2, - min_value=1, - max_value=20, - step=1, - description="What is the minimum distance each Boid will attempt to keep from any other", - ), -} - -server = mesa.visualization.ModularServer( - model_cls=BoidFlockers, - visualization_elements=[boid_canvas], - name="Boid Flocking Model", - model_params=model_params, -) diff --git a/examples/basic/boid_flockers/boid_flockers/simple_continuous_canvas.js b/examples/basic/boid_flockers/boid_flockers/simple_continuous_canvas.js deleted file mode 100644 index 812cadced8b..00000000000 --- a/examples/basic/boid_flockers/boid_flockers/simple_continuous_canvas.js +++ /dev/null @@ -1,78 +0,0 @@ -const ContinuousVisualization = function(width, height, context) { - this.draw = function(objects) { - for (const p of objects) { - if (p.Shape == "rect") - this.drawRectange(p.x, p.y, p.w, p.h, p.Color, p.Filled); - if (p.Shape == "circle") - this.drawCircle(p.x, p.y, p.r, p.Color, p.Filled); - }; - }; - - this.drawCircle = function(x, y, radius, color, fill) { - const cx = x * width; - const cy = y * height; - const r = radius; - - context.beginPath(); - context.arc(cx, cy, r, 0, Math.PI * 2, false); - context.closePath(); - - context.strokeStyle = color; - context.stroke(); - - if (fill) { - context.fillStyle = color; - context.fill(); - } - - }; - - this.drawRectange = function(x, y, w, h, color, fill) { - context.beginPath(); - const dx = w * width; - const dy = h * height; - - // Keep the drawing centered: - const x0 = (x*width) - 0.5*dx; - const y0 = (y*height) - 0.5*dy; - - context.strokeStyle = color; - context.fillStyle = color; - if (fill) - context.fillRect(x0, y0, dx, dy); - else - context.strokeRect(x0, y0, dx, dy); - }; - - this.resetCanvas = function() { - context.clearRect(0, 0, width, height); - context.beginPath(); - }; -}; - -const Simple_Continuous_Module = function(canvas_width, canvas_height) { - // Create the element - // ------------------ - - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: 'border:1px dotted' - }); - // Append it to body: - document.getElementById("elements").appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - const canvasDraw = new ContinuousVisualization(canvas_width, canvas_height, context); - - this.render = function(data) { - canvasDraw.resetCanvas(); - canvasDraw.draw(data); - }; - - this.reset = function() { - canvasDraw.resetCanvas(); - }; -}; diff --git a/examples/basic/boid_flockers/requirements.txt b/examples/basic/boid_flockers/requirements.txt deleted file mode 100644 index da2b9972efd..00000000000 --- a/examples/basic/boid_flockers/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -jupyter -matplotlib -mesa~=2.0 diff --git a/examples/basic/boid_flockers/run.py b/examples/basic/boid_flockers/run.py deleted file mode 100644 index 0d9ca624248..00000000000 --- a/examples/basic/boid_flockers/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from boid_flockers.server import server - -server.launch(open_browser=True) From 140002059c2153e2e2993879fb2f5b4511f05ec5 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 16:56:46 +0200 Subject: [PATCH 3/9] examples/boid: Structure in agents.py and model.py --- examples/basic/boid_flockers/Readme.md | 2 +- .../{boid_flockers/model.py => agents.py} | 75 +------------------ examples/basic/boid_flockers/app.py | 2 +- examples/basic/boid_flockers/model.py | 74 ++++++++++++++++++ 4 files changed, 78 insertions(+), 75 deletions(-) rename examples/basic/boid_flockers/{boid_flockers/model.py => agents.py} (55%) create mode 100644 examples/basic/boid_flockers/model.py diff --git a/examples/basic/boid_flockers/Readme.md b/examples/basic/boid_flockers/Readme.md index da78e40d71a..51e96382d19 100644 --- a/examples/basic/boid_flockers/Readme.md +++ b/examples/basic/boid_flockers/Readme.md @@ -34,7 +34,7 @@ Directly run the file ``run.py`` in the terminal. e.g. ## Files -* [boid_flockers/model.py](boid_flockers/model.py): Core model file; contains the Boid Model and Boid Agent class. +* [model.py](model.py): Core model file; contains the Boid Model and Boid Agent class. * [app.py](app.py): Visualization code. ## Further Reading diff --git a/examples/basic/boid_flockers/boid_flockers/model.py b/examples/basic/boid_flockers/agents.py similarity index 55% rename from examples/basic/boid_flockers/boid_flockers/model.py rename to examples/basic/boid_flockers/agents.py index ae3099f3549..857c7b98f23 100644 --- a/examples/basic/boid_flockers/boid_flockers/model.py +++ b/examples/basic/boid_flockers/agents.py @@ -1,15 +1,8 @@ -""" -Flockers -============================================================= -A Mesa implementation of Craig Reynolds's Boids flocker model. -Uses numpy arrays to represent vectors. -""" - -import mesa +from mesa import Agent import numpy as np -class Boid(mesa.Agent): +class Boid(Agent): """ A Boid-style flocker agent. @@ -80,67 +73,3 @@ def step(self): self.direction /= np.linalg.norm(self.direction) new_pos = self.pos + self.direction * self.speed self.model.space.move_agent(self, new_pos) - - -class BoidFlockers(mesa.Model): - """ - Flocker model class. Handles agent creation, placement and scheduling. - """ - - def __init__( - self, - seed=None, - population=100, - width=100, - height=100, - vision=10, - speed=1, - separation=1, - cohere=0.03, - separate=0.015, - match=0.05, - ): - """ - Create a new Flockers model. - - Args: - population: Number of Boids - width, height: Size of the space. - speed: How fast should the Boids move. - vision: How far around should each Boid look for its neighbors - separation: What's the minimum distance each Boid will attempt to - keep from any other - cohere, separate, match: factors for the relative importance of - the three drives. - """ - super().__init__(seed=seed) - self.population = population - self.vision = vision - self.speed = speed - self.separation = separation - - self.space = mesa.space.ContinuousSpace(width, height, True) - self.factors = {"cohere": cohere, "separate": separate, "match": match} - self.make_agents() - - def make_agents(self): - """ - Create self.population agents, with random positions and starting headings. - """ - for _ in range(self.population): - x = self.random.random() * self.space.x_max - y = self.random.random() * self.space.y_max - pos = np.array((x, y)) - direction = np.random.random(2) * 2 - 1 - boid = Boid( - model=self, - speed=self.speed, - direction=direction, - vision=self.vision, - separation=self.separation, - **self.factors, - ) - self.space.place_agent(boid, pos) - - def step(self): - self.agents.shuffle_do("step") diff --git a/examples/basic/boid_flockers/app.py b/examples/basic/boid_flockers/app.py index a38c23fd44d..8decb9832c5 100644 --- a/examples/basic/boid_flockers/app.py +++ b/examples/basic/boid_flockers/app.py @@ -1,4 +1,4 @@ -from boid_flockers.model import BoidFlockers +from model import BoidFlockers from mesa.visualization import SolaraViz, make_space_matplotlib from mesa.visualization import Slider diff --git a/examples/basic/boid_flockers/model.py b/examples/basic/boid_flockers/model.py new file mode 100644 index 00000000000..6f9177163d3 --- /dev/null +++ b/examples/basic/boid_flockers/model.py @@ -0,0 +1,74 @@ +""" +Flockers +============================================================= +A Mesa implementation of Craig Reynolds's Boids flocker model. +Uses numpy arrays to represent vectors. +""" + +import mesa +import numpy as np +from agents import Boid + + +class BoidFlockers(mesa.Model): + """ + Flocker model class. Handles agent creation, placement and scheduling. + """ + + def __init__( + self, + seed=None, + population=100, + width=100, + height=100, + vision=10, + speed=1, + separation=1, + cohere=0.03, + separate=0.015, + match=0.05, + ): + """ + Create a new Flockers model. + + Args: + population: Number of Boids + width, height: Size of the space. + speed: How fast should the Boids move. + vision: How far around should each Boid look for its neighbors + separation: What's the minimum distance each Boid will attempt to + keep from any other + cohere, separate, match: factors for the relative importance of + the three drives. + """ + super().__init__(seed=seed) + self.population = population + self.vision = vision + self.speed = speed + self.separation = separation + + self.space = mesa.space.ContinuousSpace(width, height, True) + self.factors = {"cohere": cohere, "separate": separate, "match": match} + self.make_agents() + + def make_agents(self): + """ + Create self.population agents, with random positions and starting headings. + """ + for _ in range(self.population): + x = self.random.random() * self.space.x_max + y = self.random.random() * self.space.y_max + pos = np.array((x, y)) + direction = np.random.random(2) * 2 - 1 + boid = Boid( + model=self, + speed=self.speed, + direction=direction, + vision=self.vision, + separation=self.separation, + **self.factors, + ) + self.space.place_agent(boid, pos) + + def step(self): + self.agents.shuffle_do("step") From 5aaba259ed88da61ce4152d55876d3862d2ac4c1 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 17:10:45 +0200 Subject: [PATCH 4/9] examples/boltzmann: Restructure - Restructure into agents.py and model.py - Make compute_gini a Model function --- .../basic/boltzmann_wealth_model/agents.py | 31 +++++++++++ .../basic/boltzmann_wealth_model/model.py | 53 ++++--------------- .../boltzmann_wealth_model/requirements.txt | 1 - .../basic/boltzmann_wealth_model/st_app.py | 2 + 4 files changed, 42 insertions(+), 45 deletions(-) create mode 100644 examples/basic/boltzmann_wealth_model/agents.py delete mode 100644 examples/basic/boltzmann_wealth_model/requirements.txt diff --git a/examples/basic/boltzmann_wealth_model/agents.py b/examples/basic/boltzmann_wealth_model/agents.py new file mode 100644 index 00000000000..12abff186a9 --- /dev/null +++ b/examples/basic/boltzmann_wealth_model/agents.py @@ -0,0 +1,31 @@ +from mesa import Agent + + +class MoneyAgent(Agent): + """An agent with fixed initial wealth.""" + + def __init__(self, model): + super().__init__(model) + self.wealth = 1 + + def move(self): + possible_steps = self.model.grid.get_neighborhood( + self.pos, moore=True, include_center=False + ) + new_position = self.random.choice(possible_steps) + self.model.grid.move_agent(self, new_position) + + def give_money(self): + cellmates = self.model.grid.get_cell_list_contents([self.pos]) + cellmates.pop( + cellmates.index(self) + ) # Ensure agent is not giving money to itself + if len(cellmates) > 0: + other = self.random.choice(cellmates) + other.wealth += 1 + self.wealth -= 1 + + def step(self): + self.move() + if self.wealth > 0: + self.give_money() diff --git a/examples/basic/boltzmann_wealth_model/model.py b/examples/basic/boltzmann_wealth_model/model.py index ac091a6ce34..26c2f676ddb 100644 --- a/examples/basic/boltzmann_wealth_model/model.py +++ b/examples/basic/boltzmann_wealth_model/model.py @@ -1,13 +1,5 @@ import mesa - - -def compute_gini(model): - agent_wealths = [agent.wealth for agent in model.agents] - x = sorted(agent_wealths) - N = model.num_agents - B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x)) - return 1 + (1 / N) - 2 * B - +from agents import MoneyAgent class BoltzmannWealthModel(mesa.Model): """A simple model of an economy where agents exchange currency at random. @@ -23,7 +15,8 @@ def __init__(self, N=100, width=10, height=10): self.grid = mesa.space.MultiGrid(width, height, True) self.datacollector = mesa.DataCollector( - model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} + model_reporters={"Gini": self.compute_gini}, + agent_reporters={"Wealth": "wealth"} ) # Create agents for _ in range(self.num_agents): @@ -39,39 +32,11 @@ def __init__(self, N=100, width=10, height=10): def step(self): self.agents.shuffle_do("step") - # collect data self.datacollector.collect(self) - def run_model(self, n): - for i in range(n): - self.step() - - -class MoneyAgent(mesa.Agent): - """An agent with fixed initial wealth.""" - - def __init__(self, model): - super().__init__(model) - self.wealth = 1 - - def move(self): - possible_steps = self.model.grid.get_neighborhood( - self.pos, moore=True, include_center=False - ) - new_position = self.random.choice(possible_steps) - self.model.grid.move_agent(self, new_position) - - def give_money(self): - cellmates = self.model.grid.get_cell_list_contents([self.pos]) - cellmates.pop( - cellmates.index(self) - ) # Ensure agent is not giving money to itself - if len(cellmates) > 0: - other = self.random.choice(cellmates) - other.wealth += 1 - self.wealth -= 1 - - def step(self): - self.move() - if self.wealth > 0: - self.give_money() + def compute_gini(self): + agent_wealths = [agent.wealth for agent in self.agents] + x = sorted(agent_wealths) + N = self.num_agents + B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x)) + return 1 + (1 / N) - 2 * B diff --git a/examples/basic/boltzmann_wealth_model/requirements.txt b/examples/basic/boltzmann_wealth_model/requirements.txt deleted file mode 100644 index 95044bedf78..00000000000 --- a/examples/basic/boltzmann_wealth_model/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -mesa[viz]>=3.0.0b0 diff --git a/examples/basic/boltzmann_wealth_model/st_app.py b/examples/basic/boltzmann_wealth_model/st_app.py index 665f8067a6a..4e722935ede 100644 --- a/examples/basic/boltzmann_wealth_model/st_app.py +++ b/examples/basic/boltzmann_wealth_model/st_app.py @@ -1,3 +1,5 @@ +# Run with streamlit run st_app.py + import time import altair as alt From f377e88f249e1e2505b52af0a385c69c66218330 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 17:19:26 +0200 Subject: [PATCH 5/9] Conway's Game of Life: Restructure - Rename cell.py to agents.py - Flatten structure - Remove unneeded viz --- examples/basic/conways_game_of_life/Readme.md | 14 ++++++-------- .../{conways_game_of_life/cell.py => agents.py} | 4 ++-- .../{conways_game_of_life => }/model.py | 9 +++++---- .../{conways_game_of_life => }/portrayal.py | 0 examples/basic/conways_game_of_life/run.py | 3 --- .../{conways_game_of_life => }/server.py | 4 ++-- .../conways_game_of_life/{app.py => st_app.py} | 4 ++-- 7 files changed, 17 insertions(+), 21 deletions(-) rename examples/basic/conways_game_of_life/{conways_game_of_life/cell.py => agents.py} (97%) rename examples/basic/conways_game_of_life/{conways_game_of_life => }/model.py (85%) rename examples/basic/conways_game_of_life/{conways_game_of_life => }/portrayal.py (100%) delete mode 100644 examples/basic/conways_game_of_life/run.py rename examples/basic/conways_game_of_life/{conways_game_of_life => }/server.py (79%) rename examples/basic/conways_game_of_life/{app.py => st_app.py} (95%) diff --git a/examples/basic/conways_game_of_life/Readme.md b/examples/basic/conways_game_of_life/Readme.md index 85b591aa713..1423646f85d 100644 --- a/examples/basic/conways_game_of_life/Readme.md +++ b/examples/basic/conways_game_of_life/Readme.md @@ -19,19 +19,17 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p ## Files -* ``conways_game_of_life/cell.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE. -* ``conways_game_of_life/model.py``: Defines the model itself, initialized with a random configuration of alive and dead cells. -* ``conways_game_of_life/portrayal.py``: Describes for the front end how to render a cell. -* ``conways_game_of_life/server.py``: Defines an interactive visualization. -* ``run.py``: Launches the visualization +* ``agents.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE. +* ``model.py``: Defines the model itself, initialized with a random configuration of alive and dead cells. +* ``portrayal.py``: Describes for the front end how to render a cell. +* ``st_app.py``: Defines an interactive visualization using Streamlit. ## Optional -* ``conways_game_of_life/app.py``: can be used to run the simulation via the streamlit interface. +* ``conways_game_of_life/st_app.py``: can be used to run the simulation via the streamlit interface. * For this some additional packages like ``streamlit`` and ``altair`` needs to be installed. -* Once installed, the app can be opened in the browser using : ``streamlit run app.py`` +* Once installed, the app can be opened in the browser using : ``streamlit run st_app.py`` ## Further Reading [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) - diff --git a/examples/basic/conways_game_of_life/conways_game_of_life/cell.py b/examples/basic/conways_game_of_life/agents.py similarity index 97% rename from examples/basic/conways_game_of_life/conways_game_of_life/cell.py rename to examples/basic/conways_game_of_life/agents.py index 35c8d3f2791..92d12bc2380 100644 --- a/examples/basic/conways_game_of_life/conways_game_of_life/cell.py +++ b/examples/basic/conways_game_of_life/agents.py @@ -1,7 +1,7 @@ -import mesa +from mesa import Agent -class Cell(mesa.Agent): +class Cell(Agent): """Represents a single ALIVE or DEAD cell in the simulation.""" DEAD = 0 diff --git a/examples/basic/conways_game_of_life/conways_game_of_life/model.py b/examples/basic/conways_game_of_life/model.py similarity index 85% rename from examples/basic/conways_game_of_life/conways_game_of_life/model.py rename to examples/basic/conways_game_of_life/model.py index 76d9ca9fef4..abf068bace3 100644 --- a/examples/basic/conways_game_of_life/conways_game_of_life/model.py +++ b/examples/basic/conways_game_of_life/model.py @@ -1,9 +1,10 @@ -import mesa +from mesa import Model +from mesa.space import SingleGrid -from .cell import Cell +from agents import Cell -class ConwaysGameOfLife(mesa.Model): +class ConwaysGameOfLife(Model): """ Represents the 2-dimensional array of cells in Conway's Game of Life. @@ -15,7 +16,7 @@ def __init__(self, width=50, height=50): """ super().__init__() # Use a simple grid, where edges wrap around. - self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = SingleGrid(width, height, torus=True) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. diff --git a/examples/basic/conways_game_of_life/conways_game_of_life/portrayal.py b/examples/basic/conways_game_of_life/portrayal.py similarity index 100% rename from examples/basic/conways_game_of_life/conways_game_of_life/portrayal.py rename to examples/basic/conways_game_of_life/portrayal.py diff --git a/examples/basic/conways_game_of_life/run.py b/examples/basic/conways_game_of_life/run.py deleted file mode 100644 index 7095816577c..00000000000 --- a/examples/basic/conways_game_of_life/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from conways_game_of_life.server import server - -server.launch(open_browser=True) diff --git a/examples/basic/conways_game_of_life/conways_game_of_life/server.py b/examples/basic/conways_game_of_life/server.py similarity index 79% rename from examples/basic/conways_game_of_life/conways_game_of_life/server.py rename to examples/basic/conways_game_of_life/server.py index 6da932f3ee6..8277437d6bb 100644 --- a/examples/basic/conways_game_of_life/conways_game_of_life/server.py +++ b/examples/basic/conways_game_of_life/server.py @@ -1,7 +1,7 @@ import mesa -from .model import ConwaysGameOfLife -from .portrayal import portrayCell +from model import ConwaysGameOfLife +from portrayal import portrayCell # Make a world that is 50x50, on a 250x250 display. canvas_element = mesa.visualization.CanvasGrid(portrayCell, 50, 50, 250, 250) diff --git a/examples/basic/conways_game_of_life/app.py b/examples/basic/conways_game_of_life/st_app.py similarity index 95% rename from examples/basic/conways_game_of_life/app.py rename to examples/basic/conways_game_of_life/st_app.py index 5be8327a35b..bff865a8d12 100644 --- a/examples/basic/conways_game_of_life/app.py +++ b/examples/basic/conways_game_of_life/st_app.py @@ -4,9 +4,9 @@ import numpy as np import pandas as pd import streamlit as st -from conways_game_of_life.model import ConwaysGameOfLife +from model import ConwaysGameOfLife -model = st.title("Boltzman Wealth Model") +model = st.title("Conway's Game of Life") num_ticks = st.slider("Select number of Steps", min_value=1, max_value=100, value=50) height = st.slider("Select Grid Height", min_value=10, max_value=100, step=10, value=15) width = st.slider("Select Grid Width", min_value=10, max_value=100, step=10, value=20) From 6d62194eca47b73108b73e64f326baf014403d16 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 17:33:45 +0200 Subject: [PATCH 6/9] examples/schelling: Restructure - Split of SchellingAgent in agents.py - Remove run_ascii.py (not supported anymore) - Clean up files --- examples/basic/schelling/README.md | 1 - examples/basic/schelling/__init__.py | 0 examples/basic/schelling/agents.py | 29 ++++++++++++++ examples/basic/schelling/model.py | 31 ++------------- examples/basic/schelling/requirements.txt | 3 -- examples/basic/schelling/run_ascii.py | 48 ----------------------- 6 files changed, 32 insertions(+), 80 deletions(-) delete mode 100644 examples/basic/schelling/__init__.py create mode 100644 examples/basic/schelling/agents.py delete mode 100644 examples/basic/schelling/requirements.txt delete mode 100644 examples/basic/schelling/run_ascii.py diff --git a/examples/basic/schelling/README.md b/examples/basic/schelling/README.md index b0116b55e76..af3f01b3563 100644 --- a/examples/basic/schelling/README.md +++ b/examples/basic/schelling/README.md @@ -33,7 +33,6 @@ To run the model with the grid displayed as an ASCII text, run `python run_ascii ## Files * ``app.py``: Code for the interactive visualization. -* ``run_ascii.py``: Run the model in text mode. * ``schelling.py``: Contains the agent class, and the overall model class. * ``analysis.ipynb``: Notebook demonstrating how to run experiments and parameter sweeps on the model. diff --git a/examples/basic/schelling/__init__.py b/examples/basic/schelling/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/basic/schelling/agents.py b/examples/basic/schelling/agents.py new file mode 100644 index 00000000000..80bd5cadcd0 --- /dev/null +++ b/examples/basic/schelling/agents.py @@ -0,0 +1,29 @@ +from mesa import Agent, Model + + +class SchellingAgent(Agent): + """ + Schelling segregation agent + """ + + def __init__(self, model: Model, agent_type: int) -> None: + """ + Create a new Schelling agent. + + Args: + agent_type: Indicator for the agent's type (minority=1, majority=0) + """ + super().__init__(model) + self.type = agent_type + + def step(self) -> None: + neighbors = self.model.grid.iter_neighbors( + self.pos, moore=True, radius=self.model.radius + ) + similar = sum(1 for neighbor in neighbors if neighbor.type == self.type) + + # If unhappy, move: + if similar < self.model.homophily: + self.model.grid.move_to_empty(self) + else: + self.model.happy += 1 diff --git a/examples/basic/schelling/model.py b/examples/basic/schelling/model.py index b7523ef2b6d..16b8fe5da8c 100644 --- a/examples/basic/schelling/model.py +++ b/examples/basic/schelling/model.py @@ -1,35 +1,10 @@ import mesa +from mesa import Model - -class SchellingAgent(mesa.Agent): - """ - Schelling segregation agent - """ - - def __init__(self, model: mesa.Model, agent_type: int) -> None: - """ - Create a new Schelling agent. - - Args: - agent_type: Indicator for the agent's type (minority=1, majority=0) - """ - super().__init__(model) - self.type = agent_type - - def step(self) -> None: - neighbors = self.model.grid.iter_neighbors( - self.pos, moore=True, radius=self.model.radius - ) - similar = sum(1 for neighbor in neighbors if neighbor.type == self.type) - - # If unhappy, move: - if similar < self.model.homophily: - self.model.grid.move_to_empty(self) - else: - self.model.happy += 1 +from agents import SchellingAgent -class Schelling(mesa.Model): +class Schelling(Model): """ Model class for the Schelling segregation model. """ diff --git a/examples/basic/schelling/requirements.txt b/examples/basic/schelling/requirements.txt deleted file mode 100644 index 79bc35553ee..00000000000 --- a/examples/basic/schelling/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -jupyter -matplotlib -mesa[viz]>=3.0.0b0 diff --git a/examples/basic/schelling/run_ascii.py b/examples/basic/schelling/run_ascii.py deleted file mode 100644 index 460fabbb746..00000000000 --- a/examples/basic/schelling/run_ascii.py +++ /dev/null @@ -1,48 +0,0 @@ -import mesa -from model import Schelling - - -class SchellingTextVisualization(mesa.visualization.TextVisualization): - """ - ASCII visualization for schelling model - """ - - def __init__(self, model): - """ - Create new Schelling ASCII visualization. - """ - self.model = model - - grid_viz = mesa.visualization.TextGrid(self.model.grid, self.print_ascii_agent) - happy_viz = mesa.visualization.TextData(self.model, "happy") - self.elements = [grid_viz, happy_viz] - - @staticmethod - def print_ascii_agent(a): - """ - Minority agents are X, Majority are O. - """ - if a.type == 0: - return "O" - if a.type == 1: - return "X" - - -if __name__ == "__main__": - model_params = { - "height": 20, - "width": 20, - # Agent density, from 0.8 to 1.0 - "density": 0.8, - # Fraction minority, from 0.2 to 1.0 - "minority_pc": 0.2, - # Homophily, from 3 to 8 - "homophily": 3, - } - - model = Schelling(**model_params) - viz = SchellingTextVisualization(model) - for i in range(10): - print("Step:", i) - viz.step() - print("---") From 2ccc8537f817cb3b6e289686ba59d616dd73b8e9 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 17:34:41 +0200 Subject: [PATCH 7/9] examples/schelling: Fix analysis.ipynb by updating to new batch_run Still used the old BatchRunner, this commit updates it to use batch_run and cleans it up --- examples/basic/schelling/analysis.ipynb | 340 +++--------------------- 1 file changed, 44 insertions(+), 296 deletions(-) diff --git a/examples/basic/schelling/analysis.ipynb b/examples/basic/schelling/analysis.ipynb index 71d925c1802..2022156bbab 100644 --- a/examples/basic/schelling/analysis.ipynb +++ b/examples/basic/schelling/analysis.ipynb @@ -17,16 +17,17 @@ }, { "cell_type": "code", - "execution_count": 1, "metadata": {}, - "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", "\n", "%matplotlib inline\n", "\n", "from model import Schelling" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -37,12 +38,10 @@ }, { "cell_type": "code", - "execution_count": 2, "metadata": {}, + "source": "model = Schelling(height=10, width=10, homophily=3, density=0.8, minority_pc=0.2)", "outputs": [], - "source": [ - "model = Schelling(10, 10, 0.8, 0.2, 3)" - ] + "execution_count": null }, { "cell_type": "markdown", @@ -53,22 +52,14 @@ }, { "cell_type": "code", - "execution_count": 3, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "100\n" - ] - } - ], "source": [ "while model.running and model.steps < 100:\n", " model.step()\n", "print(model.steps) # Show how many steps have actually run" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -79,83 +70,21 @@ }, { "cell_type": "code", - "execution_count": 4, "metadata": {}, - "outputs": [], "source": [ "model_out = model.datacollector.get_model_vars_dataframe()" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": 5, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
happy
00
173
267
372
472
\n", - "
" - ], - "text/plain": [ - " happy\n", - "0 0\n", - "1 73\n", - "2 72\n", - "3 73\n", - "4 72" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "model_out.head()" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -166,178 +95,30 @@ }, { "cell_type": "code", - "execution_count": 6, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], "source": [ "model_out.happy.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For testing purposes, here is a table giving each agent's x and y values at each step." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "x_positions = model.datacollector.get_agent_vars_dataframe()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xy
StepAgentID
0(0, 0)01
(0, 1)89
(0, 2)52
(0, 3)00
(0, 4)17
\n", - "
" - ], - "text/plain": [ - " x y\n", - "Step AgentID \n", - "0 (0, 0) 0 1\n", - " (0, 1) 8 9\n", - " (0, 2) 5 2\n", - " (0, 3) 0 0\n", - " (0, 4) 1 7" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } ], - "source": [ - "x_positions.head()" - ] + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Effect of Homophily on segregation\n", + "# Effect of Homophily on happiness\n", "\n", - "Now, we can do a parameter sweep to see how segregation changes with homophily.\n", + "Now, we can do a parameter sweep to see how happiness changes with homophily.\n", "\n", "First, we create a function which takes a model instance and returns what fraction of agents are segregated -- that is, have no neighbors of the opposite type." ] }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "from mesa.batchrunner import BatchRunner" - ] - }, - { - "cell_type": "code", - "execution_count": 10, "metadata": {}, + "source": "from mesa.batchrunner import batch_run", "outputs": [], - "source": [ - "def get_segregation(model):\n", - " \"\"\"\n", - " Find the % of agents that only have neighbors of their same type.\n", - " \"\"\"\n", - " segregated_agents = 0\n", - " for agent in model.agents:\n", - " segregated = True\n", - " for neighbor in model.grid.iter_neighbors(agent.pos, True):\n", - " if neighbor.type != agent.type:\n", - " segregated = False\n", - " break\n", - " if segregated:\n", - " segregated_agents += 1\n", - " return segregated_agents / len(model.agents)" - ] + "execution_count": null }, { "cell_type": "markdown", @@ -348,85 +129,52 @@ }, { "cell_type": "code", - "execution_count": 11, "metadata": {}, - "outputs": [], "source": [ "fixed_params = {\"height\": 10, \"width\": 10, \"density\": 0.8, \"minority_pc\": 0.2}\n", - "variable_parms = {\"homophily\": range(1, 9)}" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, + "variable_parms = {\"homophily\": range(1, 9)}\n", + "all_params = fixed_params | variable_parms" + ], "outputs": [], - "source": [ - "model_reporters = {\"Segregated_Agents\": get_segregation}" - ] + "execution_count": null }, { "cell_type": "code", - "execution_count": 13, "metadata": {}, - "outputs": [], "source": [ - "param_sweep = BatchRunner(\n", + "results = batch_run(\n", " Schelling,\n", - " variable_parameters=variable_parms,\n", - " fixed_parameters=fixed_params,\n", + " parameters=all_params,\n", " iterations=10,\n", " max_steps=200,\n", - " model_reporters=model_reporters,\n", ")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "80it [00:15, 3.13it/s]\n" - ] - } ], - "source": [ - "param_sweep.run_all()" - ] + "outputs": [], + "execution_count": null }, { - "cell_type": "code", - "execution_count": 15, "metadata": {}, - "outputs": [], + "cell_type": "code", "source": [ - "df = param_sweep.get_model_vars_dataframe()" - ] + "df = pd.DataFrame(results)\n", + "df" + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": 16, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD8CAYAAACMwORRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAHQhJREFUeJzt3X+QVfd53/H3wwpGK1YBxVLX1gUbRUPIIG8lwhYp1Yy7KzsFOQnasUUGGmnqjFUmM0K2Y5UWxhpZUtWKmio/ptVkQpXWzTjWVsHqFts7wR3DTmM1UhBCeI0QKcKqYImRYgPW2uuwLE//2Hvh7nJ/LXvPnvOc/bxmGO0599y9j+6e+9zveb4/jrk7IiKSL3PSDkBERJpPyV1EJIeU3EVEckjJXUQkh5TcRURySMldRCSHlNxFRHJIyV1EJIeU3EVEcuiqtF74+uuv9yVLllzx83/yk58wf/785gWUIMWanEjxRooVYsU7m2Ldv3//37n7DXUPdPdU/q1cudKnY+/evdN6/kxSrMmJFG+kWN1jxTubYgVe8QZyrMoyIiI5pOQuIpJDSu4iIjmk5C4ikkNK7iIiOaTkLiKSQ0ruIiI5pOQuIpJDSu4iIjmk5C4h9R0Y4s5texgcOsud2/bQd2Ao7ZBEMiW1tWVErlTfgSG2vjDIyOgYLIahMyNsfWEQgJ4VhZSjE8kGtdwlnO27j4wn9jIjo2Ns330kpYhEskfJXcI5eWZkSvvTphKSpEHJXcK5cWHrlPanqVRCGip+8ZRKSErwkjQldwln8+pltM5tmbCvdW4Lm1cvSymi6lRCkrQ0lNzNbI2ZHTGzo2a2pcLjv29mrxX//Y2ZnWl+qCLjelYUeOoTHRSKLfXCwlae+kRHJjtTo5WQJD/qJnczawGeAe4GlgMbzGx5+THu/rvufpu73wb8R+CFJIKV5ESrC/esKPDilrvoKCzgxS13ZTKxQ6wSkuRLIy33VcBRdz/m7ueAXuCeGsdvAJ5rRnDRRUmYqgsnJ1IJSfKlkeReAI6XbZ8o7ruMmX0IuAnYM/3QYouUMFUXTk6kEpLki43fkq/GAWbrgNXu/kBx+35glbs/VOHYfw0sqvRY8fGNwEaA9vb2lb29vVcc+PDwMG1tbVf8/KQd+cF7nBu7AEB7K5wqlljntcxh2fuvTTGyyw0Onb34c3msAB2FBSlE1LisnwflIsUKseKdTbF2d3fvd/fOesc1MkP1BLC4bHsRcLLKseuBB6v9InffAewA6Ozs9K6urgZevrKBgQGm8/yk/faWb1K6Re3DHed5enD8rTbg+9u60gusgi9s23PxCqM81sLCVh76ra4UI6sv6+dBuUixQqx4FevlGinL7AOWmtlNZjaP8QS+a/JBZrYMuA74q+aGGFOkjrSIdeEo/Rkiaamb3N39PLAJ2A0cBp5390Nm9oSZrS07dAPQ6/XqPLNEpIQZrS4cqT9DJC0NLRzm7v1A/6R9j07afqx5YcVXSozjnZLvUVjYyubVyzKbMHtWFOhZUWBgYCDzpZhaHcBZfH/7DgyxffcR1i9+jy9s25Pp80DyQ6tCJihSwowk0sQgrWApadHyAxJOpP4MDTOVtCi5SziR+jMiXWVIvii5J0gjOpIRqQM40lWG5IuSe0I0okMg1lWG5IuSe0JUa01OpC/OSFcZki9K7glRrTU5+uIUqU/JPSGqtSYn0hdnpKsMyRcl94So1pqcSF+cusqQtCi5J0S11uRE+uKMdJUh+aIZqgnSDNVkRFra4caFrRdLMpP3iyRJLXcJKcpt9iJdZUi+qOUukqBIVxmSL0ruIglTeU7SoLKMhKSlHURqU8tdwtEyuiL1qeUu4WjsuEh9Su4SjsaOi9Sn5C7hLGidO6X9IrORkruEYza1/SKzUUPJ3czWmNkRMztqZluqHPObZva6mR0ys682N0yRS878dHRK+9OmkT2ShrqjZcysBXgG+FXgBLDPzHa5++tlxywFtgJ3uvtpM/sHSQUsEmlKv0b2SFoaabmvAo66+zF3Pwf0AvdMOuZfAM+4+2kAd3+nuWGKXBJpSr9G9khazN1rH2B2L7DG3R8obt8P3O7um8qO6QP+BrgTaAEec/e/qPC7NgIbAdrb21f29vZeceDDw8O0tbVd8fNnkmJtvjMjo5w6+zOum3eB0+fm0L7gahZmsEN1cOjsxZ/bW+FU2QVHR2FBChE1Lsq5ALMr1u7u7v3u3lnvuEYmMVXqppr8jXAVsBToAhYBf2lmH3b3MxOe5L4D2AHQ2dnpXV1dDbx8ZQMDA0zn+TNJsTZf34Eh/vTgEdYvfo/e4/PZvHoZXRksc3x6az9jxQbUwx3neXpw/CPXYsabGV+KIMq5AIq1kkbKMieAxWXbi4CTFY75n+4+6u7fB44wnuxFmi7S3Y3GqlwZV9sv0iyNJPd9wFIzu8nM5gHrgV2TjukDugHM7HrgF4FjzQxUpCRSHbtQpZO32n6RZqmb3N39PLAJ2A0cBp5390Nm9oSZrS0ethv4oZm9DuwFNrv7D5MKWma3SDNUI3X+Sr40tHCYu/cD/ZP2PVr2swOfL/4TSVSkoZBaz13SohmqAsSaaBOtNRzlrlGSL0ruEqqDEuLdfDzSF6fkh5K7hOqgLInSGu47MMTmnQcnfHFu3nlQCV4Sp+QuoTooo3n864cYHZs47HF0zHn864dSikhmCyV3qdoRmcUOymhOV1nMrNp+kWZRcpdwHZQiUp+Su4TroIQ4nZTV1rvJ4jo4ki9K7gLE6aCEWKN7Hlt7C3PnTFyeae4c47G1t6QUkcwWSu4STqTRPT0rCmxfd+uEq6Lt627N9Jen5ENDM1RFsiTa6J6eFQV6VhQYGBjgoYyvBCn5oZa7hKPRPSL1KblLOBrdI1KfkrsAcUafQMzRPSIzTTV3CXkTZ9WxRWpTy11CjT4RkcYouUu40SciUp+Su2j0ScIi9WdAvHilMiV30eiTBEWaTQvx4pXqlNyFnhUFPrmyQIuNT5NvMeOTKwuZ7UyNJFp/RrR4pbqGkruZrTGzI2Z21My2VHj8U2b2rpm9Vvz3QPNDlaT0HRjia/uHGPPxdcfH3Pna/iG11pogWn9GpXvT1tov2VU3uZtZC/AMcDewHNhgZssrHPrf3f224r9nmxynJEitteRE688oXb01ul+yq5GW+yrgqLsfc/dzQC9wT7JhyUyK1rqMpPuXbpjS/rSVrt4a3S/Z1UhyLwDHy7ZPFPdN9kkz+66Z7TSzxU2JTmZEtNZlJHvfeHdK+9NWqPI3r7Zfssu8zjeyma0DVrv7A8Xt+4FV7v5Q2THvA4bd/e/N7HeA33T3uyr8ro3ARoD29vaVvb29Vxz48PAwbW1tV/z8mZT1WM+MjDJ0eoQL7rS3wqkRmGNG4brWzN9UIuvv7eDQ2Ys/l97bko7CghQiqi3quZD186DcdGPt7u7e7+6d9Y5rZPmBE0B5S3wRcLL8AHf/Ydnmfwb+faVf5O47gB0AnZ2d3tXV1cDLVzYwMMB0nj+TIsTad2CI7buPsH7xe/Qev5bNq5eFGC2T9ff2C9v2XOyMfLjjPE8Pjn/kCgtbM7tswiN9gzz38nE+9+FR/uB7c9lw+2IevLsj7bBqyvp5UG6mYm2kLLMPWGpmN5nZPGA9sKv8ADP7QNnmWuBw80KUmRDpTkyRRJtDoJFT+VE3ubv7eWATsJvxpP28ux8ysyfMbG3xsM+Y2SEzOwh8BvhUUgGLRBJtBUuNnMqPhsa5u3u/u/+iu9/s7v+2uO9Rd99V/Hmru9/i7re6e7e7v5Fk0NJ8mnKenEhXRdFGTum8rS7cDFX9MZtPU86lJNLIKZ23tYVK7vpjJkOX4lISqY9A521toZK7/pjJiHYpLsmJ1Eeg87a2UHdi0h8zGTcubK24dkgWL8UleVHucqXztrZQLfdI9UCI0z8Q6VJcpETnbW2hknukP2ak/oFIl+IiJTpvawtVlin90cZr7O9RWNia2ZmUtfoHshhvlEtxkXI6b6sLldwhzh9T/QMikqZQZZlIovUPRBOlP0MkLUruCYnUPxBNpP4MkbQouSdEnT3J0XwHkfqU3BMUaU2RSNSfkSyVvPJByV3CUX9GclTyyg8ldwlH/RnJUckrP8INhRSJNN8hGpW88kMtdxG5SCWv/FByl3BUF06OSl75oeQu4agunBwN4c0P1dwlHNWFkxVliQ+pLVzLXWNwRXVhkfoaSu5mtsbMjpjZUTPbUuO4e83MzayzeSFeolqrgOrCIo2om9zNrAV4BrgbWA5sMLPlFY67FvgM8HKzgyxRrVVAdWGRRjTScl8FHHX3Y+5+DugF7qlw3L8BvgT8rInxTaBaq5RoaQeR2hpJ7gXgeNn2ieK+i8xsBbDY3b/RxNguo1qriEhjzN1rH2C2Dljt7g8Ut+8HVrn7Q8XtOcAe4FPu/paZDQD/0t1fqfC7NgIbAdrb21f29vZOKdgzI6Oc+NEIjtPeCqdGwDAW/XwrC1vnTul3zaTh4WHa2trSDqMhkWKFWPFGihVixTubYu3u7t7v7nX7NRsZCnkCWFy2vQg4WbZ9LfBhYMDMAN4P7DKztZMTvLvvAHYAdHZ2eldXVwMvf0nfgSH+8DsHGR1zHu44z9ODVzG3xdh+73K6MnxZPjAwwFT/X9MSKVaIFW+kWCFWvIr1co2UZfYBS83sJjObB6wHdpUedPez7n69uy9x9yXAS8Blib0Ztu8+wujYxCuN0TFXh6qIyCR1k7u7nwc2AbuBw8Dz7n7IzJ4ws7VJB1hOHaoSkeZmSBoamqHq7v1A/6R9j1Y5tmv6YVV248LWi2PcJ+8XyaLS3IyR0TFYfGluBqARPpKoUDNUu3/phintF0mb5mZIWkIl971vvDul/WnT5biolChpCZXcI31QtFSCgOZmSHpCJfdIHxRdjgtoHRxJT6jkHumDEukqQ5KjdXAkLaHWc49078wFrXM5MzJacb/MLlofXdIQKrlDnA/K+GTdxveLiDRTqLIMxBmBcuanl7faa+0XEWmmUMk90giUSJ2/IuWiNKCktlDJPdIIlEidvyIlfQeG2Lzz4IQG1OadB5XgAwqV3CONQNEoCYno8a8fqrg43+NfP5RSRLVFusqY6VhDdahGW1smSuevSMnpKn1C1fanKdK6PWnEGqrlrlKHiJREKtOmEWuo5B6t1BHpkvGRvkFu3trP4NBZbt7azyN9g2mHVFOk9zaSSEN4I5Vp04g1VFkG4pQ6Il0yPtI3yFdeevvi9pj7xe0nezrSCquqSO9tNNXuulnnbpypiFSmTSPWUC33SCJdMj738vEp7U9bpPc2mkKVZFNtf5oilWnTWK5cyT0hkS4Zx6o0y6rtT1uk9zaaSAkzUpk2jeXKw5Vlooh0ydhiVjGRt2Sx0Eqs9zaaSOs3QZwybRoNErXcExKpBbTh9sVT2p+2SO9tRD0rCry45S46Cgt4cctdmU3skaQxY13JPSGRLhmf7Ongvjs+eLGl3mLGfXd8MJOdqRDrvRWBdBokDZVlzGwN8IdAC/Csu2+b9PjvAA8CY8AwsNHdX29yrOFEuWSE8QT/ZE8HAwMDvJnxWCHWeyuSRrmrbsvdzFqAZ4C7geXABjNbPumwr7p7h7vfBnwJ+L2mRxqQxmKLSMlMl7saKcusAo66+zF3Pwf0AveUH+DuPy7bnA8kNswiSsKMtIKliORPI8m9AJQPeD5R3DeBmT1oZm8y3nL/THPCmyhSwtRYbBFJk3mdscxmtg5Y7e4PFLfvB1a5+0NVjv9nxeP/eYXHNgIbAdrb21f29vZOKdgjP3iPc2MXAGhvhVPFUUTzWuaw7P3XTul3JW1w6OzFn8tjBegoLEghosYMDw/T1taWdhgNixRvlFjPjIxy6uzPuG7eBU6fm0P7gqtZmPHbQ0Z5b2H6sXZ3d+939856xzXSoXoCKB8Ttwg4WeP4XuCPKj3g7juAHQCdnZ3e1dXVwMtf8ttbvokXLzYe7jjP04Pj4Rvw/W1T+11J+/TW/otjx8tjbTHLdIflwMAAU/27pClSvBFi7TswxNZvDzIyOoeHOy7w9OAcWueO8dQnlmd6NFKE97ZkpmJtpCyzD1hqZjeZ2TxgPbCr/AAzW1q2+WvA/21eiJdEurtRtFmfIhCvnBilDy4NdZO7u58HNgG7gcPA8+5+yMyeMLO1xcM2mdkhM3sN+DxwWUmmGSJNXom0RodISaSlHSL1waWhoXHu7t4P9E/a92jZz59tclwVRZoavXn1sksrFxZl9YtIpCTS0g61rjKymBNmWrgZqlGmRmsWpUQU6eo40lVGGrRwWII0i1KiiXR1HOkqIw1K7iIyQZRGyZL3VU7uS96n5A4ByzIiIgD/59iPprR/tlFyF0BDyiSeSLcETIPKMqJ7korkkFruEm7iiojUp+QuGlImIVVb7ybr6+DMlHDJXbXh5ou0rINIyWNrb2HunIn3+Z07x3hs7S0pRZQtoZK7phsnY/PqZRU/JFmcuCLJi9KA6llRYPu6WydMFNy+7lb1ExWFSu6qDSfI6mzLrBCtARVlxnoaQiV31YaTsX33EUbHJo4fGx1zfWnOQmpAJeeRvkFu3trP4NBZbt7azyN9g4m+XqjkrtpwMvSlKSU6F5LxSN8gX3np7YtLfo+585WX3k40wYdK7pEWNYKZ/6a+UvrSlBKdC8l47uXjU9rfDKGSe6SVFtP4pr5S0b40JTk6F5KRxs17ws1QjbKoUa1v6id7OmY4mtoirQQoydK5kIwWs4qJvMWSG7kQquUOcYZpRbvNnkYdSInOhea74xeum9L+ZgjVco+0Bkoa39Qikk1v/bByh3S1/c0QquUeaZjWhtsXT2m/iORXGqOQQiX3SMO0nuzp4L47Pnixpd5ixn13fDBz9XYRSV4ao5AaSu5mtsbMjpjZUTPbUuHxz5vZ62b2XTP7tpl9qPmhxhum9WRPB28+9XE6Cgt486mPK7GLzFJpjEKqm9zNrAV4BrgbWA5sMLPlkw47AHS6+z8EdgJfanagoGFaIhJTGsO4G2m5rwKOuvsxdz8H9AL3lB/g7nvd/afFzZeARc0Nc1ykce7RRBmFJCKNMa8zNM/M7gXWuPsDxe37gdvdfVOV4/8T8AN3f7LCYxuBjQDt7e0re3t7rzjw4eFh2trarvj5MynrsZ4ZGWXo9AgX3GlvhVMjMMeMwnWtmV8bO+vvbblIsUKMeM+MjHLq7M+4bt4FTp+bQ/uCqzN5zp4ZGeXEj0ZwLn3GDGPRz0/9M9bd3b3f3TvrHdfIUMhKY/cqfiOY2X1AJ/BPKj3u7juAHQCdnZ3e1dXVwMtXNjAwwHSeP5OyHuud2/YwdGa83PVwx3meHhw/LQoLW3hxS1eKkdWX9fe2XKRYIfvx9h0YYuu3BxkZncPDHRd4enAOrXPHeOoTyzN3NX/b49/izMjln7GFrc5rX+xK5DUbKcucAMrH7y0CTk4+yMw+BnwBWOvuf9+c8GQmRBqFJFISaWj0mZHRKe1vhkaS+z5gqZndZGbzgPXArvIDzGwF8MeMJ/Z3mh+mJCnaKCQR4OKa843un23qJnd3Pw9sAnYDh4Hn3f2QmT1hZmuLh20H2oA/N7PXzGxXlV83ber4az6NQpKIqs32zuIs8OuuqVxXr7a/GRpafsDd+4H+SfseLfv5Y02Oq6JIyw9EosWiJKJI6zd98TduYfPOgxNuijO3xfjibyR3v9dQM1Qj1dii0WJREk2hStmw2v409awosP3eSfd7vTfZ+72GSu7q+BORkmjlxJluQIVaFfLGha0VO0vU8Scy+6icWFuolvvm1cuY2zKxs2Rui2X2m1pEkhWpnDjTg0FCtdyBy6dPZa/vRERkgjQGg4RquW/ffYTRCxOz+egFV4eqiGRaGoNBQiV3daiKJE9zSZpPN+uoQzMpRZJVKh+UBi6UygdK8NOT2Zt1ZEW0oU8i0WguSTLSyF2hOlQ19EkkWSp9JiON3BUqucP4m9SzosDAwAAP/VZX2uGI5IrmkiRnpnNXqLKMiCRLpc/kzHRHtZK7iFwU7VaWUUb2pNFRreQuIhNEmfUZaWSPxrmLiDQo0sgejXMXEWlQpJE9GucuItKgSJMa0+ioVnIXkZAijexJo6M63Dh3ERGIN6kxk+PczWyNmR0xs6NmtqXC4x8xs1fN7LyZ3dv8MEVELhdlZE8a6iZ3M2sBngHuBpYDG8xs+aTD3gY+BXy12QGKiMjUNVKWWQUcdfdjAGbWC9wDvF46wN3fKj52IYEYRURkihopyxSA42XbJ4r7REQko8y99n3qzGwdsNrdHyhu3w+scveHKhz7ZeAb7r6zyu/aCGwEaG9vX9nb23vFgQ8PD9PW1nbFz59JijU5keKNFCvEinc2xdrd3b3f3TvrHujuNf8BvwLsLtveCmytcuyXgXvr/U53Z+XKlT4de/fundbzZ5JiTU6keCPF6h4r3tkUK/CKN5BjGynL7AOWmtlNZjYPWA/supJvHBERmRl1k7u7nwc2AbuBw8Dz7n7IzJ4ws7UAZvaPzOwEsA74YzM7lGTQIiJSW0OTmNy9H+iftO/Rsp/3AYuaG5qIiFwpLT8gIpJDSu4iIjmk5C4ikkNK7iIiM2CmbwmoVSFFRBJWuiXgyOgYLL50S0AgscXO1HIXEUmY7qEqIpJDuoeqiEgO6R6qIiI5lMYtAdWhKiKSsDRuCajkLiIyAzJ5D1UREYlFyV1EJIeU3EVEckjJXUQkh5TcRURySMldRCSHlNxFRHJIyV1EJIeU3EVEcsjcPZ0XNnsX+H/T+BXXA3/XpHCSpliTEyneSLFCrHhnU6wfcvcb6h2UWnKfLjN7xd07046jEYo1OZHijRQrxIpXsV5OZRkRkRxSchcRyaHIyX1H2gFMgWJNTqR4I8UKseJVrJOErbmLiEh1kVvuIiJSRbjkbmb/xczeMbPvpR1LPWa22Mz2mtlhMztkZp9NO6ZqzOxqM/trMztYjPXxtGOqx8xazOyAmX0j7VjqMbO3zGzQzF4zs1fSjqcWM1toZjvN7I3iufsracdUjZktK76npX8/NrPPpR1XNWb2u8XP1/fM7Dkzuzqx14pWljGzjwDDwJ+6+4fTjqcWM/sA8AF3f9XMrgX2Az3u/nrKoV3GzAyY7+7DZjYX+A7wWXd/KeXQqjKzzwOdwM+5+6+nHU8tZvYW0OnumR+LbWb/DfhLd3/WzOYB17j7mbTjqsfMWoAh4HZ3n84cmkSYWYHxz9Vydx8xs+eBfnf/chKvF67l7u7/G/hR2nE0wt3/1t1fLf78HnAYSO6midPg44aLm3OL/zL7zW9mi4BfA55NO5Y8MbOfAz4C/AmAu5+LkNiLPgq8mcXEXuYqoNXMrgKuAU4m9ULhkntUZrYEWAG8nG4k1RXLHK8B7wD/y90zGyvwB8C/Ai6kHUiDHPiWme03s41pB1PDLwDvAv+1WPJ61szmpx1Ug9YDz6UdRDXuPgT8B+Bt4G+Bs+7+raReT8l9BphZG/A14HPu/uO046nG3cfc/TZgEbDKzDJZ9jKzXwfecff9accyBXe6+y8DdwMPFsuLWXQV8MvAH7n7CuAnwJZ0Q6qvWD5aC/x52rFUY2bXAfcANwE3AvPN7L6kXk/JPWHF+vXXgD9z9xfSjqcRxcvwAWBNyqFUcyewtljH7gXuMrOvpBtSbe5+svjfd4D/AaxKN6KqTgAnyq7adjKe7LPubuBVdz+VdiA1fAz4vru/6+6jwAvAP07qxZTcE1TspPwT4LC7/17a8dRiZjeY2cLiz62Mn4hvpBtVZe6+1d0XufsSxi/F97h7Yi2g6TKz+cUOdYoljn8KZHK0l7v/ADhuZsuKuz4KZG4AQAUbyHBJpuht4A4zu6aYGz7KeD9cIsIldzN7DvgrYJmZnTCzT6cdUw13Avcz3rIsDdX6eNpBVfEBYK+ZfRfYx3jNPfNDDINoB75jZgeBvwa+6e5/kXJMtTwE/FnxXLgN+Hcpx1OTmV0D/CrjLeHMKl4N7QReBQYZz7+JzVYNNxRSRETqC9dyFxGR+pTcRURySMldRCSHlNxFRHJIyV1EJIeU3EVEckjJXUQkh5TcRURy6P8DovBSQVnzoQgAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], "source": [ - "plt.scatter(df.homophily, df.Segregated_Agents)\n", - "plt.grid(True)" - ] + "plt.scatter(df.homophily, df.happy)\n", + "plt.xlabel(\"Homophily\")\n", + "plt.ylabel(\"Happy Agents\")\n", + "plt.grid()\n", + "plt.title(\"Effect of Homophily on segregation\")\n", + "plt.show()" + ], + "outputs": [], + "execution_count": null } ], "metadata": { From ca51e7cc7dc7c282745df4e0b3ea309a61ffbfb8 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 17:41:06 +0200 Subject: [PATCH 8/9] examples/virus: Fix sliders in Viz Fix the sliders in the SolaraViz app.py to use the new API. --- examples/basic/virus_on_network/app.py | 112 ++++++++++++------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/examples/basic/virus_on_network/app.py b/examples/basic/virus_on_network/app.py index caa1360f880..4ed96b79b0b 100644 --- a/examples/basic/virus_on_network/app.py +++ b/examples/basic/virus_on_network/app.py @@ -3,7 +3,7 @@ import solara from matplotlib.figure import Figure from matplotlib.ticker import MaxNLocator -from mesa.visualization import SolaraViz, make_space_matplotlib +from mesa.visualization import SolaraViz, Slider, make_space_matplotlib from virus_on_network.model import State, VirusOnNetwork, number_infected @@ -57,66 +57,61 @@ def make_plot(model): fig.legend() # Set integer x axis ax.xaxis.set_major_locator(MaxNLocator(integer=True)) + ax.set_xlabel("Step") + ax.set_ylabel("Number of Agents") return solara.FigureMatplotlib(fig) model_params = { - "num_nodes": { - "type": "SliderInt", - "value": 10, - "label": "Number of agents", - "min": 10, - "max": 100, - "step": 1, - }, - "avg_node_degree": { - "type": "SliderInt", - "value": 3, - "label": "Avg Node Degree", - "min": 3, - "max": 8, - "step": 1, - }, - "initial_outbreak_size": { - "type": "SliderInt", - "value": 1, - "label": "Initial Outbreak Size", - "min": 1, - "max": 10, - "step": 1, - }, - "virus_spread_chance": { - "type": "SliderFloat", - "value": 0.4, - "label": "Virus Spread Chance", - "min": 0.0, - "max": 1.0, - "step": 0.1, - }, - "virus_check_frequency": { - "type": "SliderFloat", - "value": 0.4, - "label": "Virus Check Frequency", - "min": 0.0, - "max": 1.0, - "step": 0.1, - }, - "recovery_chance": { - "type": "SliderFloat", - "value": 0.3, - "label": "Recovery Chance", - "min": 0.0, - "max": 1.0, - "step": 0.1, - }, - "gain_resistance_chance": { - "type": "SliderFloat", - "value": 0.5, - "label": "Gain Resistance Chance", - "min": 0.0, - "max": 1.0, - "step": 0.1, - }, + "num_nodes": Slider( + label="Number of agents", + value=10, + min=10, + max=100, + step=1, + ), + "avg_node_degree": Slider( + label="Avg Node Degree", + value=3, + min=3, + max=8, + step=1, + ), + "initial_outbreak_size": Slider( + label="Initial Outbreak Size", + value=1, + min=1, + max=10, + step=1, + ), + "virus_spread_chance": Slider( + label="Virus Spread Chance", + value=0.4, + min=0.0, + max=1.0, + step=0.1, + ), + "virus_check_frequency": Slider( + label="Virus Check Frequency", + value=0.4, + min=0.0, + max=1.0, + step=0.1, + ), + "recovery_chance": Slider( + label="Recovery Chance", + value=0.3, + min=0.0, + max=1.0, + step=0.1, + ), + "gain_resistance_chance": Slider( + label="Gain Resistance Chance", + value=0.5, + min=0.0, + max=1.0, + step=0.1, + ), } SpacePlot = make_space_matplotlib(agent_portrayal) @@ -128,8 +123,9 @@ def make_plot(model): [ SpacePlot, make_plot, - get_resistant_susceptible_ratio, + # get_resistant_susceptible_ratio, # TODO: Fix and uncomment ], + model_params=model_params, name="Virus Model", ) page # noqa From d7a3834c99a3be809abe2edc8b83610f3d4438ba Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 17:50:56 +0200 Subject: [PATCH 9/9] examples/virus: Restructure - VirusAgent in agents.py - Flatten structure - Remove old visualisation code --- examples/basic/virus_on_network/README.md | 4 +- examples/basic/virus_on_network/agents.py | 70 +++++++++ examples/basic/virus_on_network/app.py | 2 +- .../{virus_on_network => }/model.py | 72 +-------- .../basic/virus_on_network/requirements.txt | 2 - examples/basic/virus_on_network/run.py | 3 - .../virus_on_network/server.py | 140 ------------------ 7 files changed, 76 insertions(+), 217 deletions(-) create mode 100644 examples/basic/virus_on_network/agents.py rename examples/basic/virus_on_network/{virus_on_network => }/model.py (57%) delete mode 100644 examples/basic/virus_on_network/requirements.txt delete mode 100644 examples/basic/virus_on_network/run.py delete mode 100644 examples/basic/virus_on_network/virus_on_network/server.py diff --git a/examples/basic/virus_on_network/README.md b/examples/basic/virus_on_network/README.md index f6a51fd580b..484a4c6ffdf 100644 --- a/examples/basic/virus_on_network/README.md +++ b/examples/basic/virus_on_network/README.md @@ -43,9 +43,9 @@ Directly run the file ``run.py`` in the terminal. e.g. ## Files -* ``run.py``: Launches a model visualization server. * ``model.py``: Contains the agent class, and the overall model class. -* ``server.py``: Defines classes for visualizing the model (network layout) in the browser via Mesa's modular server, and instantiates a visualization server. +* ``agents.py``: Contains the agent class. +* ``app.py``: Contains the code for the interactive Solara visualization. ## Further Reading diff --git a/examples/basic/virus_on_network/agents.py b/examples/basic/virus_on_network/agents.py new file mode 100644 index 00000000000..3d9486aa4eb --- /dev/null +++ b/examples/basic/virus_on_network/agents.py @@ -0,0 +1,70 @@ +from mesa import Agent +from enum import Enum + + +class State(Enum): + SUSCEPTIBLE = 0 + INFECTED = 1 + RESISTANT = 2 + + +class VirusAgent(Agent): + """ + Individual Agent definition and its properties/interaction methods + """ + + def __init__( + self, + model, + initial_state, + virus_spread_chance, + virus_check_frequency, + recovery_chance, + gain_resistance_chance, + ): + super().__init__(model) + + self.state = initial_state + + self.virus_spread_chance = virus_spread_chance + self.virus_check_frequency = virus_check_frequency + self.recovery_chance = recovery_chance + self.gain_resistance_chance = gain_resistance_chance + + def try_to_infect_neighbors(self): + neighbors_nodes = self.model.grid.get_neighborhood( + self.pos, include_center=False + ) + susceptible_neighbors = [ + agent + for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) + if agent.state is State.SUSCEPTIBLE + ] + for a in susceptible_neighbors: + if self.random.random() < self.virus_spread_chance: + a.state = State.INFECTED + + def try_gain_resistance(self): + if self.random.random() < self.gain_resistance_chance: + self.state = State.RESISTANT + + def try_remove_infection(self): + # Try to remove + if self.random.random() < self.recovery_chance: + # Success + self.state = State.SUSCEPTIBLE + self.try_gain_resistance() + else: + # Failed + self.state = State.INFECTED + + def try_check_situation(self): + if (self.random.random() < self.virus_check_frequency) and ( + self.state is State.INFECTED + ): + self.try_remove_infection() + + def step(self): + if self.state is State.INFECTED: + self.try_to_infect_neighbors() + self.try_check_situation() diff --git a/examples/basic/virus_on_network/app.py b/examples/basic/virus_on_network/app.py index 4ed96b79b0b..89c377b25d2 100644 --- a/examples/basic/virus_on_network/app.py +++ b/examples/basic/virus_on_network/app.py @@ -4,7 +4,7 @@ from matplotlib.figure import Figure from matplotlib.ticker import MaxNLocator from mesa.visualization import SolaraViz, Slider, make_space_matplotlib -from virus_on_network.model import State, VirusOnNetwork, number_infected +from model import State, VirusOnNetwork, number_infected def agent_portrayal(graph): diff --git a/examples/basic/virus_on_network/virus_on_network/model.py b/examples/basic/virus_on_network/model.py similarity index 57% rename from examples/basic/virus_on_network/virus_on_network/model.py rename to examples/basic/virus_on_network/model.py index d892a0c4c06..d0ad600b8d7 100644 --- a/examples/basic/virus_on_network/virus_on_network/model.py +++ b/examples/basic/virus_on_network/model.py @@ -1,14 +1,10 @@ import math -from enum import Enum import mesa +from mesa import Model import networkx as nx - -class State(Enum): - SUSCEPTIBLE = 0 - INFECTED = 1 - RESISTANT = 2 +from agents import VirusAgent, State def number_state(model, state): @@ -27,7 +23,7 @@ def number_resistant(model): return number_state(model, State.RESISTANT) -class VirusOnNetwork(mesa.Model): +class VirusOnNetwork(Model): """ A virus model with some number of agents """ @@ -102,65 +98,3 @@ def step(self): def run_model(self, n): for i in range(n): self.step() - - -class VirusAgent(mesa.Agent): - """ - Individual Agent definition and its properties/interaction methods - """ - - def __init__( - self, - model, - initial_state, - virus_spread_chance, - virus_check_frequency, - recovery_chance, - gain_resistance_chance, - ): - super().__init__(model) - - self.state = initial_state - - self.virus_spread_chance = virus_spread_chance - self.virus_check_frequency = virus_check_frequency - self.recovery_chance = recovery_chance - self.gain_resistance_chance = gain_resistance_chance - - def try_to_infect_neighbors(self): - neighbors_nodes = self.model.grid.get_neighborhood( - self.pos, include_center=False - ) - susceptible_neighbors = [ - agent - for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) - if agent.state is State.SUSCEPTIBLE - ] - for a in susceptible_neighbors: - if self.random.random() < self.virus_spread_chance: - a.state = State.INFECTED - - def try_gain_resistance(self): - if self.random.random() < self.gain_resistance_chance: - self.state = State.RESISTANT - - def try_remove_infection(self): - # Try to remove - if self.random.random() < self.recovery_chance: - # Success - self.state = State.SUSCEPTIBLE - self.try_gain_resistance() - else: - # Failed - self.state = State.INFECTED - - def try_check_situation(self): - if (self.random.random() < self.virus_check_frequency) and ( - self.state is State.INFECTED - ): - self.try_remove_infection() - - def step(self): - if self.state is State.INFECTED: - self.try_to_infect_neighbors() - self.try_check_situation() diff --git a/examples/basic/virus_on_network/requirements.txt b/examples/basic/virus_on_network/requirements.txt deleted file mode 100644 index 03e3c237233..00000000000 --- a/examples/basic/virus_on_network/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -networkx>=2.0 -mesa~=2.0 \ No newline at end of file diff --git a/examples/basic/virus_on_network/run.py b/examples/basic/virus_on_network/run.py deleted file mode 100644 index c911c372a9a..00000000000 --- a/examples/basic/virus_on_network/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from virus_on_network.server import server - -server.launch(open_browser=True) diff --git a/examples/basic/virus_on_network/virus_on_network/server.py b/examples/basic/virus_on_network/virus_on_network/server.py deleted file mode 100644 index dcc7643f080..00000000000 --- a/examples/basic/virus_on_network/virus_on_network/server.py +++ /dev/null @@ -1,140 +0,0 @@ -import math - -import mesa - -from .model import State, VirusOnNetwork, number_infected - - -def network_portrayal(G): - # The model ensures there is always 1 agent per node - - def node_color(agent): - return {State.INFECTED: "#FF0000", State.SUSCEPTIBLE: "#008000"}.get( - agent.state, "#808080" - ) - - def edge_color(agent1, agent2): - if State.RESISTANT in (agent1.state, agent2.state): - return "#000000" - return "#e8e8e8" - - def edge_width(agent1, agent2): - if State.RESISTANT in (agent1.state, agent2.state): - return 3 - return 2 - - def get_agents(source, target): - return G.nodes[source]["agent"][0], G.nodes[target]["agent"][0] - - portrayal = {} - portrayal["nodes"] = [ - { - "size": 6, - "color": node_color(agents[0]), - "tooltip": f"id: {agents[0].unique_id}
state: {agents[0].state.name}", - } - for (_, agents) in G.nodes.data("agent") - ] - - portrayal["edges"] = [ - { - "source": source, - "target": target, - "color": edge_color(*get_agents(source, target)), - "width": edge_width(*get_agents(source, target)), - } - for (source, target) in G.edges - ] - - return portrayal - - -network = mesa.visualization.NetworkModule( - portrayal_method=network_portrayal, - canvas_height=500, - canvas_width=500, -) -chart = mesa.visualization.ChartModule( - [ - {"Label": "Infected", "Color": "#FF0000"}, - {"Label": "Susceptible", "Color": "#008000"}, - {"Label": "Resistant", "Color": "#808080"}, - ] -) - - -def get_resistant_susceptible_ratio(model): - ratio = model.resistant_susceptible_ratio() - ratio_text = "∞" if ratio is math.inf else f"{ratio:.2f}" - infected_text = str(number_infected(model)) - - return f"Resistant/Susceptible Ratio: {ratio_text}
Infected Remaining: {infected_text}" - - -model_params = { - "num_nodes": mesa.visualization.Slider( - name="Number of agents", - value=10, - min_value=10, - max_value=100, - step=1, - description="Choose how many agents to include in the model", - ), - "avg_node_degree": mesa.visualization.Slider( - name="Avg Node Degree", - value=3, - min_value=3, - max_value=8, - step=1, - description="Avg Node Degree", - ), - "initial_outbreak_size": mesa.visualization.Slider( - name="Initial Outbreak Size", - value=1, - min_value=1, - max_value=10, - step=1, - description="Initial Outbreak Size", - ), - "virus_spread_chance": mesa.visualization.Slider( - name="Virus Spread Chance", - value=0.4, - min_value=0.0, - max_value=1.0, - step=0.1, - description="Probability that susceptible neighbor will be infected", - ), - "virus_check_frequency": mesa.visualization.Slider( - name="Virus Check Frequency", - value=0.4, - min_value=0.0, - max_value=1.0, - step=0.1, - description="Frequency the nodes check whether they are infected by a virus", - ), - "recovery_chance": mesa.visualization.Slider( - name="Recovery Chance", - value=0.3, - min_value=0.0, - max_value=1.0, - step=0.1, - description="Probability that the virus will be removed", - ), - "gain_resistance_chance": mesa.visualization.Slider( - name="Gain Resistance Chance", - value=0.5, - min_value=0.0, - max_value=1.0, - step=0.1, - description="Probability that a recovered agent will become " - "resistant to this virus in the future", - ), -} - -server = mesa.visualization.ModularServer( - model_cls=VirusOnNetwork, - visualization_elements=[network, get_resistant_susceptible_ratio, chart], - name="Virus on Network Model", - model_params=model_params, -) -server.port = 8521