source: java2python/geniuswebtranslator/geniuswebsrc/geniusweb/profile/utilityspace/SumOfGroupsUtilitySpace.java@ 734

Last change on this file since 734 was 566, checked in by wouter, 18 months ago

fixes to help translator

File size: 9.3 KB
Line 
1package geniusweb.profile.utilityspace;
2
3import java.math.BigDecimal;
4import java.util.Arrays;
5import java.util.HashMap;
6import java.util.HashSet;
7import java.util.LinkedList;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11
12import com.fasterxml.jackson.annotation.JsonAutoDetect;
13import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
14import com.fasterxml.jackson.annotation.JsonCreator;
15import com.fasterxml.jackson.annotation.JsonProperty;
16
17import geniusweb.issuevalue.Bid;
18import geniusweb.issuevalue.Domain;
19import geniusweb.issuevalue.NumberValueSet;
20import geniusweb.issuevalue.Value;
21import geniusweb.profile.DefaultProfile;
22
23/**
24 * This is a utility space that defines the utility of bids as a sum of the
25 * utilities of a number of subsets, or groups, of the issue values.
26 * <p>
27 * A group defines the utility of a non-empty subset of the issues in the
28 * domain. The parts are non-overlapping. Missing issue values have utility 0.
29 * <p>
30 * This space enables handling {@link UtilitySpace}s that are not simply linear
31 * additive, but with interacting issue values. For example, if you would like
32 * to have a car with good speakers but only if there is also a good hifi set,
33 * you can make a part that has {@link PartsUtilities} like
34 * <code> { { speakers:yes, hifi:yes }:1, {speakers, no: hifh:yes}:0.2, ...} </code>
35 * <p>
36 * NOTICE this space is completely discrete. There is no equivalent of the
37 * {@link NumberValueSet} here.
38 */
39@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
40public class SumOfGroupsUtilitySpace extends DefaultProfile
41 implements UtilitySpace {
42 /**
43 * the key is the part name, the value is the valuesetutilities for this
44 * issue. Should be immutable so do not return direct access to this field.
45 */
46 private final HashMap<String, PartsUtilities> partUtilities = new HashMap<>();
47
48 /**
49 * @param domain the {@link Domain} in which this profile is defined.
50 * @param name the name of this profile. Must be simple name (a-Z, 0-9)
51 * @param partutils a map with key: part names (String) and value: the
52 * {@link PartsUtilities} for that part. There MUST NOT be
53 * a null part name. All values MUST NOT be null. The
54 * PartsUtilities must match the
55 * @param resBid the reservation bid. Only bids that are
56 * {@link #isPreferredOrEqual(Bid, Bid)} should be
57 * accepted. Can be null, meaning that there is no
58 * reservation bid and any agreement is better than no
59 * agreement.
60 * @throws NullPointerException if values are incorrectly null.
61 * @throws IllegalArgumentException if preconditions not met.
62 */
63 @JsonCreator
64 public SumOfGroupsUtilitySpace(@JsonProperty("domain") Domain domain,
65 @JsonProperty("name") String name,
66 @JsonProperty("partUtilities") HashMap<String, PartsUtilities> partutils,
67 @JsonProperty("reservationBid") Bid resBid) {
68 super(name, domain, resBid);
69 this.partUtilities.putAll(partutils);
70
71 String err = checkParts();
72 if (err != null) {
73 throw new IllegalArgumentException(err);
74 }
75 }
76
77 /**
78 * Copy settings in las. This will have the exact same utilities as the las
79 * but this then gives you the power to change it into a non-linear space by
80 * grouping.
81 *
82 * @param las the {@link LinearAdditive} to be converted/copied.
83 *
84 */
85 public SumOfGroupsUtilitySpace(LinearAdditive las) {
86 this(las.getDomain(), las.getName(), las2parts(las),
87 las.getReservationBid());
88 }
89
90 @Override
91 public BigDecimal getUtility(Bid bid) {
92 //#PY return sum( [ self._util(partname, self._bid) for partname in self._partUtilities.keys() ] )
93 return partUtilities.keySet().stream()
94 .map(partname -> util(partname, bid))
95 .reduce(BigDecimal.ZERO, BigDecimal::add);
96 }
97
98 @Override
99 public String toString() {
100 return "PartialAdditive[" + partUtilities + "," + getReservationBid()
101 + "]";
102 }
103
104 /**
105 *
106 * @param partnames the partnames to remove from this. There must be at
107 * least 2 parts
108 * @param newpartname the name of the new part that contains all partnames,
109 * grouped into 1 "issue". This name must not be an issue
110 * in this.
111 * @return new {@link SumOfGroupsUtilitySpace} that takes all partnames out
112 * of this, and makes a newaprtname that contains these.
113 */
114 public SumOfGroupsUtilitySpace group(List<String> partnames,
115 String newpartname) {
116 final Set<String> allpartnames = partUtilities.keySet();
117
118 if (partnames.size() < 2) {
119 throw new IllegalArgumentException(
120 "Group must contain at least 2 parts");
121 }
122 if (allpartnames.contains(newpartname)) {
123 throw new IllegalArgumentException(
124 "newpartname " + newpartname + " is already in use");
125 }
126 for (String name : partnames) {
127 if (!allpartnames.contains(name)) {
128 throw new IllegalArgumentException("Unknown part name " + name);
129 }
130 }
131
132 HashMap<String, PartsUtilities> newutils = new HashMap<String, PartsUtilities>();
133
134 PartsUtilities newpartutils = null;
135
136 for (String name : partUtilities.keySet()) {
137 if (partnames.contains(name)) {
138 if (newpartutils == null) {
139 newpartutils = partUtilities.get(name);
140 } else {
141 newpartutils = newpartutils.add(partUtilities.get(name));
142 }
143 } else {
144 newutils.put(name, partUtilities.get(name));
145 }
146 }
147 newutils.put(newpartname, newpartutils);
148
149 return new SumOfGroupsUtilitySpace(getDomain(), getName(), newutils,
150 getReservationBid());
151 }
152
153 @Override
154 public int hashCode() {
155 final int prime = 31;
156 int result = super.hashCode();
157 result = prime * result
158 + ((partUtilities == null) ? 0 : partUtilities.hashCode());
159 return result;
160 }
161
162 @Override
163 public boolean equals(Object obj) {
164 if (this == obj)
165 return true;
166 if (!super.equals(obj))
167 return false;
168 if (getClass() != obj.getClass())
169 return false;
170 SumOfGroupsUtilitySpace other = (SumOfGroupsUtilitySpace) obj;
171 if (partUtilities == null) {
172 if (other.partUtilities != null)
173 return false;
174 } else if (!partUtilities.equals(other.partUtilities))
175 return false;
176 return true;
177 }
178
179 /***************************** private funcs ***************************/
180 /**
181 *
182 * @param partname the name of the part to get the utility of
183 * @param bid the bid
184 * @return weighted util of just the part: utilities[part].getUtility(part
185 * of bid)
186 */
187 private BigDecimal util(String partname, Bid bid) {
188 PartsUtilities partutils = partUtilities.get(partname);
189 ProductOfValue value = collectValues(bid, partutils);
190 return partutils.getUtility(value);
191 }
192
193 /**
194 *
195 * @param bid a full bid
196 * @param partutils the part utils for which a list of values from the bid
197 * is needed
198 * @return a list of values from the given bid, ordered as indicated in
199 * partutils
200 */
201 private ProductOfValue collectValues(Bid bid, PartsUtilities partutils) {
202 List<Value> values = new LinkedList<>();
203 for (String issue : partutils.getIssues()) {
204 values.add(bid.getValue(issue));
205 }
206 return new ProductOfValue(values);
207 }
208
209 /**
210 *
211 * @param las a {@link LinearAdditive}
212 * @return a Map with partname-PartsUtilities. The partnames are identical
213 * to the issues in the given las.
214 */
215 private static HashMap<String, PartsUtilities> las2parts(
216 LinearAdditive las) {
217
218 HashMap<String, PartsUtilities> map = new HashMap<>();
219 for (String issue : las.getUtilities().keySet()) {
220 ValueSetUtilities valset = las.getUtilities().get(issue);
221 Map<ProductOfValue, BigDecimal> utilslist = new HashMap<>();
222 BigDecimal weight = las.getWeight(issue);
223 for (Value val : las.getDomain().getValues(issue)) {
224 BigDecimal util = valset.getUtility(val).multiply(weight);
225 if (BigDecimal.ZERO.compareTo(util) != 0) {
226 utilslist.put(new ProductOfValue(Arrays.asList(val)), util);
227 }
228 }
229 map.put(issue, new PartsUtilities(Arrays.asList(issue), utilslist));
230 }
231 return map;
232 }
233
234 /**
235 *
236 * @return error string, or null if no error (all parts seem fine)
237 */
238 private String checkParts() {
239
240 Set<String> collectedIssues = new HashSet<>();
241 for (String partname : partUtilities.keySet()) {
242 PartsUtilities part = partUtilities.get(partname);
243 if (part == null) {
244 return "partUtilities " + partname + " contains null value";
245 }
246 List<String> issues = part.getIssues();
247 HashSet<String> intersection = new HashSet<String>(collectedIssues);
248 intersection.retainAll(issues);
249 if (!intersection.isEmpty()) {
250 return "issues " + intersection + " occur multiple times";
251 }
252 collectedIssues.addAll(issues);
253 }
254
255 if (!collectedIssues.equals(getDomain().getIssues())) {
256 return "parts must cover the domain issues "
257 + getDomain().getIssues() + " but cover " + collectedIssues;
258 }
259 if (getMaxUtility().compareTo(BigDecimal.ONE) > 0) {
260 return "Max utility of the space exceedds 1";
261 }
262 return null;
263
264 }
265
266 /**
267 *
268 * @return the max possible utility in this utility space.
269 */
270 private BigDecimal getMaxUtility() {
271 //#PY return sum( [ partutils.getMaxUtility() for partutils in self._partUtilities.values() ] )
272 return partUtilities.values().stream()
273 .map(partutils -> partutils.getMaxUtility())
274 .reduce(BigDecimal.ZERO, BigDecimal::add);
275 }
276
277}
Note: See TracBrowser for help on using the repository browser.