diff --git a/environmentgrammar.py b/environmentgrammar.py index 9efc37f..7177d00 100644 --- a/environmentgrammar.py +++ b/environmentgrammar.py @@ -1,4 +1,3 @@ - from lark import Transformer import sys from sys import exit @@ -21,8 +20,9 @@ environment_definition: "features: {" environpair+ "}" environpair: CNAME ":" (NUMBER | dist) actionpair: CNAME ":" dist - dist: (normal | truncated_normal | logisitic | inactive | constant | userfunction) + dist: (uniform | normal | truncated_normal | logisitic | inactive | constant | userfunction) normal: "normal(" mean "," stdev ")" + uniform: "uniform(" lower "," upper ")" logisitic: "logistic(" mean "," stdev ")" truncated_normal: "truncnorm(" lower "," upper "," mean "," stdev ")" constant: "constant(" value ")" @@ -49,15 +49,66 @@ class EnvironmentTransformer(Transformer): all_arms = set() - + def generator_factory(self, args): + funcname, *funpara = args + + try: + my_function = getattr(userfunctions, funcname) + except AttributeError: + raise RuntimeError("Used a user-defined function which doesn't exist") + + def generic_generator(variables = None): + if(variables): + while True: + try: + env_state = self.environment_grabber() + except AttributeError: + env_state = variables + final_params = [] + for para in funpara: + if(type(para) == str): + variable_value = env_state[para] + if(isinstance(variable_value,GeneratorType)): + final_params.append(next(variable_value)) + elif(callable(variable_value)): + raise RuntimeError('Nested more than implemented') + else: + final_params.append(variable_value) + elif(isinstance(para, GeneratorType)): + final_params.append(next(para)) + elif(callable(para)): + gen_from_callable = para(variables) + x = next(gen_from_callable) + final_params.append(x) + funpara[funpara.index(para)] = gen_from_callable #should now remain a generator for next time + + else: + final_params.append(para) + + yield my_function(*final_params) + + else: + while True: + final_params = [] + for para in funpara: + if(isinstance(para, GeneratorType)): + final_params.append(next(para)) + else: + final_params.append(para) + yield my_function(*final_params) + + if(any(type(param) == str for param in funpara) or any(callable(param) for param in funpara)): + return generic_generator + else: + return generic_generator() + def start(self,arg): - #print(arg) - #input("startpause") + return_dict = {} if(len(arg) == 3): given_seed, context_and_feature_dict, context_tuples = arg - np.random.seed(given_seed) + #np.random.seed(given_seed) print("seed set") else: context_and_feature_dict, context_tuples = arg @@ -105,12 +156,12 @@ def feature_generator(): r_gen = reward_generator() f_gen = None if(context_to_feature): f_gen = feature_generator() - + self.all_arms = list(self.all_arms) + self.all_arms.sort() return_dict["all_arms"] = self.all_arms return_dict["reward_generator"] = r_gen return_dict["feature_generator"] = f_gen - #pprint.pprint(return_dict) - #input("return pause") + return return_dict def parse_dictionary(self, pair_list): @@ -164,11 +215,10 @@ def context(self,argss): context_name, variable_dict, action_pairs = argss else: context_name, action_pairs = argss - + for action_pair in action_pairs: action_name, action_gen = action_pair - print(action_gen) - #input("Im here") + if(callable(action_gen)): action_gen = action_gen(variable_dict) #if it is not a generator yet that implies it needs to use the variables. self.all_arms.add(action_name) if(action_name in action_dict): raise RuntimeError("Same action specified multiple times: action names not unique within context") @@ -184,35 +234,45 @@ def dist(self,args): return args[0] # return "foo" def normal(self,argg): - def normal_generator(variables = None): - if(variables): - while True: - try: - env_state = self.environment_grabber() - except AttributeError: - env_state = variables - final_params = [] - for para in argg: - if(type(para) == str): - variable_value = env_state[para] - if(isinstance(variable_value,GeneratorType)): - final_params.append(next(variable_value)) - else: - final_params.append(variable_value) - else: - final_params.append(para) - mean, stdev = final_params - - yield np.random.normal(loc=mean,scale=stdev) - else: - mean, stdev = argg - while True: - yield np.random.normal(loc=mean,scale=stdev) - - if(any(type(param) == str for param in argg)): - return normal_generator - else: - return normal_generator() + args = ["normal"] + argg + genn = self.generator_factory(args) + + return genn + + def uniform(self,argg): + args = ["uniform"] + argg + genn = self.generator_factory(args) + + return genn + # def normal_generator(variables = None): + # if(variables): + # while True: + # try: + # env_state = self.environment_grabber() + # except AttributeError: + # env_state = variables + # final_params = [] + # for para in argg: + # if(type(para) == str): + # variable_value = env_state[para] + # if(isinstance(variable_value,GeneratorType)): + # final_params.append(next(variable_value)) + # else: + # final_params.append(variable_value) + # else: + # final_params.append(para) + # mean, stdev = final_params + + # yield np.random.normal(loc=mean,scale=stdev) + # else: + # mean, stdev = argg + # while True: + # yield np.random.normal(loc=mean,scale=stdev) + + # if(any(type(param) == str for param in argg)): + # return normal_generator + # else: + # return normal_generator() def logistic(self,argg): def logistic_generator(variables = None): @@ -249,53 +309,66 @@ def empty_generator(): yield None return empty_generator() def truncated_normal(self, args): - def truncnorm_generator(variables = None): - if(variables): - print("2oooo") - - while True: - try: - env_state = self.environment_grabber() - except AttributeError: - env_state = variables - final_params = [] - print(args) - #input("dem args") - for para in args: - if(type(para) == str): - variable_value = env_state[para] - if(isinstance(variable_value,GeneratorType)): - final_params.append(next(variable_value)) - else: - final_params.append(variable_value) - elif(isinstance(para, GeneratorType)): - final_params.append(next(para)) - elif(callable(para)): - input("this should only happen once") - gen_from_callable = para(variables) - final_params.append(next(gen_from_callable)) - args[args.index(para)] = gen_from_callable #should now remain a generator for next time - - else: - final_params.append(para) - print(final_params) - input("final_params") - lower, upper, mean, stdev = final_params + args = ["truncated_normal"] + args + genn = self.generator_factory(args) + + return genn + # def truncnorm_generator(variables = None): + # if(variables): + # print("2oooo") + + # while True: + # try: + # env_state = self.environment_grabber() + # except AttributeError: + # env_state = variables + # final_params = [] + # print(args) + # #input("dem args") + # for para in args: + # if(type(para) == str): + # variable_value = env_state[para] + # if(isinstance(variable_value,GeneratorType)): + # final_params.append(next(variable_value)) + # else: + # final_params.append(variable_value) + # elif(isinstance(para, GeneratorType)): + # final_params.append(next(para)) + # elif(callable(para)): + # input("this should only happen once") + # gen_from_callable = para(variables) + # final_params.append(next(gen_from_callable)) + # args[args.index(para)] = gen_from_callable #should now remain a generator for next time + + # else: + # final_params.append(para) + # print(final_params) + # input("final_params") + # lower, upper, mean, stdev = final_params - a, b = (lower - mean) / stdev, (upper - mean) / stdev + # a, b = (lower - mean) / stdev, (upper - mean) / stdev - yield truncnorm.rvs(a,b, loc= mean, scale=stdev) - else: - print("1oooo") - lower, upper, mean, stdev = args #this can be simplified by using **kwargs and the correct names from the beginning and then unpacking it into the needed function - a, b = (lower - mean) / stdev, (upper - mean) / stdev - while True: - yield truncnorm.rvs(a,b, loc= mean, scale=stdev) - - if(any(type(param) == str for param in args)): - return truncnorm_generator - else: - return truncnorm_generator() + # yield truncnorm.rvs(a,b, loc= mean, scale=stdev) + # else: + # print("1oooo") + # input(args) + # final_params = [] + # for para in args: + # if(isinstance(para, GeneratorType)): + # final_params.append(next(para)) + # else: + # final_params.append(para) + # lower, upper, mean, stdev = final_params #this can be simplified by using **kwargs and the correct names from the beginning and then unpacking it into the needed function + # a, b = (lower - mean) / stdev, (upper - mean) / stdev + # while True: + # yield truncnorm.rvs(a,b, loc= mean, scale=stdev) + + # print("looking at the args here") + # input(args) + # if(any(type(param) == str for param in args) or any(callable(param) for param in args)): + # return truncnorm_generator + # else: + # return truncnorm_generator() def constant(self,args): def const_generator(variables = None): @@ -321,7 +394,7 @@ def const_generator(variables = None): else: return const_generator() #return const_generator() def value(self,args): - print(args) + #print(args) #input("pause for cause") if(len(args) == 1): args = args[0] @@ -330,44 +403,47 @@ def value(self,args): except (ValueError, TypeError): return args def userfunction(self,args): - - funcname, *funpara = args - - try: - my_function = getattr(userfunctions, funcname) - except AttributeError: - raise RuntimeError("Used a user-defined function which doesn't exist") - def functional_generator(variables = None): - if(variables): - while True: - try: - env_state = self.environment_grabber() - except AttributeError: - env_state = variables - final_params = [] - for para in funpara: - if(type(para) == str): - variable_value = env_state[para] - if(isinstance(variable_value,GeneratorType)): - final_params.append(next(variable_value)) - else: - final_params.append(variable_value) - else: - final_params.append(para) + genn = self.generator_factory(args) + + return genn + + # funcname, *funpara = args + + # try: + # my_function = getattr(userfunctions, funcname) + # except AttributeError: + # raise RuntimeError("Used a user-defined function which doesn't exist") + # def functional_generator(variables = None): + # if(variables): + # while True: + # try: + # env_state = self.environment_grabber() + # except AttributeError: + # env_state = variables + # final_params = [] + # for para in funpara: + # if(type(para) == str): + # variable_value = env_state[para] + # if(isinstance(variable_value,GeneratorType)): + # final_params.append(next(variable_value)) + # else: + # final_params.append(variable_value) + # else: + # final_params.append(para) - #input("paaa" + str(one_for_now)) - - yield my_function(*final_params) - else: - one_for_now, = funpara - #input("paaa2" + str(one_for_now)) - - while True: - yield my_function(one_for_now) - if(any(type(param) == str for param in funpara)): - return functional_generator - else: - return functional_generator() + # #input("paaa" + str(one_for_now)) + + # yield my_function(*final_params) + # else: + + # #input("paaa2" + str(one_for_now)) + + # while True: + # yield my_function(*funpara) + # if(any(type(param) == str for param in funpara)): + # return functional_generator + # else: + # return functional_generator() diff --git a/managedsystem.py b/managedsystem.py index aeb451b..dfeafee 100644 --- a/managedsystem.py +++ b/managedsystem.py @@ -9,37 +9,35 @@ from math import floor class MockSAS: - def __init__(self, num_sys, policies, parse_tree): + def __init__(self, policy, parse_tree): #could be multiple systems with one environment self.managing_systems = [] - for i in range(num_sys): - - transformer_dict = EnvironmentTransformer().transform(parse_tree) - managed = [self.ManagedSystem(transformer_dict["reward_generator"], transformer_dict["feature_generator"])] #extend this with multiple managed systems if thats what you want to model. - EnvironmentTransformer.environment_grabber = managed[0].get_observations - - m_sys = self.ManagingSystem(policies[i], managed, list(transformer_dict["all_arms"])) - self.managing_systems.append(m_sys) + + transformer_dict = EnvironmentTransformer().transform(parse_tree) + #pprint.pprint(transformer_dict) + managed = [self.ManagedSystem(transformer_dict["reward_generator"], transformer_dict["feature_generator"])] #extend this with multiple managed systems if thats what you want to model. + EnvironmentTransformer.environment_grabber = managed[0].get_observations + + + m_sys = self.ManagingSystem(policy, managed, list(transformer_dict["all_arms"])) + self.managing_system = m_sys def operation(self, res = {}): - for m_sys in self.managing_systems: - managed_busy = [True] - - while all(managed_busy): - managed_busy = [] - for managed in m_sys.managed: - try: - managed.environment.notify_observers() - except AttributeError: - #no environment' - pass - acks = managed.notify_observers() #check whether this order makes sense - #print("acks:" + str(acks)) - busy = all(acks) - #print("busy: " + str(busy)) - managed_busy.append(busy) - res[m_sys.name] = m_sys.avg_rw_record + managed_busy = [True] + + while all(managed_busy): + managed_busy = [] + for managed in self.managing_system.managed: + try: + managed.environment.notify_observers() #Observe environment + except AttributeError: + #no environment + pass + acks = managed.notify_observers() #Notify the managing system that an adaptation is necessary + busy = all(acks) + managed_busy.append(busy) + res[self.managing_system.name] = self.managing_system.avg_rw_record return res @@ -59,16 +57,18 @@ def __init__(self, policy_tuple, managed, arms): for managed_system in managed: managed_system.register_observer(self) - def notify(self, environment): + def notify(self, reward_distributions): #metrics = environment.metrics; reward_function(metrics) - if(environment): - reward = next(environment[self.current_action]) + if(reward_distributions): + reward = next(reward_distributions[self.current_action]) #add check for none reward due to inactive arm. - #print("received " + str(reward)) self.current_action = self.policy.start_strategy(reward) + self.average_reward = self.average_reward + ((1/self.round) * (reward - self.average_reward)) self.avg_rw_record.append(self.average_reward) + self.round = self.round + 1 + return True else: #end of trace @@ -93,8 +93,9 @@ def __init__(self, generator, env_generator): def get_observations(self): return self.observations def notify(self, new_observations): - self.observations = new_observations + + def register_observer(self, observer): self._observers.append(observer) diff --git a/orderingstats.py b/orderingstats.py new file mode 100644 index 0000000..84b853e --- /dev/null +++ b/orderingstats.py @@ -0,0 +1,52 @@ +import matplotlib.pyplot as plt +import matplotlib +import numpy as np +import sys +import re +import csv +from scipy import stats +from itertools import groupby +import glob +import collections +from statistics import mean +import numpy as np +from pprint import pprint as p + + +csv_files = glob.glob("*.csv") + +for i, file in enumerate(csv_files): + print(str(i) + ": " + str(file)) + +indices = input("Which two indices should be compared").split() +index1, index2 = indices +print(indices) + +files = [csv_files[int(index1)], csv_files[int(index2)]] + +result_dict = {} + +for filename in files: + result_dict[filename] = {} + with open(filename, newline='') as csvfile: + spamreader = csv.reader(csvfile) + for i, row in enumerate(spamreader): + if(i == 0): pass + else: + result_dict[filename][row[0]] = float(row[2]) + +p(result_dict) +temp = [] +for key in result_dict.keys(): + x = result_dict[key] + temp.append(list({k: v for k, v in sorted(x.items(), key=lambda item: item[1])}.keys())) + +x1, x2 = temp + +print(x1) +print(x2) +tau, p_value = stats.kendalltau(x1,x2) + + + +print(tau,p_value) \ No newline at end of file diff --git a/profiles/DynamicSystem.txt b/profiles/DynamicSystem.txt index 0fcf2b6..fec629f 100644 --- a/profiles/DynamicSystem.txt +++ b/profiles/DynamicSystem.txt @@ -1,25 +1,23 @@ SixtyRequests{ features: { - noise_due_service_time: truncnorm(0.0,0.5,0.2,0.2) + arrival_rate: uniform(54,66) } arms: { - ThreeServers: truncnorm(0.0,1.0,0.715,noise_due_service_time) - SixServers: truncnorm(0.0,1.0,0.614,noise_due_service_time) - EightServers: truncnorm(0.0,1.0,0.508,noise_due_service_time) - ElevenServers: truncnorm(0.0,1.0,0.348,noise_due_service_time) - ThirteenServers: truncnorm(0.0,1.0,0.241,noise_due_service_time) + ThreeServers: utilitySWIM(arrival_rate, 1.0, normal(0.064,0.009), 13, 3) + SixServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.001), 13, 6) + EightServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.001), 13, 8) + ElevenServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.001), 13, 11) + ThirteenServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.002), 13, 13) } } EightyRequests{ features: { - noise_due_service_time: truncnorm(0.0,0.5,0.2,0.2) + arrival_rate: uniform(72,88) } arms: { - - - ThreeServers: truncnorm(0.0,1.0,0.190,noise_due_service_time) - SixServers: truncnorm(0.0,1.0,0.949,noise_due_service_time) - EightServers: truncnorm(0.0,1.0,0.870,noise_due_service_time) - ElevenServers: truncnorm(0.0,1.0,0.712,noise_due_service_time) - ThirteenServers: truncnorm(0.0,1.0,0.605,noise_due_service_time) + ThreeServers: utilitySWIM(arrival_rate, 1.0, normal(1.681,1.436), 13, 3) + SixServers: utilitySWIM(arrival_rate, 1.0, normal(0.041,0.001), 13, 6) + EightServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.0004), 13, 8) + ElevenServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.001), 13, 11) + ThirteenServers: utilitySWIM(arrival_rate, 1.0, normal(0.039,0.001), 13, 13) } } -Trace: (StochasticContext, 500) \ No newline at end of file +Trace: (SixtyRequests, 166) (EightyRequests,166) \ No newline at end of file diff --git a/profiles/SWIMProfile.txt b/profiles/SWIMProfile.txt new file mode 100644 index 0000000..751fa8b --- /dev/null +++ b/profiles/SWIMProfile.txt @@ -0,0 +1,12 @@ +StochasticContext{ + features: { + noise_due_service_time: truncnorm(0.0,0.5,0.2,0.2) + } + arms: { + ThreeServers: truncnorm(0.0,1.0,0.715,noise_due_service_time) + SixServers: truncnorm(0.0,1.0,0.614,noise_due_service_time) + EightServers: truncnorm(0.0,1.0,0.508,noise_due_service_time) + ElevenServers: truncnorm(0.0,1.0,0.348,noise_due_service_time) + ThirteenServers: truncnorm(0.0,1.0,0.241,noise_due_service_time) + } } +Trace: (StochasticContext, 500) \ No newline at end of file diff --git a/profiles/TestSys.txt b/profiles/TestSys.txt index 1f42279..6deacb9 100644 --- a/profiles/TestSys.txt +++ b/profiles/TestSys.txt @@ -1,10 +1,10 @@ StochasticContext{ features: { - noise_due_service_time: truncnorm(0.0,0.5,0.2,0.2) + noise_due_service_time: truncnorm(0.0,0.5,0.2,0.2) service_time: 10 some_value: 0.715 } arms: { - ThreeServers: truncnorm(0.0,myfunction(noise_due_service_time, 20),some_value,0.04) + ThreeServers: normal(0.1,0.1) } } Trace: (StochasticContext, 500) diff --git a/run.py b/run.py index 07ede25..5c750fd 100644 --- a/run.py +++ b/run.py @@ -1,4 +1,5 @@ +from turtle import position from masced_bandits.bandits import init_bandit from masced_bandits.bandit_options import initialize_arguments from environmentgrammar import environment_grammar, EnvironmentTransformer @@ -11,19 +12,42 @@ from managedsystem import MockSAS import os import userfunctions +import sys +import csv LINE_DATA_POINTS = 10 TOTAL_ROUNDS = 0 -NUM_SEEDS = 100 +NUM_SEEDS = 30 + +SWIM_ORDERING = {"UCB-TN" : 1, +"egreedy-0.2" : 2, +"DUCB-0.997" : 3, +"DUCB-0.995" : 4, +"egreedy-0.4" : 5, +"EXP3-333" : 6, +"DUCB-0.992" : 7, +"DUCB-0.99" : 8, +"egreedy-0.6" :9 , +"egreedy-0.8" : 10, +"DUCB-0.97" : 11, +"DUCB-0.95" : 12, +"egreedy-1.0" : 13, +"DUCB-0.92" : 14, +"DUCB-0.89" : 15} plt.style.use('seaborn-bright') parser = Lark(environment_grammar) -mission_statement = "profiles/TestSys.txt" +mission_statement = "profiles/DynamicSystem.txt" sys_name = mission_statement.split(".txt")[0] - result_path = "results/" + sys_name + "/" os.makedirs(result_path, exist_ok=True) +# input(sys.argv) + +NUM_SEEDS = int(sys.argv[1]) +CSV_name = str(sys.argv[2]) + + try: with open(mission_statement, 'r') as source: source_code = source.read() @@ -44,27 +68,44 @@ final_res = {} def describe_config(config): - return str(str(config[0]['name']) + "-" + str(config[0].get('formula',"")) + str(config[0].get('epsilon',"")) + str(config[0].get('exploration_rounds',"")) + str(config[0].get('decay_rate',"") + str(config[0].get('learning_rate',"")) + str(config[0].get('horizon',"")))) + return str(str(config['name']) + "-" + str(config.get('formula',"")) + str(config.get('epsilon',"")) + str(config.get('exploration_rounds',"")) + str(config.get('decay_rate',"")) + str(config.get('learning_rate',"")) + str(config.get('horizon',"")) + str(config.get('gamma',"")) ) + + +# egreedies = [[{'name': "egreedy", 'epsilon': "0.1"}], [{'name': "egreedy", 'epsilon': "0.2"}],[{'name': "egreedy", 'epsilon': "0.3"}], \ +# [{'name': "egreedy", 'epsilon': "0.4"}],[{'name': "egreedy", 'epsilon': "0.5"}], [{'name': "egreedy", 'epsilon': "0.6"}],[{'name': "egreedy", 'epsilon': "0.7"}], \ +# [{'name': "egreedy", 'epsilon': "0.8"}],[{'name': "egreedy", 'epsilon': "0.9"}],[{'name': "egreedy", 'epsilon': "1.0"}]] + +egreedies = [{'name': "egreedy", 'epsilon': "0.2"}, {'name': "egreedy", 'epsilon': "0.4"},{'name': "egreedy", 'epsilon': "0.6"}, \ +{'name': "egreedy", 'epsilon': "0.8"},{'name': "egreedy", 'epsilon': "1.0"}] + +ucbs = [{'name': "UCB", 'formula': "TN"}] + + +# exp3s = [[{'name': "EXP3", 'learning_rate': "0.1"}], \ +# [{'name': "EXP3", 'learning_rate': "0.2"}],[{'name': "EXP3", 'learning_rate': "0.3"}], [{'name': "EXP3", 'learning_rate': "0.4"}], [{'name': "EXP3", 'learning_rate': "0.5"}],\ +# [{'name': "EXP3", 'learning_rate': "0.6"}], [{'name': "EXP3", 'learning_rate': "0.7"}], [{'name': "EXP3", 'learning_rate': "0.8"}], [{'name': "EXP3", 'learning_rate': "0.9"}], \ +# [{'name': "EXP3", 'learning_rate': "1.0"}]] +exp3s = [{'name': "EXP3", 'horizon': "333"}] -# configs = [[{'name': "UCB", 'formula': "TN"}], [{'name': "egreedy", 'epsilon': "0.1"}], [{'name': "egreedy", 'epsilon': "0.2"}],[{'name': "egreedy", 'epsilon': "0.3"}],[{'name': "egreedy", 'epsilon': "0.4"}],[{'name': "egreedy", 'epsilon': "0.5"}], [{'name': "egreedy", 'epsilon': "0.6"}],[{'name': "egreedy", 'epsilon': "0.7"}],[{'name': "egreedy", 'epsilon': "0.8"}],[{'name': "egreedy", 'epsilon': "0.9"}],[{'name': "egreedy", 'epsilon': "1.0"}], [{'name': "EXP3", 'learning_rate': "0.1"}], [{'name': "EXP3", 'learning_rate': "0.2"}],[{'name': "EXP3", 'learning_rate': "0.3"}], \ -# [{'name': "EXP3", 'learning_rate': "0.4"}], [{'name': "EXP3", 'learning_rate': "0.5"}], [{'name': "EXP3", 'learning_rate': "0.6"}], [{'name': "EXP3", 'learning_rate': "0.7"}], [{'name': "EXP3", 'learning_rate': "0.8"}], [{'name': "EXP3", 'learning_rate': "0.9"}], [{'name': "EXP3", 'horizon': "500"}], -# [{'name': "EXP3", 'learning_rate': "0.01"}], [{'name': "EXP3", 'learning_rate': "0.02"}],[{'name': "EXP3", 'learning_rate': "0.03"}], \ -# [{'name': "EXP3", 'learning_rate': "0.04"}], [{'name': "EXP3", 'learning_rate': "0.05"}], [{'name': "EXP3", 'learning_rate': "0.06"}], [{'name': "EXP3", 'learning_rate': "0.07"}], [{'name': "EXP3", 'learning_rate': "0.08"}], [{'name': "EXP3", 'learning_rate': "0.09"}], [{'name': "ETC", 'exploration_rounds': "1"}]] +discountucbs = [{'name': "DUCB", 'gamma': "0.89"}, {'name': "DUCB", 'gamma': "0.92"}, {'name': "DUCB", 'gamma': "0.95"}, {'name': "DUCB", 'gamma': "0.97"}, {'name': "DUCB", 'gamma': "0.99"},\ + {'name': "DUCB", 'gamma': "0.992"}, {'name': "DUCB", 'gamma': "0.995"}, {'name': "DUCB", 'gamma': "0.997"}] -simple_configs = [ [{'name': "ETC", 'exploration_rounds': "1"}], [{'name': "UCB", 'formula': "TN"}], [{'name': "egreedy", 'epsilon': "0.1"}], [{'name': "egreedy", 'epsilon': "0.2"}],[{'name': "egreedy", 'epsilon': "0.3"}]] +configs = egreedies + ucbs + exp3s + discountucbs -configs = [[{'name': "ETC", 'exploration_rounds': "1"}], [{'name': "UCB", 'formula': "TN"}], [{'name': "egreedy", 'epsilon': "0.1"}], [{'name': "EXP3", 'learning_rate': "0.03"}], [{'name': "EXP3", 'learning_rate': "0.05"}], [{'name': "EXP3", 'learning_rate': "0.07"}], [{'name': "EXP3", 'learning_rate': "0.08"}]] +#simple_configs = [ [{'name': "ETC", 'exploration_rounds': "1"}], [{'name': "UCB", 'formula': "TN"}], [{'name': "egreedy", 'epsilon': "0.1"}], [{'name': "egreedy", 'epsilon': "0.2"}],[{'name': "egreedy", 'epsilon': "0.3"}]] + +#configs = [[{'name': "ETC", 'exploration_rounds': "1"}], [{'name': "UCB", 'formula': "TN"}], [{'name': "egreedy", 'epsilon': "0.1"}], [{'name': "EXP3", 'learning_rate': "0.03"}], [{'name': "EXP3", 'learning_rate': "0.05"}], [{'name': "EXP3", 'learning_rate': "0.07"}], [{'name': "EXP3", 'learning_rate': "0.08"}]] for config in configs: - final_res[describe_config(config)] = {"boxplot": [], "lineplot" : []} + final_res[describe_config(config)] = {"boxplot": [], "lineplot" : [], "positions": [], "match_swim": []} for i in range(NUM_SEEDS): all_res = {} for config in configs: #input(str(config) + ">") - np.random.seed(i * 1339) - mksas = MockSAS(1,config, parse_tree) + np.random.seed(i * 293019) + mksas = MockSAS(config, parse_tree) res = mksas.operation({}) for key in res.keys(): #input(str(key) + ">>") @@ -75,15 +116,35 @@ def describe_config(config): TOTAL_ROUNDS = len(res[key]) line_points = res[key][::every_nth] final_res[config_key]['lineplot'].append(line_points) + + position_dict = {} + for config in configs: + for key in res.keys(): + config_key = describe_config(config) + position_dict[config_key] = final_res[config_key]['boxplot'][-1] + + ranking = (list({k: v for k, v in sorted(position_dict.items(), key=lambda item: item[1], reverse= True)}.keys())) + + for policy in ranking: + final_res[policy]["match_swim"].append(1 if ((ranking.index(policy)+1) == SWIM_ORDERING[policy]) else 0) + final_res[policy]["positions"].append(ranking.index(policy)+1) + +#Average Position +#SWIM as a global truth +#Kendall-TAU + + +#SWIM + +# #pprint.pprint(final_res) #print(str(final_res.keys())) + boxplot_data = [] boxplot_labels = [] lineplot_data = {} -#lineplot_labels = [] - for config_key in final_res.keys(): boxplot_data.append(final_res[config_key]["boxplot"]) boxplot_labels.append(str(config_key)) @@ -96,23 +157,62 @@ def describe_config(config): # input("zip_tuple") lineplot_data[str(config_key)] = [mean(run_tuple) for run_tuple in zip(*stacked_data)] +row_titles = ["Policy Name", "Mean", "Median", "Upper Q", "Lower Q", "IQR", "Upper W", "Lower W", "Average Ranking", "Percentage Matching SWIM"] +all_rows = [row_titles] +for i, datum in enumerate(boxplot_data): + + entry_row = [] + + entry_row.append(boxplot_labels[i]) + + data_mean = np.mean(datum) + median = np.median(datum) + upper_quartile = np.percentile(datum, 75) + lower_quartile = np.percentile(datum, 25) + + iqr = upper_quartile - lower_quartile + upper_whisker = np.array(datum)[datum<=upper_quartile+1.5*iqr].max() + lower_whisker = np.array(datum)[datum>=lower_quartile-1.5*iqr].min() + + average_rank = np.mean(final_res[boxplot_labels[i]]["positions"]) + perct_match = str(np.mean(final_res[boxplot_labels[i]]["match_swim"]) * 100) + "%" + + + entry_row+=[median,data_mean,upper_quartile,lower_quartile,iqr,upper_whisker,lower_whisker, average_rank, perct_match] + + all_rows.append(entry_row) + + + + +with open(CSV_name, 'w', newline='') as csvfile: + spamwriter = csv.writer(csvfile) + for the_row in all_rows: + spamwriter.writerow(the_row) + + + + +def boxplotter(boxplot_data, boxplot_labels, result_path): + plt.boxplot(boxplot_data, showmeans=True, meanline=True) + plt.ylabel("average reward after " + str(TOTAL_ROUNDS-1) + " rounds (" + str(NUM_SEEDS) + " seeds)", fontdict={'size':14}) + plt.xlabel("policies", fontdict={'size':14}) -plt.boxplot(boxplot_data, showmeans=True, meanline=True) -plt.ylabel("average reward after " + str(TOTAL_ROUNDS-1) + " rounds (" + str(NUM_SEEDS) + " seeds)", fontdict={'size':14}) -plt.xlabel("policies", fontdict={'size':14}) + plt.xticks(ticks=list(range(1,len(configs)+1,1)),labels=boxplot_labels) + plt.tight_layout() -plt.xticks(ticks=list(range(1,len(configs)+1,1)),labels=boxplot_labels) -plt.tight_layout() + plt.savefig(result_path + "boxplot.pdf") + plt.cla() -plt.savefig(result_path + "boxplot.pdf") -plt.cla() -for line_key in lineplot_data.keys(): - plt.plot(lineplot_data[line_key], label=str(line_key)) +def lineplotter(lineplot_data,result_path): + for line_key in lineplot_data.keys(): + plt.plot(lineplot_data[line_key], label=str(line_key)) -plt.xticks(ticks= list(range(0,LINE_DATA_POINTS+1,1)), labels = [num for num in range(0,TOTAL_ROUNDS+1,floor(TOTAL_ROUNDS / LINE_DATA_POINTS))]) -plt.legend(fontsize='medium', title='policy') -plt.ylabel("average reward", fontdict={'size':14}) -plt.xlabel("rounds elapsed", fontdict={'size':14}) -plt.tight_layout() + plt.xticks(ticks= list(range(0,LINE_DATA_POINTS+1,1)), labels = [num for num in range(0,TOTAL_ROUNDS+1,floor(TOTAL_ROUNDS / LINE_DATA_POINTS))]) + plt.legend(fontsize='medium', title='policy') + plt.ylabel("average reward", fontdict={'size':14}) + plt.xlabel("rounds elapsed", fontdict={'size':14}) + plt.tight_layout() -plt.savefig(result_path + "lineplot.pdf") + plt.savefig(result_path + "lineplot.pdf") + plt.cla() diff --git a/userfunctions.py b/userfunctions.py index 6fd14e9..551c0f3 100644 --- a/userfunctions.py +++ b/userfunctions.py @@ -1,3 +1,6 @@ +from ast import arg +from scipy.stats import truncnorm +import numpy as np def myfunction(*args): posarg, posarg2 = args @@ -5,4 +8,71 @@ def myfunction(*args): #rq_rate = kwargs["request_rate"] print(posarg) - return (posarg + posarg2) /10 \ No newline at end of file + return (posarg + posarg2) /10 + +def truncated_normal(*args): + lower, upper, mean, stdev = args + + a, b = (lower - mean) / stdev, (upper - mean) / stdev + + return truncnorm.rvs(a,b, loc= mean, scale=stdev) + +def normal(*args): + mean, stdev = args + return np.random.normal(loc=mean,scale=stdev) + +def uniform(*args): + lower, upper = args + return np.random.uniform(low=lower,high=upper) + +def truncate(utility): + bounds = (140,300) + + lower_bound, upper_bound = bounds + + if(utility > upper_bound): utility = upper_bound + + elif(utility < lower_bound): utility = lower_bound + + range = upper_bound - lower_bound + + result = float((utility - lower_bound)/range) + + return result + + +def utilitySWIM(arrival_rate, dimmer, avg_response_time, max_servers, servers): + OPT_REVENUE = 1.5 + BASIC_REVENUE = 1 + SERVER_COST = 10 + RT_THRESH = 0.75 + + ur = arrival_rate * ((1 - dimmer) * BASIC_REVENUE + dimmer * OPT_REVENUE) + uc = SERVER_COST * (max_servers - servers) + urt = 1 - ((avg_response_time-RT_THRESH)/RT_THRESH) + + + UPPER_RT_THRESHOLD = RT_THRESH * 4 + + delta_threshold = UPPER_RT_THRESHOLD-RT_THRESH + + UrtPosFct = (delta_threshold/RT_THRESH) + + urt = None + if(avg_response_time <= UPPER_RT_THRESHOLD): + urt = ((RT_THRESH - avg_response_time)/RT_THRESH) + else: + urt = ((RT_THRESH - UPPER_RT_THRESHOLD)/RT_THRESH) + + urt_final = None + if(avg_response_time <= RT_THRESH): + urt_final = urt*UrtPosFct + else: + urt_final = urt + + revenue_weight = 0.7 + server_weight = 0.3 + utility = urt_final*((revenue_weight*ur)+(server_weight*uc)) + + truncated_reward = truncate(utility) + return truncated_reward \ No newline at end of file