1 | package geniusweb.profile.utilityspace;
|
---|
2 |
|
---|
3 | import java.math.BigDecimal;
|
---|
4 | import java.util.Collections;
|
---|
5 | import java.util.HashMap;
|
---|
6 | import java.util.LinkedList;
|
---|
7 | import java.util.List;
|
---|
8 | import java.util.Map;
|
---|
9 |
|
---|
10 | import org.eclipse.jdt.annotation.NonNull;
|
---|
11 |
|
---|
12 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
---|
13 | import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
---|
14 | import com.fasterxml.jackson.annotation.JsonCreator;
|
---|
15 | import com.fasterxml.jackson.annotation.JsonProperty;
|
---|
16 |
|
---|
17 | import geniusweb.issuevalue.Value;
|
---|
18 | import geniusweb.issuevalue.ValueSet;
|
---|
19 |
|
---|
20 | /**
|
---|
21 | * Contains utilities of a ProductOfValue of a {@link SumOfGroupsUtilitySpace}.
|
---|
22 | * So this is similar to a {@link DiscreteValueSetUtilities} but instead of
|
---|
23 | * issues this contains an (ordered) list of issues. This object serializes to
|
---|
24 | * something like <code>
|
---|
25 | * {"partsutils":
|
---|
26 | * {"issues":["issue1","issue2"],
|
---|
27 | * "utilslist":[{"values":["low","low"],"util":0.3},{"values":["high","high"],"util":0.9}]}}
|
---|
28 | * </code> The issues field contains a list of N issues in the domain, The
|
---|
29 | * utilslist contains a list of dictionaries, with "values" containing a list of
|
---|
30 | * N issue values, in the same order as the issues list and with "util"
|
---|
31 | * containng the utility value of that set of values.
|
---|
32 | */
|
---|
33 | @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
|
---|
34 | public class PartsUtilities implements ValueSetUtilities {
|
---|
35 | private final @NonNull List<@NonNull String> issues;
|
---|
36 | // the real but not-serializable values (because Map can only have String
|
---|
37 | // keys in jackson)
|
---|
38 | private transient final @NonNull Map<@NonNull ProductOfValue, @NonNull BigDecimal> utilities = new HashMap<>();
|
---|
39 | // copy only for serializing
|
---|
40 | private final @NonNull List<@NonNull PartUtil> utilslist = new LinkedList<>();
|
---|
41 |
|
---|
42 | /**
|
---|
43 | *
|
---|
44 | * @param issues list of issues
|
---|
45 | * @param utils with keys: list of values and value: utility value for that
|
---|
46 | * list of values. All list-of-values missing from the map are
|
---|
47 | * assumed to have utility 0. This includes partial bids where
|
---|
48 | * list-of-values contain null objects.
|
---|
49 | */
|
---|
50 | public PartsUtilities(@NonNull List<@NonNull String> issues,
|
---|
51 | @NonNull Map<@NonNull ProductOfValue, @NonNull BigDecimal> utils) {
|
---|
52 | if (issues == null || utils == null) {
|
---|
53 | throw new IllegalArgumentException(
|
---|
54 | "issues and utils must be not null");
|
---|
55 | }
|
---|
56 |
|
---|
57 | this.issues = issues;
|
---|
58 | this.utilities.putAll(utils);
|
---|
59 |
|
---|
60 | checkUtilities();
|
---|
61 | for (final @NonNull ProductOfValue pval : utils.keySet()) {
|
---|
62 | utilslist.add(new PartUtil(pval.getValues(), utils.get(pval)));
|
---|
63 | }
|
---|
64 | }
|
---|
65 |
|
---|
66 | @JsonCreator
|
---|
67 | public PartsUtilities(
|
---|
68 | @JsonProperty("issues") @NonNull List<@NonNull String> issues,
|
---|
69 | @JsonProperty("utilslist") @NonNull List<@NonNull PartUtil> utilslist) {
|
---|
70 | this(issues, list2map(utilslist));
|
---|
71 | }
|
---|
72 |
|
---|
73 | /**
|
---|
74 | *
|
---|
75 | * @param pval the {@link ProductOfValue} value
|
---|
76 | * @return the utility of the value, in the same order as
|
---|
77 | * {@link #getIssues()}. Returns 0 if there is no utility set for
|
---|
78 | * the given combination of values. Notice, partial bids will
|
---|
79 | * usually have utility 0.
|
---|
80 | *
|
---|
81 | */
|
---|
82 | @Override
|
---|
83 | public @NonNull BigDecimal getUtility(@NonNull Value pval) {
|
---|
84 | @NonNull
|
---|
85 | BigDecimal val = utilities.get(pval);
|
---|
86 | if (val == null)
|
---|
87 | return BigDecimal.ZERO;
|
---|
88 | return val;
|
---|
89 | }
|
---|
90 |
|
---|
91 | @Override
|
---|
92 | public String isFitting(@NonNull ValueSet valueset) {
|
---|
93 | return null; // what can we tests here?
|
---|
94 | }
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * @return the issues that are contained here.
|
---|
98 | */
|
---|
99 | public @NonNull List<@NonNull String> getIssues() {
|
---|
100 | return issues;
|
---|
101 | }
|
---|
102 |
|
---|
103 | /**
|
---|
104 | *
|
---|
105 | * @return map with all available values and their utilities. The map and
|
---|
106 | * its contents should all be immutable.
|
---|
107 | */
|
---|
108 | public @NonNull Map<@NonNull ProductOfValue, @NonNull BigDecimal> getUtilities() {
|
---|
109 | return Collections.unmodifiableMap(utilities);
|
---|
110 | }
|
---|
111 |
|
---|
112 | /**
|
---|
113 | *
|
---|
114 | * @param other another {@link PartsUtilities} map. The issues in the other
|
---|
115 | * map must be different from {@link #issues}.
|
---|
116 | * @return new PartsUtils, with the powermap of all combinations of one
|
---|
117 | * element from this and one from the other map, and with the
|
---|
118 | * utilities computed as the sum of thisvalue + othervalue.
|
---|
119 | */
|
---|
120 | public @NonNull PartsUtilities add(@NonNull PartsUtilities other) {
|
---|
121 | for (@NonNull
|
---|
122 | String issue : issues) {
|
---|
123 | if (other.issues.contains(issue)) {
|
---|
124 | throw new IllegalArgumentException(
|
---|
125 | "Issue " + issue + " exists already");
|
---|
126 | }
|
---|
127 | }
|
---|
128 |
|
---|
129 | final @NonNull List<@NonNull String> combinedissues = new LinkedList<>(
|
---|
130 | issues);
|
---|
131 | combinedissues.addAll(other.issues);
|
---|
132 |
|
---|
133 | final @NonNull Map<@NonNull ProductOfValue, @NonNull BigDecimal> combinedvalues = new HashMap<>();
|
---|
134 | for (@NonNull
|
---|
135 | ProductOfValue productOfValue : this.utilities.keySet()) {
|
---|
136 | for (@NonNull
|
---|
137 | ProductOfValue otherProductOfValue : other.utilities.keySet()) {
|
---|
138 | final @NonNull BigDecimal combinedutil = getUtility(
|
---|
139 | productOfValue)
|
---|
140 | .add(other.getUtility(otherProductOfValue));
|
---|
141 | combinedvalues.put(productOfValue.merge(otherProductOfValue),
|
---|
142 | combinedutil);
|
---|
143 | }
|
---|
144 | }
|
---|
145 | return new PartsUtilities(combinedissues, combinedvalues);
|
---|
146 |
|
---|
147 | }
|
---|
148 |
|
---|
149 | @Override
|
---|
150 | public int hashCode() {
|
---|
151 | final int prime = 31;
|
---|
152 | int result = 1;
|
---|
153 | result = prime * result + ((issues == null) ? 0 : issues.hashCode());
|
---|
154 | result = prime * result
|
---|
155 | + ((utilities == null) ? 0 : utilities.hashCode());
|
---|
156 | return result;
|
---|
157 | }
|
---|
158 |
|
---|
159 | @Override
|
---|
160 | public boolean equals(Object obj) {
|
---|
161 | if (this == obj)
|
---|
162 | return true;
|
---|
163 | if (obj == null)
|
---|
164 | return false;
|
---|
165 | if (getClass() != obj.getClass())
|
---|
166 | return false;
|
---|
167 | PartsUtilities other = (PartsUtilities) obj;
|
---|
168 | if (issues == null) {
|
---|
169 | if (other.issues != null)
|
---|
170 | return false;
|
---|
171 | } else if (!issues.equals(other.issues))
|
---|
172 | return false;
|
---|
173 | if (utilities == null) {
|
---|
174 | if (other.utilities != null)
|
---|
175 | return false;
|
---|
176 | } else if (!utilities.equals(other.utilities))
|
---|
177 | return false;
|
---|
178 | return true;
|
---|
179 | }
|
---|
180 |
|
---|
181 | @Override
|
---|
182 | public @NonNull String toString() {
|
---|
183 | return "PartsUtilities[" + issues + "," + utilities + "]";
|
---|
184 | }
|
---|
185 |
|
---|
186 | private void checkUtilities() {
|
---|
187 | /*#PY
|
---|
188 | * if True in (x==None or x<0 pr x>1 for x in self._utilities.values():
|
---|
189 | * raise ValueError("part weights must all be in [0,1]")
|
---|
190 | */
|
---|
191 | if (utilities.values().stream()
|
---|
192 | .anyMatch(v -> v == null || v.compareTo(BigDecimal.ZERO) < 0
|
---|
193 | || v.compareTo(BigDecimal.ONE) > 0)) {
|
---|
194 | throw new IllegalArgumentException(
|
---|
195 | "part weights must all be in [0,1]");
|
---|
196 | }
|
---|
197 | }
|
---|
198 |
|
---|
199 | private static @NonNull Map<@NonNull ProductOfValue, @NonNull BigDecimal> list2map(
|
---|
200 | @NonNull List<@NonNull PartUtil> list) {
|
---|
201 | final @NonNull Map<@NonNull ProductOfValue, @NonNull BigDecimal> map = new HashMap<>();
|
---|
202 | for (final @NonNull PartUtil partutil : list) {
|
---|
203 | map.put(new ProductOfValue(partutil.getValues()),
|
---|
204 | partutil.getUtil());
|
---|
205 | }
|
---|
206 | return map;
|
---|
207 | }
|
---|
208 |
|
---|
209 | /**
|
---|
210 | *
|
---|
211 | * @return the max utility of all values contained here.
|
---|
212 | */
|
---|
213 | public @NonNull BigDecimal getMaxUtility() {
|
---|
214 | @NonNull
|
---|
215 | BigDecimal maxutil = BigDecimal.ZERO;
|
---|
216 | for (@NonNull
|
---|
217 | ProductOfValue vakye : utilities.keySet()) {
|
---|
218 | if (utilities.get(vakye).compareTo(maxutil) > 0) {
|
---|
219 | maxutil = utilities.get(vakye);
|
---|
220 | }
|
---|
221 | }
|
---|
222 | return maxutil;
|
---|
223 | }
|
---|
224 |
|
---|
225 | }
|
---|
226 |
|
---|
227 | /**
|
---|
228 | * contains list of values, and the utility for this combi of values. The values
|
---|
229 | * make only sense with a corresponding list of issues, but that is managed in
|
---|
230 | * (see {@link PartsUtilities}.
|
---|
231 | * <p>
|
---|
232 | * immutable.
|
---|
233 | */
|
---|
234 | @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
|
---|
235 | class PartUtil {
|
---|
236 | private final @NonNull List<@NonNull Value> values;
|
---|
237 |
|
---|
238 | private final @NonNull BigDecimal util;
|
---|
239 |
|
---|
240 | @JsonCreator
|
---|
241 | public PartUtil(
|
---|
242 | @JsonProperty("values") @NonNull List<@NonNull Value> values,
|
---|
243 | @JsonProperty("util") @NonNull BigDecimal util) {
|
---|
244 | this.values = values;
|
---|
245 | this.util = util;
|
---|
246 | }
|
---|
247 |
|
---|
248 | public @NonNull List<@NonNull Value> getValues() {
|
---|
249 | return values;
|
---|
250 | }
|
---|
251 |
|
---|
252 | public @NonNull BigDecimal getUtil() {
|
---|
253 | return util;
|
---|
254 | }
|
---|
255 | }
|
---|