@@ -184,37 +184,43 @@ def __init__(self, problem, initial_point, num_constraints):
184184 self .time_jacobian = 0.0
185185 self .time_jacobian_c = 0.0
186186 self .time_hessian_c = 0.0
187-
187+
188188 def objective (self , x ):
189189 """Returns the scalar value of the objective given x."""
190190 return self .c_problem .objective_forward (x )
191191
192192 def gradient (self , x ):
193- """Returns the gradient of the objective with respect to x."""
194- self .c_problem .objective_forward (x )
193+ """Returns the gradient of the objective with respect to x.
194+
195+ Note: objective() is always called before gradient() by NLP solvers,
196+ so objective_forward has already been called with x.
197+ """
195198 return self .c_problem .gradient ()
196199
197200 def constraints (self , x ):
198201 """Returns the constraint values."""
199202 return self .c_problem .constraint_forward (x )
200203
201204 def jacobian (self , x ):
202- """Returns the Jacobian values in COO format at the sparsity structure."""
203- self .c_problem .constraint_forward (x )
205+ """Returns the Jacobian values in COO format at the sparsity structure.
204206
207+ Note: constraints() is always called before jacobian() by NLP solvers,
208+ so constraint_forward has already been called with x.
209+ """
205210 start = time ()
206211 jac_csr = self .c_problem .jacobian ()
207212 self .time_jacobian_c += time () - start
208213 jac_coo = jac_csr .tocoo ()
209214
210215 if self ._jac_structure is None :
211- # First call - return values directly
212- return jac_coo .data
216+ # First call - cache structure and return values directly
217+ self ._jac_structure = (
218+ jac_coo .row .astype (np .int32 ),
219+ jac_coo .col .astype (np .int32 )
220+ )
213221
214- # Extract values at the known sparsity pattern
215- rows_struct , cols_struct = self ._jac_structure
216- jac_dense = jac_csr .toarray ()
217- return np .array ([jac_dense [r , c ] for r , c in zip (rows_struct , cols_struct )])
222+ # Sparsity pattern is fixed, COO data ordering is consistent
223+ return jac_coo .data .copy ()
218224
219225 def jacobianstructure (self ):
220226 """Returns the sparsity structure of the Jacobian."""
@@ -233,24 +239,27 @@ def jacobianstructure(self):
233239 return self ._jac_structure
234240
235241 def hessian (self , x , duals , obj_factor ):
236- """Returns the lower triangular Hessian values in COO format."""
237- self .c_problem .objective_forward (x )
238- if self .num_constraints > 0 :
239- self .c_problem .constraint_forward (x )
242+ """Returns the lower triangular Hessian values in COO format.
243+
244+ Note: objective() and constraints() are always called before hessian()
245+ by NLP solvers, so forward passes have already been done.
246+ """
240247 start = time ()
241248 hess_csr = self .c_problem .hessian (obj_factor , duals )
242249 self .time_hessian_c += time () - start
243250 hess_coo = hess_csr .tocoo ()
244251
252+ # Extract lower triangular values
253+ mask = hess_coo .row >= hess_coo .col
254+
245255 if self ._hess_structure is None :
246- # First call - extract lower triangular and return
247- mask = hess_coo .row >= hess_coo .col
248- return hess_coo .data [mask ]
249-
250- # Extract values at the known sparsity pattern
251- rows_struct , cols_struct = self ._hess_structure
252- hess_dense = hess_csr .toarray ()
253- return np .array ([hess_dense [r , c ] for r , c in zip (rows_struct , cols_struct )])
256+ # First call - cache structure
257+ self ._hess_structure = (
258+ hess_coo .row [mask ].astype (np .int32 ),
259+ hess_coo .col [mask ].astype (np .int32 )
260+ )
261+
262+ return hess_coo .data [mask ].copy ()
254263
255264 def hessianstructure (self ):
256265 """Returns the sparsity structure of the lower triangular Hessian."""
0 commit comments