[74] | 1 | import curses
|
---|
| 2 | import itertools
|
---|
| 3 | import os
|
---|
| 4 | import random
|
---|
| 5 | import time
|
---|
| 6 | from multiprocessing import Process, Manager
|
---|
| 7 |
|
---|
| 8 | from Group18_NegotiationAssignment_Agent import Group18_NegotiationAssignment_Agent
|
---|
| 9 | from utils.runners import run_tournament
|
---|
| 10 |
|
---|
| 11 | from Group18_NegotiationAssignment_Project.Group18_NegotiationAssignment_Agent.ranker import *
|
---|
| 12 |
|
---|
| 13 | class AgentProcess:
|
---|
| 14 | def __init__(self, id, process, return_dict):
|
---|
| 15 | self.id = id
|
---|
| 16 | self.process = process
|
---|
| 17 | self.return_dict = return_dict
|
---|
| 18 | return_dict[f"status_{self.id}"] = self.get_starting()
|
---|
| 19 |
|
---|
| 20 | def get_score(self):
|
---|
| 21 | return return_dict[f"score_{self.id}"]
|
---|
| 22 |
|
---|
| 23 | def get_status(self):
|
---|
| 24 | return return_dict[f"status_{self.id}"]
|
---|
| 25 |
|
---|
| 26 | @staticmethod
|
---|
| 27 | def get_finished():
|
---|
| 28 | return f"Process --: finished!"
|
---|
| 29 |
|
---|
| 30 | @staticmethod
|
---|
| 31 | def get_starting():
|
---|
| 32 | return f"Process --: starting process..."
|
---|
| 33 |
|
---|
| 34 | @staticmethod
|
---|
| 35 | def get_waiting():
|
---|
| 36 | return f"Process --: waiting for process..."
|
---|
| 37 |
|
---|
| 38 | def start(self):
|
---|
| 39 | return self.process.start()
|
---|
| 40 |
|
---|
| 41 | def join(self):
|
---|
| 42 | return self.process.join()
|
---|
| 43 |
|
---|
| 44 | def is_alive(self):
|
---|
| 45 | return self.process.is_alive()
|
---|
| 46 |
|
---|
| 47 |
|
---|
| 48 | class HiddenPrints:
|
---|
| 49 | def __enter__(self):
|
---|
| 50 | self._original_stdout = sys.stdout
|
---|
| 51 | sys.stdout = open(os.devnull, 'w')
|
---|
| 52 |
|
---|
| 53 | def __exit__(self, exc_type, exc_val, exc_tb):
|
---|
| 54 | sys.stdout.close()
|
---|
| 55 | sys.stdout = self._original_stdout
|
---|
| 56 |
|
---|
| 57 |
|
---|
| 58 | def mutant_filename(i, my_agent):
|
---|
| 59 | split_agent_string = my_agent.split('.')
|
---|
| 60 | split_agent_string[2] = split_agent_string[2] + f"_calibration_temp_{i}"
|
---|
| 61 | return '.'.join(split_agent_string)
|
---|
| 62 |
|
---|
| 63 |
|
---|
| 64 | def agent_worker(i, my_file, my_agent, deadline_rounds, sample_size, num_domains, agent_pool, domains, thresholds,
|
---|
| 65 | counter, start, return_dict):
|
---|
| 66 | proc_num = i + 1
|
---|
| 67 | start_agent = time.time()
|
---|
| 68 | out = open(my_file[:len(my_file) - 3] + f"_calibration_temp_{i}.py", 'w')
|
---|
| 69 | with open(my_file, "r") as f:
|
---|
| 70 | lines = f.readlines()
|
---|
| 71 | for index, line in enumerate(lines):
|
---|
| 72 | if "self.thresholds: [float] = [" in line:
|
---|
| 73 | lines[index] = f" self.thresholds: [float] = {thresholds[i]}\n"
|
---|
| 74 | break
|
---|
| 75 | out.writelines(lines)
|
---|
| 76 | out.close()
|
---|
| 77 | new_agent = mutant_filename(i, my_agent)
|
---|
| 78 | results = []
|
---|
| 79 | agent_iter = itertools.cycle(agent_pool)
|
---|
| 80 | for j in range(sample_size):
|
---|
| 81 | pick = [new_agent, next(agent_iter)]
|
---|
| 82 | settings = {
|
---|
| 83 | "agents": pick,
|
---|
| 84 | "profile_sets": random.sample(domains, k=num_domains),
|
---|
| 85 | "deadline_rounds": deadline_rounds,
|
---|
| 86 | }
|
---|
| 87 | with HiddenPrints():
|
---|
| 88 | results_trace, results_summary = run_tournament(settings, new_agent, verbose=False)
|
---|
| 89 | results.append(results_summary)
|
---|
| 90 | # add the result of the process to the return data structure
|
---|
| 91 | return_dict[f"status_{i}"] = \
|
---|
| 92 | f"Thresholds {int(proc_num):-2}: [{''.join(['=' if k <= j else '_' for k in range(sample_size)])}] |" \
|
---|
| 93 | f" process runtime: {int(time.time() - start_agent):-3}s"
|
---|
| 94 | return_dict[f"status_{i}"] = AgentProcess.get_finished()
|
---|
| 95 | score = metric(results)
|
---|
| 96 | return_dict[f"score_{i}"] = score[new_agent.split('.')[-1]]
|
---|
| 97 |
|
---|
| 98 |
|
---|
| 99 | def report_progress(stdscr, outs):
|
---|
| 100 | stdscr.clear()
|
---|
| 101 | # If the programme crashes with "_curses.error: addwstr() returned ERR", resize the console bigger
|
---|
| 102 | for n, out in enumerate(outs):
|
---|
| 103 | stdscr.addstr(n, 0, out)
|
---|
| 104 | stdscr.refresh()
|
---|
| 105 |
|
---|
| 106 |
|
---|
| 107 | def refresh_console_output(stdscr, active_processes, num_processes_left, num_total, epoch, number_of_epochs):
|
---|
| 108 | output = [f"[Epoch {epoch+1} / {number_of_epochs}] Processes [remaining/active/finished/total]: "
|
---|
| 109 | f"[{num_processes_left}/{len(active_processes)}/{num_total - num_processes_left - len(active_processes)}/{num_total}]"]
|
---|
| 110 | for id, process in enumerate(active_processes):
|
---|
| 111 | try:
|
---|
| 112 | if process:
|
---|
| 113 | output.append(process.get_status())
|
---|
| 114 | else:
|
---|
| 115 | raise KeyError
|
---|
| 116 | except KeyError:
|
---|
| 117 | output.append(AgentProcess.get_waiting())
|
---|
| 118 | output.append(f"Overall runtime: {int(time.time() - start):-3}s")
|
---|
| 119 | report_progress(stdscr, output)
|
---|
| 120 |
|
---|
| 121 |
|
---|
| 122 | def pick_thresholds(number_of_agents, reff):
|
---|
| 123 | thresholds = []
|
---|
| 124 | number_of_thresholds = len(reff.thresholds)
|
---|
| 125 | for _ in range(number_of_agents):
|
---|
| 126 | temp = []
|
---|
| 127 | i = 0
|
---|
| 128 | while len(temp) != number_of_thresholds:
|
---|
| 129 | pick = random.uniform(reff.threshold_checks[i][0], reff.threshold_checks[i][1])
|
---|
| 130 | temp.append(pick)
|
---|
| 131 | i += 1
|
---|
| 132 | thresholds.append(temp)
|
---|
| 133 | return thresholds
|
---|
| 134 |
|
---|
| 135 |
|
---|
| 136 | if __name__ == '__main__':
|
---|
| 137 | # create results directory if it does not exist
|
---|
| 138 | if not os.path.exists("results"):
|
---|
| 139 | os.mkdir("results")
|
---|
| 140 |
|
---|
| 141 | agent_pool = {
|
---|
| 142 | "Shreker": "agents.shreker.shreker.Shreker",
|
---|
| 143 | "AveragedTitForTat": "agents.averaged_tit_for_tat_agent.averaged_tit_for_tat_agent.AveragedTitForTat",
|
---|
| 144 | "TradeOffAgent": "agents.trade_off_agent.trade_off_agent.TradeOffAgent",
|
---|
| 145 | "SocialWelfareAgent": "agents.social_welfare_agent.social_welfare_agent.SocialWelfareAgent",
|
---|
| 146 | "BoulwareAgent": "agents.boulware_agent.boulware_agent.BoulwareAgent",
|
---|
| 147 | "ConcederAgent": "agents.conceder_agent.conceder_agent.ConcederAgent",
|
---|
| 148 | "HardlinerAgent": "agents.hardliner_agent.hardliner_agent.HardlinerAgent",
|
---|
| 149 | "ConcedeOneAgent": "agents.concede_one_agent.concede_one_agent.ConcedeOneAgent",
|
---|
| 150 | "LinearAgent": "agents.linear_agent.linear_agent.LinearAgent",
|
---|
| 151 | "RandomAgent": "agents.random_agent.random_agent.RandomAgent",
|
---|
| 152 | "TimeDependentAgent": "agents.time_dependent_agent.time_dependent_agent.TimeDependentAgent",
|
---|
| 153 | "AgreeableAgent": "agents.agreeable_agent.agreeable_agent.AgreeableAgent"
|
---|
| 154 | }
|
---|
| 155 | domains = [
|
---|
| 156 | ["domains/domain00/profileA.json", "domains/domain00/profileB.json"],
|
---|
| 157 | ["domains/domain01/profileA.json", "domains/domain01/profileB.json"],
|
---|
| 158 | ["domains/domain02/profileA.json", "domains/domain02/profileB.json"],
|
---|
| 159 | ["domains/domain03/profileA.json", "domains/domain03/profileB.json"],
|
---|
| 160 | ["domains/domain04/profileA.json", "domains/domain04/profileB.json"],
|
---|
| 161 | ["domains/domain05/profileA.json", "domains/domain05/profileB.json"],
|
---|
| 162 | ["domains/domain06/profileA.json", "domains/domain06/profileB.json"],
|
---|
| 163 | ["domains/domain07/profileA.json", "domains/domain07/profileB.json"],
|
---|
| 164 | ["domains/domain08/profileA.json", "domains/domain08/profileB.json"],
|
---|
| 165 | ["domains/domain09/profileA.json", "domains/domain09/profileB.json"],
|
---|
| 166 | ]
|
---|
| 167 | sample_size = 50 # How many times to run your agent against another random agent in several random domains
|
---|
| 168 | number_of_agents = 20 # How many times to generate random thresholds for your agent
|
---|
| 169 | max_num_processes = 10 # How many processes to run at once
|
---|
| 170 | num_domains = 10 # How many unique domains within which to run the agents.
|
---|
| 171 | number_of_epochs = 5 # How many epochs between sets of agents will take place
|
---|
| 172 | # All data needed to create an agent mutant
|
---|
| 173 | deadline_rounds = 100
|
---|
| 174 | references = {
|
---|
| 175 | "agents.shreker.shreker.Shreker": [Group18_NegotiationAssignment_Agent(), "agents/shreker/shreker.py"],
|
---|
| 176 | }
|
---|
| 177 |
|
---|
| 178 | counter = 1
|
---|
| 179 | start = time.time()
|
---|
| 180 | manager = Manager()
|
---|
| 181 | return_dict = manager.dict() # For getting scores out of the processes
|
---|
| 182 | results = None
|
---|
| 183 |
|
---|
| 184 | # Displaying multiline output
|
---|
| 185 | stdscr = curses.initscr()
|
---|
| 186 | curses.noecho()
|
---|
| 187 | curses.cbreak()
|
---|
| 188 | # Iterate over all epochs
|
---|
| 189 | for epoch in range(number_of_epochs):
|
---|
| 190 | # Fill process queue with all processes
|
---|
| 191 | process_queue = []
|
---|
| 192 | ranking = dict()
|
---|
| 193 | id_to_agent = dict()
|
---|
| 194 |
|
---|
| 195 | for index, my_agent in enumerate(references):
|
---|
| 196 | thresholds = pick_thresholds(len(references) * number_of_agents, references[my_agent][0])
|
---|
| 197 | for n in range(number_of_agents):
|
---|
| 198 | id_to_agent[index * number_of_agents + n] = mutant_filename(index * number_of_agents + n, my_agent)
|
---|
| 199 | python_process = Process(
|
---|
| 200 | target=agent_worker,
|
---|
| 201 | args=(index * number_of_agents + n, references[my_agent][1], my_agent, deadline_rounds, sample_size,
|
---|
| 202 | num_domains, list(agent_pool.values()), domains, thresholds, counter, start, return_dict)
|
---|
| 203 | )
|
---|
| 204 | agent_process = AgentProcess(index * number_of_agents + n, python_process, return_dict)
|
---|
| 205 | process_queue.append(agent_process)
|
---|
| 206 |
|
---|
| 207 | active_processes = np.empty(np.min([max_num_processes, len(references) * number_of_agents]), dtype=Process)
|
---|
| 208 | process_queue.reverse()
|
---|
| 209 | processes = []
|
---|
| 210 | while len(process_queue) != 0 or any(map(lambda p: not p or p.is_alive(), active_processes)):
|
---|
| 211 | refresh_console_output(stdscr, active_processes, len(process_queue), number_of_agents, epoch, number_of_epochs)
|
---|
| 212 | for i in range(10):
|
---|
| 213 | time.sleep(0.5)
|
---|
| 214 | if len(process_queue) != 0:
|
---|
| 215 | for i in range(len(active_processes)):
|
---|
| 216 | if not active_processes[i] or not active_processes[i].is_alive():
|
---|
| 217 | next_process = process_queue.pop()
|
---|
| 218 | next_process.start()
|
---|
| 219 | active_processes[i] = next_process
|
---|
| 220 | processes.append(next_process)
|
---|
| 221 | break
|
---|
| 222 | for process in processes:
|
---|
| 223 | ranking[id_to_agent[process.id]] = process.get_score()
|
---|
| 224 | # Prepare agent pool for the next epoch by replacing with calibrated agents
|
---|
| 225 | results = sorted([agent for agent in ranking], key=lambda agent: ranking[agent])[:10]
|
---|
| 226 | visited = []
|
---|
| 227 | for agent in results:
|
---|
| 228 | if agent in visited:
|
---|
| 229 | continue
|
---|
| 230 | temp = agent.split('.')
|
---|
| 231 | my_file = '/'.join(temp[:-1]) + '.py'
|
---|
| 232 | out = open(temp[0] + '/' + temp[1] + f"/{temp[-1]}_calibration_saved_epoch_{epoch}.py", 'w')
|
---|
| 233 | with open(my_file, "r") as f:
|
---|
| 234 | lines = f.readlines()
|
---|
| 235 | out.writelines(lines)
|
---|
| 236 | out.close()
|
---|
| 237 | temp[-2] = f"{temp[-1]}_calibration_saved_epoch_{epoch}"
|
---|
| 238 | agent_pool[agent.split('.')[-1]] = '.'.join(temp)
|
---|
| 239 | visited.append(agent)
|
---|
| 240 |
|
---|
| 241 | curses.echo()
|
---|
| 242 | curses.nocbreak()
|
---|
| 243 | curses.endwin()
|
---|
| 244 |
|
---|
| 245 | print(f"Total time taken: {int(time.time() - start):-3}s")
|
---|
| 246 | # Pick the top 10 scores
|
---|
| 247 | time_str = time.strftime("%Y%m%d-%H%M%S")
|
---|
| 248 | w = open(f"metric_{time_str}.log", "w")
|
---|
| 249 | w.write(f"Top 10 metric agents: \n")
|
---|
| 250 | [w.write(str(tup) + "\n") for tup in results]
|
---|
| 251 | w.close()
|
---|