How to declare parameters in matlabFunction?

Hi there,
I have been struggling with this for a while, and I am hoping someone will be able to tell me where I am making mistakes. Thank you very much in advance for taking your time to look into this :)
I have been trying to convert symbolic expressions into MATLAB functions using matlabFunction. The original symbolic expressions were generated by another function and contain both parameters and variables, and the converted function doesn't seem to understand the difference btween the two. I am interested in converting the symbolic expressions into a MATLAB functions, and only then bind the values of the parameters in. I am doing this because it takes about an hour and half to do the conversion (not the example shown below!). Delaying the binding to a later stage would allow me to avoid running matlabFunction every time I run my code for a different set of parameter values. The ultimate goal is to use these functions in ode45.
Here is an example:
syms a b c d e % parameters
syms u v w x y z % variables
M_sym = [a*b*w c*x e*d*v;
c*y a*e*w z;
c*e*y a*b*u a*c*e*z];
B_sym = [a*x + b*y - e*z;
c*x + d*u - a*e*v;
d*w + e*a];
param = [c a d e b]
var = [u x y w z u v]
vec = [param var];
M_fun = matlabFunction(M_sym,'Vars',{vec});
B_fun = matlabFunction(B_sym,'Vars',{vec});
The resulting function handles M_fun and B_fun expect a vector input argument with the parameters and the variables combined as elements of the vector (length = 12). Ideally, what I want is to bind in the values of the parameters in M_fun and B_fun first and use the resulting functions in ode45 as M(var,t) and the B(var,t) (M*y = B).
My question is:
a) Is there a way to tell matlabFunction in advance what the parameters are and what their order/sequence is?
Eg. param = [c a d e b]
b) If (a) is not an option, is there a method for passing the parameter values to M_fun and B_fun in a vector form and still use the results in ode45?
Eg. param_val = [0.1 0.25 0.012 0.5 0.33]
I have tried something like the following, but it didn't work (probably because M_fun and B_fun don’t recognize param and var)
f1 = @(param,var)(@(var)M_fun);
f2 = @(param,var)(@(var)B_fun);
param_val = [0.1 0.25 0.012 0.5 0.33]
g1 = f1(param_val);
g2 = f2(param_val);
M = @(t,Y_val)g2(Y_val);
B = @(t,Y_val)g1(Y_val);
time_span = linspace(0.0, 5, 30);
int_cond = [0.0 0.01 0.02 0.1 0.13 0.12 0.05];
opt = odeset('Mass',M);
[t_span,QUf_sol] = ode45(B,time_span,int_cond,opt);
And i am getting the following error:
Error using odearguments (line 95)
@(T,Y_VAL)G1(Y_VAL) returns a vector of length 1, but the length of initial conditions vector is 7. The vector returned by @(T,Y_VAL)G1(Y_VAL) and the initial conditions vector must have the same number of elements.
Error in ode45 (line 115)
odearguments(FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin);
Any help will be greatly appreciated.
Thanks,

2 Commenti

Hi darova,
Thank you for your comment! Could you please elaborate or provide an example?
Cheers!

Accedi per commentare.

 Risposta accettata

Walter Roberson
Walter Roberson il 29 Set 2019
Modificato: Walter Roberson il 2 Ott 2019
MATLAB does not have any distinction between parameters and variables.
You should probably be looking more closely at the 'vars' option of matlabFunction. It accepts a cell array. Within the cell array you can have vectors (even arrays) of variables as entries. All of the entries that are in the same vector or array are bundled into the same input slot.
matlabFunction(a+b+c, 'vars', {a, b, c}) -> @(a, b, c) a+b+c
matlabFunction(a+b+c, 'vars', {a, [b c]}) -> @(a, in1) a+in1(:,1)+in2(:,2)
matlabFunction(a+b+c, 'vars', {a, [b;c]}) -> @(a, in1) a+in1(1,:)+in2(2,:)
The state information for ode45 calls should go into a column vector of variables.
If you use odeFunction instead of matlabFunction then it constructs the state information automatically.

6 Commenti

How to use this construction if i want
syms a b c
f = a+b+c;
clear a b c
matlabFunction(f, 'vars', {'a', ['b'; 'c']})
You cannot do that in R2019a. The closest you can get is
matlabFunction(f, 'vars', {sym('a'), [sym('b'); sym('c')]})
Why are you clearing the symbolic variables?
It is for the case if a,b,c is already in use
Thanks
If a, b, c are already in use, but it is important to you that the anonymous function uses those exact variable names (even though we are talking about rewriting the b,c pair to use in1 then use different variable names to hold the symbols:
%assume that a, b, c are already in use as variables for other
%purposes, but we want to generate a symbolic expression that uses
%symbolic a, b, c
alt_a = sym('a'); alt_b = sym('b'); alt_c = sym('c');
f = alt_a + alt_b + alt_c;
matlabFunction(f, 'vars', {alt_a, [alt_b; alt_c]})
This works because
syms a
is the same as
a = sym('a');
which is to say that a gets assigned a reference to the Symbolic Engine variable named a and every time you use the variable a the content of the variable is looked up and found to be a reference to the symbolic engine variable a . But the same thing happens if you use
alt_a = sym('a');
then every time you mention alt_a, the content of the variable is looked up and found to be a reference to the symbolic engine variable a . MATLAB does not handle the symbolic variables by name, it handles them by value with the value having a printable representation that does not have to be the same as the MATLAB variable name used to store the reference.
Hi Walter,
The odeFunction you suggested resolved the problem. I created vectors of the state variables and the parameters symbols and passed them to odeFunction as in1 and in2, respectively.
Thank you so much for your help!

Accedi per commentare.

Più risposte (1)

clc,clear
syms x(t) y(t) z(t) % variables
syms a b c % parameters
% set up derivatives
d2y = diff(y,2);
dx = diff(x);
d3z = diff(z,3);
% equations
eqn = [d2y + y^2 + 3*t
dx*y - a
d3z/t - b - c];
% let MATLAB do all work for us
[Y, Y_subs] = odeToVectorField(eqn);
% create a function for ode45
F = matlabFunction(Y,'vars',{'t' 'Y' [a b c]});
% set up order of initial conditions
X0 = matlabFunction(Y_subs,'vars',{'x' 'y' 'z' 'Dy' 'Dz' 'D2z'});
tspan = [0.1 2];
x0 = X0( 0,1,1,2,0.5,1.1 ); % initial x y z Dy Dz D2z (order is important)
in3 = [2 1 -0.5]; % parameters (a,b,c)
[t, X] = ode45(@(t,y)F(t,y,in3), tspan, x0);
plot(t,X)
And here is outputs
% EQUATIONS CONVERTED TO FIRST ORDER
Y =
a/Y[2]
Y[3]
- 3*t - Y[2]^2
Y[5]
Y[6]
t*(b + c)
% SUBSTITUTIONS WERE MADE
Y_subs =
x
y
Dy
z
Dz
D2z
% MATLAB FUNCTION FOR ode45 INPUT (in3 - a,b,c coeffs)
F =
@(t,Y,in3)[in3(:,1)./Y(2);Y(3);t.*-3.0-Y(2).^2;Y(5);Y(6);t.*(in3(:,2)+in3(:,3))]
% ORDER OF INITIAL CONDITIONS
X0 =
@(x,y,z,Dy,Dz,D2z)[x;y;Dy;z;Dz;D2z]
See also HERE

Prodotti

Release

R2019a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by