source: java2python/geniuswebtranslator/geniuswebsrc/geniusweb/bidspace/BidsWithUtility.java@ 749

Last change on this file since 749 was 744, checked in by wouter, 12 months ago

#254 PyProgram now throws TranslationException. Ignore broken test in tudunit-t

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