Skip to content

Commit cf12877

Browse files
authored
Merge pull request #9 from dance858/prod
[Ready for review] Prod atom
2 parents aaebdce + d824b83 commit cf12877

6 files changed

Lines changed: 501 additions & 0 deletions

File tree

include/other.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@
77

88
expr *new_quad_form(expr *child, CSR_Matrix *Q);
99

10+
/* product of all entries, without axis argument */
11+
expr *new_prod(expr *child);
12+
1013
#endif /* OTHER_H */

include/subexpr.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ typedef struct sum_expr
4040
struct int_double_pair *int_double_pairs; /* for sorting jacobian entries */
4141
} sum_expr;
4242

43+
/* Product of all entries */
44+
typedef struct prod_expr
45+
{
46+
expr base;
47+
int num_of_zeros;
48+
int zero_index; /* index of zero element when num_of_zeros == 1 */
49+
double prod_nonzero; /* product of non-zero elements */
50+
} prod_expr;
51+
4352
/* Horizontal stack (concatenate) */
4453
typedef struct hstack_expr
4554
{

src/other/prod.c

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
#include "other.h"
2+
#include <assert.h>
3+
#include <math.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
#define IS_ZERO(x) (fabs((x)) < 1e-8)
8+
9+
static inline void wsum_hess_no_zeros(expr *node, const double *w);
10+
static inline void wsum_hess_one_zero(expr *node, const double *w);
11+
static inline void wsum_hess_two_zeros(expr *node, const double *w);
12+
static inline void wsum_hess_many_zeros(expr *node, const double *w);
13+
14+
static void forward(expr *node, const double *u)
15+
{
16+
expr *x = node->left;
17+
18+
/* forward pass of child */
19+
x->forward(x, u);
20+
21+
/* local forward pass and count zeros */
22+
double prod_nonzero = 1.0;
23+
int zeros = 0;
24+
int zero_idx = -1;
25+
for (int i = 0; i < x->size; i++)
26+
{
27+
if (IS_ZERO(x->value[i]))
28+
{
29+
zeros++;
30+
zero_idx = i;
31+
}
32+
else
33+
{
34+
prod_nonzero *= x->value[i];
35+
}
36+
}
37+
38+
node->value[0] = (zeros > 0) ? 0.0 : prod_nonzero;
39+
prod_expr *pnode = (prod_expr *) node;
40+
pnode->num_of_zeros = zeros;
41+
pnode->zero_index = zero_idx;
42+
pnode->prod_nonzero = prod_nonzero;
43+
}
44+
45+
static void jacobian_init(expr *node)
46+
{
47+
expr *x = node->left;
48+
49+
/* initialize child's jacobian */
50+
x->jacobian_init(x);
51+
52+
/* if x is a variable */
53+
if (x->var_id != NOT_A_VARIABLE)
54+
{
55+
node->jacobian = new_csr_matrix(1, node->n_vars, x->size);
56+
node->jacobian->p[0] = 0;
57+
node->jacobian->p[1] = x->size;
58+
for (int j = 0; j < x->size; j++)
59+
{
60+
node->jacobian->i[j] = x->var_id + j;
61+
}
62+
}
63+
else
64+
{
65+
assert(false && "not implemented");
66+
}
67+
}
68+
69+
static void eval_jacobian(expr *node)
70+
{
71+
expr *x = node->left;
72+
prod_expr *pnode = (prod_expr *) node;
73+
int num_of_zeros = pnode->num_of_zeros;
74+
75+
/* if x is a variable */
76+
if (x->var_id != NOT_A_VARIABLE)
77+
{
78+
if (num_of_zeros == 0)
79+
{
80+
for (int j = 0; j < x->size; j++)
81+
{
82+
node->jacobian->x[j] = node->value[0] / x->value[j];
83+
}
84+
}
85+
else if (num_of_zeros == 1)
86+
{
87+
memset(node->jacobian->x, 0, sizeof(double) * x->size);
88+
node->jacobian->x[pnode->zero_index] = pnode->prod_nonzero;
89+
}
90+
else
91+
{
92+
memset(node->jacobian->x, 0, sizeof(double) * x->size);
93+
}
94+
}
95+
else
96+
{
97+
assert(false && "not implemented");
98+
}
99+
}
100+
101+
static void wsum_hess_init(expr *node)
102+
{
103+
expr *x = node->left;
104+
105+
/* if x is a variable */
106+
if (x->var_id != NOT_A_VARIABLE)
107+
{
108+
/* allocate n_vars x n_vars CSR matrix with dense block */
109+
int block_size = x->size;
110+
int nnz = block_size * block_size;
111+
node->wsum_hess = new_csr_matrix(node->n_vars, node->n_vars, nnz);
112+
113+
/* fill row pointers for the dense block */
114+
for (int i = 0; i < block_size; i++)
115+
{
116+
node->wsum_hess->p[x->var_id + i] = i * block_size;
117+
}
118+
119+
/* fill row pointers for rows after the block */
120+
for (int i = x->var_id + block_size; i <= node->n_vars; i++)
121+
{
122+
node->wsum_hess->p[i] = nnz;
123+
}
124+
125+
/* fill column indices for the dense block */
126+
for (int i = 0; i < block_size; i++)
127+
{
128+
for (int j = 0; j < block_size; j++)
129+
{
130+
node->wsum_hess->i[i * block_size + j] = x->var_id + j;
131+
}
132+
}
133+
}
134+
else
135+
{
136+
assert(false && "not implemented");
137+
}
138+
}
139+
140+
static void eval_wsum_hess(expr *node, const double *w)
141+
{
142+
expr *x = node->left;
143+
int num_of_zeros = ((prod_expr *) node)->num_of_zeros;
144+
145+
/* if x is a variable */
146+
if (x->var_id != NOT_A_VARIABLE)
147+
{
148+
if (num_of_zeros == 0)
149+
{
150+
wsum_hess_no_zeros(node, w);
151+
}
152+
else if (num_of_zeros == 1)
153+
{
154+
wsum_hess_one_zero(node, w);
155+
}
156+
else if (num_of_zeros == 2)
157+
{
158+
wsum_hess_two_zeros(node, w);
159+
}
160+
else
161+
{
162+
wsum_hess_many_zeros(node, w);
163+
}
164+
}
165+
else
166+
{
167+
assert(false && "not implemented");
168+
}
169+
}
170+
171+
static bool is_affine(const expr *node)
172+
{
173+
(void) node;
174+
return false;
175+
}
176+
177+
static void free_type_data(expr *node)
178+
{
179+
(void) node;
180+
}
181+
182+
expr *new_prod(expr *child)
183+
{
184+
/* Output is scalar: 1 x 1 */
185+
prod_expr *pnode = (prod_expr *) calloc(1, sizeof(prod_expr));
186+
expr *node = &pnode->base;
187+
init_expr(node, 1, 1, child->n_vars, forward, jacobian_init, eval_jacobian,
188+
is_affine, free_type_data);
189+
node->wsum_hess_init = wsum_hess_init;
190+
node->eval_wsum_hess = eval_wsum_hess;
191+
node->left = child;
192+
expr_retain(child);
193+
return node;
194+
}
195+
196+
// ---------------------------------------------------------------------------------------
197+
// Helper functions for Hessian evaluation
198+
// ---------------------------------------------------------------------------------------
199+
static inline void wsum_hess_no_zeros(expr *node, const double *w)
200+
{
201+
double *x = node->left->value;
202+
int n = node->left->size;
203+
double wf = w[0] * node->value[0];
204+
205+
for (int i = 0; i < n; i++)
206+
{
207+
for (int j = 0; j < n; j++)
208+
{
209+
if (i == j)
210+
{
211+
node->wsum_hess->x[i * n + j] = 0.0;
212+
}
213+
else
214+
{
215+
node->wsum_hess->x[i * n + j] = wf / (x[i] * x[j]);
216+
}
217+
}
218+
}
219+
}
220+
221+
static inline void wsum_hess_one_zero(expr *node, const double *w)
222+
{
223+
expr *x = node->left;
224+
double *H = node->wsum_hess->x;
225+
memset(H, 0, sizeof(double) * (x->size * x->size));
226+
int p = ((prod_expr *) node)->zero_index;
227+
double prod_nonzero = ((prod_expr *) node)->prod_nonzero;
228+
double w_prod = w[0] * prod_nonzero;
229+
230+
/* fill row p and column p */
231+
for (int j = 0; j < x->size; j++)
232+
{
233+
if (j == p) continue;
234+
235+
double hess_val = w_prod / x->value[j];
236+
H[p * x->size + j] = hess_val;
237+
H[j * x->size + p] = hess_val;
238+
}
239+
}
240+
241+
static inline void wsum_hess_two_zeros(expr *node, const double *w)
242+
{
243+
expr *x = node->left;
244+
int n = x->size;
245+
memset(node->wsum_hess->x, 0, sizeof(double) * (n * n));
246+
247+
/* find indices p and q where x[p] = x[q] = 0 */
248+
int p = -1, q = -1;
249+
for (int i = 0; i < n; i++)
250+
{
251+
if (IS_ZERO(x->value[i]))
252+
{
253+
if (p == -1)
254+
{
255+
p = i;
256+
}
257+
else
258+
{
259+
q = i;
260+
break;
261+
}
262+
}
263+
}
264+
assert(p != -1 && q != -1);
265+
266+
double hess_val = w[0] * ((prod_expr *) node)->prod_nonzero;
267+
node->wsum_hess->x[p * n + q] = hess_val;
268+
node->wsum_hess->x[q * n + p] = hess_val;
269+
}
270+
271+
static inline void wsum_hess_many_zeros(expr *node, const double *w)
272+
{
273+
expr *x = node->left;
274+
memset(node->wsum_hess->x, 0, sizeof(double) * (x->size * x->size));
275+
(void) w;
276+
}

tests/all_tests.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "jacobian_tests/test_elementwise_mult.h"
1616
#include "jacobian_tests/test_hstack.h"
1717
#include "jacobian_tests/test_log.h"
18+
#include "jacobian_tests/test_prod.h"
1819
#include "jacobian_tests/test_quad_form.h"
1920
#include "jacobian_tests/test_quad_over_lin.h"
2021
#include "jacobian_tests/test_rel_entr.h"
@@ -31,6 +32,7 @@
3132
#include "wsum_hess/elementwise/test_xexp.h"
3233
#include "wsum_hess/test_hstack.h"
3334
#include "wsum_hess/test_multiply.h"
35+
#include "wsum_hess/test_prod.h"
3436
#include "wsum_hess/test_quad_form.h"
3537
#include "wsum_hess/test_quad_over_lin.h"
3638
#include "wsum_hess/test_rel_entr.h"
@@ -75,6 +77,9 @@ int main(void)
7577
mu_run_test(test_quad_form, tests_run);
7678
/* commented out - see test_quad_form.h */
7779
// mu_run_test(test_quad_form2, tests_run);
80+
mu_run_test(test_jacobian_prod_no_zero, tests_run);
81+
mu_run_test(test_jacobian_prod_one_zero, tests_run);
82+
mu_run_test(test_jacobian_prod_two_zeros, tests_run);
7883
mu_run_test(test_jacobian_sum_log, tests_run);
7984
mu_run_test(test_jacobian_sum_mult, tests_run);
8085
mu_run_test(test_jacobian_sum_log_axis_0, tests_run);
@@ -103,6 +108,10 @@ int main(void)
103108
mu_run_test(test_wsum_hess_sum_log_linear, tests_run);
104109
mu_run_test(test_wsum_hess_sum_log_axis0, tests_run);
105110
mu_run_test(test_wsum_hess_sum_log_axis1, tests_run);
111+
mu_run_test(test_wsum_hess_prod_no_zero, tests_run);
112+
mu_run_test(test_wsum_hess_prod_one_zero, tests_run);
113+
mu_run_test(test_wsum_hess_prod_two_zeros, tests_run);
114+
mu_run_test(test_wsum_hess_prod_many_zeros, tests_run);
106115
mu_run_test(test_wsum_hess_rel_entr_1, tests_run);
107116
mu_run_test(test_wsum_hess_rel_entr_2, tests_run);
108117
mu_run_test(test_wsum_hess_hstack, tests_run);

0 commit comments

Comments
 (0)