source: ANL2022/BIU_agent/BIU_agent.py

Last change on this file was 75, checked in by wouter, 21 months ago

#6 added ANAC2022 parties

File size: 12.1 KB
Line 
1import logging
2import numpy as np
3from pandas import array
4from random import randint
5from sklearn.linear_model import LinearRegression
6from sklearn.ensemble import RandomForestRegressor
7from sklearn.ensemble import VotingRegressor
8from sklearn.neighbors import KNeighborsRegressor
9from time import time
10from typing import cast
11import random
12from geniusweb.actions.Accept import Accept
13from geniusweb.actions.Action import Action
14from geniusweb.actions.Offer import Offer
15from geniusweb.actions.PartyId import PartyId
16from geniusweb.bidspace.AllBidsList import AllBidsList
17from geniusweb.inform.ActionDone import ActionDone
18from geniusweb.inform.Finished import Finished
19from geniusweb.inform.Inform import Inform
20from geniusweb.inform.Settings import Settings
21from geniusweb.inform.YourTurn import YourTurn
22from geniusweb.issuevalue.Bid import Bid
23from geniusweb.issuevalue.Domain import Domain
24from geniusweb.party.Capabilities import Capabilities
25from geniusweb.party.DefaultParty import DefaultParty
26from geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace import (
27 LinearAdditiveUtilitySpace,
28)
29from geniusweb.profileconnection.ProfileConnectionFactory import (
30 ProfileConnectionFactory,
31)
32from geniusweb.progress.ProgressTime import ProgressTime
33from geniusweb.references.Parameters import Parameters
34from tudelft_utilities_logging.ReportToLogger import ReportToLogger
35
36from agents.template_agent.utils.opponent_model import OpponentModel
37
38
39class BIU_agent(DefaultParty):
40 """
41 BIU_agent of a Python geniusweb agent.
42 """
43 def __init__(self):
44 super().__init__()
45 self.logger: ReportToLogger = self.getReporter()
46
47 self.domain: Domain = None
48 self.parameters: Parameters = None
49 self.profile: LinearAdditiveUtilitySpace = None
50 self.progress: ProgressTime = None
51 self.me: PartyId = None
52 self.other: str = None
53 self.settings: Settings = None
54 self.storage_dir: str = None
55
56 self.last_received_bid: Bid = None
57 self.opponent_model: OpponentModel = None
58 self.logger.log(logging.INFO, "party is initialized")
59
60 self.bids_given: list = None
61 self.bids_received: list = None
62 self.proposal_time: float = None
63 self.opponent_bid_times: list = None
64
65 def notifyChange(self, data: Inform):
66 """MUST BE IMPLEMENTED
67 This is the entry point of all interaction with your agent after is has been initialised.
68 How to handle the received data is based on its class type.
69
70 Args:
71 info (Inform): Contains either a request for action or information.
72 """
73
74 # a Settings message is the first message that will be send to your
75 # agent containing all the information about the negotiation session.
76 if isinstance(data, Settings):
77 self.settings = cast(Settings, data)
78 self.me = self.settings.getID()
79
80 # progress towards the deadline has to be tracked manually through the use of the Progress object
81 self.progress = self.settings.getProgress()
82
83 self.parameters = self.settings.getParameters()
84 self.storage_dir = self.parameters.get("storage_dir")
85
86 # the profile contains the preferences of the agent over the domain
87 profile_connection = ProfileConnectionFactory.create(
88 data.getProfile().getURI(), self.getReporter()
89 )
90 self.profile = profile_connection.getProfile()
91 self.domain = self.profile.getDomain()
92 profile_connection.close()
93
94 self.opponent_bid_times = []
95
96 # ActionDone informs you of an action (an offer or an accept)
97 # that is performed by one of the agents (including yourself).
98 elif isinstance(data, ActionDone):
99 action = cast(ActionDone, data).getAction()
100 actor = action.getActor()
101
102 # ignore action if it is our action
103 if actor != self.me:
104 # obtain the name of the opponent, cutting of the position ID.
105 self.other = str(actor).rsplit("_", 1)[0]
106
107 # process action done by opponent
108 self.opponent_action(action)
109 # YourTurn notifies you that it is your turn to act
110 elif isinstance(data, YourTurn):
111 # execute a turn
112 if self.proposal_time is not None:
113 self.opponent_bid_times.append(self.progress.get(time() * 1000) - self.proposal_time)
114 self.my_turn()
115 self.proposal_time = self.progress.get(time() * 1000)
116
117 # Finished will be send if the negotiation has ended (through agreement or deadline)
118 elif isinstance(data, Finished):
119 self.save_data()
120 # terminate the agent MUST BE CALLED
121 self.logger.log(logging.INFO, "party is terminating:")
122 super().terminate()
123 else:
124 self.logger.log(logging.WARNING, "Ignoring unknown info " + str(data))
125
126 def getCapabilities(self) -> Capabilities:
127 """MUST BE IMPLEMENTED
128 Method to indicate to the protocol what the capabilities of this agent are.
129 Leave it as is for the ANL 2022 competition
130
131 Returns:
132 Capabilities: Capabilities representation class
133 """
134 return Capabilities(
135 set(["SAOP"]),
136 set(["geniusweb.profile.utilityspace.LinearAdditive"]),
137 )
138
139 def send_action(self, action: Action):
140 """Sends an action to the opponent(s)
141
142 Args:
143 action (Action): action of this agent
144 """
145 self.getConnection().send(action)
146
147 # give a description of your agent
148 def getDescription(self) -> str:
149 """MUST BE IMPLEMENTED
150 Returns a description of your agent. 1 or 2 sentences.
151
152 Returns:
153 str: Agent description
154 """
155 return "This is a Bar Ilan University agent that learns from the opponent's bids, by using a random forest, a linear regression and a KNN. The agent also using random stochastic to take the offers."
156
157 def opponent_action(self, action):
158 """Process an action that was received from the opponent.
159
160 Args:
161 action (Action): action of opponent
162 """
163 # if it is an offer, set the last received bid
164 if isinstance(action, Offer):
165 # create opponent model if it was not yet initialised
166 if self.opponent_model is None:
167 self.opponent_model = OpponentModel(self.domain)
168
169 bid = cast(Offer, action).getBid()
170
171 # update opponent model with bid
172 self.opponent_model.update(bid)
173 # set bid as last received
174 self.last_received_bid = bid
175
176 def my_turn(self):
177 """This method is called when it is our turn. It should decide upon an action
178 to perform and send this action to the opponent.
179 """
180 if self.accept_condition(self.last_received_bid):
181 action = Accept(self.me, self.last_received_bid)
182 else:
183 t = self.progress.get(time() * 1000)
184 self.logger.log(logging.INFO, t)
185 bid = self.find_bid()
186 if t >= 0.95:
187 t_o = self.regression_opponent_time(self.opponent_bid_times[-10:])
188 self.logger.log(logging.INFO, self.opponent_bid_times)
189 self.logger.log(logging.INFO, t_o)
190 while all(t < 1 - t_o):
191 t = self.progress.get(time() * 1000)
192 action = Offer(self.me, bid)
193
194 self.send_action(action)
195
196 def save_data(self):
197 """This method is called after the negotiation is finished. It can be used to store data
198 for learning capabilities. Note that no extensive calculations can be done within this method.
199 Taking too much time might result in your agent being killed, so use it for storage only.
200 """
201 data = " ".join(str(x) for x in self.opponent_bid_times)
202 # self_dir = "./agents/BIU_agent/data.md"
203 with open(f"{self.storage_dir}/data.md", "w") as f:
204 f.write(data)
205
206 ###########################################################################################
207 ################################## Example methods below ##################################
208 ###########################################################################################
209
210 def accept_condition(self, bid: Bid) -> bool:
211 if bid is None:
212 return False
213
214 # progress of the negotiation session between 0 and 1 (1 is deadline)
215 progress = self.progress.get(time() * 1000)
216
217 # very basic approach that accepts if the offer is valued above 0.7 and
218 # 95% of the time towards the deadline has passed
219 threshold = 0.9
220 if 0 < progress < 0.2:
221 threshold = 0.9
222 if 0.2 < progress <0.3:
223 threshold = 0.8
224 elif 0.3 < progress < 0.5:
225 threshold = 0.6
226 elif 0.5 < progress < 0.9:
227 threshold = 0.5
228 elif 0.9 < progress < 1:
229 threshold = 0.25
230
231 conditions = [
232 self.profile.getUtility(bid) > 0.8
233 ]
234 return all(conditions)
235
236 def find_bid(self) -> Bid:
237 # compose a list of all possible bids
238 domain = self.profile.getDomain()
239 all_bids = AllBidsList(domain)
240
241 best_bid_score = 0.0
242 best_bid = None
243
244 # take 500 attempts to find a bid according to a heuristic score
245 for _ in range(500):
246 bid = all_bids.get(randint(0, all_bids.size() - 1))
247 bid_score = self.score_bid(bid)
248 if bid_score > best_bid_score:
249 best_bid_score, best_bid = bid_score, bid
250
251 return best_bid
252
253 def score_bid(self, bid: Bid, alpha: float = 0.95, eps: float = 0.5) -> float:
254 """Calculate heuristic score for a bid
255
256 Args:
257 bid (Bid): Bid to score
258 alpha (float, optional): Trade-off factor between self interested and
259 altruistic behaviour. Defaults to 0.95.
260 eps (float, optional): Time pressure factor, balances between conceding
261 and Boulware behaviour over time. Defaults to 0.1.
262
263 Returns:
264 float: score
265 """
266
267 # progress = self.progress.get(time() * 1000)
268
269 # our_utility = float(self.profile.getUtility(bid))
270
271 # time_pressure = 1.0 - progress ** (1 / eps)
272 # score = alpha * time_pressure * our_utility
273
274 # if self.opponent_model is not None:
275 # opponent_utility = self.opponent_model.get_predicted_utility(bid)
276 # opponent_score = (1.0 - alpha * time_pressure) * opponent_utility
277 # score += opponent_score
278
279 # return our_utility
280 stochastic_alpha = 0
281 stochastic_eps = 0
282 STOCHASTIC_TRANSITION = random.randint(0,9)
283 if 0 < STOCHASTIC_TRANSITION < 9: # alpha stay the same
284 stochastic_alpha = alpha
285 elif STOCHASTIC_TRANSITION == 0:
286 stochastic_alpha = alpha - eps
287 stochastic_eps = 0.005
288 else: # STOCHASTIC_TRANSITION = 9
289 stochastic_alpha = alpha + eps
290 stochastic_eps = -0.005
291
292 progress = self.progress.get(time() * 1000)
293
294 utility = float(self.profile.getUtility(bid))
295
296 time_pressure = 1.0 - progress ** (1 / eps)
297 score = stochastic_alpha * time_pressure * utility
298
299 if self.opponent_model is not None:
300 opponent_utility = self.opponent_model.get_predicted_utility(bid)
301 opponent_score = (1.0 - stochastic_alpha * time_pressure) * opponent_utility
302 score += opponent_score
303 if utility > 0.994 and stochastic_eps > 0:
304 stochastic_eps = 0
305 if utility < 0.005 and stochastic_eps < 0:
306 stochastic_eps = 0
307 final_score = utility + stochastic_eps
308 return final_score
309
310
311
312 def regression_opponent_time(self, bid_times):
313 r1 = LinearRegression()
314 r2 = RandomForestRegressor(n_estimators=10, random_state=1)
315 r3 = KNeighborsRegressor()
316 X = array(range(len(bid_times))).reshape(-1, 1)
317 y = array(bid_times).reshape(-1, 1)
318 er = VotingRegressor([('lr', r1), ('rf', r2), ('r3', r3)])
319 return er.fit(X, y).predict(X)
Note: See TracBrowser for help on using the repository browser.