source: ANL2022/dreamteam109_agent/utils/opponent_model.py

Last change on this file was 75, checked in by wouter, 21 months ago

#6 added ANAC2022 parties

File size: 4.5 KB
Line 
1from collections import defaultdict
2import logging
3
4from geniusweb.issuevalue.Bid import Bid
5from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
6from geniusweb.issuevalue.Domain import Domain
7from geniusweb.issuevalue.Value import Value
8
9from .logger import Logger
10
11from .utils import bid_to_string
12
13class OpponentModel:
14 def __init__(self, domain: Domain, logger: Logger):
15 self.offers = []
16 self.domain = domain
17 self.logger = logger
18
19 self.issue_estimators = {
20 i: IssueEstimator(v) for i, v in domain.getIssuesValues().items()
21 }
22
23 def update(self, bid: Bid):
24 self.logger.log(logging.INFO, "updating opponent model with received bid = " + bid_to_string(bid))
25 # keep track of all bids received
26 self.offers.append(bid)
27
28 # update all issue estimators with the value that is offered for that issue
29 for issue_id, issue_estimator in self.issue_estimators.items():
30 issue_estimator.update(bid.getValue(issue_id))
31
32 def get_predicted_utility(self, bid: Bid):
33 if len(self.offers) == 0 or bid is None:
34 return 0
35
36 # initiate
37 total_issue_weight = 0.0
38 value_utilities = []
39 issue_weights = []
40
41 for issue_id, issue_estimator in self.issue_estimators.items():
42 # get the value that is set for this issue in the bid
43 value: Value = bid.getValue(issue_id)
44
45 # collect both the predicted weight for the issue and
46 # predicted utility of the value within this issue
47 value_utilities.append(issue_estimator.get_value_utility(value))
48 issue_weights.append(issue_estimator.weight)
49
50 total_issue_weight += issue_estimator.weight
51
52 # normalise the issue weights such that the sum is 1.0
53 if total_issue_weight == 0.0:
54 issue_weights = [1 / len(issue_weights) for _ in issue_weights]
55 else:
56 issue_weights = [iw / total_issue_weight for iw in issue_weights]
57
58 # calculate predicted utility by multiplying all value utilities with their issue weight
59 predicted_utility = sum(
60 [iw * vu for iw, vu in zip(issue_weights, value_utilities)]
61 )
62
63 return predicted_utility
64
65
66class IssueEstimator:
67 def __init__(self, value_set: DiscreteValueSet):
68 if not isinstance(value_set, DiscreteValueSet):
69 raise TypeError(
70 "This issue estimator only supports issues with discrete values"
71 )
72
73 self.bids_received = 0
74 self.max_value_count = 0
75 self.num_values = value_set.size()
76 self.value_trackers = defaultdict(ValueEstimator)
77 self.weight = 0
78
79 def update(self, value: Value):
80 self.bids_received += 1
81
82 # get the value tracker of the value that is offered
83 value_tracker = self.value_trackers[value]
84
85 # register that this value was offered
86 value_tracker.update()
87
88 # update the count of the most common offered value
89 self.max_value_count = max([value_tracker.count, self.max_value_count])
90
91 # update predicted issue weight
92 # the intuition here is that if the values of the receiverd offers spread out over all
93 # possible values, then this issue is likely not important to the opponent (weight == 0.0).
94 # If all received offers proposed the same value for this issue,
95 # then the predicted issue weight == 1.0
96 equal_shares = self.bids_received / self.num_values
97 self.weight = (self.max_value_count - equal_shares) / (
98 self.bids_received - equal_shares
99 )
100
101 # recalculate all value utilities
102 for value_tracker in self.value_trackers.values():
103 value_tracker.recalculate_utility(self.max_value_count, self.weight)
104
105 def get_value_utility(self, value: Value):
106 if value in self.value_trackers:
107 return self.value_trackers[value].utility
108
109 return 0
110
111
112class ValueEstimator:
113 def __init__(self):
114 self.count = 0
115 self.utility = 0
116
117 def update(self):
118 self.count += 1
119
120 def recalculate_utility(self, max_value_count: int, weight: float):
121 if weight < 1:
122 mod_value_count = ((self.count + 1) ** (1 - weight)) - 1
123 mod_max_value_count = ((max_value_count + 1) ** (1 - weight)) - 1
124
125 self.utility = mod_value_count / mod_max_value_count
126 else:
127 self.utility = 1
Note: See TracBrowser for help on using the repository browser.