def test_solver4(self):
     '''
     Tests 6 papers, 6 reviewers.   Reviewers review min: 2, max: 3 papers.   Each paper needs 2 reviews.
     All scores set to 1 so that any match that does not violate constraints is optimal
     Purpose:  Honors minimums == 2 for all reviewers
     '''
     cost_matrix = np.array([[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]])
     constraint_matrix = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0],
                                   [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0],
                                   [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]])
     graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
     solver = AssignmentGraph([2, 2, 2, 2, 2, 2], [3, 3, 3, 3, 3, 3],
                              [2, 2, 2, 2, 2, 2], cost_matrix,
                              constraint_matrix, graph_builder)
     res = solver.solve()
     assert res.shape == (6, 6)
     # make sure every reviewer is reviewing 2 papers
     nrows, ncols = res.shape
     for rix in range(nrows):
         reviewer_count_reviews = 0
         for pix in range(ncols):
             if res[rix, pix] != 0:
                 reviewer_count_reviews += 1
         assert reviewer_count_reviews == 2
     self.check_solution(solver, solver.min_cost_flow.OptimalCost())
 def test_solver_respects_constraints(self):
     '''
     Tests 5 papers, 4 reviewers.   Reviewers review min: 1, max: 3 papers.   Each paper needs 2 reviews.
     Constrained such that:
     Reviewer 0: available for all papers
              1: cannot review papers 2,3
              2: cannot review papers 2,3
              3: cannot review papers 0, 1
     All scores set to 1 so that any match that does not violate constraints is optimal
     Purpose:  Honors constraints in its solution
     '''
     cost_matrix = np.array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1],
                             [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]])
     constraint_matrix = np.array([[0, 0, 0, 0, 0], [0, 0, -1, -1, 0],
                                   [0, 0, -1, -1, 0], [-1, -1, 0, 0, 0]])
     graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
     solver = AssignmentGraph([1, 1, 1, 1], [3, 3, 3, 3], [2, 2, 2, 2, 2],
                              cost_matrix, constraint_matrix, graph_builder)
     res = solver.solve()
     assert res.shape == (4, 5)
     # make sure result does not violate constraints (i.e. no flow at i,j if there is a -1 constraint at i,j
     nrows, ncols = res.shape
     for i in range(nrows):
         for j in range(ncols):
             assert not (
                 constraint_matrix[i, j] == -1 and res[i, j] > 0
             ), "Solution violates constraint at [{},{}]".format(i, j)
     self.print_header()
     self.check_solution(solver, solver.min_cost_flow.OptimalCost())
 def test_solver_find_lowest_cost_and_respect_constraints(self):
     '''
     Tests 5 papers, 4 reviewers.   Reviewers review min: 1, max: 3 papers.   Each paper needs 2 reviews.
     Constrained such that:
     Reviewer 0: available for all papers
              1: cannot review papers 0,3
              2: cannot review papers 3,4
              3: cannot review papers 1,2
     Scores set such that a lowest-cost solution can be found along all reviewer-paper arcs with cost = -10 and no others.
     Purpose:  Finds the lowest cost solution in combination with honoring constraints (i.e. ignores lower-cost paths that are constrained to be ommitted)
     '''
     cost_matrix = np.array([[-10, 1, 1, -10, -10],
                             [-100, -10, -10, -100, 1],
                             [1, -10, -10, -100, -100],
                             [-10, -100, -100, -10, -10]])
     constraint_matrix = np.array([[0, 0, 0, 0, 0], [-1, 0, 0, -1, 0],
                                   [0, 0, 0, -1, -1], [0, -1, -1, 0, 0]])
     graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
     solver = AssignmentGraph([1, 1, 1, 1], [3, 3, 3, 3], [2, 2, 2, 2, 2],
                              cost_matrix, constraint_matrix, graph_builder)
     res = solver.solve()
     assert res.shape == (4, 5)
     # make sure result does not violate constraints (i.e. no flow at i,j if there is a -1 constraint at i,j
     # make sure the score at i,j = -10 if there is flow there.
     nrows, ncols = res.shape
     for i in range(nrows):
         for j in range(ncols):
             assert not (
                 constraint_matrix[i, j] == -1 and res[i, j] > 0
             ), "Solution violates constraint at [{},{}]".format(i, j)
             assert not (
                 res[i, j] > 0 and cost_matrix[i, j] > -10
             ), "Solution contains an arc that is not part of an lowest-cost solution"
     self.print_header()
     self.check_solution(solver, solver.min_cost_flow.OptimalCost())
    def test_solver6(self):
        '''
        Tests 3 papers, 4 reviewers.   Reviewers review min: 2, max: 3 papers.   Each paper needs 3 reviews.
        Reviewer 4 has very high cost.  Other reviewers have 0 cost.
        Purpose:  Make sure all reviewers get at least their minimum
        '''
        num_papers = 3
        num_reviewers = 4
        min_papers_per_reviewer = 2
        max_papers_per_reviewer = 3
        paper_revs_reqd = 3
        cost_matrix = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0],
                                [2000, 2000, 2000]])
        constraint_matrix = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0],
                                      [0, 0, 0]])

        graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
        rev_mins = [min_papers_per_reviewer] * num_reviewers
        rev_maxs = [max_papers_per_reviewer] * num_reviewers
        papers_reqd = [paper_revs_reqd] * num_papers
        solver = AssignmentGraph(rev_mins, rev_maxs, papers_reqd, cost_matrix,
                                 constraint_matrix, graph_builder)
        res = solver.solve()
        assert res.shape == (4, 3)
        # make sure every reviewer has at least 1 paper
        nrows, ncols = res.shape
        for rix in range(nrows):
            reviewer_count_reviews = 0
            for pix in range(ncols):
                if res[rix, pix] != 0:
                    reviewer_count_reviews += 1
            assert reviewer_count_reviews >= 1
        # TestSolver.silent = False
        self.check_solution(solver, solver.min_cost_flow.OptimalCost())
    def test_solver_respects_minimums_icml(self):
        '''
        Based on ICML workshop which produces a match that has users either getting 4 or 6 papers with one user
        getting just 2.
        There are 44 papers, 27 reviewers. 3 users per paper and between 4 and 6 papers per user.
        All reviewers are 0 cost.
        Purpose:  Make sure all reviewers get between their min and max
        TODO Why do all reviewers get either the min or max?  None of them get a number in between!
        '''
        num_papers = 44
        num_reviewers = 27
        min_papers_per_reviewer = 4
        max_papers_per_reviewer = 6
        paper_revs_reqd = 3
        cost_matrix = np.zeros((num_reviewers, num_papers))
        constraint_matrix = np.zeros((num_reviewers, num_papers))

        graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
        rev_mins = [min_papers_per_reviewer] * num_reviewers
        rev_maxs = [max_papers_per_reviewer] * num_reviewers
        papers_reqd = [paper_revs_reqd] * num_papers
        solver = AssignmentGraph(rev_mins, rev_maxs, papers_reqd, cost_matrix,
                                 constraint_matrix, graph_builder)
        res = solver.solve()
        assert res.shape == (27, 44)
        # make sure every reviewer has >= minimum number of papers
        # make sure every reviewer has <= maximum number of papers
        nrows, ncols = res.shape
        total = 0
        num4 = 0
        num6 = 0
        for rix in range(nrows):
            reviewer_count_reviews = 0
            for pix in range(ncols):
                if res[rix, pix] != 0:
                    reviewer_count_reviews += 1
            total += reviewer_count_reviews
            if reviewer_count_reviews == 4:
                num4 += 1
            else:
                num6 += 1
            print("Reviewer: {} has {} papers".format(rix,
                                                      reviewer_count_reviews))
            assert reviewer_count_reviews >= min_papers_per_reviewer
            assert reviewer_count_reviews <= max_papers_per_reviewer
        assert total == num_papers * paper_revs_reqd
        assert total == num4 * 4 + num6 * 6
        print("Total reviews", total, "num 4", num4, "num 6", num6)
 def test_solver_finds_lowest_cost_soln(self):
     '''
     4 reviewers 3 papers.   Papers 0,1 need 1 review; Paper 2 needs 2 reviews.  Reviewers can do max of 2 reviews
     Setup so that lowest cost solution should be
     Reviewer 0 reviews paper 0
              1 reviews paper 1
              2 reviews paper 2
              3 reviews paper 2
     Purpose:  Finds the lowest cost solution
     '''
     cost_matrix = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0], [2, 2, 0]])
     constraint_matrix = np.zeros(np.shape(cost_matrix))
     graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
     solver = AssignmentGraph([1, 1, 1, 1], [2, 2, 2, 2], [1, 1, 2],
                              cost_matrix, constraint_matrix, graph_builder)
     res = solver.solve()
     assert res.shape == (4, 3)
     self.print_header()
     expected_cost = 0
     self.check_solution(solver, expected_cost)
    def test_solver_respects_two_minimum(self):
        '''
        Tests 3 papers, 4 reviewers.   Reviewers review min: 2, max: 3 papers.   Each paper needs 3 reviews.
        Reviewer 4 has very high cost.  Other reviewers have 0 cost.
        Purpose:  Make sure all reviewers (including reviewer 4) get at least their minimum
        '''
        num_papers = 3
        num_reviewers = 4
        min_papers_per_reviewer = 2
        max_papers_per_reviewer = 3
        paper_revs_reqd = 3
        cost_matrix = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0],
                                [2000, 2000, 2000]])
        constraint_matrix = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0],
                                      [0, 0, 0]])

        graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
        rev_mins = [min_papers_per_reviewer] * num_reviewers
        rev_maxs = [max_papers_per_reviewer] * num_reviewers
        papers_reqd = [paper_revs_reqd] * num_papers
        solver = AssignmentGraph(rev_mins, rev_maxs, papers_reqd, cost_matrix,
                                 constraint_matrix, graph_builder)
        res = solver.solve()
        assert res.shape == (4, 3)
        # make sure every reviewer has at least 1 paper
        nrows, ncols = res.shape
        for rix in range(nrows):
            reviewer_count_reviews = 0
            for pix in range(ncols):
                if res[rix, pix] != 0:
                    reviewer_count_reviews += 1
            assert reviewer_count_reviews >= 1
        # TestSolver.silent = False
        print("-----")
        print("Reviewer min: {}, max: {}".format(min_papers_per_reviewer,
                                                 max_papers_per_reviewer))
        print("cost matrix")
        print(cost_matrix)
        print("solution matrix")
        print(res)
Exemple #8
0
    def test_decode(self, test_util):
        '''
        Test that the decode returns a correct assignment dictionary.
        Must manually call the encoder.encode and solver.
        We send a score matrix that forces
        it to choose the expected solution reviewer-0->paper-0, 1->1, 2->2, 3->2
        '''
        score_matrix = np.array([[10, 0, 0], [0, 10, 0], [0, 0, 10],
                                 [0, 0, 10]])
        num_papers = 3
        num_reviewers = 4
        num_reviews_needed_per_paper = 2
        reviewer_max_papers = 2
        reviewer_min_papers = 1
        paper_demands = [1, 1, 2]
        params = Params({
            Params.NUM_PAPERS: num_papers,
            Params.NUM_REVIEWERS: num_reviewers,
            Params.NUM_REVIEWS_NEEDED_PER_PAPER: num_reviews_needed_per_paper,
            Params.REVIEWER_MAX_PAPERS: reviewer_max_papers,
            Params.REVIEWER_MIN_PAPERS: reviewer_min_papers,
            Params.SCORES_CONFIG: {
                Params.SCORES_SPEC: {
                    'affinity': {
                        'weight': 1,
                        'default': 0
                    }
                },
                Params.SCORE_TYPE: Params.MATRIX_SCORE,
                Params.SCORE_MATRIX: score_matrix
            }
        })
        '''
        Test that the decoder produces the expected assignment.   Its necessary to configure
        the inputs to get a predictable solution.   
                         
        '''
        or_client = test_util.client
        conf = ConferenceConfigWithEdges(or_client,
                                         test_util.next_conference_count(),
                                         params)
        papers = conf.get_paper_notes()
        reviewers = conf.reviewers
        config = conf.get_config_note()
        scores_spec = config.content[Configuration.SCORES_SPECIFICATION]
        edge_invitations = PaperReviewerEdgeInvitationIds(
            conf.score_invitation_ids, conf.conf_ids.CONFLICTS_INV_ID,
            conf.conf_ids.CUSTOM_LOAD_INV_ID)
        prd = PaperReviewerData(conf.paper_notes,
                                conf.reviewers,
                                edge_invitations,
                                scores_spec,
                                edge_fetcher=EdgeFetcher(or_client))

        enc = Encoder(prd)
        cost_matrix = enc.cost_matrix
        constraint_matrix = np.zeros(np.shape(cost_matrix))
        graph_builder = GraphBuilder.get_builder('SimpleGraphBuilder')
        solver = AssignmentGraph([reviewer_min_papers] * num_reviewers,
                                 [reviewer_max_papers] * num_reviewers,
                                 paper_demands, cost_matrix, constraint_matrix,
                                 graph_builder)
        solution = solver.solve()
        assignments_by_forum = enc.decode(solution)
        assert assignments_by_forum[papers[0].id][0].user == reviewers[0]
        assert assignments_by_forum[papers[1].id][0].user == reviewers[1]
        assert assignments_by_forum[papers[2].id][0].user == reviewers[2]
        assert assignments_by_forum[papers[2].id][1].user == reviewers[3]