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("utf8")


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("utf8")


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 

