Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import simpy
- import numpy
- import sys
- import argparse
- from decimal import *
- from random import Random, expovariate, uniform
- from datetime import datetime
- from confidence_interval import *
- # Global variables (initialized by program's options).
- TOTAL_CLIENTS = None
- TOTAL_ROUNDS = None
- ARRIVAL_RATE = None
- SERVICE_RATE = 1.0
- LOG = None
- STATISTICS = None
- TRANSIENT = None
- class Statistics(object):
- """Class designed for generate simulation's statistics."""
- all_ex1 = [] # E[X_1] for each round.
- all_ex2 = [] # E[x_2] for each round.
- all_ew1 = [] # E[W_1] for each round.
- all_ew2 = [] # E[W_2] for each round.
- all_enq1 = [] # E[N_q1] for each round.
- all_enq2 = [] # E[N_q2] for each round.
- all_ens1 = [] # E[N_s1] for each round.
- all_ens2 = [] # E[N_s2] for each round.
- w1s = [] # Time on waiting queue 1 for each cliente.
- w2s = [] # Time on waiting queue 2 for each cliente.
- x1s = [] # Time on server 1 for each cliente.
- x2s = [] # Time on server 2 for each cliente.
- area_wq1 = 0 # Area from waiting queue 1.
- area_wq2 = 0 # Area from waiting queue 2.
- area_sq1 = 0 # Area from service 1.
- area_sq2 = 0 # Area from waiting queue 2.
- total_time = 0 # Because env.now not work properly after the end.
- @staticmethod
- def discart_first(n):
- Statistics.w1s = Statistics.w1s[n:]
- Statistics.w2s = Statistics.w2s[n:]
- Statistics.x1s = Statistics.x1s[n:]
- Statistics.x2s = Statistics.x2s[n:]
- @staticmethod
- def show():
- """Print statistics of one round of the simulation."""
- print("Statistics:")
- ex1 = numpy.mean(numpy.array(Statistics.x1s))
- ex2 = numpy.mean(numpy.array(Statistics.x2s))
- ex = ex1 + ex2
- print(" E[X] = E[X_1] + E[X_2] = %lf + %lf = %lf" % (ex1, ex2, ex))
- ew1 = numpy.mean(numpy.array(Statistics.w1s))
- ew2 = numpy.mean(numpy.array(Statistics.w2s))
- ew = ew1 + ew2
- print(" E[W] = E[W_1] + E[W_2] = %lf + %lf = %lf" % (ew1, ew2, ew))
- print(" E[T_1] = E[W_1] + E[X_1] = %lf + %lf = %lf" % (ew1, ex1, ew1 + ex1))
- print(" E[T_2] = E[W_2] + E[X_2] = %lf + %lf = %lf" % (ew2, ex2, ew2 + ex2))
- print(" E[T] = E[T_1] + E[T_2] = %lf + %lf = %lf" % (ew1 + ex1, ew2 + ex2, ew1 + ex1 + ew2 + ex2))
- enq1 = Statistics.area_wq1 / Statistics.total_time
- enq2 = Statistics.area_wq2 / Statistics.total_time
- esq1 = Statistics.area_sq1 / Statistics.total_time
- esq2 = Statistics.area_sq2 / Statistics.total_time
- print(" E[N_1] = E[N_q1] + E[N_s1] = %lf + %lf = %lf" % (enq1, esq1, enq1 + esq1))
- print(" E[N_2] = E[N_q2] + E[N_s2] = %lf + %lf = %lf" % (enq2, esq2, enq2 + esq2))
- print(" Var(W_1) = %lf" % (numpy.var(numpy.array(Statistics.w1s))))
- print(" Var(W_2) = %lf" % (numpy.var(numpy.array(Statistics.w2s))))
- print(" Var(X_1) = %lf" % (numpy.var(numpy.array(Statistics.x1s))))
- print(" Var(X_2) = %lf" % (numpy.var(numpy.array(Statistics.x2s))))
- @staticmethod
- def reset():
- """Reset all parameters. Useful between rounds."""
- Statistics.all_ex1.append(numpy.mean(numpy.array(Statistics.x1s)))
- Statistics.all_ex2.append(numpy.mean(numpy.array(Statistics.x2s)))
- Statistics.all_ew1.append(numpy.mean(numpy.array(Statistics.w1s)))
- Statistics.all_ew2.append(numpy.mean(numpy.array(Statistics.w2s)))
- Statistics.all_enq1.append(Statistics.area_wq1 / Statistics.total_time)
- Statistics.all_enq2.append(Statistics.area_wq2 / Statistics.total_time)
- Statistics.all_ens1.append(Statistics.area_sq1 / Statistics.total_time)
- Statistics.all_ens2.append(Statistics.area_sq2 / Statistics.total_time)
- Statistics.w1s = []
- Statistics.w2s = []
- Statistics.x1s = []
- Statistics.x2s = []
- Statistics.area_wq1 = 0
- Statistics.area_wq2 = 0
- Statistics.area_sq1 = 0
- Statistics.area_sq2 = 0
- Statistics.total_time = 0
- @staticmethod
- def clear():
- """Reset all parameters. Useful between rounds."""
- Statistics.all_ex1 = []
- Statistics.all_ex2 = []
- Statistics.all_ew1 = []
- Statistics.all_ew2 = []
- Statistics.all_enq1 = []
- Statistics.all_enq2 = []
- Statistics.all_ens1 = []
- Statistics.all_ens2 = []
- Statistics.w1s = []
- Statistics.w2s = []
- Statistics.x1s = []
- Statistics.x2s = []
- Statistics.area_wq1 = 0
- Statistics.area_wq2 = 0
- Statistics.area_sq1 = 0
- Statistics.area_sq2 = 0
- Statistics.total_time = 0
- class Log(object):
- """Log control"""
- @staticmethod
- def msg1(now, lenght1, length2):
- if LOG: print("%lf: Queues' length are: (%d, %d)." % (now, lenght1, length2))
- @staticmethod
- def msg2(now, id, client_class, service_time):
- if LOG: print("%lf: Client %d (of class %d) got a being served now and needs %lf time units." % (now, id, client_class, service_time))
- @staticmethod
- def msg3(now, id, client_class):
- if LOG: print("%lf: Finished attending client %d, of class %d." % (now, id, client_class))
- @staticmethod
- def msg4(now, id, service_time):
- if LOG: print("%lf: Server interrupted! Client %d (of class 2) will finish in %lf time units." % (now, id, service_time))
- @staticmethod
- def msg5(now, id):
- if LOG: print("%lf: Server woke up and will serve client %d" % (now, id))
- @staticmethod
- def msg6(now, id, client_class, service_time, current_class_on_server):
- if LOG: print("%lf: Client %d (of class %d) arrives and will need %lf for service. Current class on server: %d." % (now, id, client_class, service_time, current_class_on_server))
- @staticmethod
- def msg7(now, id):
- if LOG: print("%lf: Interrupting server because client %d more important." % (now, id))
- class Server(object):
- """Class that represents a server."""
- def __init__(self, env):
- """Constructor of a server object."""
- self.env = env
- self.action = env.process(self.run()) # Start the run process everytime an instance is created.
- self.service_2_starting_time = 0 # It'll be useful when client of class 2 is interrupted.
- self.queue_1 = [] # Waiting queue 1.
- self.queue_2 = [] # Waiting queue 2.
- self.ndone = 0 # Number of clients fully served.
- self.current_class_on_server = 0 # 1 if client of class 1 is being served, 2 if client of class 2 is being served, 0 otherwise.
- # Statistics.
- self.last_wq1_event_time = 0 # Last time when someone arrived or left waiting queue 1.
- self.last_wq2_event_time = 0 # Last time when someone arrived or left waiting queue 2.
- self.last_sq1_event_time = 0 # Last time when someone arrived or left service 1.
- self.last_sq2_event_time = 0 # Last time when someone arrived or left service 2.
- def run(self):
- """Server activities (behaviour) on simulation."""
- while self.ndone < TOTAL_CLIENTS:
- try:
- # If server is idle, it waits until next client arrives.
- while (len(self.queue_1) == 0 and len(self.queue_2) == 0):
- forever = 999999999999999999999999999999999999999
- yield self.env.timeout(forever)
- Log().msg1(self.env.now, len(self.queue_1), len(self.queue_2))
- # Serve job at head of queue 1.
- if (len(self.queue_1) > 0):
- Log().msg2(self.env.now, self.queue_1[0].id, 1, self.queue_1[0].service_time)
- self.current_class_on_server = 1 # Client of class 1 is being served.
- Statistics.w1s.append(self.env.now - self.queue_1[0].arrival_time) # Save waiting time for this client for later.
- Statistics.area_wq1 += (self.env.now - self.last_wq1_event_time) * len(self.queue_1) # First client of queue 1 is lefting waiting queue. Update wq1 area to find E[N_q1].
- self.last_wq1_event_time = self.env.now
- self.last_sq1_event_time = self.env.now
- yield self.env.timeout(self.queue_1[0].service_time) # Serve client 1.
- Statistics.area_sq1 += (self.env.now - self.last_sq1_event_time) * 1 # First client of queue 1 left server 1. Update sq1 area to find E[N_s1].
- # After first service, the client will have another service time, for the second service.
- self.queue_1[0].service_time = Random().expovariate(SERVICE_RATE)
- Statistics.x2s.append(self.queue_1[0].service_time)
- Statistics.area_wq2 += (self.env.now - self.last_wq2_event_time) * len(self.queue_2) # First client of queue 1 is joining waiting queue 2. Update wq2 area to find E[N_q2].
- self.last_wq2_event_time = self.env.now
- self.queue_2.append(self.queue_1[0])
- self.queue_2[-1].arrival_time = self.env.now
- Log().msg3(self.env.now, self.queue_1[0].id, 1)
- del self.queue_1[0] # Remove client from queue 1 (because it's now on queue 2).
- # Serve job at head of queue 2.
- else:
- Log().msg2(self.env.now, self.queue_2[0].id, 2, self.queue_2[0].service_time)
- self.current_class_on_server = 2 # Client of class 2 is being served.
- service_2_starting_time = self.env.now # It'll be useful when client of class 2 is interrupted.
- Statistics.area_wq2 += (self.env.now - self.last_wq2_event_time) * len(self.queue_2) # First client of queue 2 is lefting waiting queue. Update wq2 area to find E[N_q2].
- self.last_wq2_event_time = self.env.now
- self.queue_2[0].waiting_time_in_queue_2 += (self.env.now - self.queue_2[0].arrival_time) # Each client knows how much time spent on waiting queue 2.
- self.last_sq2_event_time = self.env.now
- yield self.env.timeout(self.queue_2[0].service_time) # Serve client 2.
- Statistics.area_sq2 += (self.env.now - self.last_sq2_event_time) * 1 # First client of queue 2 left server 2. Update sq2 area to find E[N_s2].
- Log().msg3(self.env.now, self.queue_2[0].id, 2)
- self.ndone += 1
- Statistics.w2s.append(self.queue_2[0].waiting_time_in_queue_2) # Waiting queue 2 is saved now because interruptions could happen at any time.
- del self.queue_2[0]
- self.current_class_on_server = 0
- except simpy.Interrupt:
- # If client of class 1 arrives while a client of class 2 is being served.
- if (self.current_class_on_server == 2):
- self.queue_2[0].service_time -= self.env.now - service_2_starting_time # Update service time needed by client partially.
- Log().msg4(self.env.now, self.queue_2[0].id, self.queue_2[0].service_time)
- Statistics.area_sq2 += (self.env.now - self.last_sq2_event_time) * 1 # Client of server 2 left. Update sq2 area to find E[N_s2].
- Statistics.area_wq2 += (self.env.now - self.last_wq2_event_time) * (len(self.queue_2) - 1) # It's like current client (of class 2) arrives on queue 2 again. Update wq2 area to find E[N_q2].
- self.last_wq2_event_time = self.env.now
- self.queue_2[0].arrival_time = self.env.now
- # If client of class 1 arrives when server is idle.
- elif (self.current_class_on_server == 0):
- Log().msg5(self.env.now, self.queue_1[0].id)
- Statistics.total_time = self.env.now # Because env.now goes crazy at the end of the simulation.
- class Client(object):
- """Class that reoresents a client (or an arrival)."""
- LAST_ARRIVAL_TIME = 0 # Needed because the time between arives is exponential. (Don't forget to reset between rounds!)
- def __init__(self, env, server, i):
- """Constructor of a client object."""
- self.env = env
- self.server = server
- self.id = i
- self.client_class = 1
- self.action = env.process(self.run()) # Start the run process everytime an instance is created.
- self.arrival_time = Client.LAST_ARRIVAL_TIME + Random().expovariate(ARRIVAL_RATE) # Time when client will arrive on queue.
- self.service_time = Random().expovariate(SERVICE_RATE) # Time client will waste being served.
- Statistics.x1s.append(self.service_time) # Save service time for later.
- Client.LAST_ARRIVAL_TIME = self.arrival_time
- self.waiting_time_in_queue_2 = 0
- def run(self):
- """Client activities (behaviour) on simulation."""
- # Client is living somewhere else and will be woken up when arrives on waiting queue.
- yield self.env.timeout(self.arrival_time)
- # Client arrives on waiting queue to be served eventually.
- waiting_queue1_length = len(self.server.queue_1)
- if self.server.current_class_on_server == 1: waiting_queue1_length -= 1 # If there's some client of class 1 being served, only len(queue_1) - 1 is in waiting queue.
- Statistics.area_wq1 += (self.env.now - self.server.last_wq1_event_time) * waiting_queue1_length # Client is joining waiting queue 1. Update wq1 area to find E[N_q1].
- self.server.last_wq1_event_time = self.env.now
- self.server.queue_1.append(self)
- Log().msg6(self.env.now, self.id, self.client_class, self.service_time, self.server.current_class_on_server)
- # If server is sleeping because it's idle.
- if (len(self.server.queue_2) == 0 and len(self.server.queue_1) == 1):
- self.server.action.interrupt()
- # If there is a client of class 2 on server, this client will interrompt it to be served (class 1 has priority over class 2)
- elif (self.server.current_class_on_server == 2):
- Log().msg7(self.env.now, self.id)
- self.server.action.interrupt()
- def main():
- """Main function. It creates one server and TOTAL_CLIENTS clients. Then, it runs the environment for simulation."""
- print('Starting simulation with %d clients and %d rounds, with arrival rate equals %lf' % (TOTAL_CLIENTS, TOTAL_ROUNDS, ARRIVAL_RATE))
- for _ in range(TOTAL_ROUNDS):
- env = simpy.Environment()
- server = Server(env)
- clients = [Client(env, server, i) for i in range(TOTAL_CLIENTS)]
- env.run()
- if LOG and STATISTICS: print("")
- if STATISTICS: Statistics.show()
- #Statistics.discart_first(TRANSIENT)
- Statistics.reset() # Reset statistics between rounds.
- Client.LAST_ARRIVAL_TIME = 0 # Reset static data in client.
- print("E[X_1]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_ex1]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_ex1))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_ex1))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_ex1))))
- print("E[X_2]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_ex2]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_ex2))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_ex2))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_ex2))))
- print("E[W_1]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_ew1]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_ew1))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_ew1))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_ew1))))
- print("E[W_2]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_ew2]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_ew2))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_ew2))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_ew2))))
- print("E[N_q1]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_enq1]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_enq1))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_enq1))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_enq1))))
- print("E[N_q2]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_enq2]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_enq2))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_enq2))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_enq2))))
- print("E[N_s1]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_ens1]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_ens1))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_ens1))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_ens1))))
- print("E[N_s2]")
- print(" " + str([float(Decimal("%.5lf" % x)) for x in Statistics().all_ens2]))
- print(" MEAN: %.5lf" % (numpy.mean(numpy.array(Statistics().all_ens2))))
- print(" VARIANCE: %.5lf" % (numpy.var(numpy.array(Statistics().all_ens2))))
- print(" STD: %.5lf\n" % (numpy.std(numpy.array(Statistics().all_ens2))))
- t_student(Statistics(), TOTAL_ROUNDS)
- chi_square(Statistics(), TOTAL_ROUNDS)
- if __name__ == "__main__":
- """Python stuff."""
- parser = argparse.ArgumentParser(description="Project of the course Avaliacao & Desempenho (UFRJ). Made by Igor Carpanese, Thais Luca and Raffael Siqueira.", formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=120))
- parser.add_argument('-c', '--clients', type=int, default=10000, help='number of clients (default: 10000)')
- parser.add_argument('-r', '--rounds', type=int, default=1, help='number of rounds (default: 1)')
- parser.add_argument('-l', '--lambda', type=float, default=0.1, help='arrival rate (default: 0.1)')
- parser.add_argument('-t', '--transient', type=int, default=0, help='ignore first clients (default: 0)')
- parser.add_argument('--log', default=False, action='store_true', help='print simulation log (default: False)')
- parser.add_argument('--statistics', default=False, action='store_true', help='print data for each round (default: False)')
- parser.add_argument('-v', '--version', action='version', version='SIM 1.0')
- args = vars(parser.parse_args())
- TOTAL_CLIENTS = args["clients"]
- TOTAL_ROUNDS = args["rounds"]
- ARRIVAL_RATE = args["lambda"]
- LOG = args["log"]
- STATISTICS = args["statistics"]
- TRANSIENT = args["transient"]
- if TRANSIENT >= TOTAL_CLIENTS:
- print("Number of clients in transient phrase cannot be greater than total number of clients")
- elif TOTAL_ROUNDS < 30:
- print("Simulation does not work with proper precision for less than 30 rounds.")
- else:
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement