I am new to CVXPY and I am not an optimization expert, so please be patient with me. I am trying to solve a very standard problem, i.e., the (ordinary) least squares problem, in CVXPY using the solver ECOS. I am aware that there are very specialized algorithms to solve this class of problems, but I chose it as a learning example to familiarize myself with CVXPY/ECOS due to its conceptual simplicity and widespread usage.
The problem can be formulated as
where, as usual, y is a vector of dimension n × 1, X is a n × p design matrix, Xj represents the vector corresponding to column j of the matrix X, β is the p × 1 parameter vector, and βj is the jth parameter.
Clearly, this is a quadratic minimization problem that the ECOS solver (being a conic optimizer) should be able to solve, however, I observed an unexpected behavior, namely that the solver's ability to find a solution depends on the magnitude of the input data.
Specifically, as can be seen in my code snippet below, I generated a mock random vector y and matrix X * and tried to solve the least squares problem two times: one with the variables X and y as is and a second time with the variables X and y scaled (y2 = y × 10,000,000 and the columns 2 to 14 of X multiplied by 1,000). To my surprise, ECOS found a solution to the first problem (unscaled data), but failed to do so and called the second problem (scaled data) infeasible.
Why does this happen? Is there a limitation of the variable scale in the ECOS solver or in the CVXPY interface?
Perhaps the CVXPY "atom" sum_squares
returns a value that is too large when the data is scaled, making the solver conclude that the problem is infeasible. If that is the case, how should I scale X and y, and what should I do to recover the "correct" solution (i.e., in the same scale as the original data)?
import cvxpy as cp
import numpy as np
m = 133
n = 17
X = np.empty((m, n))
X[:, 0] = 1
X[:, 1:n-3] = np.random.rand(m, n-4)
X[:, n-3:] = np.random.randint(low=0, high=2, size=(m, 3))
X_1 = X.copy()
X_2 = X.copy()
X_2[:, 1:n-3] = X_2[:, 1:n-3] * 1_000
y = np.random.rand(m)
y_1 = y.copy()
y_2 = y.copy() * 10_000_000
params = cp.Variable(n)
objective_1 = cp.Minimize(cp.sum_squares(y_1 - X_1 @ params))
objective_2 = cp.Minimize(cp.sum_squares(y_2 - X_2 @ params))
problem_1 = cp.Problem(objective_1)
problem_2 = cp.Problem(objective_2)
problem_1.solve(solver=cp.ECOS, verbose=True)
print("optimal value with ECOS:", problem_1.value)
print("solution", params.value)
problem_2.solve(solver=cp.ECOS, verbose=True)
print("optimal value with ECOS:", problem_1.value)
print("solution", params.value)
Below is the CVXPY/ECOS output of the two problems for reference.
problem_1.solve(solver=cp.ECOS, verbose=True)
===============================================================================
CVXPY
v1.3.1
===============================================================================
(CVXPY) Mar 24 06:33:24 PM: Your problem has 17 variables, 0 constraints, and 0 parameters.
(CVXPY) Mar 24 06:33:24 PM: It is compliant with the following grammars: DCP, DQCP
(CVXPY) Mar 24 06:33:24 PM: (If you need to solve this problem multiple times, but with different data, consider using parameters.)
(CVXPY) Mar 24 06:33:24 PM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.
-------------------------------------------------------------------------------
Compilation
-------------------------------------------------------------------------------
(CVXPY) Mar 24 06:33:24 PM: Compiling problem (target solver=ECOS).
(CVXPY) Mar 24 06:33:24 PM: Reduction chain: Dcp2Cone -> CvxAttr2Constr -> ConeMatrixStuffing -> ECOS
(CVXPY) Mar 24 06:33:24 PM: Applying reduction Dcp2Cone
(CVXPY) Mar 24 06:33:24 PM: Applying reduction CvxAttr2Constr
(CVXPY) Mar 24 06:33:24 PM: Applying reduction ConeMatrixStuffing
(CVXPY) Mar 24 06:33:24 PM: Applying reduction ECOS
(CVXPY) Mar 24 06:33:24 PM: Finished problem compilation (took 1.675e-02 seconds).
-------------------------------------------------------------------------------
Numerical solver
-------------------------------------------------------------------------------
(CVXPY) Mar 24 06:33:24 PM: Invoking solver ECOS to obtain a solution.
ECOS 2.0.10 - (C) embotech GmbH, Zurich Switzerland, 2012-15. Web: www.embotech.com/ECOS
It pcost dcost gap pres dres k/t mu step sigma IR | BT
0 +0.000e+00 -5.259e-03 +1e+02 5e-01 5e-04 1e+00 5e+01 --- --- 1 1 - | - -
1 +2.044e-02 +2.501e-02 +4e+00 3e-02 2e-05 4e-02 2e+00 0.9617 1e-04 1 2 2 | 0 0
2 +1.874e+00 +2.913e+00 +1e+00 2e-02 1e-05 1e+00 7e-01 0.9890 4e-01 2 2 2 | 0 0
3 +6.400e+00 +8.939e+00 +2e-01 1e-02 7e-06 3e+00 1e-01 0.9862 2e-01 3 3 3 | 0 0
4 +8.877e+00 +9.436e+00 +2e-02 2e-03 1e-06 6e-01 2e-02 0.8759 3e-03 3 2 2 | 0 0
5 +9.473e+00 +1.059e+01 +1e-02 2e-03 1e-06 1e+00 9e-03 0.7220 4e-01 3 3 3 | 0 0
6 +1.061e+01 +1.062e+01 +3e-04 5e-05 2e-08 1e-02 2e-04 0.9823 3e-03 3 2 2 | 0 0
7 +1.063e+01 +1.064e+01 +4e-05 5e-06 3e-09 1e-03 2e-05 0.8899 4e-03 3 3 3 | 0 0
8 +1.064e+01 +1.064e+01 +1e-06 7e-07 1e-10 1e-04 1e-06 0.9836 4e-02 4 3 3 | 0 0
9 +1.064e+01 +1.064e+01 +5e-08 1e-08 5e-12 4e-06 4e-08 0.9644 1e-04 3 2 2 | 0 0
10 +1.064e+01 +1.064e+01 +3e-09 2e-08 3e-14 2e-07 2e-09 0.9593 1e-02 4 2 2 | 0 0
11 +1.064e+01 +1.064e+01 +4e-11 2e-09 3e-14 3e-09 3e-11 0.9866 4e-04 4 2 2 | 0 0
OPTIMAL (within feastol=2.4e-09, reltol=3.3e-12, abstol=3.5e-11).
Runtime: 0.007806 seconds.
-------------------------------------------------------------------------------
Summary
-------------------------------------------------------------------------------
(CVXPY) Mar 24 06:33:24 PM: Problem status: optimal
(CVXPY) Mar 24 06:33:24 PM: Optimal value: 1.064e+01
(CVXPY) Mar 24 06:33:24 PM: Compilation took 1.675e-02 seconds
(CVXPY) Mar 24 06:33:24 PM: Solver (including time spent in interface) took 8.717e-03 seconds
10.640240221833896
problem_2.solve(solver=cp.ECOS, verbose=True)
===============================================================================
CVXPY
v1.3.1
===============================================================================
(CVXPY) Mar 24 06:34:04 PM: Your problem has 17 variables, 0 constraints, and 0 parameters.
(CVXPY) Mar 24 06:34:04 PM: It is compliant with the following grammars: DCP, DQCP
(CVXPY) Mar 24 06:34:04 PM: (If you need to solve this problem multiple times, but with different data, consider using parameters.)
(CVXPY) Mar 24 06:34:04 PM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.
-------------------------------------------------------------------------------
Compilation
-------------------------------------------------------------------------------
(CVXPY) Mar 24 06:34:04 PM: Compiling problem (target solver=ECOS).
(CVXPY) Mar 24 06:34:04 PM: Reduction chain: Dcp2Cone -> CvxAttr2Constr -> ConeMatrixStuffing -> ECOS
(CVXPY) Mar 24 06:34:04 PM: Applying reduction Dcp2Cone
(CVXPY) Mar 24 06:34:04 PM: Applying reduction CvxAttr2Constr
(CVXPY) Mar 24 06:34:04 PM: Applying reduction ConeMatrixStuffing
(CVXPY) Mar 24 06:34:04 PM: Applying reduction ECOS
(CVXPY) Mar 24 06:34:04 PM: Finished problem compilation (took 6.039e-03 seconds).
-------------------------------------------------------------------------------
Numerical solver
-------------------------------------------------------------------------------
(CVXPY) Mar 24 06:34:04 PM: Invoking solver ECOS to obtain a solution.
ECOS 2.0.10 - (C) embotech GmbH, Zurich Switzerland, 2012-15. Web: www.embotech.com/ECOS
It pcost dcost gap pres dres k/t mu step sigma IR | BT
0 +0.000e+00 -1.789e-04 +3e+07 1e-01 7e-06 1e+00 2e+07 --- --- 1 1 - | - -
1 -4.612e+07 -4.427e+07 +3e+07 3e+01 7e-04 2e+06 2e+07 0.0011 1e+00 1 1 1 | 0 0
2 -4.510e+07 -2.988e+07 +1e+06 1e+00 2e-05 2e+07 5e+05 0.9704 1e-04 2 1 1 | 0 0
3 +6.561e+08 +8.610e+08 +1e+04 2e-01 1e-04 2e+08 5e+03 0.9890 1e-04 2 2 2 | 0 0
4 +5.427e+09 +6.944e+09 +1e+02 2e-02 5e-05 2e+09 6e+01 0.9890 1e-04 2 1 1 | 0 0
5 +9.734e+09 +1.050e+10 +4e+00 2e-03 3e-05 8e+08 2e+00 0.9582 1e-04 2 2 2 | 0 0
6 +1.566e+10 +1.762e+10 +5e-01 1e-03 1e-03 2e+09 4e-01 0.9890 1e-01 1 2 2 | 0 0
7 +2.388e+10 +4.886e+10 +2e-01 3e-03 2e-02 2e+10 1e-01 0.9890 2e-01 3 2 2 | 0 0
8 +6.709e+10 +8.528e+10 +6e-03 3e-04 4e-04 2e+10 5e-03 0.9865 3e-02 2 2 3 | 0 0
9 +1.056e+11 +1.318e+11 +5e-04 1e-04 7e-03 3e+10 7e-04 0.9410 6e-02 3 2 2 | 0 0
PRIMAL INFEASIBLE (within feastol=7.6e-09).
Runtime: 0.005062 seconds.
-------------------------------------------------------------------------------
Summary
-------------------------------------------------------------------------------
(CVXPY) Mar 24 06:34:04 PM: Problem status: infeasible
(CVXPY) Mar 24 06:34:04 PM: Optimal value: inf
(CVXPY) Mar 24 06:34:04 PM: Compilation took 6.039e-03 seconds
(CVXPY) Mar 24 06:34:04 PM: Solver (including time spent in interface) took 4.988e-04 seconds
inf
The script was executed on a Windows 10 Pro version 22H2 machine with a 11th Gen Intel(R) Core(TM) i5 Intel processor running Python 3.10.9, CVXPY 1.3.1, ECOS 2.0.11, and NumPy 1.23.5.
* Note that the first column of X has all entries equal to 1 to mimic the intercept of an OLS design matrix 1 and the last three columns are binary to mimic dummy variables (due to the random nature of the mock data, we cannot exclude multicollinearity but the chance of it occurring is small, I always checked X for that before attempting to solve the problem, and it should not matter in this context).