Skip to content

Commit 06e5539

Browse files
committed
add initial tests
1 parent f979db7 commit 06e5539

1 file changed

Lines changed: 243 additions & 0 deletions

File tree

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
"""
2+
Copyright, the CVXPY authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
17+
import numpy as np
18+
import pytest
19+
import scipy.sparse as sp
20+
21+
import cvxpy as cp
22+
from cvxpy.reductions.solvers.defines import INSTALLED_SOLVERS
23+
from cvxpy.tests.nlp_tests.derivative_checker import DerivativeChecker
24+
25+
26+
@pytest.mark.skipif('IPOPT' not in INSTALLED_SOLVERS, reason='IPOPT is not installed.')
27+
class TestPermutedDense:
28+
# Stress tests for the permuted_dense (PD) Jacobian/Hessian path in the diff engine.
29+
# PD originates only at left_matmul when a dense constant multiplies a leaf vector
30+
# variable, so all tests here use vector variables.
31+
32+
def test_multiply_pd_pd(self):
33+
# A dense, B dense
34+
np.random.seed(0)
35+
n, m = 5, 6
36+
A = np.random.rand(m, n)
37+
B = np.random.rand(m, n)
38+
x = cp.Variable(n, bounds=[-1, 1])
39+
y = cp.Variable(n, bounds=[-1, 1])
40+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(A @ x), cp.cos(B @ y))))
41+
prob = cp.Problem(obj)
42+
prob.solve(nlp=True)
43+
checker = DerivativeChecker(prob)
44+
checker.run_and_assert()
45+
46+
def test_multiply_pd_sparse(self):
47+
# A dense, B sparse
48+
np.random.seed(0)
49+
n, m = 5, 6
50+
A = np.random.rand(m, n)
51+
B = sp.random(m, n, density=0.5, format='csr')
52+
x = cp.Variable(n, bounds=[-1, 1])
53+
y = cp.Variable(n, bounds=[-1, 1])
54+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(A @ x), cp.cos(B @ y))))
55+
prob = cp.Problem(obj)
56+
prob.solve(nlp=True)
57+
checker = DerivativeChecker(prob)
58+
checker.run_and_assert()
59+
60+
def test_multiply_sparse_pd(self):
61+
# A sparse, B dense
62+
np.random.seed(0)
63+
n, m = 5, 6
64+
A = sp.random(m, n, density=0.5, format='csr')
65+
B = np.random.rand(m, n)
66+
x = cp.Variable(n, bounds=[-1, 1])
67+
y = cp.Variable(n, bounds=[-1, 1])
68+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(A @ x), cp.cos(B @ y))))
69+
prob = cp.Problem(obj)
70+
prob.solve(nlp=True)
71+
checker = DerivativeChecker(prob)
72+
checker.run_and_assert()
73+
74+
def test_multiply_pd_plain_var(self):
75+
np.random.seed(0)
76+
n, m = 5, 6
77+
A = np.random.rand(m, n)
78+
x = cp.Variable(n, bounds=[-1, 1])
79+
y = cp.Variable(m, bounds=[-1, 1])
80+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(A @ x), cp.cos(y))))
81+
prob = cp.Problem(obj)
82+
prob.solve(nlp=True)
83+
checker = DerivativeChecker(prob)
84+
checker.run_and_assert()
85+
86+
def test_multiply_plain_var_pd(self):
87+
np.random.seed(0)
88+
n, m = 5, 6
89+
A = np.random.rand(m, n)
90+
x = cp.Variable(n, bounds=[-1, 1])
91+
y = cp.Variable(m, bounds=[-1, 1])
92+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(y), cp.cos(A @ x))))
93+
prob = cp.Problem(obj)
94+
prob.solve(nlp=True)
95+
checker = DerivativeChecker(prob)
96+
checker.run_and_assert()
97+
98+
def test_pd_index_propagation(self):
99+
# Indexing into a permuted dense propagates permuted dense via index_alloc /
100+
# index_fill_values. Use a non-sorted index with duplicates to stress the
101+
# permutation path.
102+
np.random.seed(0)
103+
n, m = 5, 8
104+
A = np.random.rand(m, n)
105+
B = np.random.rand(m, n)
106+
x = cp.Variable(n, bounds=[-1, 1])
107+
y = cp.Variable(n, bounds=[-1, 1])
108+
idx_A = [0, 2, 4, 1, 3, 0, 7]
109+
idx_B = [0, 4, 2, 3, 1, 0, 7]
110+
obj = cp.Minimize(
111+
cp.sum(cp.multiply(cp.sin((A @ x)[idx_A]), cp.cos((B @ y)[idx_B])))
112+
)
113+
prob = cp.Problem(obj)
114+
prob.solve(nlp=True)
115+
checker = DerivativeChecker(prob)
116+
checker.run_and_assert()
117+
118+
def test_pd_transpose_propagation(self):
119+
# Transpose of a PD result. Column-shape variables make .T non-trivial:
120+
# (A @ x) is (m, 1), (A @ x).T is (1, m).
121+
np.random.seed(0)
122+
n, m = 5, 6
123+
A = np.random.rand(m, n)
124+
B = np.random.rand(m, n)
125+
x = cp.Variable((n, 1), bounds=[-1, 1])
126+
y = cp.Variable((n, 1), bounds=[-1, 1])
127+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin((A @ x).T), cp.cos((B @ y).T))))
128+
prob = cp.Problem(obj)
129+
prob.solve(nlp=True)
130+
checker = DerivativeChecker(prob)
131+
checker.run_and_assert()
132+
133+
def test_pd_broadcast_propagation(self):
134+
# Reshape PD results to column / row vectors and let multiply broadcast.
135+
np.random.seed(0)
136+
n, m = 5, 6
137+
A = np.random.rand(m, n)
138+
B = np.random.rand(m, n)
139+
x = cp.Variable(n, bounds=[-1, 1])
140+
y = cp.Variable(n, bounds=[-1, 1])
141+
obj = cp.Minimize(cp.sum(cp.multiply(
142+
cp.reshape(cp.sin(A @ x), (m, 1), order='F'),
143+
cp.reshape(cp.cos(B @ y), (1, m), order='F'),
144+
)))
145+
prob = cp.Problem(obj)
146+
prob.solve(nlp=True)
147+
checker = DerivativeChecker(prob)
148+
checker.run_and_assert()
149+
150+
def test_deep_composition(self):
151+
# A deep composition of PD results
152+
np.random.seed(0)
153+
n, m = 5, 10
154+
A = np.random.rand(m, n)
155+
B = sp.random(n, m, density=0.5, format='csr')
156+
C = np.random.rand(m, n)
157+
x = cp.Variable(n, bounds=[-1, 1])
158+
y = cp.Variable(n, bounds=[-1, 1])
159+
obj = cp.Minimize(cp.sum(cp.multiply(
160+
cp.sin(A @ cp.cos(B @ cp.logistic(C @ x))),
161+
cp.cos(A @ cp.cos(B @ cp.logistic(C @ y))),
162+
)))
163+
prob = cp.Problem(obj)
164+
prob.solve(nlp=True)
165+
checker = DerivativeChecker(prob)
166+
checker.run_and_assert()
167+
168+
def test_multiply_pd_pd_right(self):
169+
# Right matmul with dense A and dense B
170+
np.random.seed(0)
171+
n, m = 5, 6
172+
A = np.random.rand(n, m)
173+
B = np.random.rand(n, m)
174+
x = cp.Variable(n, bounds=[-1, 1])
175+
y = cp.Variable(n, bounds=[-1, 1])
176+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(x @ A), cp.cos(y @ B))))
177+
prob = cp.Problem(obj)
178+
prob.solve(nlp=True)
179+
checker = DerivativeChecker(prob)
180+
checker.run_and_assert()
181+
182+
def test_multiply_pd_sparse_right(self):
183+
# Right matmul with dense A and sparse B
184+
np.random.seed(0)
185+
n, m = 5, 6
186+
A = np.random.rand(n, m)
187+
B = sp.random(n, m, density=0.5, format='csr')
188+
x = cp.Variable(n, bounds=[-1, 1])
189+
y = cp.Variable(n, bounds=[-1, 1])
190+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin(x @ A), cp.cos(y @ B))))
191+
prob = cp.Problem(obj)
192+
prob.solve(nlp=True)
193+
checker = DerivativeChecker(prob)
194+
checker.run_and_assert()
195+
196+
def test_pd_index_propagation_right(self):
197+
# Right matmul with index
198+
np.random.seed(0)
199+
n, m = 5, 8
200+
A = np.random.rand(n, m)
201+
B = np.random.rand(n, m)
202+
x = cp.Variable(n, bounds=[-1, 1])
203+
y = cp.Variable(n, bounds=[-1, 1])
204+
idx_A = [0, 2, 4, 1, 3, 0, 7]
205+
idx_B = [0, 4, 2, 3, 1, 0, 7]
206+
obj = cp.Minimize(
207+
cp.sum(cp.multiply(cp.sin((x @ A)[idx_A]), cp.cos((y @ B)[idx_B])))
208+
)
209+
prob = cp.Problem(obj)
210+
prob.solve(nlp=True)
211+
checker = DerivativeChecker(prob)
212+
checker.run_and_assert()
213+
214+
def test_pd_transpose_propagation_right(self):
215+
# Right matmul with transpose
216+
np.random.seed(0)
217+
n, m = 5, 6
218+
A = np.random.rand(n, m)
219+
B = np.random.rand(n, m)
220+
x = cp.Variable((1, n), bounds=[-1, 1])
221+
y = cp.Variable((1, n), bounds=[-1, 1])
222+
obj = cp.Minimize(cp.sum(cp.multiply(cp.sin((x @ A).T), cp.cos((y @ B).T))))
223+
prob = cp.Problem(obj)
224+
prob.solve(nlp=True)
225+
checker = DerivativeChecker(prob)
226+
checker.run_and_assert()
227+
228+
def test_pd_broadcast_propagation_right(self):
229+
# Reshape right-rooted PD results and force (m, 1) * (1, m) broadcast.
230+
np.random.seed(0)
231+
n, m = 5, 6
232+
A = np.random.rand(n, m)
233+
B = np.random.rand(n, m)
234+
x = cp.Variable(n, bounds=[-1, 1])
235+
y = cp.Variable(n, bounds=[-1, 1])
236+
obj = cp.Minimize(cp.sum(cp.multiply(
237+
cp.reshape(cp.sin(x @ A), (m, 1), order='F'),
238+
cp.reshape(cp.cos(y @ B), (1, m), order='F'),
239+
)))
240+
prob = cp.Problem(obj)
241+
prob.solve(nlp=True)
242+
checker = DerivativeChecker(prob)
243+
checker.run_and_assert()

0 commit comments

Comments
 (0)