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

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

#278 added NonNull annotation in many places in the geniusweb code

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