source: opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java@ 35

Last change on this file since 35 was 35, checked in by bart, 3 years ago

Fixed windows time-out.

File size: 6.9 KB
Line 
1package geniusweb.opponentmodel;
2
3import java.math.BigDecimal;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.Map;
7import java.util.stream.Collectors;
8
9import geniusweb.actions.Action;
10import geniusweb.actions.Offer;
11import geniusweb.issuevalue.Bid;
12import geniusweb.issuevalue.Domain;
13import geniusweb.issuevalue.NumberValue;
14import geniusweb.issuevalue.Value;
15import geniusweb.profile.utilityspace.NumberValueSetUtilities;
16import geniusweb.profile.utilityspace.UtilitySpace;
17import geniusweb.progress.Progress;
18import geniusweb.references.Parameters;
19
20/**
21 * implements an {@link OpponentModel} by counting frequencies of bids placed by
22 * the opponent.
23 * <p>
24 * NOTE: {@link NumberValue}s are also treated as 'discrete', so the frequency
25 * of one value does not influence the influence the frequency of nearby values
26 * (as you might expect as {@link NumberValueSetUtilities} is only affected by
27 * the endpoints).
28 * <p>
29 * immutable.
30 */
31public class FrequencyOpponentModel implements UtilitySpace, OpponentModel {
32
33 private static final int DECIMALS = 4; // accuracy of our computations.
34 private final Domain domain;
35 private final Map<String, Map<Value, Integer>> bidFrequencies;
36 private final BigDecimal totalBids;
37 private final Bid resBid;
38
39 public FrequencyOpponentModel() {
40 this.domain = null;
41 this.bidFrequencies = null;
42 this.totalBids = BigDecimal.ZERO;
43 this.resBid = null;
44 }
45
46 /**
47 * internal constructor. Assumes the freqs keyset is equal to the available
48 * issues.
49 *
50 * @param domain the domain
51 * @param freqs the observed frequencies for all issue values. This map is
52 * assumed to be a fresh private-access only copy.
53 * @param total the total number of bids contained in the freqs map. This
54 * must be equal to the sum of the Integer values in the
55 * {@link #bidFrequencies} for each issue (this is not
56 * checked).
57 * @param resBid the reservation bid. Can be null
58 */
59 protected FrequencyOpponentModel(Domain domain,
60 Map<String, Map<Value, Integer>> freqs, BigDecimal total,
61 Bid resBid) {
62 if (domain == null) {
63 throw new IllegalStateException("domain is not initialized");
64 }
65 this.domain = domain;
66 this.bidFrequencies = freqs;
67 this.totalBids = total;
68 this.resBid = resBid;
69 }
70
71 @Override
72 public FrequencyOpponentModel with(Domain newDomain, Bid newResBid) {
73 if (newDomain == null) {
74 throw new NullPointerException("domain is not initialized");
75 }
76 // FIXME merge already available frequencies?
77 return new FrequencyOpponentModel(newDomain,
78 newDomain.getIssues().stream().collect(
79 Collectors.toMap(iss -> iss, iss -> new HashMap<>())),
80 BigDecimal.ZERO, newResBid);
81 }
82
83 @Override
84 public BigDecimal getUtility(Bid bid) {
85 if (domain == null) {
86 throw new IllegalStateException("domain is not initialized");
87 }
88 if (totalBids == BigDecimal.ZERO) {
89 return BigDecimal.ONE;
90 }
91 BigDecimal sum = BigDecimal.ZERO;
92 // Assume all issues have equal weight.
93 for (String issue : domain.getIssues()) {
94 if (bid.getIssues().contains(issue)) {
95 sum = sum.add(getFraction(issue, bid.getValue(issue)));
96 }
97 }
98 return sum.divide(new BigDecimal(bidFrequencies.size()), DECIMALS,
99 BigDecimal.ROUND_HALF_UP);
100 }
101
102 @Override
103 public String getName() {
104 if (domain == null) {
105 throw new IllegalStateException("domain is not initialized");
106 }
107 return "FreqOppModel" + this.hashCode() + "For" + domain;
108 }
109
110 @Override
111 public Domain getDomain() {
112 return domain;
113 }
114
115 @Override
116 public FrequencyOpponentModel with(Action action, Progress progress) {
117 if (domain == null) {
118 throw new IllegalStateException("domain is not initialized");
119 }
120
121 if (!(action instanceof Offer))
122 return this;
123
124 Bid bid = ((Offer) action).getBid();
125 Map<String, Map<Value, Integer>> newFreqs = cloneMap(bidFrequencies);
126 for (String issue : domain.getIssues()) {
127 Map<Value, Integer> freqs = newFreqs.get(issue);
128 Value value = bid.getValue(issue);
129 if (value != null) {
130 Integer oldfreq = freqs.get(value);
131 if (oldfreq == null) {
132 oldfreq = 0;
133 }
134 freqs.put(value, oldfreq + 1);
135 }
136 }
137
138 return new FrequencyOpponentModel(domain, newFreqs,
139 totalBids.add(BigDecimal.ONE), resBid);
140 }
141
142 /**
143 *
144 * @param issue the issue to get frequency info for
145 * @return a map containing a map of values and the number of times that
146 * value was used in previous bids. Values that are possible but not
147 * in the map have frequency 0.
148 */
149 public Map<Value, Integer> getCounts(String issue) {
150 if (domain == null) {
151 throw new IllegalStateException("domain is not initialized");
152 }
153 if (!(bidFrequencies.containsKey(issue))) {
154 return Collections.emptyMap();
155 }
156 return Collections.unmodifiableMap(bidFrequencies.get(issue));
157 }
158
159 @Override
160 public OpponentModel with(Parameters parameters) {
161 return this; // ignore parameters
162 }
163
164 /**
165 *
166 * @param issue the issue to check
167 * @param value the value to check
168 * @return the fraction of the total cases that bids contained given value
169 * for the issue.
170 */
171 private BigDecimal getFraction(String issue, Value value) {
172 if (totalBids == BigDecimal.ZERO) {
173 return BigDecimal.ONE;
174 }
175 Integer freq = bidFrequencies.get(issue).get(value);
176 if (freq == null) {
177 freq = 0;
178 }
179 return new BigDecimal(freq).divide(totalBids, DECIMALS,
180 BigDecimal.ROUND_HALF_UP);
181 }
182
183 /**
184 *
185 * @param freqs
186 * @return deep copy of freqs map.
187 */
188 private static Map<String, Map<Value, Integer>> cloneMap(
189 Map<String, Map<Value, Integer>> freqs) {
190 Map<String, Map<Value, Integer>> map = new HashMap<>();
191 for (String issue : freqs.keySet()) {
192 map.put(issue, new HashMap<Value, Integer>(freqs.get(issue)));
193 }
194 return map;
195 }
196
197 @Override
198 public Bid getReservationBid() {
199 return resBid;
200 }
201
202 @Override
203 public int hashCode() {
204 final int prime = 31;
205 int result = 1;
206 result = prime * result
207 + ((bidFrequencies == null) ? 0 : bidFrequencies.hashCode());
208 result = prime * result + ((domain == null) ? 0 : domain.hashCode());
209 result = prime * result
210 + ((totalBids == null) ? 0 : totalBids.hashCode());
211 return result;
212 }
213
214 @Override
215 public boolean equals(Object obj) {
216 if (this == obj)
217 return true;
218 if (obj == null)
219 return false;
220 if (getClass() != obj.getClass())
221 return false;
222 FrequencyOpponentModel other = (FrequencyOpponentModel) obj;
223 if (bidFrequencies == null) {
224 if (other.bidFrequencies != null)
225 return false;
226 } else if (!bidFrequencies.equals(other.bidFrequencies))
227 return false;
228 if (domain == null) {
229 if (other.domain != null)
230 return false;
231 } else if (!domain.equals(other.domain))
232 return false;
233 if (totalBids == null) {
234 if (other.totalBids != null)
235 return false;
236 } else if (!totalBids.equals(other.totalBids))
237 return false;
238 return true;
239 }
240
241 @Override
242 public String toString() {
243 return "FrequencyOpponentModel[" + totalBids + "," + bidFrequencies
244 + "]";
245 }
246
247}
Note: See TracBrowser for help on using the repository browser.