コード例 #1
0
ファイル: test_complex.py プロジェクト: lkire/cvxpy
 def test_log_det(self):
     """Test log det.
     """
     P = np.arange(9) - 2j*np.arange(9)
     P = np.reshape(P, (3, 3))
     P = np.conj(P.T).dot(P)/100 + np.eye(3)*.1
     value = cvx.log_det(P).value
     X = Variable((3, 3), complex=True)
     prob = Problem(cvx.Maximize(cvx.log_det(X)), [X == P])
     result = prob.solve(solver=cvx.SCS, eps=1e-6)
     self.assertAlmostEqual(result, value, places=2)
コード例 #2
0
    def stuffed_objective(self, problem, extractor):
        # Extract to c.T * x + r
        C, R = extractor.affine(problem.objective.expr)

        c = np.asarray(C.todense()).flatten()
        boolean, integer = extract_mip_idx(problem.variables())
        x = Variable(extractor.N, boolean=boolean, integer=integer)

        new_obj = c.T * x + 0

        return new_obj, x, R[0]
コード例 #3
0
    def test_max(self) -> None:
        """Test max.
        """
        # One arg, test sign.
        self.assertEqual(cp.max(1).sign, s.NONNEG)
        self.assertEqual(cp.max(-2).sign, s.NONPOS)
        self.assertEqual(cp.max(Variable()).sign, s.UNKNOWN)
        self.assertEqual(cp.max(0).sign, s.ZERO)

        # Test with axis argument.
        self.assertEqual(cp.max(Variable(2), axis=0, keepdims=True).shape, (1,))
        self.assertEqual(cp.max(Variable(2), axis=1).shape, (2,))
        self.assertEqual(cp.max(Variable((2, 3)), axis=0, keepdims=True).shape, (1, 3))
        self.assertEqual(cp.max(Variable((2, 3)), axis=1).shape, (2,))

        # Invalid axis.
        with self.assertRaises(Exception) as cm:
            cp.max(self.x, axis=4)
        self.assertEqual(str(cm.exception),
                         "Invalid argument for axis.")
コード例 #4
0
def matrix_frac_canon(expr, args):
    X = args[0]  # n by m matrix.
    P = args[1]  # n by n matrix.

    if len(X.shape) == 1:
        X = reshape(X, (X.shape[0], 1))
    n, m = X.shape

    # Create a matrix with Schur complement T - X.T*P^-1*X.
    M = Variable((n + m, n + m), PSD=True)
    T = Variable((m, m))
    constraints = []
    # Fix M using the fact that P must be affine by the DCP rules.
    # M[0:n, 0:n] == P.
    constraints.append(M[0:n, 0:n] == P)
    # M[0:n, n:n+m] == X
    constraints.append(M[0:n, n:n + m] == X)
    # M[n:n+m, n:n+m] == T
    constraints.append(M[n:n + m, n:n + m] == T)
    return trace(T), constraints
コード例 #5
0
def exprval_in_vec_eq(expr, vec):
    # Reference: https://docs.mosek.com/modeling-cookbook/mio.html#fixed-set-of-values

    assert len(expr.shape) == 1
    n_entries = expr.shape[0]
    repeated_vec = np.broadcast_to(vec, (n_entries, len(vec)))
    z = Variable(repeated_vec.shape, boolean=True)

    main_con = cp.sum(cp.multiply(repeated_vec, z), axis=1) == expr
    aux_cons = [cp.sum(z, axis=1) == 1]
    return main_con, aux_cons
コード例 #6
0
 def test_params(self):
     """Test with parameters.
     """
     p = cvx.Parameter(imag=True, value=1j)
     x = Variable(2, complex=True)
     prob = Problem(cvx.Maximize(cvx.sum(cvx.imag(x) + cvx.real(x))),
                    [cvx.abs(p * x) <= 2])
     result = prob.solve()
     self.assertAlmostEqual(result, 4 * np.sqrt(2))
     val = np.ones(2) * np.sqrt(2)
     self.assertItemsAlmostEqual(x.value, val + 1j * val)
コード例 #7
0
 def test_quadratic_form(self) -> None:
     x = Variable(5)
     P = np.eye(5) - 2 * np.ones((5, 5))
     q = np.ones((5, 1))
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         s = x.T @ P @ x + q.T @ x
     self.assertFalse(s.is_constant())
     self.assertFalse(s.is_affine())
     self.assertTrue(s.is_quadratic())
     self.assertFalse(s.is_dcp())
コード例 #8
0
ファイル: sigma_max_canon.py プロジェクト: zhangys2/cvxpy
def sigma_max_canon(expr, args):
    A = args[0]
    n, m = A.shape
    X = Variable((n+m, n+m), PSD=True)

    shape = expr.shape
    t = Variable(shape)
    constraints = []

    # Fix X using the fact that A must be affine by the DCP rules.
    # X[0:n, 0:n] == I_n*t
    constraints.append(X[0:n, 0:n] == Constant(sp.eye(n)) * t)

    # X[0:n, n:n+m] == A
    constraints.append(X[0:n, n:n+m] == A)

    # X[n:n+m, n:n+m] == I_m*t
    constraints.append(X[n:n+m, n:n+m] == Constant(sp.eye(m)) * t)

    return t, constraints
コード例 #9
0
ファイル: test_complex.py プロジェクト: lkire/cvxpy
    def test_objective(self):
        """Test objectives.
        """
        x = Variable(complex=True)
        with self.assertRaises(Exception) as cm:
            Minimize(x)
        self.assertEqual(str(cm.exception), "The 'minimize' objective must be real valued.")

        with self.assertRaises(Exception) as cm:
            cvx.Maximize(x)
        self.assertEqual(str(cm.exception), "The 'maximize' objective must be real valued.")
コード例 #10
0
    def test_min(self):
        """Test min.
        """
        # One arg, test sign.
        self.assertEqual(cp.min(1).sign, s.NONNEG)
        self.assertEqual(cp.min(-2).sign, s.NONPOS)
        self.assertEqual(cp.min(Variable()).sign, s.UNKNOWN)
        self.assertEqual(cp.min(0).sign, s.ZERO)

        # Test with axis argument.
        self.assertEqual(cp.min(Variable(2), axis=0).shape, tuple())
        self.assertEqual(cp.min(Variable(2), axis=1).shape, (2,))
        self.assertEqual(cp.min(Variable((2, 3)), axis=0).shape, (3,))
        self.assertEqual(cp.min(Variable((2, 3)), axis=1).shape, (2,))

        # Invalid axis.
        with self.assertRaises(Exception) as cm:
            cp.min(self.x, axis=4)
        self.assertEqual(str(cm.exception),
                         "Invalid argument for axis.")
コード例 #11
0
ファイル: test_grad.py プロジェクト: sjschneider/cvxpy
    def test_affine(self) -> None:
        """Test grad for affine atoms.
        """
        expr = -self.a
        self.a.value = 2
        self.assertAlmostEqual(expr.grad[self.a], -1)

        expr = 2 * self.a
        self.a.value = 2
        self.assertAlmostEqual(expr.grad[self.a], 2)

        expr = self.a / 2
        self.a.value = 2
        self.assertAlmostEqual(expr.grad[self.a], 0.5)

        expr = -(self.x)
        self.x.value = [3, 4]
        val = np.zeros((2, 2)) - np.diag([1, 1])
        self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)

        expr = -(self.A)
        self.A.value = [[1, 2], [3, 4]]
        val = np.zeros((4, 4)) - np.diag([1, 1, 1, 1])
        self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val)

        expr = self.A[0, 1]
        self.A.value = [[1, 2], [3, 4]]
        val = np.zeros((4, 1))
        val[2] = 1
        self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val)

        z = Variable(3)
        expr = cp.hstack([self.x, z])
        self.x.value = [1, 2]
        z.value = [1, 2, 3]
        val = np.zeros((2, 5))
        val[:, 0:2] = np.eye(2)
        self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)

        val = np.zeros((3, 5))
        val[:, 2:] = np.eye(3)
        self.assertItemsAlmostEqual(expr.grad[z].toarray(), val)

        # cumsum
        expr = cp.cumsum(self.x)
        self.x.value = [1, 2]
        val = np.ones((2, 2))
        val[1, 0] = 0
        self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)

        expr = cp.cumsum(self.x[:, None], axis=1)
        self.x.value = [1, 2]
        val = np.eye(2)
        self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)
コード例 #12
0
    def stuffed_objective(self, problem, extractor):
        # extract to x.T * P * x + q.T * x + r
        # TODO need to copy objective?
        P, q, r = extractor.quad_form(problem.objective.expr)

        # concatenate all variables in one vector
        boolean, integer = extract_mip_idx(problem.variables())
        x = Variable(extractor.N, boolean=boolean, integer=integer)
        new_obj = QuadForm(x, P) + q.T * x

        return new_obj, x, r
コード例 #13
0
ファイル: test_robustness.py プロジェクト: lkire/cvxpy
 def test_large_square(self):
     """Test large number of variables squared.
     """
     self.skipTest("Too slow.")
     for n in [10, 20, 30, 40, 50]:
         A = np.arange(n * n)
         A = np.reshape(A, (n, n))
         x = Variable((n, n))
         p = Problem(Minimize(at.square(x[0, 0])), [x >= A])
         result = p.solve()
         self.assertAlmostEqual(result, 0)
コード例 #14
0
ファイル: huber_canon.py プロジェクト: xinyueshen/cvxpy
def huber_canon(expr, args):
    M = expr.M
    x = args[0]
    shape = expr.shape
    n = Variable(shape)
    s = Variable(shape)

    # n**2 + 2*M*|s|
    # TODO(akshayka): Make use of recursion inherent to canonicalization
    # process and just return a power / abs expressions for readability sake
    power_expr = power(n, 2)
    n2, constr_sq = power_canon(power_expr, power_expr.args)
    abs_expr = abs(s)
    abs_s, constr_abs = abs_canon(abs_expr, abs_expr.args)
    obj = n2 + 2 * M * abs_s

    constraints = constr_sq + constr_abs
    constraints.append(x == s + n)

    return obj, constraints
コード例 #15
0
 def test_index(self):
     """Test the copy function for index.
     """
     # Test copy with args=None
     shape = (5, 4)
     A = Variable(shape)
     atom = A[0:2, 0:1]
     copy = atom.copy()
     self.assertTrue(type(copy) is type(atom))
     # A new object is constructed, so copy.args == atom.args but copy.args
     # is not atom.args.
     self.assertEqual(copy.args, atom.args)
     self.assertFalse(copy.args is atom.args)
     self.assertEqual(copy.get_data(), atom.get_data())
     # Test copy with new args
     B = Variable((4, 5))
     copy = atom.copy(args=[B])
     self.assertTrue(type(copy) is type(atom))
     self.assertTrue(copy.args[0] is B)
     self.assertEqual(copy.get_data(), atom.get_data())
コード例 #16
0
ファイル: test_atoms.py プロジェクト: zhouyonglong/cvxpy
    def test_sum_largest(self):
        """Test the sum_largest atom and related atoms.
        """
        with self.assertRaises(Exception) as cm:
            cp.sum_largest(self.x, -1)
        self.assertEqual(str(cm.exception),
                         "Second argument must be a positive integer.")

        with self.assertRaises(Exception) as cm:
            cp.lambda_sum_largest(self.x, 2.4)
        self.assertEqual(str(cm.exception),
                         "First argument must be a square matrix.")

        with self.assertRaises(Exception) as cm:
            cp.lambda_sum_largest(Variable((2, 2)), 2.4)
        self.assertEqual(str(cm.exception),
                         "Second argument must be a positive integer.")

        with self.assertRaises(ValueError) as cm:
            cp.lambda_sum_largest([[1, 2], [3, 4]], 2).value
        self.assertEqual(str(cm.exception),
                         "Input matrix was not Hermitian/symmetric.")

        # Test copy with args=None
        atom = cp.sum_largest(self.x, 2)
        copy = atom.copy()
        self.assertTrue(type(copy) is type(atom))
        # A new object is constructed, so copy.args == atom.args but copy.args
        # is not atom.args.
        self.assertEqual(copy.args, atom.args)
        self.assertFalse(copy.args is atom.args)
        self.assertEqual(copy.get_data(), atom.get_data())
        # Test copy with new args
        copy = atom.copy(args=[self.y])
        self.assertTrue(type(copy) is type(atom))
        self.assertTrue(copy.args[0] is self.y)
        self.assertEqual(copy.get_data(), atom.get_data())
        # Test copy with lambda_sum_largest, which is in fact an AddExpression
        atom = cp.lambda_sum_largest(Variable((2, 2)), 2)
        copy = atom.copy()
        self.assertTrue(type(copy) is type(atom))
コード例 #17
0
    def setUp(self):
        self.a = Variable(name='a')
        self.b = Variable(name='b')
        self.c = Variable(name='c')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')

        # TODO(akshayka): Why are these solvers commented out? If it is
        # because their interfaces are not yet implemented, then a comment
        # along those lines should exist.
        self.solvers = [ECOS()]  #, GUROBI(), MOSEK(), SCS(), CVXOPT(), GLPK()]
コード例 #18
0
    def test_affine_atoms_canon(self):
        """Test canonicalization for affine atoms.
        """
        # Scalar.
        x = Variable()
        expr = cvx.imag(x + 1j * x)
        prob = Problem(Minimize(expr), [x >= 0])
        result = prob.solve()
        self.assertAlmostEqual(result, 0)
        self.assertAlmostEqual(x.value, 0)

        x = Variable(imag=True)
        expr = 1j * x
        prob = Problem(Minimize(expr), [cvx.imag(x) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -1)
        self.assertAlmostEqual(x.value, 1j)

        x = Variable(2)
        expr = x / 1j
        prob = Problem(Minimize(expr[0] * 1j + expr[1] * 1j),
                       [cvx.real(x + 1j) >= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -np.inf)
        prob = Problem(Minimize(expr[0] * 1j + expr[1] * 1j),
                       [cvx.real(x + 1j) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -2)
        self.assertItemsAlmostEqual(x.value, [1, 1])
        prob = Problem(
            Minimize(expr[0] * 1j + expr[1] * 1j),
            [cvx.real(x + 1j) >= 1, cvx.conj(x) <= 0])
        result = prob.solve()
        self.assertAlmostEqual(result, np.inf)

        x = Variable((2, 2))
        y = Variable((3, 2), complex=True)
        expr = cvx.vstack([x, y])
        prob = Problem(Minimize(cvx.sum(cvx.imag(cvx.conj(expr)))),
                       [x == 0, cvx.real(y) == 0,
                        cvx.imag(y) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -6)
        self.assertItemsAlmostEqual(y.value, 1j * np.ones((3, 2)))
        self.assertItemsAlmostEqual(x.value, np.zeros((2, 2)))

        x = Variable((2, 2))
        y = Variable((3, 2), complex=True)
        expr = cvx.vstack([x, y])
        prob = Problem(Minimize(cvx.sum(cvx.imag(expr.H))),
                       [x == 0, cvx.real(y) == 0,
                        cvx.imag(y) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -6)
        self.assertItemsAlmostEqual(y.value, 1j * np.ones((3, 2)))
        self.assertItemsAlmostEqual(x.value, np.zeros((2, 2)))
コード例 #19
0
    def test_variable(self):
        x = Variable(2)
        y = Variable(2)
        assert y.name() != x.name()

        x = Variable(2, name='x')
        y = Variable()
        self.assertEqual(x.name(), 'x')
        self.assertEqual(x.shape, (2,))
        self.assertEqual(y.shape, tuple())
        self.assertEqual(x.curvature, s.AFFINE)
        # self.assertEqual(x.canonical_form[0].shape, (2, 1))
        # self.assertEqual(x.canonical_form[1], [])

        self.assertEqual(repr(self.x), "Variable((2,))")
        self.assertEqual(repr(self.A), "Variable((2, 2))")

        # # Scalar variable
        # coeff = self.a.coefficients()
        # self.assertEqual(coeff[self.a.id], [1])

        # # Vector variable.
        # coeffs = x.coefficients()
        # self.assertItemsEqual(coeffs.keys(), [x.id])
        # vec = coeffs[x.id][0]
        # self.assertEqual(vec.shape, (2,2))
        # self.assertEqual(vec[0,0], 1)

        # # Matrix variable.
        # coeffs = self.A.coefficients()
        # self.assertItemsEqual(coeffs.keys(), [self.A.id])
        # self.assertEqual(len(coeffs[self.A.id]), 2) or 0 in self.shape
        # mat = coeffs[self.A.id][1]
        # self.assertEqual(mat.shape, (2,4))
        # self.assertEqual(mat[0,2], 1)

        with self.assertRaises(Exception) as cm:
            p = Variable((2, 2), diag=True, symmetric=True)
        self.assertEqual(str(cm.exception), "Cannot set more than one special attribute in Variable.")

        with self.assertRaises(Exception) as cm:
            p = Variable((2, 0))
        self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0).")

        with self.assertRaises(Exception) as cm:
            p = Variable((2, .5))
        self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0.5).")

        with self.assertRaises(Exception) as cm:
            p = Variable(2, 1)
        self.assertEqual(str(cm.exception), "Variable name 1 must be a string.")
コード例 #20
0
ファイル: test_domain.py プロジェクト: zhb0318/cvxpy
    def setUp(self):
        self.a = Variable(name='a')

        self.x = Variable(2, name='x')
        self.y = Variable(2, name='y')
        self.z = Variable(3, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')
コード例 #21
0
ファイル: test_robustness.py プロジェクト: zhb0318/cvxpy
 def test_large_sum(self):
     """Test large number of variables summed.
     """
     for n in [10, 20, 30, 40, 50]:
         A = np.arange(n * n)
         A = np.reshape(A, (n, n))
         x = Variable((n, n))
         p = Problem(Minimize(at.sum(x)), [x >= A])
         result = p.solve()
         answer = n * n * (n * n + 1) / 2 - n * n
         print(result - answer)
         self.assertAlmostEqual(result, answer)
コード例 #22
0
 def _value_impl(self):
     from cvxpy.problems.problem import Problem
     from cvxpy.problems.objective import Maximize
     y_val = self.args[0].value.round(decimals=9).ravel(order='F')
     x_flat = self._parent.x.flatten()
     cons = self._parent.constraints
     if len(cons) == 0:
         dummy = Variable()
         cons = [dummy == 1]
     prob = Problem(Maximize(y_val @ x_flat), cons)
     val = prob.solve(solver='SCS', eps=1e-6)
     return val
コード例 #23
0
    def stuffed_objective(self, problem, extractor):
        # extract to 0.5 * x.T * P * x + q.T * x + r
        expr = problem.objective.expr.copy()
        params_to_P, params_to_q = extractor.quad_form(expr)
        # Handle 0.5 factor.
        params_to_P = 2 * params_to_P

        # concatenate all variables in one vector
        boolean, integer = extract_mip_idx(problem.variables())
        x = Variable(extractor.x_length, boolean=boolean, integer=integer)

        return params_to_P, params_to_q, x
コード例 #24
0
    def test_sum_smallest(self):
        """Test the sum_smallest atom and related atoms.
        """
        with self.assertRaises(Exception) as cm:
            cp.sum_smallest(self.x, -1)
        self.assertEqual(str(cm.exception),
                         "Second argument must be a positive integer.")

        with self.assertRaises(Exception) as cm:
            cp.lambda_sum_smallest(Variable((2, 2)), 2.4)
        self.assertEqual(str(cm.exception),
                         "Second argument must be a positive integer.")
コード例 #25
0
ファイル: test_expressions.py プロジェクト: xinyueshen/cvxpy
    def test_log_log_curvature(self) -> None:
        """Test that the curvature string is populated for log-log expressions.
        """
        x = Variable(pos=True)
        monomial = x * x * x
        assert monomial.curvature == s.LOG_LOG_AFFINE

        posynomial = x * x * x + x
        assert posynomial.curvature == s.LOG_LOG_CONVEX

        llcv = 1 / (x * x * x + x)
        assert llcv.curvature == s.LOG_LOG_CONCAVE
コード例 #26
0
ファイル: test_atoms.py プロジェクト: zhouyonglong/cvxpy
 def test_vec_to_upper_tri(self):
     from cvxpy.atoms.affine.upper_tri import vec_to_upper_tri
     x = Variable(shape=(3,))
     X = vec_to_upper_tri(x)
     x.value = np.array([1, 2, 3])
     actual = X.value
     expect = np.array([[1, 2], [0, 3]])
     assert np.allclose(actual, expect)
     y = Variable(shape=(1,))
     y.value = np.array([4])
     Y = vec_to_upper_tri(y, strict=True)
     actual = Y.value
     expect = np.array([[0, 4], [0, 0]])
     assert np.allclose(actual, expect)
     A_expect = np.array([[0, 11, 12, 13],
                          [0, 0, 16, 17],
                          [0, 0, 0, 21],
                          [0, 0, 0, 0]])
     a = np.array([11, 12, 13, 16, 17, 21])
     A_actual = vec_to_upper_tri(a, strict=True).value
     assert np.allclose(A_actual, A_expect)
コード例 #27
0
 def _compute_conic_repr_of_set(self):
     if len(self.constraints) == 0:
         dummy = Variable()
         constrs = [dummy == 1]
     else:
         constrs = self.constraints
     A, b, K = scs_coniclift(self.x, constrs)
     K_sels = scs_cone_selectors(K)
     self._A = A
     self._b = b
     self._K_sels = K_sels
     pass
コード例 #28
0
    def test_neg_indices(self):
        """Test negative indices.
        """
        c = Constant([[1, 2], [3, 4]])
        exp = c[-1, -1]
        self.assertEqual(exp.value, 4)
        self.assertEqual(exp.shape, tuple())
        self.assertEqual(exp.curvature, s.CONSTANT)

        c = Constant([1, 2, 3, 4])
        exp = c[1:-1]
        self.assertItemsAlmostEqual(exp.value, [2, 3])
        self.assertEqual(exp.shape, (2, ))
        self.assertEqual(exp.curvature, s.CONSTANT)

        c = Constant([1, 2, 3, 4])
        exp = c[::-1]
        self.assertItemsAlmostEqual(exp.value, [4, 3, 2, 1])
        self.assertEqual(exp.shape, (4, ))
        self.assertEqual(exp.curvature, s.CONSTANT)

        x = Variable(4)
        self.assertEqual(x[::-1].shape, (4, ))
        Problem(Minimize(0), [x[::-1] == c]).solve()
        self.assertItemsAlmostEqual(x.value, [4, 3, 2, 1])

        x = Variable(2)
        self.assertEqual(x[::-1].shape, (2, ))

        x = Variable(100, name="x")
        self.assertEqual("x[0:99]", str(x[:-1]))

        c = Constant([[1, 2], [3, 4]])
        expr = c[0, 2:0:-1]
        self.assertEqual(expr.shape, (1, ))
        self.assertAlmostEqual(expr.value, 3)

        expr = c[0, 2::-1]
        self.assertEqual(expr.shape, (2, ))
        self.assertItemsAlmostEqual(expr.value, [3, 1])
コード例 #29
0
    def stuffed_objective(self, problem, inverse_data):
        extractor = CoeffExtractor(inverse_data)
        # Extract to c.T * x, store r
        C, R = extractor.get_coeffs(problem.objective.expr)

        c = np.asarray(C.todense()).flatten()
        boolean, integer = extract_mip_idx(problem.variables())
        x = Variable(inverse_data.x_length, boolean=boolean, integer=integer)

        new_obj = c.T * x + 0

        inverse_data.r = R[0]
        return new_obj, x
コード例 #30
0
def geo_mean_canon(expr, args):
    x = args[0]
    w = expr.w
    shape = expr.shape
    t = Variable(shape)

    x_list = [x[i] for i in range(len(w))]

    # todo: catch cases where we have (0, 0, 1)?
    # todo: what about curvature case (should be affine) in trivial
    #       case of (0, 0 , 1)?
    # should this behavior match with what we do in power?
    return t, gm_constrs(t, x_list, w)