Changeset 4 for bidspace/src
- Timestamp:
- 09/18/19 10:00:22 (5 years ago)
- Location:
- bidspace/src
- Files:
-
- 5 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java
r2 r4 3 3 import java.math.BigDecimal; 4 4 import java.math.BigInteger; 5 import java.util. ArrayList;6 import java.util. LinkedList;5 import java.util.Collections; 6 import java.util.HashMap; 7 7 import java.util.List; 8 import java.util.Map; 9 import java.util.stream.Collectors; 8 10 9 11 import geniusweb.issuevalue.Bid; 12 import geniusweb.issuevalue.Domain; 13 import geniusweb.issuevalue.Value; 10 14 import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace; 11 15 import tudelft.utilities.immutablelist.AbstractImmutableList; 16 import tudelft.utilities.immutablelist.FixedList; 17 import tudelft.utilities.immutablelist.ImmutableList; 18 import tudelft.utilities.immutablelist.JoinedList; 19 import tudelft.utilities.immutablelist.MapList; 20 import tudelft.utilities.immutablelist.Tuple; 12 21 13 22 /** 14 * Object containing a subset of all bids, that have utility within given range. 23 * Tool class containing functions dealing with utilities of all bids in a given 24 * {@link LinearAdditiveUtilitySpace}. This class caches previously computed 25 * values to accelerate the calls and subsequent calls. Re-use the object to 26 * keep/reuse the cache. 15 27 */ 16 public class BidsWithUtility extends AbstractImmutableList<Bid> { 17 18 private final List<Bid> collectedbids; 19 20 /** 21 * Create Bids with utility within given bounds. 22 * 23 * @param space the {@link LinearAdditiveUtilitySpace} to search 24 * @param min the minimum required utility 25 * @param max the maximum required utility 26 */ 27 public BidsWithUtility(LinearAdditiveUtilitySpace space, BigDecimal min, BigDecimal max) { 28 this.collectedbids = new ArrayList<Bid>(getBids(space, min, max)); 29 } 30 31 /** 32 * Brute force hack for this time. Maybe we can construct this in a lazy way. 33 * 34 * @param space 35 * @param min 36 * @param max 37 * @return 38 */ 39 private List<Bid> getBids(LinearAdditiveUtilitySpace space, BigDecimal min, BigDecimal max) { 40 List<Bid> goodbids = new LinkedList<Bid>(); 41 for (Bid bid : new AllBidsList(space.getDomain())) { 42 BigDecimal util = space.getUtility(bid); 43 if (util.compareTo(min) >= 0 && util.compareTo(max) <= 0) { 44 goodbids.add(bid); 45 } 46 } 47 return goodbids; 28 public class BidsWithUtility { 29 30 private final List<IssueInfo> issueInfo; 31 private final int precision; // #digits used for Intervals 32 33 /** 34 * cache. Key = call arguments for {@link #get(int, Interval)}. Value=return 35 * value of that call. 36 */ 37 private final Map<Tuple<Integer, Interval>, ImmutableList<Bid>> cache = new HashMap<>(); 38 39 /** 40 * Default constructor, uses default precision 6. This value seems practical 41 * for the common range of issues, utilities and weights. See 42 * {@link #BidsWithUtil(LinearAdditiveUtilitySpace, int)} for more details 43 * on the precision. 44 * 45 * @param space the {@link LinearAdditiveUtilitySpace} to analyze 46 */ 47 public BidsWithUtility(LinearAdditiveUtilitySpace space) { 48 this(space, 6); 49 } 50 51 /** 52 * 53 * @param space the {@link LinearAdditiveUtilitySpace} to analyze 54 * @param precision the number of digits to use for computations. This value 55 * should match the max number of (digits used in the 56 * weight of an issue + number of digits used in the issue 57 * utility). Usually you also look at the step size, and 58 * the range of interest. For instance if your utility 59 * function has values 1/3 and 2/3, then these have an 60 * 'infinite' number of relevant digits. But if we want to 61 * search bids between utility 0.1 and 0.2, then computing 62 * in 2 digits might already be sufficient. This algorithm 63 * has memory and space complexity O( |nissues| 64 * 10^precision ). For spaces up to 7 issues, 7 digits 65 * should be feasible; for 9 issues, 6 digits may be the 66 * maximum. 67 */ 68 public BidsWithUtility(LinearAdditiveUtilitySpace space, int precision) { 69 this(getInfo(space, precision), precision); 70 } 71 72 /** 73 * 74 * @param issuesInfo List of the relevant issues (in order of relevance) and 75 * all info of each issue. 76 * @param precision the number of digits used in Intervals. 77 */ 78 public BidsWithUtility(List<IssueInfo> issuesInfo, int precision) { 79 if (issuesInfo == null || issuesInfo.isEmpty()) { 80 throw new IllegalArgumentException( 81 "sortedissues list must contain at least 1 element"); 82 } 83 84 this.issueInfo = issuesInfo; 85 this.precision = precision; 86 87 } 88 89 /** 90 * @return the utility {@link Interval} of this space: minimum and maximum 91 * achievable utility. 92 */ 93 public Interval getRange() { 94 return getRange(issueInfo.size() - 1); 95 } 96 97 /** 98 * 99 * @param range the minimum and maximum utility required of the bids. to be 100 * included (both ends inclusive). 101 * @return a list with bids that have utility inside range. possibly empty. 102 * With decreasing precision, more bids may drop out due to rounding 103 * errors. 104 */ 105 public ImmutableList<Bid> getBids(Interval range) { 106 return get(issueInfo.size() - 1, range.round(precision)); 107 } 108 109 public List<IssueInfo> getInfo() { 110 return Collections.unmodifiableList(issueInfo); 111 } 112 113 /** 114 * Create partial BidsWithUtil list considering only issues 0..n, with 115 * utilities in given range. 116 * 117 * @param n the number of {@link #issueRanges} to consider, we consider 118 * 0..n here. The recursion decreases n until n=0 119 * @param goal the minimum and maximum utility required of the bids. to be 120 * included (both ends inclusive) 121 * @return BidsWithUtil list, possibly empty. 122 */ 123 protected ImmutableList<Bid> get(int n, Interval goal) { 124 if (goal == null) { 125 throw new NullPointerException("Interval=null"); 126 } 127 128 // clamp goal into what is reachable. Avoid caching empty 129 goal = goal.intersect(getRange(n)); 130 if (goal.isEmpty()) { 131 return new FixedList<>(); 132 } 133 134 Tuple<Integer, Interval> cachetuple = new Tuple<>(n, goal); 135 ImmutableList<Bid> cached = cache.get(cachetuple); 136 if (cached != null) { 137 // hits++; 138 return cached; 139 } 140 141 ImmutableList<Bid> result = checkedGet(n, goal); 142 cache.put(cachetuple, result); 143 return result; 144 } 145 146 private static List<IssueInfo> getInfo(LinearAdditiveUtilitySpace space2, 147 int precision) { 148 Domain dom = space2.getDomain(); 149 return space2.getDomain().getIssues().stream() 150 .map(issue -> new IssueInfo(issue, dom.getValues(issue), 151 space2.getUtilities().get(issue), 152 space2.getWeight(issue), precision)) 153 .collect(Collectors.toList()); 154 } 155 156 private ImmutableList<Bid> checkedGet(int n, Interval goal) { 157 IssueInfo info = issueInfo.get(n); 158 // issue is the first issuesWithRange. 159 String issue = issueInfo.get(n).getName(); 160 161 if (n == 0) 162 return new OneIssueSubset(info, goal); 163 164 // make new list, joining all sub-lists 165 ImmutableList<Bid> fulllist = new FixedList<>(); 166 for (Value val : info.getValues()) { 167 BigDecimal weightedutil = info.getWeightedUtil(val); 168 Interval subgoal = goal.subtract(weightedutil); 169 // recurse: get list of bids for the subspace 170 ImmutableList<Bid> partialbids = get(n - 1, subgoal); 171 172 Bid bid = new Bid(issue, val); 173 ImmutableList<Bid> fullbids = new MapList<Bid, Bid>( 174 pbid -> pbid.merge(bid), partialbids); 175 if (!fullbids.size().equals(BigInteger.ZERO)) 176 fulllist = new JoinedList<Bid>(fullbids, fulllist); 177 } 178 return fulllist; 179 } 180 181 /** 182 * @param n the maximum issuevalue utility to include. Use n=index of last 183 * issue s= (#issues in the domain - 1) for the full range of this 184 * domain. 185 * @return Interval (min, max) of the total weighted utility Interval of 186 * issues 0..n. All weighted utilities have been rounded to the set 187 * {@link #precision} 188 */ 189 private Interval getRange(int n) { 190 Interval value = Interval.ZERO; 191 for (int i = 0; i <= n; i++) { 192 value = value.add(issueInfo.get(i).getInterval()); 193 } 194 return value; 195 } 196 197 } 198 199 /** 200 * List of all one-issue bids that have utility inside given interval. 201 */ 202 class OneIssueSubset extends AbstractImmutableList<Bid> { 203 private final IssueInfo info; 204 private final Interval interval; 205 private BigInteger size; 206 207 /** 208 * 209 * @param info the {@link IssueInfo} 210 * @param interval a utility interval (weighted) 211 */ 212 OneIssueSubset(IssueInfo info, Interval interval) { 213 this.info = info; 214 this.interval = interval; 215 this.size = BigInteger.valueOf(info.subsetSize(interval)); 48 216 } 49 217 50 218 @Override 51 219 public Bid get(BigInteger index) { 52 return collectedbids.get(index.intValue()); 220 return new Bid(info.getName(), 221 info.subset(interval).get(index.intValue())); 53 222 } 54 223 55 224 @Override 56 225 public BigInteger size() { 57 return BigInteger.valueOf(collectedbids.size());226 return size; 58 227 } 59 228 -
bidspace/src/test/java/geniusweb/bidspace/BidsWithUtilityTest.java
r2 r4 1 1 package geniusweb.bidspace; 2 2 3 import static org.junit.Assert.assertEquals; 4 import static org.mockito.Matchers.any; 5 import static org.mockito.Mockito.mock; 6 import static org.mockito.Mockito.when; 3 import static org.junit.Assert.assertTrue; 7 4 8 5 import java.io.IOException; 9 6 import java.math.BigDecimal; 10 7 import java.math.BigInteger; 8 import java.math.RoundingMode; 11 9 import java.nio.file.Files; 12 10 import java.nio.file.Paths; 11 import java.util.Arrays; 13 12 import java.util.Collection; 14 import java.util.HashMap; 15 import java.util.LinkedList; 16 import java.util.Map; 13 import java.util.Random; 17 14 18 import org.junit.Before;19 15 import org.junit.Test; 16 import org.junit.runner.RunWith; 17 import org.junit.runners.Parameterized; 18 import org.junit.runners.Parameterized.Parameters; 20 19 21 20 import com.fasterxml.jackson.databind.ObjectMapper; 22 21 23 22 import geniusweb.issuevalue.Bid; 24 import geniusweb.issuevalue.DiscreteValue;25 import geniusweb.issuevalue.DiscreteValueSet;26 import geniusweb.issuevalue.Domain;27 import geniusweb.issuevalue.NumberValue;28 import geniusweb.issuevalue.NumberValueSet;29 import geniusweb.issuevalue.ValueSet;30 23 import geniusweb.profile.Profile; 31 24 import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace; 25 import tudelft.utilities.immutablelist.ImmutableList; 32 26 27 @RunWith(Parameterized.class) 33 28 public class BidsWithUtilityTest { 34 private static final DiscreteValue I1V2 = new DiscreteValue("i1v2"); 35 private static final DiscreteValue I1V1 = new DiscreteValue("i1v1"); 36 private static final NumberValue I2V1 = new NumberValue("2.00"); 37 private static final NumberValue I2V2 = new NumberValue("2.45"); 38 private static final NumberValue I2V3 = new NumberValue("2.90"); 39 private static final String DOMAINNAME = "testdomain"; 40 private static final String ISSUE1 = "issue1"; 41 private static final String ISSUE2 = "issue2"; 42 private static final Map<String, ValueSet> issues = new HashMap<>(); 29 private final static ObjectMapper jackson = new ObjectMapper(); 30 private final Interval utilityGoal = new Interval(new BigDecimal("0.50"), 31 new BigDecimal("0.51")); 32 private LinearAdditiveUtilitySpace profile; 33 private long expectedSize; 34 private int accuracy = 5; 43 35 44 private static ValueSet values1; 45 private static ValueSet values2; 46 private static final BigDecimal TWO = new BigDecimal("2"); 47 private static final BigDecimal THREE = new BigDecimal("3"); 48 private final static BigDecimal ZERO_1 = new BigDecimal("0.1"), ZERO_2 = new BigDecimal("0.2"), 49 ZERO_3 = new BigDecimal("0.3"); 36 @Parameters 37 public static Collection<Object[]> data() { 38 return Arrays.asList(new Object[][] { 39 { "src/test/resources/jobs/jobs1.json", 11 }, 40 { "src/test/resources/7issues/7issues1.json", 260000 }, 41 { "src/test/resources/9issues/9issues1.json", 25000000 } }); 42 } 50 43 51 private static Domain domain; 52 private final static ObjectMapper jackson = new ObjectMapper(); 44 public BidsWithUtilityTest(String filename, long expectedsize) 45 throws IOException { 46 String file = new String(Files.readAllBytes(Paths.get(filename))); 47 profile = (LinearAdditiveUtilitySpace) jackson.readValue(file, 48 Profile.class); 49 this.expectedSize = expectedsize; 50 } 53 51 54 @Before 55 public void before() { 56 Collection<DiscreteValue> discretevalues1 = new LinkedList<>(); 57 discretevalues1.add(I1V1); 58 discretevalues1.add(I1V2); 59 values1 = new DiscreteValueSet(discretevalues1); 60 issues.put(ISSUE1, values1); 61 62 values2 = new NumberValueSet(TWO, THREE, new BigDecimal("0.45")); 63 issues.put(ISSUE2, values2); 64 65 domain = new Domain(DOMAINNAME, issues); 52 /** 53 * Test if the values are within acceptable range from the goal. 54 */ 55 @Test 56 public void test() { 57 ImmutableList<Bid> list = new BidsWithUtility(profile, accuracy) 58 .getBids(utilityGoal); 59 Random rand = new Random(); 60 // check not all but only 10000 random bids as list may be way too large 61 // to test them all. Testing 1 billion bids may take 15 minutes or so on 62 // quad core i7 @2.4GHz..... 63 // also notice that we may be checking only part of the list if 64 // the size of the list would become bigger than maxint. 65 for (int n = 0; n < 10000; n++) { 66 Bid bid = list.get(rand.nextInt(list.size().intValue())); 67 assertTrue(utilityGoal.contains(profile.getUtility(bid) 68 .setScale(accuracy - 1, RoundingMode.HALF_UP))); 69 } 70 assertTrue( 71 list.size().compareTo(BigInteger.valueOf(expectedSize)) >= 0); 66 72 67 73 } 68 74 69 75 @Test 70 public void smokeTest() { 71 LinearAdditiveUtilitySpace space = mock(LinearAdditiveUtilitySpace.class); 72 when(space.getDomain()).thenReturn(domain); 73 when(space.getUtility(any(Bid.class))).thenReturn(ZERO_2); 74 new BidsWithUtility(space, new BigDecimal("0.1"), new BigDecimal("0.3")); 76 public void testMaxUtil() { 77 BidsWithUtility bidswithutil = new BidsWithUtility(profile); 78 // notice, this is the *rounded* max util 79 BigDecimal maxutil = bidswithutil.getRange().getMax(); 80 Interval goal = new Interval( 81 maxutil.subtract(new BigDecimal("0.00001")), maxutil); 82 83 ImmutableList<Bid> bidsnearmax = bidswithutil.getBids(goal); 84 assertTrue(bidsnearmax.size() != BigInteger.ZERO); 85 86 BigDecimal foundmax = BigDecimal.ZERO; 87 for (Bid bid : bidsnearmax) { 88 BigDecimal util = profile.getUtility(bid); 89 if (util.compareTo(foundmax) > 0) { 90 foundmax = util; 91 } 92 } 93 // found maximum may be slightly lower or higher because we rounded 94 // all weighted utilities. 95 assertTrue(Math 96 .abs(foundmax.doubleValue() - maxutil.doubleValue()) < 0.0001); 75 97 } 76 98 77 99 @Test 78 public void testSelection() { 79 LinearAdditiveUtilitySpace space = mock(LinearAdditiveUtilitySpace.class); 100 public void benchmark() { 101 System.out.println("\nBenchmarking " + profile.getName()); 102 BigInteger domainsize = new AllBidsList(profile.getDomain()).size(); 80 103 81 when(space.getDomain()).thenReturn(domain); 82 when(space.getUtility(any(Bid.class))).thenReturn(ZERO_2); 83 BidsWithUtility selected = new BidsWithUtility(space, new BigDecimal("0.1"), new BigDecimal("0.3")); 84 assertEquals(new BigInteger("6"), selected.size()); 85 } 86 87 @Test 88 public void testRealSpace() throws IOException { 89 String file1 = new String(Files.readAllBytes(Paths.get("src/test/resources/jobs/jobs1.json"))); 90 LinearAdditiveUtilitySpace profile = (LinearAdditiveUtilitySpace) jackson.readValue(file1, Profile.class); 91 BidsWithUtility selected = new BidsWithUtility(profile, ZERO_1, ZERO_3); 92 93 assertEquals(new BigInteger("56"), selected.size()); 94 for (Bid bid : selected) { 95 assertEquals(1, profile.getUtility(bid).compareTo(ZERO_1)); 96 assertEquals(-1, profile.getUtility(bid).compareTo(ZERO_3)); 97 } 104 long start = System.currentTimeMillis(); 105 BidsWithUtility body = new BidsWithUtility(profile, accuracy); 106 ImmutableList<Bid> list = body.getBids(utilityGoal); 107 long end = System.currentTimeMillis(); 108 System.out.println("run time: " + (end - start) / 1000. + "s"); 109 System.out.println("Total size of bidspace:" + domainsize); 110 System.out.println("Result size: " + list.size()); 98 111 } 99 112
Note:
See TracChangeset
for help on using the changeset viewer.