From e9f1164331a71c5d9b3ab5806f0ab911e2a928c1 Mon Sep 17 00:00:00 2001 From: Brendan O'Donoghue Date: Tue, 18 Nov 2025 21:52:37 +0000 Subject: [PATCH 1/4] fmt --- scs/py/__init__.py | 308 ++++++++++++++++++++++----------------------- 1 file changed, 154 insertions(+), 154 deletions(-) diff --git a/scs/py/__init__.py b/scs/py/__init__.py index 9c977e8..0f0d1e1 100644 --- a/scs/py/__init__.py +++ b/scs/py/__init__.py @@ -27,171 +27,171 @@ # Choose which SCS to import based on settings. def _select_scs_module(stgs): - if stgs.pop("gpu", False): # False by default - if not stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): - raise NotImplementedError( - "For the GPU direct solver, pass `use_indirect=False cudss=True`.") - from scs import _scs_gpu - - return _scs_gpu + if stgs.pop("gpu", False): # False by default + if not stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): + raise NotImplementedError( + "For the GPU direct solver, pass `use_indirect=False cudss=True`." + ) + from scs import _scs_gpu - if stgs.pop("mkl", False): # False by default - if stgs.pop("use_indirect", False): - raise NotImplementedError( - "MKL indirect solver not yet available, pass `use_indirect=False`." - ) - from scs import _scs_mkl + return _scs_gpu - return _scs_mkl + if stgs.pop("mkl", False): # False by default + if stgs.pop("use_indirect", False): + raise NotImplementedError( + "MKL indirect solver not yet available, pass `use_indirect=False`." + ) + from scs import _scs_mkl - if stgs.pop("cudss", False): # False by default - if stgs.pop("use_indirect", False): - raise NotImplementedError( - "cuDSS is a direct solver, pass `use_indirect=False`." - ) - from scs import _scs_cudss + return _scs_mkl - return _scs_cudss + if stgs.pop("cudss", False): # False by default + if stgs.pop("use_indirect", False): + raise NotImplementedError( + "cuDSS is a direct solver, pass `use_indirect=False`." + ) + from scs import _scs_cudss - if stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): - from scs import _scs_indirect + return _scs_cudss - return _scs_indirect + if stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): + from scs import _scs_indirect - return _scs_direct + return _scs_indirect + + return _scs_direct class SCS(object): - def __init__(self, data, cone, **settings): - """Initialize the SCS solver. - - @param data Dictionary containing keys `P`, `A`, `b`, `c`. - @param cone Dictionary containing cone information. - @param settings Settings as kwargs, see docs. - - """ - self._settings = settings - if not data or not cone: - raise ValueError("Missing data or cone information") - - if "b" not in data or "c" not in data: - raise ValueError("Missing one of b, c from data dictionary") - if "A" not in data: - raise ValueError("Missing A from data dictionary") - - A = data["A"] - b = data["b"] - c = data["c"] - - if A is None or b is None or c is None: - raise ValueError("Incomplete data specification") - - if not sparse.issparse(A): - raise TypeError("A is required to be a sparse matrix") - if not A.format == "csc": - warn( - "Converting A to a CSC (compressed sparse column) matrix;" - " may take a while." - ) - A = A.tocsc() - - if sparse.issparse(b): - b = b.todense() - - if sparse.issparse(c): - c = c.todense() - - m = len(b) - n = len(c) - - if not A.has_sorted_indices: - A.sort_indices() - Adata, Aindices, Acolptr = A.data, A.indices, A.indptr - if A.shape != (m, n): - raise ValueError("A shape not compatible with b,c") - - Pdata, Pindices, Pcolptr = None, None, None - if "P" in data: - P = data["P"] - if P is not None: - if not sparse.issparse(P): - raise TypeError("P is required to be a sparse matrix") - if P.shape != (n, n): - raise ValueError("P shape not compatible with A,b,c") - if not P.format == "csc": - warn( - "Converting P to a CSC (compressed sparse column) " - "matrix; may take a while." - ) - P = P.tocsc() - # extract upper triangular component only - if sparse.tril(P, -1).data.size > 0: - P = sparse.triu(P, format="csc") - if not P.has_sorted_indices: - P.sort_indices() - Pdata, Pindices, Pcolptr = P.data, P.indices, P.indptr - - # Which scs are we using (scs_direct, scs_indirect, ...) - _scs = _select_scs_module(self._settings) - - # Initialize solver - self._solver = _scs.SCS( - (m, n), - Adata, - Aindices, - Acolptr, - Pdata, - Pindices, - Pcolptr, - b, - c, - cone, - **self._settings - ) - - def solve(self, warm_start=True, x=None, y=None, s=None): - """Solve the optimization problem. - - @param warm_start Whether to warm-start. By default the solution of - the previous problem is used as the warm-start. The - warm-start can be overriden to another value by - passing `x`, `y`, `s` args. - @param x Primal warm-start override. - @param y Dual warm-start override. - @param s Slack warm-start override. - - @return dictionary with solution with keys: - 'x' - primal solution - 's' - primal slack solution - 'y' - dual solution - 'info' - information dictionary (see docs) - """ - return self._solver.solve(warm_start, x, y, s) - - def update(self, b=None, c=None): - """Update the `b` vector, `c` vector, or both, before another solve. - - After a solve we can reuse the SCS workspace in another solve if the - only problem data that has changed are the `b` and `c` vectors. - - @param b New `b` vector. - @param c New `c` vector. - - """ - self._solver.update(b, c) + + def __init__(self, data, cone, **settings): + """Initialize the SCS solver. + + @param data Dictionary containing keys `P`, `A`, `b`, `c`. + @param cone Dictionary containing cone information. + @param settings Settings as kwargs, see docs. + """ + self._settings = settings + if not data or not cone: + raise ValueError("Missing data or cone information") + + if "b" not in data or "c" not in data: + raise ValueError("Missing one of b, c from data dictionary") + if "A" not in data: + raise ValueError("Missing A from data dictionary") + + A = data["A"] + b = data["b"] + c = data["c"] + + if A is None or b is None or c is None: + raise ValueError("Incomplete data specification") + + if not sparse.issparse(A): + raise TypeError("A is required to be a sparse matrix") + if not A.format == "csc": + warn( + "Converting A to a CSC (compressed sparse column) matrix;" + " may take a while." + ) + A = A.tocsc() + + if sparse.issparse(b): + b = b.todense() + + if sparse.issparse(c): + c = c.todense() + + m = len(b) + n = len(c) + + if not A.has_sorted_indices: + A.sort_indices() + Adata, Aindices, Acolptr = A.data, A.indices, A.indptr + if A.shape != (m, n): + raise ValueError("A shape not compatible with b,c") + + Pdata, Pindices, Pcolptr = None, None, None + if "P" in data: + P = data["P"] + if P is not None: + if not sparse.issparse(P): + raise TypeError("P is required to be a sparse matrix") + if P.shape != (n, n): + raise ValueError("P shape not compatible with A,b,c") + if not P.format == "csc": + warn( + "Converting P to a CSC (compressed sparse column) " + "matrix; may take a while." + ) + P = P.tocsc() + # extract upper triangular component only + if sparse.tril(P, -1).data.size > 0: + P = sparse.triu(P, format="csc") + if not P.has_sorted_indices: + P.sort_indices() + Pdata, Pindices, Pcolptr = P.data, P.indices, P.indptr + + # Which scs are we using (scs_direct, scs_indirect, ...) + _scs = _select_scs_module(self._settings) + + # Initialize solver + self._solver = _scs.SCS( + (m, n), + Adata, + Aindices, + Acolptr, + Pdata, + Pindices, + Pcolptr, + b, + c, + cone, + **self._settings, + ) + + def solve(self, warm_start=True, x=None, y=None, s=None): + """Solve the optimization problem. + + @param warm_start Whether to warm-start. By default the solution of + the previous problem is used as the warm-start. The + warm-start can be overriden to another value by + passing `x`, `y`, `s` args. + @param x Primal warm-start override. + @param y Dual warm-start override. + @param s Slack warm-start override. + + @return dictionary with solution with keys: + 'x' - primal solution + 's' - primal slack solution + 'y' - dual solution + 'info' - information dictionary (see docs) + """ + return self._solver.solve(warm_start, x, y, s) + + def update(self, b=None, c=None): + """Update the `b` vector, `c` vector, or both, before another solve. + + After a solve we can reuse the SCS workspace in another solve if the + only problem data that has changed are the `b` and `c` vectors. + + @param b New `b` vector. + @param c New `c` vector. + """ + self._solver.update(b, c) # Backwards compatible helper function that simply calls the main API. def solve(data, cone, **settings): - solver = SCS(data, cone, **settings) - - # Hack out the warm start data from old API - x = y = s = None - if "x" in data: - x = data["x"] - if "y" in data: - y = data["y"] - if "s" in data: - s = data["s"] - - return solver.solve(warm_start=True, x=x, y=y, s=s) + solver = SCS(data, cone, **settings) + + # Hack out the warm start data from old API + x = y = s = None + if "x" in data: + x = data["x"] + if "y" in data: + y = data["y"] + if "s" in data: + s = data["s"] + + return solver.solve(warm_start=True, x=x, y=y, s=s) From 07d9c5846c84b9a4a6a4be38e7bd24460c5aefe0 Mon Sep 17 00:00:00 2001 From: Brendan O'Donoghue Date: Tue, 18 Nov 2025 21:54:55 +0000 Subject: [PATCH 2/4] use gpu rather than cudss arg --- scs/py/__init__.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/scs/py/__init__.py b/scs/py/__init__.py index 0f0d1e1..f1a6ed2 100644 --- a/scs/py/__init__.py +++ b/scs/py/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -from warnings import warn from scipy import sparse from scs import _scs_direct +import warnings __version__ = _scs_direct.version() __sizeof_int__ = _scs_direct.sizeof_int() @@ -28,34 +28,26 @@ def _select_scs_module(stgs): if stgs.pop("gpu", False): # False by default - if not stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): - raise NotImplementedError( - "For the GPU direct solver, pass `use_indirect=False cudss=True`." - ) - from scs import _scs_gpu + if stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): + from scs import _scs_gpu # pylint: disable=g-import-not-at-top - return _scs_gpu + return _scs_gpu + else: + from scs import _scs_cudss # pylint: disable=g-import-not-at-top + + return _scs_cudss if stgs.pop("mkl", False): # False by default if stgs.pop("use_indirect", False): raise NotImplementedError( "MKL indirect solver not yet available, pass `use_indirect=False`." ) - from scs import _scs_mkl + from scs import _scs_mkl # pylint: disable=g-import-not-at-top return _scs_mkl - if stgs.pop("cudss", False): # False by default - if stgs.pop("use_indirect", False): - raise NotImplementedError( - "cuDSS is a direct solver, pass `use_indirect=False`." - ) - from scs import _scs_cudss - - return _scs_cudss - if stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): - from scs import _scs_indirect + from scs import _scs_indirect # pylint: disable=g-import-not-at-top return _scs_indirect @@ -90,7 +82,7 @@ def __init__(self, data, cone, **settings): if not sparse.issparse(A): raise TypeError("A is required to be a sparse matrix") if not A.format == "csc": - warn( + warnings.warn( "Converting A to a CSC (compressed sparse column) matrix;" " may take a while." ) @@ -120,7 +112,7 @@ def __init__(self, data, cone, **settings): if P.shape != (n, n): raise ValueError("P shape not compatible with A,b,c") if not P.format == "csc": - warn( + warnings.warn( "Converting P to a CSC (compressed sparse column) " "matrix; may take a while." ) @@ -155,7 +147,7 @@ def solve(self, warm_start=True, x=None, y=None, s=None): @param warm_start Whether to warm-start. By default the solution of the previous problem is used as the warm-start. The - warm-start can be overriden to another value by + warm-start can be overridden to another value by passing `x`, `y`, `s` args. @param x Primal warm-start override. @param y Dual warm-start override. From bed11f616a29bfd7ebf6c53fcb7e55fdaf3506ac Mon Sep 17 00:00:00 2001 From: Brendan O'Donoghue Date: Tue, 18 Nov 2025 22:05:45 +0000 Subject: [PATCH 3/4] add error when using old flag --- scs/py/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scs/py/__init__.py b/scs/py/__init__.py index f1a6ed2..2894250 100644 --- a/scs/py/__init__.py +++ b/scs/py/__init__.py @@ -27,6 +27,9 @@ # Choose which SCS to import based on settings. def _select_scs_module(stgs): + if stgs.pop("cudss", False): + raise ValueError("To use cuDSS set gpu=True and use_indirect=False.") + if stgs.pop("gpu", False): # False by default if stgs.pop("use_indirect", _USE_INDIRECT_DEFAULT): from scs import _scs_gpu # pylint: disable=g-import-not-at-top From 268440728880e3fce5267cd7e08c6e54d4871c89 Mon Sep 17 00:00:00 2001 From: Brendan O'Donoghue Date: Tue, 18 Nov 2025 22:19:38 +0000 Subject: [PATCH 4/4] update scs source --- scs_source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scs_source b/scs_source index c829717..fa34daf 160000 --- a/scs_source +++ b/scs_source @@ -1 +1 @@ -Subproject commit c8297172633bcb3a10d4781a19d4769ce5282d29 +Subproject commit fa34dafa9f01f1ee0f953c16a57c6eea4b70014e