Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try the sample with GraphMachine extension #324

Closed
occoder opened this issue Nov 8, 2018 · 6 comments
Closed

Try the sample with GraphMachine extension #324

occoder opened this issue Nov 8, 2018 · 6 comments

Comments

@occoder
Copy link

occoder commented Nov 8, 2018

from transitions import Machine
import random
# from transitions.extensions import GraphMachine as Machine
from transitions.extensions import MachineFactory

# create a machine with mixins
diagram_cls = MachineFactory.get_predefined(graph=True)

class NarcolepticSuperhero(object):

    # Define some states. Most of the time, narcoleptic superheroes are just like
    # everyone else. Except for...
    states = ['asleep', 'hanging out', 'hungry', 'sweaty', 'saving the world']

    def __init__(self, name):

        # No anonymous superheroes on my watch! Every narcoleptic superhero gets
        # a name. Any name at all. SleepyMan. SlumberGirl. You get the idea.
        self.name = name

        # What have we accomplished today?
        self.kittens_rescued = 0

        # Initialize the state machine
        self.machine = diagram_cls(model=self, states=NarcolepticSuperhero.states, initial='asleep')

        # Add some transitions. We could also define these using a static list of
        # dictionaries, as we did with states above, and then pass the list to
        # the Machine initializer as the transitions= argument.

        # At some point, every superhero must rise and shine.
        self.machine.add_transition(trigger='wake_up', source='asleep', dest='hanging out')

        # Superheroes need to keep in shape.
        self.machine.add_transition('work_out', 'hanging out', 'hungry')

        # Those calories won't replenish themselves!
        self.machine.add_transition('eat', 'hungry', 'hanging out')

        # Superheroes are always on call. ALWAYS. But they're not always
        # dressed in work-appropriate clothing.
        self.machine.add_transition('distress_call', '*', 'saving the world',
                         before='change_into_super_secret_costume')

        # When they get off work, they're all sweaty and disgusting. But before
        # they do anything else, they have to meticulously log their latest
        # escapades. Because the legal department says so.
        self.machine.add_transition('complete_mission', 'saving the world', 'sweaty',
                         after='update_journal')

        # Sweat is a disorder that can be remedied with water.
        # Unless you've had a particularly long day, in which case... bed time!
        self.machine.add_transition('clean_up', 'sweaty', 'asleep', conditions=['is_exhausted'])
        self.machine.add_transition('clean_up', 'sweaty', 'hanging out')

        # Our NarcolepticSuperhero can fall asleep at pretty much any time.
        self.machine.add_transition('nap', '*', 'asleep')

    def update_journal(self):
        """ Dear Diary, today I saved Mr. Whiskers. Again. """
        self.kittens_rescued += 1

    def is_exhausted(self):
        """ Basically a coin toss. """
        return random.random() < 0.5

    def change_into_super_secret_costume(self):
        print("Beauty, eh?")

if __name__ == "__main__":
    batman = NarcolepticSuperhero("Batman")
    print(batman.state)
    batman.get_graph().draw('my_state_diagram.png', prog='dot')
    batman.get_graph(show_roi=True).draw('my_state_diagram.png', prog='dot')

Run this giving following exceptions:

Traceback (most recent call last):
  File "C:\Python34\lib\site-packages\pygraphviz\agraph.py", line 478, in add_edge
    eh = gv.agedge(self.handle, uh, vh, key, _Action.create)
KeyError: 'agedge: no key'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:/PycharmProjects/test/test.py", line 71, in <module>
    batman = NarcolepticSuperhero("Batman")
  File "C:/PycharmProjects/test/test.py", line 43, in __init__
    before='change_into_super_secret_costume')
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 425, in add_transition
    model.get_graph(force_new=True)
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 373, in _get_graph
    self.model_graphs[model] = self.graph_cls(self).get_graph(title if title is not None else self.title)
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 179, in get_graph
    self._add_edges(self.machine.events.copy(), fsm_graph)
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 137, in _add_edges
    container.add_edge(src, dst, **edge_attr)
  File "C:\Python34\lib\site-packages\pygraphviz\agraph.py", line 481, in add_edge
    eh = gv.agedge(self.handle, uh, vh, key, _Action.find)
KeyError: 'agedge: no key'

Process finished with exit code 1

I was using Python 3.4 64-bit, Graphviz (Stable 2.38 Windows install packages), Pygraphviz 1.3.1, transitions 0.6.9 and windows 7 64-bit

@aleneum
Copy link
Member

aleneum commented Nov 8, 2018

Hi @occoder,

unfortunately, I have currently no Windows at hand.

Could you give this a try and tell me if it results in the same error:

from transitions.extensions import MachineFactory
diagram_cls = MachineFactory.get_predefined(graph=True)

class NarcolepticSuperhero(object):
    states = ['asleep', 'saving the world']

    def __init__(self, name):
        self.name = name
        self.kittens_rescued = 0
        self.machine = diagram_cls(model=self, states=NarcolepticSuperhero.states, initial='asleep')
        self.machine.add_transition('distress_call', '*', 'saving the world')

if __name__ == "__main__":
    batman = NarcolepticSuperhero("Batman")
    print(batman.state)
    batman.get_graph().draw('my_state_diagram.png', prog='dot')

@occoder
Copy link
Author

occoder commented Nov 9, 2018

Just ran it and got the same kind of exception

Traceback (most recent call last):
  File "C:/PycharmProjects/test/test.py", line 88, in <module>
    batman = NarcolepticSuperhero("Batman")
  File "C:/PycharmProjects/test/test.py", line 85, in __init__
    self.machine.add_transition('distress_call', '*', 'saving the world')
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 425, in add_transition
    model.get_graph(force_new=True)
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 373, in _get_graph
    self.model_graphs[model] = self.graph_cls(self).get_graph(title if title is not None else self.title)
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 179, in get_graph
    self._add_edges(self.machine.events.copy(), fsm_graph)
  File "C:\Python34\lib\site-packages\transitions\extensions\diagrams.py", line 137, in _add_edges
    container.add_edge(src, dst, **edge_attr)
  File "C:\Python34\lib\site-packages\pygraphviz\agraph.py", line 481, in add_edge
    eh = gv.agedge(self.handle, uh, vh, key, _Action.find)
KeyError: 'agedge: no key'

Process finished with exit code 1

@aleneum
Copy link
Member

aleneum commented Nov 9, 2018

This is a problem with (py)graphviz and self-referencing edges under Windows. I will close this issue because it is a duplicate of #258. The issue has been reported to the maintainers of pygraphviz (issue) but hasn't been fixed yet. Unfortunately, there is nothing I can do about it. However, if you want to assist the maintainers of pygraphviz with identifying/fixing the issue, they'll probably appreciate it.

@aleneum
Copy link
Member

aleneum commented Nov 15, 2018

Hello again @occoder,

I am working on an alternative to pygraphviz for transitions. If you like, check out the dev-graphiz branch and give it a try. You will need the graphviz package and also install Graphviz manually or via Anaconda (conda install graphviz). If you use Anaconda, install graphviz via Anaconda as well with conda install python-graphviz. The dot program provided by Graphviz needs to be executable for Python (it needs to be in %PATH%). You might need to add Graphviz to your %PATH% yourself if you don't use (Ana)conda. Note that the way in which graphs are generated and saved has changed a bit:

from transitions import Machine
import random
# from transitions.extensions import GraphMachine as Machine
from transitions.extensions import MachineFactory

# create a machine with mixins
diagram_cls = MachineFactory.get_predefined(graph=True)

class NarcolepticSuperhero(object):

    # Define some states. Most of the time, narcoleptic superheroes are just like
    # everyone else. Except for...
    states = ['asleep', 'hanging out', 'hungry', 'sweaty', 'saving the world']

    def __init__(self, name):

        # No anonymous superheroes on my watch! Every narcoleptic superhero gets
        # a name. Any name at all. SleepyMan. SlumberGirl. You get the idea.
        self.name = name

        # What have we accomplished today?
        self.kittens_rescued = 0

        # Initialize the state machine
        self.machine = diagram_cls(model=self, states=NarcolepticSuperhero.states, initial='asleep')

        # Add some transitions. We could also define these using a static list of
        # dictionaries, as we did with states above, and then pass the list to
        # the Machine initializer as the transitions= argument.

        # At some point, every superhero must rise and shine.
        self.machine.add_transition(trigger='wake_up', source='asleep', dest='hanging out')

        # Superheroes need to keep in shape.
        self.machine.add_transition('work_out', 'hanging out', 'hungry')

        # Those calories won't replenish themselves!
        self.machine.add_transition('eat', 'hungry', 'hanging out')

        # Superheroes are always on call. ALWAYS. But they're not always
        # dressed in work-appropriate clothing.
        self.machine.add_transition('distress_call', '*', 'saving the world',
                         before='change_into_super_secret_costume')

        # When they get off work, they're all sweaty and disgusting. But before
        # they do anything else, they have to meticulously log their latest
        # escapades. Because the legal department says so.
        self.machine.add_transition('complete_mission', 'saving the world', 'sweaty',
                         after='update_journal')

        # Sweat is a disorder that can be remedied with water.
        # Unless you've had a particularly long day, in which case... bed time!
        self.machine.add_transition('clean_up', 'sweaty', 'asleep', conditions=['is_exhausted'])
        self.machine.add_transition('clean_up', 'sweaty', 'hanging out')

        # Our NarcolepticSuperhero can fall asleep at pretty much any time.
        self.machine.add_transition('nap', '*', 'asleep')

    def update_journal(self):
        """ Dear Diary, today I saved Mr. Whiskers. Again. """
        self.kittens_rescued += 1

    def is_exhausted(self):
        """ Basically a coin toss. """
        return random.random() < 0.5

    def change_into_super_secret_costume(self):
        print("Beauty, eh?")

if __name__ == "__main__":
    batman = NarcolepticSuperhero("Batman")
    print(batman.state)
    batman.get_graph().generate().render('my_state_diagram', format='png')
    batman.get_graph(show_roi=True).generate().render('my_roi_diagram', format='png')

The output should look like this:

  • my_state_diagram.png
    my_state_diagram
  • my_roi_diagram.png
    my_roi_diagram

@occoder
Copy link
Author

occoder commented Jan 3, 2019

Hello @aleneum
Just tried out the new approach. It worked perfectly.
Thanks for the excellent work.

@renanlarrieu
Copy link

Unfortunately, it returns:

`---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in
71 batman = NarcolepticSuperhero("Batman")
72 print(batman.state)
---> 73 batman.get_graph().generate().render('my_state_diagram', format='png')
74 batman.get_graph(show_roi=True)#.generate().render('my_roi_diagram', format='png')

AttributeError: 'Digraph' object has no attribute 'generate'`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants