1 | package negotiator.boaframework.opponentmodel.agentx;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.List;
|
---|
5 |
|
---|
6 | import genius.core.Bid;
|
---|
7 | import genius.core.Domain;
|
---|
8 | import genius.core.issue.Issue;
|
---|
9 | import genius.core.issue.IssueDiscrete;
|
---|
10 | import genius.core.issue.ValueDiscrete;
|
---|
11 | import genius.core.utility.AdditiveUtilitySpace;
|
---|
12 |
|
---|
13 | /**
|
---|
14 | * Class for processing discrete issues, weighting them based on opponent bids.
|
---|
15 | * Also contains functionality for opponent stubbornness.
|
---|
16 | *
|
---|
17 | * @author E. Jacobs
|
---|
18 | *
|
---|
19 | */
|
---|
20 | public class DiscreteIssueProcessor {
|
---|
21 |
|
---|
22 | private ArrayList<IssueDiscrete> issueList = new ArrayList<IssueDiscrete>();
|
---|
23 | private ArrayList<Double> weightList = new ArrayList<Double>();
|
---|
24 | private ArrayList<Double> changeList = new ArrayList<Double>();
|
---|
25 | private ArrayList<Bid> pastBidList = new ArrayList<Bid>();
|
---|
26 |
|
---|
27 | private Bid previousBid;
|
---|
28 |
|
---|
29 | private int nIssues;
|
---|
30 | private int nBidRepeats = 0;
|
---|
31 | private long nPossibleBids;
|
---|
32 | private int nMaxBidMemory = 5;
|
---|
33 | private int nBidsInMemory = 0;
|
---|
34 | private int nBidsProcessed = 0;
|
---|
35 | private int nRepeatLimitOverrides;
|
---|
36 | private int nRepeatLimit;
|
---|
37 | private int averageBidsPerSecond = 100;
|
---|
38 | private int totalTime = 180;
|
---|
39 |
|
---|
40 | private double nBidsPerSecond;
|
---|
41 |
|
---|
42 | private double stubbornness = 0;
|
---|
43 | private double corr; // weighting correction factor
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * Creates a DiscreteIssueProcessor for a domain. Use this for opponent
|
---|
47 | * modeling
|
---|
48 | *
|
---|
49 | * @param d
|
---|
50 | * The domain
|
---|
51 | */
|
---|
52 | public DiscreteIssueProcessor(Domain d) {
|
---|
53 |
|
---|
54 | List<Issue> issues = d.getIssues();
|
---|
55 |
|
---|
56 | for (Issue i : issues) {
|
---|
57 | issueList.add((IssueDiscrete) i);
|
---|
58 | }
|
---|
59 |
|
---|
60 | nIssues = issueList.size();
|
---|
61 |
|
---|
62 | for (int i = 0; i < nIssues; i++) {
|
---|
63 | weightList.add(1.0 / nIssues);
|
---|
64 | changeList.add(1.0);
|
---|
65 | }
|
---|
66 |
|
---|
67 | nPossibleBids = d.getNumberOfPossibleBids();
|
---|
68 | nRepeatLimit = (totalTime * averageBidsPerSecond) / (int) nPossibleBids;
|
---|
69 | }
|
---|
70 |
|
---|
71 | /**
|
---|
72 | * Creates a DiscreteIssueProcessor for a domain, given a certain utility.
|
---|
73 | * Use this for modeling your own agent
|
---|
74 | *
|
---|
75 | * @param u
|
---|
76 | * @param d
|
---|
77 | */
|
---|
78 | public DiscreteIssueProcessor(AdditiveUtilitySpace u, Domain d) {
|
---|
79 |
|
---|
80 | List<Issue> issues = d.getIssues();
|
---|
81 |
|
---|
82 | for (Issue i : issues) {
|
---|
83 | issueList.add((IssueDiscrete) i);
|
---|
84 | weightList.add(u.getWeight(i.getNumber()));
|
---|
85 | changeList.add(1.0);
|
---|
86 | }
|
---|
87 |
|
---|
88 | nIssues = issueList.size();
|
---|
89 | }
|
---|
90 |
|
---|
91 | public ArrayList<IssueDiscrete> getIssueList() {
|
---|
92 | return issueList;
|
---|
93 | }
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * Adapts the different issue weights according to the current bid the
|
---|
97 | * opponent has made. Issues with more changes get a lower weight
|
---|
98 | *
|
---|
99 | * @param currentBid
|
---|
100 | * Bid done by the opponent
|
---|
101 | * @param time
|
---|
102 | * Time at which the bid was done
|
---|
103 | */
|
---|
104 | public void adaptWeightsByBid(Bid currentBid, double time) {
|
---|
105 |
|
---|
106 | processBid(currentBid);
|
---|
107 |
|
---|
108 | // correction factor for weight adaptation is
|
---|
109 | // number of bids per second
|
---|
110 | if (time == 0) {
|
---|
111 | nBidsPerSecond = 0;
|
---|
112 | } else {
|
---|
113 | nBidsPerSecond = (double) nBidsProcessed
|
---|
114 | / (time * (double) totalTime);
|
---|
115 | }
|
---|
116 |
|
---|
117 | corr = 1 / nBidsPerSecond;
|
---|
118 |
|
---|
119 | if (time > 0.05) {
|
---|
120 | // more stubborn gets even less added change
|
---|
121 | corr = corr * (1 - stubbornness);
|
---|
122 | }
|
---|
123 |
|
---|
124 | if (previousBid != null) {
|
---|
125 |
|
---|
126 | IssueDiscrete issue;
|
---|
127 |
|
---|
128 | for (int i = 0; i < nIssues; i++) {
|
---|
129 |
|
---|
130 | issue = issueList.get(i);
|
---|
131 |
|
---|
132 | ValueDiscrete oldValue = null;
|
---|
133 | ValueDiscrete newValue = null;
|
---|
134 |
|
---|
135 | try {
|
---|
136 | oldValue = (ValueDiscrete) previousBid.getValue(issue
|
---|
137 | .getNumber());
|
---|
138 | newValue = (ValueDiscrete) currentBid.getValue(issue
|
---|
139 | .getNumber());
|
---|
140 | } catch (Exception e) {
|
---|
141 | }
|
---|
142 |
|
---|
143 | if (!oldValue.equals(newValue)) {
|
---|
144 | changeList.set(i,
|
---|
145 | changeList.get(i) + corr / (changeList.get(i)));
|
---|
146 | }
|
---|
147 | }
|
---|
148 | }
|
---|
149 |
|
---|
150 | adaptWeightsByChangeList();
|
---|
151 |
|
---|
152 | previousBid = currentBid;
|
---|
153 | }
|
---|
154 |
|
---|
155 | /**
|
---|
156 | * Gives a descending list of the issues ordered by weight, so the highest
|
---|
157 | * weighted issue is first in the list
|
---|
158 | *
|
---|
159 | * @return A list of discrete issues, ordered by weight
|
---|
160 | */
|
---|
161 | public ArrayList<IssueDiscrete> getOrderedIssueList() {
|
---|
162 |
|
---|
163 | ArrayList<IssueDiscrete> orderedIssueList = new ArrayList<IssueDiscrete>();
|
---|
164 | ArrayList<IssueDiscrete> otherIssueList = issueList;
|
---|
165 | ArrayList<Double> otherWeightList = weightList;
|
---|
166 |
|
---|
167 | int maxIndex;
|
---|
168 |
|
---|
169 | while (otherWeightList.size() > 1) {
|
---|
170 |
|
---|
171 | maxIndex = getIndexHighestWeight(otherWeightList);
|
---|
172 |
|
---|
173 | orderedIssueList.add(otherIssueList.get(maxIndex));
|
---|
174 |
|
---|
175 | otherIssueList.remove(maxIndex);
|
---|
176 | otherWeightList.remove(maxIndex);
|
---|
177 |
|
---|
178 | }
|
---|
179 |
|
---|
180 | orderedIssueList.add(otherIssueList.get(0));
|
---|
181 |
|
---|
182 | return orderedIssueList;
|
---|
183 | }
|
---|
184 |
|
---|
185 | /**
|
---|
186 | * Gives the weight that belongs to the given issue
|
---|
187 | *
|
---|
188 | * @param i
|
---|
189 | * Issue for which the weight is required
|
---|
190 | * @return The weight for the given issue
|
---|
191 | */
|
---|
192 | public double getWeightByIssue(IssueDiscrete i) {
|
---|
193 |
|
---|
194 | return weightList.get(issueList.indexOf(i));
|
---|
195 | }
|
---|
196 |
|
---|
197 | /**
|
---|
198 | * Gives the stubbornness of the opponent; closer to 1 is more stubborn.
|
---|
199 | *
|
---|
200 | * @return
|
---|
201 | */
|
---|
202 | public double getStubbornness() {
|
---|
203 | return stubbornness;
|
---|
204 | }
|
---|
205 |
|
---|
206 | /**
|
---|
207 | * Processes each bid to give an indication of the stubbornness of the
|
---|
208 | * opponent
|
---|
209 | *
|
---|
210 | * @param bid
|
---|
211 | */
|
---|
212 | private void processBid(Bid bid) {
|
---|
213 |
|
---|
214 | nBidsProcessed++;
|
---|
215 |
|
---|
216 | // find the number of bid repeats and change in the list
|
---|
217 | // for a new bid, add to list if there is space in the list
|
---|
218 | // else decrease the number of repeats
|
---|
219 | if (pastBidList.contains(bid)) {
|
---|
220 | nBidRepeats++;
|
---|
221 | } else if (nBidsInMemory < nMaxBidMemory) {
|
---|
222 | pastBidList.add(bid);
|
---|
223 | nBidsInMemory++;
|
---|
224 | } else {
|
---|
225 | nBidRepeats--;
|
---|
226 | }
|
---|
227 |
|
---|
228 | // check if the repeat limit is crossed, or comes under 0
|
---|
229 | // in both cases, reset repeats and allow more different bids to be
|
---|
230 | // stored
|
---|
231 | // adapt the number of limit overrides accordingly
|
---|
232 | if (nBidRepeats > nRepeatLimit) {
|
---|
233 |
|
---|
234 | nBidRepeats = 0;
|
---|
235 | nMaxBidMemory++;
|
---|
236 | nRepeatLimitOverrides++;
|
---|
237 |
|
---|
238 | } else if (nBidRepeats < 0) {
|
---|
239 |
|
---|
240 | nBidRepeats = 0;
|
---|
241 | nMaxBidMemory++;
|
---|
242 |
|
---|
243 | if (nRepeatLimitOverrides > 1) {
|
---|
244 | nRepeatLimitOverrides--;
|
---|
245 | }
|
---|
246 | }
|
---|
247 |
|
---|
248 | // if more then 20 bids are allowed to be stored, reset to 5 bids
|
---|
249 | // storage
|
---|
250 | // and add the last 5 bids
|
---|
251 | // if there are 5 bids or less, just keep the list.
|
---|
252 | if (nMaxBidMemory > 20) {
|
---|
253 |
|
---|
254 | ArrayList<Bid> tempBidList = new ArrayList<Bid>();
|
---|
255 |
|
---|
256 | if (nBidsInMemory >= 5) {
|
---|
257 | for (int i = nBidsInMemory - 5; i < nBidsInMemory; i++) {
|
---|
258 | tempBidList.add(pastBidList.get(i));
|
---|
259 | }
|
---|
260 |
|
---|
261 | pastBidList.clear();
|
---|
262 | pastBidList = tempBidList;
|
---|
263 |
|
---|
264 | nBidsInMemory = 5;
|
---|
265 | }
|
---|
266 |
|
---|
267 | nMaxBidMemory = 10;
|
---|
268 |
|
---|
269 | }
|
---|
270 |
|
---|
271 | if (nBidsProcessed == 0) {
|
---|
272 | stubbornness = 0;
|
---|
273 | } else {
|
---|
274 | stubbornness = nRepeatLimitOverrides
|
---|
275 | / ((double) nBidsProcessed / (double) nRepeatLimit);
|
---|
276 | }
|
---|
277 |
|
---|
278 | // System.out.println("Updated stubbornness - new value is " +
|
---|
279 | // stubbornness + ", total changes:" + changes);
|
---|
280 |
|
---|
281 | }
|
---|
282 |
|
---|
283 | /**
|
---|
284 | * Returns the index of the highest weight from some weightList
|
---|
285 | *
|
---|
286 | * @param wList
|
---|
287 | * The weightList
|
---|
288 | * @return The index of the highest weight.
|
---|
289 | */
|
---|
290 | private int getIndexHighestWeight(ArrayList<Double> wList) {
|
---|
291 |
|
---|
292 | int maxIndex = 0;
|
---|
293 | int nWeights = wList.size();
|
---|
294 |
|
---|
295 | for (int i = 1; i < nWeights; i++) {
|
---|
296 | if (wList.get(i) > wList.get(maxIndex)) {
|
---|
297 | maxIndex = i;
|
---|
298 | }
|
---|
299 | }
|
---|
300 |
|
---|
301 | return maxIndex;
|
---|
302 | }
|
---|
303 |
|
---|
304 | /**
|
---|
305 | * Sets all the weights based on the list of changes
|
---|
306 | */
|
---|
307 | private void adaptWeightsByChangeList() {
|
---|
308 |
|
---|
309 | double inverseChangeTotal = 0;
|
---|
310 | double normalizedWeight = 0;
|
---|
311 |
|
---|
312 | for (int i = 0; i < nIssues; i++) {
|
---|
313 | inverseChangeTotal += 1 / changeList.get(i);
|
---|
314 | }
|
---|
315 |
|
---|
316 | for (int i = 0; i < nIssues; i++) {
|
---|
317 |
|
---|
318 | normalizedWeight = (1 / changeList.get(i)) / inverseChangeTotal;
|
---|
319 |
|
---|
320 | weightList.set(i, normalizedWeight);
|
---|
321 | }
|
---|
322 | }
|
---|
323 |
|
---|
324 | @Override
|
---|
325 | public String toString() {
|
---|
326 |
|
---|
327 | String str = "";
|
---|
328 |
|
---|
329 | for (int i = 0; i < nIssues; i++) {
|
---|
330 | str += "Issue: " + issueList.get(i).getName() + ", changes: "
|
---|
331 | + changeList.get(i) + ", weight: " + weightList.get(i)
|
---|
332 | + "\n";
|
---|
333 | }
|
---|
334 |
|
---|
335 | return str;
|
---|
336 |
|
---|
337 | }
|
---|
338 |
|
---|
339 | }
|
---|