[74] | 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)
|
---|