Skip to content

Commit dcf1de0

Browse files
Transurgeonclaude
andcommitted
Move parameter update into solve_via_data, use _solver_cache['NLP']
Move update_params call into ipopt solve_via_data (where oracles are reused). Remove _get_solver_cache helper and _nlp_cache dict. Store solver_cache in the existing problem._solver_cache['NLP'] — zero changes to problem.py. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b2d88c1 commit dcf1de0

3 files changed

Lines changed: 14 additions & 35 deletions

File tree

cvxpy/problems/problem.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ def __init__(
176176
self._status: Optional[str] = None
177177
self._solution = None
178178
self._cache = Cache()
179-
self._nlp_cache = None
180179
self._solver_cache = {}
181180
# Information about the shape of the problem and its constituent parts
182181
self._size_metrics: Optional["SizeMetrics"] = None

cvxpy/reductions/solvers/nlp_solvers/ipopt_nlpif.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ def solve_via_data(self, data, warm_start: bool, verbose: bool, solver_opts, sol
155155
oracles = Oracles(bounds.new_problem, verbose=verbose, use_hessian=use_hessian)
156156
elif 'oracles' in solver_cache:
157157
oracles = solver_cache['oracles']
158+
# Update parameter values in the cached C DAG if needed
159+
params = list(bounds.new_problem.parameters())
160+
if params:
161+
from cvxpy.reductions.solvers.nlp_solvers.diff_engine.converters import (
162+
build_theta,
163+
)
164+
oracles.update_params(build_theta(params))
158165
else:
159166
oracles = Oracles(bounds.new_problem, verbose=verbose, use_hessian=use_hessian)
160167
solver_cache['oracles'] = oracles

cvxpy/reductions/solvers/nlp_solving_chain.py

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from cvxpy.reductions.dnlp2smooth.dnlp2smooth import Dnlp2Smooth
2222
from cvxpy.reductions.flip_objective import FlipObjective
2323
from cvxpy.reductions.solvers.defines import INSTALLED_SOLVERS, NLP_SOLVER_VARIANTS, SOLVER_MAP_NLP
24-
from cvxpy.reductions.solvers.nlp_solvers.diff_engine.converters import build_theta
2524
from cvxpy.reductions.solvers.solving_chain import SolvingChain
2625

2726

@@ -154,28 +153,6 @@ def _set_random_nlp_initial_point(problem, run, user_initials):
154153
var.save_value(initial_val)
155154

156155

157-
def _get_nlp_solver_cache(problem, solver, canon_problem):
158-
"""Return a solver_cache dict for Oracles reuse, or None.
159-
160-
When the problem has parameters and a cached solver_cache exists from a
161-
previous solve, update the C DAG parameter values and return it.
162-
Otherwise return an empty dict (Oracles will be created by solve_via_data)
163-
or None (no parameters).
164-
"""
165-
if not problem.parameters():
166-
return None
167-
168-
nlp_cache = getattr(problem, '_nlp_cache', None)
169-
if nlp_cache is not None and nlp_cache.get('solver') == solver:
170-
solver_cache = nlp_cache['solver_cache']
171-
canon_cvxpy = canon_problem["_bounds"].new_problem
172-
theta = build_theta(list(canon_cvxpy.parameters()))
173-
solver_cache['oracles'].update_params(theta)
174-
return solver_cache
175-
176-
return {}
177-
178-
179156
def solve_nlp(problem, solver, warm_start, verbose, **kwargs):
180157
"""Solve an NLP problem using the DNLP reduction chain.
181158
@@ -199,20 +176,19 @@ def solve_nlp(problem, solver, warm_start, verbose, **kwargs):
199176
"""
200177
nlp_chain, kwargs = _build_nlp_chain(problem, solver, kwargs)
201178

179+
# Reuse cached Oracles across solve() calls when problem has parameters.
180+
# Parameter updates happen inside solve_via_data when reusing.
181+
solver_cache = problem._solver_cache.get('NLP')
182+
if solver_cache is None and problem.parameters():
183+
solver_cache = {}
184+
problem._solver_cache['NLP'] = solver_cache
185+
202186
if "best_of" not in kwargs:
203187
_set_nlp_initial_point(problem)
204188
canon_problem, inverse_data = nlp_chain.apply(problem=problem)
205-
206-
solver_cache = _get_nlp_solver_cache(problem, solver, canon_problem)
207-
208189
solution = nlp_chain.solver.solve_via_data(
209190
canon_problem, warm_start, verbose, solver_opts=kwargs,
210191
solver_cache=solver_cache)
211-
212-
if solver_cache is not None:
213-
problem._nlp_cache = {
214-
'solver': solver, 'solver_cache': solver_cache}
215-
216192
problem.unpack_results(solution, nlp_chain, inverse_data)
217193
return problem.value
218194

@@ -225,9 +201,6 @@ def solve_nlp(problem, solver, warm_start, verbose, **kwargs):
225201
all_objs = np.zeros(shape=(best_of,))
226202
user_initials = {}
227203

228-
# inside solve_via_data we cache the construction of oracles
229-
solver_cache = {}
230-
231204
for run in range(best_of):
232205
_set_random_nlp_initial_point(problem, run, user_initials)
233206
canon_problem, inverse_data = nlp_chain.apply(problem=problem)

0 commit comments

Comments
 (0)