package geniusweb.exampleparties.simpleshaop; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import geniusweb.issuevalue.Bid; import geniusweb.issuevalue.Domain; import geniusweb.issuevalue.Value; import geniusweb.issuevalue.ValueSet; import geniusweb.profile.Profile; public class CompRegress { private final Domain domain; private final int numIssues; private final Bid minBid; private final Bid maxBid; private int totalNonMinMax; private Map indicatorBidMap; private Map> estUtil; private Map> nonMinMaxIndex; private Map numValuesPerIssue; private List issueOrder; // list of issues in ascending importance private double[] estTheta; // smallest to largest theta values for corresponding issues private Map> fullBidOrdering; CompRegress(Profile profile, List initBidOrder, HashMap indicatorBidMap) { this.domain = profile.getDomain(); this.numIssues = domain.getIssues().size(); this.minBid = initBidOrder.get(0); this.maxBid = initBidOrder.get(initBidOrder.size()-1); this.indicatorBidMap = indicatorBidMap; this.issueOrder = new ArrayList(); this.estTheta = new double[numIssues]; this.estUtil = new HashMap>(); this.nonMinMaxIndex = new HashMap>(); this.numValuesPerIssue = new HashMap(); this.totalNonMinMax = 0; initImportance(initBidOrder); initEstUtilMap(); fit(initBidOrder); } //////////////////////////////////////////////////////////////////////////////// // initialization methods //////////////////////////////////////////////////////////////////////////////// /** * initialize the theta values and importance ordering of the issues * @param initBidOrder - initial ordered bid list with the indicator bids */ private void initImportance(List initBidOrder) { // initialize issue importance ordered list int count = numIssues; int index = initBidOrder.size() - 2; while (count > 0) { Bid bid = initBidOrder.get(index); String issue = indicatorBidMap.get(bid); if (issue != null) { issueOrder.add(issue); count--; } index--; } // initialize theta values double multInverse = 1.0 / numIssues; double minTheta = multInverse / 2.0; double incr = multInverse / (numIssues - 1); for (int i = 0; i < numIssues; i++) { estTheta[i] = minTheta + (incr * i); } } /** * initialize the HashMap of estimated utilities for each value */ public void initEstUtilMap() { for (String issue : issueOrder) { ValueSet values = domain.getValues(issue); int numValues = values.size().intValue(); numValuesPerIssue.put(issue, numValues); totalNonMinMax += numValues - 2; estUtil.put(issue, new HashMap()); nonMinMaxIndex.put(issue, new ArrayList()); int count = 1; for (Value value : values) { if (value.equals(maxBid.getValue(issue))) { estUtil.get(issue).put(value, BigDecimal.ONE); } else if (value.equals(minBid.getValue(issue))) { estUtil.get(issue).put(value, BigDecimal.ZERO); } else { estUtil.get(issue).put(value, BigDecimal.valueOf(0.5 + 0.001 * (count - 0.5*(numValues-1)))); nonMinMaxIndex.get(issue).add(value); count++; } } } } //////////////////////////////////////////////////////////////////////////////// // utility function learner fit method //////////////////////////////////////////////////////////////////////////////// /** * updates the value utilities given a list of ordered Bids * @param orderedBids - a list of ordered bids with ascending utility */ public void fit(List orderedBids) { int numBids = orderedBids.size(); int numConstraints = numBids - 1; Calcfc calcfc = setCalcfc(orderedBids); double[] x = flattenEstUtil(); double rhobeg = 1.0; double rhoend = 2.0e-4; int iprint = 0; int maxfun = 1000; Cobyla.FindMinimum(calcfc, totalNonMinMax, numConstraints, x, rhobeg, rhoend, iprint, maxfun); updateEstUtil(x); createFullOrdering(); } /** * the function to be minimized in fit() * @param orderedBids - a list of ordered bids with ascending utility * @return a Calcfc to be used in fit() */ private Calcfc setCalcfc(List orderedBids) { int numBids = orderedBids.size(); Calcfc calcfc = new Calcfc() { @Override public double Compute(int n, int m, double[] x, double[] con) { double[] bidUtil = new double[numBids]; for (int i = 0; i < numBids; i++) { Bid bid = orderedBids.get(i); bidUtil[i] = 0.0; int baseIndex = 0; for (int j = 0; j < numIssues; j++) { String issue = issueOrder.get(j); Value value = bid.getValue(issue); int index = nonMinMaxIndex.get(issue).indexOf(value); if (index >= 0) { bidUtil[i] += x[baseIndex + index] * estTheta[j]; } else { bidUtil[i] += estUtil.get(issue).get(value). doubleValue() * estTheta[j]; } baseIndex += numValuesPerIssue.get(issue) - 2; } } // constraints double tolerance = 0.0; for (int i = 0; i < numBids - 1; i++) { con[i] = bidUtil[i+1] - (bidUtil[i] + tolerance); } // penalize inversions double err = 0.0; for (int i = 0; i < numBids - 1; i++) { if (bidUtil[i] > bidUtil[i+1]) err += bidUtil[i] - bidUtil[i+1]; } return err/n; } }; return calcfc; } /** * flatten the estUtil values into a single double array * @return a double array that contains the initial estimated utilities */ private double[] flattenEstUtil() { double[] x = new double[totalNonMinMax]; int baseIndex = 0; for (String issue : issueOrder) { List valuesIndex = nonMinMaxIndex.get(issue); int numValues = valuesIndex.size(); for (int i = 0; i< numValues; i++) { x[baseIndex + i] = estUtil.get(issue). get(valuesIndex.get(i)).doubleValue(); } baseIndex += numValues; } return x; } /** * update the estUtil values form a single double array * @param x - a double array that contains the calculated estimated utilities */ private void updateEstUtil(double[] x) { int baseIndex = 0; for (String issue : issueOrder) { List valuesIndex = nonMinMaxIndex.get(issue); int numValues = valuesIndex.size(); for (int i = 0; i < numValues; i++) { estUtil.get(issue).put(valuesIndex.get(i), BigDecimal.valueOf(x[baseIndex + i])); } baseIndex += numValues; } } /** * creates full ordering of bids according to utility function */ private void createFullOrdering() { this.fullBidOrdering = new TreeMap>(Collections.reverseOrder()); this.ordering(0, maxBid); } /** * recursive helper method to create bid ordering * @param i - counter * @param bid - base bid */ private void ordering(int i, Bid bid) { if (i < issueOrder.size()) { String issue = issueOrder.get(i); ValueSet values = domain.getValues(issue); for (Value value : values) { Bid newBid = putValue(bid, issue, value); BigDecimal util = getUtil(newBid); List bidsList; if (fullBidOrdering.get(util) == null) bidsList = new ArrayList(); else bidsList = fullBidOrdering.get(util); bidsList.add(newBid); fullBidOrdering.put(this.getUtil(newBid), bidsList); this.ordering(i+1, newBid); } } } //////////////////////////////////////////////////////////////////////////////// // public helper methods //////////////////////////////////////////////////////////////////////////////// /** * calculates the total utility of a bid from the current parameters * @param bid - a bid to calculate its utility * @return BigDecimal; the estimated utility */ public BigDecimal getUtil(Bid bid) { if (bid == null) return BigDecimal.ZERO; else { BigDecimal totalUtil = BigDecimal.ZERO; for (int i = 0; i < numIssues; i++) { String issue = issueOrder.get(i); Value value = bid.getValue(issue); BigDecimal util = estUtil.get(issue).get(value); totalUtil = totalUtil.add(util.multiply(BigDecimal. valueOf(estTheta[i]))); } return totalUtil; } } /** * * @return the list of issues in ascending order of importance */ public List getIssues() { return issueOrder; } /** * retrieves the values of a specific issue * @param issue - want the values from this issue * @return a ValueSet of the values from the input issue */ public ValueSet getValues(String issue) { return domain.getValues(issue); } /** * gets list of bids better than the given threshold from estimated utility * @param threshold - the threshold value compared * @param currOrderedBids - a list of current ordered bids * @return a list of bids from the currOrderedBids better than the threshold * in descending order */ public List getBetterThan(BigDecimal threshold) { List betterBids = new ArrayList(); Set>> set = fullBidOrdering.entrySet(); Iterator>> i = set.iterator(); BigDecimal currUtil = BigDecimal.ONE; while (currUtil.compareTo(threshold) > 0 && i.hasNext()) { Map.Entry> map = (Map.Entry>)i.next(); List bidsList = map.getValue(); for (Bid bid : bidsList) { betterBids.add(bid); } currUtil = (BigDecimal) map.getKey(); } betterBids.add(maxBid); return betterBids; } /** * gets a value from input issue that is neither in maxBid or minBid * @param issue - the issue we want to search the values * @param index - a random index * @return a value from the nonMinMaxIndex */ public Value randomNonMinMax(String issue, int index) { return nonMinMaxIndex.get(issue).get(index); } //////////////////////////////////////////////////////////////////////////////// // method putValue to put a value in a bid //////////////////////////////////////////////////////////////////////////////// /** * replaces one value in a bid with a new value * @param originalBid - the original bid * @param inputIssue - the issue of the value to replace * @param inputValue - the value to replace * @return - the new bid with the replaced value */ private Bid putValue(Bid originalBid, String inputIssue, Value inputValue) { Bid modifiedBid = new Bid(inputIssue, inputValue); Set issues = originalBid.getIssues(); for (String issue : issues) { if (!issue.equals(inputIssue)) { modifiedBid = modifiedBid.merge(new Bid(issue, originalBid.getValue(issue))); } } return modifiedBid; } }