source: exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java@ 41

Last change on this file since 41 was 41, checked in by bart, 3 years ago

Reduced memory need of websockets.

File size: 10.5 KB
Line 
1package geniusweb.exampleparties.timedependentparty;
2
3import java.io.IOException;
4import java.math.BigDecimal;
5import java.math.BigInteger;
6import java.math.RoundingMode;
7import java.util.Arrays;
8import java.util.Collections;
9import java.util.HashSet;
10import java.util.Random;
11import java.util.Set;
12import java.util.logging.Level;
13import java.util.stream.Collectors;
14
15import geniusweb.actions.Accept;
16import geniusweb.actions.Action;
17import geniusweb.actions.LearningDone;
18import geniusweb.actions.Offer;
19import geniusweb.actions.PartyId;
20import geniusweb.actions.Vote;
21import geniusweb.actions.Votes;
22import geniusweb.inform.ActionDone;
23import geniusweb.inform.Finished;
24import geniusweb.inform.Inform;
25import geniusweb.inform.OptIn;
26import geniusweb.inform.Settings;
27import geniusweb.inform.Voting;
28import geniusweb.inform.YourTurn;
29import geniusweb.issuevalue.Bid;
30import geniusweb.party.Capabilities;
31import geniusweb.party.DefaultParty;
32import geniusweb.profile.Profile;
33import geniusweb.profile.utilityspace.LinearAdditive;
34import geniusweb.profile.utilityspace.UtilitySpace;
35import geniusweb.profileconnection.ProfileConnectionFactory;
36import geniusweb.profileconnection.ProfileInterface;
37import geniusweb.progress.Progress;
38import geniusweb.progress.ProgressRounds;
39import tudelft.utilities.immutablelist.ImmutableList;
40import tudelft.utilities.logging.Reporter;
41
42/**
43 * General time dependent party. This is a simplistic implementation that does
44 * brute-force search through the bidspace and can handle bidspace sizes up to
45 * 2^31 (approx 1 billion bids). It may take excessive time and run out of time
46 * on bidspaces > 10000 bids. In special cases it may even run out of memory,
47 *
48 * <p>
49 * Supports parameters as follows
50 * <table>
51 * <caption>parameters</caption>
52 * <tr>
53 * <td>e</td>
54 * <td>e determines how fast the party makes concessions with time. Typically
55 * around 1. 0 means no concession, 1 linear concession, &gt;1 faster than
56 * linear concession.</td>
57 * </tr>
58 *
59 * <tr>
60 * <td>minPower</td>
61 * <td>This value is used as minPower for placed {@link Vote}s. Default value is
62 * 1.</td>
63 * </tr>
64 *
65 * <tr>
66 * <td>maxPower</td>
67 * <td>This value is used as maxPower for placed {@link Vote}s. Default value is
68 * infinity.</td>
69 * </tr>
70 *
71 * <tr>
72 * <td>delay</td>
73 * <td>The average time in seconds to wait before responding to a YourTurn. The
74 * actual waiting time will be random in [0.5*time, 1.5*time]. This can be used
75 * to simulate human users that take thinking time.</td>
76 * </tr>
77 *
78 * </table>
79 * <p>
80 * TimeDependentParty requires a {@link UtilitySpace}
81 */
82public class TimeDependentParty extends DefaultParty {
83
84 private ProfileInterface profileint = null;
85 private LinearAdditive utilspace = null; // last received space
86 private PartyId me;
87 private Progress progress;
88 private Bid lastReceivedBid = null;
89 private ExtendedUtilSpace extendedspace;
90 private double e = 1.2;
91 private Votes lastvotes;
92 private Settings settings;
93
94 public TimeDependentParty() {
95 super();
96 }
97
98 public TimeDependentParty(Reporter reporter) {
99 super(reporter); // for debugging
100 }
101
102 @Override
103 public Capabilities getCapabilities() {
104 return new Capabilities(
105 new HashSet<>(Arrays.asList("SAOP", "MOPAC", "Learn")),
106 Collections.singleton(LinearAdditive.class));
107 }
108
109 @Override
110 public void notifyChange(Inform info) {
111 try {
112 if (info instanceof Settings) {
113 settings = (Settings) info;
114 this.me = settings.getID();
115 this.progress = settings.getProgress();
116 Object newe = settings.getParameters().get("e");
117 if (newe != null) {
118 if (newe instanceof Double) {
119 this.e = (Double) newe;
120 } else {
121 getReporter().log(Level.WARNING,
122 "parameter e should be Double but found "
123 + newe);
124 }
125 }
126 String protocol = settings.getProtocol().getURI().getPath();
127 if ("Learn".equals(protocol)) {
128 getConnection().send(new LearningDone(me));
129 } else {
130 this.profileint = ProfileConnectionFactory.create(
131 settings.getProfile().getURI(), getReporter());
132 }
133
134 } else if (info instanceof ActionDone) {
135 Action otheract = ((ActionDone) info).getAction();
136 if (otheract instanceof Offer) {
137 lastReceivedBid = ((Offer) otheract).getBid();
138 }
139 } else if (info instanceof YourTurn) {
140 delayResponse();
141 myTurn();
142 } else if (info instanceof Finished) {
143 getReporter().log(Level.INFO, "Final ourcome:" + info);
144 terminate(); // stop this party and free resources.
145 } else if (info instanceof Voting) {
146 lastvotes = vote((Voting) info);
147 getConnection().send(lastvotes);
148 } else if (info instanceof OptIn) {
149 getConnection().send(lastvotes);
150 }
151 } catch (Exception ex) {
152 getReporter().log(Level.SEVERE, "Failed to handle info", ex);
153 }
154 updateRound(info);
155 }
156
157 /**
158 * @return the E value that controls the party's behaviour. Depending on the
159 * value of e, extreme sets show clearly different patterns of
160 * behaviour [1]:
161 *
162 * 1. Boulware: For this strategy e &lt; 1 and the initial offer is
163 * maintained till time is almost exhausted, when the agent concedes
164 * up to its reservation value.
165 *
166 * 2. Conceder: For this strategy e &gt; 1 and the agent goes to its
167 * reservation value very quickly.
168 *
169 * 3. When e = 1, the price is increased linearly.
170 *
171 * 4. When e = 0, the agent plays hardball.
172 */
173 public double getE() {
174 return e;
175 }
176
177 @Override
178 public String getDescription() {
179 return "Time-dependent conceder. Aims at utility u(t) = scale * t^(1/e) "
180 + "where t is the time (0=start, 1=end), e is the concession speed parameter (default 1.1), and scale such that u(0)=minimum and "
181 + "u(1) = maximum possible utility. Parameters minPower (default 1) and maxPower (default infinity) are used "
182 + "when voting";
183 }
184
185 @Override
186 public void terminate() {
187 super.terminate();
188 if (this.profileint != null) {
189 this.profileint.close();
190 this.profileint = null;
191 }
192 }
193
194 /******************* private support funcs ************************/
195
196 /**
197 * Update {@link #progress}, depending on the protocol and last received
198 * {@link Inform}
199 *
200 * @param info the received info.
201 */
202 private void updateRound(Inform info) {
203 if (settings == null) // not yet initialized
204 return;
205 String protocol = settings.getProtocol().getURI().getPath();
206
207 switch (protocol) {
208 case "SAOP":
209 case "SHAOP":
210 if (!(info instanceof YourTurn))
211 return;
212 break;
213 case "MOPAC":
214 if (!(info instanceof OptIn))
215 return;
216 break;
217 default:
218 return;
219 }
220 // if we get here, round must be increased.
221 if (progress instanceof ProgressRounds) {
222 progress = ((ProgressRounds) progress).advance();
223 }
224
225 }
226
227 private void myTurn() throws IOException {
228 updateUtilSpace();
229 Bid bid = makeBid();
230
231 Action myAction;
232 if (bid == null || (lastReceivedBid != null
233 && utilspace.getUtility(lastReceivedBid)
234 .compareTo(utilspace.getUtility(bid)) >= 0)) {
235 // if bid==null we failed to suggest next bid.
236 myAction = new Accept(me, lastReceivedBid);
237 } else {
238 myAction = new Offer(me, bid);
239 }
240 getConnection().send(myAction);
241
242 }
243
244 private LinearAdditive updateUtilSpace() throws IOException {
245 Profile newutilspace = profileint.getProfile();
246 if (!newutilspace.equals(utilspace)) {
247 utilspace = (LinearAdditive) newutilspace;
248 extendedspace = new ExtendedUtilSpace(utilspace);
249 }
250 return utilspace;
251 }
252
253 /**
254 * @return next possible bid with current target utility, or null if no such
255 * bid.
256 */
257 private Bid makeBid() {
258 double time = progress.get(System.currentTimeMillis());
259
260 BigDecimal utilityGoal = getUtilityGoal(time, getE(),
261 extendedspace.getMin(), extendedspace.getMax());
262 ImmutableList<Bid> options = extendedspace.getBids(utilityGoal);
263 if (options.size() == BigInteger.ZERO) {
264 // if we can't find good bid, get max util bid....
265 options = extendedspace.getBids(extendedspace.getMax());
266 }
267 // pick a random one.
268 return options.get(new Random().nextInt(options.size().intValue()));
269
270 }
271
272 /**
273 *
274 * @param t the time in [0,1] where 0 means start of nego and 1 the
275 * end of nego (absolute time/round limit)
276 * @param e the e value that determinses how fast the party makes
277 * concessions with time. Typically around 1. 0 means no
278 * concession, 1 linear concession, &gt;1 faster than linear
279 * concession.
280 * @param minUtil the minimum utility possible in our profile
281 * @param maxUtil the maximum utility possible in our profile
282 * @return the utility goal for this time and e value
283 */
284 protected BigDecimal getUtilityGoal(double t, double e, BigDecimal minUtil,
285 BigDecimal maxUtil) {
286
287 BigDecimal ft1 = BigDecimal.ONE;
288 if (e != 0)
289 ft1 = BigDecimal.valueOf(1 - Math.pow(t, 1 / e)).setScale(6,
290 RoundingMode.HALF_UP);
291 return minUtil.add((maxUtil.subtract(minUtil).multiply(ft1)))
292 .min(maxUtil).max(minUtil);
293 }
294
295 /**
296 * @param voting the {@link Voting} object containing the options
297 *
298 * @return our next Votes.
299 */
300 private Votes vote(Voting voting) throws IOException {
301 Object val = settings.getParameters().get("minPower");
302 // max utility requires smallest possible group/power
303 Integer minpower = (val instanceof Integer) ? (Integer) val : 1;
304 val = settings.getParameters().get("maxPower");
305 Integer maxpower = (val instanceof Integer) ? (Integer) val
306 : Integer.MAX_VALUE;
307
308 Set<Vote> votes = voting.getOffers().stream().distinct()
309 .filter(offer -> isGood(offer.getBid()))
310 .map(offer -> new Vote(me, offer.getBid(), minpower, maxpower))
311 .collect(Collectors.toSet());
312 return new Votes(me, votes);
313 }
314
315 /**
316 * @param bid the bid to check
317 * @return true iff bid is good for us.
318 */
319 private boolean isGood(Bid bid) {
320 if (bid == null || profileint == null)
321 return false;
322 Profile profile;
323 try {
324 profile = profileint.getProfile();
325 } catch (IOException ex) {
326 throw new IllegalStateException(ex);
327 }
328 // the profile MUST contain UtilitySpace
329 double time = progress.get(System.currentTimeMillis());
330 return ((UtilitySpace) profile).getUtility(bid)
331 .compareTo(getUtilityGoal(time, getE(), extendedspace.getMin(),
332 extendedspace.getMax())) >= 0;
333
334 }
335
336 /**
337 * Do random delay of provided delay in seconds, randomized by factor in
338 * [0.5, 1.5]. Does not delay if set to 0.
339 *
340 * @throws InterruptedException
341 */
342 private void delayResponse() throws InterruptedException {
343 Double delay = settings.getParameters().getDouble("delay", 0d, 0d,
344 10000000d);
345 if (delay > 0) {
346 Thread.sleep(Math.round((delay * 1000. * (0.5 + Math.random()))));
347 }
348 }
349}
Note: See TracBrowser for help on using the repository browser.