CertainTrust.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import numpy as np
  2. class Opinion:
  3. """
  4. This class represents opinion.
  5. t - average rating
  6. c - certainty
  7. f - initial expectation value
  8. doc - degree of conflict
  9. """
  10. def __init__(self, t=0, c=0, f=1, doc=0):
  11. self.t = t
  12. self.c = c
  13. self.f = f
  14. self.doc = 0
  15. def __str__(self):
  16. return "Opinion{t: "+str(self.t)+", c: "+str(self.c)+", f: "+str(self.f)+"}"
  17. def expectation_value(self):
  18. return self.c * self.t + ( 1 - self.c ) * self.f
  19. @staticmethod
  20. def _single_or(o_A, o_B):
  21. """
  22. Computes OR function of two Opinion objects. Result is returned as a new object,
  23. arguments remain unchanged.
  24. N values of both objects should be equal.
  25. For detailed information see TODO
  26. :param o_A: opinion
  27. :param o_B: opinion
  28. :return: opinion as described above
  29. """
  30. r_f = o_A.f + o_B.f - o_A.f * o_B.f
  31. # FIXME: js implementation of this comparison is different from paper, using paper variant now
  32. if np.isclose(r_f, 0):
  33. # c1 + c2 - c1*c2
  34. r_c = o_A.c + o_B.c - o_A.c * o_B.c
  35. else:
  36. # restC = restC - (c1*f2*(1-c2)*(1-t1)+c2*f1*(1-c1)*(1-t2)) / resF
  37. r_c = o_A.c + o_B.c - o_A.c * o_B.c - (o_A.c * o_B.f * (1 - o_B.c) * (1 - o_A.t) + o_B.c * o_A.f * (1 - o_A.c) * (1 - o_B.t)) / r_f
  38. if np.isclose(r_c, 0):
  39. r_t = 0.5
  40. else:
  41. # resT = (1/resC) * (c1*t1 + c2*t2 - c1*c2*t1*t2);
  42. r_t = (1 / r_c) * (o_A.c * o_A.t + o_B.c * o_B.t - (o_A.c * o_B.c * o_A.t * o_B.t))
  43. r_t = Opinion._adjust_value(r_t)
  44. r_c = Opinion._adjust_value(r_c)
  45. r_f = Opinion._adjust_value(r_f)
  46. return Opinion(r_t, r_c, r_f)
  47. @staticmethod
  48. def _single_and(o_A, o_B):
  49. """
  50. Computes AND function of two Opinion objects. Result is returned as a new object,
  51. arguments remain unchanged.
  52. N values of both objects should be equal.
  53. For detailed information see TODO
  54. :param o_A: opinion
  55. :param o_B: opinion
  56. :return: opinion as described above
  57. """
  58. t1 = o_A.t
  59. c1 = o_A.c
  60. f1 = o_A.f
  61. t2 = o_B.t
  62. c2 = o_B.c
  63. f2 = o_B.f
  64. if np.isclose(f1*f2, 1):
  65. f1 = 0.999
  66. f2 = 0.999
  67. r_f = f1*f2
  68. if not np.isclose(r_f, 1):
  69. r_c = c1 + c2 - c1*c2 - (c2*t2*(1-c1)*(1-f1)+(c1*t1*(1-c2)*(1-f2))) / (1 - r_f);
  70. else:
  71. r_c = c1 + c2 - c1 * c2
  72. #FIXME: js implementation of this comparison is different from paper, using js variant now
  73. if np.isclose(r_c, 0):
  74. r_t = 0.5
  75. else:
  76. r_t = (1/r_c) * ((c1*t1*c2*t2) + (c1*f2*t1*(1-c2)*(1-f1)+c2*f1*t2*(1-c1)*(1-f2)) / (1 - r_f));
  77. r_t = Opinion._adjust_value(r_t)
  78. r_c = Opinion._adjust_value(r_c)
  79. r_f = Opinion._adjust_value(r_f)
  80. return Opinion(r_t, r_c, r_f)
  81. @staticmethod
  82. def _not(o):
  83. """
  84. Computes NOT function of an Opinion object. Result is returned as a new object,
  85. argument remains unchanged.
  86. :param o: Opinion
  87. :return: opinion as described above
  88. """
  89. r_t=o.t
  90. r_c=o.c
  91. r_f=1-o.f
  92. r_doc=0
  93. return Opinion(r_t, r_c, r_f, r_doc)
  94. @staticmethod
  95. def _cum_or(opar):
  96. """
  97. For a list of opinions, compute a cummulated OR opinion.
  98. :param opar:list of opinions
  99. :return: OR-cummulated opinion
  100. """
  101. res = opar[0]
  102. for o in opar[1:]:
  103. res = Opinion._single_or(res, o)
  104. return res
  105. @staticmethod
  106. def _cum_and(opar):
  107. """
  108. For a list of opinions, compute a cummulated AND opinion.
  109. :param opar: list of opinions
  110. :return: AND-cummulated opinion
  111. """
  112. res = opar[0]
  113. for o in opar[1:]:
  114. res = Opinion._single_and(res, o)
  115. return res
  116. @staticmethod
  117. def _internal_fusion(args, weights, doc=1.0):
  118. """
  119. An internal implementation of fusion function.
  120. Is called by _weighted_fusion and cFusion.
  121. :param args: list of opinions
  122. :param weights: list of weights
  123. :param doc: degree of conflict (float)
  124. :return:
  125. """
  126. allOne = True;
  127. allZero = True;
  128. allWeightsZero = True;
  129. atLeastOne1 = False;
  130. arrLength = len(args);
  131. for o in args:
  132. if o.c != 1:
  133. allOne = False
  134. if o.c != 0:
  135. allZero = False
  136. if o.c == 1:
  137. atLeastOne1 = True
  138. for w in weights:
  139. if w != 0:
  140. allWeightsZero = False
  141. break
  142. numeratorT = 0
  143. denominatorT = 0
  144. if allOne:
  145. r_c = 1 * (1 - doc)
  146. if(allWeightsZero):
  147. r_t = 0
  148. else:
  149. for i in range(0,arrLength):
  150. numeratorT += weights[i] * args[i].t
  151. denominatorT += weights[i]
  152. else:
  153. if atLeastOne1:
  154. raise Exception("Illegal arguments. Either all C values must equal 1 or none of them. Operation not allowed!")
  155. else:
  156. if allWeightsZero:
  157. r_t = 0
  158. r_c = 0
  159. else:
  160. denominatorC = 0
  161. numeratorC = 0
  162. for i in range(0, arrLength):
  163. mult = 1
  164. for j in range(0, arrLength):
  165. if j != i:
  166. mult *= 1 - args[j].c
  167. numeratorT = numeratorT + weights[i] * args[i].t * args[i].c * mult
  168. denominatorT = denominatorT + weights[i] * args[i].c * mult
  169. denominatorC = denominatorC + weights[i] * mult
  170. numeratorC = denominatorT
  171. r_c = (numeratorC / denominatorC) * (1 - doc)
  172. if allZero:
  173. r_t = 0.5
  174. else:
  175. r_t = numeratorT / denominatorT
  176. if allZero:
  177. r_t = 0.5;
  178. if allWeightsZero:
  179. r_f = 0;
  180. else:
  181. numerator = 0
  182. denominator = 0
  183. for i in range(0, arrLength):
  184. numerator = numerator + weights[i] * args[i].f
  185. denominator = denominator + weights[i]
  186. r_f = numerator / denominator
  187. return Opinion(r_t, r_c, r_f, doc);
  188. @staticmethod
  189. def _weighted_fusion(args, weights):
  190. """
  191. Performs weighted fusion for an array of Opinions objects in correspondence with
  192. an array of weights. Returns new Opinion object.
  193. Requirements: N values of Opinion objects must be equal.
  194. Number of weights should equal the number of Opinion objects.
  195. Arrays must be non-empty
  196. Either all of CertainTrust must be of certainty 1 or none of them.
  197. :param args: an array of Opinions
  198. :param weights: an integer array of corresponding weights
  199. :return: new Opinion
  200. """
  201. if len(args) == len(weights) and len(args) != 0:
  202. return Opinion._internal_fusion(args, weights, 0)
  203. else:
  204. raise Exception("_weighted_fusion is not allowed for these arguments")
  205. @staticmethod
  206. def _conflicted_fusion(args, weights):
  207. """
  208. Conflicted Fusion is a variation of weighted fusion, which additionally computes the degree of conflict
  209. between given Opinions and takes it into consideration while performing fusion.
  210. The degree of conflict is then saved in the resulting Opinion object.
  211. :param args:
  212. :param weights:
  213. :return:
  214. """
  215. if len(args) == len(weights) and len(args) != 0:
  216. denominator = len(args)*(len(args) - 1) / 2
  217. numerator = 0
  218. for i in range(0,len(args)):
  219. for j in range(i,len(args)):
  220. numerator = numerator + abs(args[i].t - args[j].t) * args[i].c * args[j].c * (1 - abs((weights[i] - weights[j]) / (weights[i] + weights[j])))
  221. doc = numerator/denominator
  222. return Opinion._internal_fusion(args, weights, doc)
  223. else:
  224. raise Exception("_conflicted_fusion is not allowed for these arguments")
  225. @staticmethod
  226. def _adjust_value(val):
  227. return max(min(val, 1), 0)
  228. """
  229. // TESTS
  230. os = [Opinion(0.7,0.9,0.9), Opinion(0.4,0.7,1),Opinion(0.7,0.7,1),Opinion(0.2,0.9,1),Opinion(0.55,0.8,1)]
  231. w = [1, 0.3, 0.2, 1, 0.5]
  232. print(Opinion._single_or(os[0],os[1]))
  233. print(Opinion._single_and(os[0],os[1]))
  234. print(Opinion._cum_or(os))
  235. print(Opinion._cum_and(os))
  236. print(Opinion._internal_fusion(os, w, 1))
  237. print(Opinion._conflicted_fusion(os, w))
  238. """