Changeset 4 for bidspace


Ignore:
Timestamp:
09/18/19 10:00:22 (5 years ago)
Author:
bart
Message:

Faster example parties

Location:
bidspace
Files:
5 added
3 edited

Legend:

Unmodified
Added
Removed
  • bidspace/pom.xml

    r2 r4  
    140140                                        <target>1.8</target>
    141141                                </configuration>
     142                        </plugin>
     143                        <plugin>
     144                                <groupId>org.apache.maven.plugins</groupId>
     145                                <artifactId>maven-source-plugin</artifactId>
     146                                <executions>
     147                                        <execution>
     148                                                <id>attach-sources</id>
     149                                                <goals>
     150                                                        <goal>jar</goal>
     151                                                </goals>
     152                                        </execution>
     153                                </executions>
    142154                        </plugin>
    143155
  • bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java

    r2 r4  
    33import java.math.BigDecimal;
    44import java.math.BigInteger;
    5 import java.util.ArrayList;
    6 import java.util.LinkedList;
     5import java.util.Collections;
     6import java.util.HashMap;
    77import java.util.List;
     8import java.util.Map;
     9import java.util.stream.Collectors;
    810
    911import geniusweb.issuevalue.Bid;
     12import geniusweb.issuevalue.Domain;
     13import geniusweb.issuevalue.Value;
    1014import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
    1115import tudelft.utilities.immutablelist.AbstractImmutableList;
     16import tudelft.utilities.immutablelist.FixedList;
     17import tudelft.utilities.immutablelist.ImmutableList;
     18import tudelft.utilities.immutablelist.JoinedList;
     19import tudelft.utilities.immutablelist.MapList;
     20import tudelft.utilities.immutablelist.Tuple;
    1221
    1322/**
    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.
    1527 */
    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;
     28public 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 */
     202class 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));
    48216        }
    49217
    50218        @Override
    51219        public Bid get(BigInteger index) {
    52                 return collectedbids.get(index.intValue());
     220                return new Bid(info.getName(),
     221                                info.subset(interval).get(index.intValue()));
    53222        }
    54223
    55224        @Override
    56225        public BigInteger size() {
    57                 return BigInteger.valueOf(collectedbids.size());
     226                return size;
    58227        }
    59228
  • bidspace/src/test/java/geniusweb/bidspace/BidsWithUtilityTest.java

    r2 r4  
    11package geniusweb.bidspace;
    22
    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;
     3import static org.junit.Assert.assertTrue;
    74
    85import java.io.IOException;
    96import java.math.BigDecimal;
    107import java.math.BigInteger;
     8import java.math.RoundingMode;
    119import java.nio.file.Files;
    1210import java.nio.file.Paths;
     11import java.util.Arrays;
    1312import java.util.Collection;
    14 import java.util.HashMap;
    15 import java.util.LinkedList;
    16 import java.util.Map;
     13import java.util.Random;
    1714
    18 import org.junit.Before;
    1915import org.junit.Test;
     16import org.junit.runner.RunWith;
     17import org.junit.runners.Parameterized;
     18import org.junit.runners.Parameterized.Parameters;
    2019
    2120import com.fasterxml.jackson.databind.ObjectMapper;
    2221
    2322import 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;
    3023import geniusweb.profile.Profile;
    3124import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
     25import tudelft.utilities.immutablelist.ImmutableList;
    3226
     27@RunWith(Parameterized.class)
    3328public 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;
    4335
    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        }
    5043
    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        }
    5351
    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);
    6672
    6773        }
    6874
    6975        @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);
    7597        }
    7698
    7799        @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();
    80103
    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());
    98111        }
    99112
Note: See TracChangeset for help on using the changeset viewer.