Skip to content

Commit da4ca72

Browse files
committed
Merge remote-tracking branch 'upstream/master'
# Conflicts: # README.md # cvxpy/atoms/__init__.py # cvxpy/problems/problem.py # cvxpy/reductions/solvers/defines.py # cvxpy/reductions/solvers/solving_chain.py # cvxpy/settings.py
2 parents 4641971 + 7b4067f commit da4ca72

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+4005
-1640
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ jobs:
137137
sed -i.bak -e 's/name = "cvxpy"/name = "cvxpy-base"/g' pyproject.toml
138138
sed -i.bak '/clarabel >= /d' pyproject.toml
139139
sed -i.bak '/osqp >= /d' pyproject.toml
140-
sed -i.bak '/ecos >= /d' pyproject.toml
141140
sed -i.bak '/scs >= /d' pyproject.toml
141+
sed -i.bak '/highspy >= /d' pyproject.toml
142142
sed -i.bak -e 's/name="cvxpy",/name="cvxpy-base",/g' setup.py
143143
rm -rf pyproject.toml.bak setup.py.bak
144144

.github/workflows/test_optional_solvers.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ jobs:
3737
version: 'lts'
3838
- name: Install COSMO
3939
run: julia -e 'using Pkg; Pkg.add("COSMO")'
40+
- name: Install SuiteSparse (macOS)
41+
if: runner.os == 'macOS'
42+
run: brew install suite-sparse
4043
- name: Install cvxpy dependencies and all optional solvers
4144
run: uv sync --all-extras --dev
4245
- name: Print installed solvers

cvxpy/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
NonNeg as NonNeg,
2727
Zero as Zero,
2828
PowCone3D as PowCone3D,
29+
PowCone3DApprox as PowCone3DApprox,
2930
PowConeND as PowConeND,
3031
ExpCone as ExpCone,
3132
OpRelEntrConeQuad as OpRelEntrConeQuad,
@@ -68,6 +69,7 @@
6869
CBC as CBC,
6970
CLARABEL as CLARABEL,
7071
CUCLARABEL as CUCLARABEL,
72+
PDCS as PDCS,
7173
COPT as COPT,
7274
COSMO as COSMO,
7375
CPLEX as CPLEX,

cvxpy/atoms/__init__.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
limitations under the License.
1515
"""
1616

17-
from cvxpy.atoms.affine.binary_operators import (matmul, multiply,
17+
from cvxpy.atoms.affine.add_expr import AddExpression
18+
from cvxpy.atoms.affine.binary_operators import (matmul, MulExpression, multiply,
1819
vdot, scalar_product, outer,)
1920
from cvxpy.atoms.affine.bmat import bmat
2021
from cvxpy.atoms.affine.broadcast_to import broadcast_to
@@ -36,7 +37,7 @@
3637
from cvxpy.atoms.affine.squeeze import squeeze
3738
from cvxpy.atoms.affine.concatenate import concatenate
3839
from cvxpy.atoms.affine.stack import stack
39-
from cvxpy.atoms.affine.sum import sum
40+
from cvxpy.atoms.affine.sum import Sum, sum
4041
from cvxpy.atoms.affine.trace import trace, Trace
4142
from cvxpy.atoms.affine.transpose import (transpose, permute_dims, swapaxes, moveaxis)
4243
from cvxpy.atoms.affine.upper_tri import upper_tri, vec_to_upper_tri
@@ -167,3 +168,30 @@
167168
maximum,
168169
minimum,
169170
]
171+
172+
# DGP atoms whose Dgp2Dcp canonicalization produces ExpCone-requiring DCP atoms.
173+
GP_EXP_ATOMS = [
174+
AddExpression,
175+
exp,
176+
eye_minus_inv,
177+
log,
178+
MulExpression,
179+
norm1,
180+
one_minus_pos,
181+
Pnorm,
182+
PnormApprox,
183+
QuadForm,
184+
Sum,
185+
Trace,
186+
xexp,
187+
]
188+
189+
# DGP atoms whose Dgp2Dcp canonicalization produces NonNeg constraints.
190+
GP_NONPOS_ATOMS = [
191+
norm_inf,
192+
max,
193+
min,
194+
maximum,
195+
minimum,
196+
pf_eigenvalue,
197+
]

cvxpy/constraints/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
RelEntrConeQuad,)
2121
from cvxpy.constraints.finite_set import FiniteSet
2222
from cvxpy.constraints.nonpos import Inequality, NonNeg, NonPos
23-
from cvxpy.constraints.power import PowCone3D, PowConeND
23+
from cvxpy.constraints.power import PowCone3D, PowCone3DApprox, PowConeND
2424
from cvxpy.constraints.psd import PSD
2525
from cvxpy.constraints.second_order import SOC
2626
from cvxpy.constraints.zero import Equality, Zero

cvxpy/constraints/power.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@ def _dual_cone(self, *args):
157157
args[2], self.alpha)
158158

159159

160+
class PowCone3DApprox(PowCone3D):
161+
"""PowCone3D with SOC-based rational approximation.
162+
163+
Identical semantics to PowCone3D, but the solving chain will
164+
convert this constraint to second-order cone (SOC) constraints
165+
via rational approximation of the exponent, following the same
166+
pattern as PowerApprox / PnormApprox for atoms.
167+
"""
168+
pass
169+
170+
160171
class PowConeND(Cone):
161172
"""
162173
Represents a collection of N-dimensional power cone constraints
@@ -237,7 +248,12 @@ def shape(self) -> Tuple[int, int]:
237248
# This constitutes the shape of the hypograph variable z
238249
# appended to W in the standard conic form.
239250
# TODO: support arbitrary z.dim
240-
m, n = self.W.shape if self.axis == 0 else (self.W.shape[1], self.W.shape[0])
251+
if self.W.ndim == 1:
252+
m, n = self.W.shape[0], 1
253+
elif self.axis == 0:
254+
m, n = self.W.shape
255+
else:
256+
m, n = self.W.shape[1], self.W.shape[0]
241257
s = (m + 1, n)
242258
return s
243259

@@ -285,13 +301,14 @@ def is_dqcp(self) -> bool:
285301
return self.is_dcp()
286302

287303
def save_dual_value(self, value) -> None:
288-
dW = value[:, :-1]
289-
dz = value[:, -1]
290-
if self.axis == 0:
304+
# Value has shape (n+1, k) from ConeMatrixStuffing (constraint.shape).
305+
# First n rows are W duals, last row is z duals.
306+
dW = value[:-1, :] # Shape (n, k)
307+
dz = value[-1, :] # Shape (k,)
308+
if self.axis == 1:
291309
dW = dW.T
292-
dz = dz.T
293-
if dW.shape[1] == 1:
294-
#NOTE: Targetting problems where duals have the shape
310+
if dW.shape[-1] == 1:
311+
# NOTE: Targetting problems where duals have the shape
295312
# (n, 1) --- dropping the extra dimension is crucial for
296313
# the `_dual_cone` and `dual_residual` methods to work properly
297314
dW = np.squeeze(dW)

cvxpy/expressions/expression.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from typing import List, Literal, Optional, Self, Tuple
2121

2222
import numpy as np
23+
import scipy.sparse as sp
2324

2425
import cvxpy as cp
2526
import cvxpy.settings as s
@@ -542,13 +543,14 @@ def is_nonpos(self) -> bool:
542543
raise NotImplementedError()
543544

544545
@abc.abstractmethod
545-
def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
546+
def get_bounds(self) -> tuple[np.ndarray | sp.sparray, np.ndarray | sp.sparray]:
546547
"""Returns bounds (lower, upper) of the expression.
547548
548549
Returns
549550
-------
550-
tuple of np.ndarray
551+
tuple of (np.ndarray | sp.sparray)
551552
(lower_bound, upper_bound) arrays with shape matching self.shape.
553+
For sparse variables with sparse bounds, may return sparse arrays.
552554
"""
553555
raise NotImplementedError()
554556

0 commit comments

Comments
 (0)