[1] | 1 | package geniusweb.exampleparties.simpleshaop;
|
---|
| 2 |
|
---|
| 3 | import java.math.BigDecimal;
|
---|
| 4 | import java.util.ArrayList;
|
---|
| 5 | import java.util.Collections;
|
---|
| 6 | import java.util.HashMap;
|
---|
| 7 | import java.util.Iterator;
|
---|
| 8 | import java.util.List;
|
---|
| 9 | import java.util.Map;
|
---|
| 10 | import java.util.Map.Entry;
|
---|
| 11 | import java.util.Set;
|
---|
| 12 | import java.util.TreeMap;
|
---|
| 13 |
|
---|
| 14 | import geniusweb.issuevalue.Bid;
|
---|
| 15 | import geniusweb.issuevalue.Domain;
|
---|
| 16 | import geniusweb.issuevalue.Value;
|
---|
| 17 | import geniusweb.issuevalue.ValueSet;
|
---|
| 18 | import geniusweb.profile.Profile;
|
---|
| 19 |
|
---|
| 20 | public class CompRegress {
|
---|
| 21 |
|
---|
| 22 | private final Domain domain;
|
---|
| 23 | private final int numIssues;
|
---|
| 24 | private final Bid minBid;
|
---|
| 25 | private final Bid maxBid;
|
---|
| 26 | private int totalNonMinMax;
|
---|
| 27 | private Map<Bid, String> indicatorBidMap;
|
---|
| 28 | private Map<String, HashMap<Value, BigDecimal>> estUtil;
|
---|
| 29 | private Map<String, List<Value>> nonMinMaxIndex;
|
---|
| 30 | private Map<String, Integer> numValuesPerIssue;
|
---|
| 31 | private List<String> issueOrder; // list of issues in ascending importance
|
---|
| 32 | private double[] estTheta; // smallest to largest theta values for corresponding issues
|
---|
| 33 | private Map<BigDecimal, List<Bid>> fullBidOrdering;
|
---|
| 34 |
|
---|
| 35 | CompRegress(Profile profile, List<Bid> initBidOrder, HashMap<Bid, String> indicatorBidMap) {
|
---|
| 36 | this.domain = profile.getDomain();
|
---|
| 37 | this.numIssues = domain.getIssues().size();
|
---|
| 38 | this.minBid = initBidOrder.get(0);
|
---|
| 39 | this.maxBid = initBidOrder.get(initBidOrder.size()-1);
|
---|
| 40 | this.indicatorBidMap = indicatorBidMap;
|
---|
| 41 | this.issueOrder = new ArrayList<String>();
|
---|
| 42 | this.estTheta = new double[numIssues];
|
---|
| 43 | this.estUtil = new HashMap<String, HashMap<Value, BigDecimal>>();
|
---|
| 44 | this.nonMinMaxIndex = new HashMap<String, List<Value>>();
|
---|
| 45 | this.numValuesPerIssue = new HashMap<String, Integer>();
|
---|
| 46 | this.totalNonMinMax = 0;
|
---|
| 47 |
|
---|
| 48 | initImportance(initBidOrder);
|
---|
| 49 | initEstUtilMap();
|
---|
| 50 | fit(initBidOrder);
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 54 | // initialization methods
|
---|
| 55 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 56 |
|
---|
| 57 | /**
|
---|
| 58 | * initialize the theta values and importance ordering of the issues
|
---|
| 59 | * @param initBidOrder - initial ordered bid list with the indicator bids
|
---|
| 60 | */
|
---|
| 61 | private void initImportance(List<Bid> initBidOrder) {
|
---|
| 62 | // initialize issue importance ordered list
|
---|
| 63 | int count = numIssues;
|
---|
| 64 | int index = initBidOrder.size() - 2;
|
---|
| 65 | while (count > 0) {
|
---|
| 66 | Bid bid = initBidOrder.get(index);
|
---|
| 67 | String issue = indicatorBidMap.get(bid);
|
---|
| 68 | if (issue != null) {
|
---|
| 69 | issueOrder.add(issue);
|
---|
| 70 | count--;
|
---|
| 71 | }
|
---|
| 72 | index--;
|
---|
| 73 | }
|
---|
| 74 | // initialize theta values
|
---|
| 75 | double multInverse = 1.0 / numIssues;
|
---|
| 76 | double minTheta = multInverse / 2.0;
|
---|
| 77 | double incr = multInverse / (numIssues - 1);
|
---|
| 78 |
|
---|
| 79 | for (int i = 0; i < numIssues; i++) {
|
---|
| 80 | estTheta[i] = minTheta + (incr * i);
|
---|
| 81 | }
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | /**
|
---|
| 85 | * initialize the HashMap of estimated utilities for each value
|
---|
| 86 | */
|
---|
| 87 | public void initEstUtilMap() {
|
---|
| 88 | for (String issue : issueOrder) {
|
---|
| 89 | ValueSet values = domain.getValues(issue);
|
---|
| 90 | int numValues = values.size().intValue();
|
---|
| 91 |
|
---|
| 92 | numValuesPerIssue.put(issue, numValues);
|
---|
| 93 | totalNonMinMax += numValues - 2;
|
---|
| 94 |
|
---|
| 95 | estUtil.put(issue, new HashMap<Value, BigDecimal>());
|
---|
| 96 | nonMinMaxIndex.put(issue, new ArrayList<Value>());
|
---|
| 97 |
|
---|
| 98 | int count = 1;
|
---|
| 99 | for (Value value : values) {
|
---|
| 100 | if (value.equals(maxBid.getValue(issue))) {
|
---|
| 101 | estUtil.get(issue).put(value, BigDecimal.ONE);
|
---|
| 102 | }
|
---|
| 103 | else if (value.equals(minBid.getValue(issue))) {
|
---|
| 104 | estUtil.get(issue).put(value, BigDecimal.ZERO);
|
---|
| 105 | }
|
---|
| 106 | else {
|
---|
| 107 | estUtil.get(issue).put(value, BigDecimal.valueOf(0.5 + 0.001 *
|
---|
| 108 | (count - 0.5*(numValues-1))));
|
---|
| 109 | nonMinMaxIndex.get(issue).add(value);
|
---|
| 110 | count++;
|
---|
| 111 | }
|
---|
| 112 | }
|
---|
| 113 | }
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 117 | // utility function learner fit method
|
---|
| 118 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 119 |
|
---|
| 120 | /**
|
---|
| 121 | * updates the value utilities given a list of ordered Bids
|
---|
| 122 | * @param orderedBids - a list of ordered bids with ascending utility
|
---|
| 123 | */
|
---|
| 124 | public void fit(List<Bid> orderedBids) {
|
---|
| 125 | int numBids = orderedBids.size();
|
---|
| 126 | int numConstraints = numBids - 1;
|
---|
| 127 |
|
---|
| 128 | Calcfc calcfc = setCalcfc(orderedBids);
|
---|
| 129 |
|
---|
| 130 | double[] x = flattenEstUtil();
|
---|
| 131 | double rhobeg = 1.0;
|
---|
| 132 | double rhoend = 2.0e-4;
|
---|
| 133 | int iprint = 0;
|
---|
| 134 | int maxfun = 1000;
|
---|
| 135 |
|
---|
| 136 | Cobyla.FindMinimum(calcfc, totalNonMinMax, numConstraints, x,
|
---|
| 137 | rhobeg, rhoend, iprint, maxfun);
|
---|
| 138 |
|
---|
| 139 | updateEstUtil(x);
|
---|
| 140 |
|
---|
| 141 | createFullOrdering();
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | /**
|
---|
| 145 | * the function to be minimized in fit()
|
---|
| 146 | * @param orderedBids - a list of ordered bids with ascending utility
|
---|
| 147 | * @return a Calcfc to be used in fit()
|
---|
| 148 | */
|
---|
| 149 | private Calcfc setCalcfc(List<Bid> orderedBids) {
|
---|
| 150 | int numBids = orderedBids.size();
|
---|
| 151 | Calcfc calcfc = new Calcfc() {
|
---|
| 152 |
|
---|
| 153 | @Override
|
---|
| 154 | public double Compute(int n, int m, double[] x, double[] con) {
|
---|
| 155 | double[] bidUtil = new double[numBids];
|
---|
| 156 | for (int i = 0; i < numBids; i++) {
|
---|
| 157 | Bid bid = orderedBids.get(i);
|
---|
| 158 | bidUtil[i] = 0.0;
|
---|
| 159 | int baseIndex = 0;
|
---|
| 160 | for (int j = 0; j < numIssues; j++) {
|
---|
| 161 | String issue = issueOrder.get(j);
|
---|
| 162 | Value value = bid.getValue(issue);
|
---|
| 163 | int index = nonMinMaxIndex.get(issue).indexOf(value);
|
---|
| 164 | if (index >= 0) {
|
---|
| 165 | bidUtil[i] += x[baseIndex + index] * estTheta[j];
|
---|
| 166 | }
|
---|
| 167 | else {
|
---|
| 168 | bidUtil[i] += estUtil.get(issue).get(value).
|
---|
| 169 | doubleValue() * estTheta[j];
|
---|
| 170 | }
|
---|
| 171 | baseIndex += numValuesPerIssue.get(issue) - 2;
|
---|
| 172 | }
|
---|
| 173 | }
|
---|
| 174 | // constraints
|
---|
| 175 | double tolerance = 0.0;
|
---|
| 176 | for (int i = 0; i < numBids - 1; i++) {
|
---|
| 177 | con[i] = bidUtil[i+1] - (bidUtil[i] + tolerance);
|
---|
| 178 | }
|
---|
| 179 | // penalize inversions
|
---|
| 180 | double err = 0.0;
|
---|
| 181 | for (int i = 0; i < numBids - 1; i++) {
|
---|
| 182 | if (bidUtil[i] > bidUtil[i+1]) err += bidUtil[i] - bidUtil[i+1];
|
---|
| 183 | }
|
---|
| 184 | return err/n;
|
---|
| 185 | }
|
---|
| 186 | };
|
---|
| 187 | return calcfc;
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | /**
|
---|
| 191 | * flatten the estUtil values into a single double array
|
---|
| 192 | * @return a double array that contains the initial estimated utilities
|
---|
| 193 | */
|
---|
| 194 | private double[] flattenEstUtil() {
|
---|
| 195 | double[] x = new double[totalNonMinMax];
|
---|
| 196 | int baseIndex = 0;
|
---|
| 197 | for (String issue : issueOrder) {
|
---|
| 198 | List<Value> valuesIndex = nonMinMaxIndex.get(issue);
|
---|
| 199 | int numValues = valuesIndex.size();
|
---|
| 200 | for (int i = 0; i< numValues; i++) {
|
---|
| 201 | x[baseIndex + i] = estUtil.get(issue).
|
---|
| 202 | get(valuesIndex.get(i)).doubleValue();
|
---|
| 203 | }
|
---|
| 204 | baseIndex += numValues;
|
---|
| 205 | }
|
---|
| 206 | return x;
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | /**
|
---|
| 210 | * update the estUtil values form a single double array
|
---|
| 211 | * @param x - a double array that contains the calculated estimated utilities
|
---|
| 212 | */
|
---|
| 213 | private void updateEstUtil(double[] x) {
|
---|
| 214 | int baseIndex = 0;
|
---|
| 215 | for (String issue : issueOrder) {
|
---|
| 216 | List<Value> valuesIndex = nonMinMaxIndex.get(issue);
|
---|
| 217 | int numValues = valuesIndex.size();
|
---|
| 218 | for (int i = 0; i < numValues; i++) {
|
---|
| 219 | estUtil.get(issue).put(valuesIndex.get(i),
|
---|
| 220 | BigDecimal.valueOf(x[baseIndex + i]));
|
---|
| 221 | }
|
---|
| 222 | baseIndex += numValues;
|
---|
| 223 | }
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | /**
|
---|
| 227 | * creates full ordering of bids according to utility function
|
---|
| 228 | */
|
---|
| 229 | private void createFullOrdering() {
|
---|
| 230 | this.fullBidOrdering = new TreeMap<BigDecimal,
|
---|
| 231 | List<Bid>>(Collections.reverseOrder());
|
---|
| 232 | this.ordering(0, maxBid);
|
---|
| 233 | }
|
---|
| 234 |
|
---|
| 235 | /**
|
---|
| 236 | * recursive helper method to create bid ordering
|
---|
| 237 | * @param i - counter
|
---|
| 238 | * @param bid - base bid
|
---|
| 239 | */
|
---|
| 240 | private void ordering(int i, Bid bid) {
|
---|
| 241 | if (i < issueOrder.size()) {
|
---|
| 242 | String issue = issueOrder.get(i);
|
---|
| 243 | ValueSet values = domain.getValues(issue);
|
---|
| 244 | for (Value value : values) {
|
---|
| 245 | Bid newBid = putValue(bid, issue, value);
|
---|
| 246 | BigDecimal util = getUtil(newBid);
|
---|
| 247 | List<Bid> bidsList;
|
---|
| 248 | if (fullBidOrdering.get(util) == null) bidsList = new ArrayList<Bid>();
|
---|
| 249 | else bidsList = fullBidOrdering.get(util);
|
---|
| 250 | bidsList.add(newBid);
|
---|
| 251 | fullBidOrdering.put(this.getUtil(newBid), bidsList);
|
---|
| 252 | this.ordering(i+1, newBid);
|
---|
| 253 | }
|
---|
| 254 | }
|
---|
| 255 | }
|
---|
| 256 |
|
---|
| 257 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 258 | // public helper methods
|
---|
| 259 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 260 |
|
---|
| 261 | /**
|
---|
| 262 | * calculates the total utility of a bid from the current parameters
|
---|
| 263 | * @param bid - a bid to calculate its utility
|
---|
| 264 | * @return BigDecimal; the estimated utility
|
---|
| 265 | */
|
---|
| 266 | public BigDecimal getUtil(Bid bid) {
|
---|
| 267 | if (bid == null) return BigDecimal.ZERO;
|
---|
| 268 | else {
|
---|
| 269 | BigDecimal totalUtil = BigDecimal.ZERO;
|
---|
| 270 | for (int i = 0; i < numIssues; i++) {
|
---|
| 271 | String issue = issueOrder.get(i);
|
---|
| 272 | Value value = bid.getValue(issue);
|
---|
| 273 | BigDecimal util = estUtil.get(issue).get(value);
|
---|
| 274 | totalUtil = totalUtil.add(util.multiply(BigDecimal.
|
---|
| 275 | valueOf(estTheta[i])));
|
---|
| 276 | }
|
---|
| 277 | return totalUtil;
|
---|
| 278 | }
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | /**
|
---|
| 282 | *
|
---|
| 283 | * @return the list of issues in ascending order of importance
|
---|
| 284 | */
|
---|
| 285 | public List<String> getIssues() {
|
---|
| 286 | return issueOrder;
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | /**
|
---|
| 290 | * retrieves the values of a specific issue
|
---|
| 291 | * @param issue - want the values from this issue
|
---|
| 292 | * @return a ValueSet of the values from the input issue
|
---|
| 293 | */
|
---|
| 294 | public ValueSet getValues(String issue) {
|
---|
| 295 | return domain.getValues(issue);
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | /**
|
---|
| 299 | * gets list of bids better than the given threshold from estimated utility
|
---|
| 300 | * @param threshold - the threshold value compared
|
---|
| 301 | * @param currOrderedBids - a list of current ordered bids
|
---|
| 302 | * @return a list of bids from the currOrderedBids better than the threshold
|
---|
| 303 | * in descending order
|
---|
| 304 | */
|
---|
| 305 | public List<Bid> getBetterThan(BigDecimal threshold) {
|
---|
| 306 | List<Bid> betterBids = new ArrayList<Bid>();
|
---|
| 307 | Set<Entry<BigDecimal, List<Bid>>> set = fullBidOrdering.entrySet();
|
---|
| 308 | Iterator<Entry<BigDecimal, List<Bid>>> i = set.iterator();
|
---|
| 309 | BigDecimal currUtil = BigDecimal.ONE;
|
---|
| 310 | while (currUtil.compareTo(threshold) > 0 && i.hasNext()) {
|
---|
| 311 | Map.Entry<BigDecimal, List<Bid>> map = (Map.Entry<BigDecimal,
|
---|
| 312 | List<Bid>>)i.next();
|
---|
| 313 | List<Bid> bidsList = map.getValue();
|
---|
| 314 | for (Bid bid : bidsList) {
|
---|
| 315 | betterBids.add(bid);
|
---|
| 316 | }
|
---|
| 317 | currUtil = (BigDecimal) map.getKey();
|
---|
| 318 | }
|
---|
| 319 | betterBids.add(maxBid);
|
---|
| 320 | return betterBids;
|
---|
| 321 | }
|
---|
| 322 |
|
---|
| 323 | /**
|
---|
| 324 | * gets a value from input issue that is neither in maxBid or minBid
|
---|
| 325 | * @param issue - the issue we want to search the values
|
---|
| 326 | * @param index - a random index
|
---|
| 327 | * @return a value from the nonMinMaxIndex
|
---|
| 328 | */
|
---|
| 329 | public Value randomNonMinMax(String issue, int index) {
|
---|
| 330 | return nonMinMaxIndex.get(issue).get(index);
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 334 | // method putValue to put a value in a bid
|
---|
| 335 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 336 |
|
---|
| 337 | /**
|
---|
| 338 | * replaces one value in a bid with a new value
|
---|
| 339 | * @param originalBid - the original bid
|
---|
| 340 | * @param inputIssue - the issue of the value to replace
|
---|
| 341 | * @param inputValue - the value to replace
|
---|
| 342 | * @return - the new bid with the replaced value
|
---|
| 343 | */
|
---|
| 344 | private Bid putValue(Bid originalBid, String inputIssue, Value inputValue) {
|
---|
| 345 | Bid modifiedBid = new Bid(inputIssue, inputValue);
|
---|
| 346 | Set<String> issues = originalBid.getIssues();
|
---|
| 347 | for (String issue : issues) {
|
---|
| 348 | if (!issue.equals(inputIssue)) {
|
---|
| 349 | modifiedBid = modifiedBid.merge(new Bid(issue, originalBid.getValue(issue)));
|
---|
| 350 | }
|
---|
| 351 | }
|
---|
| 352 | return modifiedBid;
|
---|
| 353 | }
|
---|
| 354 |
|
---|
| 355 | }
|
---|