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