Skip to content

Commit ff5dd34

Browse files
committed
replace right matmul with left matmul
1 parent a2e08a8 commit ff5dd34

2 files changed

Lines changed: 14 additions & 123 deletions

File tree

include/utils/CSR_Matrix.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,8 @@ CSR_Matrix *kron_identity_csr(const CSR_Matrix *A, int p);
4646
void csr_matvec(const CSR_Matrix *A, const double *x, double *y, int col_offset);
4747
void csr_matvec_wo_offset(const CSR_Matrix *A, const double *x, double *y);
4848

49-
/* C = z^T A is assumed to have one row. C must have column indices pre-computed
50-
and transposed matrix AT must be provided. Fills in values of C only.
51-
*/
49+
/* Computes values of the row matrix C = z^T A (column indices must have been
50+
pre-computed) and transposed matrix AT must be provided) */
5251
void csr_matvec_fill_values(const CSR_Matrix *AT, const double *z, CSR_Matrix *C);
5352

5453
/* Insert value into CSR matrix A with just one row at col_idx. Assumes that A

src/bivariate/right_matmul.c

Lines changed: 12 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -15,137 +15,29 @@
1515
* See the License for the specific language governing permissions and
1616
* limitations under the License.
1717
*/
18+
#include "affine.h"
1819
#include "bivariate.h"
1920
#include "subexpr.h"
21+
#include "utils/CSR_Matrix.h"
2022
#include "utils/linalg_sparse_matmuls.h"
2123
#include <stdlib.h>
2224

2325
/* This file implements the atom 'right_matmul' corresponding to the operation y =
2426
f(x) @ A, where A is a given matrix and f(x) is an arbitrary expression.
25-
Here, f(x) can be a vector-valued expression and a matrix-valued
26-
expression. The dimensions are f(x) - p x n, A - n x q, y - p x q.
27-
Note that here A does not have global column indices but it is a local matrix.
28-
This is an important distinction compared to linear_op_expr.
29-
30-
* To compute the forward pass: vec(y) = B @ vec(f(x)),
31-
where B = A^T kron I_p is a Kronecker product of size (p*q) x (p*n).
32-
33-
* To compute the Jacobian: J_y = B @ J_f(x), where J_f(x) is the
34-
Jacobian of f(x) of size (p*n) x n_vars.
35-
36-
* To compute the contribution to the Lagrange Hessian: we form
37-
w_child = B^T @ w and then evaluate the hessian of f(x).
38-
*/
39-
40-
static void forward(expr *node, const double *u)
41-
{
42-
expr *x = node->left;
43-
44-
/* child's forward pass */
45-
node->left->forward(node->left, u);
46-
47-
/* y = x * A, vec(y) = B @ vec(x) */
48-
csr_matvec_wo_offset(((right_matmul_expr *) node)->B, x->value, node->value);
49-
}
50-
51-
static bool is_affine(const expr *node)
52-
{
53-
return node->left->is_affine(node->left);
54-
}
55-
56-
static void free_type_data(expr *node)
57-
{
58-
right_matmul_expr *right_node = (right_matmul_expr *) node;
59-
free_csr_matrix(right_node->B);
60-
free_csr_matrix(right_node->BT);
61-
if (right_node->CSC_work)
62-
{
63-
free_csc_matrix(right_node->CSC_work);
64-
}
65-
right_node->B = NULL;
66-
right_node->BT = NULL;
67-
right_node->CSC_work = NULL;
68-
}
69-
70-
static void jacobian_init(expr *node)
71-
{
72-
expr *x = node->left;
73-
right_matmul_expr *right_node = (right_matmul_expr *) node;
74-
75-
/* initialize child's jacobian and precompute sparsity of its transpose */
76-
x->jacobian_init(x);
77-
right_node->CSC_work = csr_to_csc_fill_sparsity(x->jacobian, node->iwork);
78-
79-
/* precompute sparsity of this node's jacobian */
80-
node->jacobian = csr_csc_matmul_alloc(right_node->B, right_node->CSC_work);
81-
}
82-
83-
static void eval_jacobian(expr *node)
84-
{
85-
expr *x = node->left;
86-
right_matmul_expr *right_node = (right_matmul_expr *) node;
87-
88-
/* evaluate child's jacobian and convert to CSC*/
89-
x->eval_jacobian(x);
90-
csr_to_csc_fill_values(x->jacobian, right_node->CSC_work, node->iwork);
91-
92-
/* compute this node's jacobian */
93-
csr_csc_matmul_fill_values(right_node->B, right_node->CSC_work, node->jacobian);
94-
}
95-
96-
static void wsum_hess_init(expr *node)
97-
{
98-
/* initialize child's hessian */
99-
expr *x = node->left;
100-
x->wsum_hess_init(x);
101-
102-
/* allocate this node's hessian with the same sparsity as child's */
103-
node->wsum_hess = new_csr_matrix(node->n_vars, node->n_vars, x->wsum_hess->nnz);
104-
memcpy(node->wsum_hess->p, x->wsum_hess->p, (node->n_vars + 1) * sizeof(int));
105-
memcpy(node->wsum_hess->i, x->wsum_hess->i, x->wsum_hess->nnz * sizeof(int));
106-
107-
/* Allocate workspace for B^T @ w */
108-
node->dwork = (double *) malloc(x->d1 * x->d2 * sizeof(double));
109-
}
110-
111-
static void eval_wsum_hess(expr *node, const double *w)
112-
{
113-
/* Compute B^T @ w, where B = A^T ⊗ I_p */
114-
right_matmul_expr *right_node = (right_matmul_expr *) node;
115-
116-
/* B^T @ w computes the weights for the child expression */
117-
csr_matvec_wo_offset(right_node->BT, w, node->dwork);
118-
119-
/* Propagate to child */
120-
node->left->eval_wsum_hess(node->left, node->dwork);
121-
memcpy(node->wsum_hess->x, node->left->wsum_hess->x,
122-
node->wsum_hess->nnz * sizeof(double));
123-
}
124-
27+
We implement this by expressing right matmul in terms of left matmul and
28+
transpose: f(x) @ A = (A^T @ f(x)^T)^T. */
12529
expr *new_right_matmul(expr *u, const CSR_Matrix *A)
12630
{
127-
/* Allocate the type-specific struct */
128-
right_matmul_expr *right_matmul_node =
129-
(right_matmul_expr *) calloc(1, sizeof(right_matmul_expr));
130-
expr *node = &right_matmul_node->base;
131-
132-
/* Output dimensions: u is p x n, A is n x q, output is p x q */
133-
int p = u->d1;
134-
int q = A->n;
31+
/* We can express right matmul using left matmul and transpose:
32+
u @ A = (A^T @ u^T)^T. */
33+
int *work_transpose = (int *) malloc(A->n * sizeof(int));
34+
CSR_Matrix *AT = transpose(A, work_transpose);
13535

136-
init_expr(node, p, q, u->n_vars, forward, jacobian_init, eval_jacobian,
137-
is_affine, wsum_hess_init, eval_wsum_hess, free_type_data);
138-
node->left = u;
139-
expr_retain(u);
36+
expr *u_transpose = new_transpose(u);
37+
expr *left_matmul = new_left_matmul(u_transpose, AT);
38+
expr *node = new_transpose(left_matmul);
14039

141-
/* create B = A^T kron I_p and its transpose */
142-
node->iwork = (int *) malloc(node->n_vars * sizeof(int));
143-
CSR_Matrix *AT = transpose(A, node->iwork);
144-
right_matmul_node->B = kron_identity_csr(AT, p);
145-
right_matmul_node->BT = kron_identity_csr(A, p);
14640
free_csr_matrix(AT);
147-
148-
right_matmul_node->CSC_work = NULL;
149-
41+
free(work_transpose);
15042
return node;
15143
}

0 commit comments

Comments
 (0)