source: ai2020/group5/src/main/java/Group5BiddingStrategy.java@ 3

Last change on this file since 3 was 2, checked in by wouter, 4 years ago

#1925 added aitechniques group5

File size: 9.9 KB
Line 
1import java.math.BigDecimal;
2import java.util.Collection;
3import java.util.HashMap;
4import java.util.List;
5import java.util.concurrent.ThreadLocalRandom;
6import java.util.logging.Level;
7import java.util.stream.Collectors;
8import java.util.stream.StreamSupport;
9
10import geniusweb.actions.Accept;
11import geniusweb.actions.Action;
12import geniusweb.actions.EndNegotiation;
13import geniusweb.actions.Offer;
14import geniusweb.actions.PartyId;
15import geniusweb.boa.BoaState;
16import geniusweb.boa.biddingstrategy.BiddingStrategy;
17import geniusweb.boa.biddingstrategy.ExtendedUtilSpace;
18import geniusweb.inform.Settings;
19import geniusweb.issuevalue.Bid;
20import geniusweb.opponentmodel.FrequencyOpponentModel;
21import geniusweb.opponentmodel.OpponentModel;
22import geniusweb.profile.Profile;
23import geniusweb.profile.utilityspace.LinearAdditive;
24import tudelft.utilities.immutablelist.ImmutableList;
25
26import javax.swing.*;
27
28// Adapted Time dependent bidding strategy, given that class isn't subclassable
29public class Group5BiddingStrategy implements BiddingStrategy {
30 // bidSpace=null means we're not yet initialized.
31 private ExtendedUtilSpace bidSpace = null;
32 private Double e, k, min, max; // min, max attainable utility
33 private PartyId me;
34
35 protected ImmutableList<Bid> getPossibleOptions(BoaState state) {
36 if (bidSpace == null) {
37 init(state);
38 }
39
40 double utilityGoal = p(
41 state.getProgress().get(System.currentTimeMillis()));
42
43 // if there is no opponent model available
44 return bidSpace.getBids((BigDecimal.valueOf(utilityGoal)));
45
46 }
47
48 protected BigDecimal bidToUtility(Bid bid, OpponentModel model) {
49 if (!(model instanceof FrequencyOpponentModel)) {
50 return BigDecimal.ZERO;
51 }
52 return ((FrequencyOpponentModel) model).getUtility(bid);
53 }
54
55 protected Bid rouletteWheel(Group5BoaState state, ImmutableList<Bid> bidOptions) {
56 Collection<OpponentModel> opponentModels = state.getOpponentModels().values();
57 BigDecimal numOpponents = BigDecimal.valueOf(opponentModels.size());
58 if (numOpponents.equals(BigDecimal.ZERO)) {
59 return bidOptions.get(0);
60 }
61 List<BigDecimal> utilities = StreamSupport.stream(bidOptions.spliterator(), false)
62 .map(bid -> opponentModels.stream()
63 .map(model -> bidToUtility(bid, model))
64 .reduce(BigDecimal.ZERO, BigDecimal::add)
65 .divide(numOpponents, 4, BigDecimal.ROUND_HALF_UP))
66 .collect(Collectors.toList());
67
68 // Perform Roulette Wheel Selection
69 BigDecimal randomNumber = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble());
70 BigDecimal utilitiesSum = utilities.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
71 BigDecimal cumSum = BigDecimal.ZERO;
72 for (int i = 0; i < utilities.size(); i++) {
73 cumSum = cumSum.add(utilities.get(i));
74 BigDecimal cumRelativeSum = cumSum.divide(utilitiesSum, 4, BigDecimal.ROUND_HALF_UP);
75 if (randomNumber.compareTo(cumRelativeSum) < 0) { // RandomNumber is smaller than cumRelativeSum
76 return bidOptions.get(i > 0 ? i - 1 : 0);
77 }
78 }
79 return bidOptions.get(utilities.size() - 1);
80 }
81
82 public Action getAction(BoaState state) {
83 ImmutableList<Bid> bidOptions = getPossibleOptions(state);
84
85 if (!(state instanceof Group5BoaState) || bidOptions.size().intValue() == 0) {
86 // should not happen, emergency exit
87 state.getReporter().log(Level.WARNING,
88 "No viable bids found around current utility target");
89 Bid lastBid = getLastBid(state.getActionHistory());
90 if (lastBid == null)
91 return new EndNegotiation(me);
92 return new Accept(me, lastBid);
93 }
94 return new Offer(me, rouletteWheel((Group5BoaState)state, bidOptions));
95
96 }
97
98 /**
99 * Overrideable for hard configuring this component.
100 *
101 * @param state the {@link BoaState}
102 * @return the parameter e for the time depemdency function
103 * {@link #f(double)}. The parameter is 1 by default, or the value
104 * for the "e" parameter in the {@link Settings} if available.
105 */
106 protected Double getE(BoaState state) {
107 HashMap<String, Object> params = state.getSettings().getParameters();
108 Double e = 1d;
109 if (params.containsKey("e")) {
110 e = (Double) params.get("e");
111 if (e < 0 || e > 1)
112 throw new IllegalArgumentException(
113 "e must be in [0,1] but got " + this.e);
114 }
115 return e;
116 }
117
118 /**
119 * Overrideable for hard configuring this component.
120 *
121 * @param state the {@link BoaState}
122 * @return the parameter k for the time dependency function
123 * {@link #f(double)}. The parameter is 0 by default, or the value
124 * for the "k" parameter in the {@link Settings} if available.
125 */
126 protected Double getK(BoaState state) {
127 HashMap<String, Object> params = state.getSettings().getParameters();
128 Double k = 0d; // default
129 if (params.containsKey("k")) {
130 k = (Double) params.get("k");
131 if (k < 0 || k > 1)
132 throw new IllegalArgumentException(
133 "k must be in [0,1] but got " + this.e);
134 }
135 return k;
136 }
137
138 /**
139 * Assumes {@link #bidSpace} has been initialized.
140 * <p>
141 * Overrideable for hard configuring this component.
142 *
143 * @param state the {@link BoaState}
144 * @return the min value for {@link #p(double)}. We use the "min" parameter
145 * in the {@link Settings} if available. If not available, the
146 * parameter is computed as the utility of the reservation bid. If
147 * there is no reservation bid, we use the minimum utility of the
148 * available profile.
149 */
150 protected Double getMin(BoaState state) {
151 HashMap<String, Object> params = state.getSettings().getParameters();
152 if (params.containsKey("min")) {
153 return Math.max(0f, Math.min(1f, (Double) params.get("min")));
154 }
155 LinearAdditive profile = (LinearAdditive) state.getProfile();
156 if (profile.getReservationBid() != null) {
157 return profile.getUtility(profile.getReservationBid())
158 .doubleValue();
159 }
160
161 return bidSpace.getMin().doubleValue();
162 }
163
164 /**
165 * Assumes {@link #bidSpace} has been initialized.
166 * <p>
167 * Overrideable for hard configuring this component.
168 *
169 * @param state the {@link BoaState}
170 * @return the max value for {@link #p(double)}. We use the "max" parameter
171 * in the {@link Settings} if available. If not available, we use
172 * the maximum utility of the available profile.
173 */
174 protected Double getMax(BoaState state) {
175 HashMap<String, Object> params = state.getSettings().getParameters();
176 if (params.containsKey("max")) {
177 return Math.max(0f, Math.min(1f, (Double) params.get("max")));
178 }
179 return bidSpace.getMax().doubleValue();
180
181 }
182
183 /**
184 * @return the most recent bid that was offered, or null if no offer has
185 * been done yet.
186 */
187 private Bid getLastBid(List<Action> history) {
188 for (int n = history.size() - 1; n >= 0; n--) {
189 Action action = history.get(n);
190 if (action instanceof Offer) {
191 return ((Offer) action).getBid();
192 }
193 }
194 return null;
195 }
196
197 /**
198 * initializes bidSpace
199 *
200 * @param state
201 */
202 private void init(BoaState state) {
203 this.me = state.getSettings().getID();
204 Profile prof = state.getProfile();
205 if (!(prof instanceof LinearAdditive))
206 throw new IllegalArgumentException(
207 "Requires a LinearAdditive space but got " + prof);
208 LinearAdditive profile = (LinearAdditive) prof;
209
210 this.bidSpace = getBidSpace(profile);
211 this.e = getE(state);
212 this.k = getK(state);
213 this.min = getMin(state);
214 this.max = getMax(state);
215
216 state.getReporter().log(Level.INFO,
217 "BOA biddingstrategy min util = " + this.min);
218 }
219
220 /**
221 * Makes sure the target utility with in the acceptable range according to
222 * the domain
223 *
224 * @param t the normalized current time in the negotiation, where t=9 at
225 * start of negotiation and t=1 at end of negotiation.
226 * @return double
227 */
228 private double p(double t) {
229 return this.min + (this.max - this.min) * (1.0 - f(t));
230 }
231
232 /**
233 * From the paper:
234 *
235 * A wide range of time dependent functions can be defined by varying the
236 * way in which f(t) is computed. However, functions must ensure that 0 <=
237 * f(t) <= 1, f(0) = k, and f(1) = 1.
238 *
239 * That is, the offer will always be between the value range, at the
240 * beginning it will give the initial constant and when the deadline is
241 * reached, it will offer the reservation value.
242 *
243 * @param t the normalized current time in the negotiation, where t=9 at
244 * start of negotiation and t=1 at end of negotiation.
245 * @return
246 */
247 private double f(double t) {
248 double ft = k + (1.0 - k) * Math.pow(t, 1.0 / e);
249 return ft;
250 }
251
252 /**
253 * Factory method to get the {@link ExtendedUtilSpace} helper class.
254 * Overridable eg for tests
255 *
256 * @param profile a {@link LinearAdditive} profile
257 *
258 * @return {@link ExtendedUtilSpace}
259 */
260 protected ExtendedUtilSpace getBidSpace(LinearAdditive profile) {
261 return new ExtendedUtilSpace(profile);
262 }
263
264}
Note: See TracBrowser for help on using the repository browser.