1 | import decimal
|
---|
2 |
|
---|
3 | import numpy as np
|
---|
4 |
|
---|
5 |
|
---|
6 | class AcceptanceStrategy:
|
---|
7 | """
|
---|
8 | Contains acceptance strategies that agents can use. According to "Acceptance Conditions in Automated Negotiation"
|
---|
9 | (https://homepages.cwi.nl/~baarslag/pub/Acceptance_conditions_in_automated_negotiation.pdf) the following are the
|
---|
10 | best strategies ranked according the average utility for the agent:
|
---|
11 | 1. combi_max_w
|
---|
12 | 2. combi_avg_w
|
---|
13 | 3. gap(0.1)
|
---|
14 | 4. combi_max_t
|
---|
15 | 5. time(0.99)
|
---|
16 | 6. gap(0.2)
|
---|
17 | 7. gap(0.05)
|
---|
18 | 8. next(1.02, 0)
|
---|
19 | 9. gap(0.02)
|
---|
20 | 10. prev(1.02, 0)
|
---|
21 | 11. next(1, 0)
|
---|
22 | 12. prev(1, 0)
|
---|
23 | """
|
---|
24 |
|
---|
25 | def __init__(self, progress, profile, rec_bid_hist=None, next_sent_bid=None, prev_sent_bid=None):
|
---|
26 | """
|
---|
27 | Constructs an acceptance strategy object.
|
---|
28 | @param progress: the current negotiation progress from 0 to 1 (essentially time).
|
---|
29 | @param rec_bid_hist: the history of all the opponent's bids so far.
|
---|
30 | """
|
---|
31 | self.progress = progress
|
---|
32 | self.profile = profile
|
---|
33 | if rec_bid_hist and len(rec_bid_hist) == 0:
|
---|
34 | Exception(f"Expected history of at least 1 bid but got 0")
|
---|
35 | elif rec_bid_hist:
|
---|
36 | self.rec_utility_hist = list(map(lambda bid: self.profile.getUtility(bid), rec_bid_hist))
|
---|
37 | self.rec_bid_hist = rec_bid_hist
|
---|
38 | self.last_rec_bid = rec_bid_hist[-1]
|
---|
39 | self.next_sent_bid = next_sent_bid
|
---|
40 | self.prev_sent_bid = prev_sent_bid
|
---|
41 |
|
---|
42 | def combi_max_w(self, progress_thresh, scale, const):
|
---|
43 | """Combined strategy that checks a window of previously received bids"""
|
---|
44 | window = self._get_bid_window()
|
---|
45 | if len(window) != 0:
|
---|
46 | # Take the maximum of the bid window
|
---|
47 | alpha = np.max(window)
|
---|
48 | else:
|
---|
49 | alpha = 0
|
---|
50 | return self._combi(progress_thresh, alpha, scale, const)
|
---|
51 |
|
---|
52 | def combi_avg_w(self, progress_thresh, scale, const):
|
---|
53 | """Combined strategy that checks a window of previously received bids"""
|
---|
54 | window = self._get_bid_window()
|
---|
55 | if len(window) != 0:
|
---|
56 | alpha = np.mean(window)
|
---|
57 | else:
|
---|
58 | alpha = 0
|
---|
59 | return self._combi(progress_thresh, alpha, scale, const)
|
---|
60 |
|
---|
61 | def combi_max_t(self, progress_thresh, scale, const):
|
---|
62 | """Combined strategy that checks all previously received bids"""
|
---|
63 | alpha = np.max(self.rec_utility_hist)
|
---|
64 | return self._combi(progress_thresh, alpha, scale, const)
|
---|
65 |
|
---|
66 | def _get_bid_window(self):
|
---|
67 | if self.progress == 0:
|
---|
68 | self.max_bids = 100
|
---|
69 | else:
|
---|
70 | # automatically figure out how many max bids there will be
|
---|
71 | self.max_bids = len(self.rec_utility_hist) // self.progress
|
---|
72 | num_bids = int(self.max_bids * self.progress)
|
---|
73 | remaining_bids = int(self.max_bids * (1 - self.progress))
|
---|
74 | # compute bounds of the bid window
|
---|
75 | bounds = np.clip([num_bids - remaining_bids, num_bids], 0, len(self.rec_utility_hist) - 1)
|
---|
76 | # print(bounds)
|
---|
77 | if bounds[1] < bounds[0]:
|
---|
78 | raise Exception("Invalid bounds")
|
---|
79 | return self.rec_utility_hist[bounds[0]: bounds[1]]
|
---|
80 |
|
---|
81 | def _combi(self, progress_thresh, alpha, scale, const):
|
---|
82 | """Helper method for the combi acceptance strategy. According to research most effective with T = 0.99."""
|
---|
83 | early = self.next(scale, const)
|
---|
84 | late = self.time(progress_thresh) and self.profile.getUtility(self.last_rec_bid) >= alpha
|
---|
85 | # print(early, late)
|
---|
86 | return early or late
|
---|
87 |
|
---|
88 | def gap(self, gap):
|
---|
89 | return self.prev(1, gap)
|
---|
90 |
|
---|
91 | def time(self, progress_thresh):
|
---|
92 | """Accepts bids after a certain time period"""
|
---|
93 | return self.progress >= progress_thresh
|
---|
94 |
|
---|
95 | def next(self, scale_factor, utility_gap):
|
---|
96 | """Accepts bids better the next bids that should be sent"""
|
---|
97 | # print(f"|| Next sent for agent {hash(self.profile)}: {self.profile.getUtility(self.next_sent_bid)}")
|
---|
98 | return decimal.Decimal(scale_factor) * self.profile.getUtility(self.last_rec_bid) \
|
---|
99 | + decimal.Decimal(utility_gap) >= self.profile.getUtility(self.next_sent_bid)
|
---|
100 |
|
---|
101 | def prev(self, scale_factor, utility_gap):
|
---|
102 | """Accepts bids better than the previous bid that has been sent"""
|
---|
103 | return decimal.Decimal(scale_factor) * self.profile.getUtility(self.last_rec_bid) \
|
---|
104 | + decimal.Decimal(utility_gap) >= self.profile.getUtility(self.prev_sent_bid)
|
---|
105 |
|
---|
106 | def const(self, utility_thresh):
|
---|
107 | """Accepts bids over a utility threshold"""
|
---|
108 | return self.profile.getUtility(self.last_rec_bid) > utility_thresh
|
---|
109 |
|
---|
110 | def IAMHaggler(self):
|
---|
111 | return self.const(0.88) and self.next(1.02, 0) and self.prev(1.02, 0)
|
---|