UserPreferences

PyroModuleFSM:UsingGenerators


Pyro Advanced Module: FSM using Python Generators

In the previous section we saw a Brain architecture for simulating a state machine. However, state machines only allow you to go to one state or another. There really isn't the idea of a subroutine. However, we did introduce the ideas of push/pop in the previous section which allows control to pass back to the state from a substate. But, control always begins again at the beginning of the state.

Sometimes we might like to do something like the following:

    if A:
       self.gosub("step1")
       self.gosub("step2")
       self.gosub("step3")
    else:
       self.gosub("step4")
       self.gosub("step5")

This idea of sequential gosubs would be very difficult using the push/pop machinery from the previous section. In fact, there is no clear way of doing these short of creating a different path through a FSM for each possible sequence of states.

The problem is that push/pop didn't really transfer control, but merely pushed or popped values off a stack, so when the step method exited, the next state would be picked appropriately.

To implement the above combination of function and state, we need a bit more power in our language. We could just call functions directly, but that would require us to take care of FSM details in our code directly. That is, we would though out the FSM abstractions. However, Python now includes ideas from more sophisticated languages. One idea that we can take advantage here is the idea of a "generator". Using the "yield" command in Python automatically turns a normal function into a generator.

Consider this simple example:

def generator():
    yield 1
    yield 2
    yield 3

g = generator()
for j in g:
    print j

Here, the function generator that returns a value (1) with the yield statement. The statement "for j in g:" then asks for the instance g to give it a sequence of results. When g is asked for another value, g() continues on from where is last left off, and returns the second yield. When there are no more yields, then j is done.

We can use this same technique to build a control system. The following code sketches this idea out.

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 

"""
An advance FSM Brain architecture for allowing a true GOSUB which
saves the state of the State, and returns to continue where it left
off. Takes advantage of the new "yield" command in Python.

All states are generators. Control is handled by using "yield". Yield
passes back:

"continue" - let others have a turn, and continue
"goto"     - continue processing in another state
"gosub"    - go do another state as a function
"quit"     - system will stop

TODO: allow system to be in multiple states simultaneously.
"""

import random, time

def process(control, level=0):
    """ This function processes the states. """
    command = "goto"
    state = None
    arg = None
    while command not in ["return", "quit"]:
        print "at level", level
        for stepResults in control: # this is where the call is
            if type(stepResults) == type(""):
                command, state, arg = stepResults, None, None
            else:
                command, state, arg = stepResults
            # -----------------------------
            if command == "goto" or command == "quit":
                break
            elif command == "gosub":
                command = process(state(*arg), level+1) # recursive
                if command == "quit":
                    break
            elif command == "return":
                break
            time.sleep(.001)
        print "Ending level", level
        if command == "goto":
            control = state(*arg)
    return command

def State1(*args):
    """ Sample state """
    print "Computing in State1"
    for i in range(100):
        yield "continue"
    print
    r = random.random()
    if r < .33:
        print "   pre-goto..."
        yield "goto", State2, (11,)
        print "   ERROR post-goto..." # never gets here
    elif r < .66:
        print "   pre-gosub..."
        yield "gosub", State2, (12,)
        print "   post-gosub..."
    print "end loop..."
    yield "return" # only do this when done, otherwise will loop

def State2(*args):
    print "Computing in State2"
    for i in range(100):
        yield "continue"
    print
    r = random.random()
    if r < .33:
        print "   pre-goto..."
        yield "goto", State1, (11,)
        print "   ERROR post-goto..." # never gets here
    elif r < .66:
        print "   pre-gosub..."
        yield "gosub", State1, (12,)
        print "   post-gosub..."
    print "end loop..."
    yield "return" # only do this when done, otherwise will loop

# Get the process running:
retval = None
while retval != "quit":
    retval = process(State1())

Pyro Modules Table of Contents

Modules

  1. PyroModuleIntroduction

  2. PyroModuleObjectOverview

  3. PyroModulePythonIntro

  4. PyroModuleDirectControl

  5. PyroModuleSequencingControl

  6. PyroModuleBehaviorBasedControl

  7. PyroModuleReinforcementLearning

  8. PyroModuleNeuralNetworks

  9. PyroModuleEvolutionaryAlgorithms

  10. PyroModuleComputerVision

  11. PyroModuleMapping

  12. PyroModuleMultirobot

  13. FurtherReading

Additional Resources

  1. PyroIndex

  2. PyroAdvancedTopics

  3. PyroUserManual

  4. [WWW]Pyro Tutorial Movies

Reference: PyroSiteNotes