1 | package negotiator.boaframework.sharedagentstate.anac2011;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.Arrays;
|
---|
5 | import java.util.Collections;
|
---|
6 | import java.util.HashMap;
|
---|
7 | import java.util.List;
|
---|
8 |
|
---|
9 | import genius.core.Bid;
|
---|
10 | import genius.core.bidding.BidDetails;
|
---|
11 | import genius.core.bidding.BidDetailsStrictSorterUtility;
|
---|
12 | import genius.core.boaframework.NegotiationSession;
|
---|
13 | import genius.core.boaframework.SharedAgentState;
|
---|
14 | import genius.core.issue.Issue;
|
---|
15 | import genius.core.issue.IssueDiscrete;
|
---|
16 | import genius.core.issue.Value;
|
---|
17 | import genius.core.issue.ValueDiscrete;
|
---|
18 | import genius.core.misc.Pair;
|
---|
19 | import genius.core.misc.Queue;
|
---|
20 |
|
---|
21 | /**
|
---|
22 | * This is the shared code of the acceptance condition and bidding strategy of
|
---|
23 | * ANAC 2011 TheNegotiator. The code was taken from the ANAC2011 TheNegotiator
|
---|
24 | * and adapted to work within the BOA framework.
|
---|
25 | *
|
---|
26 | * @author Mark Hendrikx
|
---|
27 | */
|
---|
28 | public class TheNegotiatorSAS extends SharedAgentState {
|
---|
29 |
|
---|
30 | private final boolean TEST_EQUIVALENCE = false;
|
---|
31 |
|
---|
32 | private NegotiationSession negotiationSession;
|
---|
33 |
|
---|
34 | private final double totalTime = 180;
|
---|
35 | private final double[] endPhases1 = { 140.0 / totalTime, 35.0 / totalTime,
|
---|
36 | 5.0 / totalTime };
|
---|
37 | private double[] endPhases2;
|
---|
38 | private double[] maxThresArray1;
|
---|
39 | private double[] maxThresArray2;
|
---|
40 | private int propArray[];
|
---|
41 | private ArrayList<BidDetails> possibleBids;
|
---|
42 |
|
---|
43 | // queue holding last 30 lapsed time
|
---|
44 | private Queue queue = new Queue();
|
---|
45 | private double lastTime;
|
---|
46 | // queue size
|
---|
47 | private int queueSize;
|
---|
48 | private double discount;
|
---|
49 |
|
---|
50 | private int phase;
|
---|
51 | private double threshold;
|
---|
52 | private int movesLeft;
|
---|
53 |
|
---|
54 | public TheNegotiatorSAS(NegotiationSession negoSession) {
|
---|
55 | negotiationSession = negoSession;
|
---|
56 | NAME = "TheNegotiator";
|
---|
57 | possibleBids = new ArrayList<BidDetails>();
|
---|
58 | createAllBids();
|
---|
59 | if (TEST_EQUIVALENCE) {
|
---|
60 | Collections.sort(possibleBids, new BidDetailsStrictSorterUtility());
|
---|
61 | } else {
|
---|
62 | Collections.sort(possibleBids);
|
---|
63 | }
|
---|
64 |
|
---|
65 | endPhases2 = new double[3];
|
---|
66 | Arrays.fill(endPhases2, 0);
|
---|
67 |
|
---|
68 | maxThresArray1 = new double[4];
|
---|
69 | Arrays.fill(maxThresArray1, 0);
|
---|
70 |
|
---|
71 | maxThresArray2 = new double[4];
|
---|
72 | Arrays.fill(maxThresArray2, 0);
|
---|
73 |
|
---|
74 | propArray = new int[3];
|
---|
75 | Arrays.fill(propArray, 0);
|
---|
76 |
|
---|
77 | queueSize = 15;
|
---|
78 | lastTime = 0;
|
---|
79 | discount = negoSession.getDiscountFactor();
|
---|
80 |
|
---|
81 | // no discounts
|
---|
82 | calculateEndPhaseThresholds();
|
---|
83 |
|
---|
84 | // discounts
|
---|
85 | if (negotiationSession.getDiscountFactor() != 0) {
|
---|
86 | calculatePropArray();
|
---|
87 | calculateEndPhases();
|
---|
88 | }
|
---|
89 | }
|
---|
90 |
|
---|
91 | public ArrayList<BidDetails> getPossibleBids() {
|
---|
92 | return possibleBids;
|
---|
93 | }
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * Calculates the time which should be spend on each phase based on the
|
---|
97 | * distribution of the utilities of the bids.
|
---|
98 | */
|
---|
99 | public void calculateEndPhases() {
|
---|
100 | int sum = 0;
|
---|
101 | for (int i = 0; i < propArray.length; i++) {
|
---|
102 | sum += propArray[i];
|
---|
103 | }
|
---|
104 |
|
---|
105 | endPhases2[0] = discount
|
---|
106 | + (((double) propArray[0] / (double) sum) * (1 - discount));
|
---|
107 | endPhases2[1] = (((double) propArray[1] / (double) sum) * (1 - discount));
|
---|
108 | endPhases2[2] = (((double) propArray[2] / (double) sum) * (1 - discount));
|
---|
109 | }
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * Returns the current phase of the negotiation.
|
---|
113 | *
|
---|
114 | * @return phase of the negotiation
|
---|
115 | */
|
---|
116 | public int calculateCurrentPhase(double time) {
|
---|
117 |
|
---|
118 | int phase = 1;
|
---|
119 | double[] endPhases = endPhases1;
|
---|
120 |
|
---|
121 | if (discount != 0 && time > discount) {
|
---|
122 | endPhases = endPhases2;
|
---|
123 | }
|
---|
124 |
|
---|
125 | if (time > (endPhases[1] + endPhases[0])) {
|
---|
126 | double lapsedTime = time - lastTime;
|
---|
127 | queue.enqueue(lapsedTime);
|
---|
128 | if (queue.size() > queueSize) {
|
---|
129 | queue.dequeue();
|
---|
130 | }
|
---|
131 | phase = 3;
|
---|
132 | } else if (time > endPhases[0]) {
|
---|
133 | phase = 2;
|
---|
134 | }
|
---|
135 | lastTime = time;
|
---|
136 |
|
---|
137 | this.phase = phase;
|
---|
138 | return phase;
|
---|
139 | }
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * Returns the time dependent threshold which specifies how good a bid of an
|
---|
143 | * opponent should be to be accepted. This threshold is also used as a
|
---|
144 | * minimum for the utility of the bid of our agent.
|
---|
145 | *
|
---|
146 | * @return threshold
|
---|
147 | */
|
---|
148 | public double calculateThreshold(double time) {
|
---|
149 | int phase = calculateCurrentPhase(time);
|
---|
150 | double threshold = 0.98; // safe value
|
---|
151 | double[] maxThresArray = maxThresArray1;
|
---|
152 | double[] endPhases = endPhases1;
|
---|
153 |
|
---|
154 | if (discount != 0 && time > discount) {
|
---|
155 | maxThresArray = maxThresArray2;
|
---|
156 | endPhases = endPhases2;
|
---|
157 | }
|
---|
158 | double discountActive = discount;
|
---|
159 | if (time <= discount) {
|
---|
160 | discountActive = 0;
|
---|
161 | }
|
---|
162 |
|
---|
163 | switch (phase) {
|
---|
164 | case 1:
|
---|
165 | threshold = maxThresArray[0]
|
---|
166 | - ((time - discountActive) / (endPhases[0] - discountActive))
|
---|
167 | * (maxThresArray[0] - maxThresArray[1]);
|
---|
168 | break;
|
---|
169 | case 2:
|
---|
170 | threshold = maxThresArray[1]
|
---|
171 | - (((time - endPhases[0]) / (endPhases[1])) * (maxThresArray[1] - maxThresArray[2]));
|
---|
172 | break;
|
---|
173 | case 3:
|
---|
174 | threshold = maxThresArray[2]
|
---|
175 | - (((time - endPhases[0] - endPhases[1]) / (endPhases[2])) * (maxThresArray[2] - maxThresArray[3]));
|
---|
176 | break;
|
---|
177 | }
|
---|
178 | this.threshold = threshold;
|
---|
179 | return threshold;
|
---|
180 | }
|
---|
181 |
|
---|
182 | public int calculateMovesLeft() {
|
---|
183 | int movesLeft = -1;
|
---|
184 |
|
---|
185 | if (queue.isEmpty()) {
|
---|
186 | movesLeft = 500; // to avoid an error
|
---|
187 | } else {
|
---|
188 | Double[] lapsedTimes = queue.toArray();
|
---|
189 | double total = 0;
|
---|
190 | for (int i = 0; i < queueSize; i++) {
|
---|
191 | if (lapsedTimes[i] != null) {
|
---|
192 | total += lapsedTimes[i];
|
---|
193 | }
|
---|
194 | }
|
---|
195 | movesLeft = (int) Math.floor((1.0 - negotiationSession.getTime())
|
---|
196 | / (total / (double) queueSize));
|
---|
197 | }
|
---|
198 | this.movesLeft = movesLeft;
|
---|
199 | return movesLeft;
|
---|
200 | }
|
---|
201 |
|
---|
202 | public void calculateEndPhaseThresholds() {
|
---|
203 | maxThresArray1[0] = possibleBids.get(0).getMyUndiscountedUtil();
|
---|
204 | int size = possibleBids.size();
|
---|
205 | maxThresArray1[3] = possibleBids.get(size - 1).getMyUndiscountedUtil();
|
---|
206 | double range = maxThresArray1[0] - maxThresArray1[3];
|
---|
207 | maxThresArray1[1] = maxThresArray1[0] - ((1.0 / 8) * range);
|
---|
208 | maxThresArray1[2] = maxThresArray1[0] - ((3.0 / 8) * range);
|
---|
209 | }
|
---|
210 |
|
---|
211 | /**
|
---|
212 | * Calculate how many possible bids are within a certain threshold interval.
|
---|
213 | * This is done for all the bins (phases).
|
---|
214 | */
|
---|
215 | public void calculatePropArray() {
|
---|
216 | double max = calculateThreshold(discount); // 0.0001 is just to be sure
|
---|
217 | // :)
|
---|
218 | double min = possibleBids.get(possibleBids.size() - 1)
|
---|
219 | .getMyUndiscountedUtil();
|
---|
220 | double range = max - min;
|
---|
221 | double rangeStep = range / 3;
|
---|
222 |
|
---|
223 | for (int i = 0; i < possibleBids.size(); i++) {
|
---|
224 | double util = possibleBids.get(i).getMyUndiscountedUtil();
|
---|
225 |
|
---|
226 | // calculate if a utility of a bid is within a certain interval.
|
---|
227 | // Intervals should be calculated!!!!
|
---|
228 | if (util >= max - rangeStep && util <= max) {
|
---|
229 | propArray[0]++;
|
---|
230 | } else if (util >= max - 2 * rangeStep && util < max - rangeStep) {
|
---|
231 | propArray[1]++;
|
---|
232 | } else if (util >= max - 3 * rangeStep
|
---|
233 | && util < max - 2 * rangeStep) {
|
---|
234 | propArray[2]++;
|
---|
235 | }
|
---|
236 | }
|
---|
237 | // find the maximum possible utility within a bin (plus the lowest bid
|
---|
238 | // of the last bin)
|
---|
239 | maxThresArray2[0] = max;
|
---|
240 |
|
---|
241 | if (propArray[0] == 0) {
|
---|
242 | maxThresArray2[1] = possibleBids.get(0).getMyUndiscountedUtil();
|
---|
243 | } else {
|
---|
244 | maxThresArray2[1] = possibleBids.get(propArray[0] - 1)
|
---|
245 | .getMyUndiscountedUtil(); // -1 to correct for array offset
|
---|
246 | // of zero
|
---|
247 | }
|
---|
248 |
|
---|
249 | if (propArray[0] == 0 && propArray[1] == 0) {
|
---|
250 | maxThresArray2[2] = possibleBids.get(0).getMyUndiscountedUtil();
|
---|
251 |
|
---|
252 | } else {
|
---|
253 | maxThresArray2[2] = possibleBids.get(
|
---|
254 | propArray[0] + propArray[1] - 1).getMyUndiscountedUtil();
|
---|
255 | }
|
---|
256 | maxThresArray2[3] = min;
|
---|
257 | }
|
---|
258 |
|
---|
259 | public int[] getPropArray() {
|
---|
260 | return propArray;
|
---|
261 | }
|
---|
262 |
|
---|
263 | public int getPhase() {
|
---|
264 | return phase;
|
---|
265 | }
|
---|
266 |
|
---|
267 | public double getThreshold() {
|
---|
268 | return threshold;
|
---|
269 | }
|
---|
270 |
|
---|
271 | public int getMovesLeft() {
|
---|
272 | return movesLeft;
|
---|
273 | }
|
---|
274 |
|
---|
275 | /**
|
---|
276 | * Create all possible bids using a call to the recursive Cartestian product
|
---|
277 | * options generator.
|
---|
278 | */
|
---|
279 | private void createAllBids() {
|
---|
280 | List<Issue> issues = negotiationSession.getUtilitySpace().getDomain()
|
---|
281 | .getIssues();
|
---|
282 |
|
---|
283 | ArrayList<IssueDiscrete> discreteIssues = new ArrayList<IssueDiscrete>();
|
---|
284 |
|
---|
285 | for (Issue issue : issues) {
|
---|
286 | discreteIssues.add((IssueDiscrete) issue);
|
---|
287 | }
|
---|
288 |
|
---|
289 | ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>> result = generateAllBids(
|
---|
290 | discreteIssues, 0);
|
---|
291 |
|
---|
292 | for (ArrayList<Pair<Integer, ValueDiscrete>> bidSet : result) {
|
---|
293 | HashMap<Integer, Value> values = new HashMap<Integer, Value>();
|
---|
294 | for (Pair<Integer, ValueDiscrete> pair : bidSet) {
|
---|
295 | values.put(pair.getFirst(), pair.getSecond());
|
---|
296 | }
|
---|
297 | try {
|
---|
298 | Bid bid = new Bid(negotiationSession.getUtilitySpace()
|
---|
299 | .getDomain(), values);
|
---|
300 | double utility = negotiationSession.getUtilitySpace()
|
---|
301 | .getUtility(bid);
|
---|
302 | possibleBids.add(new BidDetails(bid, utility, -1.0));
|
---|
303 | } catch (Exception e) {
|
---|
304 | e.printStackTrace();
|
---|
305 | }
|
---|
306 | }
|
---|
307 | }
|
---|
308 |
|
---|
309 | /**
|
---|
310 | * The recursive Cartestian product options generator. Generates all
|
---|
311 | * possible bids.
|
---|
312 | *
|
---|
313 | * @param issueList
|
---|
314 | * @param i
|
---|
315 | * , parameter used in the recursion
|
---|
316 | * @return a list of a list with pairs of integer (issue at stake) and a
|
---|
317 | * value (the chosen option)
|
---|
318 | */
|
---|
319 | private ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>> generateAllBids(
|
---|
320 | ArrayList<IssueDiscrete> issueList, int i) {
|
---|
321 |
|
---|
322 | // stop condition
|
---|
323 | if (i == issueList.size()) {
|
---|
324 | // return a list with an empty list
|
---|
325 | ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>> result = new ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>>();
|
---|
326 | result.add(new ArrayList<Pair<Integer, ValueDiscrete>>());
|
---|
327 | return result;
|
---|
328 | }
|
---|
329 |
|
---|
330 | ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>> result = new ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>>();
|
---|
331 | ArrayList<ArrayList<Pair<Integer, ValueDiscrete>>> recursive = generateAllBids(
|
---|
332 | issueList, i + 1); // recursive call
|
---|
333 |
|
---|
334 | // for each element of the first list of input
|
---|
335 | for (int j = 0; j < issueList.get(i).getValues().size(); j++) {
|
---|
336 | // add the element to all combinations obtained for the rest of the
|
---|
337 | // lists
|
---|
338 | for (int k = 0; k < recursive.size(); k++) {
|
---|
339 | // copy a combination from recursive
|
---|
340 | ArrayList<Pair<Integer, ValueDiscrete>> newList = new ArrayList<Pair<Integer, ValueDiscrete>>();
|
---|
341 | for (Pair<Integer, ValueDiscrete> set : recursive.get(k)) {
|
---|
342 | newList.add(set);
|
---|
343 | }
|
---|
344 | // add element of the first list
|
---|
345 | ValueDiscrete value = issueList.get(i).getValues().get(j);
|
---|
346 | int issueNr = issueList.get(i).getNumber();
|
---|
347 | newList.add(new Pair<Integer, ValueDiscrete>(issueNr, value));
|
---|
348 |
|
---|
349 | // add new combination to result
|
---|
350 | result.add(newList);
|
---|
351 | }
|
---|
352 | }
|
---|
353 | return result;
|
---|
354 | }
|
---|
355 | } |
---|