def test_conditional_constrained_sage_2(self): # Background # # This is Problem 4 from a 2005 paper by Yanjun Wang and Zhian Liang. # The optimal objective is between 11.95 and 11.96. # # Tests - (p, q, ell) = (0, 2, 0) # # (1) Verify similar primal / dual objectives. # # (2) Verify that primal objective is within 1 percent of a reference value. # # (3) Recover a solution feasible within 1e-8, within 1 percent of optimality. # n = 2 y = standard_sig_monomials(n) f = 3.7 * y[0] ** 0.85 + 1.985 * y[0] + 700.3 * y[1] ** -0.75 gts = [1 - 0.7673 * y[1] ** 0.05 + 0.05 * y[0], 5 - y[0], y[0] - 0.1, 450 - y[1], y[1] - 380] eqs = [] X = infer_domain(f, gts, eqs) p, q, ell = 0, 2, 0 vals, dual = constrained_primal_dual_vals(f, gts, eqs, p, q, ell, X, solver='MOSEK') assert abs(vals[0] - vals[1]) < 1e-1 assert abs(vals[0] - 11.95) / vals[0] < 1e-2 solns = sig_solrec(dual, ineq_tol=1e-8) assert (f(solns[0]) - dual.value) / dual.value < 1e-2
def test_expcone_infer_sigdomain(self): y = standard_sig_monomials(1)[0] dummy_f = y * 0 g0 = 10 - y - y**2 - 1 / y d0 = infer_domain(dummy_f, [g0], []) g1 = y * g0 d1 = infer_domain(dummy_f, [g1], []) g2 = g0 / y**0.5 d2 = infer_domain(dummy_f, [g2], []) for di in [d0, d1, d2]: assert len(di.K) == 4 assert di.K[0].type == '+' assert di.K[0].len == 1 for j in [1, 2, 3]: assert di.K[j].type == 'e' for di in [d0, d1, d2]: A = di.A assert np.allclose(A[0, :], np.array([0, -1, -1, -1])) selector = np.zeros(shape=(A.shape[0], ), dtype=bool) selector[[1, 4, 7]] = True coeffs = np.sort(A[selector, 0]) assert np.allclose(coeffs, np.array([-1, 1, 2])) assert np.allclose(A[~selector, 0], np.zeros(A.shape[0] - 3)) compare = np.zeros(shape=(A.shape[0] - 1, A.shape[1] - 1)) compare[1, 0] = 1 compare[4, 1] = 1 compare[7, 2] = 1 assert np.allclose(A[1:, 1:], compare) pass
def test_conditional_constrained_sage_1(self): # Background # # This is Problem 1 from a 2014 paper by Xue-Ping Hou, Pei-Ping Shen, and Yong-Qiang Chen. # It can also be found in * many * other papers. # The optimal objective is 0.76508 # # Tests - (p, q, ell) = (0, 1, 0). # # (1) Check that primal and dual objectives are close. # # (2) Check that primal objective is close to reference value. # # (3) Verify that we can recover an optimal solution. # n = 4 y = standard_sig_monomials(n) f = y[2] ** 0.8 * y[3] ** 1.2 gts = [y[0] * y[3] ** -1 + y[1] ** -1 * y[3] ** -1 - 1, - y[0] ** -2 * y[2] ** -1 - y[1] * y[2] ** -1 + 1] gts = [-g for g in gts] gts += [1 - y[0], y[0] - 0.1, 10 - y[1], y[1] - 5, 15 - y[2], y[2] - 8, 1 - y[3], y[3] - 0.01] eqs = [] X = infer_domain(f, gts, eqs) p, q, ell = 0, 1, 0 vals, dual = constrained_primal_dual_vals(f, gts, eqs, p, q, ell, X) assert abs(vals[0] - vals[1]) < 1e-5 assert abs(vals[0] - 0.765082) < 1e-4 solns = sig_solrec(dual, ineq_tol=0) assert f(solns[0]) < 1e-8 + dual.value
def test_constrained_sage_2(self): # Background # # This is a signomial formulation of a nonnegative polynomial optimization problem. # # The problem can be found on page 16 of the gloptipoly3 manual # http://homepages.laas.fr/henrion/papers/gloptipoly3.pdf # among other places. The optimal objective is -4. # # Tests - (p, q, ell) = (0, 1, 0) # # (1) Check for similar primal / dual objectives. # x = standard_sig_monomials(3) f = -2 * x[0] + x[1] - x[2] g1 = Signomial.from_dict({(0, 0, 0): 24, (1, 0, 0): -20, (0, 1, 0): 9, (0, 0, 1): -13, (2, 0, 0): 4, (1, 1, 0): -4, (1, 0, 1): 4, (0, 2, 0): 2, (0, 1, 1): -2, (0, 0, 2): 2}) g2 = 4 - x[0] - x[1] - x[2] g3 = 6 - 3*x[1] - x[2] g4 = 2 - x[0] g5 = 3 - x[2] gts = [g1, g2, g3, g4, g5] res01, _ = constrained_primal_dual_vals(f, gts, [], p=0, q=1, ell=0, X=None) expect = -6 assert abs(res01[0] - expect) < 1e-4 assert abs(res01[1] - expect) < 1e-4 assert abs(res01[0] - res01[1]) < 1e-5
def test_exponentiation(self): x = standard_sig_monomials(2) y0 = (x[0] - x[1])**2 y1 = x[0]**2 - 2 * x[0] * x[1] + x[1]**2 assert y0 == y1 z0 = x[0]**0.5 z1 = Signomial.from_dict({(0.5, 0): 1}) assert z0 == z1
def test_invalid_signomial_operations(self): x = standard_sig_monomials(2) y = standard_sig_monomials(1) try: s = x[0] + y[0] assert False except RuntimeError as err: err_str = str(err) assert 'Cannot add' in err_str try: s = x[0] * y[0] assert False except RuntimeError as err: err_str = str(err) assert 'Cannot multiply' in err_str s = sum(x) try: t = s**0.5 assert False except ValueError as err: err_str = str(err) assert 'Only signomials with exactly one term' in err_str z = cl.Variable() s = 0 * s + z # a Signomial, with only a constant term. try: t = s**2 assert False except RuntimeError as err: err_str = str(err) assert 'Cannot exponentiate signomials with symbolic coefficients' in err_str try: y = x[0]**x[1] assert False except RuntimeError as err: err_str = str(err) assert 'Cannot raise a signomial to non-numeric powers.' == err_str pass
def test_freecomponent_infer_sigdomain(self): x = standard_sig_monomials(4) dummy_f = x[0] * 0 gts = [1 - x[1]**0.5 - x[2]**3] dom = infer_domain(dummy_f, gts, []) A, b, K = dom.A, dom.b, dom.K # ^ Two exponential cones, two epigraph variables, one linear inequality assert A.shape == (7, 6) assert len(K) == 3 assert K[0].type == '+' and K[0].len == 1 assert K[1].type == 'e' and K[1].len == 3 assert K[2].type == 'e' and K[2].len == 3 assert np.count_nonzero(A[:, 0]) == 0 assert np.count_nonzero(A[:, 3]) == 0 pass
def test_eqcon_infer_sigdomain(self): y = standard_sig_monomials(3) expx0 = np.exp([1, 2, 3]) eqs = [y[0] - expx0[0], 2*y[1] - 2*expx0[1], 0.5*expx0[2] - 0.5*y[2]] gts = [y[0] - y[1] + y[2]] # not convex f = -np.sum(y) + 1 X = infer_domain(f, gts, eqs) is_in = X.check_membership(np.array([1, 2, 3]), tol=1e-10) assert is_in is_in = X.check_membership(np.array([1.0001, 2, 3]), tol=1e-5) assert not is_in assert X.A.shape == (3, 3) assert all([co.type == '0' for co in X.K]) residual = X.A @ np.array([1, 2, 3]) + X.b assert np.allclose(residual, np.zeros(3))
def test_composition_sigs(self): p = Polynomial.from_dict({ (1, ): 2, (0, ): -1 }) # represents lambda x: 2*x - 1 s = Signomial.from_dict({(2, ): -1, (0, ): 1}) f = p(s) # lambda x: -2*exp(x) + 1 self.assertAlmostEqual(f(0.5), -2 * np.exp(1.0) + 1, places=4) self.assertAlmostEqual(f(1), -2 * np.exp(2.0) + 1, places=4) p = np.prod(standard_poly_monomials(3)) exp_x = standard_sig_monomials(2) sig_vec = np.array([exp_x[0], exp_x[0] - exp_x[1], 1.0 / exp_x[1]]) f = p(sig_vec) self.assertEqual(f.n, 2) self.assertEqual(f(np.array([1, 1])), 0) x_test = np.array([-3, 3]) self.assertAlmostEqual(f(x_test), np.exp(-6) * (np.exp(-3) - np.exp(3)), places=4)
def test_conditional_sage_1(self): # Background # # This is Problem 1 from MCW2019. It appears in many papers on algorithms # for signomial progamming. All constraints are convex. # # Tests # # ell=0 and ell=3. Test for recovery of a feasible solution for # ell=0, and test for recovery of an optimal solution for ell=3. # # Notes # # ECOS can't solve the primal when ell > 0. Only try ell=3 if MOSEK is installed. # y = standard_sig_monomials(3) f = 0.5 * y[0] * y[1] ** -1 - y[0] - 5 * y[1] ** -1 gts = [100 - y[1] * y[2] ** -1 - y[1] - 0.05 * y[0] * y[2], y[0] - 70, y[1] - 1, y[2] - 0.5, 150 - y[0], 30 - y[1], 21 - y[2]] eqs = [] X = infer_domain(f, gts, eqs) vals, prob = primal_dual_vals(f, 0, X) expect = -147.85712 assert abs(vals[0] - expect) <= 1e-3 assert abs(vals[1] - expect) <= 1e-3 solutions = sig_solrec(prob) assert len(solutions) > 0 if cl.Mosek.is_installed(): vals, prob = primal_dual_vals(f, 3, X, solver='MOSEK') expect = -147.6666 assert abs(vals[0] - expect) <= 1e-3 assert abs(vals[1] - expect) <= 1e-3 solutions = sig_solrec(prob) assert len(solutions) > 0 x_star = solutions[0] gap = f(x_star) - prob.value assert abs(gap) <= 1e-3 pass
def test_conditional_constrained_sage_3(self): # Background # # This is a modification of Problem 10 from the 1978 paper by M. Rijckaert and X. Martens. # The bound constraints come from a different paper (but I dont recall where...). # We also add an additional trivially-valid constraint (simply to strengthen the Lagrange dual). # The optimal objective is reported in the literature as approximately -83.21. # # Tests - (p, q, ell) = (0, 1, 1) # # (1) Verify similar primal / dual objectives. # # (2) Recover a strictly feasible solution, within 0.7 % of optimality. # # Notes # # With (p, q, ell) = (0, 0, 2) (not shown here), we get a SAGE bound of -83.253. # If the (0,0,2) SAGE bound of -83.253 is to be believed, then the recovered solution # actually has a relative optimality gap of only 0.003 percent. # n = 3 x = standard_sig_monomials(n) f = 0.5 * x[0] * (x[1] ** -1) - x[0] - 5.0 * (x[1] ** -1) g = 1 - 0.01 * x[1] * (x[2] ** -1) - 0.01 * x[0] - 0.0005 * x[0] * x[2] g = 100.0 * g gts = [g, g * (x[1] ** -2), 1e2 - x[0], 1e2 - x[1], 1e2 - x[2], x[0] - 1, x[1] - 1, x[2] - 1] eqs = [] X = infer_domain(f, gts, eqs) p, q, ell = 0, 1, 1 vals, dual = constrained_primal_dual_vals(f, gts, eqs, p, q, ell, X, solver='MOSEK') assert abs(vals[0] - vals[1]) < 1e-4 assert abs(vals[0] - (-83.3235)) < 1e-4 solns = sig_solrec(dual, ineq_tol=0) assert (f(solns[0]) - dual.value) / abs(dual.value) < 0.007