Skip to content

Commit

Permalink
Linting & bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
charwick committed Jun 15, 2023
1 parent 84f0924 commit de22eab
Show file tree
Hide file tree
Showing 9 changed files with 28 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ If you use Helipad in your own research, please cite as follows:
## Version History

* 1.6: Geospatial models, agent scatterplot visualizer, new `Agents` and `Edges` containers
* [1.6](https://helipad.dev/2023/06/helipad-1-6/): Geospatial models, agent scatterplot visualizer, new `Agents` and `Edges` containers
* [1.5](https://helipad.dev/2023/01/helipad-1-5/): Polar grid spatial models, various spatial improvements
* [1.4](https://helipad.dev/2022/07/helipad-1-4/): More consistent container API, localization, miscellaneous interface improvements
* [1.3](https://helipad.dev/2021/06/helipad-1-3/): Allow mixing time series and other plots, display networks on spatial maps, goods API improvements
Expand Down
1 change: 1 addition & 0 deletions helipad/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ def __setitem__(self, key: str, val):
elif isinstance(key, tuple) and isinstance(key[1], str): self.goods[key[0]][key[1]] = val
else: raise KeyError

def __repr__(self): return {k: g['quantity'] for k,g in self.goods.items()}.__repr__()
def __iter__(self): return iter({k: g['quantity'] for k,g in self.goods.items()})
def __next__(self): return next({k: g['quantity'] for k,g in self.goods.items()})
def __len__(self): return len(self.goods)
Expand Down
1 change: 0 additions & 1 deletion helipad/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""

import warnings
from numbers import Number
from sys import __stdout__
from io import BufferedWriter

Expand Down
13 changes: 7 additions & 6 deletions helipad/spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
from random import uniform
from math import sqrt, sin, cos, atan2, pi, copysign, floor
from abc import ABC, abstractmethod
from numbers import Number
from helipad.agent import Patch, baseAgent
from helipad.visualize import Charts
from helipad.helpers import ï, Number, Item
from helipad.helpers import ï, Item

#===============
# SETUP
Expand Down Expand Up @@ -178,7 +179,7 @@ def __init__(self, dim, wrap=True, **kwargs):
self.dim = dim
self.offmap = kwargs['offmap']
self.corners = kwargs['corners']
if not 'noinstall' in kwargs: RectFuncs.install()
if 'noinstall' not in kwargs: RectFuncs.install()
super().__init__([])

def at(self, x, y): return self[round(x), round(y)]
Expand Down Expand Up @@ -349,14 +350,14 @@ def revive(self, index):
class RectFuncs:
"""Installs distance, neighbor, and angle functions for rectangular coordinates into agent objects. This is a static class and should not be instantiated."""
@staticmethod
def install(patches=True, agents=True, all=True):
def install(patches=True, agents=True, allagents=True):
if patches:
for p in ['up', 'right', 'down', 'left', 'agentsOn', 'center', 'area', 'vertices']:
setattr(Patch, p, property(getattr(RectFuncs,p)))
if agents:
for f in ['moveUp', 'moveRight', 'moveDown', 'moveLeft', 'forward', 'orientTo']:
setattr(baseAgent, f, NotPatches(getattr(RectFuncs,f)))
if all:
if allagents:
for h in ['_offset', 'distanceFrom']:
setattr(baseAgent, h, getattr(RectFuncs,h))

Expand Down Expand Up @@ -418,14 +419,14 @@ def distanceFrom(agent, agent2):
class PolarFuncs:
"""Installs distance, neighbor, and angle functions for polar coordinates into agent objects. This is a static class and should not be instantiated."""
@staticmethod
def install(patches=True, agents=True, all=True):
def install(patches=True, agents=True, allagents=True):
if patches:
for p in ['inward', 'outward', 'clockwise', 'counterclockwise', 'agentsOn', 'center', 'area', 'vertices']:
setattr(Patch, p, property(getattr(PolarFuncs,p)))
if agents:
for f in ['moveInward', 'moveOutward', 'moveClockwise', 'moveCounterclockwise', 'forward', 'orientTo']:
setattr(baseAgent, f, NotPatches(getattr(PolarFuncs,f)))
if all:
if allagents:
baseAgent.distanceFrom = PolarFuncs.distanceFrom

def inward(patch):
Expand Down
16 changes: 8 additions & 8 deletions helipad/visualize.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __repr__(self): return f'<{self.__class__.__name__} with {len(self)} plots>'

# Subclasses should call super().launch **after** the figure is created.
@abstractmethod
def launch(self, title: str, dim=None, pos=None):
def launch(self, title: str):
if not isNotebook():
self.fig.canvas.manager.set_window_title(title)
if self.model.cpanel: self.model.cpanel.setAppIcon()
Expand Down Expand Up @@ -616,7 +616,7 @@ def update(self, data: dict, t: int):

def draw(self, t: int=None, forceUpdate: bool=False):
if t is None: t=self.viz.scrubval
i = int(t/self.viz.refresh.get())-1
i = int(t/self.viz.model.param('refresh'))-1
for b in self.bars:
setbar = b.element.set_width if self.horizontal else b.element.set_height
setbar(b.data[i])
Expand Down Expand Up @@ -725,16 +725,16 @@ def MPLEvent(self, event):
self.viz.model.doHooks('patchClick', [self.viz.model.patches.at(x,y), self, self.viz.scrubval])

def update(self, data: dict, t: int):
G = self.viz.model.agents.network(self.network, self.prim, excludePatches=(self.prim!='patch'))
G = self.viz.model.agents.network(self.network, self.prim, excludePatches=self.prim!='patch')

#Capture data for label, size, and scatterplot position
vars = {}
if self.params['agentLabel'] and self.params['agentLabel'] is not True: vars['label'] = self.params['agentLabel']
if self.params['agentSize'] and type(self.params['agentSize']) not in [int, float]: vars['size'] = self.params['agentSize']
capture = {}
if self.params['agentLabel'] and self.params['agentLabel'] is not True: capture['label'] = self.params['agentLabel']
if self.params['agentSize'] and type(self.params['agentSize']) not in [int, float]: capture['size'] = self.params['agentSize']
if self.scatter:
for v in self.scatter: vars[v] = v
for v in self.scatter: capture[v] = v
agents = {a.id:a for a in (self.viz.model.agents[self.prim] if self.prim else self.viz.model.agents.all)}
for k,v in vars.items():
for k,v in capture.items():
if 'good:' in v:
for n in G.nodes: G.nodes[n][k] = agents[n].stocks[v.split(':')[1]]
else:
Expand Down
6 changes: 3 additions & 3 deletions sample-models/cpanel-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def modelPostStep(model):
random.choice(model.agents.edges['edge']).cut()
newedge(model)

net = viz.addPlot('net', 'Network Structure', type='agents', scatter=['s1', 's2'], network=None)
net = viz.addPlot('net', 'Network Structure', type='agents', scatter=['s1', 's2'])
bar1 = viz.addPlot('prop', 'Bar Chart')
bar2 = viz.addPlot('prop2', 'Horizontal Bar Chart', horizontal=True)
net.config({
Expand All @@ -106,7 +106,7 @@ def modelPostStep(model):
'labelAlpha': 0.75,
'labelWeight': 'bold',
'labelSize': 8,
'regLine': True
'regLine': True
})

gcolors = ['F00', 'F03', 'F06', 'F09', 'F0C', 'C0F', '90F', '60F', '30F', '00F']
Expand All @@ -125,7 +125,7 @@ def agentClick(agents, plot, t):
agent.die()
for e in range(enum): newedge(heli)
print('Killing agent',agent.id,'and creating agent',new.id)
plot.record(None, t)
plot.update(None, t)
plot.draw(t, forceUpdate=True)

#===============
Expand Down
2 changes: 1 addition & 1 deletion sample-models/gameoflife.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def modelStep(model, stage):
@heli.hook
def patchClick(patch, plot, t):
patch.active = not patch.active
plot.record(None, t)
plot.update(None, t)
plot.draw(t, forceUpdate=True)

@heli.button
Expand Down
3 changes: 2 additions & 1 deletion sample-notebooks/pricediscover.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"This line configures the model to match agents each period and run them through a [`match`](https://helipad.dev/hooks/match) hook that we'll define later, rather than stepping through them individually. `'match'` is equivalent to `'match-2'`. We could also set [`heli.order`](https://helipad.dev/functions/model/#order) to `'match-3'` if we wanted it to group in triplets, or any other number. But for a trading model, and for most others, pairs are what we want.\n",
"This line configures the model to match agents each period and run them through a [`match`](https://helipad.dev/hooks/match) hook that we'll define later, rather than stepping through them individually. `'match'` is equivalent to `'match-2'`. We could also set [`heli.agents.order`](https://helipad.dev/functions/agents#order) to `'match-3'` if we wanted it to group in triplets, or any other number. But for a trading model, and for most others, pairs are what we want.\n",
"\n",
"For the others, we’ll hook (1) and (2) into [agent initialization](https://helipad.dev/hooks/agentinit), (4) into [agent activation](https://helipad.dev/hooks/agentstep), and (5) we’ll gather afterward.\n",
"\n",
Expand Down
10 changes: 5 additions & 5 deletions sample-notebooks/viz-tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"source": [
"## The Visualizer\n",
"\n",
"In order to visualize the population on each patch, we'll use Matplotlib's [`bar3d`](https://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d) function. `ChartPlot` requires us to specify three methods: `launch(axes)`, `record(data,t)`, and `draw(t)`. We'll write these functions one at a time and then compile them into the class at the end so we can intersperse exposition, but more usually you'll just put all of these methods underneath the class definition."
"In order to visualize the population on each patch, we'll use Matplotlib's [`bar3d`](https://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d) function. `ChartPlot` requires us to specify three methods: `launch(axes)`, `update(data,t)`, and `draw(t)`. We'll write these functions one at a time and then compile them into the class at the end so we can intersperse exposition, but more usually you'll just put all of these methods underneath the class definition."
]
},
{
Expand All @@ -129,10 +129,10 @@
" def launch(self, axes):\n",
" self.heights = {}\n",
" super().launch(axes)\n",
" self.record(self.viz.model.data.all, 0)\n",
" self.update(self.viz.model.data.all, 0)\n",
" self.draw(0)\n",
" \n",
" def record(self, data, t):\n",
" def update(self, data, t):\n",
" Z = np.array([list(range(self.viz.model.param('x'))) for i in list(range(self.viz.model.param('y')))])\n",
" \n",
" for x,col in enumerate(self.viz.model.patches):\n",
Expand All @@ -159,9 +159,9 @@
"\n",
"For some plot types, we will be able to update the plot data without redrawing it entirely. In this case, we would draw the plot in `launch()` and update it in `draw()`. Matplotlib's `bar3d` is not one of these, unfortunately. Since we have to redraw the plot each time, we can use a minimal `launch()` method and put all of our drawing code in `draw()`. Here, all we need to do is set aside a variable to store height data which we can scrub back over later.\n",
"\n",
"We populate `self.heights` in the `record()` method, using the model time as the dict index. Note that since the data we need is not reported in a reporter, and thus not passed to the function in the `data` argument, we can retrieve the model object directly using `self.viz.model`. Since the `X` and `Y` coordinates of all the bars stays the same, the only thing we need to store is the `Z` height.\n",
"We populate `self.heights` in the `update()` method, using the model time as the dict index. Note that since the data we need is not reported in a reporter, and thus not passed to the function in the `data` argument, we can retrieve the model object directly using `self.viz.model`. Since the `X` and `Y` coordinates of all the bars stays the same, the only thing we need to store is the `Z` height.\n",
"\n",
"As mentioned earlier, Matplotlib's `axes3d` class does not provide an easy way to update chart data without redrawing it, so we must recreate the chart on each update, using the `Z` data that we stored above in `record()`.\n",
"As mentioned earlier, Matplotlib's `axes3d` class does not provide an easy way to update chart data without redrawing it, so we must recreate the chart on each update, using the `Z` data that we stored above in `update()`.\n",
"\n",
"The Matplotlib [`bar3d` method](https://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d) requires a rather particular array structure. The code above is adapted from the [3D bar chart demo](https://matplotlib.org/3.3.3/gallery/mplot3d/3d_bars.html)."
]
Expand Down

0 comments on commit de22eab

Please sign in to comment.