1- // SPDX-License-Identifier: Apache-2.0
2-
31#include "affine.h"
42#include "subexpr.h"
53#include <stdlib.h>
64#include <string.h>
75
8- /* Index/slicing: y = child[indices] where indices is a list of flattened positions */
6+ /* Index/slicing: y = child[indices] where indices is a list of flat positions */
97
108/* Check if indices array contains duplicates using a bitmap.
119 * Returns true if duplicates exist, false otherwise. */
12- static bool check_for_duplicates (const int * indices , int n_selected , int max_idx )
10+ static bool check_for_duplicates (const int * indices , int n_idxs , int max_idx )
1311{
14- bool * seen = (bool * )calloc (max_idx , sizeof (bool ));
12+ bool * seen = (bool * ) calloc (max_idx , sizeof (bool ));
1513 bool has_dup = false;
16- for (int i = 0 ; i < n_selected && !has_dup ; i ++ )
14+ for (int i = 0 ; i < n_idxs && !has_dup ; i ++ )
1715 {
1816 if (seen [indices [i ]])
1917 {
@@ -27,115 +25,109 @@ static bool check_for_duplicates(const int *indices, int n_selected, int max_idx
2725
2826static void forward (expr * node , const double * u )
2927{
30- expr * child = node -> left ;
31- index_expr * idx = (index_expr * )node ;
28+ expr * x = node -> left ;
29+ index_expr * idx = (index_expr * ) node ;
3230
3331 /* child's forward pass */
34- child -> forward (child , u );
32+ x -> forward (x , u );
3533
3634 /* gather selected elements */
37- for (int i = 0 ; i < idx -> n_selected ; i ++ )
35+ for (int i = 0 ; i < idx -> n_idxs ; i ++ )
3836 {
39- node -> value [i ] = child -> value [idx -> indices [i ]];
37+ node -> value [i ] = x -> value [idx -> indices [i ]];
4038 }
4139}
4240
4341static void jacobian_init (expr * node )
4442{
45- expr * child = node -> left ;
46- index_expr * idx = (index_expr * )node ;
47-
48- child -> jacobian_init (child );
49- CSR_Matrix * J_child = child -> jacobian ;
50-
51- /* count nnz */
52- int nnz = 0 ;
53- for (int i = 0 ; i < idx -> n_selected ; i ++ )
54- {
55- int row = idx -> indices [i ];
56- nnz += J_child -> p [row + 1 ] - J_child -> p [row ];
57- }
43+ expr * x = node -> left ;
44+ index_expr * idx = (index_expr * ) node ;
45+ x -> jacobian_init (x );
5846
59- node -> jacobian = new_csr_matrix ( idx -> n_selected , node -> n_vars , nnz ) ;
60- CSR_Matrix * J = node -> jacobian ;
47+ CSR_Matrix * Jx = x -> jacobian ;
48+ CSR_Matrix * J = new_csr_matrix ( node -> size , node -> n_vars , Jx -> nnz ) ;
6149
62- /* fill p and i arrays in one pass */
50+ /* set sparsity pattern */
6351 J -> p [0 ] = 0 ;
64- for (int i = 0 ; i < idx -> n_selected ; i ++ )
52+ for (int i = 0 ; i < idx -> n_idxs ; i ++ )
6553 {
6654 int row = idx -> indices [i ];
67- int src = J_child -> p [row ];
68- int len = J_child -> p [row + 1 ] - src ;
69- memcpy (J -> i + J -> p [i ], J_child -> i + src , len * sizeof (int ));
55+ int len = Jx -> p [row + 1 ] - Jx -> p [row ];
56+ memcpy (J -> i + J -> p [i ], Jx -> i + Jx -> p [row ], len * sizeof (int ));
7057 J -> p [i + 1 ] = J -> p [i ] + len ;
7158 }
59+
60+ node -> jacobian = J ;
7261}
7362
7463static void eval_jacobian (expr * node )
7564{
76- expr * child = node -> left ;
77- index_expr * idx = (index_expr * )node ;
78-
79- child -> eval_jacobian (child );
65+ expr * x = node -> left ;
66+ index_expr * idx = (index_expr * ) node ;
67+ x -> eval_jacobian (x );
8068
8169 CSR_Matrix * J = node -> jacobian ;
82- CSR_Matrix * J_child = child -> jacobian ;
70+ CSR_Matrix * Jx = x -> jacobian ;
8371
84- for (int i = 0 ; i < idx -> n_selected ; i ++ )
72+ for (int i = 0 ; i < idx -> n_idxs ; i ++ )
8573 {
8674 int row = idx -> indices [i ];
8775 int len = J -> p [i + 1 ] - J -> p [i ];
88- memcpy (J -> x + J -> p [i ], J_child -> x + J_child -> p [row ], len * sizeof (double ));
76+ memcpy (J -> x + J -> p [i ], Jx -> x + Jx -> p [row ], len * sizeof (double ));
8977 }
9078}
9179
9280static void wsum_hess_init (expr * node )
9381{
94- expr * child = node -> left ;
95- index_expr * idx = (index_expr * )node ;
82+ expr * x = node -> left ;
9683
9784 /* initialize child's wsum_hess */
98- child -> wsum_hess_init (child );
99-
100- /* allocate scatter buffer (zeroed) */
101- idx -> parent_w = (double * )calloc (child -> size , sizeof (double ));
102-
103- /* wsum_hess inherits from child (affine has no local Hessian) */
104- /* We need to allocate our own to avoid aliasing issues */
105- CSR_Matrix * child_hess = child -> wsum_hess ;
106- node -> wsum_hess = new_csr_matrix (child_hess -> m , child_hess -> n , child_hess -> nnz );
107- memcpy (node -> wsum_hess -> p , child_hess -> p , (child_hess -> m + 1 ) * sizeof (int ));
108- memcpy (node -> wsum_hess -> i , child_hess -> i , child_hess -> nnz * sizeof (int ));
85+ x -> wsum_hess_init (x );
86+
87+ /* for setting weight vector to evaluate hessian of child */
88+ node -> dwork = (double * ) calloc (x -> size , sizeof (double ));
89+
90+ /* in the implementation of eval_wsum_hess we evaluate the
91+ child's hessian with a weight vector that has w[i] = 0
92+ if i is not included in idx->indices. This can lead to
93+ many numerical zeros in child->wsum_hess that are actually
94+ structural zeros, but we do not try to exploit that sparsity
95+ right now. */
96+ CSR_Matrix * H_child = x -> wsum_hess ;
97+ node -> wsum_hess = new_csr_matrix (H_child -> m , H_child -> n , H_child -> nnz );
98+ memcpy (node -> wsum_hess -> p , H_child -> p , (H_child -> m + 1 ) * sizeof (int ));
99+ memcpy (node -> wsum_hess -> i , H_child -> i , H_child -> nnz * sizeof (int ));
109100}
110101
111102static void eval_wsum_hess (expr * node , const double * w )
112103{
113104 expr * child = node -> left ;
114- index_expr * idx = (index_expr * )node ;
105+ index_expr * idx = (index_expr * ) node ;
115106
116107 if (idx -> has_duplicates )
117108 {
118- /* slow path: must zero and accumulate for repeated indices */
119- memset (idx -> parent_w , 0 , child -> size * sizeof (double ));
120- for (int i = 0 ; i < idx -> n_selected ; i ++ )
109+ /* zero and accumulate for repeated indices */
110+ memset (node -> dwork , 0 , child -> size * sizeof (double ));
111+ for (int i = 0 ; i < idx -> n_idxs ; i ++ )
121112 {
122- idx -> parent_w [idx -> indices [i ]] += w [i ];
113+ node -> dwork [idx -> indices [i ]] += w [i ];
123114 }
124115 }
125116 else
126117 {
127- /* fast path: direct write (no memset needed, no accumulation) */
128- for (int i = 0 ; i < idx -> n_selected ; i ++ )
118+ /* direct write (no memset needed, no accumulation) */
119+ for (int i = 0 ; i < idx -> n_idxs ; i ++ )
129120 {
130- idx -> parent_w [idx -> indices [i ]] = w [i ];
121+ node -> dwork [idx -> indices [i ]] = w [i ];
131122 }
132123 }
133124
134125 /* delegate to child */
135- child -> eval_wsum_hess (child , idx -> parent_w );
126+ child -> eval_wsum_hess (child , node -> dwork );
136127
137128 /* copy values from child */
138- memcpy (node -> wsum_hess -> x , child -> wsum_hess -> x , child -> wsum_hess -> nnz * sizeof (double ));
129+ memcpy (node -> wsum_hess -> x , child -> wsum_hess -> x ,
130+ child -> wsum_hess -> nnz * sizeof (double ));
139131}
140132
141133static bool is_affine (const expr * node )
@@ -145,44 +137,37 @@ static bool is_affine(const expr *node)
145137
146138static void free_type_data (expr * node )
147139{
148- index_expr * idx = (index_expr * )node ;
140+ index_expr * idx = (index_expr * ) node ;
149141 if (idx -> indices )
150142 {
151143 free (idx -> indices );
152144 idx -> indices = NULL ;
153145 }
154- if (idx -> parent_w )
155- {
156- free (idx -> parent_w );
157- idx -> parent_w = NULL ;
158- }
159146}
160147
161- expr * new_index (expr * child , const int * indices , int n_selected )
148+ expr * new_index (expr * child , const int * indices , int n_idxs )
162149{
163150 /* allocate type-specific struct */
164- index_expr * idx = (index_expr * )calloc (1 , sizeof (index_expr ));
151+ index_expr * idx = (index_expr * ) calloc (1 , sizeof (index_expr ));
165152 expr * node = & idx -> base ;
166153
167- /* output shape is (n_selected , 1) - flattened */
168- init_expr (node , n_selected , 1 , child -> n_vars , forward , jacobian_init ,
169- eval_jacobian , is_affine , free_type_data );
154+ /* output shape is (n_idxs , 1) - flattened */
155+ init_expr (node , n_idxs , 1 , child -> n_vars , forward , jacobian_init , eval_jacobian ,
156+ is_affine , free_type_data );
170157
171- node -> wsum_hess_init = wsum_hess_init ;
172- node -> eval_wsum_hess = eval_wsum_hess ;
173158 node -> left = child ;
174159 expr_retain (child );
175160
161+ node -> wsum_hess_init = wsum_hess_init ;
162+ node -> eval_wsum_hess = eval_wsum_hess ;
163+
176164 /* copy indices */
177- idx -> indices = (int * )malloc (n_selected * sizeof (int ));
178- memcpy (idx -> indices , indices , n_selected * sizeof (int ));
179- idx -> n_selected = n_selected ;
165+ idx -> indices = (int * ) malloc (n_idxs * sizeof (int ));
166+ memcpy (idx -> indices , indices , n_idxs * sizeof (int ));
167+ idx -> n_idxs = n_idxs ;
180168
181169 /* detect duplicates for Hessian optimization */
182- idx -> has_duplicates = check_for_duplicates (indices , n_selected , child -> size );
183-
184- /* parent_w allocated lazily in wsum_hess_init */
185- idx -> parent_w = NULL ;
170+ idx -> has_duplicates = check_for_duplicates (indices , n_idxs , child -> size );
186171
187172 return node ;
188173}
0 commit comments