Exemplo n.º 1
0
 def altpw(self,
           username: str,
           row: str,
           col: str,
           val: float,
           createUnknownUser=True) -> None:
     '''
     Does a pairwise comparison of the alts.  If there is not a pairwise comparison
     object being used to prioritize the children, we create one first.
     :param username: The user to perform the comparison on.
     :param row:  The name of the row alt of the comparison
     :param col:  The name of the column alt of the comparison
     :param val: The comparison value
     :param createUnknownUser: If True, and username did not exist, it will be created and then the vote set.
     Otherwise if the user did not exist, will raise an exception.
     :return:
     Nothing
     :raises ValueError: If the user did not exist and createUnknownUser is False.
     '''
     if not isinstance(self.alt_prioritizer, Pairwise):
         self.alt_prioritizer = Pairwise(self.alt_names)
     self.alt_prioritizer.vote(username,
                               row,
                               col,
                               val,
                               createUnknownUser=createUnknownUser)
Exemplo n.º 2
0
 def test_sim_pw(self):
     pw = np.array([[1, 2, 4], [1 / 2, 1, 2], [1 / 4, 1 / 3, 1]])
     mc = MCAnp()
     pwobj = Pairwise(alts=['alt ' + str(i) for i in range(3)])
     pwobj.vote_matrix(user_name='u1', val=pw)
     s1 = mc.sim(pwobj)
     #print(s1)
     err = 0.5
     npt.assert_allclose([4 / 7, 2 / 7, 1 / 7], s1, rtol=err)
     # Next I need to test with a full sim with multiple count
     sim1 = mc.sim(pw, count=20)
     self.assertEqual(20, len(sim1.df))
     print(sim1)
Exemplo n.º 3
0
 def sim_pw_fill(self,
                 pwsrc: Pairwise,
                 pwdest: Pairwise = None) -> Pairwise:
     """
     Fills in the pairwise comparison structure of pwdest with noisy pairwise data from pwsrc.
     If pwdest is None, we create one first, then fill in.  In either case, we return the pwdest
     object with new noisy data in it.
     """
     if pwdest is None:
         pwdest = deepcopy(pwsrc)
     for user in pwsrc.usernames():
         srcmat = pwsrc.matrix(user)
         destmat = pwdest.matrix(user)
         self.sim_pwmat_fill(srcmat, destmat)
     return pwdest
Exemplo n.º 4
0
 def sim_pw(self,
            pwsrc: Pairwise,
            pwdest: Pairwise,
            pritype: PriorityType = None) -> np.ndarray:
     """
     Performs a simulation on a pairwise comparison matrix object and returns the
     resulting priorities
     """
     pwdest = self.sim_pw_fill(pwsrc, pwdest)
     return pwdest.priority(self.username, pritype)
Exemplo n.º 5
0
    def __init__(self, parent, name:str, alt_names):
        '''
        Initial a new AHPTreeNode

        :param parent: The parent AHPTree this AHPTreeNode is in.

        :param name: The string name of this node.  It should be unique in its parent tree.

        :param alt_names: The alternatives we are comparing in the AHPTree.  As currently implemented
        the parent tree has the list of alternatives and we pass that object to the nodes.  This allows us
        to add new alternatives once in the parent tree and the addition cascades down.
        '''
        self.parent = parent
        self.children = []
        nalts = len(alt_names)
        self.alt_scores = pd.Series(data=[0]*nalts, index=alt_names)
        self.child_prioritizer = Direct()
        self.alt_prioritizer = Direct(alt_names)
        self.alt_names = alt_names
        self.alt_scores_manually_set=False
        self.name = name
Exemplo n.º 6
0
class AHPTreeNode:
    '''
    Represents a node in an AHPTree class
    '''
    def __init__(self, parent, name:str, alt_names):
        '''
        Initial a new AHPTreeNode

        :param parent: The parent AHPTree this AHPTreeNode is in.

        :param name: The string name of this node.  It should be unique in its parent tree.

        :param alt_names: The alternatives we are comparing in the AHPTree.  As currently implemented
        the parent tree has the list of alternatives and we pass that object to the nodes.  This allows us
        to add new alternatives once in the parent tree and the addition cascades down.
        '''
        self.parent = parent
        self.children = []
        nalts = len(alt_names)
        self.alt_scores = pd.Series(data=[0]*nalts, index=alt_names)
        self.child_prioritizer = Direct()
        self.alt_prioritizer = Direct(alt_names)
        self.alt_names = alt_names
        self.alt_scores_manually_set=False
        self.name = name

    def has_child(self, name:str)->bool:
        '''
        Returns a boolean telling if this node has a child with the given name.

        :param name: The name of the child to check for.

        :return: True/False if the node has the child by the given name or not.
        '''
        return name in [kid.name for kid in self.children]

    def add_child(self, childname:str)->None:
        '''
        Adds a child to this node.
        :param childname: The string name of the child to add.
        :return:
        Nothing
        :raises ValueError:
        If a child by the given name already existed
        '''
        if self.has_child(childname):
            raise ValueError("Cannot duplicate children names")
        kidnode = AHPTreeNode(self.parent, childname, self.alt_names)
        self.children.append(kidnode)
        self.child_prioritizer.add_alt(childname)

    def childnames(self):
        '''
        Get the names of the children of this node
        :return:
        A list of str's of the names of this nodes children.  If it has no children
        we return the empty list.
        '''
        return [child.name for child in self.children]

    def add_alt(self, alt_name:str)->None:
        '''
        Adds an alternative to the alternatives under this node.
        :param alt_name: The new alternative to add
        :return:
        Nothing
        :raises ValueError:
        If the alternative already existed
        '''
        self.alt_prioritizer.add_alt(alt_name)
        self.alt_scores[alt_name]=0.0
        for kid in self.children:
            kid.add_alt(alt_name)

    def nalts(self)->int:
        '''
        Gets the number of alternatives under this node.
        '''
        return len(self.alt_names)


    def has_children(self)->int:
        '''
        :return:
        A boolean telling if this node has children
        '''
        return len(self.children) > 0

    def synthesize(self, username=None)->None:
        '''
        Synthesizes up the alternative scores below this alternative and stores the
        result in the alt_scores.  However if the node has no children and has had
        it's alternative scores manually set via AHPTreeNode.set_alt_scores, then this
        does nothing.  Otherwise it synthesizes upward.

        :param username: The name of the user (or list of names of the users) to synthesize for.

        :return:
        Nothing
        '''
        if self.has_children():
            nalts = self.nalts()
            rval = pd.Series(data=[0]*nalts, index=self.alt_names)
            kidpris = self.child_prioritizer.priority(username, PriorityType.NORMALIZE)
            if np.sum(np.abs(kidpris)) == 0:
                nkids = len(kidpris)
                for key, value in kidpris.iteritems():
                    kidpris[key]=1.0 / nkids
            for child, childpri in zip(self.children, kidpris):
                child.synthesize(username)
                rval += childpri * child.alt_scores
            self.alt_scores = rval
        else:
            if self.alt_scores_manually_set:
                # Do nothing here, alt scores are already setup
                pass
            else:
                self.alt_scores = self.alt_prioritizer.priority(username)

    def set_alt_scores(self, new_scores:dict):
        '''
        Used to manually set (or unset) alternative scores.  If new_scores is None, it unsets
        the manually set values, so that the next call to AHPTreeNode.synthesize() will actually
        synthesize the scores and not use the manually set values.
        :param new_scores: If None, it means undo the manual setting of the scores, otherwise
        it loops over each key, value pair and sets the score in AHPTreeNode.alt_scores
        :return:
        '''
        if new_scores is None:
            self.alt_scores_manually_set = False
        else:
            self.alt_scores_manually_set=True
            #self.alt_scores = pd.Series([0]*self.nalts(), index=self.alt_names, dtype=float)
            if isinstance(new_scores, dict):
                for key, value in new_scores.items():
                    if key not in self.alt_scores.index:
                        raise ValueError("Tried to score alt "+key+" that did not exist.")
                    self.alt_scores[key] = value
            else:
                raise ValueError("Do not know how to set alt scores from type "+type(new_scores))

    def get_nodes_under_hash(self, rval:dict = None)->dict:
        '''
        Returns a dictionary of nodeName:AHPTreeNode of the nodes under this node.  It includes this node as well.
        :param rval: If passed in, we add the dictionary items to this dictionary
        :return: The dictionary of name:AHPTreeNode objects
        '''
        if rval is None:
            rval = {}
        rval[self.name] = self
        for child in self.children:
            child.get_nodes_under_hash(rval)
        return rval

    def nodepw(self, username:str, row:str, col:str, val:float, createUnknownUser=True)->None:
        '''
        Does a pairwise comparison of the children.  If there is not a pairwise comparison
        object being used to prioritize the children, we create one first.
        :param username: The user to perform the comparison on.
        :param row:  The name of the row node of the comparison
        :param col:  The name of the column node of the comparison
        :param val: The comparison value
        :param createUnknownUser: If True, and username did not exist, it will be created and then the vote set.
        Otherwise if the user did not exist, will raise an exception.
        :return:
        Nothing
        :raises ValueError: If the user did not exist and createUnknownUser is False.
        '''
        if not isinstance(self.child_prioritizer, Pairwise):
            self.child_prioritizer = Pairwise(self.childnames())
        self.child_prioritizer.vote(username, row, col, val, createUnknownUser=createUnknownUser)

    def altpw(self, username:str, row:str, col:str, val:float, createUnknownUser=True)->None:
        '''
        Does a pairwise comparison of the alts.  If there is not a pairwise comparison
        object being used to prioritize the children, we create one first.
        :param username: The user to perform the comparison on.
        :param row:  The name of the row alt of the comparison
        :param col:  The name of the column alt of the comparison
        :param val: The comparison value
        :param createUnknownUser: If True, and username did not exist, it will be created and then the vote set.
        Otherwise if the user did not exist, will raise an exception.
        :return:
        Nothing
        :raises ValueError: If the user did not exist and createUnknownUser is False.
        '''
        if not isinstance(self.alt_prioritizer, Pairwise):
            self.alt_prioritizer = Pairwise(self.alt_names)
        self.alt_prioritizer.vote(username, row, col, val, createUnknownUser=createUnknownUser)

    def add_user(self, user:str)->None:
        '''
        Adds a user to the prioritizers below this
        :param user: The name of the user to add
        :return:
        Nothing
        :raises ValueError: If the user already existed
        '''
        self.child_prioritizer.add_user(user)
        self.alt_prioritizer.add_user(user)

    def alt_direct(self, node, val):
        '''
        Manually sets the alternative score.  See AHPTreeNode.set_alt_scores() for more info.
        :param node:
        :param val:
        :return:
        '''
        self.set_alt_scores({node:val})

    def _repr_html(self, tab=""):
        '''
        Used by Jupyter to pretty print an instance of AHPTreeNode
        :param tab: How many tabs should we indent?
        :return:
        The html string pretty print version of this
        '''
        rval = tab+"<li><b>Node:</b>"+self.name+"\n"
        if self.has_children():
            # Append child prioritizer info
            rval += self.child_prioritizer._repr_html(tab+"\t")
        if self.has_children():
            rval += tab+"<ul>\n"
            for child in self.children:
                rval += child._repr_html(tab+"\t")
            rval += "</ul>\n"
        else:
            # Should connect to alternatives, let's just report scores
            altscoresstr = tab+"\t\t"+str(self.alt_scores)+"\n"
            altscoresstr = re.sub("\n", "\n"+tab+"\t\t", altscoresstr)
            altscoresstr = altscoresstr.rstrip()
            rval += tab+"\t"+"<ul><li>AltScores=\n"+altscoresstr+"\n"
            rval += tab+"\t"+"</ul>\n"
        return rval

    def usernames(self, rval:list=None)->list:
        '''
        Returns the names of all users involved in this AHPTreeNode
        :param rval: If not None, we add the names to this list
        :return:
        List of str user names.
        '''
        if rval is None:
            rval = []
        if self.child_prioritizer is not None:
            users = self.child_prioritizer.usernames()
            for user in users:
                if user not in rval:
                    rval.append(user)
        if self.alt_prioritizer is not None:
            for user in self.alt_prioritizer.usernames():
                if user not in rval:
                    rval.append(user)
        return rval
Exemplo n.º 7
0
 def test_crud(self):
     alts = ['alt1', 'alt2', 'alt3']
     a1, a2, a3 = alts
     pw = Pairwise(alts=alts)
     u1 = 'Bill'
     pw.add_user(u1)
     self.assertTrue(pw.is_user(u1))
     mat = pw.matrix(u1)
     assert_array_equal(mat, np.identity(3))
     pw.vote('Bill', a1, a2, 3)
     assert_array_equal(mat, [[1, 3, 0], [1. / 3, 1, 0], [0, 0, 1]])
     pw.unvote('Bill', a1, a2)
     assert_array_equal(mat, np.identity(3))
     u2 = 'Leanne'
     pw.add_user(u2)
     pw.vote(u2, a1, a2, 5)
     pw.vote(u1, a1, a2, 3)
     group = pw.matrix(None)
     assert_allclose(
         group, [[1, np.sqrt(15), 0], [1 / np.sqrt(15), 1, 0], [0, 0, 1]])
     pw.vote(u1, a2, a3, 2)
     pw.vote(u1, a1, a3, 6)
     pri = pw.priority(u1)
     assert_allclose(pri, [6 / 9, 2 / 9, 1 / 9])
Exemplo n.º 8
0
 def test_addalt(self):
     pw = Pairwise()
     a1, a2, a3 = ["alt1", "a2", "a3"]
     u1, u2 = ["Bill", "Leanne"]
     pw.add_alt(a1)
     pw.add_user(u1)
     pw.add_alt(a2)
     m = pw.matrix(u1)
     assert_array_equal(m, np.identity(2))
     pw.vote(u1, a1, a2, 5)
     assert_allclose(m, [[1, 5.0], [1 / 5, 1]])