Putting, getting multiple objects into STORE

1,487 views
Skip to first unread message

Cyril Schmidiger

unread,
Nov 6, 2015, 5:12:47 AM11/6/15
to python-simpy
Hello all
Thanks for setting this up!
I have two questions regarding Simpy 3:

a) Is it possible to put/get multiple objects (class objects) to a Store/FilterStore?
b) How to monitor the "level" of a Store? Is there a way to get the total amount of objects in the Store?

Some feedback is much appreciated.
Cyril

James Arruda

unread,
Nov 6, 2015, 12:59:46 PM11/6/15
to python-simpy
For (a):

The simplest way to get or put multiple objects is to (obviously) loop over a get or put request. That solution won't work, though, if you have a requirement that you must get all the objects at once or put all the objects at once.

The Stores that come with SimPy 3 don't allow multiple items to be get or put at once. If you need to bottleneck on that kind of behavior, then you'll have to subclass a Store to do that for you. This exchange on the old mailing list: http://sourceforge.net/p/simpy/mailman/message/34574959/ has some code that you can work with to create a Store that does what you need.



For (b):

Every store has an attribute 'items', which you can take the len() of to find out the amount of objects. 

For monitoring the level of a resource, the documents have some suggestions:


Adapting that to stores should be straightforward, since stores only have a 'get' and a 'put' method. 

Cyril Schmidiger

unread,
Nov 13, 2015, 2:53:38 AM11/13/15
to python-simpy
Great stuff, thanks for your comments!

nkz

unread,
May 27, 2016, 3:19:40 AM5/27/16
to python-simpy
Hello Everybody (James, Cyril), 

I apologize for resurrecting an old tread, but I have a similar problem. I would like a job to simultaneously retrieve multiple items (resources) from a FilterStore. If all items are not available, the job (job A) needs to wait until all items are available. In the meantime, if new job (job B) comes along and wants to retrieve only one item which was also requested by one of the items of the previous job (job A), then the new job (job B) is prioritized and able to retrieve that resource. The previous job (job A) will have to wait a bit longer. 

Like mentioned above, a for loop doesn't work, simply because a for loop simply reserves the item (resource) for the previous job (job A) and the new job (job B) doesn't get prioritized. An infinite while loop (while True:) might be a possibility, albeit a problematic one. 

I am looking for a solution similar to the one in the FilterStore example for retrieving one specific resource (https://simpy.readthedocs.io/en/latest/topical_guides/resources.html#stores). 

machine = yield ms.get(lambda machine: machine.size == size)
machines = yield ms.getmultiple(lambda machine: machine.size == size, lambda machine: machine.size == size)

I was curious to learn more about the possibility of sub classing the FilterStore. However, the exchange in the old mailing list not available any more (http://sourceforge.net/p/simpy/mailman/message/34574959/). 

Does anybody know where to retrieve a copy of the old mailing list? Or, other information about subclassing the FilterStore to accomodate the behaviour listed above? Or, any other solution?

Thanks you,

nkz

Adrian Mc

unread,
Feb 11, 2018, 10:29:15 AM2/11/18
to python-simpy

I have a similar predicament to @nkz. Did anyone find out how to subclass FilterStore to retrieve multiple items at once?

James Arruda

unread,
Feb 12, 2018, 8:25:10 AM2/12/18
to python-simpy
The solution is to sublcass StoreGet and base.BaseResource. Here is a minimal example:

from simpy.core import BoundClass
from simpy.resources import base
from simpy.resources.store import StorePut, StoreGet
from functools import reduce


class MultipleItemStoreGet(StoreGet):
    """ Store a list of items you want to get
    """
    def __init__(self, store, item_filters):
        self.item_filters= item_filters if isinstance(item_filters, list) else [item_filters]
        super(StoreGet, self).__init__(store)

class MultipleItemStore(base.BaseResource):
    def __init__(self, env=None):
        super(MultipleItemStore, self).__init__(env=set_env(env), capacity=inf)
        self.items = []

    put = BoundClass(StorePut)
    """Request to put *item* into the store."""

    get = BoundClass(MultipleItemStoreGet)
    """Request to get an *item* out of the store."""

    def _do_put(self, event):
        # here you could add code for multiple items being put back at once
        # for now, just assume one item at a time
       self.items.append(event.item)
       event.succeed()

   def _do_get(self, event):
       matches = []
       items_needed = len(event.item_filters)
       for filter in event.item_filters:
            matches.append([x for x in self.items if filter(x)])
            # if no match was found, exit
            return True
       unique_matches = reduce(lambda a, b: a.union(b), matches, set())
       if len(unique_matches) < items_needed:
           # not enough unique matches
           return True
      # You'll need to do more checking to make sure the unique matches can still fit all the requirements
      # ...
      # return the items
      event.succeed(unique_matches[:items_needed ])
      return True


This code is very rough and untested. It should give you a starting point for implementing multiple item getting.

Cheers,

James

Adrian Mc

unread,
Feb 15, 2018, 9:10:36 AM2/15/18
to python-simpy
Hi James,

thanks for supplying that code, I was able to get a version of it working, which I am attaching below.
I haven't done much in terms of checking the item filters yet, but I'll post an updated version if I spot any issues.


Thanks again!
Adrian.

from simpy.core import BoundClass
from simpy.resources import base
from simpy.resources.store import StorePut, StoreGet
from functools import reduce


class BatchStoreGet(StoreGet):
    """ Store a list of items you want to get
    """
    def __init__(self, store, qty, item_filters=None):
        self.qty = qty
        if item_filters is None:
            item_filters = lambda x: x == x
        self.item_filters = item_filters if isinstance(item_filters, list) else [item_filters]
        super(StoreGet, self).__init__(store)


class BatchStore(base.BaseResource):
    
    def __init__(self, env=None, capacity=inf, name=''):
        self.name = name
        super(BatchStore, self).__init__(env=env, capacity=inf)
        self.items = []

    put = BoundClass(StorePut)
    """Request to put *item* into the store."""

    get = BoundClass(BatchStoreGet)
    """Request to get *items* out of the store."""

    def _do_put(self, event):
        # Here you could add code for multiple items being put back at once
        # for now, just assume one item at a time
        self.items.append(event.item)
        event.succeed()

    def _do_get(self, event):
        matches = []
        items_needed = event.qty
        for filter in event.item_filters:
            matches.extend([x for x in self.items if filter(x)])
            # If no match was found, exit..
            if len(matches) == 0:
                return True
            else:
                pass
        unique_matches = matches
        if len(unique_matches) < items_needed:
            # Not enough unique matches...
            return True
        else:
            pass
        # You'll need to do more checking to make sure the unique matches can still fit all the requirements
        # ...
        # Return the items
        event.succeed(unique_matches[:items_needed])
        # Clear those items from the store...
        for item in unique_matches[:items_needed]:
            self.items.remove(item)
        return True






Reply all
Reply to author
Forward
0 new messages