SeDuMi "coefficient matrix not full row rank" for data-driven LQR SDP

Hello CVX community,

I am implementing the data-driven LQR formulation from De Persis & Tesi (2020) “Formulas for Data-Driven Control: Stabilization, Optimality, and Robustness” (IEEE TAC) and am getting the following warning from SeDuMi, after which the solve returns “Failed”.

“The coefficient matrix is not full row rank, numerical problems may occur. SeDuMi 1.3.7 by AdvOL, 2005-2008 and Jos F. Sturm, 1998-2003.”

Below is a minimum reproducible example snippet of code.

clear
clc

rng(42) % Fixed seed, disable to reproduce stoachastic failure

%% System Definition
A = [-0.7833,    0.0668,    0.0004,         0;
      0.3752,   -0.0668,         0,         0;
      0.0196,         0,   -0.0004,         0;
      0.4560,         0,         0,   -0.4560];
B = [0.2342; 0; 0; 0];
C50 = 6.33;
gamma = 2.24;
E_0 = 98.8;
E_max = 94.1;

% Additional system to test with
% A = [-0.7943,    0.0665,    0.0004,         0;
%       0.3977,   -0.0665,         0,         0;
%       0.0196,         0,   -0.0004,         0;
%       0.4560,         0,         0,   -0.4560];
% B = [0.2342; 0; 0; 0];
%C50 = 6.76;
%gamma = 4.29;
%E_0 = 98.6;
%E_max = 86.0;

% Find equilibrium point and collect data about equilibrium
[x_eq, u_eq] = calc_eq(A, B, 50, C50, gamma, E_0, E_max);

%% Persistently Exciting Data Collection
% Minimum samples required for PE: N_data >= m + n = 5
dt_sim = 0.01;  % Time step for simulation
dt_data = 0.5;  % Data collection rate
N_data = 6;    % Minimum number (+1 for equilibrium states)

f_ode = @(x,u) A*x + B*u;

x_data(:,1) = x_eq;
x_current = x_data(:,1);
u_data(:,1) = u_eq;
delta_u = 2;
for l = 1:N_data
  du = delta_u * randn();  % Excitation signal
  u_data(l+1) = u_eq + du;
  for i = 1:dt_data/dt_sim
    x_current = rk4(f_ode, x_current, u_data(l), dt_sim);
  end
  x_data(:,l+1) = x_current;
end
        
%% Solve for data driven LQR
% Theory: De Persis & Tesi (2020) "Formulas for Data-Driven Control"
Q = diag([10,1,1,500]);
r = 1;
K_dlqr = findK_data(x_data(:,2:end),u_data(2:end),Q,r);


%% Functions %%
function [x_eq, u_eq] = calc_eq(A, B, BIS_target, C50, gamma, E0, Emax)
    % Calculate equilibrium effect-site concentration
    Ce_eq = C50 * ((E0 - BIS_target) / (Emax - E0 + BIS_target))^(1/gamma);
    
    % Calculate steady-state gain from u to x
    tmp = -A\B;  
    u_eq = Ce_eq/tmp(4);  % Calculate equilibrium infusion rate
    x_eq = tmp*u_eq;  % Calculate full equilibrium state
end


function K_lqr = findK_data(X,U,Qx,R)
    n = size(X,1);
    m = size(U,1);
    T = size(X,2) - 1;
    L = 1;

    R_sqrt = sqrtm(R);  % Tuning parameters
     
    X0 = X(:,1:end-1);
    X1 = X(:,2:end);
    U0 = U(:,1:end-1);
    
    % Enforce full rank condition
    UX = cat(1,U0,X0);
    cond(UX)
    if rank(UX) ~= (m+n)
        error("Signal is NOT persistently exciting")
    end

    % Solve for LQR optimal gain. NOTE: Numeric instability is quite often seen here 
    cvx_clear
    cvx_begin sdp 
        cvx_precision default
        variable Q_lqr(T/L, n)
        variable Y(m, m) symmetric
        variable P_lqr(n, n) symmetric
        minimize(trace(Qx*X0*Q_lqr) + trace(Y))
    
        subject to
            % Schur complement for cost
            [Y, R_sqrt*U0*Q_lqr; 
             Q_lqr'*U0'*R_sqrt', P_lqr] >= 0;

            % Schur complement for Lyapunov stability
            [P_lqr - eye(n), X1*Q_lqr; 
             Q_lqr'*X1', P_lqr] >= 0;

            P_lqr == X0*Q_lqr;
    cvx_end

    % Check CVX status
    if ~strcmp(cvx_status, 'Solved')
        warning('CVX optimization failed with status: %s', cvx_status);
    end

    K_lqr = U0 * Q_lqr / (X0*Q_lqr);  % Recover controller gain
end

function x_next = rk4(f,x,u,dt)
    % Fourth-order Runge-Kutta
    k1 = f(x,u);
    k2 = f((x + 0.5*dt*k1), u);
    k3 = f((x + 0.5*dt*k2), u);
    k4 = f((x + dt*k3), u);
    
    x_next = x + (dt/6) * (k1 + 2*k2 + 2*k3 + k4);
end

I believe the implementation follows the formulation correctly, but I acknowledge there may still be subtle issues in how the SDP is posed. This is one possible cause I have been considering:

  1. The system is stiff (eigenvalues span -0.0004 to -0.81), giving cond([U0; X0]) ≈ 1e7-1e10 even when full rank. Collecting more data would help numerically, but this formulation is appealing precisely because it operates at the theoretical minimum data length, so I would prefer not to rely on that as the fix. Furthermore, my current sampling time is relatively high, which is costing controller performance.

I am particularly interested in whether this issue is inherent to the SDP formulation itself. More generally, are there theoretical or practical considerations (e.g., redundancy in constraints, scaling issues, or solver limitations) that could explain why SeDuMi reports a lack of full row rank?

Thank you for your time and patience.

Welcome to the forum.

All the factors you mentioned could be at play. SeDuMi and SDPT 3 failed on my machine for this problem instance.

However, (a not quite up to date) Mosek 11.0.30 solved it. Mosek is much more numerically robust than SeDuMi and SDPT3, so you can get away with more when using Mosek.

Calling Mosek 11.0.7: 67 variables, 31 equality constraints
For improved efficiency, Mosek is solving the dual problem.

MOSEK Version 11.0.30 (Build date: 2025-11-17 21:19:36)
Copyright (c) MOSEK ApS, Denmark WWW: mosek.com
Platform: Windows/64-X86

Problem
Name                   :
Objective sense        : minimize
Type                   : CONIC (conic optimization problem)
Constraints            : 31
Affine conic cons.     : 0
Disjunctive cons.      : 0
Cones                  : 0
Scalar variables       : 16
Matrix variables       : 2 (scalarized: 51)
Integer variables      : 0

Optimizer started.
Presolve started.
Linear dependency checker started.
Linear dependency checker terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator - tries                  : 2                 time                   : 0.00
Lin. dep.  - tries                  : 1                 time                   : 0.00
Lin. dep.  - primal attempts        : 1                 successes              : 1
Lin. dep.  - dual attempts          : 0                 successes              : 0
Lin. dep.  - primal deps.           : 0                 dual deps.             : 0
Presolve terminated. Time: 0.00
Optimizer  - threads                : 24
Optimizer  - solved problem         : the primal
Optimizer  - Constraints            : 31
Optimizer  - Cones                  : 1
Optimizer  - Scalar variables       : 17                conic                  : 17
Optimizer  - Semi-definite variables: 2                 scalarized             : 51
Factor     - setup time             : 0.00
Factor     - dense det. time        : 0.00              GP order time          : 0.00
Factor     - nonzeros before factor : 496               after factor           : 496
Factor     - dense dim.             : 0                 flops                  : 2.79e+04
ITE PFEAS    DFEAS    GFEAS    PRSTATUS   POBJ              DOBJ              MU       TIME
0   6.5e+00  1.0e+00  9.0e+00  0.00e+00   8.000000000e+00   0.000000000e+00   1.0e+00  0.00
1   2.1e+00  3.3e-01  5.1e+00  -9.97e-01  5.612510102e+00   -2.907613374e-01  3.3e-01  0.00
2   3.8e-01  5.9e-02  2.1e+00  -9.77e-01  -1.461342629e+01  -7.465798287e+00  5.9e-02  0.00
3   9.0e-02  1.4e-02  7.8e-01  -8.16e-01  -1.061630778e+02  -7.322496946e+01  1.4e-02  0.00
4   2.5e-02  3.9e-03  2.1e-01  -1.67e-01  -3.259119918e+02  -2.922293994e+02  3.9e-03  0.00
5   8.4e-03  1.3e-03  1.1e-01  -3.92e-01  -8.825339561e+02  -8.002055299e+02  1.3e-03  0.00
6   2.9e-03  4.5e-04  4.2e-02  -1.14e-01  -1.667582783e+03  -1.563491413e+03  4.6e-04  0.00
7   4.0e-04  6.2e-05  9.8e-03  -4.92e-01  -5.183546306e+03  -4.873742472e+03  8.7e-05  0.00
8   2.3e-05  3.5e-06  2.9e-04  1.98e-01   -4.331941733e+03  -4.246903507e+03  8.4e-06  0.00
9   5.5e-06  8.5e-07  3.4e-05  9.22e-01   -4.027309679e+03  -4.007670632e+03  2.0e-06  0.00
10  3.0e-07  4.6e-08  4.1e-07  1.01e+00   -3.979412479e+03  -3.978444110e+03  1.1e-07  0.00
11  4.1e-08  1.8e-09  3.1e-09  1.00e+00   -3.973694519e+03  -3.973657314e+03  4.6e-09  0.00
12  3.0e-08  1.6e-09  2.6e-09  1.00e+00   -3.973664158e+03  -3.973630827e+03  4.2e-09  0.00
13  2.2e-08  1.6e-10  3.5e-11  1.00e+00   -3.973453226e+03  -3.973451350e+03  2.4e-10  0.00
14  1.9e-08  4.3e-10  1.5e-11  1.00e+00   -3.973449326e+03  -3.973448287e+03  1.3e-10  0.00
15  1.9e-08  4.3e-10  1.5e-11  1.00e+00   -3.973449326e+03  -3.973448287e+03  1.3e-10  0.01
16  1.9e-08  4.2e-10  1.4e-11  1.00e+00   -3.973449294e+03  -3.973448262e+03  1.3e-10  0.01
17  1.9e-08  4.2e-10  1.4e-11  1.00e+00   -3.973449294e+03  -3.973448262e+03  1.3e-10  0.01
Optimizer terminated. Time: 0.01

Interior-point solution summary
Problem status  : PRIMAL_AND_DUAL_FEASIBLE
Solution status : OPTIMAL
Primal.  obj: -3.9734492937e+03   nrm: 6e+03    Viol.  con: 3e-02    var: 0e+00    barvar: 0e+00
Dual.    obj: -3.9734482616e+03   nrm: 7e+05    Viol.  con: 0e+00    var: 2e-07    barvar: 1e-06
Optimizer summary
Optimizer                 -                        time: 0.01
Interior-point          - iterations : 18        time: 0.01
Simplex                 - iterations : 0         time: 0.00
Mixed integer           - relaxations: 0         time: 0.00




Status: Solved
Optimal value (cvx_optval): +3973.45


Q_lqr =
1.0e+05 *
0.002963339844067  -1.090591498145528   0.163833610843786  -0.000752697943944
0.006634210336925  -2.463416171376179   0.335055890715379   0.000398064136209
-0.019355446940746   7.056805194182560  -0.979356929023781   0.000199765295669
0.018337643749218  -6.620763432857951   0.923517229352853  -0.000619615920516
-0.008580733187631   3.117960179590550  -0.443013900304312   0.000773493202052

Y =
2.108673718917921e+02

P_lqr =
1.0e+03 *
0.004496501491971  -0.006840585509036  -0.031828254803437  -0.000171033696666
-0.006840585509036   1.283859488401043  -0.101837988258284  -0.000410328879938
-0.031828254803437  -0.101837988258284   1.136997618676731  -0.031609555145704
-0.000171033696666  -0.000410328879938  -0.031609555145704   0.002593517712276
1 Like

Awesome! I have not considered using Mosek since it required a license. Will apply for a license now and see if it helps with the problem instance on my machine.

Thank you.

Perhaps you could improve the numerical scaling by changing units, so that non-zero input data is better centered in magnitude around 1.Perhaps it will help. Even if you are using Mosek, it is still a good idea to have the input data well-scaled numerically.

You can read 9.2 Addressing numerical issues — MOSEK Fusion API for .NET 11.1.10 for some tips.

1 Like