source: java2python/geniuswebtranslator/geniuswebsrc/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java@ 566

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

added manual translations for stream

File size: 7.9 KB
Line 
1package geniusweb.profile.utilityspace;
2
3import java.math.BigDecimal;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.Map;
7
8import com.fasterxml.jackson.annotation.JsonAutoDetect;
9import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
10import com.fasterxml.jackson.annotation.JsonCreator;
11import com.fasterxml.jackson.annotation.JsonProperty;
12
13import geniusweb.issuevalue.Bid;
14import geniusweb.issuevalue.Domain;
15import geniusweb.issuevalue.Value;
16
17/**
18 * Defines a UtilitySpace in terms of a weighted sum of per-issue preferences.
19 * immutable. A {@link LinearAdditiveUtilitySpace} works with complete bids.
20 *
21 * Constructor guarantees that
22 * <ul>
23 * <li>weights are normalized to 1
24 * <li>the issues in the utility map and weights map match those in the domain
25 * <li>The utilities for each issue are proper {@link ValueSetUtilities} objects
26 * </ul>
27 */
28@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
29public class LinearAdditiveUtilitySpace implements LinearAdditive {
30
31 private final String name;
32 /**
33 * the key is the issue, the value is the valuesetutilities for this issue.
34 * Should be immutable so do not return direct access to this field.
35 */
36 private final HashMap<String, ValueSetUtilities> issueUtilities = new HashMap<>();
37
38 /**
39 * The key is the issue, the value is the weight of the utilities for that
40 * issue (utility as returned from the ValueSetUtilities for that issue).
41 */
42 private final HashMap<String, BigDecimal> issueWeights = new HashMap<>();
43
44 private final Domain domain;
45
46 private final Bid reservationBid;
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 utils a map with key: issue names (String) and value: the values
52 * for that issue. There MUST NOT be a null issue. All values
53 * MUST NOT be null.
54 * @param weights the weight of each issue in the computation of the
55 * weighted sum. The issues must be the same as those in the
56 * utils map. All weights MUST NOT be null. The weights MUST
57 * sum to 1.
58 * @param resBid the reservation bid. Only bids that are
59 * {@link #isPreferredOrEqual(Bid, Bid)} should be accepted.
60 * Can be null, meaning that there is no reservation bid and
61 * any agreement is better than no agreement.
62 * @throws NullPointerException if values are incorrectly null.
63 * @throws IllegalArgumentException if preconditions not met.
64 */
65 @JsonCreator
66 public LinearAdditiveUtilitySpace(@JsonProperty("domain") Domain domain,
67 @JsonProperty("name") String name,
68 @JsonProperty("issueUtilities") Map<String, ValueSetUtilities> utils,
69 @JsonProperty("issueWeights") Map<String, BigDecimal> weights,
70 @JsonProperty("reservationBid") Bid resBid) {
71 this.domain = domain;
72 this.name = name;
73 this.reservationBid = resBid;
74 this.issueUtilities.putAll(utils);
75 this.issueWeights.putAll(weights);
76
77 if (domain == null) {
78 throw new NullPointerException("domain=null");
79 }
80 if (utils == null) {
81 throw new NullPointerException("utils=null");
82 }
83 if (weights == null) {
84 throw new NullPointerException("weights=null");
85 }
86 if (utils.values().contains(null)) {
87 throw new NullPointerException(
88 "One of the ValueSetUtilities in issueUtilitiesis null:"
89 + utils);
90 }
91 if (weights.values().contains(null)) {
92 throw new NullPointerException("One of the weights is null");
93 }
94 if (utils.keySet().contains(null)) {
95 throw new NullPointerException("One of the issue names is null");
96 }
97 if (name == null || !(name.matches("[a-zA-Z0-9]+"))) {
98 throw new IllegalArgumentException(
99 "Name must be simple (a-Z, 0-9) but got " + name);
100 }
101 if (!(utils.keySet().equals(domain.getIssues()))) {
102 throw new IllegalArgumentException(
103 "The issues in utilityspace and domain do not match: utilityspace has issues "
104 + utils.keySet() + " but domain contains "
105 + domain.getIssues());
106 }
107 if (!(weights.keySet().equals(domain.getIssues()))) {
108 throw new IllegalArgumentException(
109 "The issues in weights and domain do not match: weights has "
110 + weights.keySet() + " but domain contains "
111 + domain.getIssues());
112 }
113 for (String issue : issueUtilities.keySet()) {
114 String message = issueUtilities.get(issue)
115 .isFitting(domain.getValues(issue));
116 if (message != null)
117 throw new IllegalArgumentException(message);
118 }
119
120 //#PY sum: Decimal = sum(issueWeights.values()) #type:ignore
121 BigDecimal sum = weights.values().stream().reduce(BigDecimal.ZERO,
122 BigDecimal::add);
123 if (BigDecimal.ONE.compareTo(sum) != 0) { // equals does NOT work for
124 // comparing BigDecimals!!
125 throw new IllegalArgumentException("The sum of the weights ("
126 + weights.values() + ") must be 1");
127 }
128 if (resBid != null) {
129 String message = domain.isFitting(resBid);
130 if (message != null)
131 throw new IllegalArgumentException(
132 "reservationbid is not fitting domain: " + message);
133 }
134 }
135
136 @Override
137 public BigDecimal getUtility(Bid bid) {
138 //#PY return sum([ self._util(iss, bid.getValue(iss)) for iss in self._issueWeights.keys() ]) #type:ignore
139 return issueWeights.keySet().stream()
140 .map(iss -> util(iss, bid.getValue(iss)))
141 .reduce(BigDecimal.ZERO, BigDecimal::add);
142 }
143
144 @Override
145 public BigDecimal getWeight(String issue) {
146 return issueWeights.get(issue);
147 }
148
149 @Override
150 public String toString() {
151 return "LinearAdditive[" + issueUtilities + "," + issueWeights + ","
152 + reservationBid + "]";
153 }
154
155 @Override
156 public Bid getReservationBid() {
157 return reservationBid;
158 }
159
160 @Override
161 public int hashCode() {
162 final int prime = 31;
163 int result = 1;
164 result = prime * result + ((domain == null) ? 0 : domain.hashCode());
165 result = prime * result
166 + ((issueUtilities == null) ? 0 : issueUtilities.hashCode());
167 result = prime * result
168 + ((issueWeights == null) ? 0 : issueWeights.hashCode());
169 result = prime * result + ((name == null) ? 0 : name.hashCode());
170 result = prime * result
171 + ((reservationBid == null) ? 0 : reservationBid.hashCode());
172 return result;
173 }
174
175 @Override
176 public boolean equals(Object obj) {
177 if (this == obj)
178 return true;
179 if (obj == null)
180 return false;
181 if (getClass() != obj.getClass())
182 return false;
183 LinearAdditiveUtilitySpace other = (LinearAdditiveUtilitySpace) obj;
184 if (domain == null) {
185 if (other.domain != null)
186 return false;
187 } else if (!domain.equals(other.domain))
188 return false;
189 if (issueUtilities == null) {
190 if (other.issueUtilities != null)
191 return false;
192 } else if (!issueUtilities.equals(other.issueUtilities))
193 return false;
194 if (issueWeights == null) {
195 if (other.issueWeights != null)
196 return false;
197 } else if (!issueWeights.equals(other.issueWeights))
198 return false;
199 if (name == null) {
200 if (other.name != null)
201 return false;
202 } else if (!name.equals(other.name))
203 return false;
204 if (reservationBid == null) {
205 if (other.reservationBid != null)
206 return false;
207 } else if (!reservationBid.equals(other.reservationBid))
208 return false;
209 return true;
210 }
211
212 @Override
213 public Domain getDomain() {
214 return domain;
215 }
216
217 @Override
218 public String getName() {
219 return name;
220 }
221
222 @Override
223 public Map<String, ValueSetUtilities> getUtilities() {
224 return Collections.unmodifiableMap(issueUtilities);
225 }
226
227 @Override
228 public Map<String, BigDecimal> getWeights() {
229 return Collections.unmodifiableMap(issueWeights);
230 }
231
232 /**
233 *
234 * @param issue the issue to get weighted util for
235 * @param value the issue value to use (typically coming from a bid)
236 * @return weighted util of just the issue value:
237 * issueUtilities[issue].utility(value) * issueWeights[issue)
238 */
239 private BigDecimal util(String issue, Value value) {
240 return issueWeights.get(issue)
241 .multiply(issueUtilities.get(issue).getUtility(value));
242 }
243
244}
Note: See TracBrowser for help on using the repository browser.