CertainTrust.py 8.8 KB

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