Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prep Patch/1.2.5 #2184

Merged
merged 18 commits into from
Aug 7, 2023
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
16 changes: 16 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Python 3",
"image": "mcr.microsoft.com/devcontainers/python:0-3.10",
"onCreateCommand": "bash .devcontainer/startup.sh",
"customizations": {
"vscode": {
"settings": {
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestArgs": [
"cvxpy"
]
}
}
}
}
3 changes: 3 additions & 0 deletions .devcontainer/startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pip install -e .
pip install pytest pre-commit
pre-commit install
10 changes: 6 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ jobs:
- uses: actions/setup-python@v2
with:
python-version: 3.8
- name: isort
uses: jamescurtin/isort-action@master
- name: flake8
uses: py-actions/flake8@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff
- name: Run Ruff
run: ruff check --format=github .

build:
runs-on: ${{ matrix.os }}
Expand Down
17 changes: 5 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
repos:
# import sorting with isort
- repo: https://github.com/pycqa/isort
rev: 5.8.0
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.251'
hooks:
- id: isort

# linting and code analysis with flake8
- repo: https://github.com/pycqa/flake8
rev: 3.8.3
hooks:
- id: flake8
args: ['--config=setup.cfg']
files: ^(cvxpy|continuous_integration)/
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
2 changes: 1 addition & 1 deletion cvxpy/atoms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
scalar_product,)
from cvxpy.atoms.affine.bmat import bmat
from cvxpy.atoms.affine.conj import conj
from cvxpy.atoms.affine.conv import conv
from cvxpy.atoms.affine.conv import conv, convolve
from cvxpy.atoms.affine.cumsum import cumsum
from cvxpy.atoms.affine.diag import diag
from cvxpy.atoms.affine.diff import diff
Expand Down
6 changes: 4 additions & 2 deletions cvxpy/atoms/affine/binary_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
from cvxpy.atoms.affine.sum import sum as cvxpy_sum
from cvxpy.constraints.constraint import Constraint
from cvxpy.error import DCPError
from cvxpy.expressions.constants.parameter import (is_param_affine,
is_param_free,)
from cvxpy.expressions.constants.parameter import (
is_param_affine,
is_param_free,
)


class BinaryOperator(AffAtom):
Expand Down
104 changes: 96 additions & 8 deletions cvxpy/atoms/affine/conv.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
limitations under the License.
"""

import warnings
from typing import List, Tuple

import numpy as np
Expand Down Expand Up @@ -44,19 +45,21 @@ class conv(AffAtom):
rh_expr : Expression
A 1D vector or a 2D column vector.
"""
# TODO work with right hand constant.
# TODO(akshayka): make DGP-compatible

def __init__(self, lh_expr, rh_expr) -> None:
warnings.warn("conv is deprecated. Use convolve instead.", DeprecationWarning)
super(conv, self).__init__(lh_expr, rh_expr)

@AffAtom.numpy_numeric
def numeric(self, values):
"""Convolve the two values.
"""
# Convert values to 1D.
values = list(map(intf.from_2D_to_1D, values))
return np.convolve(values[0], values[1])
flat_values = list(map(intf.from_2D_to_1D, values))
output = np.convolve(flat_values[0], flat_values[1])
if values[0].ndim == 2 or values[1].ndim == 2:
return output[:, None]
else:
return output

def validate_arguments(self) -> None:
"""Checks that both arguments are vectors, and the first is constant.
Expand All @@ -69,9 +72,94 @@ def validate_arguments(self) -> None:
def shape_from_args(self) -> Tuple[int, int]:
"""The sum of the argument dimensions - 1.
"""
lh_length = self.args[0].shape[0]
rh_length = self.args[1].shape[0]
return (lh_length + rh_length - 1, 1)
lh_length = self.args[0].size
rh_length = self.args[1].size
output_length = lh_length + rh_length - 1
if self.args[0].ndim == 2 or self.args[1].ndim == 2:
return (output_length, 1)
else:
return (output_length,)

def sign_from_args(self) -> Tuple[bool, bool]:
"""Same as times.
"""
return u.sign.mul_sign(self.args[0], self.args[1])

def is_incr(self, idx) -> bool:
"""Is the composition non-decreasing in argument idx?
"""
return self.args[0].is_nonneg()

def is_decr(self, idx) -> bool:
"""Is the composition non-increasing in argument idx?
"""
return self.args[0].is_nonpos()

def graph_implementation(
self, arg_objs, shape: Tuple[int, ...], data=None
) -> Tuple[lo.LinOp, List[Constraint]]:
"""Convolve two vectors.

Parameters
----------
arg_objs : list
LinExpr for each argument.
shape : tuple
The shape of the resulting expression.
data :
Additional data required by the atom.

Returns
-------
tuple
(LinOp for objective, list of constraints)
"""
return (lu.conv(arg_objs[0], arg_objs[1], shape), [])


class convolve(AffAtom):
""" 1D discrete convolution of two vectors.

The discrete convolution :math:`c` of vectors :math:`a` and :math:`b` of
lengths :math:`n` and :math:`m`, respectively, is a length-:math:`(n+m-1)`
vector where

.. math::

c_k = \\sum_{i+j=k} a_ib_j, \\quad k=0, \\ldots, n+m-2.

Matches numpy.convolve

Parameters
----------
lh_expr : Constant
A constant scalar or 1D vector.
rh_expr : Expression
A scalar or 1D vector.
"""
# TODO work with right hand constant.
# TODO(akshayka): make DGP-compatible

@AffAtom.numpy_numeric
def numeric(self, values):
"""Convolve the two values.
"""
return np.convolve(values[0], values[1])

def validate_arguments(self) -> None:
"""Checks that both arguments are vectors, and the first is constant.
"""
if not self.args[0].ndim <= 1 or not self.args[1].ndim <= 1:
raise ValueError("The arguments to conv must be scalar or 1D.")
if not self.args[0].is_constant():
raise ValueError("The first argument to conv must be constant.")

def shape_from_args(self) -> Tuple[int, int]:
"""The sum of the argument dimensions - 1.
"""
lh_length = self.args[0].size
rh_length = self.args[1].size
return (lh_length + rh_length - 1,)

def sign_from_args(self) -> Tuple[bool, bool]:
"""Same as times.
Expand Down
2 changes: 1 addition & 1 deletion cvxpy/atoms/affine/partial_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def _term(expr, j: int, dims: Tuple[int], axis: Optional[int] = 0):
return a @ expr @ b


# flake8: noqa: E501
# ruff: noqa: E501
def partial_trace(expr, dims: Tuple[int], axis: Optional[int] = 0):
"""
Assumes :math:`\\texttt{expr} = X_1 \\otimes \\cdots \\otimes X_n` is a 2D Kronecker
Expand Down
4 changes: 2 additions & 2 deletions cvxpy/atoms/elementwise/log_normcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
from cvxpy.expressions.expression import Expression


# flake8: noqa: E501
def log_normcdf(x): # noqa: E501
# ruff: noqa: E501
def log_normcdf(x):
"""Elementwise log of the cumulative distribution function of a standard normal random variable.

The implementation is a quadratic approximation with modest accuracy over [-4, 4].
Expand Down
2 changes: 1 addition & 1 deletion cvxpy/atoms/elementwise/loggamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from cvxpy.atoms.elementwise.maximum import maximum


# flake8: noqa: E501
# ruff: noqa: E501
def loggamma(x):
"""Elementwise log of the gamma function.

Expand Down
10 changes: 8 additions & 2 deletions cvxpy/atoms/geo_mean.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@
from cvxpy.atoms.atom import Atom
from cvxpy.constraints.constraint import Constraint
from cvxpy.expressions import cvxtypes
from cvxpy.utilities.power_tools import (approx_error, decompose, fracify,
lower_bound, over_bound, prettydict,)
from cvxpy.utilities.power_tools import (
approx_error,
decompose,
fracify,
lower_bound,
over_bound,
prettydict,
)


class geo_mean(Atom):
Expand Down
1 change: 1 addition & 0 deletions cvxpy/constraints/power.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def __init__(self, x, y, z, alpha, constr_id=None) -> None:
def __str__(self) -> str:
return "Pow3D(%s, %s, %s; %s)" % (self.x, self.y, self.z, self.alpha)

@property
def residual(self):
# TODO: The projection should be implemented directly.
from cvxpy import Minimize, Problem, Variable, hstack, norm2
Expand Down
2 changes: 1 addition & 1 deletion cvxpy/cvxcore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bash rebuild_cvxcore.sh
```

Rebuilding cvxcore will automatically generate ``cvxpy/cvxcore/python/cvxpy.py``.
That generated file will probably have flake8 style errors.
That generated file will probably have linter errors.
If you use ``pre-commit`` as part of development then it will
automatically fix those, but you'll need to add the modified
file again before attempting to commit.
Expand Down
7 changes: 5 additions & 2 deletions cvxpy/expressions/leaf.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@
import cvxpy.interface as intf
from cvxpy.constraints.constraint import Constraint
from cvxpy.expressions import expression
from cvxpy.settings import (GENERAL_PROJECTION_TOL, PSD_NSD_PROJECTION_TOL,
SPARSE_PROJECTION_TOL,)
from cvxpy.settings import (
GENERAL_PROJECTION_TOL,
PSD_NSD_PROJECTION_TOL,
SPARSE_PROJECTION_TOL,
)


class Leaf(expression.Expression):
Expand Down
21 changes: 15 additions & 6 deletions cvxpy/interface/scipy_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@
limitations under the License.
"""

from scipy.sparse import spmatrix
from scipy import sparse

from cvxpy.expressions import expression as exp

SPARSE_MATRIX_CLASSES = [
sparse.csc_matrix,
sparse.csr_matrix,
sparse.coo_matrix,
sparse.bsr_matrix,
sparse.lil_matrix,
sparse.dia_matrix,
sparse.dok_matrix,
]
BIN_OPS = ["__div__", "__mul__", "__add__", "__sub__",
"__le__", "__eq__", "__lt__", "__gt__"]

Expand All @@ -32,8 +41,8 @@ def new_method(self, other):
return method(self, other)
return new_method


for method_name in BIN_OPS:
method = getattr(spmatrix, method_name)
new_method = wrap_bin_op(method)
setattr(spmatrix, method_name, new_method)
for cls in SPARSE_MATRIX_CLASSES:
for method_name in BIN_OPS:
method = getattr(cls, method_name)
new_method = wrap_bin_op(method)
setattr(cls, method_name, new_method)
6 changes: 4 additions & 2 deletions cvxpy/problems/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
from cvxpy.reductions.solvers.defines import SOLVER_MAP_CONIC, SOLVER_MAP_QP
from cvxpy.reductions.solvers.qp_solvers.qp_solver import QpSolver
from cvxpy.reductions.solvers.solver import Solver
from cvxpy.reductions.solvers.solving_chain import (SolvingChain,
construct_solving_chain,)
from cvxpy.reductions.solvers.solving_chain import (
SolvingChain,
construct_solving_chain,
)
from cvxpy.settings import SOLVERS
from cvxpy.utilities.deterministic import unique_list

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"""

from cvxpy.reductions.complex2real.atom_canonicalizers.abs_canon import (
abs_canon,)
abs_canon,
)


def pnorm_canon(expr, real_args, imag_args, real2imag):
Expand Down
14 changes: 11 additions & 3 deletions cvxpy/reductions/complex2real/complex2real.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,22 @@

from cvxpy import problems
from cvxpy import settings as s
from cvxpy.constraints import (PSD, SOC, Equality, Inequality, NonNeg, NonPos,
Zero,)
from cvxpy.constraints import (
PSD,
SOC,
Equality,
Inequality,
NonNeg,
NonPos,
Zero,
)
from cvxpy.constraints.constraint import Constraint
from cvxpy.expressions import cvxtypes
from cvxpy.lin_ops import lin_utils as lu
from cvxpy.reductions import InverseData, Solution
from cvxpy.reductions.complex2real.atom_canonicalizers import (
CANON_METHODS as elim_cplx_methods,)
CANON_METHODS as elim_cplx_methods,
)
from cvxpy.reductions.reduction import Reduction


Expand Down
6 changes: 4 additions & 2 deletions cvxpy/reductions/dcp2cone/atom_canonicalizers/huber_canon.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
from cvxpy.atoms.elementwise.power import power
from cvxpy.expressions.variable import Variable
from cvxpy.reductions.dcp2cone.atom_canonicalizers.power_canon import (
power_canon,)
power_canon,
)
from cvxpy.reductions.eliminate_pwl.atom_canonicalizers.abs_canon import (
abs_canon,)
abs_canon,
)


def huber_canon(expr, args):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from cvxpy.atoms import trace
from cvxpy.expressions.variable import Variable
from cvxpy.reductions.dcp2cone.atom_canonicalizers.lambda_max_canon import (
lambda_max_canon,)
lambda_max_canon,
)


def lambda_sum_largest_canon(expr, args):
Expand Down
Loading
Loading