[81] | 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
|
---|