// WORKING ANGEL AGENT /** * TODO Current changes that need to be made outside of the methods: * 3. Experiment with batch updating for comparisons (requires filling out the method under main routines) */ package geniusweb.exampleparties.simpleshaop; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import java.util.logging.Level; import geniusweb.actions.Accept; import geniusweb.actions.Action; import geniusweb.actions.Comparison; import geniusweb.actions.ElicitComparison; import geniusweb.actions.Offer; import geniusweb.actions.PartyId; import geniusweb.inform.ActionDone; import geniusweb.inform.Finished; import geniusweb.inform.Inform; import geniusweb.inform.Settings; import geniusweb.inform.YourTurn; import geniusweb.issuevalue.Bid; import geniusweb.issuevalue.ValueSet; import geniusweb.party.Capabilities; import geniusweb.party.DefaultParty; import geniusweb.profile.Profile; import geniusweb.profileconnection.ProfileConnectionFactory; import geniusweb.profileconnection.ProfileInterface; import geniusweb.progress.Progress; import geniusweb.progress.ProgressRounds; import tudelft.utilities.logging.Reporter; /** * ANGEL is a Shaop Party that will use an intuitive heuristic to model * opponents and estimate the values of bids. Weights and the utilities of * individual issue values are learned over time. Estimations are made with a * linear additive utility function of weights*utilities, and then additionaly * processed with a confidence measure. Elicitation Requests are made when the * expected value gain exceeds the elicitation cost. * */ public class AngelParty extends DefaultParty { //***************************************************************************************************************** // BACK END variables private PartyId me; // Identifies ANGEL private final Random random = new Random(); // Used for the Gaussian Distribution protected ProfileInterface profileint; // Will allow us to access info about Domain or Profile private Progress progress; // ~TODO~ Understand this // BID SPACE INFORMATION -> remove if there is a super easy way to access this private int m; // m is the number of issues private int d; // d is the number of bids in the partial preference profile private Double e = 0.01; // elicitation cost, denoted 'epsilon' private Double spent = 0.0; // Whenever we elicit, keep track of how much has been spent private Bid reservation; // Best alternative to negotiated agreement private Double reserve_utility; // Reserve is in the form of a bid, not a value, so reserve_utility is an // estimation. private int T; // number of rounds private int t = 0; // current round, updated manually private int tmav = -1; // the last round t on which mav was updated // MEMORY -> recording all of the estimates for updating and evaluating // This models both our human's and the opponent's utility functions private HashSet issues; // Conveniently storing the Issues for access private HashMap issueValues = new HashMap(); // Conveniently storing the IssueValues, accessed via // the Issues private HashMap> au = new HashMap(); // angel // estimated_utilites[issue][issueval] // = estimate private HashMap aW = new HashMap(); // angel estimated_weights[issue] = estimate private HashMap> ac = new HashMap(); // angel // issval_confindences[issue][issueval] // = estimate private HashMap> ou = new HashMap(); // Estimated utilities for // the opponent private HashMap oW = new HashMap(); // Estimated weights for the opponent's issues private HashMap> oc = new HashMap(); // Confidence in ou private Bid highestReceivedBid; // opponents highest offer so far private Bid lastReceivedBid; // last received offer from opponent private Bid lastSentOffer; // our previous offer private int stepsDown = 0; // used to possible pick counter offer if ^^^ do not work // Additional random memory components private HashSet> comparisons = new HashSet(); // Collection of all one-one comparisons, bid1 >= bid2 private HashSet> oppComparisons = new HashSet(); // Comparisons like for ourselves made from opp // offers, models opp private SimpleLinearOrdering estimatedProfile; // The ordering of all known bids // TODO ^ stop using the getUtility method, estimate utility with // function U private boolean already_initialized = false; // Just to make sure that the agent gets initialized properly private Double mav = 1.0; // Minimum accepted value, number to decay when conceding private Bid lastElicitedBid; // Used for filling comparisons //***************************************************************************************************************** // Create agent public AngelParty() { } public AngelParty(Reporter reporter) { super(reporter); // for debugging -> Allegedly } //***************************************************************************************************************** // Have the back end handled // TODO certify that there is no conflict between this and the desired ANGEL // protocol // Probably best to leave these methods alone (for the most part) @Override public void notifyChange(Inform info) { try { if (info instanceof Settings) { Settings settings = (Settings) info; this.profileint = ProfileConnectionFactory.create(settings.getProfile().getURI(), getReporter()); this.estimatedProfile = new SimpleLinearOrdering(profileint.getProfile()); this.reservation = profileint.getProfile().getReservationBid(); // Boy howdy I hope this works this.issues = new HashSet(profileint.getProfile().getDomain().getIssues()); this.m = this.issues.size(); for (String issue : this.issues) { this.issueValues.put(issue, profileint.getProfile().getDomain().getValues(issue)); } this.me = settings.getID(); this.progress = settings.getProgress(); if (settings.getParameters().get("elicitationcost") != null) { this.e = (double) settings.getParameters().get("elicitationcost"); } this.T = ((ProgressRounds) settings.getProgress()).getTotalRounds(); if (!already_initialized) { // System.out.println(getName() + " is initializing information "); // System.out.println(getName()+"'s profile is // "+estimatedProfile.getBids().toString()); // Initialize all estimates, offer our best bid for (Bid bid : estimatedProfile.getBids()) { bid = fillBidIssues(bid); } initAngelComparisons(estimatedProfile); d = estimatedProfile.getBids().size(); Double[] normalUtils = initNormalUtils(); for (int idx = 0; idx < normalUtils.length; idx++) { // System.out.print(" "+normalUtils[idx]); } initAngelWeights(estimatedProfile, normalUtils); initOppWeights(); initAngelUtils(estimatedProfile, normalUtils); initOppUtils(); initAngelConfidences(estimatedProfile); initOppConfidences(); // System.out.println(getName()+" init the utils to " +au); // System.out.println(getName()+" init the weights to " +aW); // System.out.println("Just after initialization, there are this many faults: // "+countFaults(comparisons)+" out of "+comparisons.size()+" comparisons"); // Now adjust to see if the number of faults goes down significantly / at all // move through new comparisons and perform fault adjustments for (ArrayList comparison : comparisons) { if (isFault(comparison)) { handleFaultForComparison(comparison); } } // adjust confidences for (String issue : issues) { for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { adjustConfidenceForIssueValue(issue, lambda, comparisons); } } // System.out.println("Now there are this many faults: // "+countFaults(comparisons)+" out of "+comparisons.size()+" comparisons"); } } else if (info instanceof ActionDone) { Action otheract = ((ActionDone) info).getAction(); // System.out.println(getName()+" received action "+ otheract.toString()); if (otheract instanceof Offer) { lastReceivedBid = ((Offer) otheract).getBid(); lastReceivedBid = fillBidIssues(lastReceivedBid); // System.out.println(getName()+" evaluated the offer "+ lastReceivedBid+" as // "+calculateBidUtility(lastReceivedBid)); double lastUtil = calculateBidUtility(lastReceivedBid); double bestUtil = calculateBidUtility(highestReceivedBid); if (lastUtil > bestUtil) { highestReceivedBid = lastReceivedBid; } } else if (otheract instanceof Comparison) { estimatedProfile = estimatedProfile.with(((Comparison) otheract).getBid(), ((Comparison) otheract).getWorse()); for (Bid better : ((Comparison) otheract).getBetter()) { ArrayList comparison = new ArrayList(); comparison.add(better); comparison.add(lastElicitedBid); comparisons.add(comparison); } HashSet> newComparisons = new HashSet(); for (Bid worse : ((Comparison) otheract).getWorse()) { ArrayList comparison = new ArrayList(); comparison.add(lastElicitedBid); comparison.add(worse); newComparisons.add(comparison); } // add each new comparison to comparisons for (ArrayList comparison : newComparisons) { comparisons.add(comparison); } int beforeFaultsNewInfo = countFaults(newComparisons); int beforeFaultsTotalInfo = countFaults(comparisons); // move through new comparisons and perform fault adjustments for (ArrayList comparison : newComparisons) { if (isFault(comparison)) { handleFaultForComparison(comparison); } } // adjust confidences for (String issue : issues) { for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { adjustConfidenceForIssueValue(issue, lambda, newComparisons); } } // System.out.println(getName()+" updated personal estimates, weights are now: // "+aW.toString()); int afterFaultsNewInfo = countFaults(newComparisons); int afterFaultsTotalInfo = countFaults(comparisons); // TODO log these results if they were unfavorable if (beforeFaultsNewInfo < afterFaultsNewInfo) { // System.out.println("%%%%%%%%% Faults in new info before update: "+ // beforeFaultsNewInfo +" after: "+afterFaultsNewInfo); } if (beforeFaultsTotalInfo < afterFaultsTotalInfo) { // System.out.println("%%%%%%%%% Faults in total before update: // "+beforeFaultsTotalInfo+" after: "+afterFaultsTotalInfo); } try { myTurn(); } catch (Exception e) { throw new RuntimeException("Error inside myTurn", e); } } } else if (info instanceof YourTurn) { try { myTurn(); } catch (Exception e) { throw new RuntimeException("Error inside myTurn", e); } } else if (info instanceof Finished) { // System.out.println(getName()+" received info -> Finished"); getReporter().log(Level.INFO, "Final ourcome:" + info); } } catch (Exception e) { throw new RuntimeException("Failed to handle info", e); } } @Override public Capabilities getCapabilities() { return new Capabilities(new HashSet<>(Arrays.asList("SHAOP")), Collections.singleton(Profile.class)); } @Override public String getDescription() { return "Greedy concession strategy, elicits information from COB " + "when there is low confidence in best counter-offer prediction. Requires elicitationcost parameter to be set." + " Original design by Andrew DeVoss and Robert Geraghty"; } //***************************************************************************************************************** // Executing the heuristic by calling the methods defined later // code the ANGEL protocol by ^ /** * Called when it's (still) our turn and we should take some action. Also * Updates the progress if necessary. */ private void myTurn() throws IOException { Action action = null; // System.out.println(getName()+" is taking a turn "); t += 1; // Update the turn, making sure that this is counter-balanced if an elicitation // request is made. if (t != tmav) { recalculateMAV(T, t); // System.out.println(getName()+" has a new mav of "+mav+" on round "+t); } if (!already_initialized) { Bid bestBid = estimatedProfile.getBids().get(estimatedProfile.getBids().size() - 1); lastSentOffer = bestBid; action = new Offer(me, bestBid); already_initialized = true; if (progress instanceof ProgressRounds) { progress = ((ProgressRounds) progress).advance(); } } Bid co = counterOffer(lastReceivedBid, highestReceivedBid, lastSentOffer); if (shouldElicit(co, e) && action == null && co != lastElicitedBid) { // System.out.println(getName() + " is eliciting info about "+co); action = new ElicitComparison(me, co, estimatedProfile.getBids()); tmav = t; lastElicitedBid = co; // Note: estimates updated from the inform change method, not here. } if (action == null && lastReceivedBid != null) { // check to see if we want to accept the opponent's offer boolean acceptable = isGood(lastReceivedBid, mav); if (acceptable) { // System.out.println(getName() + " is accepting an offer: mav="+mav+" expected // value="+calculateBidUtility(lastReceivedBid)); action = new Accept(me, lastReceivedBid); } // TODO update opponent estimates based on their offer // every time they reject our offer and give a new co, we assume opp believes co // >= angel_last_offer else if (lastSentOffer != null) { // System.out.println(getName() + " is updating opponent model "); ArrayList comparison = new ArrayList(); comparison.add(lastReceivedBid); comparison.add(lastSentOffer); oppComparisons.add(comparison); // Since we have new information, update the utility estimates based on the // known space. int beforeFaults = countFaultsOpp(oppComparisons); for (ArrayList comp : oppComparisons) { if (isFault(comp)) { handleFaultForComparisonOpp(comp); } } // Now update the opp confidence estimations based on the results. for (String issue : issues) { for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { adjustConfidenceForIssueValueOpp(issue, lambda, oppComparisons); } } int afterFaults = countFaultsOpp(oppComparisons); if (beforeFaults < afterFaults) { ; } } if (progress instanceof ProgressRounds) { progress = ((ProgressRounds) progress).advance(); } } // If we don't accept, package the next bid, see if we should elicit, repackage // when necessary if (action == null && co != lastElicitedBid && shouldElicit(co, e)) { // System.out.println(getName() + " is eliciting info about "+co); action = new ElicitComparison(me, co, estimatedProfile.getBids()); tmav = t; t -= 1; lastElicitedBid = co; // Note: estimates updated from the inform change method, not here. } if (action == null) { // System.out.println(getName() + " is offering bid "+co+" with expected utility // "+calculateBidUtility(co)); action = new Offer(me, co); lastSentOffer = co; } // System.out.println(getName()+"'s action is "+action.toString()); getConnection().send(action); } //***************************************************************************************************************** // Initialization routines private Double[] initNormalUtils() { // Use these utilities for initial bid evaluations Double[] normalUtils = new Double[d]; normalUtils[0] = 0.0; normalUtils[d - 1] = 1.0; // now create d - 2 comparisons by sampling from N(.5, 1), clipping to [0, 1], // and then ordering Double[] tempNorms = new Double[d - 2]; for (int i = 0; i < d - 2; i++) { Double contender = (random.nextGaussian() + .5); while (contender < 0 || contender > 1) { contender = (random.nextGaussian() + .5); } tempNorms[i] = contender; } Arrays.sort(tempNorms); for (int i = 0; i < d - 2; i++) { normalUtils[i + 1] = tempNorms[i]; } return normalUtils; } private void initAngelWeights(SimpleLinearOrdering providedBids, Double[] utils) { // Iterate over the ordering of bids. Work with bid if it contains lambda in // best/worst bids // weight = avg(bid with best lambda) - avg(bid with worst lambda) HashMap bestLambdas = new HashMap(); HashMap worstLambdas = new HashMap(); List bids = providedBids.getBids(); // I checked, this is actually sorted for (String issue : issues) { bestLambdas.put(issue, bids.get(d - 1).getValue(issue)); } for (String issue : issues) { worstLambdas.put(issue, bids.get(0).getValue(issue)); } HashMap highSums = new HashMap(); HashMap highCounts = new HashMap(); HashMap lowSums = new HashMap(); HashMap lowCounts = new HashMap(); // Fill the initial sums and counts with 0s for (String issue : issues) { highSums.put(issue, 0.0); lowSums.put(issue, 0.0); highCounts.put(issue, 0); lowCounts.put(issue, 0); } // Even though we are iterating over all of the bids, we need to know the bid // number // so that we can retrieve the corresponding bid utility. for (int bid = 0; bid < d; bid++) { Bid curr = bids.get(bid); for (String issue : issues) { if (curr.getValue(issue).equals(bestLambdas.get(issue))) { highSums.put(issue, highSums.get(issue) + utils[bid]); highCounts.put(issue, highCounts.get(issue) + 1); } if (curr.getValue(issue).equals(worstLambdas.get(issue))) { lowSums.put(issue, lowSums.get(issue) + utils[bid]); lowCounts.put(issue, lowCounts.get(issue) + 1); } } } // Now we have a count of total val of issues with lambda in best and worst // bids, // and associated counts for computing the average. // We want to compute the average, and then normalize by the sum. // Add 1.0 to each value (ensuring positive) then divide by sum. HashMap weights = new HashMap(); Double sum = 0.0; for (String issue : issues) { Double amt = highSums.get(issue) / highCounts.get(issue) - lowSums.get(issue) / lowCounts.get(issue) + 1.0; weights.put(issue, amt); sum += amt; } for (String issue : issues) { aW.put(issue, weights.get(issue) / sum); } } private void initOppWeights() { // Initialize the weights for the opponent estimates Double avg = 1.0 / m; for (String issue : issues) { oW.put(issue, avg); } } private void initAngelUtils(SimpleLinearOrdering providedBids, Double[] utils) { // If this is not efficient enough, we could instead loop through all of the // bids once // and update values for each of the issue values inside during the singular // pass through. // I chose to do it this way because it feels more intuitive, though less // efficient. -Andrew // For each issue value: // utility estimate is avg(bid containing issue value) // If there is an issue value that is not present in any of the bids, // it gets the median value of all issue values in same issue. List bids = providedBids.getBids(); for (String issue : issues) { HashSet unseenLambdas = new HashSet(); au.put(issue, new HashMap()); for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { // If lambda for the issue is in the best bid, set the utility to 1 Bid bestBid = bids.get(d - 1); Bid worstBid = bids.get(0); geniusweb.issuevalue.Value bestLambda = bestBid.getValue(issue); geniusweb.issuevalue.Value worstLambda = worstBid.getValue(issue); if (bestLambda.equals(lambda)) { au.get(issue).put(lambda, 1.0); } else if (worstLambda.equals(lambda)) { au.get(issue).put(lambda, 0.0); } // Now loop through all of the remaining bids, checking if bid[issue] has // lambda. else { int count = 0; Double sum = 0.0; for (int bid = 1; bid < d - 1; bid++) { geniusweb.issuevalue.Value lambdaPrime = bids.get(bid).getValue(issue); if (lambdaPrime.equals(lambda)) { count += 1; sum += utils[bid]; } } if (count == 0) { unseenLambdas.add(lambda); } else { au.get(issue).put(lambda, sum / count); } } } // Finally, assign to each of the unseen lambdas the MEDIAN value of all of the // other issue values within the same issue. // Get the MEDIAN List sortedValues = new ArrayList(au.get(issue).values()); Collections.sort(sortedValues); int knownLambdas = sortedValues.size(); Double median; if (knownLambdas % 2 == 0) { int firstIdx = knownLambdas / 2; int secondIdx = firstIdx - 1; Double firstVal = sortedValues.get(firstIdx); Double secondVal = sortedValues.get(secondIdx); median = (firstVal + secondVal / 2.0); } else { int idx = knownLambdas / 2; median = sortedValues.get(idx); } // Now assign the median value to every lambda in unseenLambdas for (geniusweb.issuevalue.Value lambda : unseenLambdas) { au.get(issue).put(lambda, median); } } } private void initOppUtils() { // Init the opponent utility estimates. They start as .5 for each lambda for (String issue : issues) { ou.put(issue, new HashMap()); for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { ou.get(issue).put(lambda, 0.5); } } } private void initAngelConfidences(SimpleLinearOrdering providedBids) { // Confidence in lambdas in best/worst bids = 1 // otherwise confidence = .8 List bids = providedBids.getBids(); Bid bestBid = bids.get(d - 1); Bid worstBid = bids.get(0); for (String issue : issues) { ac.put(issue, new HashMap()); for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { if (bestBid.getValue(issue).equals(lambda) || worstBid.getValue(issue).equals(lambda)) { ac.get(issue).put(lambda, 1.0); } else { ac.get(issue).put(lambda, 0.8); } } } } private void initOppConfidences() { // Confidence in all values for the opponent start at .5 for (String issue : issues) { oc.put(issue, new HashMap()); for (geniusweb.issuevalue.Value lambda : issueValues.get(issue)) { oc.get(issue).put(lambda, 0.8); } } } private void initAngelComparisons(SimpleLinearOrdering providedBids) { List bids = providedBids.getBids(); int stop = bids.size(); for (int lowIdx = 0; lowIdx < stop; lowIdx++) { for (int highIdx = 0; highIdx < stop; highIdx++) { Bid low = bids.get(lowIdx); Bid high = bids.get(highIdx); ArrayList comparison = new ArrayList(); comparison.add(high); comparison.add(low); comparisons.add(comparison); } } } //***************************************************************************************************************** // Main routines for ANGEL during the action of the negotiation private Bid fillBidIssues(Bid bid) { // If a bid does not contain a value for every issue, then fill it // with the item worth 0 from that issue (from the worst bid) if (bid.getIssues().size() == issues.size()) { return bid; } // System.out.println(" FILLING A BID "); HashMap newBid = new HashMap(); Set hadIssues = bid.getIssues(); for (String issue : issues) { if (hadIssues.contains(issue)) { newBid.put(issue, bid.getValue(issue)); } else { geniusweb.issuevalue.Value lambda = estimatedProfile.getBids().get(0).getValue(issue); newBid.put(issue, lambda); } } Bid filledBid = new Bid(newBid); return filledBid; } private void handleFaultForComparison(ArrayList comparison) { // Given one comparison, this adjusts the estimates for // weights and then issueValue utility so that the // resulting estimates are equal. Bid tooHigh = comparison.get(1); Bid tooLow = comparison.get(0); Double difference = calculateBidUtility(tooLow) - calculateBidUtility(tooHigh); HashMap alteredWeights = new HashMap(); HashMap originalEst = new HashMap(); Double sum = 0.0; for (String issue : issues) { Double w_issue = aW.get(issue); Double u_issue = au.get(issue).get(tooHigh.getValue(issue)); originalEst.put(issue, w_issue * u_issue); Double w_alt = 1 + w_issue - ac.get(issue).get(tooHigh.getValue(issue)) * difference / issues.size(); alteredWeights.put(issue, w_alt); sum += w_alt; } double temp_sum = 0.0; for (String issue : issues) { aW.put(issue, alteredWeights.get(issue) / sum); temp_sum += alteredWeights.get(issue) / sum; } assert (temp_sum - 1.0 < .00001 && temp_sum - 1.0 > -.00001); Double distributeAmt = 0.0; int count = 1; for (String issue : issues) { if (ac.get(issue).get(tooHigh.getValue(issue)).equals(1.0)) { count += 1; distributeAmt += originalEst.get(issue) - aW.get(issue) * au.get(issue).get(tooHigh.getValue(issue)); } } for (String issue : issues) { if (!ac.get(issue).get(tooHigh.getValue(issue)).equals(1.0)) { Double newVal = (originalEst.get(issue) - (difference / issues.size() + distributeAmt / count)) / aW.get(issue); // TODO, figure out why this might be happening if (newVal > .99) { newVal = .99; } au.get(issue).put(tooHigh.getValue(issue), newVal); } } } private void handleFaultForBatchOfComparisons(HashSet> comparisons) { // TODO write this method for updating utility and weight estimates from // new information all at once, if we want that functionality. ; } private int countFaults(HashSet> comparisons) { // Note: A comparison is a collection of two bids such that bid1 >= bid2. // This method iterates over a collection of comparisons and // counts the number of faults in the collection. int numFaults = 0; for (ArrayList comparison : comparisons) { Double highVal = calculateBidUtility(comparison.get(0)); Double lowVal = calculateBidUtility(comparison.get(1)); if (highVal < lowVal) { numFaults += 1; } } return numFaults; } private void adjustConfidenceForIssueValue(String issue, geniusweb.issuevalue.Value lambda, HashSet> comparisons) { // Goes through a collection of comparisons and counts the number of faults // where lambda is in a bid. Then confidence is adjusted. if (ac.get(issue).get(lambda).equals(1.0)) { return; } int faults = 0; for (ArrayList comparison : comparisons) { Double highVal = calculateBidUtility(comparison.get(0)); Double lowVal = calculateBidUtility(comparison.get(1)); if (highVal < lowVal) { Bid highBid = comparison.get(0); Bid lowBid = comparison.get(1); if (highBid.getValue(issue).equals(lambda) || lowBid.getValue(issue).equals(lambda)) { faults += 1; } } } // Adjust the confidence as the average of the old confidence and the new // confidence Double oldConf = ac.get(issue).get(lambda); Double newConf = (oldConf + (1.0 - faults) / comparisons.size()) / 2; ac.get(issue).put(lambda, newConf); } private void recalculateMAV(int Rounds, int round) { // Decay the MAV over the rounds, conservatively at first. // For rounds<80% of Rounds, decay 10% of the way // For rounds 80%-90% of Rounds, decay 20% of the way // For rounds > 90%, decay the rest of the way // The decay is linear between 1 and max(highestOppOfferUtil, reservationUtil) Double hiOpp = calculateBidUtility(highestReceivedBid); Double reserve = calculateBidUtility(reservation); // change mav to slightly lower than hiOpp to prevent being overly greedy in the // final rounds if (hiOpp > reserve) { hiOpp = hiOpp - .2 * (hiOpp - reserve); } Double end = Math.max(reserve, hiOpp); if (round <= .7 * Rounds) { Double y2 = 1.0; Double y1 = (1 - .3 * (1 - end)); int x2 = 0; Double x1 = .7 * Rounds; Double m = (y2 - y1) / (x2 - x1); this.mav = m * round + 1; } else if (round <= .8 * Rounds) { Double y2 = (1 - .3 * (1 - end)); Double y1 = (1 - .5 * (1 - end)); Double x2 = .7 * Rounds; Double x1 = .8 * Rounds; Double m = (y2 - y1) / (x2 - x1); Double b = y2 - (m * x2); this.mav = m * round + b; } else { Double y2 = (1 - .5 * (1 - end)); Double y1 = end; Double x2 = .8 * Rounds; int x1 = Rounds; Double m = (y2 - y1) / (x2 - x1); Double b = y2 - (m * x2); this.mav = m * round + b; } if (mav < Math.max(reserve, hiOpp)) { this.mav = Math.max(reserve, hiOpp); } } private Bid counterOffer(Bid previousOpponentOffer, Bid highestOpponentOffer, Bid previousAngelOffer) { // Main idea: create three counter offers by adjusting // our previous offer, opp previous offer, opp highest offer // ensure that the values are higher than the MAV // select the offer with the highest expected return weighted by confidence(?) // For the previousOpponentOffer and the highestOpponentOffer, we want to call // the bidStepUp method // For previousAngelOffer, call the bidStepDown method. // This is just a temporary idea for how to package the next counter-offer until // we get the agent functional. // check the ****SCALED?***** utility of each of the packaged offers. If none of // them are higher than // the minimum accepted value, check the previous offer. If it is also evaluated // as not high enough // then instead, default to the best bid in our bid list. Bid bid1 = null; Bid bid2 = null; Bid bid3 = null; if (previousOpponentOffer != null) { bid1 = bidStepUp(previousOpponentOffer); } if (highestOpponentOffer != null) { bid2 = bidStepUp(highestOpponentOffer); } if (previousAngelOffer != null) { bid3 = bidStepDown(previousAngelOffer); } Double est1 = calculateBidUtility(bid1); Double est2 = calculateBidUtility(bid2); Double est3 = calculateBidUtility(bid3); if (est1 >= mav && est1 >= est2 && est1 >= est3) { // System.out.println(getName()+" Made counter offer by modifying prev opp // offer"); return bid1; } else if (est2 >= mav && est2 >= est1 && est2 >= est3) { // System.out.println(getName()+" Made counter offer by modifying highest opp // offer"); return bid2; } else if (est3 >= mav && est3 >= est1 && est3 >= est2) { // System.out.println(getName()+" Made counter offer by modifying previous own // offer"); return bid3; } else if (stepsDown < d && calculateBidUtility(estimatedProfile.getBids().get(d - 1 - stepsDown)) > mav) { // System.out.println(getName()+" Made counter offer by stepping down in // profile"); Bid known = estimatedProfile.getBids().get(d - 1 - stepsDown); stepsDown += 1; return known; } else if (calculateBidUtility(previousAngelOffer) > mav) { // System.out.println(getName()+" Made counter offer by sending previous offer // again "); return previousAngelOffer; } else { // System.out.println(getName()+" DEFAULTED TO BEST BID CO "); return estimatedProfile.getBids().get(estimatedProfile.getBids().size() - 1); } } private boolean shouldElicit(Bid counteroffer, Double elicitationCost) { // Is the expected value gained > elicitation cost if (estimatedProfile.contains(counteroffer)) { return false; } boolean needMoreInfo = false; Double expectedValueOfKnowledge = calculateBidUtility(counteroffer) - calculateConfidenceScaledBidUtility(counteroffer); // System.out.println(getName()+"'s expected value of knowledge is // "+expectedValueOfKnowledge); if (expectedValueOfKnowledge > spent + elicitationCost) { spent += elicitationCost; needMoreInfo = true; } return needMoreInfo; } //***************************************************************************************************************** // Random subroutines that flush the main routines private boolean isGood(Bid bid, Double mav) { // Check that the estimated utility of the bid is higher // than the minimum accepted utility // Make sure not to accept a bid known to be worse than reservation if (estimatedProfile.contains(bid) && estimatedProfile.contains(reservation)) { int bidx = 0; int ridx = 0; for (int i = 0; i < estimatedProfile.getBids().size(); i++) { if (estimatedProfile.getBids().get(i).equals(bid)) { bidx = i; } else if (estimatedProfile.getBids().get(i).equals(reservation)) { ridx = i; } } if (bidx < ridx) { return false; } } for (ArrayList comparison : comparisons) { if (comparison.get(0).equals(reservation) && comparison.get(0).equals(bid)) { return false; } } boolean acceptable = false; Double expectedValue = calculateBidUtility(bid); if (expectedValue >= mav) { acceptable = true; } return acceptable; } private Double calculateBidUtility(Bid bid) { // Implement the linear additive function U(omega): Omega -> [0,1] // SUM( weight*utility ) if (bid == null) { return 0.0; } Double utility = 0.0; HashMap> vals = new HashMap>(); HashMap theBid = new HashMap(bid.getIssueValues()); for (String issue : theBid.keySet()) { utility += aW.get(issue) * au.get(issue).get(theBid.get(issue)); ArrayList weightUtil = new ArrayList(); weightUtil.add(aW.get(issue)); weightUtil.add(au.get(issue).get(theBid.get(issue))); vals.put(issue, weightUtil); } if (utility > 1.0) { // System.out.println("\n\n\n*****UTILITY OF "+utility+"\n\n\n"); // System.out.println(vals.toString()); assert (!(utility > 1.00001)); } return utility; } private Double calculateConfidenceScaledBidUtility(Bid bid) { // scaled estimation = SUM( weight*utility*confidence ) if (bid == null) { return 0.0; } Double scaledUtility = 0.0; HashMap theBid = new HashMap( bid.getIssueValues()); for (String issue : theBid.keySet()) { scaledUtility += aW.get(issue) * au.get(issue).get(theBid.get(issue)) * ac.get(issue).get(theBid.get(issue)); } return scaledUtility; } private Bid bidStepDown(Bid greedyBid) { // Take a bid that we consider really good // and slightly lower the value of it with opponent model, changing only one // issue value // We want to change the value in the issue with greatest difference = (their // issue weight - our issue weight) // TODO perhaps repeat the pick and replace movement until the estimated value > // mav**** greedyBid = fillBidIssues(greedyBid); String pivotIss = ""; for (String issue : issues) { pivotIss = issue; break; } Double highestDiff = 0.0; for (String issue : issues) { Double difference = oW.get(issue) - aW.get(issue); if (difference > highestDiff) { highestDiff = difference; pivotIss = issue; } } geniusweb.issuevalue.Value nextLowestLambda = null; Double low = -2.0; Double high = au.get(pivotIss).get(greedyBid.getValue(pivotIss)); for (geniusweb.issuevalue.Value lambda : issueValues.get(pivotIss)) { Double util = au.get(pivotIss).get(lambda); if (util > low && util < high) { low = util; nextLowestLambda = lambda; } } HashMap newBid = new HashMap(); for (String issue : issues) { if (issue.equals(pivotIss)) { if (nextLowestLambda != null) { newBid.put(issue, nextLowestLambda); } else { newBid.put(issue, greedyBid.getValue(issue)); } } else { geniusweb.issuevalue.Value lambda = greedyBid.getValue(issue); newBid.put(issue, lambda); } } Bid bid = new Bid(newBid); return bid; } private Bid bidStepUp(Bid ickyBid) { // Take a bid that we think is not good enough // and alter it so that we like it better by changing only one issue value // We want to change the value in the issue with greatest difference = (our // issue weight - their issue weight) // TODO perhaps repeat the pick and replace movement until the estimated value > // mav**** ickyBid = fillBidIssues(ickyBid); String pivotIss = ""; Double highestDiff = 0.0; for (String issue : issues) { Double difference = aW.get(issue) - oW.get(issue); if (difference > highestDiff) { highestDiff = difference; pivotIss = issue; } } geniusweb.issuevalue.Value bestLambdaInIssue = (estimatedProfile.getBids() .get(estimatedProfile.getBids().size() - 1)).getValue(pivotIss); HashMap newBid = new HashMap(); for (String issue : issues) { if (issue.equals(pivotIss)) { newBid.put(issue, bestLambdaInIssue); } else { geniusweb.issuevalue.Value lambda = ickyBid.getValue(issue); newBid.put(issue, lambda); } } Bid bid = new Bid(newBid); return bid; } private boolean isFault(ArrayList comparison) { boolean fault = false; Double highVal = calculateBidUtility(comparison.get(0)); Double lowVal = calculateBidUtility(comparison.get(1)); if (highVal < lowVal) { fault = true; } return fault; } public String getName() { return me.toString(); } //***************************************************************************************************************** // Routines for Opponent updating private Double calculateBidUtilityOpp(Bid bid) { // Implement the linear additive function U(omega): Omega -> [0,1] // SUM( weight*utility ) if (bid == null) { return 0.0; } Double utility = 0.0; HashMap theBid = new HashMap(bid.getIssueValues()); for (String issue : theBid.keySet()) { utility += oW.get(issue) * ou.get(issue).get(theBid.get(issue)); } return utility; } private int countFaultsOpp(HashSet> comparisons) { int numFaults = 0; for (ArrayList comparison : comparisons) { Double highVal = calculateBidUtilityOpp(comparison.get(0)); Double lowVal = calculateBidUtilityOpp(comparison.get(1)); if (highVal < lowVal) { numFaults += 1; } } return numFaults; } private void handleFaultForComparisonOpp(ArrayList comparison) { // Given one comparison, this adjusts the estimates for // weights and then issueValue utility so that the // resulting estimates are equal. Bid tooHigh = comparison.get(1); Bid tooLow = comparison.get(0); Double difference = calculateBidUtilityOpp(tooLow) - calculateBidUtilityOpp(tooHigh); HashMap alteredWeights = new HashMap(); HashMap originalEst = new HashMap(); Double sum = 0.0; for (String issue : issues) { Double w_issue = oW.get(issue); Double u_issue = ou.get(issue).get(tooHigh.getValue(issue)); originalEst.put(issue, w_issue * u_issue); Double w_alt = 1 + w_issue - oc.get(issue).get(tooHigh.getValue(issue)) * difference / issues.size(); alteredWeights.put(issue, w_alt); sum += w_alt; } for (String issue : issues) { oW.put(issue, alteredWeights.get(issue) / sum); } Double distributeAmt = 0.0; int count = 1; for (String issue : issues) { if (oc.get(issue).get(tooHigh.getValue(issue)).equals(1.0)) { count += 1; distributeAmt += originalEst.get(issue) - oW.get(issue) * ou.get(issue).get(tooHigh.getValue(issue)); } } for (String issue : issues) { if (!oc.get(issue).get(tooHigh.getValue(issue)).equals(1.0)) { Double newVal = (originalEst.get(issue) - (difference / issues.size() + distributeAmt / count)) / oW.get(issue); ou.get(issue).put(tooHigh.getValue(issue), newVal); } } } private void adjustConfidenceForIssueValueOpp(String issue, geniusweb.issuevalue.Value lambda, HashSet> comparisons) { int faults = 0; for (ArrayList comparison : comparisons) { if (isFault(comparison)) { Collection highLambdas = comparison.get(0).getIssueValues().values(); Collection lowLambdas = comparison.get(1).getIssueValues().values(); if (highLambdas.contains(lambda) || lowLambdas.contains(lambda)) { faults += 1; } } } // Adjust the confidence as the average of the old confidence and the new // confidence Double oldConf = oc.get(issue).get(lambda); Double newConf = (oldConf + (1.0 - faults) / comparisons.size()) / 2; oc.get(issue).put(lambda, newConf); } }