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

Last change on this file was 52, checked in by ruud, 14 months ago

Fixed small issues in domaineditor.

File size: 6.9 KB
Line 
1package geniusweb.opponentmodel;
2
3import java.math.BigDecimal;
4import java.math.BigInteger;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.Map;
8import java.util.stream.Collectors;
9
10import geniusweb.actions.Action;
11import geniusweb.actions.Offer;
12import geniusweb.issuevalue.Bid;
13import geniusweb.issuevalue.Domain;
14import geniusweb.issuevalue.NumberValue;
15import geniusweb.issuevalue.Value;
16import geniusweb.profile.utilityspace.NumberValueSetUtilities;
17import geniusweb.profile.utilityspace.UtilitySpace;
18import geniusweb.progress.Progress;
19import geniusweb.references.Parameters;
20
21/**
22 * implements an {@link OpponentModel} by counting frequencies of bids placed by
23 * the opponent.
24 * <p>
25 * NOTE: {@link NumberValue}s are also treated as 'discrete', so the frequency
26 * of one value does not influence the influence the frequency of nearby values
27 * (as you might expect as {@link NumberValueSetUtilities} is only affected by
28 * the endpoints).
29 * <p>
30 * immutable.
31 */
32public class FrequencyOpponentModel implements UtilitySpace, OpponentModel {
33
34 private static final int DECIMALS = 4; // accuracy of our computations.
35 private final Domain domain;
36 private final Map<String, Map<Value, Integer>> bidFrequencies;
37 private final BigInteger totalBids;
38 private final Bid resBid;
39
40 public FrequencyOpponentModel() {
41 this.domain = null;
42 this.bidFrequencies = null;
43 this.totalBids = BigInteger.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, BigInteger 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 newDomain, Bid newResBid) {
74 if (newDomain == null) {
75 throw new NullPointerException("domain is not initialized");
76 }
77 // FIXME merge already available frequencies?
78 return new FrequencyOpponentModel(newDomain,
79 newDomain.getIssues().stream().collect(
80 Collectors.toMap(iss -> iss, iss -> new HashMap<>())),
81 BigInteger.ZERO, newResBid);
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 == BigInteger.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" + this.hashCode() + "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(BigInteger.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 @Override
161 public OpponentModel with(Parameters parameters) {
162 return this; // ignore parameters
163 }
164
165 /**
166 *
167 * @param issue the issue to check
168 * @param value the value to check
169 * @return the fraction of the total cases that bids contained given value
170 * for the issue.
171 */
172 private BigDecimal getFraction(String issue, Value value) {
173 if (totalBids == BigInteger.ZERO) {
174 return BigDecimal.ONE;
175 }
176 Integer freq = bidFrequencies.get(issue).get(value);
177 if (freq == null) {
178 freq = 0;
179 }
180 return new BigDecimal(freq).divide(new BigDecimal(totalBids), DECIMALS,
181 BigDecimal.ROUND_HALF_UP);
182 }
183
184 /**
185 *
186 * @param freqs
187 * @return deep copy of freqs map.
188 */
189 private static Map<String, Map<Value, Integer>> cloneMap(
190 Map<String, Map<Value, Integer>> freqs) {
191 Map<String, Map<Value, Integer>> map = new HashMap<>();
192 for (String issue : freqs.keySet()) {
193 map.put(issue, new HashMap<Value, Integer>(freqs.get(issue)));
194 }
195 return map;
196 }
197
198 @Override
199 public Bid getReservationBid() {
200 return resBid;
201 }
202
203 @Override
204 public int hashCode() {
205 final int prime = 31;
206 int result = 1;
207 result = prime * result
208 + ((bidFrequencies == null) ? 0 : bidFrequencies.hashCode());
209 result = prime * result + ((domain == null) ? 0 : domain.hashCode());
210 result = prime * result
211 + ((totalBids == null) ? 0 : totalBids.hashCode());
212 return result;
213 }
214
215 @Override
216 public boolean equals(Object obj) {
217 if (this == obj)
218 return true;
219 if (obj == null)
220 return false;
221 if (getClass() != obj.getClass())
222 return false;
223 FrequencyOpponentModel other = (FrequencyOpponentModel) obj;
224 if (bidFrequencies == null) {
225 if (other.bidFrequencies != null)
226 return false;
227 } else if (!bidFrequencies.equals(other.bidFrequencies))
228 return false;
229 if (domain == null) {
230 if (other.domain != null)
231 return false;
232 } else if (!domain.equals(other.domain))
233 return false;
234 if (totalBids == null) {
235 if (other.totalBids != null)
236 return false;
237 } else if (!totalBids.equals(other.totalBids))
238 return false;
239 return true;
240 }
241
242 @Override
243 public String toString() {
244 return "FrequencyOpponentModel[" + totalBids + "," + bidFrequencies
245 + "]";
246 }
247
248}
Note: See TracBrowser for help on using the repository browser.