I am trying to simulate customers going to a bank. And I want to print the average length of the queue, and each value pair of time a request begins and the pairs [time of request or release, length of queue]
I am aware that there is no monitor class in simpy3, so I tried using the example here Below I have left the relevant code from the file I'm converting from and my attempt to convert it.
simpy2 and python2
from SimPy.Simulation import *
import random as rnd
interarrival_time = 10.0
service_time = 8.0
class CustomerGenerator( Process ):
def produce( self, b ):
while True:
c = Customer( b )
c.start( c.doit() )
yield hold, self, rnd.expovariate(1.0/interarrival_time)
class Customer( Process ):
def __init__( self, resource ):
Process.__init__( self )
self.bank = resource
def doit( self ):
yield request, self, self.bank
yield hold, self, self.bank.servicetime()
yield release, self, self.bank
class Bank( Resource ):
def servicetime( self ):
return rnd.expovariate(1.0/service_time)
initialize()
bank = Bank( capacity=1, monitored=True, monitorType=Monitor )
src = CustomerGenerator()
activate( src, src.produce( bank ) )
simulate( until=100)
print bank.waitMon.mean()
print
for evt in bank.waitMon:
print evt[0], evt[1]
simpy3 and python3
from simpy import *
import random as rnd
interarrival_time = 10.0
service_time = 8.0
def produce( env, b ):
while True:
customer(env,b)
yield env.timeout(rnd.expovariate(1.0/interarrival_time))
def customer(env,bank):
yield bank.request()
yield env.timeout(bank.servicetime())
bank.release()
class MonitoredResource(Resource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.data = []
self.num_requests = 0
def mean(self):
try:
mean = 0
for el in self.data:
mean += el[1]
mean /= self.num_requests
return mean
except ZeroDivisionError:
pass
def request(self, *args, **kwargs):
self.data.append((self._env.now, len(self.queue)))
self.num_requests += 1
return super().request(*args, **kwargs)
def release(self, *args, **kwargs):
self.data.append((self._env.now, len(self.queue)))
return super().release(*args, **kwargs)
class Bank(MonitoredResource):
def servicetime( self ):
return rnd.expovariate(1.0/service_time)
env = Environment()
bank = Bank(env,capacity=1)
src = env.process(produce(env,bank))
env.run( until=100)
print(bank.mean())
print()
for evt in bank.data:
print(evt[0], evt[1])
How can I get the output of my SimPy3 simulator to resemble the SimPy2 simulator instead of just printing None?
It seems I just needed to tell release() which request() was being released to get the desired output. And to run Customer as a process.
from simpy import *
import random as rnd
interarrival_time = 10.0
service_time = 8.0
def produce( env, bank ):
print("generating")
while True:
env.process(customer(env,bank))
t = rnd.expovariate(1.0/interarrival_time)
yield env.timeout(t)
def customer(env,bank):
req = bank.request()
yield req
yield env.timeout(bank.servicetime())
bank.release(req)
class MonitoredResource(Resource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.data = []
self.num_requests = 0
def mean(self):
if(self.num_requests > 0):
mean = 0
for el in self.data:
mean += el[1]
mean /= self.num_requests
return mean
def request(self, *args, **kwargs):
self.data.append((self._env.now, len(self.queue)))
self.num_requests += 1
return super().request(*args, **kwargs)
def release(self, *args, **kwargs):
self.data.append((self._env.now, len(self.queue)))
return super().release(*args, **kwargs)
class Bank(MonitoredResource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def servicetime( self ):
return rnd.expovariate(1.0/service_time)
env = Environment()
bank = Bank(env,capacity=1)
#bank = Bank( capacity=1, monitored=True, monitorType=Monitor )
src = env.process(produce(env,bank))
env.run(until=100)
print(bank.mean())
print()
for evt in bank.data:
print(evt[0], evt[1])
Related
I want to print the elapsed time of a method
class MyObject:
def __init__(self, verbose):
self._verbose = verbose
def func(self):
start = time.time()
...
table = ...
...
end = time.time()
if self._verbose:
print("the {} {}(s) in {:.3f}".format(len(table), table.name, end - start))
recently I discover design pattern and change this code to avoid repetitive complexity when printing elapsed time for multiple methods and function
TIME_LOGGER = None
class TimeLogger:
def __init__(self, verbose=True):
self._verbose = verbose
self._format = []
def __call__(self, text):
def inner(func):
def wrapper(*args, **kwargs):
if self._verbose:
start = time.time()
result = func(*args, **kwargs)
end = time.time()
format = [(end - start) if f is TIME_LOGGER else f for f in self._format]
print(text.format(*format))
self.set_format([])
return result
return func(*args, **kwargs)
return wrapper
return inner
def set_format(self, format):
self._format = format
class MyObject:
def __init__(self, verbose):
TIME_LOGGER(verbose)
#TIME_LOGGER("the {} {}(s) in {:.3f}")
def func(self):
...
table = ...
...
TIME_LOGGER.set_format([len(table), table.name, TIME_LOGGER])
getting a global var TIME_LOGGER sound anti-pattern
After watching https://www.youtube.com/watch?v=zi0rHwfiX1Q I tried to port the example from C (implementation) and Erlang (testing) to Python and Hypothesis. Given this implementation (the rem function emulates %'s C behavior):
import math
def rem(x, y):
res = x % y
return int(math.copysign(res,x))
class Queue:
def __init__(self, capacity: int):
self.capacity = capacity + 1
self.data = [None] * self.capacity
self.inp = 0
self.outp = 0
def put(self, n: int):
self.data[self.inp] = n
self.inp = (self.inp + 1) % self.capacity
def get(self):
ans = self.data[self.outp]
self.outp = (self.outp + 1) % self.capacity
return ans
def size(self):
return rem((self.inp - self.outp), self.capacity)
and this test code
import unittest
from hypothesis.stateful import rule, precondition, RuleBasedStateMachine
from hypothesis.strategies import integers
from myqueue import Queue
class QueueMachine(RuleBasedStateMachine):
cap = 1
def __init__(self):
super().__init__()
self.queue = Queue(self.cap)
self.model = []
#rule(value=integers())
#precondition(lambda self: len(self.model) < self.cap)
def put(self, value):
self.queue.put(value)
self.model.append(value)
#rule()
def size(self):
expected = len(self.model)
actual = self.queue.size()
assert actual == expected
#rule()
#precondition(lambda self: self.model)
def get(self):
actual = self.queue.get()
expected = self.model[0]
self.model = self.model[1:]
assert actual == expected
TestQueue = QueueMachine.TestCase
if __name__ == "__main__":
unittest.main()
The actual question is how to use Hypothesis to also parametrize over QueueMachine.cap instead of setting it manually in the test class.
You can set self.queue in an initialize method rather than __init__, using a suitable integers strategy for the capacity.
I'm modifying the script from https://github.com/NVIDIA-AI-IOT/jetbot/blob/master/jetbot/robot.py removing the trailets library as its not needed for my purpose. I'm now receiving the error :
Traceback (most recent call last):
File "ak_test.py", line 4, in <module>
robot = Robot()
File "/home/jetbot/jetbot/jetbot/robot.py", line 20, in __init__
self.left_motor = Motor(self.motor_driver, channel=self.left_motor_channel, alpha=self.left_motor_alpha)
File "/home/jetbot/jetbot/jetbot/motor.py", line 14, in __init__
super(Motor, self).__init__(*args, **kwargs) # initializes traitlets
TypeError: object.__init__() takes no parameters
I've coppied the relevent bits of the code below that will replicate the issue:
import time
from Adafruit_MotorHAT import Adafruit_MotorHAT
class Motor():
value = float()
# config
alpha = float(1.0)
beta = float(0.0)
def __init__(self, driver, channel, *args, **kwargs):
super(Motor, self).__init__(*args, **kwargs) # initializes traitlets
self._driver = driver
self._motor = self._driver.getMotor(channel)
if(channel == 1):
self._ina = 1
self._inb = 0
else:
self._ina = 2
self._inb = 3
atexit.register(self._release)
def _observe_value(self, change):
self._write_value(change['new'])
def _write_value(self, value):
"""Sets motor value between [-1, 1]"""
mapped_value = int(255.0 * (self.alpha * value + self.beta))
speed = min(max(abs(mapped_value), 0), 255)
self._motor.setSpeed(speed)
if mapped_value < 0:
self._motor.run(Adafruit_MotorHAT.FORWARD)
# The two lines below are required for the Waveshare JetBot Board only
self._driver._pwm.setPWM(self._ina,0,0)
self._driver._pwm.setPWM(self._inb,0,speed*16)
else:
self._motor.run(Adafruit_MotorHAT.BACKWARD)
# The two lines below are required for the Waveshare JetBot Board only
self._driver._pwm.setPWM(self._ina,0,speed*16)
self._driver._pwm.setPWM(self._inb,0,0)
def _release(self):
"""Stops motor by releasing control"""
self._motor.run(Adafruit_MotorHAT.RELEASE)
# The two lines below are required for the Waveshare JetBot Board only
self._driver._pwm.setPWM(self._ina,0,0)
self._driver._pwm.setPWM(self._inb,0,0)
class Robot():
left_motor = Motor
right_motor = Motor
i2c_bus = 1
left_motor_channel = 1
left_motor_alpha = 1.0
right_motor_channel = 2
right_motor_alpha = 1.0
def __init__(self, *args, **kwargs):
super(Robot, self).__init__(*args, **kwargs)
self.motor_driver = Adafruit_MotorHAT(i2c_bus=self.i2c_bus)
self.left_motor = Motor(self.motor_driver, channel=self.left_motor_channel, alpha=self.left_motor_alpha)
self.right_motor = Motor(self.motor_driver, channel=self.right_motor_channel, alpha=self.right_motor_alpha)
def set_motors(self, left_speed, right_speed):
self.left_motor.value = left_speed
self.right_motor.value = right_speed
def forward(self, speed=1.0, duration=None):
self.left_motor.value = speed
self.right_motor.value = speed
def backward(self, speed=1.0):
self.left_motor.value = -speed
self.right_motor.value = -speed
def left(self, speed=1.0):
self.left_motor.value = -speed
self.right_motor.value = speed
def right(self, speed=1.0):
self.left_motor.value = speed
self.right_motor.value = -speed
def stop(self):
self.left_motor.value = 0
self.right_motor.value = 0
robot = Robot()
When you call super().__init__(*args, **kwargs), you are passing all unnamed parameters to the parent class's __init__. In your case, the parent class is object which doesn't take any parameters.
In the Robot.__init__ you pass alpha as a keyword argument to Motor.__init__, which gets bound to kwargs, hence your error.
To fix this, you need to deal with alpha in Robot.__init__:
class Motor:
def __init__(self, driver, channel, *args, alpha=None, **kwargs):
super(Motor, self).__init__(*args, **kwargs)
...
I'm working through this book and am trying to extend its code to make a half adder and eventually a full adder.
I think I should be implementing a way to return the carry and bits from the half adder separately to facilitate connection to later gates. However, that's where I'm blanking. I also need some way to call performGateLogic on the HalfAdder and 'pipe' the inputs to both the XorGate and AndGate and have tried so with Connectors. My brain is just being tied in a pretzel over the method calling and class relations here and I'd appreciate someone straightening it out.
class LogicGate:
def __init__(self,n):
self.name = n
self.output = None
def getName(self):
return self.name
def getOutput(self):
self.output = self.performGateLogic()
return self.output
class BinaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pinA = None
self.pinB = None
def getPinA(self):
if self.pinA == None:
return int(input("Enter Pin A input for gate "+self.getName()+"-->"))
else:
return self.pinA.getFrom().getOutput()
def getPinB(self):
if self.pinB == None:
return int(input("Enter Pin B input for gate "+self.getName()+"-->"))
else:
return self.pinB.getFrom().getOutput()
def setNextPin(self,source):
if self.pinA == None:
self.pinA = source
else:
if self.pinB == None:
self.pinB = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class AndGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
class XorGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if (a ==1 and b==0) or (a==0 and b==1):
return 1
else:
return 0
class Connector:
def __init__(self, fgate, tgate):
self.fromgate = fgate
self.togate = tgate
tgate.setNextPin(self)
def getFrom(self):
return self.fromgate
def getTo(self):
return self.togate
class HalfAdder(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
self.bits, self.carry = None, None
self.a, self.b = None, None
self.xor = XorGate('XO')
self.and_ = AndGate('A')
self.c1 = Connector(self.xor.getPinA(), self.and_.getPinA())
self.c2 = Connector(self.xor.getPinB(), self.and_.getPinB())
def performGateLogic(self):
self.a = self.getPinA()
self.b = self.getPinB()
self.bits = 1 if (self.a ==1 and self.b==0) or (self.a==0 and self.b==1) else 0
self.carry = 1 if (self.a==1 and self.b ==1) else 0
return self.bits + self.carry
Running print(HalfAdder('HA').getOutput()) yields
File "./circuits.py", line 178, in __init__
self.c1 = Connector(self.xor.getPinA(), self.and_.getPinA())
File "./circuits.py", line 161, in __init__
tgate.setNextPin(self)
AttributeError: 'int' object has no attribute 'setNextPin'
Something is being set to an int but where, I can't find.
I'm using python3 and when trying to run the following code, I'm facing the error:
NameError: name '_length' is not defined
The code itself:
class OFPHELLO(GenericStruct):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._length = self.get_size()
_msg_type = OFPType.OFPT_HELLO
_build_order = ('header', 'x')
header = OFPHeader(type = _msg_type, length = _length)
x = UBInt8()
The problem is the _length variable that I'm passing in OFPHeader, the value of which is computed in GenericStruct. How can I compute the _length variable inside the OFPHELLO class and use it as parameter in the OFPHeader class?
Following the GenericStruct code:
class GenericStruct(object):
def __init__(self, **kwargs):
for a in kwargs:
try:
field = getattr(self, a)
field.value = kwargs[a]
except AttributeError:
raise OFPException("Attribute error: %s" % a)
def build(self):
hexa = ""
for field in self._build_order:
hexa += getattr(self, field).build()
return hexa
def parse(self, buff):
begin = 0
for field in self._build_order:
size = getattr(self, field).get_size()
getattr(self,field).parse(buff, offset=begin)
begin += size
def get_size(self):
tot = 0
for field in self._build_order:
tot += getattr(self, field).get_size()
return tot
- how have you defined (GenericStruct)
- header = OFPHeader(type = _msg_type, length = _lenght)
- correct the spelling to _length
-- and please post the entire code next time