CertainTrust.py 8.7 KB

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