def test_keys(): """ Check a Matching can have its `keys` accessed. """ matching = Matching() assert list(matching.keys()) == [] matching = Matching(dictionary) assert list(matching.keys()) == suitors
class StableMarriage(BaseGame): """ A class for solving instances of the stable marriage problem (SM). Parameters ---------- suitors : list of Player The suitors in the game. Each suitor must rank all elements in ``reviewers``. reviewers : list of Player The reviewers in the game. Each reviewer must rank all elements in ``suitors``. Attributes ---------- matching : Matching or None Once the game is solved, a matching is available. This uses the suitors and reviewers as keys and values, respectively, in a ``Matching`` object. Initialises as `None`. blocking_pairs : list of (Player, Player) The suitor-reviewer pairs that both prefer one another to their current match. Initialises as ``None``. """ def __init__(self, suitors, reviewers): suitors, reviewers = copy.deepcopy([suitors, reviewers]) self.suitors = suitors self.reviewers = reviewers super().__init__() self._check_inputs() @classmethod def create_from_dictionaries(cls, suitor_prefs, reviewer_prefs): """ Create an instance of SM from two preference dictionaries. """ suitors, reviewers = _make_players(suitor_prefs, reviewer_prefs) game = cls(suitors, reviewers) return game def solve(self, optimal="suitor"): """ Solve the instance of SM using either the suitor- or reviewer-oriented Gale-Shapley algorithm. Return the matching. """ self.matching = Matching( stable_marriage(self.suitors, self.reviewers, optimal)) return self.matching def check_stability(self): """ Check for the existence of any blocking pairs in the current matching, thus determining the stability of the matching. """ blocking_pairs = [] for suitor in self.suitors: for reviewer in self.reviewers: if suitor.prefers(reviewer, suitor.matching) and reviewer.prefers( suitor, reviewer.matching): blocking_pairs.append((suitor, reviewer)) self.blocking_pairs = blocking_pairs return not any(blocking_pairs) def check_validity(self): """ Check whether the current matching is valid. """ self._check_all_matched() self._check_matching_consistent() return True def _check_all_matched(self): """ Check everyone has a match. """ errors = [] for player in self.suitors + self.reviewers: if player.matching is None: errors.append(ValueError(f"{player} is unmatched.")) if player not in list(self.matching.keys()) + list( self.matching.values()): errors.append( ValueError(f"{player} does not appear in matching.")) if errors: raise Exception(*errors) return True def _check_matching_consistent(self): """ Check that the game matching is consistent with the players. """ errors = [] matching = self.matching for suitor in self.suitors: if suitor.matching != matching[suitor]: errors.append( ValueError( f"{suitor} is matched to {suitor.matching} but matching" f" says {matching[suitor]}.")) for reviewer in self.reviewers: suitor = [s for s in matching if matching[s] == reviewer][0] if reviewer.matching != suitor: errors.append( ValueError( f"{reviewer} is matched to {reviewer.matching} but " f"matching says {suitor}.")) if errors: raise Exception(*errors) return True def _check_inputs(self): """ Raise an error if any of the conditions of the game have been broken. """ self._check_num_players() for suitor in self.suitors: self._check_player_ranks(suitor) for reviewer in self.reviewers: self._check_player_ranks(reviewer) def _check_num_players(self): """ Check that the number of suitors and reviewers are equal. """ if len(self.suitors) != len(self.reviewers): raise ValueError( "There must be an equal number of suitors and reviewers.") return True def _check_player_ranks(self, player): """ Check that a player has ranked all of the other group. """ others = self.reviewers if player in self.suitors else self.suitors if set(player.prefs) != set(others): raise ValueError( "Every player must rank each name from the other group. " f"{player}: {player.prefs} != {others}") return True
mV41 = Matching(genJpsis, V41) #mV42 = Matching( genJpsis, V42) #print "genJpsi : %d // Size of J/psi v1 : %d // j/psi v2 : %d // j/psi v3 : %d // j/psi v4 : %d"%(jpsi_idx, len(V1), len(V2),len(V3), len(V4)) if (len(mV1) > 0): profile1.Fill(1, 1) for cat in mV1.values(): diff_1.Fill(cat.pt()) else: profile1.Fill(1, 0) #if ( len(mV2) > 0 ) : profile1.Fill(2,1) #else : profile1.Fill(2,0) if (len(mV3) > 0): profile1.Fill(3, 1) for idx in mV3.keys(): diff_3.Fill(genJpsis[idx].pt()) else: profile1.Fill(3, 0) if (len(mV40) > 0): profile1.Fill(4, 1) for idx in mV40.keys(): diff_40.Fill(genJpsis[idx].pt()) else: profile1.Fill(4, 0) if (len(mV41) > 0): profile1.Fill(5, 1) for idx in mV41.keys(): diff_41.Fill(genJpsis[idx].pt()) else: profile1.Fill(5, 0)