1 | from decimal import Decimal
|
---|
2 | from geniusweb.issuevalue.ValueSet import ValueSet
|
---|
3 | from geniusweb.profile.utilityspace.ValueSetUtilities import ValueSetUtilities
|
---|
4 | from geniusweb.bidspace.Interval import Interval
|
---|
5 | from geniusweb.issuevalue.Value import Value
|
---|
6 | from typing import List, Dict, Optional
|
---|
7 |
|
---|
8 | class IssueInfo:
|
---|
9 | '''
|
---|
10 | Tool class to collect all relevant info about one issue (from LinearAdditive)
|
---|
11 | in one class. Used for internally grouping data for more efficient
|
---|
12 | processing. This class may change in the future, not recommended for direct
|
---|
13 | use.
|
---|
14 | <p>
|
---|
15 | immutable
|
---|
16 | '''
|
---|
17 |
|
---|
18 | def __init__(self, name:str, values:ValueSet , utils:ValueSetUtilities,
|
---|
19 | weight:Decimal , precision:int ) :
|
---|
20 | '''
|
---|
21 | @param name the issue name
|
---|
22 | @param values the {@link ValueSet} of the issue
|
---|
23 | @param utils the {@link ValueSetUtilities} of the issue profile
|
---|
24 | @param weight the weight of the {@link ValueSetUtilities}
|
---|
25 | @param precision the precision to compute with. Basically the number of
|
---|
26 | decimal places used for the computations.
|
---|
27 | '''
|
---|
28 | self._name = name;
|
---|
29 | self._values = values;
|
---|
30 | self._weightedUtils = self._computeWeightedUtils(utils, weight, precision);
|
---|
31 | self._interval = self._getRange();
|
---|
32 |
|
---|
33 | def getValues(self) ->ValueSet :
|
---|
34 | return self._values
|
---|
35 |
|
---|
36 | def getName(self)->str:
|
---|
37 | return self._name;
|
---|
38 |
|
---|
39 | def getInterval(self)->Interval:
|
---|
40 | '''
|
---|
41 | @return weighted minimum and maximum utility achievable with this issue,
|
---|
42 | rounded to the requested precision.
|
---|
43 | '''
|
---|
44 | return self._interval;
|
---|
45 |
|
---|
46 | def getExtreme(self, isMax:bool)->Value :
|
---|
47 | '''
|
---|
48 | @param isMax if true the max {@link Value} is returned, else the min is
|
---|
49 | returned.
|
---|
50 | @return the extreme value, either the minimum if isMax=false or maximum
|
---|
51 | if isMax=true
|
---|
52 | '''
|
---|
53 | extremeutil:Decimal = None #type:ignore
|
---|
54 | extremeval:Decimal = None #type:ignore
|
---|
55 | for val in self._values:
|
---|
56 | util = self._weightedUtils.get(val)
|
---|
57 | if extremeval == None:
|
---|
58 | extremeutil = self._weightedUtils.get(val) #type:ignore
|
---|
59 | extremeval = val
|
---|
60 | else:
|
---|
61 | if isMax:
|
---|
62 | if util > extremeutil: #type:ignore
|
---|
63 | extremeutil = util #type:ignore
|
---|
64 | extremeval = val
|
---|
65 | else:
|
---|
66 | if util<extremeutil: #type:ignore
|
---|
67 | extremeutil = util #type:ignore
|
---|
68 | extremeval = val
|
---|
69 | return extremeval #type:ignore
|
---|
70 |
|
---|
71 | def getWeightedUtil(self, val:Value ) -> Decimal :
|
---|
72 | '''
|
---|
73 | @param val the issue value to be evaluated
|
---|
74 | @return weighted utility of given value, rounded to nearest value with
|
---|
75 | the requested precision number of digits.
|
---|
76 | '''
|
---|
77 | return self._weightedUtils.get(val) #type:ignore
|
---|
78 |
|
---|
79 | def _subset(self, interval:Interval) -> List[Value]:
|
---|
80 | '''
|
---|
81 | @param interval an {@link Interval} of utility values.
|
---|
82 | @return all values that are inside the interval.
|
---|
83 | '''
|
---|
84 |
|
---|
85 | selection:List[Value] = []
|
---|
86 | for value in self._values:
|
---|
87 | if interval.contains(self.getWeightedUtil(value)):
|
---|
88 | selection.append(value);
|
---|
89 | return selection
|
---|
90 |
|
---|
91 | def _subsetSize( self, interval:Interval) ->int:
|
---|
92 | '''
|
---|
93 | Faster way to determine subset size, it does not create a list
|
---|
94 |
|
---|
95 | @param interval an {@link Interval} of utility values.
|
---|
96 | @return size of the subset that you will get from calling subset
|
---|
97 | '''
|
---|
98 | n = 0
|
---|
99 | for value in self._values:
|
---|
100 | if interval.contains(self.getWeightedUtil(value)):
|
---|
101 | n=n+1
|
---|
102 | return n
|
---|
103 |
|
---|
104 | def _getRange(self)->Interval :
|
---|
105 | '''
|
---|
106 | @return the {@link Interval} (minimum and maximum) of the utility of the
|
---|
107 | weighted utility of this issue, properly rounded to the
|
---|
108 | {@link #precision}/
|
---|
109 |
|
---|
110 | '''
|
---|
111 | min = Decimal(1)
|
---|
112 | max = Decimal(0)
|
---|
113 | for value in self._values:
|
---|
114 | util = self.getWeightedUtil(value)
|
---|
115 | if util < min:
|
---|
116 | min = util
|
---|
117 | if util > max:
|
---|
118 | max = util
|
---|
119 | return Interval(min, max)
|
---|
120 |
|
---|
121 | def _computeWeightedUtils(self, utilities:ValueSetUtilities, w:Decimal, prec:int) \
|
---|
122 | -> Dict[Value, Decimal] :
|
---|
123 | map:Dict[Value, Decimal] = {}
|
---|
124 |
|
---|
125 | for val in self._values:
|
---|
126 | map[val] = round(utilities.getUtility(val)* w, prec)
|
---|
127 | return map
|
---|