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
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ Adding a new diff engine atom requires:

The diff engine supports CVXPY `Parameter` objects: `C_problem` registers parameters with the C engine and `update_params()` re-pushes values without rebuilding the expression tree. Sparse parameter values are fused into sparse matmul operations.

`DerivativeChecker` in `nlp_solver.py` provides finite-difference verification of gradients, Jacobians, and Hessians during development.
`DerivativeChecker` in `cvxpy/tests/nlp_tests/derivative_checker.py` provides finite-difference verification of gradients, Jacobians, and Hessians during development.

Key files in `cvxpy/reductions/solvers/nlp_solvers/diff_engine/`:
- `converters.py` - Converts CVXPY expression AST to C diff engine trees. Contains `ATOM_CONVERTERS` dict mapping ~40 atom types to C constructors. Includes optimizations like sparse parameter matmul fusion.
Expand All @@ -182,17 +182,17 @@ Convention: all arrays are flattened in **Fortran order** ('F') for column-major
1. Create a canonicalizer in `cvxpy/reductions/dnlp2smooth/canonicalizers/`
2. The canonicalizer converts non-smooth atoms to smooth equivalents using auxiliary variables
3. Register in `canonicalizers/__init__.py` by adding to `SMOOTH_CANON_METHODS` dict
4. Classify the atom using the three-way atom-level API (see below)
4. If the atom is smooth, override `is_atom_smooth()` to return `True` (see classification below)

### DNLP Atom Classification (Three-way)
### DNLP Atom Classification

Each atom implements exactly one of three atom-level methods:
Atoms are classified as smooth or non-smooth. Non-smooth atoms reuse the existing `is_atom_convex()`/`is_atom_concave()` methods for the DNLP composition rules—no separate method is needed.

| Category | Method | Examples |
|---|---|---|
| **Smooth** | `is_atom_smooth() → True` | exp, log, power, sin, prod, quad_form |
| **NS-convex** | `is_atom_nonsmooth_convex() → True` | abs, max, norm1, norm_inf, huber |
| **NS-concave** | `is_atom_nonsmooth_concave() → True` | min, minimum |
| **Non-smooth convex** | `is_atom_convex() → True` (and not smooth) | abs, max, norm1, norm_inf, huber |
| **Non-smooth concave** | `is_atom_concave() → True` (and not smooth) | min, minimum |

### DNLP Expression-level Rules

Expand Down
14 changes: 3 additions & 11 deletions cvxpy/atoms/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,6 @@ def is_atom_smooth(self) -> bool:
"""Is the atom smooth?"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return False

def is_atom_nonsmooth_concave(self) -> bool:
"""Is the atom nonsmooth and concave?"""
return False

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
"""
Expand Down Expand Up @@ -278,7 +270,7 @@ def is_linearizable_convex(self) -> bool:
# Applies DNLP composition rule.
if self.is_constant():
return True
elif self.is_atom_smooth() or self.is_atom_nonsmooth_convex():
elif self.is_atom_smooth() or self.is_atom_convex():
for idx, arg in enumerate(self.args):
if not (arg.is_smooth() or
(arg.is_linearizable_convex() and self.is_incr(idx)) or
Expand All @@ -287,15 +279,15 @@ def is_linearizable_convex(self) -> bool:
return True
else:
return False

@perf.compute_once
def is_linearizable_concave(self) -> bool:
"""Is the expression concave after linearizing all smooth subexpressions?
"""
# Applies DNLP composition rule.
if self.is_constant():
return True
elif self.is_atom_smooth() or self.is_atom_nonsmooth_concave():
elif self.is_atom_smooth() or self.is_atom_concave():
for idx, arg in enumerate(self.args):
if not (arg.is_smooth() or
(arg.is_linearizable_concave() and self.is_incr(idx)) or
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/elementwise/abs.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_incr(self, idx) -> bool:
"""Is the composition non-decreasing in argument idx?
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/elementwise/huber.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ def is_atom_convex(self) -> bool:
def is_atom_concave(self) -> bool:
"""Is the atom concave?"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_incr(self, idx) -> bool:
"""Is the composition non-decreasing in argument idx?"""
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/elementwise/maximum.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/elementwise/minimum.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return True

def is_atom_nonsmooth_concave(self) -> bool:
"""Is the atom nonsmooth and concave?"""
return True

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/max.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/min.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return True

def is_atom_nonsmooth_concave(self) -> bool:
"""Is the atom nonsmooth and concave?"""
return True

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/norm1.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_incr(self, idx) -> bool:
"""Is the composition non-decreasing in argument idx?
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/norm_inf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
Expand Down
8 changes: 0 additions & 8 deletions cvxpy/atoms/pnorm.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,6 @@ def is_atom_concave(self) -> bool:
"""
return self.p < 1

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return self.p >= 1

def is_atom_nonsmooth_concave(self) -> bool:
"""Is the atom nonsmooth and concave?"""
return self.p < 1

def is_atom_log_log_convex(self) -> bool:
"""Is the atom log-log convex?
"""
Expand Down
4 changes: 0 additions & 4 deletions cvxpy/atoms/sum_largest.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ def is_atom_concave(self) -> bool:
"""Is the atom concave?
"""
return False

def is_atom_nonsmooth_convex(self) -> bool:
"""Is the atom nonsmooth and convex?"""
return True

def is_incr(self, idx) -> bool:
"""Is the composition non-decreasing in argument idx?
Expand Down
12 changes: 10 additions & 2 deletions cvxpy/expressions/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,16 @@


class Variable(Leaf):
"""The optimization variables in a problem."""
"""The optimization variables in a problem.

Attributes
----------
sample_bounds : tuple[np.ndarray, np.ndarray] | None
Explicit bounds ``(low, high)`` for random initial point sampling in
``best_of`` NLP solves. When set, overrides the variable's ``value``
during random initialization. When ``None`` and finite ``bounds`` are
present, those are used instead.
"""

def __init__(
self, shape: int | Iterable[int] = (), name: str | None = None,
Expand All @@ -51,7 +60,6 @@ def __init__(
self._value = None
self.delta = None
self.gradient = None
# bounds for sampling initial points in DNLP problems
self.sample_bounds = None
super(Variable, self).__init__(shape, **kwargs)

Expand Down
Loading
Loading