def allocate(self, avoid_conflicts=True): p = PanelMaker() panels = p.form_panels(self.adjudicators, len(self.debates)) assert len(self.debates) <= len(panels) self.debates.sort(key=lambda d: self.get_debate_energy(d), reverse=True) panels.sort(key=lambda p: p.get_energy(), reverse=True) self.pairings = list(zip(self.debates, panels)) if avoid_conflicts: for i, (debate, panel) in enumerate(self.pairings): if panel.conflicts(debate): j = self.search_swap(i, list(range(i, 0, -1))) if j is None: j = self.search_swap(i, list(range(i + 1, len(panels)))) from adjallocation.allocation import AdjudicatorAllocation allocation = [] for debate, panel in self.pairings: a = AdjudicatorAllocation(debate) a.chair = panel[0] a.panel = panel[1:] allocation.append(a) return allocation
def allocate(self, avoid_conflicts=True): p = PanelMaker() panels = p.form_panels(self.adjudicators, len(self.debates)) assert len(self.debates) <= len(panels) self.debates.sort(key=lambda d: self.get_debate_energy(d), reverse=True) panels.sort(key=lambda p: p.get_energy(), reverse=True) self.pairings = list(zip(self.debates, panels)) if avoid_conflicts: for i, (debate, panel) in enumerate(self.pairings): if panel.conflicts(debate): j = self.search_swap(i, list(range(i, 0, -1))) if j is None: j = self.search_swap(i, list(range(i+1, len(panels)))) from adjallocation.allocation import AdjudicatorAllocation allocation = [] for debate, panel in self.pairings: a = AdjudicatorAllocation(debate) a.chair = panel[0] a.panel = panel[1:] allocation.append(a) return allocation
def allocate(self): from adjallocation.allocation import AdjudicatorAllocation debates = self.debates adjs = self.adjudicators result = [] for debate in debates: alloc = AdjudicatorAllocation(debate) alloc.chair = adjs.pop(0) result.append(alloc) while len(adjs) >= 2: for alloc in reversed(result): if len(adjs) >= 2: alloc.panel.append(adjs.pop(0)) alloc.panel.append(adjs.pop(0)) return result
def allocate(self): from adjallocation.allocation import AdjudicatorAllocation # Remove trainees self.adjudicators = [a for a in self.adjudicators if a.score >= self.MIN_VOTING_SCORE] logger.info("There are %s non-trainee adjudidcators", len(self.adjudicators)) # Sort adjudicators and debates in descending score/importance self.adjudicators_sorted = list(self.adjudicators) shuffle(self.adjudicators_sorted) # Randomize equally-ranked judges self.adjudicators_sorted.sort(key=lambda a: a.score, reverse=True) self.debates_sorted = list(self.debates) self.debates_sorted.sort(key=lambda a: a.importance, reverse=True) n_adjudicators = len(self.adjudicators) n_debates = len(self.debates) logger.info("There are %s debates", n_debates) if n_adjudicators < n_debates: logger.critical("There are %d debates but only %d adjudicators", n_debates, n_adjudicators) # If not setting panellists allocate all debates a solo chair if self.NO_PANELLISTS is True: n_solos = n_debates else: n_solos = n_debates - (n_adjudicators - n_debates)//2 # get adjudicators that can adjudicate solo chairs = self.adjudicators_sorted[:n_solos] logger.info("There are %s chairs", len(chairs)) # get debates that will be judged by solo adjudicators chair_debates = self.debates_sorted[:len(chairs)] panel_debates = self.debates_sorted[len(chairs):] panellists = [a for a in self.adjudicators_sorted if a not in chairs] logger.info("There are %s panellists", len(panellists)) # For tournaments with duplicate allocations there are typically not # enough adjudicators to form full panels, so don't crash in that case if not self.DUPLICATE_ALLOCATIONS and len(panellists) < len(panel_debates) * 3: logger.critical("There are %d panel debates but only %d available panellists (less than %d)", len(panel_debates), len(panellists), len(panel_debates) * 3) m = Munkres() # TODO I think "chairs" actually means "solos", rename variables if correct if len(chairs) > 0: logger.info("costing chairs") n = len(chairs) cost_matrix = [[0] * n for i in range(n)] for i, debate in enumerate(chair_debates): for j, adj in enumerate(chairs): cost_matrix[i][j] = self.calc_cost(debate, adj) logger.info("optimizing") indexes = m.compute(cost_matrix) total_cost = 0 for r, c in indexes: total_cost += cost_matrix[r][c] logger.info('total cost for solos %f', total_cost) logger.info('number of solo debates %d', n) result = ((chair_debates[i], chairs[j]) for i, j in indexes if i < len(chair_debates)) alloc = [AdjudicatorAllocation(d, c) for d, c in result] for a in alloc: logger.info("%s %s", a.debate, a.chair) else: logger.info("No solo adjudicators.") alloc = [] # Skip the next step if there is the panellist position is disabled if self.NO_PANELLISTS is True: npan = False else: n = len(panel_debates) npan = len(panellists) if npan: logger.info("costing panellists") # matrix is square, dummy debates have cost 0 cost_matrix = [[0] * npan for i in range(npan)] for i, debate in enumerate(panel_debates): for j in range(3): # for the top half of these debates, the final panellist # can be of lower quality than the other 2 if i < npan/2 and j == 2: adjustment = -1.0 else: adjustment = 0 for k, adj in enumerate(panellists): cost_matrix[3*i+j][k] = self.calc_cost(debate, adj, adjustment) logger.info("optimizing") indexes = m.compute(cost_matrix) cost = 0 for r, c in indexes: cost += cost_matrix[r][c] logger.info('total cost for panellists %f', cost) # transfer the indices to the debates # the debate corresponding to row r is floor(r/3) (i.e. r // 3) p = [[] for i in range(n)] for r, c in indexes[:n*3]: p[r // 3].append(panellists[c]) # create the corresponding adjudicator allocations, making sure # that the chair is the highest-ranked adjudicator in the panel for i, d in enumerate(panel_debates): a = AdjudicatorAllocation(d) p[i].sort(key=lambda a: a.score, reverse=True) a.chair = p[i].pop(0) a.panellists = p[i] alloc.append(a) for a in alloc[len(chairs):]: logger.info("%s %s %s", a.debate, a.chair, a.panellists) return alloc
def allocate(self): from adjallocation.allocation import AdjudicatorAllocation self.populate_adj_scores(self.adjudicators) # Remove trainees self.adjudicators = [a for a in self.adjudicators if a._hungarian_score >= self.MIN_VOTING_SCORE] logger.info("There are %s non-trainee adjudidcators", len(self.adjudicators)) # Sort adjudicators and debates in descending score/importance self.adjudicators_sorted = list(self.adjudicators) shuffle(self.adjudicators_sorted) # Randomize equally-ranked judges self.adjudicators_sorted.sort(key=lambda a: a._hungarian_score, reverse=True) self.debates_sorted = list(self.debates) self.debates_sorted.sort(key=lambda a: a.importance, reverse=True) n_adjudicators = len(self.adjudicators) n_debates = len(self.debates) logger.info("There are %s debates", n_debates) if n_adjudicators < n_debates: logger.error("There are %d debates but only %d adjudicators", n_debates, n_adjudicators) # If not setting panellists allocate all debates a solo chair if self.NO_PANELLISTS is True: n_solos = n_debates else: n_solos = n_debates - (n_adjudicators - n_debates)//2 # get adjudicators that can adjudicate solo chairs = self.adjudicators_sorted[:n_solos] logger.info("There are %s chairs", len(chairs)) # get debates that will be judged by solo adjudicators chair_debates = self.debates_sorted[:len(chairs)] panel_debates = self.debates_sorted[len(chairs):] panellists = [a for a in self.adjudicators_sorted if a not in chairs] logger.info("There are %s panellists", len(panellists)) # For tournaments with duplicate allocations there are typically not # enough adjudicators to form full panels, so don't crash in that case if not self.DUPLICATE_ALLOCATIONS and len(panellists) < len(panel_debates) * 3: logger.error("There are %d panel debates but only %d available panellists (less than %d)", len(panel_debates), len(panellists), len(panel_debates) * 3) m = Munkres() # TODO I think "chairs" actually means "solos", rename variables if correct if len(chairs) > 0: logger.info("costing chairs") n = len(chairs) cost_matrix = [[0] * n for i in range(n)] for i, debate in enumerate(chair_debates): for j, adj in enumerate(chairs): cost_matrix[i][j] = self.calc_cost(debate, adj) logger.info("optimizing") indexes = m.compute(cost_matrix) total_cost = 0 for r, c in indexes: total_cost += cost_matrix[r][c] logger.info('total cost for solos %f', total_cost) logger.info('number of solo debates %d', n) result = ((chair_debates[i], chairs[j]) for i, j in indexes if i < len(chair_debates)) alloc = [AdjudicatorAllocation(d, c) for d, c in result] for a in alloc: logger.info("%s %s", a.debate, a.chair) else: logger.info("No solo adjudicators.") alloc = [] # Skip the next step if there is the panellist position is disabled if self.NO_PANELLISTS is True: npan = False else: n = len(panel_debates) npan = len(panellists) if npan: logger.info("costing panellists") # matrix is square, dummy debates have cost 0 cost_matrix = [[0] * npan for i in range(npan)] for i, debate in enumerate(panel_debates): for j in range(3): # for the top half of these debates, the final panellist # can be of lower quality than the other 2 if i < npan/2 and j == 2: adjustment = -1.0 else: adjustment = 0 for k, adj in enumerate(panellists): cost_matrix[3*i+j][k] = self.calc_cost(debate, adj, adjustment) logger.info("optimizing") indexes = m.compute(cost_matrix) cost = 0 for r, c in indexes: cost += cost_matrix[r][c] logger.info('total cost for panellists %f', cost) # transfer the indices to the debates # the debate corresponding to row r is floor(r/3) (i.e. r // 3) p = [[] for i in range(n)] for r, c in indexes[:n*3]: p[r // 3].append(panellists[c]) # create the corresponding adjudicator allocations, making sure # that the chair is the highest-ranked adjudicator in the panel for i, d in enumerate(panel_debates): a = AdjudicatorAllocation(d) p[i].sort(key=lambda a: a._hungarian_score, reverse=True) a.chair = p[i].pop(0) a.panellists = p[i] alloc.append(a) for a in alloc[len(chairs):]: logger.info("%s %s %s", a.debate, a.chair, a.panellists) return alloc