I am trying to solve the following least squares problem:
b(alpha)=A(alpha,beta)x(beta)
I am trying to use an alternative approach, which is to assume the functional form of x(beta) through the use of tunable parameters, say x(beta, a, c). How can I solve this problem in MATLAB for a least squares solution for those parameters?
I second the comments -this would be much easier if you gave a slightly more verbose description of your problem and most importantly add a minimal working example.
As far as I understand though, you want to solve a linear system of equations with some additional assumptions about the fitted parameters. This can be done by expressing them as an optimisation problem.
Here for example I've fitted a quadratic where the coefficients of x^0 and x^1 are both dependant on some other arbitrary parameter a (for this example a = 6 - that's what we're trying to recover from the data).
There are 2 different approaches plotted here - unconstrained and constrained optimisation. You can see that all of them approximate our data well, but only the constrained optimisation recovers a value of a close to 6 (5.728). Anyway, have a look at the code and I hope this helps with your problem somewhat. If you can, try to use the reduced number of parameters approach. It is always better to reduce your fitting problems to lower dimensional spaces if possible - much less risk of local minima and much faster solutions.
Here is the code:
close all; clear; clc;
%% Generate test data
x = 1:100;
rng(0); % Seed rng
% Polynomial where we know something about the parameters - we know that if
% the coefficient of x^0 is 'a'm then the coefficient of x^1 is (1-a).
a = 6;
y = a + (1-a).*x + 0.1*x.^2;
y = y + 30*randn(size(x)); % Add some noise
%% Fit with mrdivide and Vandermonde matrix
A = vander(x); A = A(:,end-2:end)';
b = y;
k1 = b/A;
%% Fit with an unconstrained optimiser
f = #(k) optimfun1(x,y,k);
k0 = [1 1 1]; % Starting point
k2 = fminsearch(f,k0);
%% Fit with a constrained optimiser
f = #(k) optimfun1(x,y,k);
k0 = [1 1 1];
Aeq = [0 1 1]; beq = 1; % Constrain k2 = 1 - k3 which is equivalent to k2+k3 = 1
k3 = fmincon(f,k0,[],[],Aeq,beq);
%% Fit with a reduced number of parameters
f = #(k) optimfun2(x,y,k);
k0 = [1 1];
k4 = fminsearch(f,k0);
k4 = [k4 1-k4(2)]; % Infer the last coeff.
%% Plot
plot(x,y,'ko');
hold on
plot(x,polyval(k1,x));
plot(x,polyval(k2,x));
plot(x,polyval(k3,x));
plot(x,polyval(k4,x));
legend('k^{dat} = [6.000 -5.000 0.100];',...
sprintf('k^{unc}_1 = [%.3f %.3f %.3f]',flipud(k1(:))),...
sprintf('k^{unc}_2 = [%.3f %.3f %.3f]',flipud(k2(:))),...
sprintf('k^{cns}_1 = [%.3f %.3f %.3f]',flipud(k3(:))),...
sprintf('k^{cns}_2 = [%.3f %.3f %.3f]',flipud(k4(:))),...
'location','northwest');
grid on;
xlabel('x');
ylabel('f(x)');
title(sprintf('f(x) = a + (1-a)x + 0.1x^2; a = %d',a));
function diff = optimfun1(x,y,k)
yfit = polyval(k,x);
dy = yfit-y;
diff = sum(dy.^2); % Sum of squared residuals
end
function diff = optimfun2(x,y,k)
k = [k 1-k(2)]; % Infer the last coeff.
yfit = polyval(k,x);
dy = yfit-y;
diff = sum(dy.^2);
end
Without knowing exactly how does the parameter works, it is difficult to figure out what to do. For example if the parameter is
x(beta, a, c) = a * x(beta) + c
Then your equation becomes
b(alpha)= A(alpha,beta) * (a * x(beta) + c)
b(alpha) - c*A(alpha,beta) = A(alpha,beta) * a * x(beta)
which then perhaps you can solve in the standard way (I'm treating b and A as numbers and x as the only variable here disregarding the alpha and beta). For more non-linear relation, it gets complex.
Related
Thanks in advance for your help. I'm not looking for an explicit solution to my problem, but rather to have my probably obvious errors pointed out.
I have been plugging away at solving a system of non-linear, first order ODEs in MATLAB. The system was solved numerically in this study: http://web.math.ku.dk/~moller/e04/bio/ludwig78.pdf
I have been following the documentation for ode45, and have code that runs.
I have done all of the work to understand and recreate the model from scratch. I presented the qualitative part for a class project. What I am doing now is taking that project a step farther by solving the system in MATLAB with runge-kutta (or any method that works). Finally, I want to dive into the theory behind the numerical analysis to find out why the chosen method converges.
Here is a plot of the numerically solved system, which I am trying to re-create:
I have found that I can create a plot with roughly the same shape, but there are several problems:
The time-scale over which the change occurs is three times that of the above plot.
The range of function values is is vastly wrong.
The desired shapes only occur if I tweak the initial conditions to
be significantly different than what is shown near t=0 above.
So what I'm looking for is a reason for these discrepancies. I've checked my system of ODEs and parameter values so many times my eyes are blurry. Perhaps I am missing something conceptually?
Code:
% System Parameters:
r_b = 1.52;
k_b = 355;
alph = 1.11;
bet = 43200;
r_e = 0.92;
k_e = 1;
p = 0.00195;
r_s = 0.095;
k_s = 25440;
tspan = [0 200];
init = [1 1 1];
[t, Y] = ode45(#(t,y) odefcn(t, y, r_b, k_b, alph, bet, r_e, k_e, p, r_s, k_s), tspan, init);
subplot(3,1,1);
plot(t,Y(:,1),'b');
title('Budworm Density');
subplot(3,1,2)
plot(t,Y(:,2),'g');
title('Branch Density');
subplot(3,1,3);
plot(t,Y(:,3),'r');
title('Foliage Condition');
function dydt = odefcn(t, y, r_b, k_b, alph, bet, r_e, k_e, p, r_s, k_s)
dydt = [ r_b*y(1)*(1 - y(1)/(k_b*y(2))) - bet*(y(1)^2/((alph*y(2))^2 + y(1)^2));
r_s*y(2)*(1 - (y(2)*k_e)/(k_s*y(3)));
r_e*y(3)*(1 - (y(3)/k_e)) - p*y(1)/y(2)
];
end
I don't see anything wrong with your code as such. But I think there are some subtleties involved in producing the figure which are not well explained in the paper.
1) The S axis is scaled (it says 'relative' in the label). I believe they've scaled S by k_s. I think you also need to scale the parameter p (set p = p*k_s) else the final term in the equation for E will be tiny and the E population won't decrease over the required timescales.
2) I think they must have enforced some lower limit on E, to avoid dividing by 0. You can see in the figure that E->0 first, but in your equation for S, if this happened then you would be dividing by 0 and the solver wouldn't converge.
Putting these together, the following slight modification of your code produces a result more similar to that in the paper:
% System Parameters:
r_b = 1.52;
k_b = 355;
alph = 1.11;
bet = 43200;
r_e = 0.92;
k_e = 1;
p = 0.00195;
r_s = 0.095;
k_s = 25440;
% Scale p with k_s
p = p*k_s;
tspan = [0 50]; % [0 200];
init = [1e-16 0.075*k_s 1]; % [1 1 1];
[t, Y] = ode45(#(t,y) odefcn(t, y, r_b, k_b, alph, bet, r_e, k_e, p, r_s, k_s), tspan, init);
% To scale before plotting, so everything fits on a 0->1 y axis.
maxB = 500;
S_scale = k_s;
figure('Position', [200 200 1000 600]);
hold on;
plot(t,Y(:,1)/maxB,'b');
plot(t,Y(:,2)/(S_scale),'g');
plot(t,Y(:,3),'r');
ylim([0, 1]);
hold off;
box on;
legend({['Budworm Density, B / ', num2str(maxB)], 'Branch Density, S / 0.75', 'Foliage Condition, E'}, ...
'Location', 'eastoutside')
function dydt = odefcn(t, y, r_b, k_b, alph, bet, r_e, k_e, p, r_s, k_s)
% Place lower limit on E
E = max(y(3), 1e-5);
dydt = [ r_b*y(1)*(1 - y(1)/(k_b*y(2))) - bet*(y(1)^2/((alph*y(2))^2 + y(1)^2));
r_s*y(2)*(1 - (y(2)*k_e)/(k_s*E));
r_e*E*(1 - (E/k_e)) - p*y(1)/y(2)
];
end
There is a lot of sensitivity to the initial conditions.
A further tweak gets you closer still to the original figure, but I'm not sure if this is just a hack: in the first equation, replace k_b*y(2) with just k_b. Without this, the Budworm density becomes too big before decreasing. The new plot is below.
I want to replicate a figure from this article. More specifically, I want to replicate Figure number 4, which I believe is the representation of Equation 9.
So far I have come up with this code:
% implementing equation 9 and figure 4
step = 0.01; t = 1:step:3600;
d = 3; % dimension
N = 8000; % number of molecules
H = 0.01; % H = [0.01,0.1,1] is in mol/micrometer^3
H = H*6.02214078^5; % hence I scaled the Avogadro's number (right or wrong?)
D = 10; % diffusion coefficient in micrometer^2/sec
u(1) = 1./(1.^(d/2)); % inner function in equation 9; first pulse
for i = 2:numel(t)/1000
u(i) = u(i-1)+(1./(i.^(d/2))); % u-> the pulse number
lmda(i) = (1/(4*pi*D))*((N/(H)).*sum(u)).^(2/d);
end
figure;plot(lmda)
But I am not able to replicate it.
Equation 9
For details on the parameters, refer to the above code. The authors did mention that the summation in equation 9 is a Reimann Zeta series. Wonder if that has anything to do with the result?
Figure 4, which I am trying to replicate:
Could someone kindly tell me the mistake I am making?
P.s: This is not a homework.
Problem 1: You think you are scaling by Avogadro's number on this line
H = H*6.02214078^5;
In fact, you're scaling by approximately 7920=6.022^5. If you wanted to scale by the Avogadro number then you should do:
H = H * 6.02214078e23 % = 6.02214078 * 10^23 : the Avogadro number
Problem 2: You aren't plotting against t, you're plotting against the sample number which doesn't really make sense (unless your t happened to be in integer seconds). Remove the /1000 from your loop
for i = 2:numel(t)
% ...
end
% Then plot
plot(t, lmda)
At this stage we can see something is really wrong. Now that we're scaling by the correct Avo number, the orders of magnitude are way out. I suggest that you trust the H in figure 4 and the H in equation 9 are the same H, it would be very confusing if the author intended anything different!
On that basis, I would suggest you are using the wrong D, N, or time between pulses. I've set up the pulse timing a bit clearer in my code below. I've also streamlined your loop somewhat using vectorisation, and removed the H scaling.
If you tweak it so dtPulses=100 as well as D=100, then the plots are almost identical. You perhaps need to consider how these two numbers affect the result...
% implementing equation 9 and figure 4
d = 3; % dimension
N = 8000; % number of molecules
D = 100; % diffusion coefficient in micrometer^2/sec
dtPulses = 10; % Seconds between pulses
tPulses = 1:dtPulses:3600; % Time array to plot against
nt = numel(tPulses);
i = 1:nt; % pulse numbers
u = 1 ./ (i.^(d/2)); % inner function in equation 9: individual pulse
for k = 2:nt % Running sum
u(k) = u(k-1)+u(k);
end
% Now plot for different H (mol/micrometer^3)
H = [0.01, 0.1, 1];
figure; hold on; linestyles = {':k', '--k', '-k'};
for nH = 1:3
lmda = ((1/(4*pi*D))*(N/H(nH)).*u).^(2/d);
plot(tPulses, lmda, linestyles{nH}, 'linewidth', 2)
end
Given
d²x/dt² + a·dx/dt + 7.9·x³ = 3.2·sin(xt)
with initial conditions
x(0) = +1.2
dx/dt(0) = −3.3
x(2.3) = −0.6
Find numerically all the possible values of a, each accurate to at least 3 significant digits.
Is there any method other than brute force for solving this?
As far as I can see, it is not possible to solve this problem as stated.
Here is what I did. I implemented your problem in a reasonably general way:
%{
Find all 'a' for which
d²x/dt² + a·dx/dt + 7.9·x³ - 3.2·sin(xt) = 0
with initial conditions
x(0) = +1.2
dx/dt(0) = −3.3
x(2.3) = −0.6
%}
function odetest
% See how the function search_a(a) behaves around a = 0:
test_as = 0 : 0.1 : 10;
da = zeros(size(test_as));
for ii = 1:numel(test_as)
da(ii) = search_a(test_as(ii)); end
figure(100), clf, hold on
plot(test_as, da)
axis tight
xlabel('a')
ylabel('|x(2.3) - 0.6|')
% Roughly cherry-pick some positive values, improve the estimate, and
% plot the solutions
opt = optimset('tolfun',1e-14, 'tolx',1e-12);
plot_x(fminsearch(#search_a, 0.0, opt), 1)
plot_x(fminsearch(#search_a, 1.4, opt), 2)
plot_x(fminsearch(#search_a, 3.2, opt), 3)
% Plot single solution
function plot_x(a,N)
[xt, t] = solve_ode(a);
figure(N), clf, hold on
plot(t,xt)
plot(2.3, -0.6, 'rx', 'markersize', 20)
title (['x(t) for a = ' num2str(a)])
xlabel('t')
ylabel('x(t)')
end
end
% Solve the problem for a value a, and return the difference between the
% actual value and desired value (-0.6)
function da = search_a(a)
a_desired = -0.6;
xt = solve_ode(a);
da = abs(xt(end) - a_desired);
end
% Solve the problem for any given value of a
function [xt, t] = solve_ode(a)
y0 = [1.2 -3.3];
tfinal = 2.3;
opt = odeset('AbsTol',1e-12, 'RelTol',1e-6);
[t,yt] = ode45(#(y,t) odefun(y,t,a), [0 tfinal], y0, opt);
xt = yt(:,1); % transform back to x(t)
end
% Most ODE solvers solve first-order systems. This is not a problem for a
% second-order system, because if we make the transformation
%
% y(t) = [ x (t)
% x'(t) ]
%
% Then we can solve for
%
% y'(t) = [ x' (t)
% x''(t) ] <- the second-order homogeneous DE
%
function dydt = odefun(t,y,a)
dydt = [y(2)
-a*y(2) - 7.9*y(1)^3 + 3.2*sin(y(1)*t)];
end
The first part gave me this figure:
Some further investigation suggests that this only grows for larger a.
This figure gave rise to the initial estimates a = [0, 1.4, 3.2], which I improved via fminsearch() and plotted the solutions of:
So, that probably enables you to hand in your homework :)
However, why I say it's impossible to answer the question as stated, is because this is what the first plot looks like for negative a:
The oscillatory behavior seems to continue indefinitely, and the spacing in between the zeros seems to decrease in a non-predictable way.
Now, my university days are long behind me, and I'm not so well-versed in ODE theory anymore. Perhaps there is a pattern to it, that just doesn't show because of numerical problems. Or perhaps the oscillation stops after some value, never to return again. Or perhaps another zero turns up at a = +1053462664212.25.
I can't prove any of these things, I just know how to brute-force it; the rest is up to you.
The equations can be found here. As you can see it is set of 8 scalar equations closed to 3 matrix ones. In order to let Matlab know that equations are matrix - wise, I declare variable time dependent vector functions as:
syms t p1(t) p2(t) p3(t)
p(t) = symfun([p1(t);p2(t);p3(t)], t);
p = formula(p(t)); % allows indexing for vector p
% same goes for w(t) and m(t)...
Known matrices are declared as follows:
A = sym('A%d%d',[3 3]);
Fq = sym('Fq%d%d',[2 3]);
Im = diag(sym('Im%d%d',[1 3]));
The system is now ready to be modeled according to guide:
eqs = [diff(p) == A*w + Fq'*m,...
diff(w) == -Im*p,...
Fq*w == 0];
vars = [p; w; m];
At this point, when I try to reduce index (since it equals 2), I receive following error:
[DAEs,DAEvars] = reduceDAEIndex(eqs,vars);
Error using sym/reduceDAEIndex (line 95)
Expecting as many equations as variables.
The error would not arise if we had declared all variables as scalars:
syms A Im Fq real p(t) w(t) m(t)
Quoting symfun documentation (tips section):
Symbolic functions are always scalars, therefore, you cannot index into a function.
However it is hard for me to believe that it's not possible to solve these equations matrix - wise. Obviously, one can expand it to 8 scalar equations, but the multi body system concerned here is very simple and the aim is to be able to solve complex ones - hence the question: is it possible to solve matrix DAE in Matlab, and if so - what has to be fixed in order for this to work?
Ps. I have another issue with Matlab DAE solver: input variables (known coefficient functions) for my model are time variant. As far as example is concerned, they are constant in all domain, however for my problem they change in time. This problem has been brought out here. I would be grateful if you referred to it, should you have any solution.
Finally, I managed to find correct syntax for this problem. I made a mistake of treating matrix variables (such as A, Fq) as a single entity. Below I present code that utilizes matrix approach and solves this particular DAE:
% Define symbolic variables.
q = sym('q%d',[3 1]); % state variables
a = sym('a'); k = sym('k'); % constant parameters
t = sym('t','real'); % independent variable
% Define system variables and group them in vectors:
p1(t) = sym('p1(t)'); p2(t) = sym('p2(t)'); p3(t) = sym('p3(t)');
w1(t) = sym('w1(t)'); w2(t) = sym('w2(t)'); w3(t) = sym('w3(t)');
m1(t) = sym('m1(t)'); m2(t) = sym('m2(t)');
pvect = [p1(t); p2(t); p3(t)];
wvect = [w1(t); w2(t); w3(t)];
mvect = [m1(t); m2(t)];
% Define matrices:
mass = diag(sym('ms%d',[1 3]));
Fq = [0 -1 a;
0 0 1];
A = [1 0 0;
0 1 a;
0 a -q(1)*a] * k;
% Define sets of equations and aggregate them into one set:
set1 = diff(pvect,t) == A*wvect + Fq'*mvect;
set2 = mass*diff(wvect,t) == -pvect;
set3 = Fq*wvect == 0;
eqs = [set1; set2; set3];
% Close all system variables in one vector:
vars = [pvect; wvect; mvect];
% Reduce index of the system and remove redundnat equations:
[DAEs,DAEvars] = reduceDAEIndex(eqs,vars);
[DAEs,DAEvars] = reduceRedundancies(DAEs,DAEvars);
[M,F] = massMatrixForm(DAEs,DAEvars);
We receive very simple 2x2 ODE for two variables p1(t) and w1(t). Keep in mind that after reducing redundancies we got rid of all elements from state vector q. This means that all left variables (k and mass(1,1)) are not time dependent. If there had been time dependency of some variables within the system, the case would have been much harder to solve.
% Replace symbolic variables with numeric ones:
M = odeFunction(M, DAEvars,mass(1,1));
F = odeFunction(F, DAEvars, k);
k = 2000; numericMass = 4;
F = #(t, Y) F(t, Y, k);
M = #(t, Y) M(t, Y, numericMass);
% set the solver:
opt = odeset('Mass', M); % Mass matrix of the system
TIME = [1; 0]; % Time boundaries of the simulation (backwards in time)
y0 = [1 0]'; % Initial conditions for left variables p1(t) and w1(t)
% Call the solver
[T, solution] = ode15s(F, TIME, y0, opt);
% Plot results
plot(T,solution(:,1),T,solution(:,2))
Please find the data in the link below, or if you can send me your private email, I can send you the data
https://dl.dropboxusercontent.com/u/5353938/test_matlab_lefou.xlsx
In the excel sheet, the first column is y, the second is x and the third is t, I hope this will make things much more clear, and many thanks for the help.
I need to use the following model because it is the one that fits best my data, but what I don't know is how to find the best values of a and b, that will allow me to get the best fit, (I can attach a file if you need the values), I already have the values of y, x and t:
y= a*sqrt(x).exp(b.t)
Thanks
Without the dependency on the curve fitting toolbox, this problem can also be solved by using fminsearch. I first generate some data, which you already have but didn't share with us. An initial guess on the parameters a and b must be made (p0). Then I do the optimiziation by minizmizing the squared errors between data and fit resulting in the vector p_fit, which contains the optimized parameters for a and b. In the end, the result is visualized.
% ----- Generating some data for x, y and t (which you already got)
N = 10; % num of data points
x = linspace(0,5,N);
t = linspace(0,10,N);
% random parameters
a = rand()*5; % a between 0 and 5
b = (rand()-1); % b between -1 and 0
y = a*sqrt(x).*exp(b*t) + rand(size(x))*0.1; % noisy data
% ----- YOU START HERE WITH YOUR PROBLEM -----
% put x and t into a 2 row matrix for simplicity
D(1,:) = x;
D(2,:) = t;
% create model function with parameters p(1) = a and p(2) = b
model = #(p, D) p(1)*sqrt(D(1,:)).*exp(p(2)*D(2,:));
e = #(p) sum((y - model(p,D)).^2); % minimize squared errors
p0 = [1,-1]; % an initial guess (positive a and probably negative b for a decay)
[p_fit, r1] = fminsearch(e, p0); % Optimize
% ----- VISUALIZATION ----
figure
plot(x,y,'ko')
hold on
X = linspace(min(x), max(x), 100);
T = linspace(min(t), max(t), 100);
plot(X, model(p_fit, [X; T]), 'r--')
legend('data', sprintf('fit: y(t,x) = %.2f*sqrt(x)*exp(%.2f*t)', p_fit))
The result can look like
UPDATE AFTER MANY MANY COMMENTS
Your data are column vectors, my solution used row vectors. The error occured when the errorfunction tryed to compute the difference of a column vector (y) and a row-vector (result of the model-function). Easy hack: make them all to row vectors and use my approach. The result is: a = 0.5296 and b = 0.0013.
However, the Optimization depends on the initial guess p0, you might want to play around with it a little bit.
clear variables
load matlab.mat
% put x and t into a 2 row matrix for simplicity
D(1,:) = x;
D(2,:) = t;
y = reshape(y, 1, length(y)); % <-- also y is a row vector, now
% create model function with parameters p(1) = a and p(2) = b
model = #(p, D) p(1)*sqrt(D(1,:)).*exp(p(2)*D(2,:));
e = #(p) sum((y - model(p,D)).^2); % minimize squared errors
p0 = [1,0]; % an initial guess (positive a and probably negative b for a decay)
[p_fit, r1] = fminsearch(e, p0); % Optimize
% p_fit = nlinfit(D, y, model, p0) % as a working alternative with dependency on the statistics toolbox
% ----- VISUALIZATION ----
figure
plot(x,y,'ko', 'markerfacecolor', 'black', 'markersize',5)
hold on
X = linspace(min(x), max(x), 100);
T = linspace(min(t), max(t), 100);
plot(X, model(p_fit, [X; T]), 'r-', 'linewidth', 2)
legend('data', sprintf('fit: y(t,x) = %.2f*sqrt(x)*exp(%.2f*t)', p_fit))
The result doesn't look too satisfying though. But that mainly is because of your data. Have a look here:
With the cftool-command (curve fitting toolbox) you can fit to your own functions, returning the variables that you need (a,b). Make sure your x-data and y-data are in separate variables. you can also specify weights for your measurements.