Skip to content

Commit 7bc6b76

Browse files
Transurgeonclaude
andcommitted
Optimize Oracles class: remove redundant forward calls and simplify value extraction
- Remove redundant objective_forward/constraint_forward calls from gradient(), jacobian(), and hessian() since NLP solvers guarantee call ordering - Replace matrix densification + zip iteration with direct COO data return for jacobian and hessian value extraction Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e8f2f91 commit 7bc6b76

File tree

1 file changed

+32
-23
lines changed

1 file changed

+32
-23
lines changed

cvxpy/reductions/solvers/nlp_solvers/nlp_solver.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)