Skip to content

Commit 6f4c281

Browse files
author
Steven Diamond
committed
made AffExpression a subclass of expression
1 parent d4f41cd commit 6f4c281

25 files changed

+401
-567
lines changed

cvxpy/atoms/affine/transpose.py

+25-15
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@
2121
from ... import utilities as u
2222
from ...utilities import bool_mat_utils as bu
2323
from ...expressions.variables import Variable
24-
from ...constraints.affine import AffEqConstraint
24+
from ...expressions.affine import AffExpression
25+
import copy
2526

2627
class transpose(AffAtom):
2728
""" Matrix transpose. """
29+
# The string representation of the atom.
30+
def name(self):
31+
return "%s.T" % self.args[0]
32+
2833
# Returns the transpose of the given value.
2934
@AffAtom.numpy_numeric
3035
def numeric(self, values):
3136
return values[0].T
3237

3338
# Transposes shape, sign, and curvature.
34-
def set_context(self):
39+
def _dcp_attr(self):
3540
rows,cols = self.args[0].size
3641
shape = u.Shape(cols, rows)
3742

@@ -41,18 +46,23 @@ def set_context(self):
4146
conc_mat = bu.transpose(self.args[0].curvature.conc_mat)
4247
constant = self.args[0].curvature.constant
4348

44-
self._context = u.Context(u.Sign(neg_mat, pos_mat),
45-
u.Curvature(cvx_mat, conc_mat, constant),
46-
shape)
49+
return u.DCPAttr(u.Sign(neg_mat, pos_mat),
50+
u.Curvature(cvx_mat, conc_mat, constant),
51+
shape)
4752

4853
# Create a new variable equal to the argument transposed.
49-
@staticmethod
50-
def graph_implementation(var_args, size):
51-
X = Variable(size[1], size[0])
52-
obj = X.T.canonical_form()[0]
53-
return (obj, [AffEqConstraint(X, var_args[0])])
54-
55-
# Index the original argument as if it were the transpose,
56-
# then return the transpose.
57-
def index_object(self, key):
58-
return transpose(self.args[0][key[1], key[0]])
54+
def graph_implementation(self, arg_objs):
55+
X = Variable(self.size[1], self.size[0])
56+
# Create a coefficients dict for the transposed variable.
57+
# Each row in each block selects the appropriate elements
58+
# from the vectorized X.
59+
var_blocks = X.coefficients()[X]
60+
transpose_coeffs = X.init_coefficients(self.size[0], self.size[1])
61+
transpose_blocks = transpose_coeffs[X]
62+
for k in xrange(self.size[0]):
63+
for row in xrange(self.size[1]):
64+
transpose_blocks[row][k,:] = var_blocks[k][row,:]
65+
transpose_coeffs[X] = transpose_blocks
66+
# No dcp_attr given because none needed.
67+
obj = AffExpression(transpose_coeffs, None)
68+
return (obj, [X == arg_objs[0]])

cvxpy/atoms/affine/vstack.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def get_sign_curv(self):
6868
def set_context(self):
6969
shape = self.get_shape()
7070
sign,curvature = self.get_sign_curv()
71-
self._context = u.Context(sign, curvature, shape)
71+
self._context = u.DCPAttr(sign, curvature, shape)
7272

7373
@staticmethod
7474
def graph_implementation(var_args, size):

cvxpy/atoms/atom.py

+16-26
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
from .. import settings as s
2121
from .. import utilities as u
2222
from .. import interface as intf
23+
from ..expressions.constants import Constant
2324
from ..expressions.variables import Variable
2425
from ..expressions.expression import Expression
25-
from ..expressions.affine import AffExpression
26-
from ..constraints.affine import AffEqConstraint, AffLeqConstraint
27-
from constant_atom import ConstantAtom
2826
import abc
2927

3028
class Atom(Expression):
@@ -40,24 +38,22 @@ def __init__(self, *args):
4038
# Convert raw values to Constants.
4139
self.args = map(Expression.cast_to_const, args)
4240
self.subexpressions = self.args
43-
# Initialize context.
44-
self.set_context()
4541
super(Atom, self).__init__()
4642

4743
# Returns the string representation of the function call.
4844
def name(self):
4945
return "%s(%s)" % (self.__class__.__name__,
5046
", ".join([arg.name() for arg in self.args]))
5147

52-
# Sets signed curvature based on the arguments' signed curvatures.
53-
def set_context(self):
48+
# Determines the curvature, sign, and shape from the arguments.
49+
def _dcp_attr(self):
5450
# Initialize _shape. Raises an error for invalid argument sizes.
55-
self.set_shape()
51+
shape = self.shape_from_args()
5652
sign = self.sign_from_args()
5753
curvature = Atom.dcp_curvature(self.base_curvature(),
5854
self.args,
5955
self.monotonicity())
60-
self._context = u.Context(sign, curvature, self._shape)
56+
self._context = u.DCPAttr(sign, curvature, self._shape)
6157

6258
# Returns argument curvatures as a list.
6359
def argument_curvatures(self):
@@ -91,30 +87,24 @@ def dcp_curvature(curvature, args, monotonicities):
9187
def canonicalize(self):
9288
# Constant atoms are treated as a leaf.
9389
if self.curvature.is_constant():
94-
obj = AffExpression({s.CONSTANT: self}, self.shape)
95-
return (obj, [])
96-
# Non-constant atoms are expanded into an affine objective and constraints.
90+
return Constant(self.value).canonicalize()
9791
else:
98-
var_args = []
99-
final_constraints = []
92+
arg_objs = []
93+
constraints = []
10094
for arg in self.args:
101-
# canonicalize arguments.
102-
obj,constraints = arg.canonical_form()
103-
var_args.append(obj)
104-
final_constraints += constraints
105-
graph_var,graph_constr = self.graph_implementation(var_args, self.size)
106-
obj = u.Affine.cast_as_affine(graph_var)
107-
return (obj,final_constraints + graph_constr)
95+
obj,constr = arg.canonicalize()
96+
arg_objs.append(obj)
97+
constraints += constr
98+
graph_obj,graph_constr = self.graph_implementation(arg_objs)
99+
return (graph_obj, constraints + graph_constr)
108100

109-
# Returns a variable and set of affine/SOC
101+
# Returns an affine expression and list of
110102
# constraints equivalent to the atom.
111-
# var_args - a list of single variable arguments.
112-
# size - the dimensions of the variable to return.
103+
# arg_objs - the canonical objectives of the arguments.
113104
@abc.abstractmethod
114-
def graph_implementation(var_args, size):
105+
def graph_implementation(self, arg_objs):
115106
return NotImplemented
116107

117-
118108
# Wraps an atom's numeric function that requires numpy ndarrays as input.
119109
# Ensures both inputs and outputs are the correct matrix types.
120110
@staticmethod

cvxpy/atoms/constant_atom.py

-34
This file was deleted.

cvxpy/constraints/eq_constraint.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"""
1919

2020
from leq_constraint import LeqConstraint
21-
from affine import AffEqConstraint, AffLeqConstraint
2221

2322
class EqConstraint(LeqConstraint):
2423
OP_NAME = "=="
@@ -29,7 +28,5 @@ def is_dcp(self):
2928
# TODO expanding equality constraints.
3029
# Verify doesn't affect dual variables.
3130
def canonicalize(self):
32-
self._expr = (self.lh_exp - self.rh_exp)
3331
obj,constr = self._expr.canonical_form()
34-
dual_holder = AffEqConstraint(obj, 0, self)
35-
return (None, [dual_holder] + constr)
32+
return (None, [obj == 0] + constr)

cvxpy/constraints/leq_constraint.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@
1919

2020
from .. import interface as intf
2121
from .. import utilities as u
22-
from ..expressions import types
23-
from affine import AffEqConstraint, AffLeqConstraint
2422

25-
class LeqConstraint(u.Canonicalizable):
23+
class LeqConstraint(object):
2624
OP_NAME = "<="
2725
def __init__(self, lh_exp, rh_exp):
2826
self.lh_exp = lh_exp
2927
self.rh_exp = rh_exp
30-
super(LeqConstraint, self).__init__()
28+
self._expr = (self.lh_exp - self.rh_exp)
3129

3230
def name(self):
3331
return ' '.join([self.lh_exp.name(),
@@ -52,7 +50,15 @@ def is_dcp(self):
5250

5351
# Replace inequality with an equality with slack.
5452
def canonicalize(self):
55-
self._expr = (self.lh_exp - self.rh_exp)
5653
obj,constr = self._expr.canonical_form()
57-
dual_holder = AffLeqConstraint(obj, 0, self)
58-
return (None, [dual_holder] + constr)
54+
return (None, [obj <= 0] + constr)
55+
56+
def variables(self):
57+
return self._expr.variables()
58+
59+
def coefficients(self):
60+
return self._expr.coefficients()
61+
62+
# Save the value of the dual variable for the constraint's parent.
63+
def save_value(self, value):
64+
self._parent.dual_value = value

cvxpy/constraints/second_order.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
from .. import interface as intf
2121
from .. import utilities as u
22-
from affine import AffLeqConstraint
2322

2423
class SOC(object):
2524
"""
@@ -35,8 +34,7 @@ def __init__(self, t, x):
3534

3635
# Formats SOC constraints for the solver.
3736
def format(self):
38-
return [AffLeqConstraint(-self.t, 0),
39-
AffLeqConstraint(-self.x, 0)]
37+
return [-self.t <= 0, -self.x <= 0]
4038

4139
# The dimensions of the second-order cone.
4240
@property

cvxpy/constraints/semi_definite.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from .. import interface as intf
2121
from .. import utilities as u
2222
from ..expressions.variables import Variable
23-
from affine import AffEqConstraint, AffLeqConstraint
2423

2524
class SDP(object):
2625
"""
@@ -36,7 +35,7 @@ def __init__(self, A):
3635

3736
# Formats SDP constraints for the solver.
3837
def format(self):
39-
return [AffLeqConstraint(-self.A, 0)]
38+
return [-self.A <= 0]
4039

4140
# The dimensions of the semi-definite cone.
4241
@property

0 commit comments

Comments
 (0)