"Thomas Rachel"  wrote in message news:isi5dk$8h1$1...@r03.glglgl.eu...

Am 04.06.2011 20:27 schrieb TommyVee:
I'm using the SimPy package to run simulations. Anyone who's used this
package knows that the way it simulates process concurrency is through
the clever use of yield statements. Some of the code in my programs is
very complex and contains several repeating sequences of yield
statements. I want to combine these sequences into common functions.

Which are then generators.

The problem of course, is that once a yield gets put into a function,
the function is now a generator and its behavior changes.

Isn't your "main" function a generator as well?


Is there  any elegant way to do this? I suppose I can do things like
ping-pong yield statements, but that solutions seems even uglier than
having a very flat, single main routine with repeating sequences.

I'm not sure if I got it right, but I think you could emulate this
"yield from" with a decorator:

def subgen1(): yield 1; yield 2;
def subgen2(): yield 1; yield 2;

Instead of doing now

def allgen():
    for i in subgen1(): yield i
    for i in subgen2(): yield i

you as well could do:

def yield_from(f):
    def wrapper(*a, **k):
        for sub in f(*a, **k):
            for i in sub:
                yield i
    return wrapper

@yield_from
def allgen():
    yield subgen1()
    yield subgen2()

(Untested.)


Thomas

Yes, the main function is a generator. Give me a day or two to absorb your code. But in the meantime, perhaps a quick explanation of the SimPy package is in order. Also, here's the link if you're interested:

http://simpy.sourceforge.net/simpy_overview.htm

The way the package works is that you instantiate what I'll call an "actor" class, inheriting from one of the SimPy base classes. Once you instantiate the actor class(es), it it "registered" in SimPy. Then when you start the simulation, SimPy's main routine will begin to "dispatch" each of the actor classes by driving what they call a "PEM" method in the actor class. Within that method, you simulate the activity of the actor. The way that you tell SimPy that you are "busy", or that you want to request or release a resource, is with a yield statement. Here's a little example:

from SimPy.Simulation import *

svc1 = Resource(capacity=1)
svc2 = Resource(capacity=1)

class Customer(Process):
   def PEM(self):

       print now(), "I am starting..."

       print now(), "I am requesting service1"
       yield request, self, svc1

print now(), "I got service 1, now I'm going ask it to do 10 ticks worth of work"
       yield hold, self, 10

       print now(), "Service 1's work is done, I am now releasing him"
       yield release, self, svc1

       print now(), "Now I am requesting service2"
       yield request, self, svc2

print now(), "I got service 2, now I'm going ask him to do 5 ticks worth of work"
       yield hold, self, 5

       print now(), "Service 2's work is done, I am now releasing him"
       yield release, self, svc2

       print now(), "I am ending..."

initialize()

# Create a customer and "register" (activate) him to SimPy
c = Customer()
activate(c, c.PEM())

# Pass control to the SimPy main dispatch routine, and run the simulation for 100 ticks
simulate(until=100)

Note that when you do the yields, you actually send back information to the SimPy main routine. For example, when you do a "yield hold, self, 10", control will be yielded to SimPy, he will simulate the passage of 10 time ticks, and redispatch you (do a "next" on your PEM). Similar deal with the "yield request, self, svc1". When you yield to SimPy, he will attempt to obtain the resource for you, and if it is already taken by another actor, you will wait in a queue until released. At that time, SimPy will then redispatch you. Obviously, this is a trivial example, since it only involves the creation of a single actor. In a real simulation, there could be hundreds of these things running "concurrently", all vying for resources, holding them for varying amounts of time, etc. SimPy also uses yield statements to simulate other things too, like waiting for a signal from another actor.

In the example you see above, note that I am "repeating" a generic sequence twice, e.g. "yield request, self, svcX", followed by "yield hold, self, time", followed by "yield release, self, svcX". In a workflow example, you may have literally dozens of services that you may hit in serial or parallel. Because of the generator "restriction", you'd have to code these three statements repeatedly in the PEM routine. It would be nice to just have a class method which did:

def UseService(self, svcName, svcTime):
   yield request, self, svcName
   yield hold, self, svcTime
   yield release, self, svcName

Then you can just call it in your main routine, passing the desired parameters (e.g., "UseService(svc1, 10)").

Anyway, hopefully you'll see that there's a reason for my original question. Let me absorb what you sent and see if it makes sense.

Thanks, Tom
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to