def generate_binary_performance_table_and_assignments(criteria, categories, fmins): cids = list(criteria.keys()) cats = categories.get_ordered_categories() aa = AlternativesAssignments() pt = PerformanceTable() i = 1 for coalition in powerset(cids): if set(coalition) == set({}) or set(coalition) == set(cids): continue aid = "a%d" % i ap = AlternativePerformances(aid, OrderedDict({c: 1 if c in coalition else 0 for c in cids})) pt.append(ap) cat = cats[0] for fmin in fmins: if fmin.issubset(set(coalition)) is True: cat = cats[1] break aa.append(AlternativeAssignment(aid, cat)) i += 1 return pt, aa
def generate_binary_performance_table_and_assignments(criteria, categories, fmins): cids = list(criteria.keys()) cats = categories.get_ordered_categories() aa = AlternativesAssignments() pt = PerformanceTable() i = 1 for coalition in powerset(cids): if set(coalition) == set({}) or set(coalition) == set(cids): continue aid = "a%d" % i ap = AlternativePerformances( aid, OrderedDict({c: 1 if c in coalition else 0 for c in cids})) pt.append(ap) cat = cats[0] for fmin in fmins: if fmin.issubset(set(coalition)) is True: cat = cats[1] break aa.append(AlternativeAssignment(aid, cat)) i += 1 return pt, aa
def compute_assignments_majority(models_ca, pt): models = sorted(models_ca, key = lambda k: models_ca[k], reverse = True) models_aa = {} for model in models[:len(models)/2]: models_aa[model] = model.get_assignments(pt) aa = AlternativesAssignments() for ap in pt: aid = ap.id cat_scores = {} for model in models[:int(math.ceil(len(models) / 2))]: cat = models_aa[model][aid].category_id if cat in cat_scores: cat_scores[cat] += models_ca[model] else: cat_scores[cat] = models_ca[model] b = AlternativeAssignment(aid, max(cat_scores, key = lambda cat: cat_scores[cat])) aa.append(b) return aa
def discard_alternatives_in_category(aa, category): out = AlternativesAssignments() for a in aa: if a.category_id != category: out.append(a) return out
def discard_undersorted_alternatives(categories, aa, aa2): out = AlternativesAssignments() cat_order = {cat: i + 1 for i, cat in enumerate(categories)} for a in aa: aid = a.id acat = cat_order[a.category_id] a2 = aa2[aid] a2cat = cat_order[a2.category_id] if a2cat >= acat: out.append(a) return out
def add_errors_in_assignments(aa, category_ids, errors_pc): n = int(len(aa)*errors_pc) aa_erroned = random.sample(list(aa), n) l = AlternativesAssignments([]) for a in aa_erroned: cat = a.category_id new_cat = a.category_id while new_cat == cat: new_cat = random.sample(category_ids, 1)[0] a.category_id = new_cat l.append(a) return l
def add_errors_in_assignments(aa, category_ids, errors_pc): n = int(len(aa) * errors_pc) aa_erroned = random.sample(list(aa), n) l = AlternativesAssignments([]) for a in aa_erroned: cat = a.category_id new_cat = a.category_id while new_cat == cat: new_cat = random.sample(category_ids, 1)[0] a.category_id = new_cat l.append(a) return l
def add_errors_in_assignments_proba(aa, category_ids, proba): l = AlternativesAssignments([]) for a in aa: r = random.random() if r <= proba: cat = a.category_id new_cat = a.category_id while new_cat == cat: new_cat = random.sample(category_ids, 1)[0] a.category_id = new_cat l.append(a) return l
def optimist(self, pt): self.__check_input_params() profiles = self.profiles assignments = AlternativesAssignments([]) for action_perfs in pt: cat_rank = 0 for i, profile in enumerate(profiles): outr = self.__outrank(action_perfs, self.criteria, self.bpt[profile], self.lbda) if outr != "-": cat_rank += 1 cat_id = self.categories[cat_rank] id = action_perfs.id alt_affect = AlternativeAssignment(id, cat_id) assignments.append(alt_affect) return assignments
def pessimist(self, pt): self.__check_input_params() profiles = self.profiles[:] profiles.reverse() assignments = AlternativesAssignments([]) for action_perfs in pt: cat_rank = len(profiles) for i, profile in enumerate(profiles): s_ab = self.credibility(action_perfs, self.bpt[profile], profile) if not eq(s_ab, self.lbda) and s_ab < self.lbda: cat_rank -= 1 cat_id = self.categories[cat_rank] id = action_perfs.id alt_affect = AlternativeAssignment(id, cat_id) assignments.append(alt_affect) return assignments
def compute_assignments_majority(models_ca, pt): models = sorted(models_ca, key=lambda k: models_ca[k], reverse=True) models_aa = {} for model in models[:len(models) / 2]: models_aa[model] = model.get_assignments(pt) aa = AlternativesAssignments() for ap in pt: aid = ap.id cat_scores = {} for model in models[:int(math.ceil(len(models) / 2))]: cat = models_aa[model][aid].category_id if cat in cat_scores: cat_scores[cat] += models_ca[model] else: cat_scores[cat] = models_ca[model] b = AlternativeAssignment( aid, max(cat_scores, key=lambda cat: cat_scores[cat])) aa.append(b) return aa
class MetaMRSortProfiles3(): def __init__(self, model, pt_sorted, aa_ori): self.na = len(aa_ori) self.model = model self.nprofiles = len(model.profiles) self.pt_sorted = pt_sorted self.aa_ori = aa_ori self.cat = self.categories_rank() self.cat_ranked = self.model.categories self.aa_by_cat = self.sort_alternative_by_category(aa_ori) self.b0 = pt_sorted.pt.get_worst(model.criteria) self.bp = pt_sorted.pt.get_best(model.criteria) self.compute_interval_ratios(3) self.build_concordance_table() self.build_assignments_table() def categories_rank(self): return {cat: i + 1 for i, cat in enumerate(self.model.categories)} def compute_interval_ratios(self, n): self.nintervals = n intervals = [] for i in range(n - 1): intervals += [math.exp(i + 1)] s = sum(intervals) self.interval_ratios = [i / s for i in intervals] + [0.9] def update_intervals(self, fitness): if fitness > 0.99: self.compute_interval_ratios(8) elif fitness > 0.95: self.compute_interval_ratios(6) elif fitness > 0.9: self.compute_interval_ratios(5) else: self.compute_interval_ratios(4) def rank_categories(self, cat): c_rank = {c.id: c.rank for c in cat} return sorted([cat for cat in c_rank.iterkeys()]) def sort_alternative_by_category(self, aa): aa_by_cat = {} for a in aa: aid = a.id cat = self.cat[a.category_id] if cat in aa_by_cat: aa_by_cat[cat].append(aid) else: aa_by_cat[cat] = [aid] return aa_by_cat def get_alternative_assignment(self, aid): profile = self.model.profiles[0] for profile in reversed(self.model.profiles): if self.ct[profile][aid] >= self.model.lbda: return self.model.categories_profiles[profile].value.upper return self.model.categories_profiles[profile].value.lower def build_assignments_table(self): self.good = 0 self.aa = AlternativesAssignments() for aa in self.aa_ori.values(): aid = aa.id cat = self.get_alternative_assignment(aid) self.aa.append(AlternativeAssignment(aid, cat)) cat_ori = aa.category_id if cat == cat_ori: self.good += 1 def build_concordance_table(self): self.ct = {bp.id: dict() for bp in self.model.bpt} for aid, bp in product(self.aa_ori.keys(), self.model.bpt): ap = self.pt_sorted[aid] conc = self.model.concordance(ap, bp) self.ct[bp.id][aid] = conc def rebuild_tables(self): self.build_concordance_table() self.build_assignments_table() def compute_above_histogram(self, cid, profile, above, cat_b, cat_a): h_above = {} size = above - profile intervals = [ profile + self.interval_ratios[i]*size \ for i in range(self.nintervals) ] intervals = [profile] + intervals ok = nok = 0 for i in range(self.nintervals): alts = self.pt_sorted.get_middle(cid, intervals[i], intervals[i + 1])[0] for a in alts: if self.aa(a) == cat_b and self.aa_ori(a) == cat_a: ok += 1 elif self.aa(a) == cat_a: if self.aa_ori(a) == cat_a: ok += 1 elif self.aa_ori(a) == cat_b: nok += 1 if (ok + nok) > 0: h_above[intervals[i + 1]] = nok / (ok + nok) else: h_above[intervals[i + 1]] = 0 return h_above def compute_below_histogram(self, cid, profile, below, cat_b, cat_a): h_below = {} size = profile - below intervals = [ profile - self.interval_ratios[i]*size \ for i in range(self.nintervals) ] intervals = [profile] + intervals ok = nok = 0 for i in range(self.nintervals): alts = self.pt_sorted.get_middle(cid, intervals[i], intervals[i + 1])[0] for a in alts: if self.aa(a) == cat_a and self.aa_ori(a) == cat_b: ok += 1 elif self.aa(a) == cat_b: if self.aa_ori(a) == cat_b: ok += 1 elif self.aa_ori(a) == cat_a: nok += 1 if (ok + nok) > 0: h_below[intervals[i + 1]] = nok / (ok + nok) else: h_below[intervals[i + 1]] = 0 return h_below def optimize_profile(self, profile, below, above, cat_b, cat_a): p_perfs = profile.performances a_perfs = above.performances b_perfs = below.performances moved = False max_val = 0 for c in self.model.criteria: cid = c.id h_below = self.compute_below_histogram(cid, p_perfs[cid], b_perfs[cid], cat_b, cat_a) h_above = self.compute_above_histogram(cid, p_perfs[cid], a_perfs[cid], cat_b, cat_a) i_b = max(h_below, key=h_below.get) i_a = max(h_above, key=h_above.get) r = random.random() if h_below[i_b] > h_above[i_a]: if r < h_below[i_b]: p_perfs[cid] = i_b moved = True elif moved is False and h_below[i_b] > max_val: max_val = h_below[i_b] max_cid = cid max_move = i_b elif h_below[i_b] < h_above[i_a]: if r < h_above[i_a]: p_perfs[cid] = i_a moved = True elif moved is False and h_above[i_a] > max_val: max_val = h_above[i_a] max_cid = cid max_move = i_a elif r > 0.5: r2 = random.random() if r2 < h_below[i_b]: p_perfs[cid] = i_b moved = True elif moved is False and h_below[i_b] > max_val: max_val = h_below[i_b] max_cid = cid max_move = i_b elif r < 0.5: r2 = random.random() if r2 < h_above[i_a]: p_perfs[cid] = i_a moved = True elif moved is False and h_above[i_a] > max_val: max_val = h_above[i_a] max_cid = cid max_move = i_a if moved is False and max_val > 0: p_perfs[max_cid] = max_move def get_below_and_above_profiles(self, i): profiles = self.model.profiles bpt = self.model.bpt if i == 0: below = self.b0 else: below = bpt[profiles[i - 1]] if i == self.nprofiles - 1: above = self.bp else: above = bpt[profiles[i + 1]] return below, above def optimize(self): profiles = self.model.profiles for i, profile in enumerate(profiles): pperfs = self.model.bpt[profile] below, above = self.get_below_and_above_profiles(i) cat_b, cat_a = self.cat_ranked[i], self.cat_ranked[i + 1] self.update_intervals(self.good / self.na) self.optimize_profile(pperfs, below, above, cat_b, cat_a) self.rebuild_tables() return self.good / self.na
def get_assignments(self, pt): aas = AlternativesAssignments([]) for ap in pt: aas.append(self.get_assignment(ap)) return aas
def get_assignments(self, pt): aa = AlternativesAssignments() for ap in pt: a = self.get_assignment(ap) aa.append(a) return aa
class MetaMRSortProfilesChoquet(): def __init__(self, model, pt_sorted, aa_ori): self.na = len(aa_ori) self.nc = len(model.criteria) self.model = model self.nprofiles = len(model.profiles) self.pt_sorted = pt_sorted self.aa_ori = aa_ori self.cat = self.categories_rank() self.cat_ranked = self.model.categories self.aa_by_cat = self.sort_alternative_by_category(aa_ori) self.b0 = pt_sorted.pt.get_worst(model.criteria) self.bp = pt_sorted.pt.get_best(model.criteria) self.rebuild_tables() def categories_rank(self): return {cat: i + 1 for i, cat in enumerate(self.model.categories)} def sort_alternative_by_category(self, aa): aa_by_cat = {} for a in aa: aid = a.id cat = self.cat[a.category_id] if cat in aa_by_cat: aa_by_cat[cat].append(aid) else: aa_by_cat[cat] = [aid] return aa_by_cat def compute_above_histogram(self, cid, perf_profile, perf_above, cat_b, cat_a, ct): lbda = self.model.lbda direction = self.model.criteria[cid].direction delta = 0.00001 * direction h_above = {} num = total = 0 alts, perfs = self.pt_sorted.get_middle(cid, perf_profile, perf_above, True, True) for i, a in enumerate(alts): if (perfs[i] + delta) * direction > perf_above * direction: continue aa_ori = self.aa_ori._d[a].category_id aa = self.aa._d[a].category_id diff = self.mobius[frozenset(ct[a] | set([cid]))] if aa_ori == cat_a: if aa == cat_a and diff < lbda: # -- total += 5 elif aa == cat_b: # - total += 1 elif aa_ori == cat_b and aa == cat_a: if diff >= lbda: # + num += 0.5 total += 1 h_above[perfs[i] + delta] = num / total else: # ++ num += 2 total += 1 h_above[perfs[i] + delta] = num / total # elif self.aa_ori(a) < self.aa(a) and \ elif aa_ori != aa and \ self.cat[aa] < self.cat[cat_a] and \ self.cat[aa_ori] < self.cat[cat_a]: num += 0.1 total += 1 h_above[perfs[i] + delta] = num / total return h_above def compute_below_histogram(self, cid, perf_profile, perf_below, cat_b, cat_a, ct): lbda = self.model.lbda h_below = {} num = total = 0 alts, perfs = self.pt_sorted.get_middle(cid, perf_profile, perf_below, True, True) for i, a in enumerate(alts): aa_ori = self.aa_ori._d[a].category_id aa = self.aa._d[a].category_id diff = self.mobius[frozenset(ct[a] | set([cid]))] if aa_ori == cat_a and aa == cat_b: if diff >= lbda: # ++ num += 2 total += 1 h_below[perfs[i]] = num / total else: # + num += 0.5 total += 1 h_below[perfs[i]] = num / total elif aa_ori == cat_b: if aa == cat_b and diff >= lbda: # -- total += 5 elif aa == cat_a: # - total += 1 # elif self.aa_ori(a) > self.aa(a) and \ elif aa_ori != aa and \ self.cat[aa] > self.cat[cat_b] and \ self.cat[aa_ori] > self.cat[cat_b]: num += 0.1 total += 1 h_below[perfs[i]] = num / total return h_below def histogram_choose(self, h, current): key = random.choice(h.keys()) val = h[key] diff = abs(current - key) for k, v in h.items(): if v >= val: tmp = abs(current - k) if tmp > diff: key = k val = v diff = tmp return key def get_alternative_assignment(self, aid): profile = self.model.profiles[0] for profile in reversed(self.model.profiles): if self.mobius[self.ct[profile][aid]] >= self.model.lbda: return self.model.categories_profiles[profile].value.upper return self.model.categories_profiles[profile].value.lower def build_assignments_table(self): self.good = 0 self.aa = AlternativesAssignments() for aa in self.aa_ori.values(): aid = aa.id cat = self.get_alternative_assignment(aid) self.aa.append(AlternativeAssignment(aid, cat)) cat_ori = aa.category_id if cat == cat_ori: self.good += 1 def build_concordance_table(self): self.ct = {bp.id: dict() for bp in self.model.bpt} for aid, bp in product(self.aa_ori.keys(), self.model.bpt): ap = self.pt_sorted[aid] conc = self.model.concordance(ap, bp) self.ct[bp.id][aid] = conc def build_coalition_table(self): self.ct = {bp.id: dict() for bp in self.model.bpt} for aid, bp in product(self.aa_ori.keys(), self.model.bpt): ap = self.pt_sorted[aid] criteria_coalition = self.model.criteria_coalition(ap, bp) self.ct[bp.id][aid] = frozenset(criteria_coalition) def build_mobius_table(self): cids = self.model.criteria.keys() self.mobius = {frozenset([]): 0} for i in range(len(cids)): for c in combinations(cids, i + 1): s = frozenset(c) wsum = self.model.coalition_weight(s) self.mobius[s] = wsum def rebuild_tables(self): self.build_mobius_table() self.build_coalition_table() self.build_assignments_table() def update_tables(self, profile, cid, old, new): direction = self.model.criteria[cid].direction if old > new: if direction == 1: down, up = True, False else: down, up = False, True add = True else: if direction == 1: down, up = False, True else: down, up = True, False add = False alts, perfs = self.pt_sorted.get_middle(cid, old, new, up, down) for a in alts: self.ct[profile][a] = set(self.ct[profile][a]) if add is True: self.ct[profile][a].add(cid) else: self.ct[profile][a].discard(cid) self.ct[profile][a] = frozenset(self.ct[profile][a]) old_cat = self.aa[a].category_id new_cat = self.get_alternative_assignment(a) ori_cat = self.aa_ori[a].category_id if old_cat == new_cat: continue elif old_cat == ori_cat: self.good -= 1 elif new_cat == ori_cat: self.good += 1 self.aa[a].category_id = new_cat def optimize_profile(self, profile, below, above, cat_b, cat_a): cids = self.model.criteria.keys() random.shuffle(cids) for cid in cids: ct = self.ct[profile.id] perf_profile = profile.performances[cid] perf_above = above.performances[cid] perf_below = below.performances[cid] h_below = self.compute_below_histogram(cid, perf_profile, perf_below, cat_b, cat_a, ct) h_above = self.compute_above_histogram(cid, perf_profile, perf_above, cat_b, cat_a, ct) h = h_below h.update(h_above) if not h: continue i = self.histogram_choose(h, perf_profile) r = random.uniform(0, 1) if r <= h[i]: self.update_tables(profile.id, cid, perf_profile, i) profile.performances[cid] = i def get_below_and_above_profiles(self, i): profiles = self.model.profiles bpt = self.model.bpt if i == 0: below = self.b0 else: below = bpt[profiles[i - 1]] if i == self.nprofiles - 1: above = self.bp else: above = bpt[profiles[i + 1]] return below, above def optimize(self): profiles = self.model.profiles for i, profile in enumerate(profiles): pperfs = self.model.bpt[profile] below, above = self.get_below_and_above_profiles(i) cat_b, cat_a = self.cat_ranked[i], self.cat_ranked[i + 1] self.optimize_profile(pperfs, below, above, cat_b, cat_a) return self.good / self.na
class MetaMRSortProfiles3(): def __init__(self, model, pt_sorted, aa_ori): self.na = len(aa_ori) self.model = model self.nprofiles = len(model.profiles) self.pt_sorted = pt_sorted self.aa_ori = aa_ori self.cat = self.categories_rank() self.cat_ranked = self.model.categories self.aa_by_cat = self.sort_alternative_by_category(aa_ori) self.b0 = pt_sorted.pt.get_worst(model.criteria) self.bp = pt_sorted.pt.get_best(model.criteria) self.compute_interval_ratios(3) self.build_concordance_table() self.build_assignments_table() def categories_rank(self): return { cat: i + 1 for i, cat in enumerate(self.model.categories) } def compute_interval_ratios(self, n): self.nintervals = n intervals = [] for i in range(n-1): intervals += [ math.exp(i+1) ] s = sum(intervals) self.interval_ratios = [ i/s for i in intervals ] + [ 0.9 ] def update_intervals(self, fitness): if fitness > 0.99: self.compute_interval_ratios(8) elif fitness > 0.95: self.compute_interval_ratios(6) elif fitness > 0.9: self.compute_interval_ratios(5) else: self.compute_interval_ratios(4) def rank_categories(self, cat): c_rank = { c.id: c.rank for c in cat } return sorted([cat for cat in c_rank.iterkeys()]) def sort_alternative_by_category(self, aa): aa_by_cat = {} for a in aa: aid = a.id cat = self.cat[a.category_id] if cat in aa_by_cat: aa_by_cat[cat].append(aid) else: aa_by_cat[cat] = [ aid ] return aa_by_cat def get_alternative_assignment(self, aid): profile = self.model.profiles[0] for profile in reversed(self.model.profiles): if self.ct[profile][aid] >= self.model.lbda: return self.model.categories_profiles[profile].value.upper return self.model.categories_profiles[profile].value.lower def build_assignments_table(self): self.good = 0 self.aa = AlternativesAssignments() for aa in self.aa_ori.values(): aid = aa.id cat = self.get_alternative_assignment(aid) self.aa.append(AlternativeAssignment(aid, cat)) cat_ori = aa.category_id if cat == cat_ori: self.good += 1 def build_concordance_table(self): self.ct = { bp.id: dict() for bp in self.model.bpt } for aid, bp in product(self.aa_ori.keys(), self.model.bpt): ap = self.pt_sorted[aid] conc = self.model.concordance(ap, bp) self.ct[bp.id][aid] = conc def rebuild_tables(self): self.build_concordance_table() self.build_assignments_table() def compute_above_histogram(self, cid, profile, above, cat_b, cat_a): h_above = {} size = above - profile intervals = [ profile + self.interval_ratios[i]*size \ for i in range(self.nintervals) ] intervals = [ profile ] + intervals ok = nok = 0 for i in range(self.nintervals): alts = self.pt_sorted.get_middle(cid, intervals[i], intervals[i+1])[0] for a in alts: if self.aa(a) == cat_b and self.aa_ori(a) == cat_a: ok += 1 elif self.aa(a) == cat_a: if self.aa_ori(a) == cat_a: ok += 1 elif self.aa_ori(a) == cat_b: nok += 1 if (ok + nok) > 0: h_above[intervals[i+1]] = nok / (ok + nok) else: h_above[intervals[i+1]] = 0 return h_above def compute_below_histogram(self, cid, profile, below, cat_b, cat_a): h_below = {} size = profile - below intervals = [ profile - self.interval_ratios[i]*size \ for i in range(self.nintervals) ] intervals = [ profile ] + intervals ok = nok = 0 for i in range(self.nintervals): alts = self.pt_sorted.get_middle(cid, intervals[i], intervals[i+1])[0] for a in alts: if self.aa(a) == cat_a and self.aa_ori(a) == cat_b: ok += 1 elif self.aa(a) == cat_b: if self.aa_ori(a) == cat_b: ok += 1 elif self.aa_ori(a) == cat_a: nok += 1 if (ok + nok) > 0: h_below[intervals[i+1]] = nok / (ok + nok) else: h_below[intervals[i+1]] = 0 return h_below def optimize_profile(self, profile, below, above, cat_b, cat_a): p_perfs = profile.performances a_perfs = above.performances b_perfs = below.performances moved = False max_val = 0 for c in self.model.criteria: cid = c.id h_below = self.compute_below_histogram(cid, p_perfs[cid], b_perfs[cid], cat_b, cat_a) h_above = self.compute_above_histogram(cid, p_perfs[cid], a_perfs[cid], cat_b, cat_a) i_b = max(h_below, key=h_below.get) i_a = max(h_above, key=h_above.get) r = random.random() if h_below[i_b] > h_above[i_a]: if r < h_below[i_b]: p_perfs[cid] = i_b moved = True elif moved is False and h_below[i_b] > max_val: max_val = h_below[i_b] max_cid = cid max_move = i_b elif h_below[i_b] < h_above[i_a]: if r < h_above[i_a]: p_perfs[cid] = i_a moved = True elif moved is False and h_above[i_a] > max_val: max_val = h_above[i_a] max_cid = cid max_move = i_a elif r > 0.5: r2 = random.random() if r2 < h_below[i_b]: p_perfs[cid] = i_b moved = True elif moved is False and h_below[i_b] > max_val: max_val = h_below[i_b] max_cid = cid max_move = i_b elif r < 0.5: r2 = random.random() if r2 < h_above[i_a]: p_perfs[cid] = i_a moved = True elif moved is False and h_above[i_a] > max_val: max_val = h_above[i_a] max_cid = cid max_move = i_a if moved is False and max_val > 0: p_perfs[max_cid] = max_move def get_below_and_above_profiles(self, i): profiles = self.model.profiles bpt = self.model.bpt if i == 0: below = self.b0 else: below = bpt[profiles[i-1]] if i == self.nprofiles-1: above = self.bp else: above = bpt[profiles[i+1]] return below, above def optimize(self): profiles = self.model.profiles for i, profile in enumerate(profiles): pperfs = self.model.bpt[profile] below, above = self.get_below_and_above_profiles(i) cat_b, cat_a = self.cat_ranked[i], self.cat_ranked[i+1] self.update_intervals(self.good / self.na) self.optimize_profile(pperfs, below, above, cat_b, cat_a) self.rebuild_tables() return self.good / self.na
class MetaMRSortProfiles4(): def __init__(self, model, pt_sorted, aa_ori): self.na = len(aa_ori) self.nc = len(model.criteria) self.model = model self.nprofiles = len(model.profiles) self.pt_sorted = pt_sorted self.aa_ori = aa_ori self.cat = self.categories_rank() self.cat_ranked = self.model.categories self.aa_by_cat = self.sort_alternative_by_category(aa_ori) self.b0 = pt_sorted.pt.get_worst(model.criteria) self.bp = pt_sorted.pt.get_best(model.criteria) self.build_concordance_table() self.build_assignments_table() def categories_rank(self): return { cat: i + 1 for i, cat in enumerate(self.model.categories) } def sort_alternative_by_category(self, aa): aa_by_cat = {} for a in aa: aid = a.id cat = self.cat[a.category_id] if cat in aa_by_cat: aa_by_cat[cat].append(aid) else: aa_by_cat[cat] = [ aid ] return aa_by_cat def compute_above_histogram(self, cid, perf_profile, perf_above, cat_b, cat_a, ct): w = self.model.cv[cid].value lbda = self.model.lbda direction = self.model.criteria[cid].direction delta = 0.00001 * direction h_above = {} num = total = 0 alts, perfs = self.pt_sorted.get_middle(cid, perf_profile, perf_above, True, True) for i, a in enumerate(alts): if (perfs[i] + delta) * direction > perf_above * direction: continue conc = ct[a] aa_ori = self.aa_ori._d[a].category_id aa = self.aa._d[a].category_id diff = conc - w if aa_ori == cat_a: if aa == cat_a and diff < lbda: # -- total += 5 elif aa == cat_b: # - total += 1 elif aa_ori == cat_b and aa == cat_a: if diff >= lbda: # + num += 0.5 total += 1 h_above[perfs[i] + delta] = num / total else: # ++ num += 2 total += 1 h_above[perfs[i] + delta] = num / total # elif self.aa_ori(a) < self.aa(a) and \ elif aa_ori != aa and \ self.cat[aa] < self.cat[cat_a] and \ self.cat[aa_ori] < self.cat[cat_a]: num += 0.1 total += 1 h_above[perfs[i] + delta] = num / total return h_above def compute_below_histogram(self, cid, perf_profile, perf_below, cat_b, cat_a, ct): w = self.model.cv[cid].value lbda = self.model.lbda h_below = {} num = total = 0 alts, perfs = self.pt_sorted.get_middle(cid, perf_profile, perf_below, True, True) for i, a in enumerate(alts): conc = ct[a] aa_ori = self.aa_ori._d[a].category_id aa = self.aa._d[a].category_id diff = conc + w if aa_ori == cat_a and aa == cat_b: if diff >= lbda: # ++ num += 2 total += 1 h_below[perfs[i]] = num / total else: # + num += 0.5 total += 1 h_below[perfs[i]] = num / total elif aa_ori == cat_b: if aa == cat_b and diff >= lbda: # -- total += 5 elif aa == cat_a: # - total += 1 # elif self.aa_ori(a) > self.aa(a) and \ elif aa_ori != aa and \ self.cat[aa] > self.cat[cat_b] and \ self.cat[aa_ori] > self.cat[cat_b]: num += 0.1 total += 1 h_below[perfs[i]] = num / total return h_below def histogram_choose(self, h, current): key = random.choice(h.keys()) val = h[key] diff = abs(current - key) for k, v in h.items(): if v >= val: tmp = abs(current - k) if tmp > diff: key = k val = v diff = tmp return key def get_alternative_assignment(self, aid): profile = self.model.profiles[0] for profile in reversed(self.model.profiles): if self.ct[profile][aid] >= self.model.lbda \ or eq(self.ct[profile][aid], self.model.lbda): return self.model.categories_profiles[profile].value.upper return self.model.categories_profiles[profile].value.lower def build_assignments_table(self): self.good = 0 self.aa = AlternativesAssignments() for aa in self.aa_ori.values(): aid = aa.id cat = self.get_alternative_assignment(aid) self.aa.append(AlternativeAssignment(aid, cat)) cat_ori = aa.category_id if cat == cat_ori: self.good += 1 def build_concordance_table(self): self.ct = { bp.id: dict() for bp in self.model.bpt } for aid, bp in product(self.aa_ori.keys(), self.model.bpt): ap = self.pt_sorted[aid] conc = self.model.concordance(ap, bp) self.ct[bp.id][aid] = conc def rebuild_tables(self): self.build_concordance_table() self.build_assignments_table() def update_tables(self, profile, cid, old, new): direction = self.model.criteria[cid].direction if old > new: if direction == 1: down, up = True, False else: down, up = False, True w = self.model.cv[cid].value * direction else: if direction == 1: down, up = False, True else: down, up = True, False w = -self.model.cv[cid].value * direction alts, perfs = self.pt_sorted.get_middle(cid, old, new, up, down) for a in alts: self.ct[profile][a] += w old_cat = self.aa[a].category_id new_cat = self.get_alternative_assignment(a) ori_cat = self.aa_ori[a].category_id if old_cat == new_cat: continue elif old_cat == ori_cat: self.good -= 1 elif new_cat == ori_cat: self.good += 1 self.aa[a].category_id = new_cat def optimize_profile(self, profile, below, above, cat_b, cat_a): cids = self.model.criteria.keys() random.shuffle(cids) for cid in cids: ct = self.ct[profile.id] perf_profile = profile.performances[cid] perf_above = above.performances[cid] perf_below = below.performances[cid] h_below = self.compute_below_histogram(cid, perf_profile, perf_below, cat_b, cat_a, ct) h_above = self.compute_above_histogram(cid, perf_profile, perf_above, cat_b, cat_a, ct) h = h_below h.update(h_above) if not h: continue i = self.histogram_choose(h, perf_profile) r = random.uniform(0, 1) if r <= h[i]: self.update_tables(profile.id, cid, perf_profile, i) profile.performances[cid] = i def get_below_and_above_profiles(self, i): profiles = self.model.profiles bpt = self.model.bpt if i == 0: below = self.b0 else: below = bpt[profiles[i-1]] if i == self.nprofiles-1: above = self.bp else: above = bpt[profiles[i+1]] return below, above def optimize(self): profiles = self.model.profiles for i, profile in enumerate(profiles): pperfs = self.model.bpt[profile] below, above = self.get_below_and_above_profiles(i) cat_b, cat_a = self.cat_ranked[i], self.cat_ranked[i+1] self.optimize_profile(pperfs, below, above, cat_b, cat_a) return self.good / self.na