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

Last change on this file since 839 was 825, checked in by wouter, 6 months ago

#291 move annotation to above the javadoc

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