How to turn a string into a line of differential equation for ode45?

I have a system which can be described at any time with the help of ode45. However, changing the parameters of the system changes the last 3 lines of the differential eq. system, so I wrote a matlab code for deriving these equations. It works fine, it can create these equation lines as strings, but then I am not able to transform them so that ode45 could use them. Instead, I have to copy those lines into the f function by hand.
eq1_string=['-(2*Y(5)*sin(Y(2))*(12*Y(6) - 115*Y(4)*cos(Y(2))))/(127*(cos(Y(2))^2 - 1))'];
%in my code, this string is created by matlab
eq1=str2sym(eq1_string); % trying to transform the string into an expression, that ode45 understands
Y0=[0.5*pi() 0.5*pi() 0 0 0 40]; %initial conditions
f=@(t,Y) [Y(4);...
Y(5);...
Y(6);...
eq1;... %this does not work
(103*sin(2*Y(2))*Y(4)^2)/254 - (24*Y(6)*sin(Y(2))*Y(4))/127 - (5886*sin(Y(2)))/635;...
-(Y(5)*sin(Y(2))*(127*Y(4) - 24*Y(6)*cos(Y(2)) + 103*Y(4)*cos(Y(2))^2))/(127*(cos(Y(2))^2 - 1))];
% I have to copy the lines by hand, like the last 2 lines
[t,Y]=ode45(f,0:0.01:10,Y0);
Is there any way to solve it? I tried str2sym and str2func, neither of them worked. Should I use structures? If yes, how?
I know, that renaming a variable while running the code is not recommended, but here I am not renaming or creating any variable.

Answers (3)

Ugh, do NOT use eval for this!
If the string is actually a symbolic expression then use odeFunction as Walter Roberson stated.
If the string is actually a character vector, then use str2func like this (converting to symbolic is a red herring):
eq1 = '-(2*Y(5)*sin(Y(2))*(12*Y(6) - 115*Y(4)*cos(Y(2))))/(127*(cos(Y(2))^2 - 1))'; % removed superfluous square brackets
fn1 = str2func(sprintf('@(Y)%s;',eq1)); % convert string to function handle
Y0 = [0.5*pi(),0.5*pi(),0,0,0,40]; % initial conditions
fun = @(t,Y) [...
Y(4);...
Y(5);...
Y(6);...
fn1(Y);... % call function handle
(103*sin(2*Y(2))*Y(4)^2)/254 - (24*Y(6)*sin(Y(2))*Y(4))/127 - (5886*sin(Y(2)))/635;...
-(Y(5)*sin(Y(2))*(127*Y(4) - 24*Y(6)*cos(Y(2)) + 103*Y(4)*cos(Y(2))^2))/(127*(cos(Y(2))^2 - 1))];
[t,Y] = ode45(fun,0:0.01:10,Y0);
plot(t,Y)

5 Comments

Yes, my string is a character vector. Thank you for showing how to use str2func() properly! It is really useful. Now I have two working versions of my code.
Also, before I asked my question, I read some discussion about this topic, turning string into a function line, and yes, everyone was advising not to use eval(), because it can cause bugs. I start my code with
clear all; close all; clc;
just to get rid of some of those possible issues. However, I was wondering, if eval() is so bad, then why is it in Matlab, or what was the original purpose of creating this funciton?
"However, I was wondering, if eval() is so bad, then why is it in Matlab, or what was the original purpose of creating this funciton?"
In a nutshell: eval allows beginners to write complex, inefficient, obfuscated code in the belief that they are "making progress" but in reality they are painting themselves into a corner and avoiding learning how to write efficient code using the correct tools (the various answers in this thread are perfect examples of this).
For example, creating a function handle once using str2func allows that function handle to be called many times efficiently, whereas if eval was used it would most likely suffer from a performance disadvantage if it is called repeatedly (e.g. by an ODE solver function like ode45) because eval would have to re-evaluate that string each time it is called in by the ODE solver.
A common mis-use of eval is to access variable names dynamically, which experienced users avoid by using much simpler and much more efficient methods (e.g. indexing, fieldnames, storing meta-data as data, etc.):
Accessing variable names like that is specifically advised against by the MATLAB documentation:
"I start my code with clear all; close all; clc;"
While those commands are useful at the command line when experimenting, well-written code rarely (if ever) relies on them. For example, clear all brutally and pointlessly clears compiled functions from memory. Why do you need to do that? You just slow down your code with little benefit: "Calling clear all decreases code performance, and is usually unnecessary." source: https://www.mathworks.com/help/matlab/ref/clear.html#btdyzbv-1-ItemType
A good rule is that your code should not perform actions unrelated to the functionality of your code. If your code is not a plotting function, why should it close all figures? If I wrote a function called cuberoot, I doubt that many users would be very impressed if as well as calculuating the cube root of its input it also cleared everything from memory without warning (goodbye compiled functions!) and closed all figures (goodbye results!) and also cleared the command window (goodbye todays work!).
I cannot think of any reason why well-written code (e.g. function or class) should close all figures.
Using functions is much much much better than scripts and clear all (or clearvars).
I kind of guessed I would draw flak for suggesting eval as a quick fix. Reading through your references I still couldn't get what are then the specific use cases of eval ? One of the answers states that the main reason is to maintain backward compatibitily with older 'inefficiently written' code. That sounds ironical given the context. Likewise the MATLAB documentation on 'alternatives to eval' begins by stating that eval is powerful and flexible. Where and when?
Every user input to the command line is eval()'d. Every user input at the keyboard() command is eval()'d. Every input to input() that does not have the 's' option is eval()'d. Everything passed to str2num() is eval()'d. Every call to an inline() object is eval()'d
... all of which should be treated as internal implementation, not as a recommendation for user code.
With eval(), you can construct blocks of code on the fly and evaluate them without having to write them out to disk... but that does not mean that you should do that.
"... what are then the specific use cases of eval ? ... Where and when?"
Some are listed here:
Personally I don't think of eval as being any different to any other tool, I would apply exactly the same question for any tool X: what is an appropriate use case for tool X? When on balance of X's pros and cons, it is the best choice for that specific task, where "best" could be determined by a kind of weighted sum of various metrics and scales, e.g.:
  • providing the correct result
  • runtime efficiency
  • coding time, debugging time, etc.
  • clarity vs. obfuscation of intent
  • extensibility, generalizability, etc.
  • robustness, edge-cases, numeric stability, etc.
  • availability (MATLAB version, third-party, DIY, etc.)
  • security, unintended behaviors, etc.
  • maintainability, understandability for future readers
  • ... etc.
Note that some of these metrics also depend on external factors as well, e.g. where the code will be used (playing around with code on my local computer vs. distributing code on MATLAB FEX to unknown users vs. legal/standard requirements for the industry I work in).
My own experience has been that writing code following some kind of best-practice has immediate benefits, e.g. moving repeated code to its own function has on several occasions allowed me to note where that functionality could be generalized, or applied to more cases, or made me aware of missing cases. The MATLAB data type for storing functions is the function handle, so the first thing I would do is make sure that it is a function handle... and often right after that I notice that this function handle can be stored, or passed as a variable, or efficiently used again for plotting, or some other operation, which can then be added with very little effort. End result: I am more productive in less time.

Sign in to comment.

See odeFunction and see the work flow there in the first example.
You can use eval for this purpose instead of symbolic math:
eq1_string=['-(2*Y(5)*sin(Y(2))*(12*Y(6) - 115*Y(4)*cos(Y(2))))/(127*(cos(Y(2))^2 - 1))'];
Y0=[0.5*pi() 0.5*pi() 0 0 0 40]; %initial conditions
f=@(t,Y) [Y(4);...
Y(5);...
Y(6);...
eval(eq1_string);...
(103*sin(2*Y(2))*Y(4)^2)/254 - (24*Y(6)*sin(Y(2))*Y(4))/127 - (5886*sin(Y(2)))/635;...
-(Y(5)*sin(Y(2))*(127*Y(4) - 24*Y(6)*cos(Y(2)) + 103*Y(4)*cos(Y(2))^2))/(127*(cos(Y(2))^2 - 1))];
[t,Y]=ode45(f,0:0.01:10,Y0);

Categories

Products

Release

R2020b

Asked:

on 23 Dec 2020

Edited:

on 25 Dec 2020

Community Treasure Hunt

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

Start Hunting!