network_selection.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import copy
  2. import numpy as np
  3. from sympy import symbols, Eq, solve, Rational
  4. def gen_task_combinations(affinity, tasks, rtn, index, path, path_dict, task_overlap):
  5. if index >= len(tasks):
  6. return
  7. for i in range(index, len(tasks)):
  8. cur_task = tasks[i]
  9. new_path = path
  10. new_dict = {k: v for k, v in path_dict.items()}
  11. # Building from a tree with two or more tasks...
  12. if new_path:
  13. new_dict[cur_task] = 0.
  14. for prev_task in path_dict:
  15. new_dict[prev_task] += affinity[prev_task][cur_task]
  16. new_dict[cur_task] += affinity[cur_task][prev_task]
  17. new_path = '{}|{}'.format(new_path, cur_task)
  18. rtn[new_path] = new_dict
  19. else: # First element in a new-formed tree
  20. new_dict[cur_task] = 0.
  21. new_path = cur_task
  22. gen_task_combinations(affinity, tasks, rtn, i + 1, new_path, new_dict, task_overlap)
  23. if '|' not in new_path:
  24. if task_overlap:
  25. new_dict[cur_task] = -1e6
  26. else:
  27. new_dict[cur_task] = average_of_self_to_others_and_others_to_self(cur_task, affinity)
  28. rtn[new_path] = new_dict
  29. def average_of_self_to_others(cur_task, affinity):
  30. scores = [score for task, score in affinity[cur_task].items() if task != cur_task]
  31. return sum(scores) / len(scores)
  32. def average_of_others_to_self(cur_task, affinity):
  33. scores = [score for source_task, a in affinity.items() for target_task, score in a.items()
  34. if source_task != cur_task and target_task == cur_task]
  35. return sum(scores) / len(scores)
  36. def average_of_self_to_others_and_others_to_self(cur_task, affinity):
  37. scores1 = [score for task, score in affinity[cur_task].items() if task != cur_task]
  38. scores2 = [score for source_task, a in affinity.items() for target_task, score in a.items()
  39. if source_task != cur_task and target_task == cur_task]
  40. return (sum(scores1) + sum(scores2)) / (len(scores1) + len(scores2))
  41. def select_groups(affinity, rtn_tup, index, cur_group, best_group, best_val, splits, task_overlap=True):
  42. # Check if this group covers all tasks.
  43. num_tasks = len(affinity.keys())
  44. if task_overlap:
  45. task_set = set()
  46. for group in cur_group:
  47. for task in group.split('|'): task_set.add(task)
  48. else:
  49. task_set = list()
  50. for group in cur_group:
  51. for task in group.split('|'):
  52. if task in task_set:
  53. return
  54. else:
  55. task_set.append(task)
  56. if len(task_set) == num_tasks:
  57. best_tasks = {task: -1e6 for task in task_set}
  58. # Compute the per-task best scores for each task and average them together.
  59. for group in cur_group:
  60. for task in cur_group[group]:
  61. best_tasks[task] = max(best_tasks[task], cur_group[group][task])
  62. group_avg = np.mean(list(best_tasks.values()))
  63. # Compare with the best grouping seen thus far.
  64. if group_avg > best_val[0]:
  65. # print(cur_group)
  66. if task_overlap or no_task_overlap(cur_group, num_tasks):
  67. best_val[0] = group_avg
  68. best_group.clear()
  69. for entry in cur_group:
  70. best_group[entry] = cur_group[entry]
  71. # Base case.
  72. if len(cur_group.keys()) == splits:
  73. return
  74. # Back to combinatorics
  75. for i in range(index, len(rtn_tup)):
  76. selected_group, selected_dict = rtn_tup[i]
  77. new_group = {k: v for k, v in cur_group.items()}
  78. new_group[selected_group] = selected_dict
  79. if len(new_group.keys()) <= splits:
  80. select_groups(affinity, rtn_tup, i + 1, new_group, best_group, best_val, splits, task_overlap)
  81. def task_grouping(affinity, task_overlap=True, split=3):
  82. tasks = list(affinity.keys())
  83. rtn = {}
  84. gen_task_combinations(affinity, tasks=tasks, rtn=rtn, index=0, path='', path_dict={}, task_overlap=task_overlap)
  85. # Normalize by the number of times the accuracy of any given element has been summed.
  86. # i.e. (a,b,c) => [acc(a|b) + acc(a|c)]/2 + [acc(b|a) + acc(b|c)]/2 + [acc(c|a) + acc(c|b)]/2
  87. for group in rtn:
  88. if '|' in group:
  89. for task in rtn[group]:
  90. rtn[group][task] /= (len(group.split('|')) - 1)
  91. assert (len(rtn.keys()) == 2 ** len(affinity.keys()) - 1)
  92. rtn_tup = [(key, val) for key, val in rtn.items()]
  93. # if not task_overlap:
  94. # rtn_tup = calculate_self_affinity(affinity, rtn_tup)
  95. selected_group = {}
  96. selected_val = [-100000000]
  97. select_groups(affinity, rtn_tup, index=0, cur_group={}, best_group=selected_group, best_val=selected_val,
  98. splits=split, task_overlap=task_overlap)
  99. return list(selected_group.keys())
  100. def rtn_tup_to_dict(rtn_tup):
  101. d = {}
  102. for tup in rtn_tup:
  103. d[tup[0]] = tup[1]
  104. return d
  105. def rtn_dict_to_tup(rtn_dict):
  106. rtn_tup = []
  107. for key, value in rtn_dict.items():
  108. rtn_tup.append((key, value))
  109. return rtn_tup
  110. def calculate_self_affinity(affinity, rtn_tup):
  111. rtn_dict = rtn_tup_to_dict(rtn_tup)
  112. task_names = list(affinity.keys())
  113. tasks = symbols(" ".join(task_names))
  114. for i, t in enumerate(task_names):
  115. rtn_dict[t] = tasks[i]
  116. equations = []
  117. for i, task in enumerate(task_names):
  118. task_combs = [comb for comb in rtn_dict.keys() if task in comb]
  119. count = len(task_combs) - 1
  120. eq = Rational(0)
  121. name1 = task + "|"
  122. name2 = "|" + task
  123. for comb in task_combs:
  124. if comb == task:
  125. eq -= count * rtn_dict[comb]
  126. continue
  127. sub_comb = comb.replace(name1, "") if name1 in comb else comb.replace(name2, "")
  128. sub = rtn_dict[sub_comb] if "|" not in sub_comb else sum(rtn_dict[sub_comb].values())
  129. eq += sum(rtn_dict[comb].values()) - sub
  130. equations.append(Eq(eq, 0))
  131. sol = solve(equations, tasks)
  132. for i, t in enumerate(task_names):
  133. rtn_dict[t] = {t: sol[tasks[i]]}
  134. rtn_tup = rtn_dict_to_tup(rtn_dict)
  135. return rtn_tup
  136. def no_task_overlap(group, num_tasks):
  137. task_set = list()
  138. for combination in group.keys():
  139. for task in combination.split("|"):
  140. if task not in task_set:
  141. task_set.append(task)
  142. else:
  143. return False
  144. return len(task_set) == num_tasks
  145. def average_task_affinity_among_clients(affinities):
  146. result = copy.deepcopy(affinities[0])
  147. for task, affinity in result.items():
  148. for target_task, score in affinity.items():
  149. total = score
  150. for a in affinities[1:]:
  151. total += a[task][target_task]
  152. result[task][target_task] = total / len(affinities)
  153. return result
  154. def run(affinities):
  155. results = []
  156. averaged_affinity = average_task_affinity_among_clients(affinities)
  157. groups = task_grouping(averaged_affinity, task_overlap=True)
  158. results.append(groups)
  159. for i, a in enumerate(affinities):
  160. print("client", i)
  161. groups = task_grouping(a, task_overlap=True)
  162. results.append(groups)
  163. print(results)
  164. return results