[81] | 1 | from collections import OrderedDict
|
---|
| 2 | from decimal import Decimal
|
---|
| 3 | import json
|
---|
| 4 | from pathlib import Path
|
---|
| 5 | from typing import List, Dict
|
---|
| 6 | import unittest
|
---|
| 7 | from unittest.mock import Mock
|
---|
| 8 |
|
---|
| 9 | from pyson.ObjectMapper import ObjectMapper
|
---|
| 10 | from unitpy.GeneralTests import GeneralTests
|
---|
| 11 |
|
---|
| 12 | from geniusweb.issuevalue.Bid import Bid
|
---|
| 13 | from geniusweb.issuevalue.DiscreteValue import DiscreteValue
|
---|
| 14 | from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
|
---|
| 15 | from geniusweb.issuevalue.Domain import Domain
|
---|
| 16 | from geniusweb.issuevalue.NumberValue import NumberValue
|
---|
| 17 | from geniusweb.issuevalue.Value import Value
|
---|
| 18 | from geniusweb.issuevalue.ValueSet import ValueSet
|
---|
| 19 | from geniusweb.profile.Profile import Profile
|
---|
| 20 | from geniusweb.profile.utilityspace.DiscreteValueSetUtilities import DiscreteValueSetUtilities
|
---|
| 21 | from geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace import LinearAdditiveUtilitySpace
|
---|
| 22 | from geniusweb.profile.utilityspace.UtilitySpace import UtilitySpace
|
---|
| 23 | from geniusweb.profile.utilityspace.ValueSetUtilities import ValueSetUtilities
|
---|
| 24 |
|
---|
| 25 |
|
---|
| 26 | class LinearAdditiveTest(unittest.TestCase, GeneralTests[LinearAdditiveUtilitySpace]):
|
---|
| 27 | NAME = "test"
|
---|
| 28 | I1V1UTIL = Decimal("0.3")
|
---|
| 29 | I1V2UTIL = Decimal("0.2")
|
---|
| 30 | I2V1UTIL = Decimal("0.6")
|
---|
| 31 | I2V2UTIL = Decimal("0.8")
|
---|
| 32 | WEIGHT1 = Decimal("0.4")
|
---|
| 33 | WEIGHT2 = Decimal("0.6")
|
---|
| 34 | ISS1 = "issue1"
|
---|
| 35 | ISS2 = "issue2"
|
---|
| 36 | ISS3 = "issue3"
|
---|
| 37 | i1v1 = DiscreteValue("issue1value1")
|
---|
| 38 | i1v2 = DiscreteValue("issue1value2")
|
---|
| 39 | i2v1 = DiscreteValue("issue2value1")
|
---|
| 40 | i2v2 = DiscreteValue("issue2value2")
|
---|
| 41 | i3v1 = DiscreteValue("issue3value1")
|
---|
| 42 | i3v2 = DiscreteValue("issue3value2")
|
---|
| 43 |
|
---|
| 44 | pyson = ObjectMapper()
|
---|
| 45 |
|
---|
| 46 | WEIGHT1a = Decimal("0.3")
|
---|
| 47 | WEIGHT2a = Decimal("0.7")
|
---|
| 48 |
|
---|
| 49 | values:Dict[str, ValueSet] = OrderedDict()
|
---|
| 50 | values[ISS1]=DiscreteValueSet([i1v1, i1v2])
|
---|
| 51 | smalldomain = Domain(NAME, values)
|
---|
| 52 | values[ISS2]= DiscreteValueSet([i2v1, i2v2])
|
---|
| 53 | domain = Domain(NAME, values)
|
---|
| 54 |
|
---|
| 55 | # build utilspace for string and equals testing.
|
---|
| 56 | utils:Dict[str, ValueSetUtilities] = OrderedDict()
|
---|
| 57 | valueUtils = OrderedDict()
|
---|
| 58 | valueUtils[i2v2]= I2V2UTIL
|
---|
| 59 | valueUtils[i2v1]= I2V1UTIL
|
---|
| 60 | value2Utils = DiscreteValueSetUtilities(valueUtils)
|
---|
| 61 | utils[ISS2]= value2Utils
|
---|
| 62 | valueUtils = OrderedDict()
|
---|
| 63 | valueUtils[i1v1]= I1V1UTIL
|
---|
| 64 | valueUtils[i1v2]= I1V2UTIL
|
---|
| 65 | value1Utils = DiscreteValueSetUtilities(valueUtils)
|
---|
| 66 | utils[ISS1]= value1Utils
|
---|
| 67 |
|
---|
| 68 | # build utilspaceb, mix up the utilities a bit.
|
---|
| 69 | utilsb:Dict[str, ValueSetUtilities] = OrderedDict()
|
---|
| 70 | valueUtils = OrderedDict()
|
---|
| 71 | valueUtils[i2v2] =I1V2UTIL
|
---|
| 72 | valueUtils[i2v1]= I1V1UTIL
|
---|
| 73 | value2UtilsB = DiscreteValueSetUtilities(valueUtils)
|
---|
| 74 | utilsb[ISS2]= value2UtilsB
|
---|
| 75 | valueUtils = OrderedDict()
|
---|
| 76 | valueUtils[i1v1]= I2V1UTIL
|
---|
| 77 | valueUtils[i1v2]= I2V2UTIL
|
---|
| 78 | value1UtilsB = DiscreteValueSetUtilities(valueUtils)
|
---|
| 79 | utilsb[ISS1] =value1UtilsB
|
---|
| 80 |
|
---|
| 81 | # weight map
|
---|
| 82 | weights = OrderedDict()
|
---|
| 83 | weights[ISS2]= WEIGHT2
|
---|
| 84 | weights[ISS1]= WEIGHT1
|
---|
| 85 |
|
---|
| 86 | # weight map 2
|
---|
| 87 | weightsb = OrderedDict()
|
---|
| 88 | weightsb[ISS2]= WEIGHT2a
|
---|
| 89 | weightsb[ISS1]= WEIGHT1a
|
---|
| 90 |
|
---|
| 91 | # bid with lowest utility
|
---|
| 92 | issuevalues:Dict[str, Value] = OrderedDict()
|
---|
| 93 | issuevalues[ISS2]= i2v1
|
---|
| 94 | issuevalues[ISS1]=i1v2
|
---|
| 95 | reservationBid = Bid(issuevalues)
|
---|
| 96 |
|
---|
| 97 | issuevalues = OrderedDict()
|
---|
| 98 | issuevalues[ISS2]= i2v1
|
---|
| 99 | issuevalues[ISS1]= i1v1
|
---|
| 100 | reservationBid2 = Bid(issuevalues)
|
---|
| 101 |
|
---|
| 102 | # make the utilspaces
|
---|
| 103 | utilspace1 = LinearAdditiveUtilitySpace(domain, NAME, utils,
|
---|
| 104 | weights, reservationBid)
|
---|
| 105 | utilspace1a = LinearAdditiveUtilitySpace(domain, NAME, utils,
|
---|
| 106 | weights, reservationBid);
|
---|
| 107 |
|
---|
| 108 | utilspace2a = LinearAdditiveUtilitySpace(domain, NAME, utils,
|
---|
| 109 | weightsb, reservationBid);
|
---|
| 110 | utilspace2b = LinearAdditiveUtilitySpace(domain, NAME, utilsb,
|
---|
| 111 | weights, reservationBid);
|
---|
| 112 | utilspace3 = LinearAdditiveUtilitySpace(domain, NAME, utilsb,
|
---|
| 113 | weights, reservationBid2);
|
---|
| 114 |
|
---|
| 115 |
|
---|
| 116 | #Override
|
---|
| 117 | def getGeneralTestData(self)->List[List[LinearAdditiveUtilitySpace]] :
|
---|
| 118 | return [ [self.utilspace1, self.utilspace1a], [self.utilspace2a],
|
---|
| 119 | [self.utilspace2b], [self.utilspace3]]
|
---|
| 120 |
|
---|
| 121 | #Override
|
---|
| 122 | def getGeneralTestStrings(self)-> List[str] :
|
---|
| 123 | return [
|
---|
| 124 | "LinearAdditive\\[\\{issue2=DiscreteValueSetUtilities\\{\"issue2value2\"=0.8, \"issue2value1\"=0.6\\}, issue1=DiscreteValueSetUtilities\\{\"issue1value1\"=0.3, \"issue1value2\"=0.2\\}\\},\\{issue2=0.6, issue1=0.4\\},Bid\\{issue2=\"issue2value1\", issue1=\"issue1value2\"\\}\\]",
|
---|
| 125 | "LinearAdditive\\[\\{issue2=DiscreteValueSetUtilities\\{\"issue2value2\"=0.8, \"issue2value1\"=0.6\\}, issue1=DiscreteValueSetUtilities\\{\"issue1value1\"=0.3, \"issue1value2\"=0.2\\}\\},\\{issue2=0.7, issue1=0.3\\},Bid\\{issue2=\"issue2value1\", issue1=\"issue1value2\"\\}\\]",
|
---|
| 126 | "LinearAdditive\\[\\{issue2=DiscreteValueSetUtilities\\{\"issue2value2\"=0.2, \"issue2value1\"=0.3\\}, issue1=DiscreteValueSetUtilities\\{\"issue1value1\"=0.6, \"issue1value2\"=0.8\\}\\},\\{issue2=0.6, issue1=0.4\\},Bid\\{issue2=\"issue2value1\", issue1=\"issue1value2\"\\}\\]",
|
---|
| 127 | "LinearAdditive\\[\\{issue2=DiscreteValueSetUtilities\\{\"issue2value2\"=0.2, \"issue2value1\"=0.3\\}, issue1=DiscreteValueSetUtilities\\{\"issue1value1\"=0.6, \"issue1value2\"=0.8\\}\\},\\{issue2=0.6, issue1=0.4\\},Bid\\{issue2=\"issue2value1\", issue1=\"issue1value1\"\\}\\]"
|
---|
| 128 | ]
|
---|
| 129 |
|
---|
| 130 |
|
---|
| 131 | def testConstructorNullIssues(self):
|
---|
| 132 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(self.domain, self.NAME, None, None,self.reservationBid))
|
---|
| 133 |
|
---|
| 134 | def testConstructorNullDomain(self):
|
---|
| 135 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(None, self.NAME, self.utils, self.weights,
|
---|
| 136 | self.reservationBid))
|
---|
| 137 |
|
---|
| 138 | # Empty profile is not allowed since the weights then don't sum up to 1
|
---|
| 139 | def testConstructorEmpty(self):
|
---|
| 140 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(self.domain, self.NAME, {}, self.weights,
|
---|
| 141 | self.reservationBid))
|
---|
| 142 |
|
---|
| 143 | def testConstructorOneIssue(self):
|
---|
| 144 | utilset = {}
|
---|
| 145 | weightset = {}
|
---|
| 146 | utilset[self.ISS1]= self.value1Utils
|
---|
| 147 | weightset[self.ISS1]=Decimal(1)
|
---|
| 148 | LinearAdditiveUtilitySpace(self.smalldomain, self.NAME, utilset, weightset,
|
---|
| 149 | None)
|
---|
| 150 |
|
---|
| 151 | # Empty profile is not allowed since the weights then don't sum up to 1
|
---|
| 152 | def testConstructoroneIssueWrongWeight(self):
|
---|
| 153 | utilset = {}
|
---|
| 154 | weightset = {}
|
---|
| 155 | utilset[self.ISS1]= self.value1Utils
|
---|
| 156 | weightset[self.ISS1]=self.WEIGHT1
|
---|
| 157 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(self.smalldomain, self.NAME, utilset, weightset,
|
---|
| 158 | self.reservationBid))
|
---|
| 159 |
|
---|
| 160 | # Try creating a domain and check isFitting
|
---|
| 161 | #@Test
|
---|
| 162 | def testCheckCoversDomain(self):
|
---|
| 163 | utilset = {}
|
---|
| 164 | weightset = {}
|
---|
| 165 |
|
---|
| 166 | utilset[self.ISS1]=self.value1Utils
|
---|
| 167 | weightset[self.ISS1]=Decimal(1)
|
---|
| 168 | space = LinearAdditiveUtilitySpace(
|
---|
| 169 | self.smalldomain, self.NAME, utilset, weightset, None)
|
---|
| 170 |
|
---|
| 171 |
|
---|
| 172 | # Try creating a domain and check isFitting but there is more issues in our
|
---|
| 173 | # map than in the actual domain
|
---|
| 174 | def testCheckCoversWrongDomain(self):
|
---|
| 175 | utilset = {}
|
---|
| 176 | weightset = {}
|
---|
| 177 |
|
---|
| 178 | utilset[self.ISS1]=self.value1Utils
|
---|
| 179 | weightset[self.ISS1]= Decimal(1)
|
---|
| 180 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(
|
---|
| 181 | self.domain, self.NAME, utilset, weightset, self.reservationBid))
|
---|
| 182 |
|
---|
| 183 | # Empty profile is not allowed since the weights then don't sum up to 1
|
---|
| 184 | def testUtility(self):
|
---|
| 185 | space = LinearAdditiveUtilitySpace(
|
---|
| 186 | self.domain, self.NAME, self.utils, self.weights, self.reservationBid)
|
---|
| 187 |
|
---|
| 188 | bid = Mock()
|
---|
| 189 | issues = [self.ISS1]
|
---|
| 190 | bid.getIssues = Mock(return_value = issues)
|
---|
| 191 | bid.getValue = Mock(return_value = self.i1v1)
|
---|
| 192 | self.assertEqual(self.WEIGHT1 * self.I1V1UTIL, space.getUtility(bid))
|
---|
| 193 |
|
---|
| 194 | def testPreferred(self) :
|
---|
| 195 | issuevalues = {}
|
---|
| 196 | issuevalues[self.ISS1]=self.i1v1
|
---|
| 197 | issuevalues[self.ISS2]=self.i2v2
|
---|
| 198 | bid1 = Bid(issuevalues)
|
---|
| 199 | issuevalues1 = {}
|
---|
| 200 | issuevalues1[self.ISS1]= self.i1v2
|
---|
| 201 | issuevalues1[self.ISS2]= self.i2v1
|
---|
| 202 | bid2 = Bid(issuevalues1)
|
---|
| 203 | self.assertTrue(self.utilspace1.isPreferredOrEqual(bid1, bid2))
|
---|
| 204 | self.assertTrue(self.utilspace1.isPreferredOrEqual(bid1, bid1))
|
---|
| 205 | self.assertTrue(self.utilspace1.isPreferredOrEqual(bid2, bid2))
|
---|
| 206 | self.assertFalse(self.utilspace1.isPreferredOrEqual(bid2, bid1))
|
---|
| 207 | self.assertFalse(self.utilspace1.isPreferredOrEqual(Bid({}), bid1))
|
---|
| 208 |
|
---|
| 209 | def testPartialBidUtilityTest(self):
|
---|
| 210 | space = LinearAdditiveUtilitySpace(
|
---|
| 211 | self.domain, self.NAME, self.utils, self.weights, self.reservationBid)
|
---|
| 212 | issuevalues = {}
|
---|
| 213 | issuevalues[self.ISS1]= self.i1v1
|
---|
| 214 | bid = Bid(issuevalues)
|
---|
| 215 | self.assertEqual(self.WEIGHT1* self.I1V1UTIL, space.getUtility(bid))
|
---|
| 216 |
|
---|
| 217 | def testLoadFullWithJson(self) :
|
---|
| 218 | serialized = Path("test/resources/party1.json").read_text("utf-8")
|
---|
| 219 | jsonobj=json.loads(serialized)
|
---|
| 220 | space = self.pyson.parse(jsonobj, Profile)
|
---|
| 221 |
|
---|
| 222 | def testLoadFullWithJsonNumber(self) :
|
---|
| 223 | serialized = Path("test/resources/japantrip1.json").read_text("utf-8")
|
---|
| 224 | jsonobj=json.loads(serialized)
|
---|
| 225 | profile:Profile = self.pyson.parse(jsonobj, Profile)
|
---|
| 226 |
|
---|
| 227 |
|
---|
| 228 | def testResBidWithNonsenseIssue(self) :
|
---|
| 229 | resBid = Bid({"nonsense": self.i1v1})
|
---|
| 230 | self.assertRaises(ValueError,
|
---|
| 231 | lambda:LinearAdditiveUtilitySpace(self.domain, self.NAME, self.utils, self.weights, resBid))
|
---|
| 232 |
|
---|
| 233 | def testResBidWithWrongValueType(self):
|
---|
| 234 | resBid = Bid({self.ISS1: NumberValue(Decimal(0))})
|
---|
| 235 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(self.domain, self.NAME, self.utils, self.weights, resBid))
|
---|
| 236 |
|
---|
| 237 | def testResBidWithNonsenseValue(self):
|
---|
| 238 | resBid = Bid({self.ISS1: DiscreteValue("nonsense")})
|
---|
| 239 | self.assertRaises(ValueError, lambda:LinearAdditiveUtilitySpace(self.domain, self.NAME, self.utils, self.weights, resBid))
|
---|
| 240 |
|
---|