Skip to content

fix: raise ValueError when cp.Parameter receives a sparse value (#2890)#3280

Open
Vivaan-Atharva wants to merge 4 commits into
cvxpy:masterfrom
Vivaan-Atharva:fix/parameter-rejects-sparse-value
Open

fix: raise ValueError when cp.Parameter receives a sparse value (#2890)#3280
Vivaan-Atharva wants to merge 4 commits into
cvxpy:masterfrom
Vivaan-Atharva:fix/parameter-rejects-sparse-value

Conversation

@Vivaan-Atharva
Copy link
Copy Markdown
Contributor

Fixes #2890

Problem

cp.Parameter silently accepted a scipy sparse matrix as its value,
storing it without error. The failure only surfaced later during
canonicalization with a confusing traceback.

Reproduction

import cvxpy as cp
x = cp.Variable((2, 2), value=[[1, 0], [0, 1]])
grad = (x + 1).grad[x]  # sparse matrix
p = cp.Parameter((4, 4), value=grad)  # should error here, didn't

Fix

Override the value setter in Parameter to check scipy.sparse.issparse()
before delegating to Leaf.value.fset. Catches the error at assignment time
with a clear message pointing to cp.Constant as the alternative.

Tests

  • Rejects CSC, CSR, COO sparse arrays at construction time
  • Rejects sparse via .value = setter after construction
  • Reproduces the exact case from the issue (grad sparse matrix)
  • Verifies dense, None, scalar still accepted

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 5, 2026

Benchmarks that have improved:

   before           after         ratio
 [f61ac402]       [6788a1e6]
  •     3.34±0s          2.95±0s     0.88  quantum_hilbert_matrix.QuantumHilbertMatrix.time_compile_problem
    

Benchmarks that have stayed the same:

   before           after         ratio
 [f61ac402]       [6788a1e6]
      1.01±0s          1.04±0s     1.03  finance.FactorCovarianceModel.time_compile_problem
      277±0ms          282±0ms     1.02  matrix_stuffing.ParamSmallMatrixStuffing.time_compile_problem
      1.39±0s          1.41±0s     1.02  matrix_stuffing.ParamConeMatrixStuffing.time_compile_problem
      4.56±0s          4.60±0s     1.01  svm_l1_regularization.SVMWithL1Regularization.time_compile_problem
      227±0ms          229±0ms     1.01  gini_portfolio.Murray.time_compile_problem
      12.6±0s          12.7±0s     1.01  finance.CVaRBenchmark.time_compile_problem
      315±0ms          317±0ms     1.01  gini_portfolio.Yitzhaki.time_compile_problem
      526±0ms          530±0ms     1.01  semidefinite_programming.SemidefiniteProgramming.time_compile_problem
      726±0ms          731±0ms     1.01  matrix_stuffing.ConeMatrixStuffingBench.time_compile_problem
      134±0ms          135±0ms     1.01  high_dim_convex_plasticity.ConvexPlasticity.time_compile_problem
      946±0ms          950±0ms     1.00  gini_portfolio.Cajas.time_compile_problem
      3.95±0s          3.96±0s     1.00  huber_regression.HuberRegression.time_compile_problem
     14.2±0ms         14.2±0ms     1.00  simple_LP_benchmarks.SimpleFullyParametrizedLPBenchmark.time_compile_problem
     42.7±0ms         42.6±0ms     1.00  matrix_stuffing.SmallMatrixStuffing.time_compile_problem
      21.0±0s          20.9±0s     0.99  sdp_segfault_1132_benchmark.SDPSegfault1132Benchmark.time_compile_problem
      1.81±0s          1.79±0s     0.99  simple_QP_benchmarks.UnconstrainedQP.time_compile_problem
      239±0ms          236±0ms     0.99  simple_QP_benchmarks.SimpleQPBenchmark.time_compile_problem
      5.29±0s          5.24±0s     0.99  optimal_advertising.OptimalAdvertising.time_compile_problem
      906±0ms          896±0ms     0.99  simple_LP_benchmarks.SimpleScalarParametrizedLPBenchmark.time_compile_problem
      10.4±0s          10.3±0s     0.98  simple_LP_benchmarks.SimpleLPBenchmark.time_compile_problem
      287±0ms          279±0ms     0.97  slow_pruning_1668_benchmark.SlowPruningBenchmark.time_compile_problem
      1.64±0s          1.60±0s     0.97  tv_inpainting.TvInpainting.time_compile_problem
     15.5±0ms         14.9±0ms     0.96  simple_QP_benchmarks.ParametrizedQPBenchmark.time_compile_problem
      783±0ms          745±0ms     0.95  simple_QP_benchmarks.LeastSquares.time_compile_problem

from __future__ import annotations

import cvxpy.lin_ops.lin_utils as lu
import scipy.sparse as sp
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please install the pre-commit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Donee, installed pre commit and ran it, ruff fixed the import ordering.

def value(self, val) -> None:
if sp.issparse(val):
raise ValueError(
"Setting a cp.Parameter value to a sparse matrix or array "
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message is not quite right. We have a distinction now between .value and .value_sparse. I think now that Scipy has ND sparse arrays we can pass in sparse inputs via .value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the message, it no longer claims Parameters are dense only. It now points to the sparsity attribute + .value_sparse for sparse parameters, and cp.Constant() for the constant use case.

@Vivaan-Atharva Vivaan-Atharva force-pushed the fix/parameter-rejects-sparse-value branch from ec120d0 to 7644bb2 Compare April 14, 2026 13:17
"To use a sparse constant, use cp.Constant() instead."
)
Leaf.value.fset(self, val)
Leaf.value.fset(self, val) # type: ignore[misc]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally avoid just masking pyright errors; can you explain why we can't fix the underlying issue? I think the problem is we don't have an annotation on val.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah got it, added an assert to narrow the type instead

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a new file? If we keep it as a new file it needs the copyright header etc.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think this is way too many tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a new file? If we keep it as a new file it needs the copyright header etc.

moved everything into test_parameters_failures, deleted the new file

p.value = None
assert p.value is None

def test_accepts_scalar(self):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely we already have this test somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah removed those, they're already in test_parameters_successes


@staticmethod
def _make_sparse_csc(n: int = 4) -> sp.csc_array:
return sp.eye(n, format="csc")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return sp.eye(n, format="csc")
return sp.eye_array(n, format="csc")


def test_rejects_sparse_csc_at_construction(self):
with pytest.raises(ValueError, match="sparse"):
cp.Parameter((4, 4), value=self._make_sparse_csc(4))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using a helper method here but not elsewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the helper, just inlined sp.eye_array directly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Naive usage of sparse cp.Parameter can lead to uncaught errors

3 participants