예제 #1
0
    def test_bound_dual(self):
        bb = BranchAndBound(infeasible2)
        bb.root_node.lp += np.matrix(
            [[0, -1, -1]]) * bb.root_node.lp.getVarByName('x') >= CyLPArray(
                [-2.5])
        bb.solve()
        terminal_nodes = bb.tree.get_leaves(0)
        infeasible_nodes = [
            n for n in terminal_nodes if n.lp_feasible is False
        ]
        n = infeasible_nodes[0]
        lp = bb._bound_dual(n.lp)

        # test that we get a CyClpSimplex object back
        self.assertTrue(isinstance(lp, CyClpSimplex),
                        'should return CyClpSimplex instance')

        # same variables plus extra 's'
        self.assertTrue(
            {v.name
             for v in lp.variables} == {'x', 's_0', 's_1'},
            'x should already exist and s_1 and s_2 should be added')
        old_x = n.lp.getVarByName('x')
        new_x, s_0, s_1 = lp.getVarByName('x'), lp.getVarByName(
            's_0'), lp.getVarByName('s_1')

        # same variable bounds, plus s >= 0
        self.assertTrue(
            all(new_x.lower == old_x.lower)
            and all(new_x.upper == old_x.upper),
            'x should have the same bounds')
        self.assertTrue(
            all(s_0.lower == [0, 0]) and all(s_0.upper > [1e300, 1e300]),
            's_0 >= 0')
        self.assertTrue(
            all(s_1.lower == [0]) and all(s_1.upper > 1e300), 's_1 >= 0')

        # same constraints, plus slack s
        self.assertTrue(lp.nConstraints == 3,
                        'should have same number of constraints')
        self.assertTrue(
            (lp.constraints[0].varCoefs[new_x] == np.array([[-1, -1, 0],
                                                            [0, 0,
                                                             -1]])).all(),
            'x coefs should stay same')
        self.assertTrue((lp.constraints[0].varCoefs[s_0] == np.matrix(
            np.identity(2))).all(), 's_0 should have coef of 2-D identity')
        self.assertTrue(
            all(lp.constraints[1].varCoefs[new_x] == np.array([0, -1, -1])),
            'x coefs should stay same')
        self.assertTrue(
            lp.constraints[1].varCoefs[s_1] == np.matrix(np.identity(1)),
            's_0 should have coef of 1-D identity')
        self.assertTrue(
            all(lp.constraints[0].lower == np.array([1, -1]))
            and all(lp.constraints[0].upper >= np.array([1e300])),
            'constraint bounds should remain same')
        self.assertTrue(
            lp.constraints[1].lower == np.array([-2.5])
            and lp.constraints[1].upper >= np.array([1e300]),
            'constraint bounds should remain same')

        # same objective, plus large s coefficient
        self.assertTrue(
            all(lp.objective == np.array([-1, -1, 0, bb._M, bb._M, bb._M])))

        # problem is now feasible
        self.assertTrue(lp.getStatusCode() == 0, 'lp should now be optimal')
예제 #2
0
    def test_dual_bound(self):

        # Ensure that BranchAndBound.dual_bound generates the dual function
        # that we saw in ISE 418 HW 3 problem 1
        bb = BranchAndBound(h3p1)
        bb.solve()
        bound = bb.dual_bound(CyLPArray([3.5, -3.5]))
        self.assertTrue(bb.objective_value == bound,
                        'dual should be strong at original rhs')

        prob = {
            0: h3p1_0,
            1: h3p1_1,
            2: h3p1_2,
            3: h3p1_3,
            4: h3p1_4,
            5: h3p1_5
        }
        sol_new = {0: 0, 1: 1, 2: 1, 3: 2, 4: 2, 5: 3}
        sol_bound = {0: 0, 1: .5, 2: 1, 3: 2, 4: 2, 5: 2.5}
        for beta in range(6):
            new_bb = BranchAndBound(prob[beta])
            new_bb.solve()
            bound = bb.dual_bound(CyLPArray(np.array([beta, -beta])))
            self.assertTrue(
                isclose(sol_new[beta], new_bb.objective_value, abs_tol=.01),
                'new branch and bound objective should match expected')
            self.assertTrue(isclose(sol_bound[beta], bound),
                            'new dual bound value should match expected')
            self.assertTrue(
                bound <= new_bb.objective_value + .01,
                'dual bound value should be at most the value function for this rhs'
            )

        bb = BranchAndBound(small_branch)
        bb.solve()
        bound = bb.dual_bound(CyLPArray([2.5, 4.5]))

        # just make sure the dual bound works here too
        self.assertTrue(
            bound <= -5.99,
            'dual bound value should be at most the value function for this rhs'
        )

        # check function calls
        bb = BranchAndBound(small_branch)
        bb.solve()
        bound_duals = [
            bb._bound_dual(n.lp)
            for n in bb.tree.get_node_instances([6, 12, 10, 8, 2])
        ]
        with patch.object(bb, '_bound_dual') as bd:
            bd.side_effect = bound_duals
            bound = bb.dual_bound(CyLPArray([3, 3]))
            self.assertTrue(bd.call_count == 5)

        bb = BranchAndBound(small_branch)
        bb.solve()
        bound = bb.dual_bound(CyLPArray([3, 3]))
        with patch.object(bb, '_bound_dual') as bd:
            bound = bb.dual_bound(CyLPArray([1, 1]))
            self.assertFalse(bd.called)