%% Load all CasADi symbols
import casadi.*

%% Scalar-atomic symbolic expression type (SX)
% Symbolic primitives
x = SX.sym('x');
y = SX.sym('y');

% Construct expressions
z = x*sin(x+y)

% Expressions may have common subexpressions
z2 = z + cos(z)

% Some simplifications (but not much)
z3 = x*y/x - y

%% Everything is a matrix
A = SX.sym('A',3,3)
B = SX.sym('B',3)
z4 = A.*A % Elementwise product, in Python "*"
z5 = A*B % Matrix product, in Python "mtimes"
z6 = trace(A) % Trace
z7 = norm_F(A)
z8 = A(3, :) % Slicing: In Python A[2,:]

%% In fact, everything is a _sparse_ matrix
I = SX.eye(3);
Ak = kron(I,A)

%% Algorithmic differentiation (AD)
t  = SX.sym('t');       % time
u  = SX.sym('u');       % control
p = SX.sym('p');
q = SX.sym('q');
c = SX.sym('c');
x = [p;q;c];            % state
ode = [(1 - q^2)*p - q + u; p; p^2+q^2+u^2];
J = jacobian(ode, x);

% Gradient of a scalar expression
g = gradient(ode(3), p)
% Hessian of a scalar expression
H = hessian(ode(3), x)
% Jacobian-times-vector product (forward mode AD)
v = SX.sym('v', 3)
Jv = jtimes(ode, x, v)
% Transposed Jacobian-times-vector product (reverse mode AD)
JTv = jtimes(ode, x, v, true)

%% CasADi functions
x = SX.sym('x');
y = SX.sym('y');
z = x*sin(x+y);

% Multiple matrix-valued inputs, multiple matrix-valued outputs
f = Function('f', {x,y}, {z});
% Or better, with names for inputs and outputs
f = Function('f', {x,y}, {z}, char('x', 'y'), char('z'));

% Evaluate a function numerically
sol = f({1.2,3.4});
disp(sol{1});

% Evaluate a function symbolically
sol = f({1.2,x+y});
disp(sol{1});

% Other things, generate C code (with MEX gateway):
opts = struct('mex', true);
f.generate('f.c', opts);

%% Matrix expression graphs (use when in doubt)
A = MX.sym('A',3,3)
B = MX.sym('B',3)
z4 = A.*A % Elementwise product, in Python "*"
z5 = A*B % Matrix product, in Python "mtimes"
z6 = trace(A) % Trace
z7 = norm_F(A)
z8 = A(3, :) % Slicing: In Python A[2,:]
I = MX.eye(3);
Ak = kron(I,A)


