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

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

Multiple learns with repeated tournament, maven use https.

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