Repository: cvxpy/cvxpy (language: cpp)

Issue:
Seg fault on simple DPP optimization
**Describe the bug**
A simple DPP optimization produces a seg fault when pre multiplying by a constant.

**To Reproduce**
```
import cvxpy as cp
import numpy as np

a = cp.Variable((1, 1))
b = cp.Variable((1,))

param = cp.Parameter(1)

constraints = []
objective = cp.Minimize(
    # cp.sum_squares(a) - b @ (2 * param) # no seg fault
    cp.sum_squares(a) - 2 * b @ param # seg fault
)

problem = cp.Problem(objective, constraints)

param.value = np.array([1])
problem.solve(verbose=True)
```

**Expected behavior**
The code should not produce a seg fault.

**Output**
```text
===============================================================================
                                     CVXPY                                     
                                    v1.1.17                                    
===============================================================================
(CVXPY) Nov 17 01:34:57 PM: Your problem has 2 variables, 0 constraints, and 1 parameters.
(CVXPY) Nov 17 01:34:57 PM: It is compliant with the following grammars: DCP, DQCP
(CVXPY) Nov 17 01:34:57 PM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.
-------------------------------------------------------------------------------
                                  Compilation                                  
-------------------------------------------------------------------------------
(CVXPY) Nov 17 01:34:57 PM: Compiling problem (target solver=OSQP).
(CVXPY) Nov 17 01:34:57 PM: Reduction chain: CvxAttr2Constr -> Qp2SymbolicQp -> QpMatrixStuffing -> OSQP
(CVXPY) Nov 17 01:34:57 PM: Applying reduction CvxAttr2Constr
(CVXPY) Nov 17 01:34:57 PM: Applying reduction Qp2SymbolicQp
(CVXPY) Nov 17 01:34:57 PM: Applying reduction QpMatrixStuffing
Segmentation fault (core dumped)
```

**Version**
 - OS: Ubuntu 18.04
 - CVXPY Version: 1.1.17

**Additional context**
Not sure why the ordering of the multiplication matters.
Seg fault on simple DPP optimization
**Describe the bug**
A simple DPP optimization produces a seg fault when pre multiplying by a constant.

**To Reproduce**
```
import cvxpy as cp
import numpy as np

a = cp.Variable((1, 1))
b = cp.Variable((1,))

param = cp.Parameter(1)

constraints = []
objective = cp.Minimize(
    # cp.sum_squares(a) - b @ (2 * param) # no seg fault
    cp.sum_squares(a) - 2 * b @ param # seg fault
)

problem = cp.Problem(objective, constraints)

param.value = np.array([1])
problem.solve(verbose=True)
```

**Expected behavior**
The code should not produce a seg fault.

**Output**
```text
===============================================================================
                                     CVXPY                                     
                                    v1.1.17                                    
===============================================================================
(CVXPY) Nov 17 01:34:57 PM: Your problem has 2 variables, 0 constraints, and 1 parameters.
(CVXPY) Nov 17 01:34:57 PM: It is compliant with the following grammars: DCP, DQCP
(CVXPY) Nov 17 01:34:57 PM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.
-------------------------------------------------------------------------------
                                  Compilation                                  
-------------------------------------------------------------------------------
(CVXPY) Nov 17 01:34:57 PM: Compiling problem (target solver=OSQP).
(CVXPY) Nov 17 01:34:57 PM: Reduction chain: CvxAttr2Constr -> Qp2SymbolicQp -> QpMatrixStuffing -> OSQP
(CVXPY) Nov 17 01:34:57 PM: Applying reduction CvxAttr2Constr
(CVXPY) Nov 17 01:34:57 PM: Applying reduction Qp2SymbolicQp
(CVXPY) Nov 17 01:34:57 PM: Applying reduction QpMatrixStuffing
Segmentation fault (core dumped)
```

**Version**
 - OS: Ubuntu 18.04
 - CVXPY Version: 1.1.17

**Additional context**
Not sure why the ordering of the multiplication matters.


Discussion / hints:
I looked into this, and the issue only comes up when ``a`` and ``b`` are scalars. If you increase the dimension of ``b`` the problem goes away. So there must be a bug somewhere around special casing of scalars.
Chiming in. NumPy behaves as follows
```
>>> np.array([[1]]) @ np.array(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)

>>> np.array([1]) @ np.array(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)
```
Of course, the problem should be fixed, and cvxpy makes no claims that we mimic numpy, but it's worth noting.

I think the issue might be that we make assumptions that the size of some expressions is a nonempty tuple. The code below would produce a segfault if it was implemented directly in C/C++.
```
>>> np.array(1).shape
()

>>> np.array(1).shape[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
```
I looked into this, and the issue only comes up when ``a`` and ``b`` are scalars. If you increase the dimension of ``b`` the problem goes away. So there must be a bug somewhere around special casing of scalars.
Chiming in. NumPy behaves as follows
```
>>> np.array([[1]]) @ np.array(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)

>>> np.array([1]) @ np.array(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)
```
Of course, the problem should be fixed, and cvxpy makes no claims that we mimic numpy, but it's worth noting.

I think the issue might be that we make assumptions that the size of some expressions is a nonempty tuple. The code below would produce a segfault if it was implemented directly in C/C++.
```
>>> np.array(1).shape
()

>>> np.array(1).shape[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
```

Fix patch:
diff --git a/cvxpy/lin_ops/lin_op.py b/cvxpy/lin_ops/lin_op.py
index 4903a0a5da..f29f48aad1 100644
--- a/cvxpy/lin_ops/lin_op.py
+++ b/cvxpy/lin_ops/lin_op.py
@@ -28,6 +28,9 @@ def __init__(self, type, shape: Tuple[int, ...], args, data) -> None:
         self.args = args
         self.data = data
 
+    def __repr__(self) -> str:
+        return f"LinOp({self.type}, {self.shape})"
+
 
 # The types of linear operators.
 
diff --git a/cvxpy/lin_ops/lin_utils.py b/cvxpy/lin_ops/lin_utils.py
index 7d2b5d3bfe..dda4fe8ac1 100644
--- a/cvxpy/lin_ops/lin_utils.py
+++ b/cvxpy/lin_ops/lin_utils.py
@@ -290,7 +290,8 @@ def multiply(lh_op, rh_op):
     LinOp
         A linear operator representing the product.
     """
-    return lo.LinOp(lo.MUL_ELEM, lh_op.shape, [rh_op], lh_op)
+    shape = max(lh_op.shape, rh_op.shape)
+    return lo.LinOp(lo.MUL_ELEM, shape, [rh_op], lh_op)
 
 
 def kron(lh_op, rh_op, shape: Tuple[int, ...]):


Test patch:
diff --git a/cvxpy/tests/test_problem.py b/cvxpy/tests/test_problem.py
index b8e3cffbda..36a214f6d5 100644
--- a/cvxpy/tests/test_problem.py
+++ b/cvxpy/tests/test_problem.py
@@ -1972,3 +1972,18 @@ def test_huber_scs(self) -> None:
         prob = cp.Problem(cp.Minimize(cp.sum(cp.huber(A @ x - b))))
 
         prob.solve(solver=cp.SCS)
+
+    def test_rmul_param(self) -> None:
+        """Test a complex rmul expression with a parameter.
+           See issue #1555.
+        """
+        b = cp.Variable((1,))
+        param = cp.Parameter(1)
+
+        constraints = []
+        objective = cp.Minimize((2 * b) @ param)
+        prob = cp.Problem(objective, constraints)
+
+        param.value = np.array([1])
+        prob.solve()
+        assert prob.value == -np.inf