Datatype conversion? - matlab

Given a Differential Equation in matrix form:
f = #(t,y) [(a*y(1) + b*y(2)); (c*y(1) + d*y(2))];
a=-2; b=-1; c=1; d=-4
Solutions to my differential equation problem are
x(t)= e^(-3t) (t+1)
y{t)= e^(-3t) *t
General Solution is
Y(t)= e^(-3t) (xo,yo)' + t * e^(-3t) (xo-yo,xo-yo)'
Initial condition is (xo,yo)=(1,0)=(e;g)
Matlab Code:
syms xSol ySol ran with and without this statement with same error msg
t=-1.1:0.1:2.1;
plot(t,xSol) error occurs here
hold on
plot(t,ySol)
hold off
Error message:
Error using plot
Data must be numeric, datetime, duration or an array convertible to double.
Further study would indicate that xSol and ySol must be numeric numbers.
I have at initial condition (1;0);
xSol(t) = exp(-3*t) + t*exp(-3*t)
ySol(t) = t*exp(-3*t)
I need to change xSol(t) and ySol(t) to xSol and ySol without (t).
How to fix? Correction must work with complex numbers.
Examples online use fplot. Is that a better function to use in this application?
I would need to define two functions for xSolv and ySolv. My code works for generic differential equation in two variables.
Example online is
f = #(x) sin(1/x);
Assume we want to plot that between 0.01 and 1:
lims = [.01 1];
fplot(f, lims, '.-')
I tried
xSolvpa = vpa(xSol)
ySolvpa = vpa(ySol)
fplot(xSolvpa,[-2.1 1.5])
hold on
fplot(t,ySolvpa,[-2.1 1.5])
but got the wrong plot. This is disconcerting. No error is given.
How to do?
MM

CHanging syntax corrects the error nicely and now the correct plot is plotted.
xSolvpa = vpa(xSol)
ySolvpa = vpa(ySol)
lims=[-1.1 1.5]
fplot(xSolvpa, lims)
hold on
fplot(ySolvpa, lims)

Related

Time varying input ODE solving

I'm trying to solve a set of ODE equations using ode45. A number of my parameters are already a function of time but I keep getting errors.
function odo
dx(1,1) = (vl+vr)/2*cos(x(3));
dx(2,1) = (vl+vr)/2*sin(x(3));
dx(3,1) = obz
where obz, vr and vl are each vectors e.g:
obz = sin(t), t = 0:dt:tf;
I use the following syntax:
[t, x1] = ode45(#(t,x) odo(t,x,b,obz,vr,vl), 0:dt:tf, [0;0;0]);
with R15 but keep getting the error:
Assignment has more non-singleton rhs dimensions than non-singleton subscripts
How to solve this issue?
You have some syntax errors here. First You can't just type function odo and let the MATLAB guess what it should do here. The second, you call t and x twice in your solver expressions. Be consistent, call function in ode solver this way:
t_span=1:1:12;
b=0.2;
[t, x] = ode45(#odo, t_span, [0;0;0], b);
You don't need to pass t and x manually, solver do it itself. Only the additional parameter b (however it is not in use, in your example). Also in declaration of function do as follow:
function dx = odo(t,x,b)
vr=sin(t); %Non-OD equations here
vl=cos(t);
obz=0.05*sin(t);
n=numel(x); %this generate a holder for results
dx=zeros(n,1);
dx(1) = (vl+vr)/2*cos(x(3)); %All ODEs formatted like this
dx(2) = (vl+vr)/2*sin(x(3));
dx(3) = obz;
As you can see, vr, vl, and obz are time-depended values, so need to be re-calculated in every step of solver. You have to place it in your solver function.

Unable to fit nonlinear curve to data in Matlab

I am trying to fit some data in Matlab to a Hill function of the form y = r^n/(r^n+K^n). I have data for r,y and I need to find K,n.
I tried two different approaches after reading the docs extensively - one uses fit from the CurveFitting Toolbox and the other uses lsqcurvefit from the Optimization Toolbox. I haven't had any success with either. What am I missing?
Here is my xdata and ydata:
xdata = logspace(-2,2,101);
ydata = [0.0981 0.1074 0.1177 0.1289 0.1411 0.1545 0.1692 0.1852 0.2027 ...
0.2219 0.2428 0.2656 0.2905 0.3176 0.3472 0.3795 0.4146 0.4528 ...
0.4944 0.5395 0.5886 0.6418 0.6994 0.7618 0.8293 0.9022 0.9808 ...
1.0655 1.1566 1.2544 1.3592 1.4713 1.5909 1.7183 1.8537 1.9972 ...
2.1490 2.3089 2.4770 2.6532 2.8371 3.0286 3.2272 3.4324 3.6437 ...
3.8603 4.0815 4.3065 4.5344 4.7642 4.9950 5.2258 5.4556 5.6833 ...
5.9082 6.1292 6.3457 6.5567 6.7616 6.9599 7.1511 7.3347 7.5105 ...
7.6783 7.8379 7.9893 8.1324 8.2675 8.3946 8.5139 8.6257 8.7301 ...
8.8276 8.9184 9.0029 9.0812 9.1539 9.2212 9.2834 9.3408 9.3939 ...
9.4427 9.4877 9.5291 9.5672 9.6022 9.6343 9.6638 9.6909 9.7157 ...
9.7384 9.7592 9.7783 9.7957 9.8117 9.8263 9.8397 9.8519 9.8630 ...
9.8732 9.8826];
'Fit' code:
HillEqn = '#(x,xdata)xdata.^x(1)./(xdata.^x(1)+x(2).^x(1))';
startPoints = [1 1];
fit(xdata',ydata',HillEqn,'Start',startPoints)
Error message:
Error using fittype>iTestCustomModelEvaluation (line 726)
Expression #(x,xdata)xdata.^x(1)./(xdata.^x(1)+x(2).^x(1)) is not a valid MATLAB
expression, has non-scalar coefficients, or cannot be evaluated:
Undefined function 'imag' for input arguments of type 'function_handle'.
'lsqcurvefit' code:
fun = #(x,xdata) xdata.^x(1)./(xdata.^x(1)+x(2).^x(1));
x0 = [1,1]; % Initial Parameter Estimates
x = lsqcurvefit(fun,x0,xdata,ydata);
Error message:
Error using snls (line 47)
Objective function is returning undefined values at initial point. lsqcurvefit cannot
continue.
First I think you need 3 variables to start from, because the hill function will be max of 1, and your data it is maxed at 10. So either normalize your data by doing ydata=ydata./max(ydata), or add a 3rd variable (which I did just for the demonstration). This is how I did it:
startPoints = [1 1 1];
s = fitoptions('Method','NonlinearLeastSquares',... %
'Lower',[0 0 0 ],...
'Upper',[inf inf inf],...
'Startpoint',startPoints);
HillEqn = fittype( 'x.^a1./(x.^a1+a2.^a1)*a3','options',s);
[ffun,gofrr] = fit(xdata(:),ydata(:),HillEqn);
yfit=feval(ffun,xdata(:)); %Fitted function
plot(xdata,ydata,'-bx',xdata,yfit,'-ro');
ffun =
General model:
ffun(x) = x.^a1./(x.^a1+a2.^a1)*a3
Coefficients (with 95% confidence bounds):
a1 = 1.004 (1.004, 1.004)
a2 = 0.9977 (0.9975, 0.9979)
a3 = 9.979 (9.978, 9.979)
Side note:
In your case what you really want to do is to transform you data by looking at Y=1./ydata instead, then fit, and then take another 1./Y to get the answer in the previous "hill" function representation. This is because your problem is bilinear in nature , so by going 1./ydata you get a bilinear relation, for which a polyfit of order 1 will do:
Y=1./ydata;
X = 1./xdata;
p=polyfit(X,Y,1);
plot(X,Y,'-bx',X,polyval(p,X),'-ro')

MatLab ode45 explanation

For a project I need to understand a matlab code, but as I am quite new I dont really understand what is happening. I have a function file and a script file.
Function:
function dxdt = sniffer_ode(t,x,par,tu)
X = x(1);
R = x(2);
k1 = par(1);
k2 = par(2);
k3 = par(3);
k4 = par(4);
S = interp1(tu(:,1),tu(:,2),t);
dxdt(1) = k3*S-k4*X;
dxdt(2) = k1*S-k2*X*R;
dxdt = dxdt(:); %dxdt should be column
and the script file:
%sniffer
close all
%initial conditions:
X0=0; R0=0;
x0=[X0 R0];
%parameters:
k1=1; k2=1; k3=1; k4=1;
par=[k1 k2 k3 k4];
%input:
tu=[ 0 , 0
1 , 0
1.01, 1
20 , 1];
[t,x] = ode45(#sniffer_ode,[0 20],x0, [],par,tu);
plot(t,x);
So the question is: What is happening? I also need to plot S in the same figure as X and R. How do I do this?
I appreciate your help!
This is a really basic Matlab question. There is tons of information about your requested topic. I think these slides will help you on the right path.
However, a quick explanation; the first code you provide is the function which describes your ordinary differential equation. This function always has to be of the form x' = f(t,x,...). Herein t is the time and x is the state. After the state (on the place of the dots ...) you can define other input parameters, such as is being done in your ode function. Furthermore, the interp1 function interpolates the data provided.
The second code you provide is the code you start within Matlab. Parameters are defined, after which the ordinary differential equation is solved and plotted.
If you have any further questions I would recommend that you first try to find your answer using a search engine.

Solving an ODE when the function is given as discrete values -matlab-

I have the following ODE:
x_dot = 3*x.^0.5-2*x.^1.5 % (Equation 1)
I am using ode45 to solve it. My solution is given as a vector of dim(k x 1) (usually k = 41, which is given by the tspan).
On the other hand, I have made a model that approximates the model from (1), but in order to compare how accurate this second model is, I want to solve it (solve the second ODE) by means of ode45. My problem is that this second ode is given discrete:
x_dot = f(x) % (Equation 2)
f is discrete and not a continuous function like in (1). The values I have for f are:
0.5644
0.6473
0.7258
0.7999
0.8697
0.9353
0.9967
1.0540
1.1072
1.1564
1.2016
1.2429
1.2803
1.3138
1.3435
1.3695
1.3917
1.4102
1.4250
1.4362
1.4438
1.4477
1.4482
1.4450
1.4384
1.4283
1.4147
1.3977
1.3773
1.3535
1.3263
1.2957
1.2618
1.2246
1.1841
1.1403
1.0932
1.0429
0.9893
0.9325
0.8725
What I want now is to solve this second ode using ode45. Hopefully I will get a solution very similar that the one from (1). How can I solve a discrete ode applying ode45? Is it possible to use ode45? Otherwise I can use Runge-Kutta but I want to be fair comparing the two methods, which means that I have to solve them by the same way.
You can use interp1 to create an interpolated lookup table function:
fx = [0.5644 0.6473 0.7258 0.7999 0.8697 0.9353 0.9967 1.0540 1.1072 1.1564 ...
1.2016 1.2429 1.2803 1.3138 1.3435 1.3695 1.3917 1.4102 1.4250 1.4362 ...
1.4438 1.4477 1.4482 1.4450 1.4384 1.4283 1.4147 1.3977 1.3773 1.3535 ...
1.3263 1.2957 1.2618 1.2246 1.1841 1.1403 1.0932 1.0429 0.9893 0.9325 0.8725];
x = 0:0.25:10
f = #(xq)interp1(x,fx,xq);
Then you should be able to use ode45 as normal:
tspan = [0 1];
x0 = 2;
xout = ode45(#(t,x)f(x),tspan,x0);
Note that you did not specify what values of of x your function (fx here) is evaluated over so I chose zero to ten. You'll also not want to use the copy-and-pasted values from the command window of course because they only have four decimal places of accuracy. Also, note that because ode45 required the inputs t and then x, I created a separate anonymous function using f, but f can created with an unused t input if desired.

Fitting model to data in matlab

i have some experimental data and a theoretical model which i would like to try and fit. i have made a function file with the model - the code is shown below
function [ Q,P ] = RodFit(k,C )
% Function file for the theoretical scattering from a Rod
% R = radius, L = length
R = 10; % radius in Å
L = 1000; % length in Å
Q = 0.001:0.0001:0.5;
fun = #(x) ( (2.*besselj(1,Q.*R.*sin(x)))./...
(Q.*R.*sin(x)).*...
(sin(Q.*L.*cos(x)./2))./...
(Q.*L.*cos(x)./2)...
).^2.*sin(x);
P = (integral(fun,0,pi/2,'ArrayValued',true))*k+C;
end
with Q being the x-values and P being the y-values. I can call the function fine from the matlab command line and it works fine e.g. [Q,P] = RodFit(1,0.001) gives me a result i can plot using plot(Q,P)
But i cannot figure how to best find the fit to some experimental data. Ideally, i would like to use the optimization toolbox and lsqcurvefit since i would then also be able to optimize the R and L parameters. but i do not know how to pass (x,y) data to lsqcurvefit. i have attempted it with the code below but it does not work
File = 30; % the specific observation you want to fit the model to
ydata = DataFiles{1,File}.data(:,2)';
% RAdius = linspace(10,1000,length(ydata));
% LEngth = linspace(100,10000,length(ydata));
Multiplier = linspace(1e-3,1e3,length(ydata));
Constant = linspace(0,1,length(ydata));
xdata = [Multiplier; Constant]; % RAdius; LEngth;
L = lsqcurvefit(#RodFit,[1;0],xdata,ydata);
it gives me the error message:
Error using *
Inner matrix dimensions must agree.
Error in RodFit (line 15)
P = (integral(fun,0,pi/2,'ArrayValued',true))*k+C;
Error in lsqcurvefit (line 199)
initVals.F = feval(funfcn_x_xdata{3},xCurrent,XDATA,varargin{:});
Caused by:
Failure in initial user-supplied objective function evaluation. LSQCURVEFIT cannot continue.
i have tried i) making all vectors/matrices the same length and ii) tried using .* instead. nothing works and i am giving the same error message
Any kind of help would be greatly appreciated, whether it is suggestion regading what method is should use, suggestions to my code or something third.
EDIT TO ANSWER Osmoses:
A really good point but i do not think that is the problem. just checked the size of the all the vectors/matrices and they should be alright
>> size(Q)
ans =
1 1780
>> size(P)
ans =
1 1780
>> size(xdata)
ans =
2 1780
>> size([1;0.001]) - the initial guess/start point for xdata (x0)
ans =
2 1
>> size(ydata)
ans =
1 1780
UPDATE
I think i have identified the problem. the function RodFit works fine when i specify the input directly e.g. [Q,P] = RodFit(1,0.001);.
however, if i define x0 as x0 = [1,0.001] i cannot pass x0 to the function
>> x0 = [1;0.001]
x0 =
1.0000
0.0010
>> RodFit(x0);
Error using *
Inner matrix dimensions must agree.
Error in RodFit (line 15)
P = (integral(fun,0,pi/2,'ArrayValued',true))*k+C;
The same happens if i use x0 = [1,0.001]
clearly, matlab is interpreting x0 as input for k only and attempts to multiplay a vector of length(ydata) and a vector of length(x0) which obviously fails.
So my problem is that i need to code so that lsqcurvefit understands that the first column of xdata and x0 is the k variable and the second column of xdata and x0 is the C variable. According to the documentation - Passing Matrix Arguments - i should be able to pass x0 as a matrix to the solver. The solver should then also pass the xdata in the same format as x0.
Have you tried (that's sometimes the mistake) looking at the orientation of your input data (e.g. if xdata & ydata are both row/column vectors?). Other than that your code looks like it should work.
I have been able to solve some of the problems. One mistake in my code was that the objective function did not use of vector a variables but instead took in two variables - k and C. changing the code to accept a vector solved this problem
function [ Q,P ] = RodFit(X)
% Function file for the theoretical scattering from a Rod
% R = radius, L = length
% Q = 0.001:0.0001:0.5;
Q = linspace(0.11198,4.46904,1780);
fun = #(x) ( (2.*besselj(1,Q.*R.*sin(x)))./...
(Q.*R.*sin(x)).*...
(sin(Q.*L.*cos(x)./2))./...
(Q.*L.*cos(x)./2)...
).^2.*sin(x);
P = (integral(fun,0,pi/2,'ArrayValued',true))*X(1)+X(2);
with the code above, i can define x0 as x0 = [1 0.001];, and pass that into RodFit and get a result. i can also pass xdata into the function and get a result e.g. [Q,P] = RodFit(xdata(2,:));
Notice i have changed the orientation of all vectors so that they are now row-vectors and xdata has size size(xdata) = 1780 2
so i thought i had solved the problem completely but i still run into problems when i run lsqcurvefit. i get the error message
Error using RodFit
Too many input arguments.
Error in lsqcurvefit (line 199)
initVals.F = feval(funfcn_x_xdata{3},xCurrent,XDATA,varargin{:});
Caused by:
Failure in initial user-supplied objective function evaluation. LSQCURVEFIT cannot continue.
i have no idea why - does anyone have any idea about why Rodfit recieves to many input arguments when i call lsqcurvefit but not when i run the function manual using xdata?