1 | // WORKING ANGEL AGENT
|
---|
2 | /**
|
---|
3 | * TODO Current changes that need to be made outside of the methods:
|
---|
4 | * 3. Experiment with batch updating for comparisons (requires filling out the method under main routines)
|
---|
5 | */
|
---|
6 |
|
---|
7 |
|
---|
8 | package geniusweb.exampleparties.simpleshaop;
|
---|
9 |
|
---|
10 | import java.io.IOException;
|
---|
11 | import java.lang.Math;
|
---|
12 | import java.math.BigDecimal;
|
---|
13 | import java.math.BigInteger;
|
---|
14 | import java.util.*;
|
---|
15 | import java.util.Dictionary;
|
---|
16 | import java.util.Arrays;
|
---|
17 | import java.util.ArrayList;
|
---|
18 | import java.util.HashSet;
|
---|
19 | import java.util.HashMap;
|
---|
20 | import java.util.Random;
|
---|
21 | import java.util.logging.Level;
|
---|
22 | import java.util.concurrent.TimeUnit;
|
---|
23 | import geniusweb.actions.Accept;
|
---|
24 | import geniusweb.actions.Action;
|
---|
25 | import geniusweb.actions.Comparison;
|
---|
26 | import geniusweb.actions.ElicitComparison;
|
---|
27 | import geniusweb.actions.Offer;
|
---|
28 | import geniusweb.actions.PartyId;
|
---|
29 | import geniusweb.bidspace.AllBidsList;
|
---|
30 | import geniusweb.issuevalue.Bid;
|
---|
31 | import geniusweb.issuevalue.Domain;
|
---|
32 | import geniusweb.issuevalue.ValueSet;
|
---|
33 | import geniusweb.party.Capabilities;
|
---|
34 | import geniusweb.party.DefaultParty;
|
---|
35 | import geniusweb.party.inform.ActionDone;
|
---|
36 | import geniusweb.party.inform.Finished;
|
---|
37 | import geniusweb.party.inform.Inform;
|
---|
38 | import geniusweb.party.inform.Settings;
|
---|
39 | import geniusweb.party.inform.YourTurn;
|
---|
40 | import geniusweb.profile.PartialOrdering;
|
---|
41 | import geniusweb.profileconnection.ProfileConnectionFactory;
|
---|
42 | import geniusweb.profileconnection.ProfileInterface;
|
---|
43 | import geniusweb.progress.Progress;
|
---|
44 | import geniusweb.progress.ProgressRounds;
|
---|
45 | import tudelft.utilities.logging.Reporter;
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * ANGEL is a Shaop Party that will use an intuitive heuristic to model opponents
|
---|
49 | * and estimate the values of bids. Weights and the utilities of individual
|
---|
50 | * issue values are learned over time. Estimations are made with a linear additive
|
---|
51 | * utility function of weights*utilities, and then additionaly processed with
|
---|
52 | * a confidence measure. Elicitation Requests are made when the expected value
|
---|
53 | * gain exceeds the elicitation cost.
|
---|
54 | *
|
---|
55 | */
|
---|
56 | public class AngelParty extends DefaultParty {
|
---|
57 |
|
---|
58 | //*****************************************************************************************************************
|
---|
59 | // BACK END variables
|
---|
60 | private PartyId me; // Identifies ANGEL
|
---|
61 | private final Random random = new Random(); // Used for the Gaussian Distribution
|
---|
62 | protected ProfileInterface profileint; // Will allow us to access info about Domain or Profile
|
---|
63 | private Progress progress; // ~TODO~ Understand this
|
---|
64 |
|
---|
65 |
|
---|
66 | // BID SPACE INFORMATION -> remove if there is a super easy way to access this
|
---|
67 | private int m; // m is the number of issues
|
---|
68 | private int d; // d is the number of bids in the partial preference profile
|
---|
69 | private Double e=0.01; // elicitation cost, denoted 'epsilon'
|
---|
70 | private Double spent = 0.0; // Whenever we elicit, keep track of how much has been spent
|
---|
71 | private Bid reservation; // Best alternative to negotiated agreement
|
---|
72 | private Double reserve_utility; // Reserve is in the form of a bid, not a value, so reserve_utility is an estimation.
|
---|
73 | private int T; // number of rounds
|
---|
74 | private int t=0; // current round, updated manually
|
---|
75 | private int tmav=-1; // the last round t on which mav was updated
|
---|
76 |
|
---|
77 |
|
---|
78 | // MEMORY -> recording all of the estimates for updating and evaluating
|
---|
79 | // This models both our human's and the opponent's utility functions
|
---|
80 | private HashSet<String> issues; // Conveniently storing the Issues for access
|
---|
81 | private HashMap<String, ValueSet> issueValues = new HashMap(); // Conveniently storing the IssueValues, accessed via the Issues
|
---|
82 |
|
---|
83 | private HashMap<String, HashMap<geniusweb.issuevalue.Value, Double>> au = new HashMap(); // angel estimated_utilites[issue][issueval] = estimate
|
---|
84 | private HashMap<String, Double> aW = new HashMap(); // angel estimated_weights[issue] = estimate
|
---|
85 | private HashMap<String, HashMap<geniusweb.issuevalue.Value, Double>> ac = new HashMap(); // angel issval_confindences[issue][issueval] = estimate
|
---|
86 |
|
---|
87 | private HashMap<String, HashMap<geniusweb.issuevalue.Value, Double>> ou = new HashMap(); // Estimated utilities for the opponent
|
---|
88 | private HashMap<String, Double> oW = new HashMap(); // Estimated weights for the opponent's issues
|
---|
89 | private HashMap<String, HashMap<geniusweb.issuevalue.Value, Double>> oc = new HashMap(); // Confidence in ou
|
---|
90 |
|
---|
91 | private Bid highestReceivedBid; // opponents highest offer so far
|
---|
92 | private Bid lastReceivedBid; // last received offer from opponent
|
---|
93 | private Bid lastSentOffer; // our previous offer
|
---|
94 | private int stepsDown=0; // used to possible pick counter offer if ^^^ do not work
|
---|
95 |
|
---|
96 | // Additional random memory components
|
---|
97 | private HashSet<ArrayList<Bid>> comparisons = new HashSet(); // Collection of all one-one comparisons, bid1 >= bid2
|
---|
98 | private HashSet<ArrayList<Bid>> oppComparisons = new HashSet(); // Comparisons like for ourselves made from opp offers, models opp
|
---|
99 | private SimpleLinearOrdering estimatedProfile; // The ordering of all known bids
|
---|
100 | // TODO ^ stop using the getUtility method, estimate utility with function U
|
---|
101 | private boolean already_initialized = false; // Just to make sure that the agent gets initialized properly
|
---|
102 | private Double mav = 1.0; // Minimum accepted value, number to decay when conceding
|
---|
103 | private Bid lastElicitedBid; // Used for filling comparisons
|
---|
104 |
|
---|
105 | //*****************************************************************************************************************
|
---|
106 | // Create agent
|
---|
107 | public AngelParty() {
|
---|
108 | }
|
---|
109 |
|
---|
110 | public AngelParty(Reporter reporter) {
|
---|
111 | super(reporter); // for debugging -> Allegedly
|
---|
112 | }
|
---|
113 |
|
---|
114 | //*****************************************************************************************************************
|
---|
115 | // Have the back end handled
|
---|
116 | // TODO certify that there is no conflict between this and the desired ANGEL protocol
|
---|
117 | // Probably best to leave these methods alone (for the most part)
|
---|
118 | @Override
|
---|
119 | public void notifyChange(Inform info) {
|
---|
120 | try {
|
---|
121 | if (info instanceof Settings) {
|
---|
122 | Settings settings = (Settings) info;
|
---|
123 | this.profileint = ProfileConnectionFactory
|
---|
124 | .create(settings.getProfile().getURI(), getReporter());
|
---|
125 | this.estimatedProfile = new SimpleLinearOrdering(
|
---|
126 | profileint.getProfile());
|
---|
127 | this.reservation = profileint.getProfile().getReservationBid(); // Boy howdy I hope this works
|
---|
128 | this.issues = new HashSet<String>(profileint.getProfile().getDomain().getIssues());
|
---|
129 | this.m = this.issues.size();
|
---|
130 | for(String issue:this.issues) {
|
---|
131 | this.issueValues.put(issue, profileint.getProfile().getDomain().getValues(issue));
|
---|
132 | }
|
---|
133 | this.me = settings.getID();
|
---|
134 | this.progress = settings.getProgress();
|
---|
135 | if (settings.getParameters().get("elicitationcost") != null) {
|
---|
136 | this.e = (double)settings.getParameters().get("elicitationcost");
|
---|
137 | }
|
---|
138 | this.T = ((ProgressRounds) settings.getProgress()).getTotalRounds();
|
---|
139 | if (!already_initialized) {
|
---|
140 | //System.out.println(getName() + " is initializing information ");
|
---|
141 | //System.out.println(getName()+"'s profile is "+estimatedProfile.getBids().toString());
|
---|
142 | // Initialize all estimates, offer our best bid
|
---|
143 | for (Bid bid: estimatedProfile.getBids()) {
|
---|
144 | bid = fillBidIssues(bid);
|
---|
145 | }
|
---|
146 | initAngelComparisons(estimatedProfile);
|
---|
147 | d = estimatedProfile.getBids().size();
|
---|
148 | Double[] normalUtils = initNormalUtils();
|
---|
149 | for (int idx=0; idx<normalUtils.length; idx++) {
|
---|
150 | //System.out.print(" "+normalUtils[idx]);
|
---|
151 | }
|
---|
152 | initAngelWeights(estimatedProfile, normalUtils);
|
---|
153 | initOppWeights();
|
---|
154 | initAngelUtils(estimatedProfile, normalUtils);
|
---|
155 | initOppUtils();
|
---|
156 | initAngelConfidences(estimatedProfile);
|
---|
157 | initOppConfidences();
|
---|
158 | //System.out.println(getName()+" init the utils to " +au);
|
---|
159 | //System.out.println(getName()+" init the weights to " +aW);
|
---|
160 | //System.out.println("Just after initialization, there are this many faults: "+countFaults(comparisons)+" out of "+comparisons.size()+" comparisons");
|
---|
161 |
|
---|
162 | // Now adjust to see if the number of faults goes down significantly / at all
|
---|
163 | // move through new comparisons and perform fault adjustments
|
---|
164 |
|
---|
165 | for (ArrayList<Bid> comparison: comparisons) {
|
---|
166 | if (isFault(comparison)) {
|
---|
167 | handleFaultForComparison(comparison);
|
---|
168 | }
|
---|
169 | }
|
---|
170 | // adjust confidences
|
---|
171 | for (String issue: issues) {
|
---|
172 | for (geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
173 | adjustConfidenceForIssueValue(issue,lambda, comparisons);
|
---|
174 | }
|
---|
175 | }
|
---|
176 | //System.out.println("Now there are this many faults: "+countFaults(comparisons)+" out of "+comparisons.size()+" comparisons");
|
---|
177 | }
|
---|
178 | } else if (info instanceof ActionDone) {
|
---|
179 | Action otheract = ((ActionDone) info).getAction();
|
---|
180 | //System.out.println(getName()+" received action "+ otheract.toString());
|
---|
181 | if (otheract instanceof Offer) {
|
---|
182 | lastReceivedBid = ((Offer) otheract).getBid();
|
---|
183 | lastReceivedBid = fillBidIssues(lastReceivedBid);
|
---|
184 | //System.out.println(getName()+" evaluated the offer "+ lastReceivedBid+" as "+calculateBidUtility(lastReceivedBid));
|
---|
185 | double lastUtil = calculateBidUtility(lastReceivedBid);
|
---|
186 | double bestUtil = calculateBidUtility(highestReceivedBid);
|
---|
187 | if (lastUtil>bestUtil) {
|
---|
188 | highestReceivedBid = lastReceivedBid;
|
---|
189 | }
|
---|
190 | } else if (otheract instanceof Comparison) {
|
---|
191 | estimatedProfile = estimatedProfile.with(
|
---|
192 | ((Comparison) otheract).getBid(),
|
---|
193 | ((Comparison) otheract).getWorse());
|
---|
194 | for (Bid better: ((Comparison)otheract).getBetter()) {
|
---|
195 | ArrayList<Bid> comparison = new ArrayList<Bid>();
|
---|
196 | comparison.add(better);
|
---|
197 | comparison.add(lastElicitedBid);
|
---|
198 | comparisons.add(comparison);
|
---|
199 | }
|
---|
200 | HashSet<ArrayList<Bid>> newComparisons = new HashSet();
|
---|
201 | for (Bid worse: ((Comparison)otheract).getWorse()) {
|
---|
202 | ArrayList<Bid> comparison = new ArrayList<Bid>();
|
---|
203 | comparison.add(lastElicitedBid);
|
---|
204 | comparison.add(worse);
|
---|
205 | newComparisons.add(comparison);
|
---|
206 | }
|
---|
207 | // add each new comparison to comparisons
|
---|
208 | for (ArrayList<Bid> comparison: newComparisons) {
|
---|
209 | comparisons.add(comparison);
|
---|
210 | }
|
---|
211 | int beforeFaultsNewInfo = countFaults(newComparisons);
|
---|
212 | int beforeFaultsTotalInfo = countFaults(comparisons);
|
---|
213 | // move through new comparisons and perform fault adjustments
|
---|
214 | for (ArrayList<Bid> comparison: newComparisons) {
|
---|
215 | if (isFault(comparison)) {
|
---|
216 | handleFaultForComparison(comparison);
|
---|
217 | }
|
---|
218 | }
|
---|
219 | // adjust confidences
|
---|
220 | for (String issue: issues) {
|
---|
221 | for (geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
222 | adjustConfidenceForIssueValue(issue,lambda, newComparisons);
|
---|
223 | }
|
---|
224 | }
|
---|
225 | //System.out.println(getName()+" updated personal estimates, weights are now: "+aW.toString());
|
---|
226 | int afterFaultsNewInfo = countFaults(newComparisons);
|
---|
227 | int afterFaultsTotalInfo = countFaults(comparisons);
|
---|
228 | // TODO log these results if they were unfavorable
|
---|
229 | if (beforeFaultsNewInfo < afterFaultsNewInfo) {
|
---|
230 | //System.out.println("%%%%%%%%% Faults in new info before update: "+ beforeFaultsNewInfo +" after: "+afterFaultsNewInfo);
|
---|
231 | }
|
---|
232 | if (beforeFaultsTotalInfo < afterFaultsTotalInfo) {
|
---|
233 | //System.out.println("%%%%%%%%% Faults in total before update: "+beforeFaultsTotalInfo+" after: "+afterFaultsTotalInfo);
|
---|
234 | }
|
---|
235 | try {
|
---|
236 | myTurn();
|
---|
237 | }
|
---|
238 | catch (Exception e) {
|
---|
239 | throw new RuntimeException("Error inside myTurn", e);
|
---|
240 | }
|
---|
241 | }
|
---|
242 | } else if (info instanceof YourTurn) {
|
---|
243 | try {
|
---|
244 | myTurn();
|
---|
245 | }
|
---|
246 | catch (Exception e) {
|
---|
247 | throw new RuntimeException("Error inside myTurn", e);
|
---|
248 | }
|
---|
249 | } else if (info instanceof Finished) {
|
---|
250 | //System.out.println(getName()+" received info -> Finished");
|
---|
251 | getReporter().log(Level.INFO, "Final ourcome:" + info);
|
---|
252 | }
|
---|
253 | } catch (Exception e) {
|
---|
254 | throw new RuntimeException("Failed to handle info", e);
|
---|
255 | }
|
---|
256 | }
|
---|
257 |
|
---|
258 | @Override
|
---|
259 | public Capabilities getCapabilities() {
|
---|
260 | return new Capabilities(new HashSet<>(Arrays.asList("SHAOP")));
|
---|
261 | }
|
---|
262 |
|
---|
263 | @Override
|
---|
264 | public String getDescription() {
|
---|
265 | return "Greedy concession strategy, elicits information from COB "
|
---|
266 | + "when there is low confidence in best counter-offer prediction. Requires elicitationcost parameter to be set."
|
---|
267 | + " Original design by Andrew DeVoss and Robert Geraghty";
|
---|
268 | }
|
---|
269 |
|
---|
270 |
|
---|
271 | //*****************************************************************************************************************
|
---|
272 | // Executing the heuristic by calling the methods defined later
|
---|
273 | // code the ANGEL protocol by ^
|
---|
274 | /**
|
---|
275 | * Called when it's (still) our turn and we should take some action. Also
|
---|
276 | * Updates the progress if necessary.
|
---|
277 | */
|
---|
278 | private void myTurn() throws IOException {
|
---|
279 | Action action = null;
|
---|
280 | //System.out.println(getName()+" is taking a turn ");
|
---|
281 | t+=1; // Update the turn, making sure that this is counter-balanced if an elicitation request is made.
|
---|
282 | if (t != tmav) {
|
---|
283 | recalculateMAV(T, t);
|
---|
284 | //System.out.println(getName()+" has a new mav of "+mav+" on round "+t);
|
---|
285 | }
|
---|
286 | if (!already_initialized) {
|
---|
287 | Bid bestBid = estimatedProfile.getBids().get(estimatedProfile.getBids().size()-1);
|
---|
288 | lastSentOffer = bestBid;
|
---|
289 | action = new Offer(me, bestBid);
|
---|
290 | already_initialized = true;
|
---|
291 | if (progress instanceof ProgressRounds) {
|
---|
292 | progress = ((ProgressRounds) progress).advance();
|
---|
293 | }
|
---|
294 | }
|
---|
295 | Bid co = counterOffer(lastReceivedBid, highestReceivedBid, lastSentOffer);
|
---|
296 | if (shouldElicit(co, e) && action==null && co!=lastElicitedBid) {
|
---|
297 | //System.out.println(getName() + " is eliciting info about "+co);
|
---|
298 | action = new ElicitComparison(me, co, estimatedProfile.getBids());
|
---|
299 | tmav = t;
|
---|
300 | lastElicitedBid = co;
|
---|
301 | // Note: estimates updated from the inform change method, not here.
|
---|
302 | }
|
---|
303 | if (action==null && lastReceivedBid != null) {
|
---|
304 | // check to see if we want to accept the opponent's offer
|
---|
305 | boolean acceptable = isGood(lastReceivedBid, mav);
|
---|
306 | if (acceptable) {
|
---|
307 | //System.out.println(getName() + " is accepting an offer: mav="+mav+" expected value="+calculateBidUtility(lastReceivedBid));
|
---|
308 | action = new Accept(me, lastReceivedBid);
|
---|
309 | }
|
---|
310 | // TODO update opponent estimates based on their offer
|
---|
311 | // every time they reject our offer and give a new co, we assume opp believes co >= angel_last_offer
|
---|
312 | else if (lastSentOffer != null) {
|
---|
313 | //System.out.println(getName() + " is updating opponent model ");
|
---|
314 | ArrayList<Bid> comparison = new ArrayList<Bid>();
|
---|
315 | comparison.add(lastReceivedBid);
|
---|
316 | comparison.add(lastSentOffer);
|
---|
317 | oppComparisons.add(comparison);
|
---|
318 | // Since we have new information, update the utility estimates based on the known space.
|
---|
319 | int beforeFaults = countFaultsOpp(oppComparisons);
|
---|
320 | for (ArrayList<Bid> comp: oppComparisons) {
|
---|
321 | if (isFault(comp)) {
|
---|
322 | handleFaultForComparisonOpp(comp);
|
---|
323 | }
|
---|
324 | }
|
---|
325 | // Now update the opp confidence estimations based on the results.
|
---|
326 | for (String issue: issues) {
|
---|
327 | for (geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
328 | adjustConfidenceForIssueValueOpp(issue,lambda, oppComparisons);
|
---|
329 | }
|
---|
330 | }
|
---|
331 | int afterFaults = countFaultsOpp(oppComparisons);
|
---|
332 | if (beforeFaults<afterFaults) {;}
|
---|
333 | }
|
---|
334 | if (progress instanceof ProgressRounds) {
|
---|
335 | progress = ((ProgressRounds) progress).advance();
|
---|
336 | }
|
---|
337 | }
|
---|
338 | // If we don't accept, package the next bid, see if we should elicit, repackage when necessary
|
---|
339 | if (action==null && co!=lastElicitedBid && shouldElicit(co, e)) {
|
---|
340 | //System.out.println(getName() + " is eliciting info about "+co);
|
---|
341 | action = new ElicitComparison(me, co, estimatedProfile.getBids());
|
---|
342 | tmav = t;
|
---|
343 | t-=1;
|
---|
344 | lastElicitedBid = co;
|
---|
345 | // Note: estimates updated from the inform change method, not here.
|
---|
346 | }
|
---|
347 | if (action == null){
|
---|
348 | //System.out.println(getName() + " is offering bid "+co+" with expected utility "+calculateBidUtility(co));
|
---|
349 | action = new Offer(me, co);
|
---|
350 | lastSentOffer = co;
|
---|
351 | }
|
---|
352 | //System.out.println(getName()+"'s action is "+action.toString());
|
---|
353 | getConnection().send(action);
|
---|
354 | }
|
---|
355 |
|
---|
356 | //*****************************************************************************************************************
|
---|
357 | // Initialization routines
|
---|
358 |
|
---|
359 | private Double[] initNormalUtils(){
|
---|
360 | // Use these utilities for initial bid evaluations
|
---|
361 | Double[] normalUtils = new Double[d];
|
---|
362 | normalUtils[0] = 0.0;
|
---|
363 | normalUtils[d-1] = 1.0;
|
---|
364 | // now create d - 2 comparisons by sampling from N(.5, 1), clipping to [0, 1], and then ordering
|
---|
365 | Double[] tempNorms = new Double[d-2];
|
---|
366 | for(int i=0; i<d-2; i++) {
|
---|
367 | Double contender = (random.nextGaussian() + .5);
|
---|
368 | while (contender<0 || contender>1) {
|
---|
369 | contender = (random.nextGaussian() + .5);
|
---|
370 | }
|
---|
371 | tempNorms[i] = contender;
|
---|
372 | }
|
---|
373 | Arrays.sort(tempNorms);
|
---|
374 |
|
---|
375 | for (int i=0; i<d-2; i++) {
|
---|
376 | normalUtils[i+1] = tempNorms[i];
|
---|
377 | }
|
---|
378 | return normalUtils;
|
---|
379 | }
|
---|
380 |
|
---|
381 | private void initAngelWeights(SimpleLinearOrdering providedBids, Double[] utils) {
|
---|
382 | // Iterate over the ordering of bids. Work with bid if it contains lambda in best/worst bids
|
---|
383 | // weight = avg(bid with best lambda) - avg(bid with worst lambda)
|
---|
384 | HashMap<String, geniusweb.issuevalue.Value> bestLambdas = new HashMap<String, geniusweb.issuevalue.Value>();
|
---|
385 | HashMap<String, geniusweb.issuevalue.Value> worstLambdas = new HashMap<String, geniusweb.issuevalue.Value>();
|
---|
386 | List<Bid> bids = providedBids.getBids(); // I checked, this is actually sorted
|
---|
387 | for (String issue: issues) {
|
---|
388 | bestLambdas.put(issue, bids.get(d-1).getValue(issue));
|
---|
389 | }
|
---|
390 | for (String issue: issues) {
|
---|
391 | worstLambdas.put(issue, bids.get(0).getValue(issue));
|
---|
392 | }
|
---|
393 | HashMap<String, Double> highSums = new HashMap<String, Double>();
|
---|
394 | HashMap<String, Integer> highCounts = new HashMap<String, Integer>();
|
---|
395 | HashMap<String, Double> lowSums = new HashMap<String, Double>();
|
---|
396 | HashMap<String, Integer> lowCounts = new HashMap<String, Integer>();
|
---|
397 | // Fill the initial sums and counts with 0s
|
---|
398 | for (String issue: issues) {
|
---|
399 | highSums.put(issue, 0.0);
|
---|
400 | lowSums.put(issue, 0.0);
|
---|
401 | highCounts.put(issue, 0);
|
---|
402 | lowCounts.put(issue, 0);
|
---|
403 | }
|
---|
404 | // Even though we are iterating over all of the bids, we need to know the bid number
|
---|
405 | // so that we can retrieve the corresponding bid utility.
|
---|
406 | for (int bid=0; bid<d; bid++) {
|
---|
407 | Bid curr = bids.get(bid);
|
---|
408 | for (String issue: issues) {
|
---|
409 | if (curr.getValue(issue).equals(bestLambdas.get(issue))) {
|
---|
410 | highSums.put(issue, highSums.get(issue) + utils[bid]);
|
---|
411 | highCounts.put(issue, highCounts.get(issue) + 1);
|
---|
412 | }
|
---|
413 | if (curr.getValue(issue).equals(worstLambdas.get(issue))) {
|
---|
414 | lowSums.put(issue, lowSums.get(issue) + utils[bid]);
|
---|
415 | lowCounts.put(issue, lowCounts.get(issue) + 1);
|
---|
416 | }
|
---|
417 | }
|
---|
418 | }
|
---|
419 | // Now we have a count of total val of issues with lambda in best and worst bids,
|
---|
420 | // and associated counts for computing the average.
|
---|
421 | // We want to compute the average, and then normalize by the sum.
|
---|
422 | // Add 1.0 to each value (ensuring positive) then divide by sum.
|
---|
423 | HashMap<String, Double> weights = new HashMap<String, Double>();
|
---|
424 | Double sum = 0.0;
|
---|
425 | for (String issue: issues) {
|
---|
426 | Double amt = highSums.get(issue)/highCounts.get(issue) - lowSums.get(issue)/lowCounts.get(issue) + 1.0;
|
---|
427 | weights.put(issue, amt);
|
---|
428 | sum += amt;
|
---|
429 | }
|
---|
430 | for (String issue: issues) {
|
---|
431 | aW.put(issue, weights.get(issue)/sum);
|
---|
432 | }
|
---|
433 | }
|
---|
434 |
|
---|
435 | private void initOppWeights() {
|
---|
436 | // Initialize the weights for the opponent estimates
|
---|
437 | Double avg = 1.0/m;
|
---|
438 | for (String issue: issues) {
|
---|
439 | oW.put(issue, avg);
|
---|
440 | }
|
---|
441 | }
|
---|
442 |
|
---|
443 | private void initAngelUtils(SimpleLinearOrdering providedBids, Double[] utils) {
|
---|
444 | // If this is not efficient enough, we could instead loop through all of the bids once
|
---|
445 | // and update values for each of the issue values inside during the singular pass through.
|
---|
446 | // I chose to do it this way because it feels more intuitive, though less efficient. -Andrew
|
---|
447 |
|
---|
448 | // For each issue value:
|
---|
449 | // utility estimate is avg(bid containing issue value)
|
---|
450 | // If there is an issue value that is not present in any of the bids,
|
---|
451 | // it gets the median value of all issue values in same issue.
|
---|
452 | List<Bid> bids = providedBids.getBids();
|
---|
453 | for (String issue: issues) {
|
---|
454 | HashSet<geniusweb.issuevalue.Value> unseenLambdas = new HashSet<geniusweb.issuevalue.Value>();
|
---|
455 | au.put(issue, new HashMap<geniusweb.issuevalue.Value, Double>());
|
---|
456 | for(geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
457 | // If lambda for the issue is in the best bid, set the utility to 1
|
---|
458 | Bid bestBid = bids.get(d-1);
|
---|
459 | Bid worstBid = bids.get(0);
|
---|
460 | geniusweb.issuevalue.Value bestLambda = bestBid.getValue(issue);
|
---|
461 | geniusweb.issuevalue.Value worstLambda = worstBid.getValue(issue);
|
---|
462 | if (bestLambda.equals(lambda)) {
|
---|
463 | au.get(issue).put(lambda, 1.0);
|
---|
464 | }
|
---|
465 | else if (worstLambda.equals(lambda)){
|
---|
466 | au.get(issue).put(lambda, 0.0);
|
---|
467 | }
|
---|
468 | // Now loop through all of the remaining bids, checking if bid[issue] has lambda.
|
---|
469 | else {
|
---|
470 | int count = 0;
|
---|
471 | Double sum = 0.0;
|
---|
472 | for (int bid=1; bid<d-1; bid++) {
|
---|
473 | geniusweb.issuevalue.Value lambdaPrime = bids.get(bid).getValue(issue);
|
---|
474 | if (lambdaPrime.equals(lambda)) {
|
---|
475 | count += 1;
|
---|
476 | sum += utils[bid];
|
---|
477 | }
|
---|
478 | }
|
---|
479 | if (count==0) {
|
---|
480 | unseenLambdas.add(lambda);
|
---|
481 | }
|
---|
482 | else {
|
---|
483 | au.get(issue).put(lambda, sum/count);
|
---|
484 | }
|
---|
485 | }
|
---|
486 | }
|
---|
487 | // Finally, assign to each of the unseen lambdas the MEDIAN value of all of the other issue values within the same issue.
|
---|
488 | // Get the MEDIAN
|
---|
489 | List<Double> sortedValues = new ArrayList<Double>(au.get(issue).values());
|
---|
490 | Collections.sort(sortedValues);
|
---|
491 | int knownLambdas = sortedValues.size();
|
---|
492 | Double median;
|
---|
493 | if (knownLambdas%2 == 0){
|
---|
494 | int firstIdx = knownLambdas/2;
|
---|
495 | int secondIdx = firstIdx-1;
|
---|
496 | Double firstVal = sortedValues.get(firstIdx);
|
---|
497 | Double secondVal = sortedValues.get(secondIdx);
|
---|
498 | median = (firstVal+secondVal/2.0);
|
---|
499 | }
|
---|
500 | else {
|
---|
501 | int idx = knownLambdas/2;
|
---|
502 | median = sortedValues.get(idx);
|
---|
503 | }
|
---|
504 | // Now assign the median value to every lambda in unseenLambdas
|
---|
505 | for (geniusweb.issuevalue.Value lambda: unseenLambdas) {
|
---|
506 | au.get(issue).put(lambda, median);
|
---|
507 | }
|
---|
508 | }
|
---|
509 | }
|
---|
510 |
|
---|
511 | private void initOppUtils() {
|
---|
512 | // Init the opponent utility estimates. They start as .5 for each lambda
|
---|
513 | for (String issue: issues) {
|
---|
514 | ou.put(issue, new HashMap<geniusweb.issuevalue.Value, Double>());
|
---|
515 | for(geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
516 | ou.get(issue).put(lambda, 0.5);
|
---|
517 | }
|
---|
518 | }
|
---|
519 | }
|
---|
520 |
|
---|
521 | private void initAngelConfidences(SimpleLinearOrdering providedBids) {
|
---|
522 | // Confidence in lambdas in best/worst bids = 1
|
---|
523 | // otherwise confidence = .8
|
---|
524 | List<Bid> bids = providedBids.getBids();
|
---|
525 | Bid bestBid = bids.get(d-1);
|
---|
526 | Bid worstBid = bids.get(0);
|
---|
527 | for (String issue: issues) {
|
---|
528 | ac.put(issue, new HashMap<geniusweb.issuevalue.Value, Double>());
|
---|
529 | for(geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
530 | if (bestBid.getValue(issue).equals(lambda) || worstBid.getValue(issue).equals(lambda)) {
|
---|
531 | ac.get(issue).put(lambda, 1.0);
|
---|
532 | }
|
---|
533 | else {
|
---|
534 | ac.get(issue).put(lambda, 0.8);
|
---|
535 | }
|
---|
536 | }
|
---|
537 | }
|
---|
538 | }
|
---|
539 |
|
---|
540 | private void initOppConfidences() {
|
---|
541 | // Confidence in all values for the opponent start at .5
|
---|
542 | for (String issue: issues) {
|
---|
543 | oc.put(issue, new HashMap<geniusweb.issuevalue.Value, Double>());
|
---|
544 | for(geniusweb.issuevalue.Value lambda: issueValues.get(issue)) {
|
---|
545 | oc.get(issue).put(lambda, 0.8);
|
---|
546 | }
|
---|
547 | }
|
---|
548 | }
|
---|
549 |
|
---|
550 | private void initAngelComparisons(SimpleLinearOrdering providedBids) {
|
---|
551 | List<Bid> bids = providedBids.getBids();
|
---|
552 | int stop = bids.size();
|
---|
553 | for (int lowIdx=0; lowIdx < stop; lowIdx++) {
|
---|
554 | for (int highIdx=0; highIdx < stop; highIdx++) {
|
---|
555 | Bid low = bids.get(lowIdx);
|
---|
556 | Bid high = bids.get(highIdx);
|
---|
557 | ArrayList<Bid> comparison = new ArrayList<Bid>();
|
---|
558 | comparison.add(high);
|
---|
559 | comparison.add(low);
|
---|
560 | comparisons.add(comparison);
|
---|
561 | }
|
---|
562 | }
|
---|
563 | }
|
---|
564 |
|
---|
565 | //*****************************************************************************************************************
|
---|
566 | // Main routines for ANGEL during the action of the negotiation
|
---|
567 | private Bid fillBidIssues(Bid bid){
|
---|
568 | // If a bid does not contain a value for every issue, then fill it
|
---|
569 | // with the item worth 0 from that issue (from the worst bid)
|
---|
570 | if (bid.getIssues().size() == issues.size()) {
|
---|
571 | return bid;
|
---|
572 | }
|
---|
573 | //System.out.println(" FILLING A BID ");
|
---|
574 | HashMap<String, geniusweb.issuevalue.Value> newBid = new HashMap<String, geniusweb.issuevalue.Value>();
|
---|
575 | Set<String> hadIssues = bid.getIssues();
|
---|
576 | for (String issue: issues) {
|
---|
577 | if (hadIssues.contains(issue)) {
|
---|
578 | newBid.put(issue, bid.getValue(issue));
|
---|
579 | }
|
---|
580 | else {
|
---|
581 | geniusweb.issuevalue.Value lambda = estimatedProfile.getBids().get(0).getValue(issue);
|
---|
582 | newBid.put(issue, lambda);
|
---|
583 | }
|
---|
584 | }
|
---|
585 | Bid filledBid = new Bid(newBid);
|
---|
586 | return filledBid;
|
---|
587 | }
|
---|
588 |
|
---|
589 | private void handleFaultForComparison(ArrayList<Bid> comparison) {
|
---|
590 | // Given one comparison, this adjusts the estimates for
|
---|
591 | // weights and then issueValue utility so that the
|
---|
592 | // resulting estimates are equal.
|
---|
593 | Bid tooHigh = comparison.get(1);
|
---|
594 | Bid tooLow = comparison.get(0);
|
---|
595 | Double difference = calculateBidUtility(tooLow) - calculateBidUtility(tooHigh);
|
---|
596 | HashMap<String, Double> alteredWeights = new HashMap<String, Double>();
|
---|
597 | HashMap<String, Double> originalEst = new HashMap<String, Double>();
|
---|
598 | Double sum=0.0;
|
---|
599 | for (String issue: issues) {
|
---|
600 | Double w_issue = aW.get(issue);
|
---|
601 | Double u_issue = au.get(issue).get(tooHigh.getValue(issue));
|
---|
602 | originalEst.put(issue, w_issue*u_issue);
|
---|
603 | Double w_alt = 1 + w_issue - ac.get(issue).get(tooHigh.getValue(issue))*difference/issues.size();
|
---|
604 | alteredWeights.put(issue, w_alt);
|
---|
605 | sum += w_alt;
|
---|
606 | }
|
---|
607 | double temp_sum = 0.0;
|
---|
608 | for (String issue: issues) {
|
---|
609 | aW.put(issue, alteredWeights.get(issue)/sum);
|
---|
610 | temp_sum+=alteredWeights.get(issue)/sum;
|
---|
611 | }
|
---|
612 | assert(temp_sum-1.0<.00001 && temp_sum-1.0>-.00001);
|
---|
613 | Double distributeAmt = 0.0;
|
---|
614 | int count = 1;
|
---|
615 | for (String issue: issues) {
|
---|
616 | if (ac.get(issue).get(tooHigh.getValue(issue)).equals(1.0)) {
|
---|
617 | count += 1;
|
---|
618 | distributeAmt += originalEst.get(issue)
|
---|
619 | - aW.get(issue)*au.get(issue).get(tooHigh.getValue(issue));
|
---|
620 | }
|
---|
621 | }
|
---|
622 | for (String issue: issues) {
|
---|
623 | if (!ac.get(issue).get(tooHigh.getValue(issue)).equals( 1.0)) {
|
---|
624 | Double newVal = (originalEst.get(issue)
|
---|
625 | - (difference/issues.size() + distributeAmt/count))/aW.get(issue);
|
---|
626 | // TODO, figure out why this might be happening
|
---|
627 | if (newVal>.99) {newVal=.99;}
|
---|
628 | au.get(issue).put(tooHigh.getValue(issue), newVal);
|
---|
629 | }
|
---|
630 | }
|
---|
631 | }
|
---|
632 |
|
---|
633 | private void handleFaultForBatchOfComparisons(HashSet<ArrayList<Bid>> comparisons) {
|
---|
634 | // TODO write this method for updating utility and weight estimates from
|
---|
635 | // new information all at once, if we want that functionality.
|
---|
636 | ;
|
---|
637 | }
|
---|
638 |
|
---|
639 | private int countFaults(HashSet<ArrayList<Bid>> comparisons) {
|
---|
640 | // Note: A comparison is a collection of two bids such that bid1 >= bid2.
|
---|
641 | // This method iterates over a collection of comparisons and
|
---|
642 | // counts the number of faults in the collection.
|
---|
643 | int numFaults = 0;
|
---|
644 | for (ArrayList<Bid> comparison: comparisons) {
|
---|
645 | Double highVal = calculateBidUtility(comparison.get(0));
|
---|
646 | Double lowVal = calculateBidUtility(comparison.get(1));
|
---|
647 | if (highVal<lowVal) {
|
---|
648 | numFaults+=1;
|
---|
649 | }
|
---|
650 | }
|
---|
651 | return numFaults;
|
---|
652 | }
|
---|
653 |
|
---|
654 | private void adjustConfidenceForIssueValue(String issue, geniusweb.issuevalue.Value lambda, HashSet<ArrayList<Bid>> comparisons) {
|
---|
655 | // Goes through a collection of comparisons and counts the number of faults
|
---|
656 | // where lambda is in a bid. Then confidence is adjusted.
|
---|
657 | if (ac.get(issue).get(lambda).equals(1.0)) {return;}
|
---|
658 | int faults = 0;
|
---|
659 | for (ArrayList<Bid> comparison: comparisons) {
|
---|
660 | Double highVal = calculateBidUtility(comparison.get(0));
|
---|
661 | Double lowVal = calculateBidUtility(comparison.get(1));
|
---|
662 | if (highVal<lowVal) {
|
---|
663 | Bid highBid = comparison.get(0);
|
---|
664 | Bid lowBid = comparison.get(1);
|
---|
665 | if (highBid.getValue(issue).equals(lambda) || lowBid.getValue(issue).equals(lambda)) {
|
---|
666 | faults += 1;
|
---|
667 | }
|
---|
668 | }
|
---|
669 | }
|
---|
670 | // Adjust the confidence as the average of the old confidence and the new confidence
|
---|
671 | Double oldConf = ac.get(issue).get(lambda);
|
---|
672 | Double newConf = (oldConf + (1.0-faults)/comparisons.size())/2;
|
---|
673 | ac.get(issue).put(lambda, newConf);
|
---|
674 | }
|
---|
675 |
|
---|
676 | private void recalculateMAV(int Rounds, int round) {
|
---|
677 | // Decay the MAV over the rounds, conservatively at first.
|
---|
678 | // For rounds<80% of Rounds, decay 10% of the way
|
---|
679 | // For rounds 80%-90% of Rounds, decay 20% of the way
|
---|
680 | // For rounds > 90%, decay the rest of the way
|
---|
681 | // The decay is linear between 1 and max(highestOppOfferUtil, reservationUtil)
|
---|
682 | Double hiOpp = calculateBidUtility(highestReceivedBid);
|
---|
683 | Double reserve = calculateBidUtility(reservation);
|
---|
684 | // change mav to slightly lower than hiOpp to prevent being overly greedy in the final rounds
|
---|
685 | if (hiOpp>reserve) {hiOpp = hiOpp-.2*(hiOpp-reserve);}
|
---|
686 | Double end = Math.max(reserve, hiOpp);
|
---|
687 | if (round <= .7*Rounds) {
|
---|
688 | Double y2 = 1.0;
|
---|
689 | Double y1 = (1-.3*(1-end));
|
---|
690 | int x2 = 0;
|
---|
691 | Double x1 = .7*Rounds;
|
---|
692 | Double m = (y2-y1)/(x2-x1);
|
---|
693 | this.mav = m*round + 1;
|
---|
694 | }
|
---|
695 | else if (round <= .8*Rounds) {
|
---|
696 | Double y2 = (1-.3*(1-end));
|
---|
697 | Double y1 = (1-.5*(1-end));
|
---|
698 | Double x2 = .7*Rounds;
|
---|
699 | Double x1 = .8*Rounds;
|
---|
700 | Double m = (y2-y1)/(x2-x1);
|
---|
701 | Double b = y2-(m*x2);
|
---|
702 | this.mav = m*round + b;
|
---|
703 | }
|
---|
704 | else {
|
---|
705 | Double y2 = (1-.5*(1-end));
|
---|
706 | Double y1 = end;
|
---|
707 | Double x2 = .8*Rounds;
|
---|
708 | int x1 = Rounds;
|
---|
709 | Double m = (y2-y1)/(x2-x1);
|
---|
710 | Double b = y2-(m*x2);
|
---|
711 | this.mav = m*round + b;
|
---|
712 | }
|
---|
713 | if (mav<Math.max(reserve, hiOpp)) {this.mav = Math.max(reserve, hiOpp);}
|
---|
714 | }
|
---|
715 |
|
---|
716 | private Bid counterOffer(Bid previousOpponentOffer, Bid highestOpponentOffer, Bid previousAngelOffer){
|
---|
717 | // Main idea: create three counter offers by adjusting
|
---|
718 | // our previous offer, opp previous offer, opp highest offer
|
---|
719 | // ensure that the values are higher than the MAV
|
---|
720 | // select the offer with the highest expected return weighted by confidence(?)
|
---|
721 |
|
---|
722 | // For the previousOpponentOffer and the highestOpponentOffer, we want to call the bidStepUp method
|
---|
723 | // For previousAngelOffer, call the bidStepDown method.
|
---|
724 |
|
---|
725 | // This is just a temporary idea for how to package the next counter-offer until we get the agent functional.
|
---|
726 | // check the ****SCALED?***** utility of each of the packaged offers. If none of them are higher than
|
---|
727 | // the minimum accepted value, check the previous offer. If it is also evaluated as not high enough
|
---|
728 | // then instead, default to the best bid in our bid list.
|
---|
729 | Bid bid1 = null;
|
---|
730 | Bid bid2 = null;
|
---|
731 | Bid bid3 = null;
|
---|
732 | if (previousOpponentOffer != null) {bid1 = bidStepUp(previousOpponentOffer);}
|
---|
733 | if (highestOpponentOffer != null) {bid2 = bidStepUp(highestOpponentOffer);}
|
---|
734 | if (previousAngelOffer != null) {bid3 = bidStepDown(previousAngelOffer);}
|
---|
735 | Double est1 = calculateBidUtility(bid1);
|
---|
736 | Double est2 = calculateBidUtility(bid2);
|
---|
737 | Double est3 = calculateBidUtility(bid3);
|
---|
738 | if (est1>=mav && est1>=est2 && est1>=est3) {
|
---|
739 | //System.out.println(getName()+" Made counter offer by modifying prev opp offer");
|
---|
740 | return bid1;
|
---|
741 | }
|
---|
742 | else if (est2>=mav && est2>=est1 && est2>=est3) {
|
---|
743 | //System.out.println(getName()+" Made counter offer by modifying highest opp offer");
|
---|
744 | return bid2;
|
---|
745 | }
|
---|
746 | else if (est3>=mav && est3>=est1 && est3>=est2) {
|
---|
747 | //System.out.println(getName()+" Made counter offer by modifying previous own offer");
|
---|
748 | return bid3;
|
---|
749 | }
|
---|
750 | else if (stepsDown<d && calculateBidUtility(estimatedProfile.getBids().get(d-1-stepsDown)) > mav) {
|
---|
751 | //System.out.println(getName()+" Made counter offer by stepping down in profile");
|
---|
752 | Bid known = estimatedProfile.getBids().get(d-1-stepsDown);
|
---|
753 | stepsDown+=1;
|
---|
754 | return known;
|
---|
755 |
|
---|
756 | }
|
---|
757 | else if (calculateBidUtility(previousAngelOffer) > mav){
|
---|
758 | //System.out.println(getName()+" Made counter offer by sending previous offer again ");
|
---|
759 | return previousAngelOffer;
|
---|
760 | }
|
---|
761 | else {
|
---|
762 | //System.out.println(getName()+" DEFAULTED TO BEST BID CO ");
|
---|
763 | return estimatedProfile.getBids().get(estimatedProfile.getBids().size() - 1);
|
---|
764 | }
|
---|
765 | }
|
---|
766 |
|
---|
767 | private boolean shouldElicit(Bid counteroffer, Double elicitationCost) {
|
---|
768 | // Is the expected value gained > elicitation cost
|
---|
769 | if (estimatedProfile.contains(counteroffer)) {return false;}
|
---|
770 | boolean needMoreInfo = false;
|
---|
771 | Double expectedValueOfKnowledge = calculateBidUtility(counteroffer)
|
---|
772 | -calculateConfidenceScaledBidUtility(counteroffer);
|
---|
773 | //System.out.println(getName()+"'s expected value of knowledge is "+expectedValueOfKnowledge);
|
---|
774 | if (expectedValueOfKnowledge > spent+elicitationCost) {
|
---|
775 | spent += elicitationCost;
|
---|
776 | needMoreInfo = true;
|
---|
777 | }
|
---|
778 | return needMoreInfo;
|
---|
779 | }
|
---|
780 |
|
---|
781 |
|
---|
782 | //*****************************************************************************************************************
|
---|
783 | // Random subroutines that flush the main routines
|
---|
784 | private boolean isGood(Bid bid, Double mav) {
|
---|
785 | // Check that the estimated utility of the bid is higher
|
---|
786 | // than the minimum accepted utility
|
---|
787 | // Make sure not to accept a bid known to be worse than reservation
|
---|
788 | if (estimatedProfile.contains(bid) && estimatedProfile.contains(reservation)) {
|
---|
789 | int bidx=0;
|
---|
790 | int ridx=0;
|
---|
791 | for (int i=0; i<estimatedProfile.getBids().size(); i++) {
|
---|
792 | if (estimatedProfile.getBids().get(i).equals(bid)) {bidx=i;}
|
---|
793 | else if (estimatedProfile.getBids().get(i).equals(reservation)) {ridx=i;}
|
---|
794 | }
|
---|
795 | if (bidx<ridx) {return false;}
|
---|
796 | }
|
---|
797 | for (ArrayList<Bid> comparison: comparisons) {
|
---|
798 | if (comparison.get(0).equals(reservation) && comparison.get(0).equals(bid)) {return false;}
|
---|
799 | }
|
---|
800 | boolean acceptable = false;
|
---|
801 | Double expectedValue = calculateBidUtility(bid);
|
---|
802 | if (expectedValue>=mav) {
|
---|
803 | acceptable = true;
|
---|
804 | }
|
---|
805 | return acceptable;
|
---|
806 | }
|
---|
807 |
|
---|
808 | private Double calculateBidUtility(Bid bid) {
|
---|
809 | // Implement the linear additive function U(omega): Omega -> [0,1]
|
---|
810 | // SUM( weight*utility )
|
---|
811 | if (bid==null) {
|
---|
812 | return 0.0;
|
---|
813 | }
|
---|
814 |
|
---|
815 | Double utility = 0.0;
|
---|
816 | HashMap<String, ArrayList<Double>> vals = new HashMap<String, ArrayList<Double>>();
|
---|
817 | HashMap<String, geniusweb.issuevalue.Value> theBid = new HashMap(bid.getIssueValues());
|
---|
818 | for (String issue: theBid.keySet()) {
|
---|
819 | utility += aW.get(issue) * au.get(issue).get(theBid.get(issue));
|
---|
820 | ArrayList<Double> weightUtil = new ArrayList<Double>();
|
---|
821 | weightUtil.add(aW.get(issue));
|
---|
822 | weightUtil.add(au.get(issue).get(theBid.get(issue)));
|
---|
823 | vals.put(issue, weightUtil);
|
---|
824 | }
|
---|
825 | if (utility>1.0) {
|
---|
826 | //System.out.println("\n\n\n*****UTILITY OF "+utility+"\n\n\n");
|
---|
827 | //System.out.println(vals.toString());
|
---|
828 | assert(!(utility>1.00001));}
|
---|
829 | return utility;
|
---|
830 | }
|
---|
831 |
|
---|
832 | private Double calculateConfidenceScaledBidUtility(Bid bid) {
|
---|
833 | // scaled estimation = SUM( weight*utility*confidence )
|
---|
834 | if (bid==null) {
|
---|
835 | return 0.0;
|
---|
836 | }
|
---|
837 | Double scaledUtility = 0.0;
|
---|
838 | HashMap<String, geniusweb.issuevalue.Value> theBid = new HashMap<String, geniusweb.issuevalue.Value>(bid.getIssueValues());
|
---|
839 | for (String issue: theBid.keySet()) {
|
---|
840 | scaledUtility += aW.get(issue) * au.get(issue).get(theBid.get(issue)) * ac.get(issue).get(theBid.get(issue));
|
---|
841 | }
|
---|
842 | return scaledUtility;
|
---|
843 | }
|
---|
844 |
|
---|
845 | private Bid bidStepDown(Bid greedyBid) {
|
---|
846 | // Take a bid that we consider really good
|
---|
847 | // and slightly lower the value of it with opponent model, changing only one issue value
|
---|
848 | // We want to change the value in the issue with greatest difference = (their issue weight - our issue weight)
|
---|
849 |
|
---|
850 | // TODO perhaps repeat the pick and replace movement until the estimated value > mav****
|
---|
851 | greedyBid = fillBidIssues(greedyBid);
|
---|
852 | String pivotIss="";
|
---|
853 | for (String issue: issues) {pivotIss = issue; break;}
|
---|
854 | Double highestDiff = 0.0;
|
---|
855 | for (String issue: issues) {
|
---|
856 | Double difference = oW.get(issue)-aW.get(issue);
|
---|
857 | if (difference> highestDiff) {
|
---|
858 | highestDiff = difference;
|
---|
859 | pivotIss = issue;
|
---|
860 | }
|
---|
861 | }
|
---|
862 | geniusweb.issuevalue.Value nextLowestLambda = null;
|
---|
863 | Double low = -2.0;
|
---|
864 | Double high = au.get(pivotIss).get(greedyBid.getValue(pivotIss));
|
---|
865 | for (geniusweb.issuevalue.Value lambda: issueValues.get(pivotIss)) {
|
---|
866 | Double util = au.get(pivotIss).get(lambda);
|
---|
867 | if (util > low && util < high) {
|
---|
868 | low = util;
|
---|
869 | nextLowestLambda = lambda;
|
---|
870 | }
|
---|
871 | }
|
---|
872 | HashMap<String, geniusweb.issuevalue.Value> newBid = new HashMap<String, geniusweb.issuevalue.Value>();
|
---|
873 | for (String issue: issues) {
|
---|
874 | if (issue.equals(pivotIss)) {
|
---|
875 | if (nextLowestLambda != null) {newBid.put(issue, nextLowestLambda);}
|
---|
876 | else {newBid.put(issue, greedyBid.getValue(issue));}
|
---|
877 | }
|
---|
878 | else {
|
---|
879 | geniusweb.issuevalue.Value lambda = greedyBid.getValue(issue);
|
---|
880 | newBid.put(issue, lambda);
|
---|
881 | }
|
---|
882 | }
|
---|
883 | Bid bid = new Bid(newBid);
|
---|
884 | return bid;
|
---|
885 | }
|
---|
886 |
|
---|
887 | private Bid bidStepUp(Bid ickyBid) {
|
---|
888 | // Take a bid that we think is not good enough
|
---|
889 | // and alter it so that we like it better by changing only one issue value
|
---|
890 | // We want to change the value in the issue with greatest difference = (our issue weight - their issue weight)
|
---|
891 |
|
---|
892 | // TODO perhaps repeat the pick and replace movement until the estimated value > mav****
|
---|
893 | ickyBid = fillBidIssues(ickyBid);
|
---|
894 | String pivotIss = "";
|
---|
895 | Double highestDiff = 0.0;
|
---|
896 | for (String issue: issues) {
|
---|
897 | Double difference = aW.get(issue) -oW.get(issue);
|
---|
898 | if (difference > highestDiff) {
|
---|
899 | highestDiff = difference;
|
---|
900 | pivotIss = issue;
|
---|
901 | }
|
---|
902 | }
|
---|
903 | geniusweb.issuevalue.Value bestLambdaInIssue = (estimatedProfile.getBids().get(estimatedProfile.getBids().size()-1)).getValue(pivotIss);
|
---|
904 | HashMap<String, geniusweb.issuevalue.Value> newBid = new HashMap<String, geniusweb.issuevalue.Value>();
|
---|
905 | for (String issue: issues) {
|
---|
906 | if (issue.equals(pivotIss)) {
|
---|
907 | newBid.put(issue, bestLambdaInIssue);
|
---|
908 | }
|
---|
909 | else {
|
---|
910 | geniusweb.issuevalue.Value lambda = ickyBid.getValue(issue);
|
---|
911 | newBid.put(issue, lambda);
|
---|
912 | }
|
---|
913 | }
|
---|
914 | Bid bid = new Bid(newBid);
|
---|
915 | return bid;
|
---|
916 | }
|
---|
917 |
|
---|
918 | private boolean isFault(ArrayList<Bid> comparison) {
|
---|
919 | boolean fault = false;
|
---|
920 | Double highVal = calculateBidUtility(comparison.get(0));
|
---|
921 | Double lowVal = calculateBidUtility(comparison.get(1));
|
---|
922 | if (highVal<lowVal) {
|
---|
923 | fault = true;
|
---|
924 | }
|
---|
925 | return fault;
|
---|
926 | }
|
---|
927 |
|
---|
928 | public String getName() {
|
---|
929 | return me.toString();
|
---|
930 | }
|
---|
931 |
|
---|
932 |
|
---|
933 | //*****************************************************************************************************************
|
---|
934 | // Routines for Opponent updating
|
---|
935 | private Double calculateBidUtilityOpp(Bid bid) {
|
---|
936 | // Implement the linear additive function U(omega): Omega -> [0,1]
|
---|
937 | // SUM( weight*utility )
|
---|
938 | if (bid==null) {
|
---|
939 | return 0.0;
|
---|
940 | }
|
---|
941 |
|
---|
942 | Double utility = 0.0;
|
---|
943 | HashMap<String, geniusweb.issuevalue.Value> theBid = new HashMap(bid.getIssueValues());
|
---|
944 | for (String issue: theBid.keySet()) {
|
---|
945 | utility += oW.get(issue) * ou.get(issue).get(theBid.get(issue));
|
---|
946 | }
|
---|
947 | return utility;
|
---|
948 | }
|
---|
949 |
|
---|
950 | private int countFaultsOpp(HashSet<ArrayList<Bid>> comparisons) {
|
---|
951 | int numFaults = 0;
|
---|
952 | for (ArrayList<Bid> comparison: comparisons) {
|
---|
953 | Double highVal = calculateBidUtilityOpp(comparison.get(0));
|
---|
954 | Double lowVal = calculateBidUtilityOpp(comparison.get(1));
|
---|
955 | if (highVal<lowVal) {
|
---|
956 | numFaults+=1;
|
---|
957 | }
|
---|
958 | }
|
---|
959 | return numFaults;
|
---|
960 | }
|
---|
961 |
|
---|
962 | private void handleFaultForComparisonOpp(ArrayList<Bid> comparison) {
|
---|
963 | // Given one comparison, this adjusts the estimates for
|
---|
964 | // weights and then issueValue utility so that the
|
---|
965 | // resulting estimates are equal.
|
---|
966 | Bid tooHigh = comparison.get(1);
|
---|
967 | Bid tooLow = comparison.get(0);
|
---|
968 | Double difference = calculateBidUtilityOpp(tooLow) - calculateBidUtilityOpp(tooHigh);
|
---|
969 | HashMap<String, Double> alteredWeights = new HashMap<String, Double>();
|
---|
970 | HashMap<String, Double> originalEst = new HashMap<String, Double>();
|
---|
971 | Double sum=0.0;
|
---|
972 | for (String issue: issues) {
|
---|
973 | Double w_issue = oW.get(issue);
|
---|
974 | Double u_issue = ou.get(issue).get(tooHigh.getValue(issue));
|
---|
975 | originalEst.put(issue, w_issue*u_issue);
|
---|
976 | Double w_alt = 1 + w_issue - oc.get(issue).get(tooHigh.getValue(issue))*difference/issues.size();
|
---|
977 | alteredWeights.put(issue, w_alt);
|
---|
978 | sum += w_alt;
|
---|
979 | }
|
---|
980 | for (String issue: issues) {
|
---|
981 | oW.put(issue, alteredWeights.get(issue)/sum);
|
---|
982 | }
|
---|
983 | Double distributeAmt = 0.0;
|
---|
984 | int count = 1;
|
---|
985 | for (String issue: issues) {
|
---|
986 | if (oc.get(issue).get(tooHigh.getValue(issue)).equals( 1.0)) {
|
---|
987 | count += 1;
|
---|
988 | distributeAmt += originalEst.get(issue)
|
---|
989 | - oW.get(issue)*ou.get(issue).get(tooHigh.getValue(issue));
|
---|
990 | }
|
---|
991 | }
|
---|
992 | for (String issue: issues) {
|
---|
993 | if (!oc.get(issue).get(tooHigh.getValue(issue)).equals( 1.0)) {
|
---|
994 | Double newVal = (originalEst.get(issue)
|
---|
995 | - (difference/issues.size() + distributeAmt/count))/oW.get(issue);
|
---|
996 | ou.get(issue).put(tooHigh.getValue(issue), newVal);
|
---|
997 | }
|
---|
998 | }
|
---|
999 | }
|
---|
1000 |
|
---|
1001 | private void adjustConfidenceForIssueValueOpp(String issue, geniusweb.issuevalue.Value lambda, HashSet<ArrayList<Bid>> comparisons) {
|
---|
1002 | int faults = 0;
|
---|
1003 | for (ArrayList<Bid> comparison: comparisons) {
|
---|
1004 | if (isFault(comparison)) {
|
---|
1005 | Collection<geniusweb.issuevalue.Value> highLambdas = comparison.get(0).getIssueValues().values();
|
---|
1006 | Collection<geniusweb.issuevalue.Value> lowLambdas = comparison.get(1).getIssueValues().values();
|
---|
1007 | if (highLambdas.contains(lambda) || lowLambdas.contains(lambda)) {
|
---|
1008 | faults += 1;
|
---|
1009 | }
|
---|
1010 | }
|
---|
1011 | }
|
---|
1012 | // Adjust the confidence as the average of the old confidence and the new confidence
|
---|
1013 | Double oldConf = oc.get(issue).get(lambda);
|
---|
1014 | Double newConf = (oldConf + (1.0-faults)/comparisons.size())/2;
|
---|
1015 | oc.get(issue).put(lambda, newConf);
|
---|
1016 | }
|
---|
1017 | }
|
---|
1018 |
|
---|
1019 |
|
---|