How to turn a string into a line of differential equation for ode45?
Show older comments
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)
Stephen23
on 23 Dec 2020
Edited: Walter Roberson
on 23 Dec 2020
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
Marton Bendeguz Gyani
on 23 Dec 2020
"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?"
Why it is in MATLAB: https://www.mathworks.com/matlabcentral/answers/364851-assignin-and-eval-functions
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).
Nirvik Sinha
on 23 Dec 2020
Edited: Nirvik Sinha
on 24 Dec 2020
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?
Walter Roberson
on 24 Dec 2020
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.
Walter Roberson
on 23 Dec 2020
0 votes
See odeFunction and see the work flow there in the first example.
Nirvik Sinha
on 23 Dec 2020
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);
1 Comment
Marton Bendeguz Gyani
on 23 Dec 2020
Categories
Find more on Data Type Identification in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!