Skip to content

Commit 1a2001e

Browse files
authored
[WIP] interface dense matmul support in SparseDiffPy (#176)
* add some tests and sketch new converter * flattening * 2d A in converter * one more test
1 parent 6301f44 commit 1a2001e

3 files changed

Lines changed: 95 additions & 25 deletions

File tree

cvxpy/reductions/solvers/nlp_solvers/diff_engine/converters.py

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def normalize_shape(shape):
3232
shape = tuple(shape)
3333
return (1,) * (2 - len(shape)) + shape
3434

35-
3635
def _chain_add(children):
3736
"""Chain multiple children with binary adds: a + b + c -> add(add(a, b), c)."""
3837
result = children[0]
@@ -48,31 +47,49 @@ def _convert_matmul(expr, children):
4847
if left_arg.is_constant():
4948
A = left_arg.value
5049

51-
if not isinstance(A, sparse.csr_matrix):
52-
A = sparse.csr_matrix(A)
53-
54-
return _diffengine.make_left_matmul(
55-
children[1],
56-
A.data.astype(np.float64),
57-
A.indices.astype(np.int32),
58-
A.indptr.astype(np.int32),
59-
A.shape[0],
60-
A.shape[1],
61-
)
50+
if sparse.issparse(A):
51+
if not isinstance(A, sparse.csr_matrix):
52+
A = sparse.csr_matrix(A)
53+
54+
return _diffengine.make_sparse_left_matmul(
55+
children[1],
56+
A.data.astype(np.float64, copy=False),
57+
A.indices.astype(np.int32, copy=False),
58+
A.indptr.astype(np.int32, copy=False),
59+
A.shape[0],
60+
A.shape[1],
61+
)
62+
else:
63+
m, n = normalize_shape(A.shape)
64+
return _diffengine.make_dense_left_matmul(
65+
children[1],
66+
A.flatten(order='C'),
67+
m,
68+
n,
69+
)
6270
elif right_arg.is_constant():
6371
A = right_arg.value
64-
65-
if not isinstance(A, sparse.csr_matrix):
66-
A = sparse.csr_matrix(A)
67-
68-
return _diffengine.make_right_matmul(
69-
children[0],
70-
A.data.astype(np.float64),
71-
A.indices.astype(np.int32),
72-
A.indptr.astype(np.int32),
73-
A.shape[0],
74-
A.shape[1],
75-
)
72+
73+
if sparse.issparse(A):
74+
if not isinstance(A, sparse.csr_matrix):
75+
A = sparse.csr_matrix(A)
76+
77+
return _diffengine.make_sparse_right_matmul(
78+
children[0],
79+
A.data.astype(np.float64, copy=False),
80+
A.indices.astype(np.int32, copy=False),
81+
A.indptr.astype(np.int32, copy=False),
82+
A.shape[0],
83+
A.shape[1],
84+
)
85+
else:
86+
m, n = normalize_shape(A.shape)
87+
return _diffengine.make_dense_right_matmul(
88+
children[0],
89+
A.flatten(order='C'),
90+
m,
91+
n,
92+
)
7693
else:
7794
return _diffengine.make_matmul(children[0], children[1])
7895

cvxpy/tests/nlp_tests/derivative_checker.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ def check_constraint_values(self, x=None):
8989
python_values.append(constr_val)
9090

9191
python_values = np.hstack(python_values) if python_values else np.array([])
92-
9392
match = np.allclose(c_values, python_values, rtol=1e-10, atol=1e-10)
9493
return match
9594

cvxpy/tests/nlp_tests/stress_tests_diff_engine/test_matmul_sparse.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,57 @@ def test_dense_sparse_sparse(self):
7373
assert np.allclose(dense_val, csc_val)
7474
assert np.allclose(dense_sol, sparse_sol)
7575
assert np.allclose(dense_sol, csc_sol)
76+
77+
def test_dense_left_matmul(self):
78+
np.random.seed(0)
79+
m, n = 4, 4
80+
A = np.random.rand(m, n)
81+
X = cp.Variable((n, n), nonneg=True)
82+
B = np.random.rand(m, n)
83+
obj = cp.Minimize(cp.sum_squares(A @ X - B))
84+
constraints = []
85+
problem = cp.Problem(obj, constraints)
86+
problem.solve(nlp=True, verbose=True)
87+
checker = DerivativeChecker(problem)
88+
checker.run_and_assert()
89+
90+
def test_dense_right_matmul(self):
91+
np.random.seed(0)
92+
m, n = 4, 4
93+
A = np.random.rand(m, n)
94+
X = cp.Variable((n, n), nonneg=True)
95+
B = np.random.rand(m, n)
96+
obj = cp.Minimize(cp.sum_squares(X @ A - B))
97+
constraints = []
98+
problem = cp.Problem(obj, constraints)
99+
problem.solve(nlp=True, verbose=True)
100+
checker = DerivativeChecker(problem)
101+
checker.run_and_assert()
102+
103+
def test_sparse_and_dense_matmul(self):
104+
np.random.seed(0)
105+
m, n = 4, 4
106+
A = np.random.rand(m, n)
107+
C = sp.random(m, n, density=0.5)
108+
X = cp.Variable((n, n), nonneg=True)
109+
B = np.random.rand(m, n)
110+
obj = cp.Minimize(cp.sum_squares(A @ X @ C - B))
111+
constraints = []
112+
problem = cp.Problem(obj, constraints)
113+
problem.solve(nlp=True, verbose=True)
114+
checker = DerivativeChecker(problem)
115+
checker.run_and_assert()
116+
117+
def test_sparse_and_dense_matmul2(self):
118+
np.random.seed(0)
119+
m, n = 4, 3
120+
A = np.random.rand(n, m)
121+
C = sp.random(m, n, density=0.5)
122+
X = cp.Variable((n, n), nonneg=True)
123+
B = np.random.rand(m, m)
124+
obj = cp.Minimize(cp.sum_squares(C @ X @ A - B))
125+
constraints = []
126+
problem = cp.Problem(obj, constraints)
127+
problem.solve(nlp=True, verbose=True)
128+
checker = DerivativeChecker(problem)
129+
checker.run_and_assert()

0 commit comments

Comments
 (0)