Graph Modeling 00
If this is your first time here it might be a good idea to start at the start of this series; there be spoilers ahead!
It is now time to try and stitch everything together! Not quite the finish line but it should be in sight.
In this post I’ll be adding graph/__init__.py
which’ll contain the Graph
base class
.
As a refresher here’s a sketch of
Hybrid_Iterator
’ssuper
relationships withdict
andIterator
class
es
By now the project’s file path structure may look something like…
graph/agents/__init__.py
graph/points/__init__.py
graph/points/construction.py
Here’s a sketch of what the following code hopes to build
Don’t get too caught-up with that bit going on with
__init__
and theadd_agent
, andadd_point
methods, get to the example usage and it should become much clearer; really the star of this show is theset_travel_plans
method ;-)
graph/__init__.py
#!/usr/bin/env python
from __future__ import absolute_import
from hybrid_iterator import Hybrid_Iterator
from graph.points import Point
from graph.agents import Agent
class Graph(Hybrid_Iterator):
"""
Let `agents` be a `dict` with `{'agent_name': 'address'}` key pares
Let `points` be a `dict` with `{'address': {'neighbors': {'addr': 'cost'}}}`
"""
def __init__(self, agents, points, **kwargs):
super(Graph, self).__init__(**kwargs)
self.update(
agents = {},
points = {},
off_duty = {},
travel_plans = {})
for point, neighbors in points.items():
self.add_point(point, neighbors)
for name, address in agents.items():
self.add_agent(name, address)
def add_agent(self, name, address):
"""
## Arguments
- `address` maybe a `str`ing or instance of `Point`
- `name` should be a `str`ing
## Assigns
- instance of `Agent` under `self['agents'][name]`
- reference to `self['points'][address]` under `self['agents'][name]['points']`
## Appends `name` to
- `self['points'][address]['population']`
"""
if isinstance(address, str):
address = self['points'][address]
self['agents'].update({name: Agent(name, address)})
address['population'].append(name)
def add_point(self, address, neighbors):
"""
Adds instance of `Point` under `self['points'][name]`
## Arguments
- `address` should be a `str`ing
- `neighbors` should be a `{addr: cost}` `dict`ionary
"""
self['points'].update({address: Point(address, neighbors)})
def set_travel_plans(self, agents = None):
"""
Assigns `self['travel_plans']` by calling `next()` on each of `agents`
Example: `{'Bill': Agent(name='Bill', ...), ...}`, __or__ `{}`
## Arguments
- `agents`; should be a `{key: Agent()}` `dict`ionary
> `agents` defaults to `self['agents']` if `None` where passed
"""
self['travel_plans'] = {}
if not agents:
agents = self['agents']
for key, agent in agents.items():
try:
self['travel_plans'].update({key: agent.next()})
except (StopIteration, GeneratorExit):
# Pop agents that will not move anymore
print("Moved {name} to off_duty".format(name = agent['name']))
self['off_duty'].update({key: agents.pop(key)})
if self['travel_plans'].get(key):
self['travel_plans'].pop(key)
return self['travel_plans']
def next(self):
"""
Calls `self.set_travel_plans`, `raise`s `GeneratorExit`
if out of `agents` or `travel_plans`, otherwise this
method will _simulate_ moving agents around the graph
"""
self.set_travel_plans(agents = self['agents'])
if not self['agents'] or not self['travel_plans']:
self.throw(GeneratorExit)
for key, agent in self['travel_plans'].items():
here = agent['point']['address']
heading = agent['heading']
there = heading.keys()[0]
# ... `heading` ~ `{addr: cost}`
print("{name} traveling from {here} to {there} paying {cost}".format(
name = agent['name'], here = here,
there = there, cost = heading[there]))
agent['point']['population'].remove(agent['name'])
agent['visited'].append(heading)
agent['point'] = self['points'][there]
agent['point']['population'].append(agent['name'])
return self
if __name__ == '__main__':
raise Exception("This file must be used as a module!")
Provided that the above was saved somewhere like graph/__init__.py
it could be imported via…
from graph import Graph
Time to initialize an instance of Graph
!
X, O = (0.2, 0.7)
graph = Graph(
agents = {
'Bill': 'u',
'Alice': 'u',
'Ted': 'v',
'Jain': 'w'
},
points = {
'u': {'v': X, 'w': X},
'v': {'u': O, 'w': X},
'w': {'u': O, 'v': X},
})
Note how having custom
set
ters eliminates most of the redundant repetition, maybe not under the hood, but this at least looks cleaner and much more approachable for adding more defaultPoint
s and/orAgent
s.
And now time to use it with a safety loop….
count = 0
for i, _ in enumerate(graph):
if i > 5:
raise Exception("Hunt for bugs!")
print("## {0} {1}".format(count, f_line))
Which should result if output very similar to…
Bill traveling from u to w paying 0.2
Alice traveling from u to v paying 0.2
Jain traveling from w to u paying 0.7
Ted traveling from v to u paying 0.7
## 0 _________
# ... Trimmed for brevity...
Bill traveling from u to v paying 0.2
Alice traveling from w to u paying 0.7
Jain traveling from v to w paying 0.2
Ted traveling from v to w paying 0.2
## 2 _________
Moved Bill to off_duty
Moved Alice to off_duty
Moved Jain to off_duty
Moved Ted to off_duty
## 3 _________
Excellent it didn’t hit the Hunt for bugs
Exception
and successfully moved the Agent
s about! While not quite fully fleshed out, with a bit of lurching it’s getting closer to something that will model graph related problems in an organized fashion.
Hopefully, in regards to having the tools and know-how to break a problem down, you’re now seeing the finish line fast approaching. If not open an Issue
detailing what steps you’ve already tried, maybe someone’ll be kind in their reply.