1 | from decimal import Decimal
|
---|
2 | import json
|
---|
3 | from pathlib import Path
|
---|
4 | from typing import Dict
|
---|
5 | import unittest
|
---|
6 |
|
---|
7 | from pyson.ObjectMapper import ObjectMapper
|
---|
8 |
|
---|
9 | from geniusweb.issuevalue.Bid import Bid
|
---|
10 | from geniusweb.issuevalue.DiscreteValue import DiscreteValue
|
---|
11 | from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
|
---|
12 | from geniusweb.issuevalue.Domain import Domain
|
---|
13 | from geniusweb.issuevalue.NumberValue import NumberValue
|
---|
14 | from geniusweb.issuevalue.ValueSet import ValueSet
|
---|
15 | from geniusweb.profile.Profile import Profile
|
---|
16 | from geniusweb.profile.utilityspace.DiscreteValueSetUtilities import DiscreteValueSetUtilities
|
---|
17 | from geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace import LinearAdditiveUtilitySpace
|
---|
18 | from geniusweb.profile.utilityspace.ValueSetUtilities import ValueSetUtilities
|
---|
19 |
|
---|
20 |
|
---|
21 | class LinearAdditiveTest(unittest.TestCase):
|
---|
22 | pyson=ObjectMapper()
|
---|
23 |
|
---|
24 | yesno = DiscreteValueSet([DiscreteValue("yes"), DiscreteValue("no")])
|
---|
25 | leasecarvals = yesno
|
---|
26 | permcontractvals = yesno
|
---|
27 | carreervals=DiscreteValueSet([DiscreteValue("low"),DiscreteValue("medium"),DiscreteValue("high")])
|
---|
28 | ftevals=DiscreteValueSet([DiscreteValue("0.6"),DiscreteValue("0.8"),DiscreteValue("1.0")])
|
---|
29 | salaryvals=DiscreteValueSet([DiscreteValue("2000"),DiscreteValue("2500"),
|
---|
30 | DiscreteValue("3000"),DiscreteValue("3500"),DiscreteValue("4000")])
|
---|
31 | workfromhomevals=DiscreteValueSet([DiscreteValue("0"),DiscreteValue("1"),DiscreteValue("2")])
|
---|
32 |
|
---|
33 | jobsdomain = Domain("jobs", {"lease car":leasecarvals, "permanent contract":permcontractvals, \
|
---|
34 | "career development opportunities":carreervals, "fte":ftevals, \
|
---|
35 | "salary": salaryvals, "work from home": workfromhomevals })
|
---|
36 | N0=Decimal("0")
|
---|
37 | N1=Decimal("1")
|
---|
38 | N025=Decimal("0.25")
|
---|
39 | N03=Decimal("0.3")
|
---|
40 | N05=Decimal("0.5")
|
---|
41 | N075=Decimal("0.75")
|
---|
42 |
|
---|
43 | leasevarutils = DiscreteValueSetUtilities({DiscreteValue("no"):N0, DiscreteValue("yes"):N1})
|
---|
44 | leasevarutils2 = DiscreteValueSetUtilities({DiscreteValue("no"):N0, DiscreteValue("yes"):N075})
|
---|
45 | permcontrutils = DiscreteValueSetUtilities({DiscreteValue("no"):N0, DiscreteValue("yes"):N1})
|
---|
46 | carreerutils = DiscreteValueSetUtilities({DiscreteValue("high"):N1,DiscreteValue("low"):N0,DiscreteValue("medium"):N05})
|
---|
47 | fteutils = DiscreteValueSetUtilities({DiscreteValue("1.0"):N075,DiscreteValue("0.6"):N025,DiscreteValue("0.8"):N05})
|
---|
48 | salaryutils = DiscreteValueSetUtilities({DiscreteValue("4000"):N1,DiscreteValue("2500"):N025,DiscreteValue("3500"):N075,
|
---|
49 | DiscreteValue("2000"):N0,DiscreteValue("3000"):N03})
|
---|
50 | fromhomeutils = DiscreteValueSetUtilities({DiscreteValue("1"):N05,DiscreteValue("2"):Decimal("0.666666666666"),
|
---|
51 | DiscreteValue("0"):Decimal("0.333333333")})
|
---|
52 | utils:Dict[str, ValueSetUtilities] = {'lease car': leasevarutils, 'permanent contract':permcontrutils, 'career development opportunities': carreerutils,
|
---|
53 | 'fte':fteutils, 'salary':salaryutils, 'work from home':fromhomeutils }
|
---|
54 | utils2:Dict[str, ValueSetUtilities] = {'lease car': leasevarutils2, 'permanent contract':permcontrutils, 'career development opportunities': carreerutils,
|
---|
55 | 'fte':fteutils, 'salary':salaryutils, 'work from home':fromhomeutils }
|
---|
56 | weights:Dict[str, Decimal] = {"lease car":Decimal("0.06"),"permanent contract":Decimal("0.16"),
|
---|
57 | "career development opportunities":Decimal("0.04"),"fte":Decimal("0.32"),
|
---|
58 | "salary":Decimal("0.24"),"work from home":Decimal("0.18")}
|
---|
59 | weights2:Dict[str, Decimal] = {"lease car":Decimal("0.06"),"permanent contract":Decimal("0.16"),
|
---|
60 | "career development opportunities":Decimal("0.05"),"fte":Decimal("0.31"),
|
---|
61 | "salary":Decimal("0.24"),"work from home":Decimal("0.18")}
|
---|
62 |
|
---|
63 | jobs=LinearAdditiveUtilitySpace(jobsdomain, "jobs1",utils, weights )
|
---|
64 | jobs1=LinearAdditiveUtilitySpace(jobsdomain, "jobs1",utils, weights )
|
---|
65 | jobs2=LinearAdditiveUtilitySpace(jobsdomain, "jobs2",utils, weights )
|
---|
66 | jobs3=LinearAdditiveUtilitySpace(jobsdomain, "jobs",utils2, weights )
|
---|
67 | jobs4=LinearAdditiveUtilitySpace(jobsdomain, "jobs",utils, weights2 )
|
---|
68 |
|
---|
69 | # A real jobs profile from profiles server
|
---|
70 | # "reservationBid":null was added because toJson will also add this (even though it's the default value)
|
---|
71 | jobsstr = '{"LinearAdditiveUtilitySpace":{"issueUtilities":{' \
|
---|
72 | + '"lease car":{"DiscreteValueSetUtilities":{"valueUtilities":{"no":0,"yes":1}}},' \
|
---|
73 | + '"permanent contract":{"DiscreteValueSetUtilities":{"valueUtilities":{"no":0,"yes":1}}},' \
|
---|
74 | + '"career development opportunities":{"DiscreteValueSetUtilities":{"valueUtilities":{"high":1,"low":0,"medium":0.5}}},' \
|
---|
75 | + '"fte":{"DiscreteValueSetUtilities":{"valueUtilities":{"1.0":0.75,"0.6":0.25,"0.8":0.5}}},' \
|
---|
76 | + '"salary":{"DiscreteValueSetUtilities":{"valueUtilities":{"4000":1,"2500":0.25,"3500":0.75,"2000":0,"3000":0.3}}},' \
|
---|
77 | + '"work from home":{"DiscreteValueSetUtilities":{"valueUtilities":{"1":0.5,"2":0.666666666666,"0":0.333333333}}}},' \
|
---|
78 | + '"issueWeights":{"lease car":0.06,"permanent contract":0.16,' \
|
---|
79 | + '"career development opportunities":0.04,"fte":0.32,"salary":0.24,"work from home":0.18},' \
|
---|
80 | + '"domain":{"name":"jobs","issuesValues":{"lease car":{"values":["yes","no"]},"permanent contract":{"values":["yes","no"]},"career development opportunities":{"values":["low","medium","high"]},"fte":{"values":["0.6","0.8","1.0"]},"salary":{"values":["2000","2500","3000","3500","4000"]},"work from home":{"values":["0","1","2"]}}},'\
|
---|
81 | + '"name":"jobs1", "reservationBid":null}}'
|
---|
82 |
|
---|
83 | jobsjson = json.loads(jobsstr)
|
---|
84 |
|
---|
85 | partystr='{"LinearAdditiveUtilitySpace":{"domain":{"name":"party","issuesValues":{"Invitations":{"values":["Plain","Photo","Custom, Handmade","Custom, Printed"]},"Music":{"values":["MP3","DJ","Band"]},"Drinks":{"values":["Non-Alcoholic","Beer Only","Handmade Cocktails","Catering"]},"Cleanup":{"values":["Water and Soap","Specialized Materials","Special Equiment","Hired Help"]},"Food":{"values":["Chips and Nuts","Finger-Food","Handmade Food","Catering"]},"Location":{"values":["Party Tent","Your Dorm","Party Room","Ballroom"]}}},"name":"party1","issueUtilities":{"Invitations":{"DiscreteValueSetUtilities":{"valueUtilities":{"Plain":0.24,"Custom, Printed":0.48,"Photo":0.74,"Custom, Handmade":1}}},"Music":{"DiscreteValueSetUtilities":{"valueUtilities":{"DJ":0.99,"Band":0.35,"MP3":0.65}}},"Drinks":{"DiscreteValueSetUtilities":{"valueUtilities":{"Handmade Cocktails":0.672,"Non-Alcoholic":0.263,"Beer Only":0.98,"Catering":0.334}}},"Cleanup":{"DiscreteValueSetUtilities":{"valueUtilities":{"Special Equiment":0.32,"Water and Soap":0.65,"Specialized Materials":0.96,"Hired Help":0.31}}},"Food":{"DiscreteValueSetUtilities":{"valueUtilities":{"Finger-Food":0.52,"Handmade Food":0.51,"Catering":0.25,"Chips and Nuts":0.75}}},"Location":{"DiscreteValueSetUtilities":{"valueUtilities":{"Your Dorm":0.23,"Ballroom":0.77,"Party Room":0.99,"Party Tent":0.52}}}},"issueWeights":{"Invitations":0.05,"Music":0.19,"Drinks":0.28,"Cleanup":0.10,"Food":0.19,"Location":0.19},"reservationBid":null}}'
|
---|
86 | partyjson = json.loads(partystr)
|
---|
87 |
|
---|
88 | def testSerialize(self):
|
---|
89 | print(self.pyson.toJson(self.jobs))
|
---|
90 | self.maxDiff=None
|
---|
91 | self.assertEqual(self.jobsjson, self.pyson.toJson(self.jobs))
|
---|
92 |
|
---|
93 | def testDeserialize(self):
|
---|
94 | self.assertEqual(self.jobs, self.pyson.parse(self.jobsjson, Profile))
|
---|
95 |
|
---|
96 | def testDeserializeJapanTrip(self):
|
---|
97 | serialized = Path("test/resources/japantrip1.json").read_text("utf-8")
|
---|
98 | jsonobj=json.loads(serialized)
|
---|
99 | profile:Profile = self.pyson.parse(jsonobj, Profile);
|
---|
100 | self.assertEquals("japantrip1", profile.getName())
|
---|
101 | self.assertEquals("japantrip", profile.getDomain().getName())
|
---|
102 |
|
---|
103 | def testDeserializeParty1(self):
|
---|
104 | print( str(self.pyson.parse(self.partyjson, Profile)))
|
---|
105 |
|
---|
106 | def testEquals(self):
|
---|
107 | self.assertEqual(self.jobs,self.jobs1)
|
---|
108 | self.assertEqual(hash(self.jobs),hash(self.jobs1))
|
---|
109 | self.assertNotEqual(self.jobs,self.jobs2)
|
---|
110 | self.assertNotEqual(hash(self.jobs),hash(self.jobs2))
|
---|
111 | self.assertNotEqual(self.jobs,self.jobs3)
|
---|
112 | self.assertNotEqual(hash(self.jobs),hash(self.jobs3))
|
---|
113 | self.assertNotEqual(self.jobs,self.jobs4)
|
---|
114 | self.assertNotEqual(hash(self.jobs),hash(self.jobs4))
|
---|
115 |
|
---|
116 |
|
---|
117 | def testUtil(self):
|
---|
118 | bid=Bid({"lease car":DiscreteValue("yes"), "permanent contract":DiscreteValue("yes"), \
|
---|
119 | "career development opportunities": DiscreteValue("low"), \
|
---|
120 | "fte": DiscreteValue("0.8"), "salary":DiscreteValue("2500"), "work from home": DiscreteValue("1") });
|
---|
121 | # 0.06 * 1 + 0.16 * 1 + 0.04 * 0 + 0.32 * 0.5 + 0.24 * 0.25 + 0.18 * 0.5
|
---|
122 | self.assertEqual(Decimal("0.53"), self.jobs.getUtility(bid))
|
---|