1 | package agents.anac.y2019.kakesoba;
|
---|
2 |
|
---|
3 | import genius.core.Bid;
|
---|
4 | import genius.core.BidIterator;
|
---|
5 | import genius.core.Domain;
|
---|
6 | import genius.core.actions.*;
|
---|
7 | import genius.core.issue.Issue;
|
---|
8 | import genius.core.issue.IssueDiscrete;
|
---|
9 | import genius.core.issue.ValueDiscrete;
|
---|
10 | import genius.core.parties.AbstractNegotiationParty;
|
---|
11 | import genius.core.parties.NegotiationInfo;
|
---|
12 | import genius.core.uncertainty.AdditiveUtilitySpaceFactory;
|
---|
13 | import genius.core.uncertainty.BidRanking;
|
---|
14 | import genius.core.utility.AbstractUtilitySpace;
|
---|
15 | import genius.core.utility.AdditiveUtilitySpace;
|
---|
16 | import genius.core.utility.EvaluatorDiscrete;
|
---|
17 | import genius.core.timeline.Timeline.Type;
|
---|
18 |
|
---|
19 | import java.util.*;
|
---|
20 |
|
---|
21 | /**
|
---|
22 | * KakeSoba
|
---|
23 | */
|
---|
24 | public class KakeSoba extends AbstractNegotiationParty {
|
---|
25 | private int nrChosenActions = 0; // number of times chosenAction was called.
|
---|
26 | private boolean isFirstParty;
|
---|
27 | private boolean isDebug = false;
|
---|
28 | private boolean isInvalid = false;
|
---|
29 | private Map<String, Map<String, Double>> counts = new HashMap<String, Map<String, Double>>();
|
---|
30 |
|
---|
31 | @Override
|
---|
32 | public void init(NegotiationInfo info) {
|
---|
33 | super.init(info);
|
---|
34 |
|
---|
35 | if (!(utilitySpace instanceof AdditiveUtilitySpace)) {
|
---|
36 | System.out.println("This agent displays more interesting behavior with a additive utility function; now it simply generates random bids.");
|
---|
37 | isInvalid = true;
|
---|
38 | return;
|
---|
39 | }
|
---|
40 |
|
---|
41 | AdditiveUtilitySpace additiveUtilitySpace = (AdditiveUtilitySpace) utilitySpace;
|
---|
42 | for (Issue issue : additiveUtilitySpace.getDomain().getIssues()) {
|
---|
43 | if (!(issue instanceof IssueDiscrete)) {
|
---|
44 | System.out.println("This agent displays more interesting behavior with a discrete issue domain; now it simply generates random bids.");
|
---|
45 | isInvalid = true;
|
---|
46 | return;
|
---|
47 | }
|
---|
48 | Map<String, Double> h = new HashMap<String, Double>();
|
---|
49 | for (ValueDiscrete value : ((IssueDiscrete)issue).getValues()) {
|
---|
50 | h.put(value.getValue(), 0.0D);
|
---|
51 | }
|
---|
52 | counts.put(issue.getName(), h);
|
---|
53 | }
|
---|
54 | }
|
---|
55 |
|
---|
56 | public Action chooseAction(List<Class<? extends Action>> validActions) {
|
---|
57 | double t = this.getTimeLine().getTime();
|
---|
58 | nrChosenActions++;
|
---|
59 |
|
---|
60 | if (this.isInvalid) {
|
---|
61 | return new Offer(getPartyId(), generateRandomBid());
|
---|
62 | }
|
---|
63 |
|
---|
64 | if (!(getLastReceivedAction() instanceof Offer) && !(getLastReceivedAction() instanceof Accept)){
|
---|
65 | isFirstParty = true;
|
---|
66 | }
|
---|
67 |
|
---|
68 | if (getLastReceivedAction() instanceof Offer) {
|
---|
69 | Bid receivedBid = ((Offer) getLastReceivedAction()).getBid();
|
---|
70 |
|
---|
71 | if (isAcceptableBid(receivedBid, t)) {
|
---|
72 | return new Accept(getPartyId(), receivedBid);
|
---|
73 | }
|
---|
74 |
|
---|
75 | if (timeline.getType() == Type.Rounds) {
|
---|
76 | if (!isFirstParty && timeline.getCurrentTime() >= timeline.getTotalTime() - 1) {
|
---|
77 | if (this.utilitySpace.getUtilityWithDiscount(receivedBid, t) > this.utilitySpace.getReservationValueWithDiscount(t)) {
|
---|
78 | return new Accept(getPartyId(), receivedBid);
|
---|
79 | }
|
---|
80 | }
|
---|
81 | }
|
---|
82 | }
|
---|
83 |
|
---|
84 | if (isDebug) {
|
---|
85 | System.out.println(getPartyId() + ": " + getError());
|
---|
86 | System.out.println(getPartyId() + ": " + counts);
|
---|
87 | System.out.println(getPartyId() + ": " + utilitySpace);
|
---|
88 | }
|
---|
89 |
|
---|
90 | if(nrChosenActions == 1) {
|
---|
91 | try {
|
---|
92 | Bid bid = getUtilitySpace().getMaxUtilityBid();
|
---|
93 | countBid(bid);
|
---|
94 | return new Offer(getPartyId(), bid);
|
---|
95 | } catch (Exception e) {
|
---|
96 | e.printStackTrace();
|
---|
97 | }
|
---|
98 | }
|
---|
99 |
|
---|
100 | Bid bid = generateBid(t);
|
---|
101 | countBid(bid);
|
---|
102 | return new Offer(getPartyId(), bid);
|
---|
103 | }
|
---|
104 |
|
---|
105 | private void countBid(Bid bid, double weight) {
|
---|
106 | for (Issue issue : bid.getIssues()) {
|
---|
107 | Map<String, Double> h = counts.get(issue.getName());
|
---|
108 | String value = ((ValueDiscrete) bid.getValue(issue)).getValue();
|
---|
109 | h.put(value, h.get(value) + weight);
|
---|
110 | }
|
---|
111 | }
|
---|
112 |
|
---|
113 | private void countBid(Bid bid) { countBid(bid, 1.0D); }
|
---|
114 |
|
---|
115 | private double getError() {
|
---|
116 | double error = 0.0D;
|
---|
117 |
|
---|
118 | AdditiveUtilitySpace additiveUtilitySpace = (AdditiveUtilitySpace) utilitySpace;
|
---|
119 | for (Issue issue : additiveUtilitySpace.getDomain().getIssues()) {
|
---|
120 | Map<String, Double> h = counts.get(issue.getName());
|
---|
121 | EvaluatorDiscrete evaluator = (EvaluatorDiscrete) additiveUtilitySpace.getEvaluator(issue);
|
---|
122 | double max = h.values().stream().mapToDouble(Double::doubleValue).max().getAsDouble();
|
---|
123 | for (ValueDiscrete value : ((IssueDiscrete) issue).getValues()) {
|
---|
124 | error += Math.abs((h.get(value.getValue()) / max) - evaluator.getDoubleValue(value));
|
---|
125 | }
|
---|
126 | }
|
---|
127 | return error;
|
---|
128 | }
|
---|
129 |
|
---|
130 | private double getErrorWithNewBid(Bid bid) {
|
---|
131 | double error;
|
---|
132 |
|
---|
133 | countBid(bid, 1.0D);
|
---|
134 | error = getError();
|
---|
135 | countBid(bid, -1.0D);
|
---|
136 |
|
---|
137 | return error;
|
---|
138 | }
|
---|
139 |
|
---|
140 | private Bid generateBid(double t) {
|
---|
141 | BidIterator bidIterator = new BidIterator(this.getDomain());
|
---|
142 |
|
---|
143 | Bid bestBid;
|
---|
144 | try {
|
---|
145 | bestBid = utilitySpace.getMaxUtilityBid();
|
---|
146 | } catch (Exception e) {
|
---|
147 | bestBid = bidIterator.next();
|
---|
148 | }
|
---|
149 | double minError = getErrorWithNewBid(bestBid);
|
---|
150 | while (bidIterator.hasNext()) {
|
---|
151 | Bid bid = bidIterator.next();
|
---|
152 | if (!isProposableBid(bid, t)) {
|
---|
153 | continue;
|
---|
154 | }
|
---|
155 | double error = getErrorWithNewBid(bid);
|
---|
156 | if (error < minError) {
|
---|
157 | bestBid = bid;
|
---|
158 | minError = error;
|
---|
159 | }
|
---|
160 | }
|
---|
161 | return bestBid;
|
---|
162 | }
|
---|
163 |
|
---|
164 | private boolean isProposableBid(Bid bid, double t) {
|
---|
165 | double utility ;
|
---|
166 | try {
|
---|
167 | utility = utilitySpace.getUtilityWithDiscount(bid, t);
|
---|
168 | } catch (Exception e) {
|
---|
169 | utility = -1.0D;
|
---|
170 | }
|
---|
171 |
|
---|
172 | return getLowerBound(t) <= utility && utility <= getUpperBound(t) && utility >= utilitySpace.getReservationValueWithDiscount(t);
|
---|
173 | }
|
---|
174 |
|
---|
175 | private boolean isAcceptableBid(Bid bid, double t) {
|
---|
176 | double utility ;
|
---|
177 | try {
|
---|
178 | utility = utilitySpace.getUtilityWithDiscount(bid, t);
|
---|
179 | } catch (Exception e) {
|
---|
180 | utility = -1.0D;
|
---|
181 | }
|
---|
182 |
|
---|
183 | return getLowerBound(t) <= utility && utility >= utilitySpace.getReservationValueWithDiscount(t);
|
---|
184 | }
|
---|
185 |
|
---|
186 | private double getUpperBound(double t) {
|
---|
187 | return 1.0D;
|
---|
188 | }
|
---|
189 |
|
---|
190 | private double getLowerBound(double t) {
|
---|
191 | return 0.85D;
|
---|
192 | }
|
---|
193 |
|
---|
194 | public AbstractUtilitySpace estimateUtilitySpace() {
|
---|
195 | List<Movement> TabuList = new ArrayList<Movement>();
|
---|
196 | AdditiveUtilitySpace additiveUtilitySpace = generateRandomUtilitySpace();
|
---|
197 | AdditiveUtilitySpace hallOfFame = additiveUtilitySpace;
|
---|
198 | double hallOfFameScore = getScore(hallOfFame, false);
|
---|
199 |
|
---|
200 | if (this.isInvalid) {
|
---|
201 | return defaultUtilitySpaceEstimator(getDomain(), userModel);
|
---|
202 | }
|
---|
203 |
|
---|
204 | int domainSize = 0;
|
---|
205 | for (Issue issue : this.getDomain().getIssues()) {
|
---|
206 | domainSize += ((IssueDiscrete) issue).getValues().size() + 1;
|
---|
207 | }
|
---|
208 |
|
---|
209 | int numOfMovement = 5000;
|
---|
210 | final double wightRate = this.getDomain().getIssues().size() * 1.0D / domainSize;
|
---|
211 |
|
---|
212 | for (int i = 0; i < numOfMovement; i ++) {
|
---|
213 | Map<Movement, AdditiveUtilitySpace> moveToNeighbors = new HashMap<Movement, AdditiveUtilitySpace>();
|
---|
214 |
|
---|
215 | for (int j = 0; j < domainSize; j ++) {
|
---|
216 | Movement movement = new Movement(this.getDomain(), wightRate);
|
---|
217 | while (TabuList.contains(movement)) {
|
---|
218 | movement = new Movement(this.getDomain(), wightRate);
|
---|
219 | }
|
---|
220 | moveToNeighbors.put(movement, getNeighbor(additiveUtilitySpace, movement));
|
---|
221 | }
|
---|
222 |
|
---|
223 | Iterator<Map.Entry<Movement, AdditiveUtilitySpace>> iterator = moveToNeighbors.entrySet().iterator();
|
---|
224 | Map.Entry<Movement, AdditiveUtilitySpace> bestEntry = iterator.next();
|
---|
225 | double bestScore = -100.0D;
|
---|
226 | while (iterator.hasNext()) {
|
---|
227 | Map.Entry<Movement, AdditiveUtilitySpace> entry = iterator.next();
|
---|
228 | double score = getScore(entry.getValue(), false);
|
---|
229 | if (score > bestScore) {
|
---|
230 | bestEntry = entry;
|
---|
231 | bestScore = score;
|
---|
232 | }
|
---|
233 | }
|
---|
234 |
|
---|
235 | additiveUtilitySpace = bestEntry.getValue();
|
---|
236 | if (bestScore > hallOfFameScore) {
|
---|
237 | hallOfFame = additiveUtilitySpace;
|
---|
238 | hallOfFameScore = bestScore;
|
---|
239 | }
|
---|
240 |
|
---|
241 | TabuList.add(bestEntry.getKey());
|
---|
242 | if (TabuList.size() > Math.sqrt(domainSize) / 2) {
|
---|
243 | TabuList.remove(0);
|
---|
244 | }
|
---|
245 |
|
---|
246 | if (isDebug) {
|
---|
247 | getScore(additiveUtilitySpace, true);
|
---|
248 | }
|
---|
249 | }
|
---|
250 |
|
---|
251 | if (isDebug) {
|
---|
252 | getScore(additiveUtilitySpace, true);
|
---|
253 | }
|
---|
254 |
|
---|
255 | return hallOfFame;
|
---|
256 | }
|
---|
257 |
|
---|
258 | private double getScore(AdditiveUtilitySpace additiveUtilitySpace, boolean isPrint) {
|
---|
259 | BidRanking bidRank = this.userModel.getBidRanking();
|
---|
260 |
|
---|
261 | Map<Bid, Integer> realRanks = new HashMap<Bid, Integer>();
|
---|
262 | List<Double> estimatedUtils = new ArrayList<Double>();
|
---|
263 | for (Bid bid : bidRank.getBidOrder()) {
|
---|
264 | realRanks.put(bid, realRanks.size());
|
---|
265 | estimatedUtils.add(additiveUtilitySpace.getUtility(bid));
|
---|
266 | }
|
---|
267 | Collections.sort(estimatedUtils);
|
---|
268 |
|
---|
269 | Map<Bid, Integer> estimatedRanks = new HashMap<Bid, Integer>();
|
---|
270 | for (Bid bid : bidRank.getBidOrder()) {
|
---|
271 | estimatedRanks.put(bid, estimatedUtils.indexOf(additiveUtilitySpace.getUtility(bid)));
|
---|
272 | }
|
---|
273 |
|
---|
274 | double errors = 0;
|
---|
275 | for (Bid bid : bidRank.getBidOrder()) {
|
---|
276 | errors += Math.pow(realRanks.get(bid) - estimatedRanks.get(bid), 2);
|
---|
277 | }
|
---|
278 |
|
---|
279 | double spearman = 1.0D - 6.0D * errors / (Math.pow(realRanks.size(), 3) - realRanks.size());
|
---|
280 | double lowDiff = Math.abs(bidRank.getLowUtility().doubleValue() - additiveUtilitySpace.getUtility(bidRank.getMinimalBid()));
|
---|
281 | double highDiff = Math.abs(bidRank.getHighUtility().doubleValue() - additiveUtilitySpace.getUtility(bidRank.getMaximalBid()));
|
---|
282 | if(isPrint) {
|
---|
283 | System.out.println("spearman = " + spearman + ", lowDiff = " + lowDiff + ", highDiff = " + highDiff);
|
---|
284 | }
|
---|
285 |
|
---|
286 | return spearman * 10.0D + (1.0D - lowDiff) + (1.0D - highDiff);
|
---|
287 | }
|
---|
288 |
|
---|
289 |
|
---|
290 | private AdditiveUtilitySpace generateRandomUtilitySpace() {
|
---|
291 | AdditiveUtilitySpaceFactory additiveUtilitySpaceFactory = new AdditiveUtilitySpaceFactory(this.getDomain());
|
---|
292 |
|
---|
293 | for (IssueDiscrete issue : additiveUtilitySpaceFactory.getIssues()) {
|
---|
294 | additiveUtilitySpaceFactory.setWeight(issue, this.rand.nextDouble());
|
---|
295 |
|
---|
296 | for (ValueDiscrete value : issue.getValues()) {
|
---|
297 | additiveUtilitySpaceFactory.setUtility(issue, value, this.rand.nextDouble());
|
---|
298 | }
|
---|
299 | }
|
---|
300 |
|
---|
301 | normalize(additiveUtilitySpaceFactory);
|
---|
302 | return additiveUtilitySpaceFactory.getUtilitySpace();
|
---|
303 | }
|
---|
304 |
|
---|
305 | private AdditiveUtilitySpace getNeighbor(AdditiveUtilitySpace current, Movement movement) {
|
---|
306 | double speed = 1.0D;
|
---|
307 | AdditiveUtilitySpaceFactory neighbor = getAdditiveUtilitySpaceFactoryFrom(current);
|
---|
308 |
|
---|
309 | IssueDiscrete issue = neighbor.getIssues().get(movement.getIssueID());
|
---|
310 | if (movement.getIsWeight()) {
|
---|
311 | if (rand.nextBoolean()) {
|
---|
312 | neighbor.setWeight(issue, current.getWeight(issue) + speed * rand.nextDouble() / neighbor.getIssues().size());
|
---|
313 | } else {
|
---|
314 | neighbor.setWeight(issue, Math.abs(current.getWeight(issue) - speed * rand.nextDouble() / neighbor.getIssues().size()));
|
---|
315 | }
|
---|
316 |
|
---|
317 | } else {
|
---|
318 | double averageEval = 0;
|
---|
319 | for (ValueDiscrete v : issue.getValues()) {
|
---|
320 | averageEval += ((EvaluatorDiscrete)current.getEvaluator(issue)).getDoubleValue(v);
|
---|
321 | }
|
---|
322 | averageEval /= issue.getValues().size();
|
---|
323 |
|
---|
324 | ValueDiscrete value = issue.getValue(movement.getValueID());
|
---|
325 | double eval = ((EvaluatorDiscrete)current.getEvaluator(issue)).getDoubleValue(value);
|
---|
326 | if (rand.nextBoolean()) {
|
---|
327 | neighbor.setUtility(issue, value, eval + speed * averageEval * rand.nextDouble());
|
---|
328 | } else {
|
---|
329 | neighbor.setUtility(issue, value, Math.abs(eval - speed * averageEval * rand.nextDouble()));
|
---|
330 | }
|
---|
331 | }
|
---|
332 |
|
---|
333 | normalize(neighbor);
|
---|
334 | return neighbor.getUtilitySpace();
|
---|
335 | }
|
---|
336 |
|
---|
337 | private AdditiveUtilitySpaceFactory getAdditiveUtilitySpaceFactoryFrom(AdditiveUtilitySpace additiveUtilitySpace){
|
---|
338 | AdditiveUtilitySpaceFactory additiveUtilitySpaceFactory = new AdditiveUtilitySpaceFactory(getDomain());
|
---|
339 | for (IssueDiscrete issue : additiveUtilitySpaceFactory.getIssues()) {
|
---|
340 | additiveUtilitySpaceFactory.setWeight(issue, additiveUtilitySpace.getWeight(issue));
|
---|
341 | for (ValueDiscrete value : issue.getValues()) {
|
---|
342 | additiveUtilitySpaceFactory.setUtility(issue, value, ((EvaluatorDiscrete) additiveUtilitySpace.getEvaluator(issue)).getDoubleValue(value));
|
---|
343 | }
|
---|
344 | }
|
---|
345 | return additiveUtilitySpaceFactory;
|
---|
346 | }
|
---|
347 |
|
---|
348 | private void normalize(AdditiveUtilitySpaceFactory additiveUtilitySpaceFactory){
|
---|
349 | additiveUtilitySpaceFactory.normalizeWeights();
|
---|
350 |
|
---|
351 | for (IssueDiscrete issue : additiveUtilitySpaceFactory.getIssues()) {
|
---|
352 | double max = issue.getValues().stream().map(value -> additiveUtilitySpaceFactory.getUtility(issue, value)).max(Double::compareTo).get();
|
---|
353 | for (ValueDiscrete value : issue.getValues()) {
|
---|
354 | double eval = additiveUtilitySpaceFactory.getUtility(issue, value);
|
---|
355 | additiveUtilitySpaceFactory.setUtility(issue, value, eval / max);
|
---|
356 | }
|
---|
357 | }
|
---|
358 | }
|
---|
359 |
|
---|
360 | public String getDescription() {
|
---|
361 | return "KakeSoba";
|
---|
362 | }
|
---|
363 |
|
---|
364 |
|
---|
365 | private class Movement {
|
---|
366 | private int issueID;
|
---|
367 | private int valueID;
|
---|
368 | private boolean isWeight;
|
---|
369 |
|
---|
370 | public Movement(int issueID, int valueID) {
|
---|
371 | this.issueID = issueID;
|
---|
372 | this.valueID = valueID;
|
---|
373 | this.isWeight = false;
|
---|
374 | }
|
---|
375 |
|
---|
376 | public Movement(int issueID) {
|
---|
377 | this.issueID = issueID;
|
---|
378 | this.valueID = 0;
|
---|
379 | this.isWeight = true;
|
---|
380 | }
|
---|
381 |
|
---|
382 | public Movement(Domain domain, double wightRate) {
|
---|
383 | List<Issue> issues = domain.getIssues();
|
---|
384 | this.issueID = rand.nextInt(issues.size());
|
---|
385 |
|
---|
386 | if (rand.nextDouble() > wightRate) {
|
---|
387 | this.isWeight = true;
|
---|
388 | this.valueID = 0;
|
---|
389 | } else {
|
---|
390 | this.isWeight = false;
|
---|
391 | this.valueID = rand.nextInt(((IssueDiscrete) issues.get(this.issueID)).getNumberOfValues());
|
---|
392 | }
|
---|
393 | }
|
---|
394 |
|
---|
395 | private int getIssueID() {
|
---|
396 | return this.issueID;
|
---|
397 | }
|
---|
398 |
|
---|
399 | private int getValueID() {
|
---|
400 | return this.valueID;
|
---|
401 | }
|
---|
402 |
|
---|
403 | private boolean getIsWeight() {
|
---|
404 | return this.isWeight;
|
---|
405 | }
|
---|
406 | }
|
---|
407 | }
|
---|