[PEAK] PEAK Components: beginner questions

Duncan McGreggor python at adytumsolutions.com
Tue Sep 21 03:10:49 EDT 2004


Hey everyone,

Some colleagues and I have been working on behavioral modeling in 
python, and it's just now getting to the point of moving from sandboxes 
and test code to prototype classes. Intuitively, PEAK felt like a 
perfect match for this kind of thing.

I've been reading the "Composing Hierarchies" entry on the wiki 
(http://peak.telecommunity.com/DevCenter/ComposingHierarchies) and 
trying to learn from it for about a couple days. I'm really new to 
these concepts, and the wiki example isn't practical enough for me, so 
I tried building another one. Now that I've had the chance to juggle 
the code and ideas around, I was hoping to get some feedback from the 
community about the approach I have tried and suggestions for 
improvements, best practices, or misconceptions I have worked into the 
code.

I have pasted the sample code below. The code is very silly and meant 
to be an experiment in PEAK, and not a serious model for a software 
agent ;-) Also, after the code, I've pasted a list of questions that 
came up as a result of going through this process.

I would like to integrate code, questions, answers and feedback and 
then post it on the wiki as an additional example for PEAK beginners 
like myself. Any objections?

General plan of hierarchy/component-relationships:

Body provides an interface IBody
Physiology provides an interface IPhysiology and depends on Body
Intellect, Vision and Hearing provide an interface IMind
Movement provides and interface IMovement
Emotion provides an interface IEmotion and depends on Physiology, 
Vision, and Hearing
Action depends on Emotion, Physiology, Intellect, and Movement

Code:

from protocols import Interface, advise
from peak.binding.components import Component, Obtain, Make
from random import random
from math import ceil

#------------------------------------------------------------
class IBody(Interface):
     '''Body interface. Body can be mechanical, bio, or other'''
     def getFuelLevel():
         "how long can we operate?"
     def getDamageLevel():
         "are we disabled?"

class IPhysiology(IBody):
     '''Physiology interface'''
     def getHeartRate():
         '''provide information about the heart rate'''

class Body(Component):
     '''
     >>> b = Body()
     >>> b.getFuelLevel()
     'half full'
     >>> b.getDamageLevel()
     'minimal damage'
     '''
     advise(instancesProvide=[IBody])
     def getFuelLevel(self):
         return "half full"
     def getDamageLevel(self):
         return "minimal damage"

class Physiology(Component):
     '''
     >>> p = Physiology()
     >>> p.getHeartRate()
     'nice rhythm!'
     >>> p.body.getFuelLevel()
     'half full'
     >>> p.body.getDamageLevel()
     'minimal damage'
     '''
     advise(instancesProvide=[IPhysiology])
     body = Make(Body, offerAs=[IBody])
     def getHeartRate(self):
         return "nice rhythm!"

#------------------------------------------------------------
class IMind(Interface):
     '''Mind interface'''
     def perceive():
         '''provide perception'''
     def think():
         '''provide 'thought' mechanism'''

class Intellect(Component):
     advise(instancesProvide=[IMind])
     def think(self):
         print "hmmm, fire bad, thinking hard..."

class Vision(Component):
     advise(instancesProvide=[IMind])
     def perceive(self):
         if ceil(random()*2)%2: return "see threat!"

class Hearing(Component):
     advise(instancesProvide=[IMind])
     def perceive(self):
         if ceil(random()*3)%3: return "hear threat!"
#------------------------------------------------------------
class IEmotion(Interface):
     '''Emotion interface'''
     def getIntensity():
         '''provide aa means for obtaining emotional intensities'''

class Emotion(Component):
     '''
     >>> e = Emotion()
     >>> e.vision.perceive() in [None, 'see threat!']
     True
     >>> e.hearing.perceive() in [None, 'hear threat!']
     True
     >>> e.physiology.getHeartRate()
     'nice rhythm!'
     >>> type(e.getIntensity()).__name__
     'int'
     '''
     advise(instancesProvide=[IEmotion])
     def __init__(self):
         self.feeling = 0
     vision = Make(Vision, offerAs=[IMind])
     hearing = Make(Hearing, offerAs=[IMind])
     physiology = Make(Physiology, offerAs=[IPhysiology])
     def getIntensity(self):
         '''
         Silly emotional intensity method
         '''
         if self.hearing.perceive():
             self.feeling -= 1
         else:
             self.feeling += 1
         if self.vision.perceive():
             self.feeling -= 1
         else:
             self.feeling += 1
         if self.physiology.getHeartRate():
             self.feeling += 1
         else:
             self.feeling -= 1
         return self.feeling

#------------------------------------------------------------
class IMovement(Interface):
     '''Movement interface'''
     def north():
         '''go north'''
     def south():
         '''go south'''

class IAction(Interface):
     '''Action interface'''
     def doSomething():
         '''Determine an agent action'''

class Movement(Component):
     advise(instancesProvide=[IMind])
     def north(self):
         print "you are now one step north of where you were"
     def south(self):
         print "you are now one step south of where you were"

class Action(Component):
     '''
     >>> a = Action()
     >>> a.physiology.getHeartRate()
     'nice rhythm!'
     >>> a.physiology.body.getFuelLevel()
     'half full'
     >>> a.physiology.body.getDamageLevel()
     'minimal damage'
     >>> type(a.emotion.getIntensity()).__name__
     'int'
     >>> a.intellect.think()
     hmmm, fire bad, thinking hard...
     >>> a.movement.north()
     you are now one step north of where you were
     >>> a.doSomething()
     'action!'
     '''
     advise(instancesProvide=[IAction])
     emotion = Make(Emotion, offerAs=[IMind, IPhysiology])
     physiology = Make(Physiology, offerAs=[IPhysiology])
     intellect = Make(Intellect, offerAs=[IMind])
     movement = Make(Movement, offerAs=[IMovement])
     def doSomething(self):
         '''
         Here, we can get info about the agent from
         the various components which comprise it,
         and produce some action.
         '''
         return "action!"

#------------------------------------------------------------

def _test():
     import doctest, components
     return doctest.testmod(components)

if __name__ == '__main__':
     _test()


* What's the best way for an interface to inherit from another 
interface? (I know this has been discussed... I just can't find it)
* I used Make for adding components to a class; is that the right 
approach?
* When would I choose Make over Obtain? and vice versa?
* Right now, a physiology instance p has to use p.body to get to the 
Body methods; is there are better/more direct way to do this? Seems a 
little awkward... (since I'm used to object inheritance)
* How does it complicate things that Physiology is both "in" Emotion 
and "in" Action, with action "containing" Emotion which "contains" 
Physiology?
* "In" and "contains" seem poor word choices for the last question; 
what are the standard terms used to describe such things?
* The Russian Doll component code of Emotion and Action may not be 
appropriate for an agent model, but I wanted to experiment with 
component "depth"... my approach seems amateurish, since I don't know 
what I am doing. Better approaches?
* In the Action class, Emotion is offered as IMind and IPhysiology, due 
to the fact that Vision and Hearing (in Emotion) provide IMind 
interfaces. Is this the right way to do something like this?
* Given this python/PEAK agent model, are there other interesting 
PEAK-specific questions I should be asking that I am not? Questions 
that might give better insight into design choices?




More information about the PEAK mailing list