''' File: prob_voting_method.py Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu) Date: April 14, 2024 The ProbabilisticVotingMethod class and helper functions for probabilistic voting methods.'''importfunctoolsimportnumpyasnpimportinspect
[docs]classProbVotingMethod(object):""" A class to add functionality to probabilistic voting methods Args: pvm (function): An implementation of a probabilistic voting method. The function should accept any type of profile, and a keyword parameter ``curr_cands`` to find the winner after restricting to ``curr_cands``. name (string): The human-readable name of the social welfare function. Returns: A dictionary that represents the probability on the set of candidates. """def__init__(self,pvm,name=None):self.pvm=pvmself.name=nameself.algorithm=Nonefunctools.update_wrapper(self,pvm)def__call__(self,edata,curr_cands=None,**kwargs):if(curr_candsisnotNoneandlen(curr_cands)==0)orlen(edata.candidates)==0:return{}returnself.pvm(edata,curr_cands=curr_cands,**kwargs)
[docs]defsupport(self,edata,curr_cands=None,**kwargs):""" Return the sorted list of the set of candidates that have non-zero probability. """if(curr_candsisnotNoneandlen(curr_cands)==0)orlen(edata.candidates)==0:return[]prob=self.pvm(edata,curr_cands=curr_cands,**kwargs)returnsorted([cforc,prinprob.items()ifpr>0])
[docs]defchoose(self,edata,curr_cands=None,**kwargs):""" Return a randomly chosen element according to the probability. """prob=self.pvm(edata,curr_cands=curr_cands,**kwargs)# choose a candidate according to the probability distribution probcands=list(prob.keys())probs=[prob[c]forcincands]returnnp.random.choice(cands,p=probs)
[docs]defdisplay(self,edata,curr_cands=None,cmap=None,**kwargs):""" Display the winning set of candidates. """cmap=cmapifcmapisnotNoneelseedata.cmapprob=self.__call__(edata,curr_cands=curr_cands,**kwargs)ifprobisNone:# some voting methods may return None if, for instance, it is taking long to compute the winner.print(f"{self.name} probability is not available")else:w_str=f"{self.name} probability is "print(w_str+"{"+", ".join([f"{str(cmap[c])}: {round(pr,3)}"forc,prinprob.items()])+"}")
[docs]defset_name(self,new_name):"""Set the name of the social welfare function."""self.name=new_name
[docs]defset_algorithm(self,algorithm):""" Set the algorithm for the voting method if 'algorithm' is an accepted keyword parameter. Args: algorithm: The algorithm to set for the voting method. """params=inspect.signature(self.pvm).parametersif'algorithm'inparamsandparams['algorithm'].kindin[inspect.Parameter.KEYWORD_ONLY,inspect.Parameter.POSITIONAL_OR_KEYWORD]:self.algorithm=algorithmelse:raiseValueError(f"The method {self.name} does not accept 'algorithm' as a parameter.")
def__str__(self):returnf"{self.name}"
[docs]defpvm(name=None):""" A decorator used when creating a social welfare function. """defwrapper(f):returnProbVotingMethod(f,name=name)returnwrapper