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()
|
---|