@@ -97,3 +97,98 @@ int check_jacobian(expr *node, const double *u, double h)
9797 free (J_num );
9898 return result ;
9999}
100+
101+ /* Compute g = J^T w where J is CSR (m x n) and w has m entries.
102+ * Result written into g (size n), which must be zero-initialized. */
103+ static void csr_transpose_mult_vec (const CSR_Matrix * J , const double * w , double * g )
104+ {
105+ for (int row = 0 ; row < J -> m ; row ++ )
106+ {
107+ for (int idx = J -> p [row ]; idx < J -> p [row + 1 ]; idx ++ )
108+ {
109+ g [J -> i [idx ]] += J -> x [idx ] * w [row ];
110+ }
111+ }
112+ }
113+
114+ double * numerical_wsum_hess (expr * node , const double * u , const double * w , double h )
115+ {
116+ int n = node -> n_vars ;
117+ double inv_2h = 1.0 / (2.0 * h );
118+
119+ /* Initialize jacobian sparsity once, then forward */
120+ node -> jacobian_init (node );
121+ node -> forward (node , u );
122+
123+ double * H = calloc ((size_t ) n * n , sizeof (double ));
124+ double * u_work = malloc (n * sizeof (double ));
125+ double * g_plus = malloc (n * sizeof (double ));
126+ double * g_minus = malloc (n * sizeof (double ));
127+
128+ memcpy (u_work , u , n * sizeof (double ));
129+
130+ for (int j = 0 ; j < n ; j ++ )
131+ {
132+ /* g(u + h*e_j) */
133+ u_work [j ] = u [j ] + h ;
134+ node -> forward (node , u_work );
135+ node -> eval_jacobian (node );
136+ memset (g_plus , 0 , n * sizeof (double ));
137+ csr_transpose_mult_vec (node -> jacobian , w , g_plus );
138+
139+ /* g(u - h*e_j) */
140+ u_work [j ] = u [j ] - h ;
141+ node -> forward (node , u_work );
142+ node -> eval_jacobian (node );
143+ memset (g_minus , 0 , n * sizeof (double ));
144+ csr_transpose_mult_vec (node -> jacobian , w , g_minus );
145+
146+ u_work [j ] = u [j ];
147+
148+ for (int i = 0 ; i < n ; i ++ )
149+ {
150+ H [i * n + j ] = (g_plus [i ] - g_minus [i ]) * inv_2h ;
151+ }
152+ }
153+
154+ free (g_minus );
155+ free (g_plus );
156+ free (u_work );
157+ return H ;
158+ }
159+
160+ int check_wsum_hess (expr * node , const double * u , const double * w , double h )
161+ {
162+ int n = node -> n_vars ;
163+
164+ /* Compute numerical first (does its own jacobian_init) */
165+ double * H_num = numerical_wsum_hess (node , u , w , h );
166+
167+ /* Now compute analytical (reuses jacobian from numerical) */
168+ node -> wsum_hess_init (node );
169+ node -> forward (node , u );
170+ node -> eval_jacobian (node );
171+ node -> eval_wsum_hess (node , w );
172+
173+ double * H_ana = calloc ((size_t ) n * n , sizeof (double ));
174+ csr_to_dense (node -> wsum_hess , H_ana );
175+
176+ int result = 1 ;
177+ for (int i = 0 ; i < n * n ; i ++ )
178+ {
179+ if (!is_close (H_ana [i ], H_num [i ]))
180+ {
181+ int row = i / n ;
182+ int col = i % n ;
183+ printf (" check_wsum_hess FAILED at (%d, %d):"
184+ " analytical=%e, numerical=%e\n" ,
185+ row , col , H_ana [i ], H_num [i ]);
186+ result = 0 ;
187+ break ;
188+ }
189+ }
190+
191+ free (H_ana );
192+ free (H_num );
193+ return result ;
194+ }
0 commit comments