diff --git a/cvxpy/atoms/affine/affine_atom.py b/cvxpy/atoms/affine/affine_atom.py index f806a9501d..6c5c3c69e5 100644 --- a/cvxpy/atoms/affine/affine_atom.py +++ b/cvxpy/atoms/affine/affine_atom.py @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. """ -import abc from typing import Any, List, Tuple import scipy.sparse as sp @@ -29,7 +28,6 @@ class AffAtom(Atom): """ Abstract base class for affine atoms. """ - __metaclass__ = abc.ABCMeta _allow_complex = True def sign_from_args(self) -> Tuple[bool, bool]: diff --git a/cvxpy/atoms/affine/binary_operators.py b/cvxpy/atoms/affine/binary_operators.py index bbc44d979c..7fca640aab 100644 --- a/cvxpy/atoms/affine/binary_operators.py +++ b/cvxpy/atoms/affine/binary_operators.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import division - import operator as op from functools import reduce from typing import List, Tuple diff --git a/cvxpy/atoms/atom.py b/cvxpy/atoms/atom.py index 8cc66633a6..257ea7522d 100644 --- a/cvxpy/atoms/atom.py +++ b/cvxpy/atoms/atom.py @@ -35,7 +35,6 @@ class Atom(Expression): """ Abstract base class for atoms. """ - __metaclass__ = abc.ABCMeta _allow_complex = False # args are the expressions passed into the Atom constructor. diff --git a/cvxpy/atoms/axis_atom.py b/cvxpy/atoms/axis_atom.py index 31d640d9bb..5ca184cbf0 100644 --- a/cvxpy/atoms/axis_atom.py +++ b/cvxpy/atoms/axis_atom.py @@ -14,7 +14,6 @@ limitations under the License. """ -import abc from typing import List, Optional, Tuple import numpy as np @@ -28,8 +27,6 @@ class AxisAtom(Atom): An abstract base class for atoms that can be applied along an axis. """ - __metaclass__ = abc.ABCMeta - def __init__(self, expr, axis: Optional[int] = None, keepdims: bool = False) -> None: self.axis = axis self.keepdims = keepdims diff --git a/cvxpy/atoms/elementwise/elementwise.py b/cvxpy/atoms/elementwise/elementwise.py index d59dcc2893..6964be01c5 100644 --- a/cvxpy/atoms/elementwise/elementwise.py +++ b/cvxpy/atoms/elementwise/elementwise.py @@ -14,7 +14,6 @@ limitations under the License. """ -import abc from typing import Tuple import numpy as np @@ -27,7 +26,6 @@ class Elementwise(Atom): """ Abstract base class for elementwise atoms. """ - __metaclass__ = abc.ABCMeta def shape_from_args(self) -> Tuple[int, ...]: """Shape is the same as the sum of the arguments. diff --git a/cvxpy/atoms/elementwise/kl_div.py b/cvxpy/atoms/elementwise/kl_div.py index cfbef97f3f..e5121d5f84 100644 --- a/cvxpy/atoms/elementwise/kl_div.py +++ b/cvxpy/atoms/elementwise/kl_div.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import division - from typing import List, Optional, Tuple import numpy as np diff --git a/cvxpy/atoms/elementwise/maximum.py b/cvxpy/atoms/elementwise/maximum.py index 42b1104f16..37f30644f4 100644 --- a/cvxpy/atoms/elementwise/maximum.py +++ b/cvxpy/atoms/elementwise/maximum.py @@ -14,16 +14,13 @@ limitations under the License. """ -import sys +from functools import reduce from typing import Any, List, Tuple import numpy as np from cvxpy.atoms.elementwise.elementwise import Elementwise -if sys.version_info >= (3, 0): - from functools import reduce - class maximum(Elementwise): """Elementwise maximum of a sequence of expressions. diff --git a/cvxpy/atoms/elementwise/minimum.py b/cvxpy/atoms/elementwise/minimum.py index 35ec71d490..1fef01fe44 100644 --- a/cvxpy/atoms/elementwise/minimum.py +++ b/cvxpy/atoms/elementwise/minimum.py @@ -13,16 +13,13 @@ limitations under the License. """ -import sys +from functools import reduce from typing import Any, List, Tuple import numpy as np from cvxpy.atoms.elementwise.elementwise import Elementwise -if sys.version_info >= (3, 0): - from functools import reduce - class minimum(Elementwise): """Elementwise minimum of a sequence of expressions. diff --git a/cvxpy/atoms/elementwise/rel_entr.py b/cvxpy/atoms/elementwise/rel_entr.py index 9df1675dbb..ebc3a38e55 100644 --- a/cvxpy/atoms/elementwise/rel_entr.py +++ b/cvxpy/atoms/elementwise/rel_entr.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import division - from typing import List, Optional, Tuple import numpy as np diff --git a/cvxpy/atoms/perspective.py b/cvxpy/atoms/perspective.py index 226fbc10e1..ed312dc62f 100644 --- a/cvxpy/atoms/perspective.py +++ b/cvxpy/atoms/perspective.py @@ -136,3 +136,17 @@ def shape_from_args(self) -> Tuple[int, ...]: """Returns the (row, col) shape of the expression. """ return self.f.shape + + def _grad(self, values): + """Gives the (sub/super)gradient of the atom w.r.t. each argument. + + Matrix expressions are vectorized, so the gradient is a matrix. + + Args: + values: A list of numeric values for the arguments. + + Returns: + A list of SciPy CSC sparse matrices or None. + """ + # TODO + raise NotImplementedError() diff --git a/cvxpy/atoms/quad_form.py b/cvxpy/atoms/quad_form.py index 49a217da52..3c76c04af1 100644 --- a/cvxpy/atoms/quad_form.py +++ b/cvxpy/atoms/quad_form.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import division - import warnings from typing import Tuple diff --git a/cvxpy/constraints/cones.py b/cvxpy/constraints/cones.py index 86dab02865..072b447eaf 100644 --- a/cvxpy/constraints/cones.py +++ b/cvxpy/constraints/cones.py @@ -14,8 +14,6 @@ limitations under the License. """ -import abc - from cvxpy.constraints.constraint import Constraint @@ -40,8 +38,6 @@ class Cone(Constraint): A unique id for the constraint. """ - __metaclass__ = abc.ABCMeta - def __init__(self, args, constr_id=None) -> None: super(Cone, self).__init__(args, constr_id) diff --git a/cvxpy/constraints/constraint.py b/cvxpy/constraints/constraint.py index 1d46d909bd..3286023a32 100644 --- a/cvxpy/constraints/constraint.py +++ b/cvxpy/constraints/constraint.py @@ -38,8 +38,6 @@ class Constraint(u.Canonical): A unique id for the constraint. """ - __metaclass__ = abc.ABCMeta - def __init__(self, args, constr_id=None) -> None: # TODO cast constants. # self.args = [cvxtypes.expression().cast_to_const(arg) for arg in args] @@ -129,7 +127,8 @@ def is_dpp(self, context='dcp') -> bool: else: raise ValueError("Unsupported context ", context) - @abc.abstractproperty + @property + @abc.abstractmethod def residual(self): """The residual of the constraint. @@ -213,16 +212,6 @@ def get_data(self): """ return [self.id] - def __nonzero__(self): - """Raises an exception when called. - - Python 2 version. - - Called when evaluating the truth value of the constraint. - Raising an error here prevents writing chained constraints. - """ - return self._chain_constraints() - def _chain_constraints(self): """Raises an error due to chained constraints. """ diff --git a/cvxpy/expressions/expression.py b/cvxpy/expressions/expression.py index 7eff74e178..036d3bddfa 100644 --- a/cvxpy/expressions/expression.py +++ b/cvxpy/expressions/expression.py @@ -119,8 +119,6 @@ class Expression(u.Canonical): expressions (e.g., the sum of two expressions) and constraints. """ - __metaclass__ = abc.ABCMeta - # Handles arithmetic operator overloading with Numpy. __array_priority__ = 100 diff --git a/cvxpy/expressions/leaf.py b/cvxpy/expressions/leaf.py index 073eb819bf..3983a2e8c5 100644 --- a/cvxpy/expressions/leaf.py +++ b/cvxpy/expressions/leaf.py @@ -15,7 +15,6 @@ """ from __future__ import annotations -import abc from typing import TYPE_CHECKING, Iterable if TYPE_CHECKING: @@ -95,8 +94,6 @@ class Leaf(expression.Expression): An iterable of length two specifying lower and upper bounds. """ - __metaclass__ = abc.ABCMeta - def __init__( self, shape: int | tuple[int, ...], value=None, nonneg: bool = False, nonpos: bool = False, complex: bool = False, imag: bool = False, diff --git a/cvxpy/expressions/variable.py b/cvxpy/expressions/variable.py index 3a5f121ecc..7fc62c016b 100644 --- a/cvxpy/expressions/variable.py +++ b/cvxpy/expressions/variable.py @@ -29,7 +29,7 @@ class Variable(Leaf): """ def __init__( - self, shape: int | Iterable[int, ...] = (), name: str | None = None, + self, shape: int | Iterable[int] = (), name: str | None = None, var_id: int | None = None, **kwargs: Any ): if var_id is None: diff --git a/cvxpy/interface/base_matrix_interface.py b/cvxpy/interface/base_matrix_interface.py index 331e462c96..77cc8ccf84 100644 --- a/cvxpy/interface/base_matrix_interface.py +++ b/cvxpy/interface/base_matrix_interface.py @@ -21,12 +21,11 @@ import cvxpy.interface.matrix_utilities -class BaseMatrixInterface: +class BaseMatrixInterface(metaclass=abc.ABCMeta): """ An interface between constants' internal values and the target matrix used internally. """ - __metaclass__ = abc.ABCMeta @abc.abstractmethod def const_to_matrix(self, value, convert_scalars: bool = False): diff --git a/cvxpy/performance_tests/test_warmstart.py b/cvxpy/performance_tests/test_warmstart.py index f531553160..8737403acd 100644 --- a/cvxpy/performance_tests/test_warmstart.py +++ b/cvxpy/performance_tests/test_warmstart.py @@ -18,8 +18,6 @@ DO NOT CALL THESE FUNCTIONS IN YOUR CODE! """ -from __future__ import print_function - import time import unittest diff --git a/cvxpy/problems/param_prob.py b/cvxpy/problems/param_prob.py index a44f91040a..fae25466ab 100644 --- a/cvxpy/problems/param_prob.py +++ b/cvxpy/problems/param_prob.py @@ -16,20 +16,19 @@ import abc -class ParamProb: +class ParamProb(metaclass=abc.ABCMeta): """An abstract base class for parameterized problems. Parameterized problems are produced during the first canonicalization and allow canonicalization to be short-circuited for future solves. """ - __metaclass__ = abc.ABCMeta - @abc.abstractproperty + @abc.abstractmethod def is_mixed_integer(self) -> bool: """Is the problem mixed-integer?""" raise NotImplementedError() - @abc.abstractproperty + @abc.abstractmethod def apply_parameters(self, id_to_param_value=None, zero_offset: bool = False, keep_zeros: bool = False): """Returns A, b after applying parameters (and reshaping). diff --git a/cvxpy/reductions/matrix_stuffing.py b/cvxpy/reductions/matrix_stuffing.py index b136161d68..786e4cc848 100644 --- a/cvxpy/reductions/matrix_stuffing.py +++ b/cvxpy/reductions/matrix_stuffing.py @@ -15,7 +15,6 @@ """ -import abc from typing import List, Optional, Tuple import numpy as np @@ -106,8 +105,6 @@ def ravel_multi_index(multi_index, x, vert_offset): class MatrixStuffing(Reduction): """Stuffs a problem into a standard form for a family of solvers.""" - __metaclass__ = abc.ABCMeta - def apply(self, problem) -> None: """Returns a stuffed problem. diff --git a/cvxpy/reductions/reduction.py b/cvxpy/reductions/reduction.py index 1979877e6e..851570fc24 100644 --- a/cvxpy/reductions/reduction.py +++ b/cvxpy/reductions/reduction.py @@ -17,7 +17,7 @@ from abc import ABCMeta, abstractmethod -class Reduction: +class Reduction(metaclass=ABCMeta): """Abstract base class for reductions. A reduction is an actor that transforms a problem into an @@ -46,8 +46,6 @@ class Reduction: A problem owned by this reduction; possibly None. """ - __metaclass__ = ABCMeta - def __init__(self, problem=None) -> None: """Construct a reduction for reducing `problem`. diff --git a/cvxpy/reductions/solvers/conic_solvers/conic_solver.py b/cvxpy/reductions/solvers/conic_solvers/conic_solver.py index 6100d2ebe5..aa61eccd15 100644 --- a/cvxpy/reductions/solvers/conic_solvers/conic_solver.py +++ b/cvxpy/reductions/solvers/conic_solvers/conic_solver.py @@ -165,7 +165,8 @@ def psd_format_mat(constr): # Default is identity. return sp.eye(constr.size, format='csc') - def format_constraints(self, problem, exp_cone_order): + @classmethod + def format_constraints(cls, problem, exp_cone_order): """ Returns a ParamConeProg whose problem data tensors will yield the coefficient "A" and offset "b" for the constraint in the following @@ -249,7 +250,7 @@ def format_constraints(self, problem, exp_cone_order): arg_mats.append(space_mat) restruct_mat.append(sp.hstack(arg_mats)) elif type(constr) == PSD: - restruct_mat.append(self.psd_format_mat(constr)) + restruct_mat.append(cls.psd_format_mat(constr)) else: raise ValueError("Unsupported constraint type.") diff --git a/cvxpy/reductions/solvers/solver.py b/cvxpy/reductions/solvers/solver.py index bdaf9e30e9..140072923c 100644 --- a/cvxpy/reductions/solvers/solver.py +++ b/cvxpy/reductions/solvers/solver.py @@ -30,8 +30,6 @@ class Solver(Reduction): # There are separate ConeDims classes for cone programs vs QPs. # See cone_matrix_stuffing.py and qp_matrix_stuffing.py for details. - __metaclass__ = abc.ABCMeta - # Solver capabilities. MIP_CAPABLE = False BOUNDED_VARIABLES = False diff --git a/cvxpy/tests/test_base_classes.py b/cvxpy/tests/test_base_classes.py new file mode 100644 index 0000000000..4c0759b7de --- /dev/null +++ b/cvxpy/tests/test_base_classes.py @@ -0,0 +1,27 @@ +import inspect + +import pytest + +from cvxpy.atoms.affine.affine_atom import AffAtom +from cvxpy.atoms.atom import Atom +from cvxpy.constraints.constraint import Constraint +from cvxpy.expressions.expression import Expression +from cvxpy.expressions.leaf import Leaf +from cvxpy.interface.base_matrix_interface import BaseMatrixInterface +from cvxpy.problems.param_prob import ParamProb +from cvxpy.reductions.reduction import Reduction +from cvxpy.reductions.solvers.conic_solvers.conic_solver import ConicSolver +from cvxpy.reductions.solvers.solver import Solver +from cvxpy.utilities.canonical import Canonical + + +@pytest.mark.parametrize("expected_abc", [ + Canonical, + Expression, Atom, AffAtom, Leaf, + Constraint, + Reduction, Solver, ConicSolver, + ParamProb, + BaseMatrixInterface, +]) +def test_is_abstract(expected_abc): + assert inspect.isabstract(expected_abc) diff --git a/cvxpy/tests/test_cone2cone.py b/cvxpy/tests/test_cone2cone.py index 7c0708c3b6..07a2430d53 100644 --- a/cvxpy/tests/test_cone2cone.py +++ b/cvxpy/tests/test_cone2cone.py @@ -49,7 +49,7 @@ def simulate_chain(in_prob): # Dualize the problem, reconstruct a high-level cvxpy problem for the dual. # Solve the problem, invert the dualize reduction. - cone_prog = ConicSolver().format_constraints(cone_prog, exp_cone_order=[0, 1, 2]) + cone_prog = ConicSolver.format_constraints(cone_prog, exp_cone_order=[0, 1, 2]) data, inv_data = a2d.Dualize.apply(cone_prog) A, b, c, K_dir = data[s.A], data[s.B], data[s.C], data['K_dir'] y = cp.Variable(shape=(A.shape[1],)) @@ -205,7 +205,7 @@ def simulate_chain(in_prob, affine, **solve_kwargs): # apply the Slacks reduction, reconstruct a high-level problem, # solve the problem, invert the reduction. - cone_prog = ConicSolver().format_constraints(cone_prog, exp_cone_order=[0, 1, 2]) + cone_prog = ConicSolver.format_constraints(cone_prog, exp_cone_order=[0, 1, 2]) data, inv_data = a2d.Slacks.apply(cone_prog, affine) G, h, f, K_dir, K_aff = data[s.A], data[s.B], data[s.C], data['K_dir'], data['K_aff'] G = sp.sparse.csc_matrix(G) diff --git a/cvxpy/tests/test_examples.py b/cvxpy/tests/test_examples.py index c6a9e5d5ab..10aeadd18d 100644 --- a/cvxpy/tests/test_examples.py +++ b/cvxpy/tests/test_examples.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import print_function - import unittest import numpy as np diff --git a/cvxpy/tests/test_grad.py b/cvxpy/tests/test_grad.py index 59afa7eba5..cbcc827026 100644 --- a/cvxpy/tests/test_grad.py +++ b/cvxpy/tests/test_grad.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import division - import numpy as np import cvxpy as cp diff --git a/cvxpy/tests/test_matrices.py b/cvxpy/tests/test_matrices.py index 31c18e8007..0c60433f2e 100644 --- a/cvxpy/tests/test_matrices.py +++ b/cvxpy/tests/test_matrices.py @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. """ -import sys import unittest from typing import Tuple @@ -25,8 +24,6 @@ from cvxpy.expressions.expression import Expression from cvxpy.expressions.variable import Variable -PY35 = sys.version_info >= (3, 5) - class TestMatrices(unittest.TestCase): """ Unit tests for testing different forms of matrices as constants. """ @@ -130,8 +127,7 @@ def test_scipy_sparse(self) -> None: self.assertExpression(B @ var, (4, 2)) self.assertExpression(var - A, (4, 2)) self.assertExpression(A - A - var, (4, 2)) - if PY35: - self.assertExpression(var.__rmatmul__(B), (4, 2)) + self.assertExpression(var.__rmatmul__(B), (4, 2)) # self.assertExpression(var <= A, (4, 2)) # self.assertExpression(A <= var, (4, 2)) # self.assertExpression(var == A, (4, 2)) diff --git a/cvxpy/tests/test_problem.py b/cvxpy/tests/test_problem.py index c6c1ce0a30..e3a20a2f63 100644 --- a/cvxpy/tests/test_problem.py +++ b/cvxpy/tests/test_problem.py @@ -985,13 +985,8 @@ def test_indexing(self) -> None: def test_non_python_int_index(self) -> None: """Test problems that have special types as indices. """ - import sys - if sys.version_info > (3,): - my_long = int - else: - my_long = long # noqa: F821 - # Test with long indices. - cost = self.x[0:my_long(2)][0] + # Test with int indices. + cost = self.x[0:int(2)][0] p = Problem(cp.Minimize(cost), [self.x == 1]) result = p.solve(solver=cp.SCS) self.assertAlmostEqual(result, 1) diff --git a/cvxpy/tests/test_quad_form.py b/cvxpy/tests/test_quad_form.py index 607fc25dc5..0e0e63e5bf 100644 --- a/cvxpy/tests/test_quad_form.py +++ b/cvxpy/tests/test_quad_form.py @@ -14,8 +14,6 @@ limitations under the License. """ -from __future__ import absolute_import, division, print_function - import warnings import numpy as np diff --git a/cvxpy/transforms/indicator.py b/cvxpy/transforms/indicator.py index d2e1e0f919..22d43225d3 100644 --- a/cvxpy/transforms/indicator.py +++ b/cvxpy/transforms/indicator.py @@ -94,6 +94,11 @@ def shape(self) -> Tuple[int, ...]: """ return () + def is_dpp(self, context: str = 'dcp') -> bool: + """The expression is a disciplined parameterized expression. + """ + return False + def name(self) -> str: """Returns the string representation of the expression. """ diff --git a/cvxpy/utilities/canonical.py b/cvxpy/utilities/canonical.py index 9affc12b0f..61dff88c39 100644 --- a/cvxpy/utilities/canonical.py +++ b/cvxpy/utilities/canonical.py @@ -22,13 +22,11 @@ from cvxpy.utilities.deterministic import unique_list -class Canonical: +class Canonical(metaclass=abc.ABCMeta): """ An interface for objects that can be canonicalized. """ - __metaclass__ = abc.ABCMeta - @property def expr(self): if not len(self.args) == 1: @@ -161,6 +159,10 @@ def _max_ndim(self) -> int: """ return max([self.ndim] + [arg._max_ndim() for arg in self.args]) + @abc.abstractmethod + def __str__(self) -> str: + raise NotImplementedError() + _MISSING = object() diff --git a/cvxpy/utilities/coeff_extractor.py b/cvxpy/utilities/coeff_extractor.py index ac604189e4..ef7df92b57 100644 --- a/cvxpy/utilities/coeff_extractor.py +++ b/cvxpy/utilities/coeff_extractor.py @@ -14,7 +14,7 @@ limitations under the License. """ -from __future__ import annotations, division +from __future__ import annotations import operator from typing import List diff --git a/cvxpy/utilities/key_utils.py b/cvxpy/utilities/key_utils.py index 60524ebe5b..b4aa5bf16e 100644 --- a/cvxpy/utilities/key_utils.py +++ b/cvxpy/utilities/key_utils.py @@ -16,8 +16,6 @@ # Utility functions to handle indexing/slicing into an expression. -from __future__ import division - import numbers from typing import Optional, Tuple diff --git a/examples/advanced/circuits.py b/examples/advanced/circuits.py index ba9d18c80e..ba1d9c0e0a 100644 --- a/examples/advanced/circuits.py +++ b/examples/advanced/circuits.py @@ -35,8 +35,7 @@ class Ground(Node): def constraints(self): return [self.voltage == 0] + super(Ground, self).constraints() -class Device: - __metaclass__ = abc.ABCMeta +class Device(metaclass=abc.ABCMeta): """ A device on a circuit. """ def __init__(self, pos_node, neg_node) -> None: self.pos_node = pos_node diff --git a/examples/advanced/test.py b/examples/advanced/test.py index f81b2a3265..9225a55e67 100644 --- a/examples/advanced/test.py +++ b/examples/advanced/test.py @@ -74,8 +74,7 @@ def __rmul__(self, other): a = np.random.random((2,2)) -class Bar1: - __metaclass__ = MyMeta +class Bar1(metaclass=MyMeta): def __add__(self, rhs): return 0 def __radd__(self, rhs): return 1 def __lt__(self, rhs): return 0 diff --git a/examples/extensions/mixed_integer/noncvx_variable.py b/examples/extensions/mixed_integer/noncvx_variable.py index 4bce97458a..fbfe9e101e 100644 --- a/examples/extensions/mixed_integer/noncvx_variable.py +++ b/examples/extensions/mixed_integer/noncvx_variable.py @@ -23,7 +23,6 @@ class NonCvxVariable(cvxpy.Variable): - __metaclass__ = abc.ABCMeta def __init__(self, *args, **kwargs) -> None: super(NonCvxVariable, self).__init__(*args, **kwargs) self.noncvx = True