Solving simple NLP problem (exploit BLASFEO performance)

Hello,
my goal - for now - is to solve a simple NLP problem. Hence, I have a cost function and some non-linear equality constraints. No dynamic or so on (yet). I’m working with MATLAB and I have looked through the examples but I haven’t found a suitable example for my problem. I’ve found the following thread in the forum: Python simple examples as in NLP CasADi documentation but sadly, the links to the code examples are broken.
I’m aware that the CasADi package exists and I was already able to solve my problem with the CasADi nlpsol-function. Nevertheless, I would like to exploit the enhanced performance of BLASFEO. I would be grateful for any hint.

Thanks in advance!

Hi @Steradiant,

I fixed the links in the thread you mentioned.
In general, acados is really made for optimal control structured NLPs (OCP-NLP).
If your problem does not have this structure at all, I don’t know right now what I should refer you to.

Maybe you can elaborate more on the “yet” part or the nature of your problem.

Best,
Jonathan

@FreyJo
thanks for the fast answer. I’ll have a look on the linked example. The ‘yet’ referred to a later stage in the research where there will, most likely, come up also some OCP’s. But at the moment, I’m simply interested in solving a problem like
min f(x)
s.t. h(x) = 0

I thought I could probably also benefit from the performance of the high performance BLASFEO library. Because - at least from the results of my research - CasADi doesn’t use BLASFEO as backend.

You can try to solve the problem with acados from Matlab, by setting

  • N = 1
  • have free x_0
  • set \dot{x} = 0
  • use f(x) as a terminal cost term
  • use a cheap integrator ERK with 1 stage and 1 step
  • set h(x) as a terminal constraint

Note:

  • A lot of complexity will be in the Casadi generated problem functions
  • The QP solutions will be done with Blasfeo and HPIPM
  • You will probably need a more careful initialization compared to CasADi + IPOPT. acados does not have stable globalization (yet), so it might not converge.
  • But there could be quite a speed up if it works, depending on your problem.

Cheers,
Jonathan

Thanks for the great hint. I’ve had a closer look at the problem formulation and I came also to the conclusion, that it’s worth a try. I’m somehow struggling how to get this formulation to acados correctly. I’ve tried the follwing code:

%% Model
model.nx = 273;
model.nu = 0;
model.sym_x = SX.sym('x',273);
model.expr_f_expl = zeros(273,1);
model.expr_h = ceq;  % in ceq are the 260 non-linear equality constraints
model.expr_ext_cost = 0;

%% discretization
N = 1;
T = 1; % time horizon length
x_0 = zeros(273,1);

nlp_solver = 'sqp'; 
qp_solver = 'full_condensing_hpipm';
qp_solver_cond_N = 1; 
sim_method = 'erk';

nx = model.nx;
nu = model.nu;

%% model to create the solver
ocp_model = acados_ocp_model();
model_name = 'mod';

% acados ocp model
ocp_model.set('name', model_name);
ocp_model.set('T', T);

% symbolics
ocp_model.set('sym_x', model.sym_x);

Vxe = zeros(length(x_0));
Vxe(1:10,1:10) = eye(10);

% cost
ocp_model.set('cost_type', 'linear_ls');
ocp_model.set('cost_Vx', zeros(1,273));
ocp_model.set('cost_Vu', 1:0);
ocp_model.set('cost_Vz', 1:0);
ocp_model.set('cost_W', 1);
ocp_model.set('cost_y_ref', 0);
ocp_model.set('cost_type_e', 'linear_ls');
ocp_model.set('cost_Vx_e', Vxe);
ocp_model.set('cost_W_e', 2*eye(nx));
ocp_model.set('cost_y_ref_e', zeros(273,1));

ocp_model.set('dyn_type', 'explicit');
ocp_model.set('dyn_expr_f', model.expr_f_expl);

% constraints
ocp_model.set('constr_expr_h', model.expr_h);
ocp_model.set('constr_lh', zeros(260,1)); % lower bound on h
ocp_model.set('constr_uh', zeros(260,1));  % upper bound on h

ocp_model.set('constr_x0', zeros(273,1));

%% acados ocp set opts
ocp_opts = acados_ocp_opts();
ocp_opts.set('param_scheme_N', N);
ocp_opts.set('nlp_solver', nlp_solver);
ocp_opts.set('sim_method', sim_method);
ocp_opts.set('qp_solver', qp_solver);
ocp_opts.set('qp_solver_cond_N', qp_solver_cond_N);

%% create ocp solver
ocp = acados_ocp(ocp_model, ocp_opts);

x_traj_init = zeros(nx, N+1);
u_traj_init = zeros(nu, N);

% set trajectory initialization
ocp.set('init_x', x_traj_init);
ocp.set('init_u', u_traj_init);

% solve
ocp.solve();

If I execute the code, Windows says that cc1.exe stopped working and MATLAB crashes with the following error:

Error using mbuild (line 166)
Unable to complete successfully.
The command 'C:\ProgramData\MATLAB\SupportPackages\R2020a\3P.instrset\mingw_w64.instrset\bin\gcc' exited with a return value '1'

Error in ocp_generate_casadi_ext_fun (line 185)
        mbuild(c_files_path{:}, '-output', lib_name, 'CFLAGS="$CFLAGS"', 'LDTYPE="-shared"', ['LDEXT=', ldext]);

Error in acados_ocp (line 152)
                ocp_generate_casadi_ext_fun(obj.model_struct, obj.opts_struct);

Error in main(line 187)
ocp = acados_ocp(ocp_model, ocp_opts);

which means, that already the acados_ocp-command fails. The last ouptut before the error is
acados MEX interface compiled successfully

Note: The cost function I was trying to implement is 1/2*(x(1:10)'*(x(1:10)), hence, the first 10 elements of the decision variables squared ( f(x) = \frac{1}{2}(x_1^2 + x_2^2 + \ldots + x_{10}^2))

I would be very grateful for any hint how to resolve this problem.

I figured that it’s a better idea to start this with a more simple problem, e.g. the Rosbenbrock problem
\mathrm{min}_x f(x) = 100(x_2-x_1^2)^2 + (x_1-1)^2
s.t. c(x) = x_1^2+x_2^2-2=0
because this could then also be a great addition for the example-pack.

x = SX.sym('x',2);
f = (100*(x(2)-x(1)^2)^2 + (x(1)-1)^2);
c = x(1)^2+x(2)^2-2;

%% Model
model.nx = 2;
model.nu = 0;
model.sym_x = x;
model.expr_f_expl = SX.zeros(2,1);
model.expr_h = c;
model.expr_ext_cost = 0;
model.expr_ext_cost_e = f;

%% discretization
N = 1;
T = 1; % time horizon length

nlp_solver = 'sqp'; % sqp, sqp_rti
qp_solver = 'full_condensing_hpipm';
qp_solver_cond_N = 1; % for partial condensing
sim_method = 'erk'; % erk, irk, irk_gnsf
sim_method_num_stages = 1;
sim_method_num_steps = 1;

%% model dynamics
nx = model.nx;
nu = model.nu;

%% model to create the solver
ocp_model = acados_ocp_model();
model_name = 'mod';
x_0 = [-1;-1];

% acados ocp model
ocp_model.set('name', model_name);
ocp_model.set('T', T);

% symbolics
ocp_model.set('sym_x', model.sym_x);

% cost
ny = nu+nx; % number of outputs in lagrange term
ny_e = nx; % number of outputs in mayer term
ocp_model.set('cost_type', 'linear_ls');
ocp_model.set('cost_Vx', zeros(ny, nx));
ocp_model.set('cost_Vu', zeros(ny, nu));
ocp_model.set('cost_W', zeros(ny));
ocp_model.set('cost_y_ref', zeros(ny, 1));
ocp_model.set('cost_expr_y', model.expr_ext_cost);

ocp_model.set('cost_type_e', 'nonlinear_ls');
ocp_model.set('cost_expr_y_e', model.expr_ext_cost_e);
ocp_model.set('cost_W_e', 1*eye(1));

ocp_model.set('dyn_type', 'explicit');
ocp_model.set('dyn_expr_f', model.expr_f_expl);

% constraints
ocp_model.set('constr_expr_h', model.expr_h);
ocp_model.set('constr_lh', zeros(1,1)); % lower bound on h
ocp_model.set('constr_uh', zeros(1,1));  % upper bound on h

ocp_model.set('constr_x0', x_0);

%% acados ocp set opts
ocp_opts = acados_ocp_opts();
ocp_opts.set('param_scheme_N', N);
ocp_opts.set('nlp_solver', nlp_solver);
ocp_opts.set('sim_method', sim_method);
ocp_opts.set('qp_solver', qp_solver);
ocp_opts.set('qp_solver_cond_N', qp_solver_cond_N);
ocp_opts.set('print_level', 5);

%% create ocp solver
ocp = acados_ocp(ocp_model, ocp_opts);

%% call ocp solver
% solve
ocp.solve();
% get solution
utraj = ocp.get('u');
xtraj = ocp.get('x');

status = ocp.get('status'); % 0 - success
ocp.print('stat')

This code compiles correctly (and all mex-files are generated). Nevertheless, the solving doesn’t work. I get status=4. Sadly, I can’t find any information what that means or where the problem could be.

I would be very grateful for any help!

Hi,

I just had a look at your Code and think the following should be changed.

% for the terminal cost
ocp_model.set('cost_type_e', 'nonlinear_ls'); % I think you want to use 'ext_cost'
ocp_model.set('cost_expr_ext_cost_e', model.expr_ext_cost_e);
% Dont fix initial state, i.e. remove:
ocp_model.set('constr_x0', x_0);

For error status descriptions, check Acados error status 2 on python interface

Cheers,
Jonathan

Hi,
thanks for the hints. I’ve changed the costs to be external

ocp_model.set('cost_type', 'ext_cost');
ocp_model.set('cost_expr_ext_cost', model.expr_ext_cost);

ocp_model.set('cost_type_e', 'ext_cost');
ocp_model.set('cost_expr_ext_cost_e', model.expr_ext_cost_e);

After removing this line

ocp_model.set('constr_x0', x_0);

I got the follwoing error

ocp_create: field constr_Jbx_0 not provided, is mandatory!

According to this post Time optimal formulation I’ve added the following lines

ocp_model.set('constr_lbx_0', [-inf;-inf]);
ocp_model.set('constr_ubx_0', [inf;inf]);
ocp_model.set('constr_Jbx_0', zeros(2)); 

Before I execute the ocp.solve() I set the initial condition via ocp.set('constr_x0', [-1;-1]);. Everything compiles but I’m still stuck with the status=4 (qp solver failed). Still, I sadly don’t really know where the problem could be.

Cheers

Hi,

I made a few changes such that removing ocp_model.set('constr_x0', x_0); works.

However, I was also not able to solve the problem yet.
Failing to solve this QP.

QP solver returned error status 3 in iteration 0

 Failed to solve the following QP:
BAbt =
  1.00000   0.00000 
  0.00000   1.00000 
  0.00000   0.00000 

b =
  0.00000   0.00000 

RSQrq =
  0.00000   0.00000 
  0.00000   0.00000 
  0.00000   0.00000 

  2.00000   0.00000 
  0.00000 200.00000 
  0.00000   0.00000 

rq =
  0.00000   0.00000 

 -2.00000   0.00000 

d =
  2.00000  -2.00000 



idxb =




DCt =
  0.00000 
  0.00000 

d_s =
idxs_rev =
-1 



Z =
z =

iter	res_stat	res_eq		res_ineq	res_comp	qp_stat	qp_iter
0	2.000000e+00	0.000000e+00	2.000000e+00	0.000000e+00	0	0

Probably, the problem is that the initial cost matrix RSQrq is zero.
However, formulating a problem with 0 shooting nodes seems more involved from an interface point of view.
I am sorry, but acados does not really aim to solve these kinds of problems (yet).

Thanks for the great help! I think it would be a great addition to the software package because one could possibly benefit from the BLASFEO performance and hpipm when solving non-linear optimization problems (NLPs). I’ll indeed keep an eye on the developments of the package!

Hi, this program worked and provided correct answer

clear all

import casadi.*

x = SX.sym('x',2);
xdot = SX.sym('x_dot',2);
f = (100*(x(2)-x(1)^2)^2 + (x(1)-1)^2);
c = x(1)^2+x(2)^2-2;

model.expr_f_expl = SX.zeros(2,1);
model.expr_f_impl = xdot - model.expr_f_expl;

%% Model
model.nx = 2;
model.nu = 0;
model.sym_x = x;
model.sym_xdot = xdot;
model.expr_h = c;
model.expr_ext_cost = f;
model.expr_ext_cost_e = f;

%% discretization
N = 1;
T = 1; % time horizon length

nlp_solver = 'sqp'; % sqp, sqp_rti
qp_solver = 'full_condensing_hpipm';
qp_solver_cond_N = 1; % for partial condensing
sim_method = 'erk'; % erk, irk, irk_gnsf
sim_method_num_stages = 1;
sim_method_num_steps = 1;

%% model dynamics
nx = model.nx;
nu = model.nu;

%% model to create the solver
ocp_model = acados_ocp_model();
model_name = 'mod';
x_0 = [-100000;-100000];

% acados ocp model
ocp_model.set('name', model_name);
ocp_model.set('T', T);

% symbolics
ocp_model.set('sym_x', model.sym_x);
ocp_model.set('sym_xdot', model.sym_xdot);
ocp_model.set('cost_type','ext_cost');
ocp_model.set('cost_type_e','ext_cost');
ocp_model.set('cost_expr_ext_cost', model.expr_ext_cost);
ocp_model.set('cost_expr_ext_cost_e', model.expr_ext_cost_e);

% dynamics
if (strcmp(sim_method, 'erk'))
	ocp_model.set('dyn_type', 'explicit');
	ocp_model.set('dyn_expr_f', model.expr_f_expl);
elseif (strcmp(sim_method, 'irk'))
	ocp_model.set('dyn_type', 'implicit');
	ocp_model.set('dyn_expr_f', model.expr_f_impl);
end

% constraints
ocp_model.set('constr_expr_h', model.expr_h);
ocp_model.set('constr_lh', zeros(1,1)); % lower bound on h
ocp_model.set('constr_uh', zeros(1,1));  % upper bound on h


%% acados ocp set opts
ocp_opts = acados_ocp_opts();
ocp_opts.set('param_scheme_N', N);
ocp_opts.set('nlp_solver', nlp_solver);
ocp_opts.set('sim_method', sim_method);
ocp_opts.set('qp_solver', qp_solver);
ocp_opts.set('qp_solver_cond_N', qp_solver_cond_N);


%% create ocp solver
ocp = acados_ocp(ocp_model, ocp_opts);

% initial states
ocp.set('init_x', x_0.*ones(N+1,2));

%% call ocp solver
% solve
ocp.solve();
% get solution
utraj = ocp.get('u');
xtraj = ocp.get('x')

status = ocp.get('status'); % 0 - success
ocp.print('stat')