Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cvxpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
USER_LIMIT as USER_LIMIT,
XPRESS as XPRESS,
HIGHS as HIGHS,
IPOPT as IPOPT,
get_num_threads as get_num_threads,
set_num_threads as set_num_threads,
)
15 changes: 10 additions & 5 deletions cvxpy/problems/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,8 @@ def get_problem_data(
ignore_dpp: bool = False,
verbose: bool = False,
canon_backend: str | None = None,
solver_opts: Optional[dict] = None
solver_opts: Optional[dict] = None,
nlp: bool = False
):
"""Returns the problem data used in the call to the solver.

Expand Down Expand Up @@ -757,7 +758,8 @@ def get_problem_data(
enforce_dpp=enforce_dpp,
ignore_dpp=ignore_dpp,
canon_backend=canon_backend,
solver_opts=solver_opts)
solver_opts=solver_opts,
nlp=nlp)
self._cache.key = key
self._cache.solving_chain = solving_chain
self._solver_cache = {}
Expand Down Expand Up @@ -969,7 +971,8 @@ def _construct_chain(
enforce_dpp: bool = False,
ignore_dpp: bool = False,
canon_backend: str | None = None,
solver_opts: Optional[dict] = None
solver_opts: Optional[dict] = None,
nlp: bool = False
) -> SolvingChain:
"""
Construct the chains required to reformulate and solve the problem.
Expand Down Expand Up @@ -1011,7 +1014,8 @@ def _construct_chain(
ignore_dpp=ignore_dpp,
canon_backend=canon_backend,
solver_opts=solver_opts,
specified_solver=solver)
specified_solver=solver,
nlp=nlp)

@staticmethod
def _sort_candidate_solvers(solvers) -> None:
Expand Down Expand Up @@ -1052,6 +1056,7 @@ def _solve(self,
enforce_dpp: bool = False,
ignore_dpp: bool = False,
canon_backend: str | None = None,
nlp: bool = False,
**kwargs):
"""Solves a DCP compliant optimization problem.

Expand Down Expand Up @@ -1189,7 +1194,7 @@ def _solve(self,
return self.value

data, solving_chain, inverse_data = self.get_problem_data(
solver, gp, enforce_dpp, ignore_dpp, verbose, canon_backend, kwargs
solver, gp, enforce_dpp, ignore_dpp, verbose, canon_backend, kwargs, nlp
)

if verbose:
Expand Down
37 changes: 36 additions & 1 deletion cvxpy/reductions/expr2smooth/expr2smooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@

from typing import Tuple

import cvxpy as cp
import numpy as np

import cvxpy.settings as s
from cvxpy import problems
from cvxpy.expressions.expression import Expression
from cvxpy.problems.objective import Minimize
from cvxpy.reductions.canonicalization import Canonicalization
from cvxpy.reductions.expr2smooth.canonicalizers import CANON_METHODS as smooth_canon_methods
from cvxpy.reductions.inverse_data import InverseData
from cvxpy.reductions.solution import Solution


class Expr2smooth(Canonicalization):
Expand All @@ -39,10 +43,39 @@ def accepts(self, problem):
"""A problem is always accepted"""
return True

def invert(self, solution, inverse_data):
"""Retrieves a solution to the original problem"""
var_map = inverse_data.var_offsets
# Flip sign of opt val if maximize.
opt_val = solution.opt_val
if solution.status not in s.ERROR and not inverse_data.minimize:
opt_val = -solution.opt_val

primal_vars, dual_vars = {}, {}
if solution.status not in s.SOLUTION_PRESENT:
return Solution(solution.status, opt_val, primal_vars, dual_vars,
solution.attr)

# Split vectorized variable into components.
x_opt = list(solution.primal_vars.values())[0]
for var_id, offset in var_map.items():
shape = inverse_data.var_shapes[var_id]
size = np.prod(shape, dtype=int)
primal_vars[var_id] = np.reshape(x_opt[offset:offset+size], shape,
order='F')

solution = super(Expr2smooth, self).invert(solution, inverse_data)

return Solution(solution.status, opt_val, primal_vars, dual_vars,
solution.attr)


def apply(self, problem):
"""Converts an expr to a smooth program"""
inverse_data = InverseData(problem)

inverse_data.minimize = type(problem.objective) == Minimize

# smoothen objective function
canon_objective, canon_constraints = self.canonicalize_tree(
problem.objective, True)
Expand Down Expand Up @@ -109,6 +142,7 @@ def canonicalize_expr(self, expr, args, affine_above: bool) -> Tuple[Expression,

return expr.copy(args), []

"""
def example_max():
# Define variables
x = cp.Variable(1)
Expand Down Expand Up @@ -157,3 +191,4 @@ def example_pnorm_odd():
prob = example_sqrt()
new_problem, inverse = Expr2smooth(prob).apply(prob)
print(new_problem)
"""
97 changes: 97 additions & 0 deletions cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Copyright 2013 Steven Diamond

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import annotations

import numpy as np

import cvxpy.settings as s
from cvxpy.constraints import (
PSD,

Check failure on line 22 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:22:5: F401 `cvxpy.constraints.PSD` imported but unused
SOC,

Check failure on line 23 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:23:5: F401 `cvxpy.constraints.SOC` imported but unused
Equality,

Check failure on line 24 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:24:5: F401 `cvxpy.constraints.Equality` imported but unused
ExpCone,

Check failure on line 25 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:25:5: F401 `cvxpy.constraints.ExpCone` imported but unused
Inequality,

Check failure on line 26 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:26:5: F401 `cvxpy.constraints.Inequality` imported but unused
NonNeg,

Check failure on line 27 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:27:5: F401 `cvxpy.constraints.NonNeg` imported but unused
NonPos,

Check failure on line 28 in cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py

View workflow job for this annotation

GitHub Actions / actions-linting / linters

Ruff (F401)

cvxpy/reductions/expr2smooth/nlp_matrix_stuffing.py:28:5: F401 `cvxpy.constraints.NonPos` imported but unused
PowCone3D,
Zero,
)
from cvxpy.expressions.variable import Variable
from cvxpy.problems.objective import Minimize
from cvxpy.reductions import InverseData, Solution, cvx_attr2constr
from cvxpy.reductions.matrix_stuffing import (
MatrixStuffing,
extract_lower_bounds,
extract_mip_idx,
extract_upper_bounds,
)
from cvxpy.reductions.solvers.solving_chain_utils import get_canon_backend
from cvxpy.reductions.utilities import (
are_args_affine,
group_constraints,
lower_equality,
lower_ineq_to_nonneg,
nonpos2nonneg,
)
from cvxpy.utilities.coeff_extractor import CoeffExtractor


class NLPMatrixStuffing(MatrixStuffing):
"""Construct matrices for linear cone problems.

Linear cone problems are assumed to have a linear objective and cone
constraints which may have zero or more arguments, all of which must be
affine.
"""
CONSTRAINTS = 'ordered_constraints'

def __init__(self, canon_backend: str | None = None):
self.canon_backend = canon_backend

def accepts(self, problem):
valid_obj_curv = problem.objective.expr.is_affine()
return (type(problem.objective) == Minimize
and valid_obj_curv
and not cvx_attr2constr.convex_attributes(problem.variables())
and are_args_affine(problem.constraints)
and problem.is_dpp())

def apply(self, problem):
pass

def invert(self, solution, inverse_data):
"""Retrieves a solution to the original problem"""
var_map = inverse_data.var_offsets
# Flip sign of opt val if maximize.
opt_val = solution.opt_val
if solution.status not in s.ERROR and not inverse_data.minimize:
opt_val = -solution.opt_val

primal_vars, dual_vars = {}, {}
if solution.status not in s.SOLUTION_PRESENT:
return Solution(solution.status, opt_val, primal_vars, dual_vars,
solution.attr)

# Split vectorized variable into components.
x_opt = list(solution.primal_vars.values())[0]
for var_id, offset in var_map.items():
shape = inverse_data.var_shapes[var_id]
size = np.prod(shape, dtype=int)
primal_vars[var_id] = np.reshape(x_opt[offset:offset+size], shape,
order='F')

return Solution(solution.status, opt_val, primal_vars, dual_vars,
solution.attr)
1 change: 1 addition & 0 deletions cvxpy/reductions/matrix_stuffing.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def apply(self, problem) -> None:
InverseData
Data for solution retrieval
"""

def invert(self, solution, inverse_data):
raise NotImplementedError()

Expand Down
8 changes: 8 additions & 0 deletions cvxpy/reductions/solvers/defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from cvxpy.reductions.solvers.conic_solvers.scs_conif import SCS as SCS_con
from cvxpy.reductions.solvers.conic_solvers.sdpa_conif import SDPA as SDPA_con
from cvxpy.reductions.solvers.conic_solvers.xpress_conif import XPRESS as XPRESS_con
from cvxpy.reductions.solvers.nlp_solvers.ipopt_nlpif import IPOPT as IPOPT_nlp
from cvxpy.reductions.solvers.qp_solvers.copt_qpif import COPT as COPT_qp
from cvxpy.reductions.solvers.qp_solvers.cplex_qpif import CPLEX as CPLEX_qp

Expand Down Expand Up @@ -74,9 +75,11 @@
HIGHS_qp(),
MPAX_qp(),
]
solver_nlp_intf = [IPOPT_nlp()]

SOLVER_MAP_CONIC = {solver.name(): solver for solver in solver_conic_intf}
SOLVER_MAP_QP = {solver.name(): solver for solver in solver_qp_intf}
SOLVER_MAP_NLP = {solver.name(): solver for solver in solver_nlp_intf}

# CONIC_SOLVERS and QP_SOLVERS are sorted in order of decreasing solver
# preference. QP_SOLVERS are those for which we have written interfaces
Expand All @@ -96,6 +99,7 @@
s.PROXQP,
s.DAQP,
s.MPAX]
NLP_SOLVERS = [s.IPOPT]
DISREGARD_CLARABEL_SDP_SUPPORT_FOR_DEFAULT_RESOLUTION = True
MI_SOLVERS = [s.GLPK_MI, s.MOSEK, s.GUROBI, s.CPLEX,
s.XPRESS, s.CBC, s.SCIP, s.HIGHS, s.COPT, s.ECOS_BB]
Expand All @@ -119,6 +123,10 @@ def installed_solvers():
for name, solver in SOLVER_MAP_QP.items():
if solver.is_installed():
installed.append(name)
# Check NLP solvers
for name, solver in SOLVER_MAP_NLP.items():
if solver.is_installed():
installed.append(name)

# Remove duplicate names (for solvers that handle both conic and QP)
return np.unique(installed).tolist()
Expand Down
Loading
Loading