Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abstract ALADIN implementation #84

Open
alexe15 opened this issue Mar 6, 2020 · 15 comments
Open

Abstract ALADIN implementation #84

alexe15 opened this issue Mar 6, 2020 · 15 comments
Assignees
Labels
enhancement New feature or request

Comments

@alexe15
Copy link
Owner

alexe15 commented Mar 6, 2020

ALADIN implementation independent of CasADi.

@alexe15 alexe15 added the bug Something isn't working label Mar 6, 2020
@alexe15 alexe15 added enhancement New feature or request and removed bug Something isn't working labels Mar 6, 2020
@timueh
Copy link
Collaborator

timueh commented Mar 9, 2020

Broadly speaking, the goal is to untangle Casadi from Aladin so that we can use Aladin without having to use Casadi. This is necessary for large problems, where Casadi just takes too long to set up the problem.

@timueh
Copy link
Collaborator

timueh commented Mar 27, 2020

Let me add some details:

Status quo

Currently, there is no possibility to not use Casadi. In other words, when you use the toolbox, you need to use casadi. The advantages of Casadi, but so are its disadvantages:

  • (pretty much) restricted to Ipopt
  • slow problem formulation for large problems

It is especially the last point that is painful. For instance, large (optimal) power flow problems simply take too long to set up. Playing with them is no fun 😢

Proposition

Untangle Aladin and Casadi. Essentially, make two repositories: the Aladin core and a Casadi preprocessor. Then, using the Casadi preprocessor together with Aladin core is like using the toolbox now. However, if someone is brave enough to not use Casadi, then use just Aladin core. The goal of this issue is to write Aladin core

Aladin core

The plain Aladin algorithm that does not rely on external packages. It takes a problem formulation as input and returns the solution. The critial point is how to formulate and solve the local NLPs; how to call a solver depends on the solver itself: fmincon is different from ipopt, and so on.

Question: Should the formulation of the NLP be part of Aladin core or not? If it is, we restrict ourselves to the solvers that we support.

Once we have reached consensus on this issue, we should create a file to define the interface.

@alexe15
Copy link
Owner Author

alexe15 commented Mar 27, 2020

I agree, just for you to know we have a similar feature implemented in ALADIN-M. If you have a look at the ALADIN MPC example (https://alexe15.github.io/ALADIN.m/robotEx/, you can also find this problem in the /examples folder), then you can see that Ruchuan implemented a reuse option. This means that the problem formulation can be reused after one ALADIN run for MPC problems, where only the initial condition for the ODE changes. This reuse is automatically detected within ALADIN-M. This is done here

% check whether problem setupt is present already

so if NLP solvers, sensitivities etc. are defined in the sProb struct ALADIN-M will use them. So I think to some extent, this is where we could start. If we pass solvers here, which use the same interface as the CasADi solvers as here
sol = sProb.nnlp{j}('x0' , iter.yy{j},...

then I guess we should be able to define the problems more or less completely externally also with non-CasADi solvers. Btw @Ruchuan is working on a similar issue for a machine learning problem in #102 (Yuning suggested to include this as an additional example showing that we can gain some benefits with bi-level ALADIN for machine learning problems)

@timueh
Copy link
Collaborator

timueh commented Mar 27, 2020

You agree with what? 🤣

I can ask my question again in ALADIN-M-lingo: should Aladin core be simply iterateAL()? Yes or no?

(that's really a design question i don't know the answer to)

However, I do think that we should not rely on the interface from Casadi, because it's highly customized to Casadi. Instead, can we move to function handles? Check out this prototypical example:

Taking the standard example

import casadi.*
x = SX.sym('x');
y = SX.sym('y');
z = SX.sym('z');
nlp = struct('x',[x;y;z], 'f',x^2+100*z^2, 'g',z+(1-x)^2-y);
S = nlpsol('S', 'ipopt', nlp);

the Casadi way to solve this is

x0 = [2.5,3.0,0.75]
S('x0', x0', 'lbg', 0, 'ubg', 0)

What if, instead, we make this a function handle?

solve_problem = @(x)S('x0', x, 'lbg', 0, 'ubg', 0)
solve_problem(x0)

Thinking about this a little more: this does fix a lot of Casadi dependency. As you said: as long as I provide a valid sProb together with a (function handle solve_problem) way to solve the NLPs, I am fine.. or not?!?!

@TimmFaulwasser
Copy link
Collaborator

@till: this is a valid suggestion, which has been on the table for discussion for some time. At this point, it should not get priority.

timueh added a commit that referenced this issue Mar 27, 2020
check out example_main and set the global variable `use_fmincon` to true/false.
there is some difference in convergence.
@timueh
Copy link
Collaborator

timueh commented Mar 27, 2020

Running examples/examples_main.m with use_fmincon = false yields the usual solution:

grafik

Setting now use_mincon = true uses fmincon only to solve the local NLP. The computation of the sensitivities is untouched. The result is

grafik

While overall convergence agrees, the final values differ.

Anyhow, first step taken. Yay 🎉 🎉 🎉

However, we do need to agree on a common interface for calling the NLP solvers. I do think that abstractifying everything will improve readability of the code tremendously. Check out how

nnlp{i} = @(x, z, rho, lambda, Sigma)build_local_NLP(funs.ffi{i}, funs.ggi{i}, funs.hhi{i}, sProb.AA{i}, lambda, rho, z, Sigma, x, sProb.llbx{i}, sProb.uubx{i});

is defined:

function res = build_local_NLP(f, g, h, A, lambda, rho, z, Sigma, x0, lbx, ubx)
cost = build_cost_function(f, lambda, A, rho, z, Sigma);
nonlcon = @(x)build_nonlcon(x, g, h);
[xopt, fval, flag, out, multiplier] = fmincon(cost, x0, [], [], [], [], lbx, ubx, nonlcon);
res.x = xopt;
res.lam_g = [multiplier.eqnonlin; multiplier.ineqnonlin];
res.lam_x = max(multiplier.lower, multiplier.upper);
end

@timueh
Copy link
Collaborator

timueh commented Mar 27, 2020

Having talked to @alexe15, the suggestion for the common interface for solving the local NLPs is:

solve_NLP(x0, z, rho, lambda, Sigma, pars)

where pars is a struct containing arbitrary parameters.

@timueh
Copy link
Collaborator

timueh commented Mar 30, 2020

I rectified the interface

problem = sProb.nnlp{j};
sol = problem.solve_nlp(x0, z, rho, lambda, Sigma, problem.pars);

I only tested against runtests('OptProbsTest') with Ipopt. ^^

The next step is to let the user specify the sensititivies.

timueh added a commit that referenced this issue Mar 31, 2020
timueh added a commit that referenced this issue Apr 3, 2020
timueh added a commit that referenced this issue Apr 6, 2020
timueh added a commit that referenced this issue Apr 7, 2020
- modified termination criterion in `iterateAL()`
- verified sensitivities against casadi
@timueh
Copy link
Collaborator

timueh commented Apr 7, 2020

Big fail from my side: the numerical sensitivies were not used correctly in all previous versions (and the gradient of the cost function was incorrect..). With 075888a it should be correct. Also, for the distributed power flow problem I compared the numerical sensitivities against casadi: they agree.

@timueh
Copy link
Collaborator

timueh commented Apr 9, 2020

I wrote a few lines to describe what I did the last couple of days. My preliminary conclusion is that to abstractify aladin is, luckily, less involved than I feared. I got it to work with fmincon and user-supplied sensitivities for a distributed power flow problem, and it can solve cases for which casadi+ipopt simpy takes too long to set up (e.g. coupling three 118-bus systems).

I will stop working on this for now, but @bennerh will take over beginning in May.

@alexe15, please take a look at the document I wrote. This should be the basis for future discussions.

@timueh
Copy link
Collaborator

timueh commented Apr 17, 2020

I cleaned up the parallel step tremendously, shifting all the heavy burden to local functions. Readability of the code is increased. I think this more modular approach helps also for the file doing the decentralized step.

@timueh
Copy link
Collaborator

timueh commented Apr 28, 2020

Having spoken to @alexe15, the goal of the abstractify branch can be summarized as follows:

When calling iterateAL( sProb, opts, timers ), no information about casadi need to be passed.

In its simplest form, the following check needs to evaluate to true: does iterateAL() run after having deleted all fields in sProb that have cas in their name?

In a more elegant form, sProb should not contain any casadi information to begin with.. ;)

@timueh
Copy link
Collaborator

timueh commented Apr 29, 2020

The first draft of the interface for iterateAL() can be found here.

@bennerh
Copy link
Collaborator

bennerh commented May 7, 2020

Proposal for next step:
Construct an example file that should run with the abstractified-ALADIN branch version. To do so, I would rebuild the example provided by @timueh for the abstractify branch.

In case there are no objections from your side, I would start doing that now..

Furthermore I would like to test a solver different from fmincon and ipopt, because it's a good task for me and a good test in the end. Any suggestions there?

@timueh
Copy link
Collaborator

timueh commented May 7, 2020

I would not try to replicate my example; there is simply no need. The best tests are and remain the test that you have written already.

AFAICT either all of them work or all of them fail... ;)

Implementing different solvers is on the agenda, see #106, #107, but I would not do that now. Let's do one thing at a time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants