Skip to content

Commit 5f6ed34

Browse files
Transurgeonclaude
andcommitted
Pre-PR cleanup for DNLP merge into CVXPY master
- Remove `submodules: recursive` from CI workflows (build, test_nlp_solvers, test_backends) - Revert `_grad` in affine_atom.py to upstream version - Remove NaN-allowance block in leaf.py added for NLP structural jacobian - Update is_dnlp() docstrings to use linearizable terminology - Add normalize_shape() helper in converters.py, replacing 7 inline occurrences - Remove unnecessary comment in nlp_solver.py - Export UNO in cvxpy/__init__.py - Revert README.md to upstream CVXPY version Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 99bdad5 commit 5f6ed34

10 files changed

Lines changed: 197 additions & 131 deletions

File tree

.github/workflows/build.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ jobs:
5454

5555
steps:
5656
- uses: actions/checkout@v5
57-
with:
58-
submodules: recursive
5957
- name: Set Additional Envs
6058
run: |
6159
echo "PYTHON_SUBVERSION=$(echo $PYTHON_VERSION | cut -c 3-)" >> $GITHUB_ENV
@@ -116,8 +114,6 @@ jobs:
116114

117115
steps:
118116
- uses: actions/checkout@v5
119-
with:
120-
submodules: recursive
121117
- uses: actions/setup-python@v6
122118
with:
123119
python-version: ${{ matrix.python-version }}

.github/workflows/test_backends.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ jobs:
1616
with:
1717
python-version: "3.12"
1818
- uses: actions/checkout@v5
19-
with:
20-
submodules: recursive
2119
- name: Install cvxpy dependencies
2220
run: |
2321
pip install -e .

.github/workflows/test_nlp_solvers.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ jobs:
1616
os: [ubuntu-latest, macos-latest]
1717
steps:
1818
- uses: actions/checkout@v5
19-
with:
20-
submodules: recursive
2119
- uses: astral-sh/setup-uv@v7
2220
with:
2321
python-version: "3.12"

README.md

Lines changed: 143 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,156 @@
1-
# DNLP — Disciplined Nonlinear Programming
2-
The DNLP package is an extension of [CVXPY](https://www.cvxpy.org/) to general nonlinear programming (NLP).
3-
DNLP allows smooth functions to be freely mixed with nonsmooth convex and concave functions,
4-
with some rules governing how the nonsmooth functions can be used. For details, see our paper [Disciplined Nonlinear Programming](https://web.stanford.edu/~boyd/papers/dnlp.html).
1+
CVXPY
2+
=====================
3+
[![Build Status](https://github.com/cvxpy/cvxpy/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/cvxpy/cvxpy/actions/workflows/build.yml)
4+
![PyPI - downloads](https://img.shields.io/pypi/dm/cvxpy.svg?label=Pypi%20downloads)
5+
![Conda - downloads](https://img.shields.io/conda/dn/conda-forge/cvxpy.svg?label=Conda%20downloads)
6+
[![Discord](https://img.shields.io/badge/Chat-Discord-Blue?color=5865f2)](https://discord.gg/4urRQeGBCr)
7+
[![Benchmarks](http://img.shields.io/badge/benchmarked%20by-asv-blue.svg?style=flat)](https://cvxpy.github.io/benchmarks/)
8+
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/cvxpy/cvxpy/badge)](https://api.securityscorecards.dev/projects/github.com/cvxpy/cvxpy)
59

6-
---
7-
## Installation
8-
The installation consists of two steps.
10+
**The CVXPY documentation is at [cvxpy.org](https://www.cvxpy.org/).**
911

10-
#### Step 1: Install IPOPT
11-
DNLP requires an NLP solver. The recommended solver is [Ipopt](https://coin-or.github.io/Ipopt/). First install the IPOPT system library, then install the Python interface [cyipopt](https://github.com/mechmotum/cyipopt):
12-
```bash
13-
# Ubuntu/Debian
14-
sudo apt-get install coinor-libipopt-dev
12+
*We are building a CVXPY community on [Discord](https://discord.gg/4urRQeGBCr). Join the conversation! For issues and long-form discussions, use [Github Issues](https://github.com/cvxpy/cvxpy/issues) and [Github Discussions](https://github.com/cvxpy/cvxpy/discussions).*
1513

16-
# macOS
17-
brew install ipopt
18-
```
19-
Then install the Python interface:
20-
```bash
21-
pip install cyipopt
22-
```
14+
**Contents**
15+
- [Installation](#installation)
16+
- [Getting started](#getting-started)
17+
- [Issues](#issues)
18+
- [Community](#community)
19+
- [Contributing](#contributing)
20+
- [Team](#team)
21+
- [Citing](#citing)
2322

24-
#### Step 2: Install DNLP
25-
DNLP is installed by cloning this repository and installing it locally:
26-
```bash
27-
git clone https://github.com/cvxgrp/DNLP.git
28-
cd DNLP
29-
pip install .
30-
```
3123

32-
---
33-
## Example
34-
Below we give a toy example where we maximize a convex quadratic function subject to a nonlinear equality constraint. Many more examples, including the ones in the paper, can be found at [DNLP-examples](https://github.com/cvxgrp/dnlp-examples).
35-
```python
36-
import cvxpy as cp
37-
import numpy as np
24+
CVXPY is a Python-embedded modeling language for convex optimization problems. It allows you to express your problem in a natural way that follows the math, rather than in the restrictive standard form required by solvers.
25+
26+
For example, the following code solves a least-squares problem where the variable is constrained by lower and upper bounds:
27+
28+
```python3
3829
import cvxpy as cp
30+
import numpy
3931

40-
# problem data
41-
np.random.seed(0)
42-
n = 3
43-
A = np.random.randn(n, n)
44-
A = A.T @ A
32+
# Problem data.
33+
m = 30
34+
n = 20
35+
numpy.random.seed(1)
36+
A = numpy.random.randn(m, n)
37+
b = numpy.random.randn(m)
4538

46-
# formulate optimization problem
39+
# Construct the problem.
4740
x = cp.Variable(n)
48-
obj = cp.Maximize(cp.quad_form(x, A))
49-
constraints = [cp.sum_squares(x) == 1]
50-
51-
# initialize and solve
52-
x.value = np.ones(n)
53-
prob = cp.Problem(obj, constraints)
54-
prob.solve(nlp=True, verbose=True)
55-
print("Optimal value from DNLP: ", prob.value)
56-
57-
# the optimal value for this toy problem can also be found by computing the maximum eigenvalue of A
58-
eigenvalues = np.linalg.eigvalsh(A)
59-
print("Maximum eigenvalue: " , np.max(eigenvalues))
41+
objective = cp.Minimize(cp.sum_squares(A @ x - b))
42+
constraints = [0 <= x, x <= 1]
43+
prob = cp.Problem(objective, constraints)
44+
45+
# The optimal objective is returned by prob.solve().
46+
result = prob.solve()
47+
# The optimal value for x is stored in x.value.
48+
print(x.value)
49+
# The optimal Lagrange multiplier for a constraint
50+
# is stored in constraint.dual_value.
51+
print(constraints[0].dual_value)
52+
```
53+
54+
With CVXPY, you can model
55+
* convex optimization problems,
56+
* mixed-integer convex optimization problems,
57+
* geometric programs, and
58+
* quasiconvex programs.
59+
60+
CVXPY is not a solver. It relies upon the open source solvers
61+
[Clarabel](https://github.com/oxfordcontrol/Clarabel.rs), [SCS](https://github.com/bodono/scs-python),
62+
[OSQP](https://github.com/oxfordcontrol/osqp) and [HiGHS](https://github.com/ERGO-Code/HiGHS).
63+
Additional solvers are [available](https://www.cvxpy.org/tutorial/solvers/index.html#choosing-a-solver),
64+
but must be installed separately.
65+
66+
CVXPY began as a Stanford University research project. It is now developed by
67+
many people, across many institutions and countries.
68+
69+
70+
## Installation
71+
CVXPY is available on PyPI, and can be installed with
72+
```
73+
pip install cvxpy
74+
```
75+
76+
CVXPY can also be installed with conda, using
77+
```
78+
conda install -c conda-forge cvxpy
6079
```
6180

62-
---
63-
## Supported Solvers
64-
| Solver | License | Installation |
65-
|--------|---------|--------------|
66-
| [IPOPT](https://github.com/coin-or/Ipopt) | EPL-2.0 | Install system IPOPT (see above), then `pip install cyipopt` |
67-
| [Knitro](https://www.artelys.com/solvers/knitro/) | Commercial | `pip install knitro` (requires license) |
81+
CVXPY has the following dependencies:
82+
83+
- Python >= 3.11
84+
- Clarabel >= 0.5.0
85+
- OSQP >= 1.0.0
86+
- SCS >= 3.2.4.post1
87+
- NumPy >= 2.0.0
88+
- SciPy >= 1.13.0
89+
- highspy >= 1.11.0
90+
91+
For detailed instructions, see the [installation
92+
guide](https://www.cvxpy.org/install/index.html).
93+
94+
## Getting started
95+
To get started with CVXPY, check out the following:
96+
* [official CVXPY tutorial](https://www.cvxpy.org/tutorial/index.html)
97+
* [example library](https://www.cvxpy.org/examples/index.html)
98+
* [API reference](https://www.cvxpy.org/api_reference/cvxpy.html)
99+
100+
## Issues
101+
We encourage you to report issues using the [Github tracker](https://github.com/cvxpy/cvxpy/issues). We welcome all kinds of issues, especially those related to correctness, documentation, performance, and feature requests.
102+
103+
For basic usage questions (e.g., "Why isn't my problem DCP?"), please use [StackOverflow](https://stackoverflow.com/questions/tagged/cvxpy) instead.
104+
105+
## Community
106+
The CVXPY community consists of researchers, data scientists, software engineers, and students from all over the world. We welcome you to join us!
107+
108+
* To chat with the CVXPY community in real-time, join us on [Discord](https://discord.gg/4urRQeGBCr).
109+
* To have longer, in-depth discussions with the CVXPY community, use [Github Discussions](https://github.com/cvxpy/cvxpy/discussions).
110+
* To share feature requests and bug reports, use [Github Issues](https://github.com/cvxpy/cvxpy/issues).
111+
112+
Please be respectful in your communications with the CVXPY community, and make sure to abide by our [code of conduct](https://github.com/cvxpy/cvxpy/blob/master/CODE_OF_CONDUCT.md).
113+
114+
## Contributing
115+
We appreciate all contributions. You don't need to be an expert in convex
116+
optimization to help out.
117+
118+
You should first
119+
install [CVXPY from source](https://www.cvxpy.org/install/index.html#install-from-source).
120+
Here are some simple ways to start contributing immediately:
121+
* Read the CVXPY source code and improve the documentation, or address TODOs
122+
* Enhance the [website documentation](https://github.com/cvxpy/cvxpy/tree/master/doc)
123+
* Browse the [issue tracker](https://github.com/cvxpy/cvxpy/issues), and look for issues tagged as "help wanted"
124+
* Polish the [example library](https://github.com/cvxpy/examples)
125+
* Add a [benchmark](https://github.com/cvxpy/benchmarks)
126+
127+
If you'd like to add a new example to our library, or implement a new feature,
128+
please get in touch with us first to make sure that your priorities align with
129+
ours.
130+
131+
Contributions should be submitted as [pull requests](https://github.com/cvxpy/cvxpy/pulls).
132+
A member of the CVXPY development team will review the pull request and guide
133+
you through the contributing process.
134+
135+
Before starting work on your contribution, please read the [contributing guide](https://github.com/cvxpy/cvxpy/blob/master/CONTRIBUTING.md).
136+
137+
## Team
138+
CVXPY is a community project, built from the contributions of many
139+
researchers and engineers.
140+
141+
CVXPY is developed and maintained by [Steven
142+
Diamond](https://stevendiamond.me/), [Akshay
143+
Agrawal](https://akshayagrawal.com), [Riley Murray](https://rileyjmurray.wordpress.com/),
144+
[Philipp Schiele](https://www.philippschiele.com/),
145+
[Bartolomeo Stellato](https://stellato.io/),
146+
and [Parth Nobel](https://ptnobel.github.io), with many others contributing
147+
significantly.
148+
A non-exhaustive list of people who have shaped CVXPY over the
149+
years includes Stephen Boyd, Eric Chu, Robin Verschueren,
150+
Jaehyun Park, Enzo Busseti, AJ Friend, Judson Wilson, Chris Dembia, and
151+
William Zhang.
68152

69-
---
70-
## Differentiation Engine
71-
DNLP uses [SparseDiffPy](https://github.com/SparseDifferentiation/SparseDiffPy) as its differentiation engine. SparseDiffPy is a Python wrapper around the [SparseDiffEngine](https://github.com/SparseDifferentiation/SparseDiffEngine) C library, and is installed automatically as a dependency of DNLP.
153+
For more information about the team and our processes, see our [governance document](https://github.com/cvxpy/org/blob/main/governance.md).
72154

73-
SparseDiffPy builds an expression tree from the CVXPY problem and computes exact sparse gradients, Jacobians, and Hessians required by the NLP solvers.
155+
## Citing
156+
If you use CVXPY for academic work, we encourage you to [cite our papers](https://www.cvxpy.org/resources/citing/index.html). If you use CVXPY in industry, we'd love to hear from you as well, on Discord or over email.

cvxpy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
SOLVER_ERROR as SOLVER_ERROR,
110110
UNBOUNDED as UNBOUNDED,
111111
UNBOUNDED_INACCURATE as UNBOUNDED_INACCURATE,
112+
UNO as UNO,
112113
USER_LIMIT as USER_LIMIT,
113114
XPRESS as XPRESS,
114115
HIGHS as HIGHS,

cvxpy/atoms/affine/affine_atom.py

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -113,54 +113,58 @@ def is_nsd(self) -> bool:
113113
return True
114114

115115
def _grad(self, values) -> List[Any]:
116-
"""Computes the gradient of the affine atom w.r.t. each argument.
116+
"""Gives the (sub/super)gradient of the atom w.r.t. each argument.
117117
118-
For affine atoms, the gradient is constant and independent of argument values.
119-
We compute it by constructing the canonical matrix representation and extracting
120-
the linear coefficients.
118+
Matrix expressions are vectorized, so the gradient is a matrix.
121119
122120
Args:
123-
values: Argument values (unused for affine atoms).
121+
values: A list of numeric values for the arguments.
124122
125123
Returns:
126-
List of gradient matrices, one for each argument.
124+
A list of SciPy CSC sparse matrices or None.
127125
"""
128-
# Create fake variables for each non-constant argument to build the linear system
126+
# TODO should be a simple function in cvxcore for this.
127+
# Make a fake lin op tree for the function.
129128
fake_args = []
130129
var_offsets = {}
131-
var_length = 0
132-
130+
offset = 0
133131
for idx, arg in enumerate(self.args):
134132
if arg.is_constant():
135-
fake_args.append(Constant(arg.value).canonical_form[0])
133+
fake_args += [Constant(arg.value).canonical_form[0]]
136134
else:
137-
fake_args.append(lu.create_var(arg.shape, idx))
138-
var_offsets[idx] = var_length
139-
var_length += arg.size
140-
141-
# Get the canonical matrix representation: f(x) = Ax + b
142-
fake_expr, _ = self.graph_implementation(fake_args, self.shape, self.get_data())
135+
fake_args += [lu.create_var(arg.shape, idx)]
136+
var_offsets[idx] = offset
137+
offset += arg.size
138+
var_length = offset
139+
fake_expr, _ = self.graph_implementation(fake_args, self.shape,
140+
self.get_data())
141+
param_to_size = {lo.CONSTANT_ID: 1}
142+
param_to_col = {lo.CONSTANT_ID: 0}
143+
# Get the matrix representation of the function.
143144
canon_mat = canonInterface.get_problem_matrix(
144-
[fake_expr], var_length, var_offsets,
145-
{lo.CONSTANT_ID: 1}, {lo.CONSTANT_ID: 0}, self.size
145+
[fake_expr],
146+
var_length,
147+
var_offsets,
148+
param_to_size,
149+
param_to_col,
150+
self.size,
146151
)
147-
148-
# Extract gradient matrix A (exclude constant offset b)
149-
grad_matrix = canon_mat.reshape((var_length + 1, self.size)).tocsc()[:-1, :]
150-
151-
# Split gradients by argument
152+
# HACK TODO TODO convert tensors back to vectors.
153+
# COO = (V[lo.CONSTANT_ID][0], (J[lo.CONSTANT_ID][0], I[lo.CONSTANT_ID][0]))
154+
shape = (var_length + 1, self.size)
155+
stacked_grad = canon_mat.reshape(shape).tocsc()[:-1, :]
156+
# Break up into per argument matrices.
152157
grad_list = []
153-
var_start = 0
158+
start = 0
154159
for arg in self.args:
155160
if arg.is_constant():
156-
# Zero gradient for constants
157-
grad_shape = (arg.size, self.size)
158-
grad_list.append(0 if grad_shape == (1, 1) else
159-
sp.coo_matrix(grad_shape, dtype='float64'))
161+
grad_shape = (arg.size, shape[1])
162+
if grad_shape == (1, 1):
163+
grad_list += [0]
164+
else:
165+
grad_list += [sp.coo_matrix(grad_shape, dtype='float64')]
160166
else:
161-
# Extract gradient block for this variable
162-
var_end = var_start + arg.size
163-
grad_list.append(grad_matrix[var_start:var_end, :])
164-
var_start = var_end
165-
167+
stop = start + arg.size
168+
grad_list += [stacked_grad[start:stop, :]]
169+
start = stop
166170
return grad_list

cvxpy/expressions/leaf.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -669,11 +669,7 @@ def _validate_value(self, val, sparse_path=False):
669669
attr_str = 'in bounds'
670670
else:
671671
attr_str = ([k for (k, v) in self.attributes.items() if v] + ['real'])[0]
672-
if np.isnan(val).any() and self.variables():
673-
# necessary for NLP package extension and computing the structural jacobian
674-
# Only allow NaN for Variables, not Parameters
675-
return val
676-
elif np.isnan(val).any():
672+
if np.isnan(val).any():
677673
raise ValueError(
678674
"%s value must be real." % self.__class__.__name__
679675
)

cvxpy/problems/objective.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def is_dcp(self, dpp: bool = False) -> bool:
158158

159159
def is_dnlp(self) -> bool:
160160
"""
161-
The objective must be epigraph smooth representable.
161+
The objective must be linearizable convex.
162162
"""
163163
return self.args[0].is_linearizable_convex()
164164

@@ -235,7 +235,7 @@ def is_dcp(self, dpp: bool = False) -> bool:
235235

236236
def is_dnlp(self) -> bool:
237237
"""
238-
The objective must be hypograph smooth representable.
238+
The objective must be linearizable concave.
239239
"""
240240
return self.args[0].is_linearizable_concave()
241241

0 commit comments

Comments
 (0)