def test_cartesian_product(self): # simple case n = 2 x_min = -np.ones((n, 1)) x_max = -x_min p1 = Polyhedron.from_bounds(x_min, x_max) C = np.ones((1, n)) d = np.zeros((1, 1)) p1.add_equality(C, d) p2 = p1.cartesian_product(p1) # derive the cartesian product x_min = -np.ones((2 * n, 1)) x_max = -x_min p3 = Polyhedron.from_bounds(x_min, x_max) C = block_diag(*[np.ones((1, n))] * 2) d = np.zeros((2, 1)) p3.add_equality(C, d) # compare results self.assertTrue( same_rows(np.hstack((p2.A, p2.b)), np.hstack((p3.A, p3.b)))) self.assertTrue( same_rows(np.hstack((p2.C, p2.d)), np.hstack((p3.C, p3.d))))
def test_from_convex_hull(self): # simple 2d points = [ np.array([0.,0.]), np.array([1.,0.]), np.array([0.,1.]) ] p = Polyhedron.from_convex_hull(points) A = np.array([ [-1., 0.], [0., -1.], [1., 1.], ]) b = np.array([0.,0.,1.]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) )) # simple 3d points = [ np.array([0.,0.,0.]), np.array([1.,0.,0.]), np.array([0.,1.,0.]), np.array([0.,0.,1.]) ] p = Polyhedron.from_convex_hull(points) A = np.array([ [-1., 0., 0.], [0., -1., 0.], [0., 0., -1.], [1., 1., 1.], ]) b = np.array([0.,0.,0.,1.]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) )) # another 2d with internal point points = [ np.array([0.,0.]), np.array([1.,0.]), np.array([0.,1.]), np.array([1.,1.]), np.array([.5,.5]), ] p = Polyhedron.from_convex_hull(points) A = np.array([ [-1., 0.], [0., -1.], [1., 0.], [0., 1.], ]) b = np.array([0.,0.,1.,1.]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) ))
def test_normalize(self): # construct polyhedron A = np.array([[0.,2.],[0.,-3.]]) b = np.array([2.,0.]) C = np.ones((1, 2)) d = np.ones(1) p = Polyhedron(A, b, C, d) # normalize p.normalize() A = np.array([[0., 1.], [0., -1.]]) b = np.array([1., 0.]) C = np.ones((1, 2))/np.sqrt(2.) d = np.ones(1)/np.sqrt(2.) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)), normalize=False )) self.assertTrue(same_rows( np.column_stack((C, d)), np.column_stack((p.C, p.d)), normalize=False ))
def test_add_functions(self): # add inequalities A = np.eye(2) b = np.ones(2) p = Polyhedron(A, b) A = np.ones((1, 2)) b = np.ones(1) p.add_inequality(A, b) A = np.ones((1, 1)) b = 3. * np.ones(1) p.add_inequality(A, b, [1]) A = np.array([[1., 0.], [0., 1.], [1., 1.], [0., 1.]]) b = np.array([1., 1., 1., 3.]) self.assertTrue( same_rows(np.column_stack((A, b)), np.column_stack((p.A, p.b)))) c = np.ones(2) self.assertRaises(ValueError, p.add_inequality, A, c) # add equalities A = np.eye(2) b = np.ones(2) p.add_equality(A, b) A = 3. * np.ones((1, 1)) b = np.ones(1) p.add_equality(A, b, [0]) A = np.array([[1., 0.], [0., 1.], [3., 0.]]) b = np.array([[1.], [1.], [1.]]) self.assertTrue( same_rows(np.column_stack((A, b)), np.column_stack((p.C, p.d)))) b = np.ones(2) self.assertRaises(ValueError, p.add_equality, A, b) # add lower bounds A = np.zeros((0, 2)) b = np.zeros(0) p = Polyhedron(A, b) p.add_lower_bound(-np.ones(2)) p.add_upper_bound(2 * np.ones(2)) p.add_bounds(-3 * np.ones(2), 3 * np.ones(2)) p.add_lower_bound(-4 * np.ones(1), [0]) p.add_upper_bound(5 * np.ones(1), [0]) p.add_bounds(-6 * np.ones(1), 6 * np.ones(1), [1]) A = np.array([[-1., 0.], [0., -1.], [1., 0.], [0., 1.], [-1., 0.], [0., -1.], [1., 0.], [0., 1.], [-1., 0.], [1., 0.], [0., -1.], [0., 1.]]) b = np.array([1., 1., 2., 2., 3., 3., 3., 3., 4., 5., 6., 6.]) self.assertTrue( same_rows(np.column_stack((A, b)), np.column_stack((p.A, p.b)))) # wrong size bounds A = np.eye(3) b = np.ones(3) p = Polyhedron(A, b) x = np.zeros(1) indices = [0, 1] self.assertRaises(ValueError, p.add_lower_bound, x, indices) self.assertRaises(ValueError, p.add_upper_bound, x, indices) self.assertRaises(ValueError, p.add_bounds, x, x, indices)
def test_convex_hull_method(self): np.random.seed(3) # cube from n-dimensions to n-1-dimensions for n in range(2, 6): x_min = - np.ones(n) x_max = - x_min p = Polyhedron.from_bounds(x_min, x_max) E = np.vstack(( np.eye(n-1), - np.eye(n-1) )) f = np.ones(2*(n-1)) vertices = [np.array(v) for v in product([1., -1.], repeat=n-1)] E_chm, f_chm, vertices_chm = convex_hull_method(p.A, p.b, range(n-1)) p_chm = Polyhedron(E_chm, f_chm) p_chm.remove_redundant_inequalities() self.assertTrue(same_rows( np.column_stack((E, f)), np.column_stack((p_chm.A, p_chm.b)) )) self.assertTrue(same_vectors(vertices, vertices_chm)) # test with random polytopes wiith m facets m = 10 # dimension of the higher dimensional polytope for n in range(3, 6): # dimension of the lower dimensional polytope for n_proj in range(2, n): # higher dimensional polytope A = np.random.randn(m, n) b = np.random.rand(m) p = Polyhedron(A, b) # if not empty or unbounded if p.vertices is not None: points = [v[:n_proj] for v in p.vertices] p = Polyhedron.from_convex_hull(points) # check half spaces p.remove_redundant_inequalities() E_chm, f_chm, vertices_chm = convex_hull_method(A, b, range(n_proj)) p_chm = Polyhedron(E_chm, f_chm) p_chm.remove_redundant_inequalities() self.assertTrue(same_rows( np.column_stack((p.A, p.b)), np.column_stack((p_chm.A, p_chm.b)) )) # check vertices self.assertTrue(same_vectors(p.vertices, vertices_chm))
def test_orthogonal_projection(self): # (more tests on projections are in geometry/test_orthogonal_projection.py) # unbounded polyhedron x_min = -np.ones((3, 1)) p = Polyhedron.from_lower_bound(x_min) self.assertRaises(ValueError, p.project_to, range(2)) # simple 3d onto 2d p.add_upper_bound(-x_min) E = np.vstack((np.eye(2), -np.eye(2))) f = np.ones((4, 1)) vertices = [ np.array(v).reshape(2, 1) for v in product([1., -1.], repeat=2) ] proj = p.project_to(range(2)) proj.remove_redundant_inequalities() self.assertTrue( same_rows(np.hstack((E, f)), np.hstack((proj.A, proj.b)))) self.assertTrue(same_vectors(vertices, proj.vertices)) # lower dimensional C = np.array([[1., 1., 0.]]) d = np.zeros((1, 1)) p.add_equality(C, d) self.assertRaises(ValueError, p.project_to, range(2)) # lower dimensional x_min = -np.ones((3, 1)) x_max = 2. * x_min p = Polyhedron.from_bounds(x_min, x_max) self.assertRaises(ValueError, p.project_to, range(2))
def test_intersection(self): # first polyhedron x1_min = -np.ones((2, 1)) x1_max = -x1_min p1 = Polyhedron.from_bounds(x1_min, x1_max) # second polyhedron x2_min = np.zeros((2, 1)) x2_max = 2. * np.ones((2, 1)) p2 = Polyhedron.from_bounds(x2_min, x2_max) # intersection p3 = p1.intersection(p2) p3.remove_redundant_inequalities() p4 = Polyhedron.from_bounds(x2_min, x1_max) self.assertTrue( same_rows(np.hstack((p3.A, p3.b)), np.hstack((p4.A, p4.b)))) # add equalities C1 = np.array([[1., 0.]]) d1 = np.array([-.5]) p1.add_equality(C1, d1) p3 = p1.intersection(p2) self.assertTrue(p3.empty)
def test_same_rows(self): np.random.seed(1) # test dimensions n = 10 m = 5 for i in range(10): # check with scaling factors A = np.random.rand(n, m) scaling = np.random.rand(n) B = np.multiply(A.T, scaling).T self.assertTrue(same_rows(A, B)) # check without scaling factors B_order = list(range(n)) shuffle(B_order) B = A[B_order, :] self.assertTrue(same_rows(A, B, normalize=False)) # wrong check (same columns) scaling = np.random.rand(m) B = np.multiply(A, scaling) self.assertFalse(same_rows(A, B))
def test_solve(self): # 1-dimensional mpqp H = dict() H['uu'] = np.array([[1.]]) H['xx'] = np.zeros((1, 1)) H['ux'] = np.zeros((1, 1)) f = dict() f['u'] = np.zeros((1, 1)) f['x'] = np.zeros((1, 1)) g = np.zeros((1, 1)) A = dict() A['u'] = np.array([[1.], [-1.], [0.], [0.]]) A['x'] = np.array([[-1.], [1.], [1.], [-1.]]) b = np.array([[1.], [1.], [2.], [2.]]) mpqp = MultiParametricQuadraticProgram(H, f, g, A, b) # explicit solve given active set cr = mpqp.explicit_solve_given_active_set([]) self.assertAlmostEqual(cr._V['xx'], 0.) self.assertAlmostEqual(cr._V['x'], 0.) self.assertAlmostEqual(cr._V['0'], 0.) self.assertAlmostEqual(cr._u['x'], 0.) self.assertAlmostEqual(cr._u['0'], 0.) cr = mpqp.explicit_solve_given_active_set([0]) self.assertAlmostEqual(cr._V['xx'], 1.) self.assertAlmostEqual(cr._V['x'], 1.) self.assertAlmostEqual(cr._V['0'], .5) self.assertAlmostEqual(cr._u['x'], 1.) self.assertAlmostEqual(cr._u['0'], 1.) # explicit solve given point cr = mpqp.explicit_solve_given_point(np.array([[1.5]])) self.assertAlmostEqual(cr._V['xx'], 1.) self.assertAlmostEqual(cr._V['x'], -1.) self.assertAlmostEqual(cr._V['0'], .5) self.assertAlmostEqual(cr._u['x'], 1.) self.assertAlmostEqual(cr._u['0'], -1.) # implicit solve given point sol = mpqp.solve(np.array([[1.5]])) self.assertAlmostEqual(sol['min'], .125) self.assertTrue(np.allclose(sol['argmin'], 0.5)) self.assertEqual(sol['active_set'], [1]) # solve exp_sol = mpqp.explicit_solve() for x in [ np.array([[.5]]), np.array([[1.5]]), np.array([[-1.5]]), np.array([[2.5]]), np.array([[-2.5]]) ]: sol = mpqp.solve(x) if sol['min'] is not None: self.assertAlmostEqual(sol['min'], exp_sol.V(x)) np.testing.assert_array_almost_equal(sol['argmin'], exp_sol.u(x)) np.testing.assert_array_almost_equal( sol['multiplier_inequality'], exp_sol.p(x)) else: self.assertTrue(exp_sol.V(x) is None) self.assertTrue(exp_sol.u(x) is None) self.assertTrue(exp_sol.p(x) is None) # feasible set fs = mpqp.get_feasible_set() A = np.array([[1.], [-1.]]) b = np.array([[2.], [2.]]) self.assertTrue(same_rows(np.hstack((A, b)), np.hstack((fs.A, fs.b))))
def test_from_functions(self): # row vector input x = np.ones((1, 5)) self.assertRaises(ValueError, Polyhedron.from_lower_bound, x) # from lower bound x = np.ones((2, 1)) p = Polyhedron.from_lower_bound(x) A_lb = np.array([[-1., 0.], [0., -1.]]) b_lb = np.array([[-1.], [-1.]]) self.assertTrue( same_rows(np.hstack((A_lb, b_lb)), np.hstack((p.A, p.b)))) # from upper bound p = Polyhedron.from_upper_bound(x) A_ub = np.array([[1., 0.], [0., 1.]]) b_ub = np.array([[1.], [1.]]) self.assertTrue( same_rows(np.hstack((A_ub, b_ub)), np.hstack((p.A, p.b)))) # from upper and lower bounds p = Polyhedron.from_bounds(x, x) A = np.vstack((A_lb, A_ub)) b = np.vstack((b_lb, b_ub)) self.assertTrue(same_rows(np.hstack((A, b)), np.hstack((p.A, p.b)))) # different size lower and upper bound y = np.ones((3, 1)) self.assertRaises(ValueError, Polyhedron.from_bounds, x, y) # from lower bound of not all the variables indices = [1, 2] n = 3 p = Polyhedron.from_lower_bound(x, indices, n) A_lb = np.array([[0., -1., 0.], [0., 0., -1.]]) b_lb = np.array([[-1.], [-1.]]) self.assertTrue( same_rows(np.hstack((A_lb, b_lb)), np.hstack((p.A, p.b)))) # from lower bound of not all the variables indices = [0, 2] n = 3 p = Polyhedron.from_upper_bound(x, indices, n) A_ub = np.array([[1., 0., 0.], [0., 0., 1.]]) b_ub = np.array([[1.], [1.]]) self.assertTrue( same_rows(np.hstack((A_ub, b_ub)), np.hstack((p.A, p.b)))) # from upper and lower bounds of not all the variables indices = [0, 1] n = 3 p = Polyhedron.from_bounds(x, x, indices, n) A = np.array([[-1., 0., 0.], [0., -1., 0.], [1., 0., 0.], [0., 1., 0.]]) b = np.array([[-1.], [-1.], [1.], [1.]]) self.assertTrue(same_rows(np.hstack((A, b)), np.hstack((p.A, p.b)))) # too many indices indices = [1, 2, 3] n = 5 self.assertRaises(ValueError, Polyhedron.from_lower_bound, x, indices, n) self.assertRaises(ValueError, Polyhedron.from_upper_bound, x, indices, n) self.assertRaises(ValueError, Polyhedron.from_bounds, x, x, indices, n)
def test_remove_redundant_inequalities(self): # minimal facets only inequalities A = np.array([[1., 1.], [-1., 1.], [0., -1.], [0., 1.], [0., 1.], [2., 2.]]) b = np.array([[1.], [1.], [1.], [1.], [2.], [2.]]) p = Polyhedron(A, b) mf = set(p.minimal_facets()) self.assertTrue(mf == set([1, 2, 0]) or mf == set([1, 2, 5])) # add nasty redundant inequality A = np.zeros((1, 2)) b = np.ones((1, 1)) p.add_inequality(A, b) mf = set(p.minimal_facets()) self.assertTrue(mf == set([1, 2, 0]) or mf == set([1, 2, 5])) # remove redundant facets p.remove_redundant_inequalities() A_min = np.array([[-1., 1.], [0., -1.], [1., 1.]]) b_min = np.array([[1.], [1.], [1.]]) self.assertTrue( same_rows(np.hstack((A_min, b_min)), np.hstack((p.A, p.b)))) # both inequalities and equalities x_min = -np.ones((2, 1)) x_max = -x_min p = Polyhedron.from_bounds(x_min, x_max) C = np.ones((1, 2)) d = np.ones((1, 1)) p.add_equality(C, d) self.assertEqual([2, 3], sorted(p.minimal_facets())) p.remove_redundant_inequalities() A_min = np.array([[1., 0.], [0., 1.]]) b_min = np.array([[1.], [1.]]) self.assertTrue( same_rows(np.hstack((A_min, b_min)), np.hstack((p.A, p.b)))) # add (redundant) inequality coincident with the equality p.add_inequality(C, d) p.remove_redundant_inequalities() self.assertTrue( same_rows(np.hstack((A_min, b_min)), np.hstack((p.A, p.b)))) # add (redundant) inequality p.add_inequality(np.array([[-1., 1.]]), np.array([[1.1]])) p.remove_redundant_inequalities() self.assertTrue( same_rows(np.hstack((A_min, b_min)), np.hstack((p.A, p.b)))) # add unfeasible equality (raises: 'empty polyhedron, cannot remove redundant inequalities.') C = np.ones((1, 2)) d = -np.ones((1, 1)) p.add_equality(C, d) self.assertRaises(ValueError, p.remove_redundant_inequalities) # empty polyhderon (raises: 'empty polyhedron, cannot remove redundant inequalities.') x_min = np.ones((2, 1)) x_max = np.zeros((2, 1)) p = Polyhedron.from_bounds(x_min, x_max) self.assertRaises(ValueError, p.remove_redundant_inequalities)
def test_add_functions(self): # add inequalities A = np.eye(2) b = np.ones(2) p = Polyhedron(A, b) A = np.ones((1, 2)) b = np.ones(1) p.add_inequality(A, b) A = np.ones((1, 1)) b = 3.*np.ones(1) p.add_inequality(A, b, [1]) A = np.array([[1., 0.], [0., 1.], [1., 1.], [0., 1.]]) b = np.array([1.,1.,1.,3.]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) )) c = np.ones(2) self.assertRaises(ValueError, p.add_inequality, A, c) # add equalities A = np.eye(2) b = np.ones(2) p.add_equality(A, b) A = 3.*np.ones((1, 1)) b = np.ones(1) p.add_equality(A, b, [0]) A = np.array([[1., 0.], [0., 1.], [3., 0.]]) b = np.array([[1.], [1.], [1.]]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.C, p.d)) )) b = np.ones(2) self.assertRaises(ValueError, p.add_equality, A, b) # add lower bounds A = np.zeros((0, 2)) b = np.zeros(0) p = Polyhedron(A, b) p.add_lower_bound(-np.ones(2)) p.add_upper_bound(2*np.ones(2)) p.add_bounds(-3*np.ones(2), 3*np.ones(2)) p.add_lower_bound(-4*np.ones(1), [0]) p.add_upper_bound(5*np.ones(1), [0]) p.add_bounds(-6*np.ones(1), 6*np.ones(1), [1]) A =np.array([[-1., 0.], [0., -1.], [1., 0.], [0., 1.], [-1., 0.], [0., -1.], [1., 0.], [0., 1.], [-1., 0.], [1., 0.], [0., -1.], [0., 1.] ]) b = np.array([1.,1.,2.,2.,3.,3.,3.,3.,4.,5.,6.,6.]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) )) # wrong size bounds A = np.eye(3) b = np.ones(3) p = Polyhedron(A, b) x = np.zeros(1) indices = [0, 1] self.assertRaises(ValueError, p.add_lower_bound, x, indices) self.assertRaises(ValueError, p.add_upper_bound, x, indices) self.assertRaises(ValueError, p.add_bounds, x, x, indices) # add symbolic n = 5 m = 3 A = np.random.rand(m, n) b = np.random.rand(m) C = np.random.rand(m, n) d = np.random.rand(m) P = Polyhedron(A, b) x = sp.Matrix([sp.Symbol('x'+str(i), real=True) for i in range(n)]) ineq = sp.Matrix(A)*x - sp.Matrix(b) P.add_symbolic_inequality(x, ineq) np.testing.assert_array_almost_equal(P.A, np.vstack((A, A))) np.testing.assert_array_almost_equal(P.b, np.concatenate((b, b))) eq = sp.Matrix(C)*x - sp.Matrix(d) P.add_symbolic_equality(x, eq) np.testing.assert_array_almost_equal(P.C, C) np.testing.assert_array_almost_equal(P.d, d) # throw some errors here P = Polyhedron(A, b) x = sp.Matrix([sp.Symbol('x'+str(i), real=True) for i in range(n+1)]) A1 = np.random.rand(m, n+1) ineq = sp.Matrix(A1)*x - sp.Matrix(b) self.assertRaises(ValueError, P.add_symbolic_inequality, x, ineq) P = Polyhedron(A, b, C, d) C1 = np.random.rand(m, n+1) eq = sp.Matrix(C1)*x - sp.Matrix(b) self.assertRaises(ValueError, P.add_symbolic_equality, x, eq)
def test_from_functions(self): # row vector input x = np.ones((1,5)) self.assertRaises(ValueError, Polyhedron.from_lower_bound, x) # from lower bound x = np.ones(2) p = Polyhedron.from_lower_bound(x) A_lb = np.array([[-1., 0.], [0., -1.]]) b_lb = np.array([-1.,-1.]) self.assertTrue(same_rows( np.column_stack((A_lb, b_lb)), np.column_stack((p.A, p.b)) )) # from upper bound p = Polyhedron.from_upper_bound(x) A_ub = np.array([[1., 0.], [0., 1.]]) b_ub = np.array([1.,1.]) self.assertTrue(same_rows( np.column_stack((A_ub, b_ub)), np.column_stack((p.A, p.b)) )) # from upper and lower bounds p = Polyhedron.from_bounds(x, x) A = np.vstack((A_lb, A_ub)) b = np.concatenate((b_lb, b_ub)) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) )) # different size lower and upper bound y = np.ones((3,1)) self.assertRaises(ValueError, Polyhedron.from_bounds, x, y) # from lower bound of not all the variables indices = [1, 2] n = 3 p = Polyhedron.from_lower_bound(x, indices, n) A_lb = np.array([[0., -1., 0.], [0., 0., -1.]]) b_lb = np.array([-1.,-1.]) self.assertTrue(same_rows( np.column_stack((A_lb, b_lb)), np.column_stack((p.A, p.b)) )) # from lower bound of not all the variables indices = [0, 2] n = 3 p = Polyhedron.from_upper_bound(x, indices, n) A_ub = np.array([[1., 0., 0.], [0., 0., 1.]]) b_ub = np.array([1.,1.]) self.assertTrue(same_rows( np.column_stack((A_ub, b_ub)), np.column_stack((p.A, p.b)) )) # from upper and lower bounds of not all the variables indices = [0, 1] n = 3 p = Polyhedron.from_bounds(x, x, indices, n) A = np.array([[-1., 0., 0.], [0., -1., 0.], [1., 0., 0.], [0., 1., 0.]]) b = np.array([-1.,-1.,1.,1.]) self.assertTrue(same_rows( np.column_stack((A, b)), np.column_stack((p.A, p.b)) )) # too many indices indices = [1, 2, 3] n = 5 self.assertRaises(ValueError, Polyhedron.from_lower_bound, x, indices, n) self.assertRaises(ValueError, Polyhedron.from_upper_bound, x, indices, n) self.assertRaises(ValueError, Polyhedron.from_bounds, x, x, indices, n) # from symbolic n = 5 m = 3 x = sp.Matrix([sp.Symbol('x'+str(i), real=True) for i in range(n)]) A = np.random.rand(m, n) b = np.random.rand(m) ineq = sp.Matrix(A)*x - sp.Matrix(b) P = Polyhedron.from_symbolic(x, ineq) np.testing.assert_array_almost_equal(P.A, A) np.testing.assert_array_almost_equal(P.b, b) C = np.random.rand(m, n) d = np.random.rand(m) eq = sp.Matrix(C)*x - sp.Matrix(d) P = Polyhedron.from_symbolic(x, ineq, eq) np.testing.assert_array_almost_equal(P.A, A) np.testing.assert_array_almost_equal(P.b, b) np.testing.assert_array_almost_equal(P.C, C) np.testing.assert_array_almost_equal(P.d, d)