class QueueingSystem(Model): """QueueingSystem model represents a simple queue */*/1/N or */*/1, where any distribution can be used for arrival and service times. This model consists of four children: - `queue`: a model representing the packets queue (`Queue`) - `source`: a model representing the packets source (`Source`) - `server`: a model representing the packets server (`Server`) - `sink`: a model which collects served packets (`Sink`) """ def __init__(self, sim): super().__init__(sim) arrival = sim.params.arrival service = sim.params.service queue_capacity = sim.params.queue_capacity self.children['queue'] = Queue(sim, queue_capacity) self.children['source'] = Source(sim, arrival, index=0) self.children['server'] = Server(sim, service) self.children['sink'] = Sink(sim) # Building connections: self.source.connections['queue'] = self.queue self.queue.connections['server'] = self.server self.server.connections['queue'] = self.queue self.server.connections['next'] = self.sink # Statistics: self.system_size_trace = Trace() self.system_size_trace.record(self.sim.stime, 0) self.system_wait_intervals = Statistic() @property def queue(self): return self.children['queue'] @property def source(self): return self.children['source'] @property def server(self): return self.children['server'] @property def sink(self): return self.children['sink'] @property def system_size(self): return self.queue.size + (1 if self.server.busy else 0) def update_system_size(self, index=0): assert index == 0 self.system_size_trace.record(self.sim.stime, self.system_size) def add_system_wait_interval(self, value, index=0): assert index == 0 self.system_wait_intervals.append(value)
def test_lag_k_estimation_for_positive_k(): data = [-1, 0, 1, 0] * 10 st = Statistic(data) np.testing.assert_almost_equal(st.lag(0), 1) np.testing.assert_almost_equal(st.lag(1), 0) np.testing.assert_almost_equal(st.lag(2), -1) np.testing.assert_almost_equal(st.lag(3), 0)
def __init__(self, sim, service_mean): super().__init__(sim) self.__service_mean = service_mean self.__busy = False # Statistics: self.delays = Statistic() self.busy_trace = Trace() self.busy_trace.record(self.sim.stime, 0)
def test_statistic_can_be_created_from_list(): data = [1, 2, 3] st = Statistic(data) assert len(st) == 3 assert not st.empty assert st.as_list() == [1, 2, 3] assert st.as_tuple() == tuple(data) assert st.as_list() is not data # check that data was copied
def test_lag_0_is_always_1(): st1 = Statistic([10]) st2 = Statistic([-1, 1] * 5) st3 = Statistic(np.random.exponential(1, 7)) assert st1.lag(0) == 1 assert st2.lag(0) == 1 assert st3.lag(0) == 1
def test_statistic_mean(): st = Statistic() with pytest.raises(ValueError) as excinfo: st.mean() assert 'no data' in str(excinfo.value).lower() st = Statistic([1]) assert st.mean() == 1 st.extend([2, 3]) assert st.mean() == 2
def test_statistic_var(): st = Statistic() with pytest.raises(ValueError) as excinfo: st.var() assert 'no data' in str(excinfo.value).lower() st = Statistic([1]) assert st.var() == 0 st.extend([2, 3]) np.testing.assert_allclose(st.var(), 2 / 3, atol=0.001)
class Server(Model): """Server module represents a packet server with exponential service time. Connections: queue, sink Handlers: - on_service_end(): called upon service timeout Methods: - start_service(): start new packet service; generate error if busy. Statistics: - delays: `Statistic`, stores a set of service intervals - busy_trace: `Trace`, stores a vector of server busy status Parent: `QueueingSystem` """ def __init__(self, sim, service_mean): super().__init__(sim) self.__service_mean = service_mean self.__busy = False # Statistics: self.delays = Statistic() self.busy_trace = Trace() self.busy_trace.record(self.sim.stime, 0) @property def service_mean(self): return self.__service_mean @property def busy(self): return self.__busy def on_service_end(self): assert self.__busy queue = self.connections['queue'].module self.__busy = False self.busy_trace.record(self.sim.stime, 0) if queue.size > 0: queue.pop() self.start_service() self.connections['sink'].module.receive_packet() self.parent.update_system_size() def start_service(self): assert not self.__busy delay = exponential(self.service_mean) self.sim.schedule(delay, self.on_service_end) self.delays.append(delay) self.__busy = True self.busy_trace.record(self.sim.stime, 1)
def __init__(self, sim, capacity, index=0): super().__init__(sim) self.__capacity = capacity self.packets = deque() self.index = index # Statistics: self.size_trace = Trace() self.size_trace.record(self.sim.stime, 0) self.num_dropped = 0 self.arrival_intervals = Intervals() self.arrival_intervals.record(sim.stime) self.num_arrived = 0 self.wait_intervals = Statistic()
def __init__(self, sim, service_time, index=0): super().__init__(sim) self.service_time = service_time self.packet = None self.index = index # Statistics: self.service_intervals = Statistic() self.busy_trace = Trace() self.busy_trace.record(self.sim.stime, 0) self.departure_intervals = Intervals() self.departure_intervals.record(sim.stime) self.num_served = 0
def __init__(self, sim): super().__init__(sim) self.__source_delays_data = {} self.__source_delays = ReadOnlyDict(self.__source_delays_data) self.__arrival_intervals = Intervals() self.__data_size_stat = Statistic() self.__num_packets_received = 0
def test_random_source_provides_statistics(): """Validate that `RandomSource` provides statistics. """ intervals = (10, 12, 15, 17) data_size = (123, 453, 245, 321) class TestModel(Model): def __init__(self, sim): super().__init__(sim) self.source = RandomSource( sim, source_id=34, dest_addr=13, data_size=Mock(side_effect=data_size), interval=Mock(side_effect=(intervals + (1000, ))), ) self.network = DummyModel(sim, 'Network') self.source.connections['network'] = self.network ret = simulate(TestModel, stime_limit=sum(intervals)) assert ret.data.source.arrival_intervals.as_tuple() == intervals assert ret.data.source.data_size_stat.as_tuple() == data_size # Also check that we can not replace statistics: with pytest.raises(AttributeError): from pydesim import Intervals ret.data.source.arrival_intervals = Intervals() with pytest.raises(AttributeError): from pydesim import Statistic ret.data.source.data_size_stat = Statistic() # Check that source records the number of packets being sent: assert ret.data.source.num_packets_sent == 4
def handle_message(self, app_data, sender=None, connection=None): sid = app_data.source_id if sid not in self.source_delays: self.__source_delays_data[sid] = Statistic() self.source_delays[sid].append(self.sim.stime - app_data.created_at) self.arrival_intervals.record(self.sim.stime) self.data_size_stat.append(app_data.size) self.__num_packets_received += 1 self.sim.logger.debug(f'received {app_data}', src=self)
def test_statistic_moment_raises_error_when_passed_zero_negative_or_float(): st = Statistic([1, 2, 3]) with pytest.raises(ValueError) as excinfo: st.moment(0) assert 'positive integer expected' in str(excinfo.value).lower() with pytest.raises(ValueError) as excinfo: st.moment(-1) assert 'positive integer expected' in str(excinfo.value).lower() with pytest.raises(ValueError) as excinfo: st.moment(1.5) assert 'positive integer expected' in str(excinfo.value).lower()
def __init__(self, sim, arrival, index): super().__init__(sim) self.arrival = arrival self.index = index # Statistics: self.intervals = Intervals() self.num_generated = 0 self.delays = Statistic() # Initialize: self._schedule_next_arrival()
def __init__(self, sim): super().__init__(sim) arrival = sim.params.arrival service = sim.params.service queue_capacity = sim.params.queue_capacity self.children['queue'] = Queue(sim, queue_capacity) self.children['source'] = Source(sim, arrival, index=0) self.children['server'] = Server(sim, service) self.children['sink'] = Sink(sim) # Building connections: self.source.connections['queue'] = self.queue self.queue.connections['server'] = self.server self.server.connections['queue'] = self.queue self.server.connections['next'] = self.sink # Statistics: self.system_size_trace = Trace() self.system_size_trace.record(self.sim.stime, 0) self.system_wait_intervals = Statistic()
def test_lag_k_raises_error_when_passed_negative_or_float(): st = Statistic([1, 2, 3]) with pytest.raises(ValueError) as excinfo: st.lag(-1) assert 'non-negative integer expected' in str(excinfo.value).lower() with pytest.raises(ValueError) as excinfo: st.lag(2.5) assert 'non-negative integer expected' in str(excinfo.value).lower()
def test_statistic_moment_evaluation(): st = Statistic([1]) assert st.moment(1) == 1 assert st.moment(2) == 1 st.extend([2, 3, 4]) assert st.moment(1) == 2.5 assert st.moment(2) == 7.5
def test_controlled_source_provides_statistics(): """Validate that `ControlledSource` provides statistics. """ intervals = (10, 12, 15, 17) data_size = (123, 453, 245, 321) class SourceController(Model): def __init__(self, sim, src): super().__init__(sim) self.iterator = iter(intervals) self.src = src self.sim.schedule(next(self.iterator), self.handle_timeout) def handle_timeout(self): self.src.get_next() try: interval = next(self.iterator) except StopIteration: pass else: self.sim.schedule(interval, self.handle_timeout) class TestModel(Model): def __init__(self, sim): super().__init__(sim) self.source = ControlledSource( sim, source_id=34, dest_addr=13, data_size=Mock(side_effect=data_size), ) self.network = DummyModel(sim, 'Network') self.source.connections['network'] = self.network self.controller = SourceController(sim, self.source) ret = simulate(TestModel, stime_limit=sum(intervals)) assert ret.data.source.data_size_stat.as_tuple() == data_size assert ret.data.source.arrival_intervals.as_tuple() == intervals # Also check that we can not replace statistics: with pytest.raises(AttributeError): from pydesim import Intervals ret.data.source.arrival_intervals = Intervals() with pytest.raises(AttributeError): from pydesim import Statistic ret.data.source.data_size_stat = Statistic() # Check that source records the number of packets being sent: assert ret.data.source.num_packets_sent == 4
def test_lag_k_raises_error_when_k_greater_then_length(): st = Statistic([1, 2]) error_message = 'statistic has too few samples' with pytest.raises(ValueError) as excinfo1: st.lag(2) with pytest.raises(ValueError) as excinfo2: st.lag(3) assert error_message in str(excinfo1.value).lower() assert error_message in str(excinfo2.value).lower()
def __init__(self, sim, bitrate=inf, header_size=0, preamble=0, ifs=0): super().__init__(sim) self.bitrate = bitrate self.header_size = header_size self.preamble = preamble self.ifs = ifs # State variables: self.__started = False self.__tx_frame = None self.__wait_ifs = False self.__rx_frame = None # Statistics: self.__num_received_frames = 0 self.__num_received_bits = 0 self.__rx_busy_trace = Trace() self.__rx_busy_trace.record(0, 0) self.__num_transmitted_packets = 0 self.__num_transmitted_bits = 0 self.__tx_busy_trace = Trace() self.__tx_busy_trace.record(0, 0) self.__service_time = Statistic() self.__service_started_at = None # Initialization: self.sim.schedule(self.sim.stime, self.start)
def __init__(self, arrival_mean, service_mean): # Parameters: self.arrival_mean = arrival_mean self.service_mean = service_mean # System state: self.queue_size = 0 self.server_busy = False # Statistics: self.system_size_trace = Trace() self.server_busy_trace = Trace() self.service_intervals = Statistic() self.arrivals = Intervals() self.departures = Intervals()
def __init__(self, sim): super().__init__(sim) arrivals = sim.params.arrivals services = sim.params.services queue_capacity = sim.params.queue_capacity n = sim.params.num_stations active_sources = {i for i, ar in enumerate(arrivals) if ar is not None} if n < 1: raise ValueError('num_stations must be >= 1') self.queues, self.sources, self.servers = [], [], [] for i in range(n): self.queues.append(Queue(sim, queue_capacity, i)) self.servers.append(Server(sim, services[i], i)) if i in active_sources: self.sources.append(Source(sim, arrivals[i], i)) else: self.sources.append(None) self.sink = Sink(sim) self.children['queue'] = self.queues self.children['server'] = self.servers self.children['sink'] = self.sink self.children['sources'] = [src for src in self.sources if src] # Connecting modules: for i in range(n): self.queues[i].connections['server'] = self.servers[i] self.servers[i].connections['queue'] = self.queues[i] if self.sources[i]: self.sources[i].connections['queue'] = self.queues[i] if i < n - 1: self.servers[i].connections['next'] = self.queues[i + 1] else: self.servers[i].connections['next'] = self.sink # Statistics: self.system_size_trace = [Trace() for _ in range(n)] for i in range(n): self.system_size_trace[i].record(sim.stime, 0) self.system_wait_intervals = [Statistic() for _ in range(n)]
def __init__( self, sim, address=None, phy_header_size=None, mac_header_size=None, ack_size=None, bitrate=None, preamble=None, max_propagation=0, ): super().__init__(sim) # Properties: self.__address = address self.__phy_header_size = (phy_header_size if phy_header_size is not None else sim.params.phy_header_size) self.__mac_header_size = (mac_header_size if mac_header_size is not None else sim.params.mac_header_size) self.__bitrate = bitrate if bitrate is not None else sim.params.bitrate self.__preamble = (preamble if preamble is not None else sim.params.preamble) self.__max_propagation = max_propagation self.__ack_size = (ack_size if ack_size is not None else sim.params.ack_size) # State variables: self.timeout = None self.cw = 65536 self.backoff = -1 self.num_retries = None self.pdu = None self.__state = Transmitter.State.IDLE self.__seqn = 0 # Statistics: self.backoff_vector = Statistic() self.__start_service_time = None self.service_time = Statistic() self.num_sent = 0 self.num_retries_vector = Statistic() self.__busy_trace = Trace() self.__busy_trace.record(sim.stime, 0) # Initialize: sim.schedule(0, self.start)
def __init__(self, sim, data_size, source_id, dest_addr): """Constructor. :param sim: `pydesim.Simulator` object; :param data_size: callable without arguments, iterable or constant; represents application data size distribution; :param source_id: this source ID (more like IP address, not MAC) :param dest_addr: destination MAC address. """ super().__init__(sim) self.__data_size = data_size self.__source_id = source_id self.__dest_addr = dest_addr # Attempt to build iterators for data size and intervals: try: self.__data_size_iter = iter(self.__data_size) except TypeError: self.__data_size_iter = None # Statistics: self.__arrival_intervals = Intervals() self.__data_size_stat = Statistic() self.__num_packets_sent = 0
def test_statistic_moment_raises_error_when_called_for_empty_statistic(): st = Statistic() with pytest.raises(ValueError) as excinfo: st.moment(1) assert 'no data' in str(excinfo.value).lower()
def test_statistic_append_adds_values(): st = Statistic() st.append(1) st.append(20) assert st.as_tuple() == (1, 20)
def test_statistic_extend_adds_all_items(): st = Statistic([1]) st.extend([2, 3]) assert st.as_tuple() == (1, 2, 3)
def test_statistic_is_initially_empty(): st = Statistic() assert len(st) == 0 assert st.empty assert st.as_list() == [] assert st.as_tuple() == ()
def test_statistic_extend_raises_error_when_noniterable_passed(): st = Statistic() with pytest.raises(TypeError) as excinfo: st.extend(1) assert 'not iterable' in str(excinfo.value).lower()