class IndexedOrderedStore(Store): """Resource with *capacity* slots for storing objects in order and retrieving multiple items simultaneously from arbitrary positions in the :class:`IndexedOrderedStore`. All items in an *IndexedOrderedStore* instance must be orderable, which is to say that items must implement :meth:`~object.__lt__()`. """ put = BoundClass(StorePut) """Request to put an *item* into the store.""" get = BoundClass(IndexedOrderedStoreGet) """Request to get *items* at *indices* out of the store.""" def _do_put(self, event): if len(self.items) < self._capacity: insort(self.items, event.item) event.succeed() def _do_get(self, event): if len(event.indices) == 1: event.succeed([self.items.pop(event.indices[0])]) return result = [] for idx, val in enumerate(event.indices): result.append(self.items[val]) self.items[val] = self.items[-1 - idx] if len(event.indices) > 0: del self.items[-len(event.indices):] self.items.sort() event.succeed(result)
class Store(base.BaseResource): """Resource with *capacity* slots for storing arbitrary objects. By default, the *capacity* is unlimited and objects are put and retrieved from the store in a first-in first-out order. The *env* parameter is the :class:`~simpy.core.Environment` instance the container is bound to. """ def __init__(self, env, capacity=float('inf')): if capacity <= 0: raise ValueError('"capacity" must be > 0.') super(Store, self).__init__(env, capacity) self.items = [] """List of the items available in the store.""" put = BoundClass(StorePut) """Request to put *item* into the store.""" get = BoundClass(StoreGet) """Request to get an *item* out of the store.""" def _do_put(self, event): if len(self.items) < self._capacity: self.items.append(event.item) event.succeed() def _do_get(self, event): if self.items: event.succeed(self.items.pop(0))
class FilterStore(Store): """Resource with *capacity* slots for storing arbitrary objects supporting filtered get requests. Like the :class:`Store`, the *capacity* is unlimited by default and objects are put and retrieved from the store in a first-in first-out order. Get requests can be customized with a filter function to only trigger for items for which said filter function returns ``True``. .. note:: In contrast to :class:`Store`, get requests of a :class:`FilterStore` won't necessarily be triggered in the same order they were issued. *Example:* The store is empty. *Process 1* tries to get an item of type *a*, *Process 2* an item of type *b*. Another process puts one item of type *b* into the store. Though *Process 2* made his request after *Process 1*, it will receive that new item because *Process 1* doesn't want it. """ put = BoundClass(StorePut) """Request a to put *item* into the store.""" get = BoundClass(FilterStoreGet) """Request a to get an *item*, for which *filter* returns ``True``, out of the store.""" def _do_get(self, event): for item in self.items: if event.filter(item): self.items.remove(item) event.succeed(item) break return True
class Resource(base.BaseResource): """Resource with *capacity* of usage slots that can be requested by processes. If all slots are taken, requests are enqueued. Once a usage request is released, a pending request will be triggered. The *env* parameter is the :class:`~simpy.core.Environment` instance the resource is bound to. """ def __init__(self, env: Environment, capacity: int = 1): if capacity <= 0: raise ValueError('"capacity" must be > 0.') super().__init__(env, capacity) self.users: List[Request] = [] """List of :class:`Request` events for the processes that are currently using the resource.""" self.queue = self.put_queue """Queue of pending :class:`Request` events. Alias of :attr:`~simpy.resources.base.BaseResource.put_queue`. """ @property def count(self) -> int: """Number of users currently using the resource.""" return len(self.users) if TYPE_CHECKING: def request(self) -> Request: """Request a usage slot.""" return Request(self) def release(self, request: Request) -> Release: """Release a usage slot.""" return Release(self, request) else: request = BoundClass(Request) release = BoundClass(Release) def _do_put(self, event: Request) -> None: if len(self.users) < self.capacity: self.users.append(event) event.usage_since = self._env.now event.succeed() def _do_get(self, event: Release) -> None: try: self.users.remove(event.request) # type: ignore except ValueError: pass event.succeed()
class Container(base.BaseResource): """Models the production and consumption of a homogeneous, undifferentiated bulk. It may either be continuous (like water) or discrete (like apples). The *env* parameter is the :class:`~simpy.core.Environment` instance the container is bound to. The *capacity* defines the size of the container and must be a positive number (> 0). By default, a container is of unlimited size. You can specify the initial level of the container via *init*. It must be >= 0 and is 0 by default. Raise a :exc:`ValueError` if ``capacity <= 0``, ``init < 0`` or ``init > capacity``. """ def __init__(self, env, capacity=float('inf'), init=0): super(Container, self).__init__(env) if capacity <= 0: raise ValueError('"capacity" must be > 0.') if init < 0: raise ValueError('"init" must be >= 0.') if init > capacity: raise ValueError('"init" must be <= "capacity".') self._capacity = capacity self._level = init @property def capacity(self): """The maximum capacity of the container.""" return self._capacity @property def level(self): """The current level of the container (a number between ``0`` and ``capacity``). """ return self._level put = BoundClass(ContainerPut) """Creates a new :class:`ContainerPut` event.""" get = BoundClass(ContainerGet) """Creates a new :class:`ContainerGet` event.""" def _do_put(self, event): if self._capacity - self._level >= event.amount: self._level += event.amount event.succeed() def _do_get(self, event): if self._level >= event.amount: self._level -= event.amount event.succeed()
def __init__(self, env): self._env = env self.put_queue = self.PutQueue() """Queue/list of events waiting to get something out of the resource. """ self.get_queue = self.GetQueue() """Queue/list of events waiting to put something into the resource.""" # Bind event constructors as methods BoundClass.bind_early(self)
def __init__(self, env, capacity): self._env = env self._capacity = capacity self.put_queue = self.PutQueue() """Queue of pending *put* requests.""" self.get_queue = self.GetQueue() """Queue of pending *get* requests.""" # Bind event constructors as methods BoundClass.bind_early(self)
def __init__(self, fds=None): if fds is None: fds = {} self._queue = [] """A list with all currently scheduled events.""" self._eid = count() self._active_proc = None self.fds = fds BoundClass.bind_early(self)
def __init__(self, env, capacity=float('inf')): # BaseResource self._env = env self._capacity = capacity self.lock = threading.RLock() self.put_queue = SynchronizedList(self.lock) self.get_queue = SynchronizedList(self.lock) BoundClass.bind_early(self) self.callbacks = [] # Store self.items = []
def __init__(self, env, capacity=float('inf'), hard_cap=False, items=()): self.env = env #: Capacity of the queue (maximum number of items). self.capacity = capacity self._hard_cap = hard_cap self.items = list(items) self._putters = [] self._getters = [] self._any_waiters = [] self._full_waiters = [] self._put_hook = None self._get_hook = None BoundClass.bind_early(self)
class Resource(base.BaseResource): """A resource has a limited number of slots that can be requested by a process. If all slots are taken, requesters are put into a queue. If a process releases a slot, the next process is popped from the queue and gets one slot. The *env* parameter is the :class:`~simpy.core.Environment` instance the resource is bound to. The *capacity* defines the number of slots and must be a positive integer. """ def __init__(self, env, capacity=1): super(Resource, self).__init__(env) self._capacity = capacity self.users = [] """List of :class:`Request` events for the processes that are currently using the resource.""" self.queue = self.put_queue """Queue/list of pending :class:`Request` events that represent processes waiting to use the resource.""" @property def capacity(self): """Maximum capacity of the resource.""" return self._capacity @property def count(self): """Number of users currently using the resource.""" return len(self.users) request = BoundClass(Request) """Create a new :class:`Request` event.""" release = BoundClass(Release) """Create a new :class:`Release` event.""" def _do_put(self, event): if len(self.users) < self.capacity: self.users.append(event) event.succeed() def _do_get(self, event): try: self.users.remove(event.request) except ValueError: pass event.succeed()
class Container(base.BaseResource): """Resource containing up to *capacity* of matter which may either be continuous (like water) or discrete (like apples). It supports requests to put or get matter into/from the container. The *env* parameter is the :class:`~simpy.core.Environment` instance the container is bound to. The *capacity* defines the size of the container. By default, a container is of unlimited size. The initial amount of matter is specified by *init* and defaults to ``0``. Raise a :exc:`ValueError` if ``capacity <= 0``, ``init < 0`` or ``init > capacity``. """ def __init__(self, env, capacity=float('inf'), init=0): if capacity <= 0: raise ValueError('"capacity" must be > 0.') if init < 0: raise ValueError('"init" must be >= 0.') if init > capacity: raise ValueError('"init" must be <= "capacity".') super(Container, self).__init__(env, capacity) self._level = init @property def level(self): """The current amount of the matter in the container.""" return self._level put = BoundClass(ContainerPut) """Request to put *amount* of matter into the container.""" get = BoundClass(ContainerGet) """Request to get *amount* of matter out of the container.""" def _do_put(self, event): if self._capacity - self._level >= event.amount: self._level += event.amount event.succeed() return True def _do_get(self, event): if self._level >= event.amount: self._level -= event.amount event.succeed() return True
class StampedStore(base.BaseResource): """Models the production and consumption of concrete Python objects. Items put into the store can be of any type. By default, they are put and retrieved from the store in a first-in first-out order. The *env* parameter is the :class:`~simpy.core.Environment` instance the container is bound to. The *capacity* defines the size of the Store and must be a positive number (> 0). By default, a Store is of unlimited size. A :exc:`ValueError` is raised if the value is negative. """ def __init__(self, env, capacity=float('inf')): super(StampedStore, self).__init__(env, capacity=float('inf')) if capacity <= 0: raise ValueError('"capacity" must be > 0.') self._capacity = capacity self.items = [] # we are keeping items sorted by stamp self.event_count = 0 # Used to break ties with python heap implementation # See: https://docs.python.org/3/library/heapq.html?highlight=heappush#priority-queue-implementation-notes """List of the items within the store.""" @property def capacity(self): """The maximum capacity of the store.""" return self._capacity put = BoundClass(StampedStorePut) """Create a new :class:`StorePut` event.""" get = BoundClass(StampedStoreGet) """Create a new :class:`StoreGet` event.""" # We assume the item is a tuple: (stamp, packet). The stamp is used to # sort the packet in the heap. def _do_put(self, event): self.event_count += 1 # Needed this to break heap ties if len(self.items) < self._capacity: heappush(self.items, [event.item[0], self.event_count, event.item[1]]) event.succeed() # When we return an item from the stamped store we do not # return the stamp but only the content portion. def _do_get(self, event): if self.items: event.succeed(heappop(self.items)[2])
def __init__(self, env, capacity=float('inf'), hard_cap=False, items=(), name=None): self.env = env #: Capacity of the queue (maximum number of items). self.capacity = capacity self._hard_cap = hard_cap self.items = list(items) self.name = name self._put_waiters = [] self._get_waiters = [] self._at_most_waiters = [] self._at_least_waiters = [] self._put_hook = None self._get_hook = None BoundClass.bind_early(self)
class PriorityPool(Pool): """Pool with prioritizied put() and get() requests. A priority is provided with `put()` and `get()` requests. This priority determines the strict order in which requests are fulfilled. Requests of the same priority are serviced in strict FIFO order. """ def __init__(self, env, capacity=float('inf'), init=0, hard_cap=False, name=None): super(PriorityPool, self).__init__(env, capacity, init, hard_cap, name) self._event_count = 0 #: Put amount in the pool. put = BoundClass(PriorityPoolPutEvent) #: Get amount from the pool. get = BoundClass(PriorityPoolGetEvent) def _trigger_put(self, _=None): while self._put_waiters: put_ev = self._put_waiters[0] if self.capacity - self.level >= put_ev.amount: heapq.heappop(self._put_waiters) self.level += put_ev.amount put_ev.succeed() if self._put_hook: self._put_hook() elif self._hard_cap: raise OverflowError() else: break def _trigger_get(self, _=None): while self._get_waiters: get_ev = self._get_waiters[0] if get_ev.amount <= self.level: heapq.heappop(self._get_waiters) self.level -= get_ev.amount get_ev.succeed(get_ev.amount) if self._get_hook: self._get_hook() else: break
def __init__(self, env, capacity=float('inf'), init=0, hard_cap=False, name=None): self.env = env #: Capacity of the queue (maximum number of items). self.capacity = capacity self._hard_cap = hard_cap self.level = init self.name = name self._putters = [] self._getters = [] self._new_waiters = [] self._any_waiters = [] self._full_waiters = [] self._put_hook = None self._get_hook = None BoundClass.bind_early(self)
class DPStore(store.Store): if TYPE_CHECKING: def get( self, epsilon: float, block_nr: int ) -> DPStoreGet: """Request to get an *item*, for which *filter* returns ``True``, out of the store.""" return DPStoreGet(self, epsilon, block_nr ) else: get = BoundClass(DPStoreGet) def _do_get( # type: ignore[override] # noqa: F821 self, event: DPStoreGet ) -> Optional[bool]: if len(self.items) >= event.block_nr: return_blocks = [] for i in random.sample(range(len(self.items)), k=event.block_nr): block = self.items[i] if block["epsilon"] - event.epsilon <0: block["epsilon"] = -1 else: block["epsilon"] = block["epsilon"] - event.epsilon return_blocks.append(block) event.succeed(return_blocks) return True
class ReadableFilterStore(FilterStore): """Extends simpy.resources.store.FilterStore with a non-destructive read() method.""" get = BoundClass(FilterStoreGetWithRemove) read = BoundClass(FilterStoreGetWithNoRemove) def _do_get(self, event): for item in self.items: if event.filter(item): if event.remove_item: self.items.remove(item) event.succeed(item) break return True
def __init__(self, env, capacity=float('inf'), init=0, hard_cap=False, name=None): self.env = env #: Capacity of the pool (maximum level). self.capacity = capacity #: Current fill level of the pool. self.level = init self._hard_cap = hard_cap self.name = name self._put_waiters = [] self._get_waiters = [] self._at_most_waiters = [] self._at_least_waiters = [] self._put_hook = None self._get_hook = None BoundClass.bind_early(self)
class FilterQueue(Queue[ItemType]): """Specialized queue where items are dequeued in priority order. Items in `PriorityQueue` must be orderable (implement :meth:`~object.__lt__`). Unorderable items may be used with `PriorityQueue` by wrapping with :class:`~PriorityItem`. Items that evaluate less-than other items will be dequeued first. """ if TYPE_CHECKING: def get(self,filter: Callable[[Any], bool] = lambda item: True) -> FilterQueueGet: """Dequeue an item from the queue.""" ... else: get = BoundClass(FilterQueueGet) def _trigger_get(self, _: Optional[Event] = None,*args,**kwargs) -> None: idx = 0 while self._get_waiters and idx < len(self._get_waiters): get_ev = self._get_waiters[idx] for i,item in enumerate(self.items): if get_ev.filter(item): self.items.pop(i) self._get_waiters.pop(idx) get_ev.succeed(item,*args,**kwargs) if self._get_hook: self._get_hook() break else: idx +=1
class Pkt(simpy.resources.store.Store): get = BoundClass(PktRX) def _do_get(self, event): if self.items: event.succeed(self.items) self.items = []
class ModifiedContainer(simpy.Container): def __init__(self, env, capacity, init, name, owner, resource): super().__init__(env,capacity, init) self.name = name self.owner = owner self.resource = resource if TYPE_CHECKING: def put( # type: ignore[override] # noqa: F821 self, amount: ContainerAmount, name: str, owner, resource: str ) -> ModifiedContainerPut: """Request to put *amount* of matter into the container.""" return ModifiedContainerPut(self, amount, self.name, self.owner, self.resource) def get( # type: ignore[override] # noqa: F821 self, amount: ContainerAmount, name: str, owner, resource: str ) -> ModifiedContainerGet: """Request to get *amount* of matter out of the container.""" return ModifiedContainerGet(self, amount, self.name, self.owner, self.resource) else: put = BoundClass(ModifiedContainerPut) get = BoundClass(ModifiedContainerGet) def _do_put(self, event: ModifiedContainerPut) -> Optional[bool]: if self._capacity - self._level >= event.amount: self._level += event.amount event.succeed() evnt_logger.info(f"\t {self.name} of {self.owner}'s level increased by {event.amount} {self.resource}'",extra={"sim_time":self._env.now}) return True else: evnt_logger.info(f"\t {self.name} of {self.owner}'s put failed.'",extra={"sim_time":self._env.now}) return None def _do_get(self, event: ModifiedContainerGet) -> Optional[bool]: if self._level >= event.amount: self._level -= event.amount event.succeed() evnt_logger.info(f"\t {self.name} of {self.owner}'s level decreased by {event.amount} {self.resource}'",extra={"sim_time":self._env.now}) return True else: evnt_logger.info(f"\t {self.name} of {self.owner}'s put failed.'",extra={"sim_time":self._env.now}) return None
class Store(base.BaseResource): """Resource with *capacity* slots for storing arbitrary objects. By default, the *capacity* is unlimited and objects are put and retrieved from the store in a first-in first-out order. The *env* parameter is the :class:`~simpy.core.Environment` instance the container is bound to. """ def __init__(self, env: Environment, capacity: Union[float, int] = float('inf')): if capacity <= 0: raise ValueError('"capacity" must be > 0.') super().__init__(env, capacity) self.items: List[Any] = [] """List of the items available in the store.""" if TYPE_CHECKING: def put( # type: ignore[override] # noqa: F821 self, item: Any) -> StorePut: """Request to put *item* into the store.""" return StorePut(self, item) def get(self) -> StoreGet: # type: ignore[override] # noqa: F821 """Request to get an *item* out of the store.""" return StoreGet(self) else: put = BoundClass(StorePut) get = BoundClass(StoreGet) def _do_put(self, event: StorePut) -> Optional[bool]: if len(self.items) < self._capacity: self.items.append(event.item) event.succeed() return None def _do_get(self, event: StoreGet) -> Optional[bool]: if self.items: event.succeed(self.items.pop(0)) return None
class Store(base.BaseResource): """Models the production and consumption of concrete Python objects. Items put into the store can be of any type. By default, they are put and retrieved from the store in a first-in first-out order. The *env* parameter is the :class:`~simpy.core.Environment` instance the container is bound to. The *capacity* defines the size of the Store and must be a positive number (> 0). By default, a Store is of unlimited size. A :exc:`ValueError` is raised if the value is negative. """ def __init__(self, env, capacity=float('inf')): super(Store, self).__init__(env) if capacity <= 0: raise ValueError('"capacity" must be > 0.') self._capacity = capacity self.items = [] """List of the items within the store.""" @property def capacity(self): """The maximum capacity of the store.""" return self._capacity put = BoundClass(StorePut) """Create a new :class:`StorePut` event.""" get = BoundClass(StoreGet) """Create a new :class:`StoreGet` event.""" def _do_put(self, event): if len(self.items) < self._capacity: self.items.append(event.item) event.succeed() def _do_get(self, event): if self.items: event.succeed(self.items.pop(0))
def __init__( self, env: Environment, capacity: Union[int, float] = float('inf'), hard_cap: bool = False, items: Iterable[ItemType] = (), name: Optional[str] = None, ) -> None: self.env = env #: Capacity of the queue (maximum number of items). self.capacity = capacity self._hard_cap = hard_cap self.items: List[ItemType] = list(items) self.name = name self._put_waiters: List[QueuePutEvent] = [] self._get_waiters: List[QueueGetEvent] = [] self._at_most_waiters: List[QueueWhenAtMostEvent] = [] self._at_least_waiters: List[QueueWhenAtLeastEvent] = [] self._put_hook: Optional[Callable[[], Any]] = None self._get_hook: Optional[Callable[[], Any]] = None BoundClass.bind_early(self)
class PriorityResource(Resource): """A :class:`~simpy.resources.resource.Resource` supporting prioritized requests. Pending requests in the :attr:`~Resource.queue` are sorted in ascending order by their *priority* (that means lower values are more important). """ PutQueue = SortedQueue """Type of the put queue. See :attr:`~simpy.resources.base.BaseResource.put_queue` for details.""" GetQueue = list """Type of the get queue. See :attr:`~simpy.resources.base.BaseResource.get_queue` for details.""" def __init__(self, env, capacity=1): super(PriorityResource, self).__init__(env, capacity) request = BoundClass(PriorityRequest) """Request a usage slot with the given *priority*.""" release = BoundClass(Release) """Release a usage slot."""
def __init__( self, env: Environment, capacity: PoolAmount = float('inf'), init: PoolAmount = 0, hard_cap: bool = False, name: Optional[str] = None, ): self.env = env #: Capacity of the pool (maximum level). self.capacity = capacity #: Current fill level of the pool. self.level = init self._hard_cap = hard_cap self.name = name self._put_waiters: List[PoolPutEvent] = [] self._get_waiters: List[PoolGetEvent] = [] self._at_most_waiters: List[PoolWhenAtMostEvent] = [] self._at_least_waiters: List[PoolWhenAtLeastEvent] = [] self._put_hook: Optional[Callable[[], Any]] = None self._get_hook: Optional[Callable[[], Any]] = None BoundClass.bind_early(self)
class PriorityResource(Resource): """A :class:`~simpy.resources.resource.Resource` supporting prioritized requests. Pending requests in the :attr:`~Resource.queue` are sorted in ascending order by their *priority* (that means lower values are more important). """ PutQueue = SortedQueue """Type of the put queue. See :attr:`~simpy.resources.base.BaseResource.put_queue` for details.""" GetQueue = list """Type of the get queue. See :attr:`~simpy.resources.base.BaseResource.get_queue` for details.""" def __init__(self, env: Environment, capacity: int = 1): super().__init__(env, capacity) if TYPE_CHECKING: def request( self, priority: int = 0, preempt: bool = True ) -> PriorityRequest: """Request a usage slot with the given *priority*.""" return PriorityRequest(self, priority, preempt) def release( # type: ignore[override] # noqa: F821 self, request: PriorityRequest ) -> Release: """Release a usage slot.""" return Release(self, request) else: request = BoundClass(PriorityRequest) release = BoundClass(Release)
class Pkt(simpy.resources.store.Store): get = BoundClass(PktRX) def _do_get (self, event): if self.items: # if no limit - fetch all if event.limit is None: event.succeed(self.items) self.items = [] else: # if limit was set - slice the list event.succeed(self.items[:event.limit]) self.items = self.items[event.limit:]
class FilterStore(Store): """Resource with *capacity* slots for storing arbitrary objects supporting filtered get requests. Like the :class:`Store`, the *capacity* is unlimited by default and objects are put and retrieved from the store in a first-in first-out order. Get requests can be customized with a filter function to only trigger for items for which said filter function returns ``True``. .. note:: In contrast to :class:`Store`, get requests of a :class:`FilterStore` won't necessarily be triggered in the same order they were issued. *Example:* The store is empty. *Process 1* tries to get an item of type *a*, *Process 2* an item of type *b*. Another process puts one item of type *b* into the store. Though *Process 2* made his request after *Process 1*, it will receive that new item because *Process 1* doesn't want it. """ if TYPE_CHECKING: def get( self, filter: Callable[[Any], bool] = lambda item: True) -> FilterStoreGet: """Request to get an *item*, for which *filter* returns ``True``, out of the store.""" return FilterStoreGet(self, filter) else: get = BoundClass(FilterStoreGet) def _do_get( # type: ignore[override] # noqa: F821 self, event: FilterStoreGet) -> Optional[bool]: for item in self.items: if event.filter(item): self.items.remove(item) event.succeed(item) break return True
class PriorityResource(Resource): """This class works like :class:`Resource`, but requests are sorted by priority. The :attr:`~Resource.queue` is kept sorted by priority in ascending order (a lower value for *priority* results in a higher priority), so more important request will get the resource earlier. """ PutQueue = SortedQueue """The type to be used for the :attr:`~simpy.resources.base.BaseResource.put_queue`.""" GetQueue = list """The type to be used for the :attr:`~simpy.resources.base.BaseResource.get_queue`.""" def __init__(self, env, capacity=1): super(PriorityResource, self).__init__(env, capacity) request = BoundClass(PriorityRequest) """Create a new :class:`PriorityRequest` event."""