Skip to content

Commit 367a77b

Browse files
authored
Fix multiply's and matmul's Hessians when their children share a subexpression (#169)
1 parent 6509536 commit 367a77b

4 files changed

Lines changed: 69 additions & 14 deletions

File tree

‎src/atoms/bivariate_full_dom/matmul.c‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,8 @@ static void wsum_hess_init_chain_rule(expr *node)
432432
wsum_hess_init(g);
433433

434434
/* H = C + C^T + H_f + H_g */
435-
node->wsum_hess =
436-
sum_4_matrices_alloc(mnode->C, mnode->CT, f->wsum_hess, g->wsum_hess);
435+
matrix *ops[4] = {mnode->C, mnode->CT, f->wsum_hess, g->wsum_hess};
436+
node->wsum_hess = sum_n_matrices_alloc(ops, 4);
437437

438438
/* allocate weight backprop workspace */
439439
if (!f->is_affine(f) || !g->is_affine(g))
@@ -461,23 +461,26 @@ static void eval_wsum_hess_chain_rule(expr *node, const double *w)
461461
BTA_matrices_fill_values(mnode->BJg, f->jacobian, mnode->C);
462462
transpose_matrices_fill_values(mnode->C, mnode->CT);
463463

464+
/* H = C + C^T + H_f + H_g, adding each term right after it is evaluated */
465+
sum_n_matrices_fill_zero(node->wsum_hess);
466+
sum_n_matrices_fill_add(mnode->C, 0, node->wsum_hess);
467+
sum_n_matrices_fill_add(mnode->CT, 1, node->wsum_hess);
468+
464469
/* compute Hessian of f */
465470
if (!is_f_affine)
466471
{
467472
Y_kron_I_vec(m, k, n, g->value, w, node->work->dwork);
468473
eval_wsum_hess(f, node->work->dwork);
469474
}
475+
sum_n_matrices_fill_add(f->wsum_hess, 2, node->wsum_hess);
470476

471477
/* compute Hessian of g */
472478
if (!is_g_affine)
473479
{
474480
I_kron_XT_vec(m, k, n, f->value, w, node->work->dwork);
475481
eval_wsum_hess(g, node->work->dwork);
476482
}
477-
478-
/* H = C + C^T + H_f + H_g */
479-
sum_4_matrices_fill_values(mnode->C, mnode->CT, f->wsum_hess, g->wsum_hess,
480-
node->wsum_hess);
483+
sum_n_matrices_fill_add(g->wsum_hess, 3, node->wsum_hess);
481484
}
482485

483486
expr *new_matmul(expr *x, expr *y)

‎src/atoms/bivariate_full_dom/multiply.c‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ static void wsum_hess_init_impl(expr *node)
161161
mul_node->CT = CT;
162162

163163
/* sparsity pattern of H = C + C^T + term2 + term3 */
164-
node->wsum_hess = sum_4_matrices_alloc(C, CT, x->wsum_hess, y->wsum_hess);
164+
matrix *ops[4] = {C, CT, x->wsum_hess, y->wsum_hess};
165+
node->wsum_hess = sum_n_matrices_alloc(ops, 4);
165166
}
166167
}
167168

@@ -189,8 +190,13 @@ static void eval_wsum_hess_impl(expr *node, const double *w)
189190
transpose_matrices_fill_values(mul_node->C, mul_node->CT);
190191

191192
// ---------------------------------------------------------------
192-
// compute term2 and term 3
193+
// compute H = C + C^T + term2 + term3, adding each term right
194+
// after it is evaluated
193195
// ---------------------------------------------------------------
196+
sum_n_matrices_fill_zero(node->wsum_hess);
197+
sum_n_matrices_fill_add(mul_node->C, 0, node->wsum_hess);
198+
sum_n_matrices_fill_add(mul_node->CT, 1, node->wsum_hess);
199+
194200
if (!is_x_affine)
195201
{
196202
for (int i = 0; i < node->size; i++)
@@ -199,6 +205,7 @@ static void eval_wsum_hess_impl(expr *node, const double *w)
199205
}
200206
eval_wsum_hess(x, node->work->dwork);
201207
}
208+
sum_n_matrices_fill_add(x->wsum_hess, 2, node->wsum_hess);
202209

203210
if (!is_y_affine)
204211
{
@@ -208,12 +215,7 @@ static void eval_wsum_hess_impl(expr *node, const double *w)
208215
}
209216
eval_wsum_hess(y, node->work->dwork);
210217
}
211-
212-
// ---------------------------------------------------------------
213-
// compute H = C + C^T + term2 + term3
214-
// ---------------------------------------------------------------
215-
sum_4_matrices_fill_values(mul_node->C, mul_node->CT, x->wsum_hess,
216-
y->wsum_hess, node->wsum_hess);
218+
sum_n_matrices_fill_add(y->wsum_hess, 3, node->wsum_hess);
217219
}
218220
}
219221

‎tests/all_tests.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ int main(void)
387387
mu_run_test(test_wsum_hess_sum_outer_product_sin_cos_left_matmul_dense,
388388
tests_run);
389389
mu_run_test(test_wsum_hess_add_shared_subexpr, tests_run);
390+
mu_run_test(test_wsum_hess_multiply_shared_subexpr, tests_run);
391+
mu_run_test(test_wsum_hess_matmul_shared_subexpr, tests_run);
390392
mu_run_test(test_wsum_hess_matmul_exp_exp, tests_run);
391393
mu_run_test(test_wsum_hess_matmul_sin_cos, tests_run);
392394
mu_run_test(test_wsum_hess_matmul_Ax_By, tests_run);

‎tests/wsum_hess/composite/test_chain_rule_wsum_hess.h‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,51 @@ const char *test_wsum_hess_add_shared_subexpr(void)
636636
free_expr(f);
637637
return 0;
638638
}
639+
640+
const char *test_wsum_hess_multiply_shared_subexpr(void)
641+
{
642+
/* multiply(log x, exp(log x)) and multiply(exp(log x), log x) with one
643+
* shared log x node: evaluating the side that contains log x re-evaluates
644+
* log x's Hessian with other weights, which must not leak into the other
645+
* side. */
646+
double u_vals[3] = {0.5, 1.0, 1.5};
647+
double w[3] = {1.0, 2.0, 3.0};
648+
649+
expr *x = new_variable(3, 1, 0, 3);
650+
expr *log_x = new_log(x);
651+
expr *f = new_elementwise_mult(log_x, new_exp(log_x));
652+
mu_assert("check_wsum_hess failed (shared node on the left)",
653+
check_wsum_hess(f, u_vals, w, NUMERICAL_DIFF_DEFAULT_H));
654+
free_expr(f);
655+
656+
x = new_variable(3, 1, 0, 3);
657+
log_x = new_log(x);
658+
f = new_elementwise_mult(new_exp(log_x), log_x);
659+
mu_assert("check_wsum_hess failed (shared node on the right)",
660+
check_wsum_hess(f, u_vals, w, NUMERICAL_DIFF_DEFAULT_H));
661+
free_expr(f);
662+
return 0;
663+
}
664+
665+
const char *test_wsum_hess_matmul_shared_subexpr(void)
666+
{
667+
/* log(X) @ exp(log(X)) and exp(log(X)) @ log(X) with one shared log(X)
668+
* node, X is 2x2 */
669+
double u_vals[4] = {0.5, 1.0, 1.5, 2.0};
670+
double w[4] = {1.0, 2.0, 3.0, 4.0};
671+
672+
expr *X = new_variable(2, 2, 0, 4);
673+
expr *log_X = new_log(X);
674+
expr *Z = new_matmul(log_X, new_exp(log_X));
675+
mu_assert("check_wsum_hess failed (shared node on the left)",
676+
check_wsum_hess(Z, u_vals, w, NUMERICAL_DIFF_DEFAULT_H));
677+
free_expr(Z);
678+
679+
X = new_variable(2, 2, 0, 4);
680+
log_X = new_log(X);
681+
Z = new_matmul(new_exp(log_X), log_X);
682+
mu_assert("check_wsum_hess failed (shared node on the right)",
683+
check_wsum_hess(Z, u_vals, w, NUMERICAL_DIFF_DEFAULT_H));
684+
free_expr(Z);
685+
return 0;
686+
}

0 commit comments

Comments
 (0)