def test_mul_elemwise(self): """Test the mul_elemwise atom. """ self.assertEquals( mul_elemwise([1, -1], self.x).sign, u.Sign.UNKNOWN_KEY) self.assertEquals( mul_elemwise([1, -1], self.x).curvature, u.Curvature.AFFINE_KEY) self.assertEquals(mul_elemwise([1, -1], self.x).size, (2, 1)) pos_param = Parameter(2, sign="positive") neg_param = Parameter(2, sign="negative") self.assertEquals( mul_elemwise(pos_param, pos_param).sign, u.Sign.POSITIVE_KEY) self.assertEquals( mul_elemwise(pos_param, neg_param).sign, u.Sign.NEGATIVE_KEY) self.assertEquals( mul_elemwise(neg_param, neg_param).sign, u.Sign.POSITIVE_KEY) self.assertEquals( mul_elemwise(neg_param, square(self.x)).curvature, u.Curvature.CONCAVE_KEY) # Test promotion. self.assertEquals(mul_elemwise([1, -1], 1).size, (2, 1)) self.assertEquals(mul_elemwise(1, self.C).size, self.C.size) with self.assertRaises(Exception) as cm: mul_elemwise(self.x, [1, -1]) self.assertEqual( str(cm.exception), "The first argument to mul_elemwise must be constant.")
def __init__(self, rows=1, cols=1, *args, **kwargs): self._LB = Parameter(rows, cols) self._LB.value = cvxopt.matrix(0, (rows, cols), tc='d') self._UB = Parameter(rows, cols) self._UB.value = cvxopt.matrix(1, (rows, cols), tc='d') self._fix_values = cvxopt.matrix(False, (rows, cols)) super(Boolean, self).__init__(rows, cols, *args, **kwargs)
def test_parameter(self): """Test the parameter class. """ x = Parameter(2, complex=False) y = Parameter(2, complex=True) z = Parameter(2, imag=True) assert not x.is_complex() assert not x.is_imag() assert y.is_complex() assert not y.is_imag() assert z.is_complex() assert z.is_imag() with self.assertRaises(Exception) as cm: x.value = np.array([1j, 0.]) self.assertEqual(str(cm.exception), "Parameter value must be real.") y.value = np.array([1., 0.]) y.value = np.array([1j, 0.]) with self.assertRaises(Exception) as cm: z.value = np.array([1., 0.]) self.assertEqual(str(cm.exception), "Parameter value must be imaginary.")
def test_psd_nsd_parameters(self): # Test valid rank-deficeint PSD parameter. np.random.seed(42) a = np.random.normal(size=(100, 95)) a2 = a.dot(a.T) # This must be a PSD matrix. p = Parameter((100, 100), PSD=True) p.value = a2 self.assertItemsAlmostEqual(p.value, a2, places=10) # Test positive definite matrix with non-distinct eigenvalues m, n = 10, 5 A = np.random.randn( m, n) + 1j * np.random.randn(m, n) # a random complex matrix A = np.dot(A.T.conj(), A) # a random Hermitian positive definite matrix A = np.vstack([ np.hstack([np.real(A), -np.imag(A)]), np.hstack([np.imag(A), np.real(A)]) ]) p = Parameter(shape=(2 * n, 2 * n), PSD=True) p.value = A self.assertItemsAlmostEqual(p.value, A) # Test invalid PSD parameter with self.assertRaises(Exception) as cm: p = Parameter((2, 2), PSD=True, value=[[1, 0], [0, -1]]) self.assertEqual(str(cm.exception), "Parameter value must be positive semidefinite.") # Test invalid NSD parameter with self.assertRaises(Exception) as cm: p = Parameter((2, 2), NSD=True, value=[[1, 0], [0, -1]]) self.assertEqual(str(cm.exception), "Parameter value must be negative semidefinite.")
def test_parameters_successes(self): # Parameter names and dimensions p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.shape, tuple()) # Entry-wise constraints on parameter values. val = -np.ones((4, 3)) val[0, 0] = 2 p = Parameter((4, 3)) p.value = val # Initialize a parameter with a value; later, set it to None. p = Parameter(value=10) self.assertEqual(p.value, 10) p.value = 10 p.value = None self.assertEqual(p.value, None) # Test parameter representation. p = Parameter((4, 3), nonpos=True) self.assertEqual(repr(p), 'Parameter((4, 3), nonpos=True)') # Test valid diagonal parameter. p = Parameter((2, 2), diag=True) p.value = sp.csc_matrix(np.eye(2)) self.assertItemsAlmostEqual(p.value.todense(), np.eye(2), places=10)
def test_huber(self): # Valid. huber(self.x, 1) with self.assertRaises(Exception) as cm: huber(self.x, -1) self.assertEqual(str(cm.exception), "M must be a non-negative scalar constant.") with self.assertRaises(Exception) as cm: huber(self.x, [1,1]) self.assertEqual(str(cm.exception), "M must be a non-negative scalar constant.") # M parameter. M = Parameter(sign="positive") # Valid. huber(self.x, M) M.value = 1 self.assertAlmostEquals(huber(2, M).value, 3) # Invalid. M = Parameter(sign="negative") with self.assertRaises(Exception) as cm: huber(self.x, M) self.assertEqual(str(cm.exception), "M must be a non-negative scalar constant.")
def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1, 1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1,1) for Parameter value.") val = -np.ones((4, 3)) val[0, 0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val
def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() self.assertItemsEqual(params, [p1, p2, p3])
def test_div_expression(self): # Vectors exp = self.x/2 self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.sign, s.UNKNOWN) # self.assertEqual(exp.canonical_form[0].shape, (2, 1)) # self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.shape, (2,)) with self.assertRaises(Exception) as cm: (self.x/[2, 2, 3]) print(cm.exception) self.assertRegexpMatches(str(cm.exception), "Incompatible shapes for division.*") c = Constant([3.0, 4.0, 12.0]) self.assertItemsAlmostEqual( (c / Constant([1.0, 2.0, 3.0])).value, np.array([3.0, 2.0, 4.0])) # Constant expressions. c = Constant(2) exp = c/(3 - 5) self.assertEqual(exp.curvature, s.CONSTANT) self.assertEqual(exp.shape, tuple()) self.assertEqual(exp.sign, s.NONPOS) # Parameters. p = Parameter(nonneg=True) exp = 2/p p.value = 2 self.assertEqual(exp.value, 1) rho = Parameter(nonneg=True) rho.value = 1 self.assertEqual(rho.sign, s.NONNEG) self.assertEqual(Constant(2).sign, s.NONNEG) self.assertEqual((Constant(2)/Constant(2)).sign, s.NONNEG) self.assertEqual((Constant(2)*rho).sign, s.NONNEG) self.assertEqual((rho/2).sign, s.NONNEG) # Broadcasting. x = cp.Variable((3, 3)) c = np.arange(1, 4)[:, None] expr = x / c self.assertEqual((3, 3), expr.shape) x.value = np.ones((3, 3)) A = np.ones((3, 3)) / c self.assertItemsAlmostEqual(A, expr.value) with self.assertRaises(Exception) as cm: (x/c[:, 0]) print(cm.exception) self.assertRegexpMatches(str(cm.exception), "Incompatible shapes for division.*")
def test_psd_nsd_parameters(self) -> None: # Test valid rank-deficeint PSD parameter. np.random.seed(42) a = np.random.normal(size=(100, 95)) a2 = a.dot(a.T) # This must be a PSD matrix. p = Parameter((100, 100), PSD=True) p.value = a2 self.assertItemsAlmostEqual(p.value, a2, places=10) # Test positive definite matrix with non-distinct eigenvalues m, n = 10, 5 A = np.random.randn( m, n) + 1j * np.random.randn(m, n) # a random complex matrix A = np.dot(A.T.conj(), A) # a random Hermitian positive definite matrix A = np.vstack([ np.hstack([np.real(A), -np.imag(A)]), np.hstack([np.imag(A), np.real(A)]) ]) p = Parameter(shape=(2 * n, 2 * n), PSD=True) p.value = A self.assertItemsAlmostEqual(p.value, A) # Test arithmetic. p = Parameter(shape=(2, 2), PSD=True) self.assertTrue((2 * p).is_psd()) self.assertTrue((p + p).is_psd()) self.assertTrue((-p).is_nsd()) self.assertTrue(((-2) * (-p)).is_psd()) # Test invalid PSD and NSD parameters n = 5 P = Parameter(shape=(n, n), PSD=True) N = Parameter(shape=(n, n), NSD=True) np.random.randn(0) U = np.random.randn(n, n) U = U @ U.T (evals, U) = np.linalg.eigh(U) # U is now an orthogonal matrix v1 = np.array([3, 2, 1, 1e-8, -1]) v2 = np.array([3, 2, 2, 1e-6, -1]) v3 = np.array([3, 2, 2, 1e-4, -1e-6]) v4 = np.array([-1, 3, 0, 0, 0]) vs = [v1, v2, v3, v4] for vi in vs: with self.assertRaises(Exception) as cm: P.value = U @ np.diag(vi) @ U.T self.assertEqual(str(cm.exception), "Parameter value must be positive semidefinite.") with self.assertRaises(Exception) as cm: N.value = -U @ np.diag(vi) @ U.T self.assertEqual(str(cm.exception), "Parameter value must be negative semidefinite.")
def test_parameter_problems(self): """Test problems with parameters. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Maximize(p1*self.a), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) p1.value = 2 p2.value = -numpy.ones((3,1)) p3.value = numpy.ones((4, 4)) result = p.solve() self.assertAlmostEqual(result, -6)
def test_kron(self): """Test the kron atom. """ a = np.ones((3,2)) b = Parameter(2, sign='positive') expr = kron(a, b) assert expr.is_positive() self.assertEqual(expr.size, (6, 2)) b = Parameter(2, sign='negative') expr = kron(a, b) assert expr.is_negative() with self.assertRaises(Exception) as cm: kron(self.x, -1) self.assertEqual(str(cm.exception), "The first argument to kron must be constant.")
def test_kron(self): """Test the kron atom. """ a = np.ones((3, 2)) b = Parameter((2, 1), nonneg=True) expr = cp.kron(a, b) assert expr.is_nonneg() self.assertEqual(expr.shape, (6, 2)) b = Parameter((2, 1), nonpos=True) expr = cp.kron(a, b) assert expr.is_nonpos() with self.assertRaises(Exception) as cm: cp.kron(self.x, -1) self.assertEqual(str(cm.exception), "The first argument to kron must be constant.")
def test_quad_form(self): """Test quad_form atom. """ P = Parameter(2,2) with self.assertRaises(Exception) as cm: quad_form(self.x, P) self.assertEqual(str(cm.exception), "P cannot be a parameter.")
def test_parameter_expressions(self): """Test that expressions with parameters are updated properly. """ x = Variable() y = Variable() x0 = Parameter() xSquared = x0 * x0 + 2 * x0 * (x - x0) # initial guess for x x0.value = 2 # make the constraint x**2 - y == 0 g = xSquared - y # set up the problem obj = abs(x - 1) prob = Problem(Minimize(obj), [g == 0]) prob.solve() x0.value = 1 prob.solve() self.assertAlmostEqual(g.value, 0) # Test multiplication. prob = Problem(Minimize(x0 * x), [x == 1]) x0.value = 2 prob.solve() x0.value = 1 prob.solve() self.assertAlmostEqual(prob.value, 1)
def test_broadcast_add(self): """Test addition broadcasting. """ y = Parameter((3, 1)) z = Variable((1, 3)) y.value = np.arange(3)[:, None] z.value = (np.arange(3) - 1)[None, :] expr = y + z self.assertItemsAlmostEqual(expr.value, y.value + z.value) prob = cp.Problem(cp.Minimize(cp.sum(expr)), [z == z.value]) prob.solve() self.assertItemsAlmostEqual(expr.value, y.value + z.value) np.random.seed(0) m, n = 3, 4 A = np.random.rand(m, n) col_scale = Variable(n) with self.assertRaises(ValueError) as cm: A + col_scale self.assertEqual(str(cm.exception), "Cannot broadcast dimensions (3, 4) (4,)") col_scale = Variable([1, n]) C = A + col_scale self.assertEqual(C.shape, (m, n)) row_scale = Variable([m, 1]) R = A + row_scale self.assertEqual(R.shape, (m, n))
def test_atom(): for atom_list, objective_type in atoms: for atom, obj_val in atom_list: for row in xrange(atom.size[0]): for col in xrange(atom.size[1]): for solver in [ECOS, SCS, CVXOPT]: # Atoms with Constant arguments. yield (run_atom, atom, Problem(objective_type(atom[row, col])), obj_val[row, col].value, solver) # Atoms with Variable arguments. variables = [] constraints = [] for idx, expr in enumerate(atom.subexpressions): # Special case for MulExpr because # can't multiply two variables. if (idx == 0 and isinstance(atom, MulExpression)): variables.append(expr) else: variables.append(Variable(*expr.size)) constraints.append(variables[-1] == expr) atom_func = atom.__class__ objective = objective_type( atom_func(*variables)[row, col]) yield (run_atom, atom, Problem(objective, constraints), obj_val[row, col].value, solver) # Atoms with Parameter arguments. parameters = [] for expr in atom.subexpressions: parameters.append(Parameter(*expr.size)) parameters[-1].value = expr.value objective = objective_type( atom_func(*parameters)[row, col]) yield (run_atom, atom, Problem(objective), obj_val[row, col].value, solver)
def test_atom(): for atom_list, objective_type in atoms: for atom, size, args, obj_val in atom_list: for row in range(size[0]): for col in range(size[1]): for solver in SOLVERS_TO_TRY: # Atoms with Constant arguments. const_args = [Constant(arg) for arg in args] yield (run_atom, atom, Problem( objective_type(atom(*const_args)[row, col])), obj_val[row, col].value, solver) # Atoms with Variable arguments. variables = [] constraints = [] for idx, expr in enumerate(args): variables.append(Variable(*intf.size(expr))) constraints.append(variables[-1] == expr) objective = objective_type(atom(*variables)[row, col]) yield (run_atom, atom, Problem(objective, constraints), obj_val[row, col].value, solver) # Atoms with Parameter arguments. parameters = [] for expr in args: parameters.append(Parameter(*intf.size(expr))) parameters[ -1].value = intf.DEFAULT_INTF.const_to_matrix( expr) objective = objective_type(atom(*parameters)[row, col]) yield (run_atom, atom, Problem(objective), obj_val[row, col].value, solver)
def test_constant_atoms(atom_info, objective_type) -> None: atom, size, args, obj_val = atom_info for indexer in get_indices(size): for solver in SOLVERS_TO_TRY: # Atoms with Constant arguments. prob_val = obj_val[indexer].value const_args = [Constant(arg) for arg in args] problem = Problem(objective_type(atom(*const_args)[indexer])) run_atom(atom, problem, prob_val, solver) # Atoms with Variable arguments. variables = [] constraints = [] for idx, expr in enumerate(args): variables.append(Variable(intf.shape(expr))) constraints.append(variables[-1] == expr) objective = objective_type(atom(*variables)[indexer]) new_obj_val = prob_val if objective_type == cp.Maximize: objective = -objective new_obj_val = -new_obj_val problem = Problem(objective, constraints) run_atom(atom, problem, new_obj_val, solver) # Atoms with Parameter arguments. parameters = [] for expr in args: parameters.append(Parameter(intf.shape(expr))) parameters[-1].value = intf.DEFAULT_INTF.const_to_matrix(expr) objective = objective_type(atom(*parameters)[indexer]) run_atom(atom, Problem(objective), prob_val, solver)
def test_atom(): for atom_list, objective_type in atoms: for atom, obj_val in atom_list: for row in xrange(atom.size[0]): for col in xrange(atom.size[1]): # Atoms with Constant arguments. yield run_atom, Problem(objective_type( atom[row, col])), obj_val[row, col].value # Atoms with Variable arguments. variables = [] constraints = [] for expr in atom.subexpressions: variables.append(Variable(*expr.size)) constraints.append(variables[-1] == expr) atom_func = atom.__class__ objective = objective_type(atom_func(*variables)[row, col]) yield run_atom, Problem(objective, constraints), obj_val[row, col].value # Atoms with Parameter arguments. parameters = [] for expr in atom.subexpressions: parameters.append(Parameter(*expr.size)) parameters[-1].value = expr.value objective = objective_type( atom_func(*parameters)[row, col]) yield run_atom, Problem(objective), obj_val[row, col].value
def test_broadcast_mul(self) -> None: """Test multiply broadcasting. """ y = Parameter((3, 1)) z = Variable((1, 3)) y.value = np.arange(3)[:, None] z.value = (np.arange(3) - 1)[None, :] expr = cp.multiply(y, z) self.assertItemsAlmostEqual(expr.value, y.value * z.value) prob = cp.Problem(cp.Minimize(cp.sum(expr)), [z == z.value]) prob.solve(solver=cp.SCS) self.assertItemsAlmostEqual(expr.value, y.value * z.value) np.random.seed(0) m, n = 3, 4 A = np.random.rand(m, n) col_scale = Variable(n) with self.assertRaises(ValueError) as cm: cp.multiply(A, col_scale) self.assertEqual(str(cm.exception), "Cannot broadcast dimensions (3, 4) (4,)") col_scale = Variable([1, n]) C = cp.multiply(A, col_scale) self.assertEqual(C.shape, (m, n)) row_scale = Variable([m, 1]) R = cp.multiply(A, row_scale) self.assertEqual(R.shape, (m, n))
def test_div_expression(self): # Vectors exp = self.x / 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.x / [2, 2, 3]) print cm.exception self.assertEqual(str(cm.exception), "Can only divide by a scalar constant.") # Constant expressions. c = Constant(2) exp = c / (3 - 5) self.assertEqual(exp.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.size, (1, 1)) self.assertEqual(exp.sign, u.Sign.NEGATIVE_KEY) # Parameters. p = Parameter(sign="positive") exp = 2 / p p.value = 2 self.assertEquals(exp.value, 1)
def prox_step(prob, rho_init, scaled=False, spectral=False): """Formulates the proximal operator for a given objective, constraints, and step size. Parikh, Boyd. "Proximal Algorithms." Parameters ---------- prob : Problem The objective and constraints associated with the proximal operator. The sign of the objective function is flipped if `prob` is a maximization problem. rho_init : float The initial step size. scaled : logical, optional Should the dual variable be scaled? spectral : logical, optional Will spectral step sizes be used? Returns ---------- prox : Problem The proximal step problem. vmap : dict A map of each proximal variable id to a dictionary containing that variable `x`, the mean variable parameter `xbar`, the associated dual parameter `y`, and the step size parameter `rho`. If `spectral = True`, the estimated dual parameter `yhat` is also included. """ vmap = {} # Store consensus variables f = flip_obj(prob).args[0] # Add penalty for each variable. for xvar in prob.variables(): xid = xvar.id shape = xvar.shape vmap[xid] = { "x": xvar, "xbar": Parameter(shape, value=np.zeros(shape)), "y": Parameter(shape, value=np.zeros(shape)), "rho": Parameter(value=rho_init[xid], nonneg=True) } if spectral: vmap[xid]["yhat"] = Parameter(shape, value=np.zeros(shape)) dual = vmap[xid]["y"] if scaled else vmap[xid]["y"] / vmap[xid]["rho"] f += (vmap[xid]["rho"] / 2.0) * sum_squares(xvar - vmap[xid]["xbar"] + dual) prox = Problem(Minimize(f), prob.constraints) return prox, vmap
def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1, 1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1, 1) for Parameter value.") val = -np.ones((4, 3)) val[0, 0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val # Initialize a parameter with a value. p = Parameter(value=10) self.assertEqual(p.value, 10) # Test assigning None. p.value = 10 p.value = None assert p.value is None with self.assertRaises(Exception) as cm: p = Parameter(2, 1, sign="negative", value=[2, 1]) self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") with self.assertRaises(Exception) as cm: p = Parameter(4, 3, sign="positive", value=[1, 2]) self.assertEqual(str(cm.exception), "Invalid dimensions (2, 1) for Parameter value.") # Test repr. p = Parameter(4, 3, sign="negative") self.assertEqual(repr(p), 'Parameter(4, 3, sign="NEGATIVE")')
def test_1D_array(self): """Test NumPy 1D arrays as constants. """ c = np.array([1, 2]) p = Parameter(2) p.value = [1, 1] self.assertEqual((c @ p).value, 3) self.assertEqual((c @ self.x).shape, tuple())
def test_param_copy(self): """Test the copy function for Parameters. """ x = Parameter(3, 4, name="x", sign="positive") y = x.copy() self.assertEquals(y.size, (3, 4)) self.assertEquals(y.name(), "x") self.assertEquals(y.sign, "POSITIVE")
def test_1D_array(self): """Test NumPy 1D arrays as constants. """ c = np.array([1, 2]) p = Parameter(2) p.value = [1, 1] self.assertEquals((c * p).value, 3) self.assertEqual((c * self.x).size, (1, 1))
def test_param(self): """Test creating a parameter. """ A = Parameter(5, 4) var = create_param(A, (5, 4)) self.assertEqual(var.size, (5, 4)) self.assertEqual(len(var.args), 0) self.assertEqual(var.type, PARAM)
def test_param_copy(self): """Test the copy function for Parameters. """ x = Parameter((3, 4), name="x", nonneg=True) y = x.copy() self.assertEqual(y.shape, (3, 4)) self.assertEqual(y.name(), "x") self.assertEqual(y.sign, "NONNEGATIVE")
def test_conv(self): """Test the conv atom. """ a = np.ones((3, 1)) b = Parameter(2, nonneg=True) expr = cp.conv(a, b) assert expr.is_nonneg() self.assertEqual(expr.shape, (4, 1)) b = Parameter(2, nonpos=True) expr = cp.conv(a, b) assert expr.is_nonpos() with self.assertRaises(Exception) as cm: cp.conv(self.x, -1) self.assertEqual(str(cm.exception), "The first argument to conv must be constant.") with self.assertRaises(Exception) as cm: cp.conv([[0, 1], [0, 1]], self.x) self.assertEqual(str(cm.exception), "The arguments to conv must resolve to vectors.")