[100] | 1 | from decimal import Decimal, ROUND_HALF_UP
|
---|
| 2 | from typing import cast, Optional
|
---|
| 3 |
|
---|
| 4 | from geniusweb.issuevalue.NumberValue import NumberValue
|
---|
| 5 | from geniusweb.issuevalue.NumberValueSet import NumberValueSet
|
---|
| 6 | from geniusweb.issuevalue.Value import Value
|
---|
| 7 | from geniusweb.issuevalue.ValueSet import ValueSet
|
---|
| 8 | from geniusweb.profile.utilityspace.ValueSetUtilities import ValueSetUtilities
|
---|
| 9 |
|
---|
| 10 |
|
---|
| 11 | class NumberValueSetUtilities (ValueSetUtilities ):
|
---|
| 12 | '''
|
---|
| 13 | The low and high values (from a {@link NumberValueSet} are given each a
|
---|
| 14 | different utility. This linearly interpolates in-between utility values.
|
---|
| 15 | '''
|
---|
| 16 |
|
---|
| 17 |
|
---|
| 18 | def __init__(self, lowValue:Decimal,\
|
---|
| 19 | lowUtility:Decimal, highValue: Decimal, highUtility:Decimal) :
|
---|
| 20 | '''
|
---|
| 21 | @param lowValue the low value of the {@link Range}
|
---|
| 22 | @param lowUtility the utility of the {@link #lowValue}
|
---|
| 23 | @param highValue the high value of the {@link Range}. Must be
|
---|
| 24 | >lowValue.
|
---|
| 25 | @param highUtility the utility of the {@link #highValue}
|
---|
| 26 | '''
|
---|
| 27 | if lowValue == None or highValue == None or lowUtility == None \
|
---|
| 28 | or highUtility == None :
|
---|
| 29 | raise ValueError(\
|
---|
| 30 | "arguments lowValue, lowUtility, highValue and highUtility must be non-null");
|
---|
| 31 |
|
---|
| 32 | if not self._isInZeroOne(lowUtility):
|
---|
| 33 | raise ValueError("lowUtility must be in [0,1]")
|
---|
| 34 |
|
---|
| 35 | if not self._isInZeroOne(highUtility):
|
---|
| 36 | raise ValueError("highUtility must be in [0,1]")
|
---|
| 37 |
|
---|
| 38 | if highValue<=lowValue:
|
---|
| 39 | raise ValueError("highValue must be > lowValue")
|
---|
| 40 |
|
---|
| 41 | self._lowValue = lowValue
|
---|
| 42 | self._highValue = highValue
|
---|
| 43 | self._lowUtility = lowUtility
|
---|
| 44 | self._highUtility = highUtility
|
---|
| 45 |
|
---|
| 46 | #Override
|
---|
| 47 | def getUtility(self, value:Optional[Value] ) -> Decimal :
|
---|
| 48 | if not isinstance(value, NumberValue):
|
---|
| 49 | return Decimal("0");
|
---|
| 50 |
|
---|
| 51 | x:Decimal = value.getValue()
|
---|
| 52 | if x<self._lowValue or x>self._highValue:
|
---|
| 53 | return Decimal("0")
|
---|
| 54 | # we need to be careful to avoid round errors from divides.
|
---|
| 55 | # so we return lowU + deltaU * (x-lowV) /deltaV
|
---|
| 56 | deltaU:Decimal = self._highUtility- self._lowUtility
|
---|
| 57 | deltaV:Decimal = self._highValue-self._lowValue
|
---|
| 58 |
|
---|
| 59 | return (self._lowUtility + deltaU *(x-self._lowValue)/deltaV )\
|
---|
| 60 | .quantize(Decimal('1.00000000'), rounding=ROUND_HALF_UP)
|
---|
| 61 |
|
---|
| 62 |
|
---|
| 63 | #Override
|
---|
| 64 | def isFitting(self, valueset: ValueSet ) -> Optional[str]:
|
---|
| 65 | if not isinstance(valueset, NumberValueSet):
|
---|
| 66 | return "The utilities are for a number valueset but the given values are "\
|
---|
| 67 | + str(valueset);
|
---|
| 68 |
|
---|
| 69 | numvalset= cast(NumberValueSet, valueset)
|
---|
| 70 | if numvalset.getRange().getLow() != self._lowValue:
|
---|
| 71 | return "the utilities are specified down to " + str(self._lowValue) \
|
---|
| 72 | + " but the valueset starts at " \
|
---|
| 73 | + str(numvalset.getRange().getLow())
|
---|
| 74 |
|
---|
| 75 | if numvalset.getRange().getHigh() != self._highValue:
|
---|
| 76 | return "the utilities are specified up to " + str(self._highValue) \
|
---|
| 77 | + " but the valueset ends at " \
|
---|
| 78 | + str(numvalset.getRange().getHigh())
|
---|
| 79 | return None
|
---|
| 80 |
|
---|
| 81 | def getLowValue(self) -> Decimal :
|
---|
| 82 | '''
|
---|
| 83 | @return the lowest value
|
---|
| 84 | '''
|
---|
| 85 | return self._lowValue;
|
---|
| 86 |
|
---|
| 87 |
|
---|
| 88 | def getHighValue(self) ->Decimal :
|
---|
| 89 | '''
|
---|
| 90 | @return the highest value
|
---|
| 91 | '''
|
---|
| 92 | return self._highValue
|
---|
| 93 |
|
---|
| 94 | def getLowUtility(self) ->Decimal :
|
---|
| 95 | '''
|
---|
| 96 | @return the utility of the lowest value
|
---|
| 97 | '''
|
---|
| 98 | return self._lowUtility;
|
---|
| 99 |
|
---|
| 100 | def getHighUtility(self) ->Decimal:
|
---|
| 101 | '''
|
---|
| 102 | @return the utility of the highest value
|
---|
| 103 | '''
|
---|
| 104 | return self._highUtility
|
---|
| 105 |
|
---|
| 106 | def __repr__(self):
|
---|
| 107 | return "NumberValueSetUtilities[" + str(self._lowValue) + "->" + str(self._lowUtility) + "," \
|
---|
| 108 | + str(self._highValue) + "->" + str(self._highUtility) + "]"
|
---|
| 109 |
|
---|
| 110 | def __hash__(self):
|
---|
| 111 | return hash((self._lowUtility, self._lowValue,self._highUtility, self._highValue))
|
---|
| 112 |
|
---|
| 113 | def __eq__(self, other):
|
---|
| 114 | return isinstance(other, self.__class__) \
|
---|
| 115 | and self._highUtility == other._highUtility \
|
---|
| 116 | and self._highValue == other._highValue \
|
---|
| 117 | and self._lowUtility == other._lowUtility \
|
---|
| 118 | and self._lowValue == other._lowValue
|
---|
| 119 |
|
---|
| 120 | def _isInZeroOne(self , value:Decimal) -> bool:
|
---|
| 121 | '''
|
---|
| 122 | Check if value is in range [0,1]
|
---|
| 123 |
|
---|
| 124 | @param value
|
---|
| 125 | @return true if in range.
|
---|
| 126 | '''
|
---|
| 127 | return value >= Decimal("0") and value <= Decimal("1")
|
---|
| 128 |
|
---|