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

Last change on this file since 52 was 52, checked in by ruud, 14 months ago

Fixed small issues in domaineditor.

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