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

Last change on this file since 14 was 14, checked in by bart, 4 years ago

Release 1.4.0

File size: 5.4 KB
Line 
1package geniusweb.opponentmodel;
2
3import java.math.BigDecimal;
4import java.util.HashMap;
5import java.util.Map;
6import java.util.stream.Collectors;
7
8import geniusweb.issuevalue.Bid;
9import geniusweb.issuevalue.Domain;
10import geniusweb.issuevalue.NumberValue;
11import geniusweb.issuevalue.Value;
12import geniusweb.profile.utilityspace.NumberValueSetUtilities;
13import geniusweb.profile.utilityspace.UtilitySpace;
14
15/**
16 * implements an {@link OpponentModel} by counting frequencies of bids placed by
17 * the opponent.
18 * <p>
19 * NOTE: {@link NumberValue}s are also treated as 'discrete', so the frequency
20 * of one value does not influence the influence the frequency of nearby values
21 * (as you might expect as {@link NumberValueSetUtilities} is only affected by
22 * the endpoints).
23 *
24 */
25public class FrequencyOpponentModel implements OpponentModel, UtilitySpace {
26
27 private static final int DECIMALS = 4; // accuracy of our computations.
28 private final Domain domain;
29 private final Map<String, Map<Value, Integer>> bidFrequencies;
30 private final BigDecimal totalBids;
31 private static int serial = 1; // counter for auto name generation
32
33 public FrequencyOpponentModel(Domain domain) {
34 // map with empth hashmap for each issue.
35 this(domain,
36 domain.getIssues().stream().collect(
37 Collectors.toMap(iss -> iss, iss -> new HashMap<>())),
38 BigDecimal.ZERO);
39 }
40
41 @Override
42 public BigDecimal getUtility(Bid bid) {
43 String err = domain.isComplete(bid);
44 if (err != null) {
45 throw new IllegalArgumentException(err);
46 }
47 if (totalBids == BigDecimal.ZERO) {
48 return BigDecimal.ONE;
49 }
50 BigDecimal sum = BigDecimal.ZERO;
51 // Assume all issues have equal weight.
52 for (String issue : domain.getIssues()) {
53 sum = sum.add(getFraction(issue, bid.getValue(issue)));
54 }
55 return sum.divide(new BigDecimal(bidFrequencies.size()), DECIMALS,
56 BigDecimal.ROUND_HALF_UP);
57 }
58
59 @Override
60 public String getName() {
61 return "FreqOppModel" + (serial++) + "For" + domain;
62 }
63
64 /**
65 *
66 * @param issue the issue to check
67 * @param value the value to check
68 * @return the fraction of the total cases that bids contained given value
69 * for the issue.
70 */
71 private BigDecimal getFraction(String issue, Value value) {
72 if (totalBids == BigDecimal.ZERO) {
73 return BigDecimal.ONE;
74 }
75 Integer freq = bidFrequencies.get(issue).get(value);
76 if (freq == null) {
77 freq = 0;
78 }
79 return new BigDecimal(freq).divide(totalBids, DECIMALS,
80 BigDecimal.ROUND_HALF_UP);
81 }
82
83 @Override
84 public Domain getDomain() {
85 return domain;
86 }
87
88 @Override
89 public FrequencyOpponentModel update(Bid bid) {
90 String err = domain.isComplete(bid);
91 if (err != null) {
92 throw new IllegalArgumentException(err);
93 }
94 Map<String, Map<Value, Integer>> newFreqs = cloneMap(bidFrequencies);
95 for (String issue : domain.getIssues()) {
96 Map<Value, Integer> freqs = newFreqs.get(issue);
97 Value value = bid.getValue(issue);
98 Integer oldfreq = freqs.get(value);
99 if (oldfreq == null) {
100 oldfreq = 0;
101 }
102 freqs.put(value, oldfreq + 1);
103 }
104
105 return new FrequencyOpponentModel(domain, newFreqs,
106 totalBids.add(BigDecimal.ONE));
107 }
108
109 /**
110 * internal constructor. Assumes the freqs keyset is equal to the available
111 * issues.
112 *
113 * @param domain the domain
114 * @param freqs the observed frequencies for all issue values. This map is
115 * assumed to be a fresh private-access only copy.
116 * @param total the total number of bids contained in the freqs map. This
117 * must be equal to the sum of the Integer values in the
118 * {@link #bidFrequencies} for each issue (this is not
119 * checked).
120 */
121 private FrequencyOpponentModel(Domain domain,
122 Map<String, Map<Value, Integer>> freqs, BigDecimal total) {
123 if (domain == null) {
124 throw new NullPointerException("domain=null");
125 }
126 this.domain = domain;
127 this.bidFrequencies = freqs;
128 this.totalBids = total;
129 }
130
131 /**
132 *
133 * @param freqs
134 * @return deep copy of freqs map.
135 */
136 private static Map<String, Map<Value, Integer>> cloneMap(
137 Map<String, Map<Value, Integer>> freqs) {
138 Map<String, Map<Value, Integer>> map = new HashMap<>();
139 for (String issue : freqs.keySet()) {
140 map.put(issue, new HashMap<Value, Integer>(freqs.get(issue)));
141 }
142 return map;
143 }
144
145 @Override
146 public int hashCode() {
147 final int prime = 31;
148 int result = 1;
149 result = prime * result
150 + ((bidFrequencies == null) ? 0 : bidFrequencies.hashCode());
151 result = prime * result + ((domain == null) ? 0 : domain.hashCode());
152 result = prime * result
153 + ((totalBids == null) ? 0 : totalBids.hashCode());
154 return result;
155 }
156
157 @Override
158 public boolean equals(Object obj) {
159 if (this == obj)
160 return true;
161 if (obj == null)
162 return false;
163 if (getClass() != obj.getClass())
164 return false;
165 FrequencyOpponentModel other = (FrequencyOpponentModel) obj;
166 if (bidFrequencies == null) {
167 if (other.bidFrequencies != null)
168 return false;
169 } else if (!bidFrequencies.equals(other.bidFrequencies))
170 return false;
171 if (domain == null) {
172 if (other.domain != null)
173 return false;
174 } else if (!domain.equals(other.domain))
175 return false;
176 if (totalBids == null) {
177 if (other.totalBids != null)
178 return false;
179 } else if (!totalBids.equals(other.totalBids))
180 return false;
181 return true;
182 }
183
184 @Override
185 public String toString() {
186 return "FrequencyOpponentModel[" + totalBids + "," + bidFrequencies
187 + "]";
188 }
189
190 @Override
191 public Bid getReservationBid() {
192 throw new UnsupportedOperationException();
193 }
194
195}
Note: See TracBrowser for help on using the repository browser.