Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check number of tensors at call time #21

Merged
merged 1 commit into from
Nov 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions cvxpylayers/tensorflow/cvxpylayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ def __call__(self, *parameters, solver_args={}):
a list of optimal variable values, one for each CVXPY Variable
supplied to the constructor.
"""
# TODO(akshakya): Validate shapes of params.
if len(parameters) != len(self.params):
raise ValueError('A value must be provided for each CVXPY '
'parameter; received %d values, expected %d' % (
raise ValueError('A tensor must be provided for each CVXPY '
'parameter; received %d tensors, expected %d' % (
len(parameters), len(self.params)))
compute = tf.custom_gradient(
lambda *parameters: self._compute(parameters, solver_args))
Expand Down
18 changes: 15 additions & 3 deletions cvxpylayers/tensorflow/test_cvxpylayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,19 @@ def test_not_enough_parameters(self):
objective = lam * cp.norm(x, 1) + lam2 * cp.sum_squares(x)
prob = cp.Problem(cp.Minimize(objective))
with self.assertRaisesRegex(ValueError, "The layer's parameters.*"):
CvxpyLayer(prob, [lam], [x])
CvxpyLayer(prob, [lam], [x]) # noqa: F841

def test_not_enough_parameters_at_call_time(self):
x = cp.Variable(1)
lam = cp.Parameter(1, nonneg=True)
lam2 = cp.Parameter(1, nonneg=True)
objective = lam * cp.norm(x, 1) + lam2 * cp.sum_squares(x)
prob = cp.Problem(cp.Minimize(objective))
layer = CvxpyLayer(prob, [lam, lam2], [x])
with self.assertRaisesRegex(
ValueError,
'A tensor must be provided for each CVXPY parameter.*'):
layer(lam)

def test_non_dpp(self):
x = cp.Variable(1)
Expand All @@ -263,7 +275,7 @@ def test_non_dpp(self):
objective = lam * cp.norm(x, 1)
prob = cp.Problem(cp.Minimize(objective))
with self.assertRaisesRegex(ValueError, 'Problem must be DPP.'):
CvxpyLayer(prob, [lam], [x, y])
CvxpyLayer(prob, [lam], [x, y]) # noqa: F841

def test_too_many_variables(self):
x = cp.Variable(1)
Expand All @@ -272,7 +284,7 @@ def test_too_many_variables(self):
objective = lam * cp.norm(x, 1)
prob = cp.Problem(cp.Minimize(objective))
with self.assertRaisesRegex(ValueError, 'Argument `variables`.*'):
CvxpyLayer(prob, [lam], [x, y])
CvxpyLayer(prob, [lam], [x, y]) # noqa: F841

def test_infeasible(self):
x = cp.Variable(1)
Expand Down
22 changes: 13 additions & 9 deletions cvxpylayers/torch/cvxpylayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,16 @@ def __init__(self, problem, parameters, variables):
Variables determines the order of the optimal variable
values returned from the forward pass.
"""
if not problem.is_dpp():
raise ValueError('Problem must be DPP.')

super(CvxpyLayer, self).__init__()

assert set(problem.parameters()) == set(parameters), \
"The layer's parameters must exactly match problem.parameters"
assert set(variables).issubset(set(problem.variables())), \
"Argument variables must be a subset of problem.variables"
assert hasattr(problem, "get_problem_data"), \
"cvxpy problem does not support ASA form; please upgrade cvxpy"
if not problem.is_dpp():
raise ValueError('Problem must be DPP.')
if not set(problem.parameters()) == set(parameters):
raise ValueError("The layer's parameters must exactly match "
"problem.parameters")
if not set(variables).issubset(set(problem.variables())):
raise ValueError("Argument variables must be a subset of "
"problem.variables")

self.param_order = parameters
self.param_ids = [p.id for p in self.param_order]
Expand Down Expand Up @@ -105,6 +104,11 @@ def forward(self, *params, solver_args={}):
a list of optimal variable values, one for each CVXPY Variable
supplied to the constructor.
"""
# TODO(akshakya): Validate shapes of params.
if len(params) != len(self.param_ids):
raise ValueError('A tensor must be provided for each CVXPY '
'parameter; received %d tensors, expected %d' % (
len(params), len(self.param_ids)))
info = {}
f = _CvxpyLayerFn(
param_order=self.param_order,
Expand Down
18 changes: 15 additions & 3 deletions cvxpylayers/torch/test_cvxpylayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def test_sdp(self):
constraints)
layer = CvxpyLayer(prob, [C] + A + b, [X])
torch.autograd.gradcheck(lambda *x: layer(*x,
solver_args={"eps": 1e-12}),
solver_args={'eps': 1e-12}),
[C_tch] + A_tch + b_tch,
eps=1e-6,
atol=1e-3,
Expand All @@ -227,16 +227,28 @@ def test_not_enough_parameters(self):
lam2 = cp.Parameter(1, nonneg=True)
objective = lam * cp.norm(x, 1) + lam2 * cp.sum_squares(x)
prob = cp.Problem(cp.Minimize(objective))
with self.assertRaises(AssertionError):
with self.assertRaises(ValueError):
layer = CvxpyLayer(prob, [lam], [x]) # noqa: F841

def test_not_enough_parameters_at_call_time(self):
x = cp.Variable(1)
lam = cp.Parameter(1, nonneg=True)
lam2 = cp.Parameter(1, nonneg=True)
objective = lam * cp.norm(x, 1) + lam2 * cp.sum_squares(x)
prob = cp.Problem(cp.Minimize(objective))
layer = CvxpyLayer(prob, [lam, lam2], [x]) # noqa: F841
with self.assertRaisesRegex(
ValueError,
'A tensor must be provided for each CVXPY parameter.*'):
layer(lam)

def test_too_many_variables(self):
x = cp.Variable(1)
y = cp.Variable(1)
lam = cp.Parameter(1, nonneg=True)
objective = lam * cp.norm(x, 1)
prob = cp.Problem(cp.Minimize(objective))
with self.assertRaises(AssertionError):
with self.assertRaises(ValueError):
layer = CvxpyLayer(prob, [lam], [x, y]) # noqa: F841

def test_infeasible(self):
Expand Down