source: bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java@ 52

Last change on this file since 52 was 52, checked in by ruud, 14 months ago

Fixed small issues in domaineditor.

File size: 8.6 KB
Line 
1package geniusweb.bidspace;
2
3import java.math.BigDecimal;
4import java.math.BigInteger;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.List;
8import java.util.Map;
9import java.util.stream.Collectors;
10
11import geniusweb.issuevalue.Bid;
12import geniusweb.issuevalue.Domain;
13import geniusweb.issuevalue.Value;
14import geniusweb.profile.utilityspace.LinearAdditive;
15import 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;
21
22/**
23 * Tool class containing functions dealing with utilities of all bids in a given
24 * {@link LinearAdditive}. This class caches previously computed values to
25 * accelerate the calls and subsequent calls. Re-use the object to keep/reuse
26 * the cache.
27 * <h2>Rounding</h2> Internally, utilities of bids are rounded to the given
28 * precision. This may cause inclusion/exclusion of some bids in the results.
29 * See {@link #BidsWithUtility(LinearAdditive, int)} for more details
30 */
31public class BidsWithUtility {
32
33 private final List<IssueInfo> issueInfo;
34 private final int precision; // #digits used for Intervals
35
36 /**
37 * cache. Key = call arguments for {@link #get(int, Interval)}. Value=return
38 * value of that call.
39 */
40 private final Map<Tuple<Integer, Interval>, ImmutableList<Bid>> cache = new HashMap<>();
41 private ImmutableList<Bid> emptylist = new FixedList<>();
42
43 /**
44 * Default constructor, uses default precision 6. This value seems practical
45 * for the common range of issues, utilities and weights. See
46 * {@link #BidsWithUtility(LinearAdditive, int)} for more details on the
47 * precision.
48 *
49 * @param space the {@link LinearAdditive} to analyze
50 */
51 public BidsWithUtility(LinearAdditive space) {
52 this(space, 6);
53 }
54
55 /**
56 *
57 * @param space the {@link LinearAdditive} to analyze
58 * @param precision the number of digits to use for computations. In
59 * practice, 6 seems a good default value.
60 * <p>
61 * All utilities * weight are rounded to this number of
62 * digits. This value should match the max number of
63 * (digits used in the weight of an issue + number of
64 * digits used in the issue utility). To determine the
65 * optimal value, one may consider the step size of the
66 * issues, and the range of interest. For instance if the
67 * utility function has values 1/3 and 2/3, then these have
68 * an 'infinite' number of relevant digits. But if the goal
69 * is to search bids between utility 0.1 and 0.2, then
70 * computing in 2 digits might already be sufficient.
71 * <p>
72 * This algorithm has memory and space complexity O(
73 * |nissues| 10^precision ). For spaces up to 7 issues, 7
74 * digits should be feasible; for 9 issues, 6 digits may be
75 * the maximum.
76 */
77 public BidsWithUtility(LinearAdditive space, int precision) {
78 this(getInfo(space, precision), precision);
79 }
80
81 /**
82 *
83 * @param issuesInfo List of the relevant issues (in order of relevance) and
84 * all info of each issue.
85 * @param precision the number of digits used in Intervals.
86 */
87 public BidsWithUtility(List<IssueInfo> issuesInfo, int precision) {
88 if (issuesInfo == null || issuesInfo.isEmpty()) {
89 throw new IllegalArgumentException(
90 "sortedissues list must contain at least 1 element");
91 }
92
93 this.issueInfo = issuesInfo;
94 this.precision = precision;
95
96 }
97
98 /**
99 * @return the (rounded) utility {@link Interval} of this space: minimum and
100 * maximum achievable utility.
101 */
102 public Interval getRange() {
103 return getRange(issueInfo.size() - 1);
104 }
105
106 /**
107 *
108 * @param range the minimum and maximum utility required of the bids. to be
109 * included (both ends inclusive).
110 * @return a list with bids that have a (rounded) utility inside range.
111 * possibly empty.
112 */
113 public ImmutableList<Bid> getBids(Interval range) {
114 return get(issueInfo.size() - 1, range.round(precision));
115 }
116
117 public List<IssueInfo> getInfo() {
118 return Collections.unmodifiableList(issueInfo);
119 }
120
121 /**
122 *
123 * @param isMax the extreme bid required
124 * @return the extreme bid, either the minimum if isMax=false or maximum if
125 * isMax=true
126 */
127 public Bid getExtremeBid(boolean isMax) {
128 Map<String, Value> map = new HashMap<>();
129 for (IssueInfo info : issueInfo) {
130 map.put(info.getName(), info.getExtreme(isMax));
131 }
132 return new Bid(map);
133 }
134
135 /**
136 * Create partial BidsWithUtil list considering only issues 0..n, with
137 * utilities in given range.
138 * <h2>Memory use</h2> Memory use of the return value can be large, if large
139 * domains are given and the requested interval contains large numbers of
140 * bids. To give some idea, here are some typical memory uses:
141 * <table border="1">
142 * <tr>
143 * <td>domain</td>
144 * <td>mem use (MB) of returned object</td>
145 * <td>nr. of selected bids</td>
146 * </tr>
147 * <tr>
148 * <td>jobs1</td>
149 * <td>0.028</td>
150 * <td>23</td>
151 * </tr>
152 * <tr>
153 * <td>7issues1</td>
154 * <td>34</td>
155 * <td>346327</td>
156 * </tr>
157 * <tr>
158 * <td>9issues1</td>
159 * <td>98</td>
160 * <td>37160666</td>
161 * </tr>
162 * </table>
163 *
164 * @param n the number of issueRanges to consider, we consider 0..n here.
165 * The recursion decreases n until n=0
166 * @param goal the minimum and maximum utility required of the bids. to be
167 * included (both ends inclusive)
168 * @return BidsWithUtil list, possibly empty.
169 */
170 protected ImmutableList<Bid> get(int n, Interval goal) {
171 if (goal == null) {
172 throw new NullPointerException("Interval=null");
173 }
174
175 // clamp goal into what is reachable. Avoid caching empty
176 goal = goal.intersect(getRange(n));
177 if (goal.isEmpty()) {
178 return new FixedList<>();
179 }
180
181 Tuple<Integer, Interval> cachetuple = new Tuple<>(n, goal);
182 ImmutableList<Bid> cached = cache.get(cachetuple);
183 if (cached != null) {
184 // hits++;
185 return cached;
186 }
187
188 ImmutableList<Bid> result = checkedGet(n, goal);
189 cache.put(cachetuple, result);
190 return result;
191 }
192
193 private static List<IssueInfo> getInfo(LinearAdditive space2,
194 int precision) {
195 Domain dom = space2.getDomain();
196 return space2.getDomain().getIssues().stream()
197 .map(issue -> new IssueInfo(issue, dom.getValues(issue),
198 space2.getUtilities().get(issue),
199 space2.getWeight(issue), precision))
200 .collect(Collectors.toList());
201 }
202
203 private ImmutableList<Bid> checkedGet(int n, Interval goal) {
204 IssueInfo info = issueInfo.get(n);
205 // issue is the first issuesWithRange.
206 String issue = info.getName();
207
208 if (n == 0)
209 return new OneIssueSubset(info, goal);
210
211 // make new list, joining all sub-lists
212 ImmutableList<Bid> fulllist = emptylist;
213 for (Value val : info.getValues()) {
214 BigDecimal weightedutil = info.getWeightedUtil(val);
215 Interval subgoal = goal.subtract(weightedutil);
216 // recurse: get list of bids for the subspace
217 ImmutableList<Bid> partialbids = get(n - 1, subgoal);
218
219 ImmutableList<Bid> fullbids = new MapList<Bid, Bid>(
220 pbid -> pbid.merge(new Bid(issue, val)), partialbids);
221 if (!fullbids.size().equals(BigInteger.ZERO))
222 fulllist = new JoinedList<Bid>(fullbids, fulllist);
223 }
224 return fulllist;
225 }
226
227 /**
228 * @param n the maximum issuevalue utility to include. Use n=index of last
229 * issue s= (#issues in the domain - 1) for the full range of this
230 * domain.
231 * @return Interval (min, max) of the total weighted utility Interval of
232 * issues 0..n. All weighted utilities have been rounded to the set
233 * {@link #precision}
234 */
235 private Interval getRange(int n) {
236 Interval value = Interval.ZERO;
237 for (int i = 0; i <= n; i++) {
238 value = value.add(issueInfo.get(i).getInterval());
239 }
240 return value;
241 }
242
243}
244
245/**
246 * List of all one-issue bids that have utility inside given interval.
247 */
248class OneIssueSubset extends AbstractImmutableList<Bid> {
249 private final IssueInfo info;
250 private final Interval interval;
251 private BigInteger size;
252
253 /**
254 *
255 * @param info the {@link IssueInfo}
256 * @param interval a utility interval (weighted)
257 */
258 OneIssueSubset(IssueInfo info, Interval interval) {
259 this.info = info;
260 this.interval = interval;
261 this.size = BigInteger.valueOf(info.subsetSize(interval));
262 }
263
264 @Override
265 public Bid get(BigInteger index) {
266 return new Bid(info.getName(),
267 info.subset(interval).get(index.intValue()));
268 }
269
270 @Override
271 public BigInteger size() {
272 return size;
273 }
274
275}
Note: See TracBrowser for help on using the repository browser.