-
Notifications
You must be signed in to change notification settings - Fork 14
feature: Inlining data when possible #69
Description
For model predictive control (and other applications), it is common to calculate a quadratic function as follows.
quad_cost {n, p, N} -- For a horizon of N
(P :: double[n, n]) -- Terminal cost
(Q :: double[n, n]) -- Stage cost
(R :: double[p, p]) -- Controller cost
(x :: double[n, N]) -- N state vectors
(u :: double[p, N-1]) -- (N-1) control vectors
:: double
:= ( x[:, N]^T*P*x[:, N] +
sum (vec k in N-1 -> x[:, k]^T*Q*x[:, k] + u[:, k]^T*R*u[:,k]);
)[0];
In many cases, since we are not changing the matrices used in the quadratic function (either the matrices do not need to change or can't anyway because doing so would require a recompile), it is convenient to make functions where some of the inputs are prefilled in.
quad_cost_filled
(x :: double[2, 2])
(u :: double[2, 1]) :: double
:= quad_cost (mat (1, 0; 0, 1)) (mat (1, 0; 0, 1)) (mat (1, 0; 0, 1)) x u;
However, this version results in more assembly instructions (using gcc -O2) than the following, where the inlined data has been placed inside the quadratic function itself (loops can be unrolled, stack size can be constant, etc).
quad_cost2 -- For a horizon of 2
(x :: double[2, 2])
(u :: double[2, 1]) :: double
:= (
P := (mat (1, 0; 0, 1));
Q := (mat (1, 0; 0, 1));
R := (mat (1, 0; 0, 1));
N := 2;
x[:, N]^T*P*x[:, N] +
sum (vec k in N-1 -> x[:, k]^T*Q*x[:, k] + u[:, k]^T*R*u[:,k]);
)[0];
When converted into assembly, quad_cost2 has 5x fewer instructions than quad_cost and it is possible to get rid of the loops.
It would be convenient if Plover had a way of simplifying any functions where some of the fields already contain constant data to help the C compiler later make more optimized versions of the code.
This could also probably be done if an inline tag existed that could be passed into the C code.