Using arrayfun to solve a equation system (with vpasolve) MatLab - matlab

I am struggling with solving a problem as efficient as possible.
I have two equations and want to solve them together. Furthermore, I want to solve them for four different cases. I can solve them independently altering the code for each case. I can also solve them by accessing a vector that contains the desired value for one of them (e.g., the first value of A) by using arrayfun. However I can't manage to do all of them together.
My code:
clc
clear all
close all
% scalar parameters
g = 9.807; % Gravity constant, m/s2
d = 1.225; % Density of air, kg/m3
x = 1000; % Height, m
% vector parameters
%t = 0:2:10; % time, seconds
A = [1.2; 1.7; 1.8; 0.3]; % Area, m^2
m = [82; 84; 90; 25]; % Mass, kg
Cd = [0.3; 1.14; 0.29; 0.045]; % Drag coefficient, -
syms v t
eqn1 = 2*m./(A.*Cd*d).* log(abs(cosh(t.*sqrt(A.*Cd*d*g./(2*m))))) == x;
eqn2 = (2*g*m./(d*A.*Cd)).*tanh(t.* sqrt((g*d*Cd.*A)./(2*m))) - v == 0;
eqns=[eqn1 eqn2];
variables = [v t];
result = arrayfun(#vpasolve, eqns, 'uniform', 0)
%disp('v='),disp(eval(v));
%disp('t='),disp(eval(t));
I get a result for t (which is weirdly negative and I don't know why), but I only get a {1×1 struct} for v, which I don't want. I know I can solve this also by using a for loop, but I wanted to make it more efficient.
I tried the code written above and solved it in various forms, however not as desired.

The way you have it implemented you're effectively trying to solve four equations with two unknowns, which isn't supported by vpasolve except in the case of polynomial systems.
It looks like you're trying to solve the same system for four different combinations of values for the parameters A, m, and Cd. The easiest way to do that is to just break this out as a for loop (there is simply no benefit to using arrayfun here – speed or otherwise). You'll need to create some temporary symbolic variable for the parameter (there are a few ways this could be done). Here is example code to do just that:
% scalar parameters
g = 9.807; % Gravity constant, m/s2
d = 1.225; % Density of air, kg/m3
x = 1000; % Height, m
% vector parameters
%t = 0:2:10; % time, seconds
A = [1.2; 1.7; 1.8; 0.3]; % Area, m^2
m = [82; 84; 90; 25]; % Mass, kg
Cd = [0.3; 1.14; 0.29; 0.045]; % Drag coefficient, -
syms v t real
syms A_sym m_sym Cd_sym real % Temporary symbolic variables
eqn1 = 2*m_sym./(A_sym.*Cd_sym*d).* log(abs(cosh(t.*sqrt(A_sym.*Cd_sym*d*g./(2*m_sym))))) == x;
eqn2 = (2*g*m_sym./(d*A_sym.*Cd_sym)).*tanh(t.* sqrt((g*d*Cd_sym.*A_sym)./(2*m_sym))) - v == 0;
eqns=[eqn1 eqn2];
variables = [v t];
n = length(A); % Number of prameter sets
result = cell(n,1); % Preallocate resultant cell array
for i = 1:n
% Substitute in numeric values for i-th parameter set
eqns_sub = subs(eqns,{A_sym,m_sym,Cd_sym},{A(i),m(i),Cd(i)});
% Solve and store in cell array
result{i} = vpasolve(eqns_sub,variables);
end
The output, result, is a 4-by-1 cell array of structs with your two variables, v and t, as VPA-valued fields for each.
You could replace the last few lines with the following if what you want is a numeric array as the output (this assumes that only one solution is found for each parameter set):
n = length(A); % Number of prameter sets
result = zeros(n,length(variables)); % Preallocate resultant array
for i = 1:n
% Substitute in numeric values for i-th parameter set
eqns_sub = subs(eqns,{A_sym,m_sym,Cd_sym},{A(i),m(i),Cd(i)});
% Solve, convert to double precision, and store in array
out = vpasolve(eqns_sub,variables);
result(i,:) = double([out.v out.t]);
end

Related

Matlab 'Matrix dimensions must agree' ode23s

The following is my code. I try to model PFR in Matlab using ode23s. It works well with one component irreversible reaction. But when extending more dependent variables, 'Matrix dimensions must agree' problem shows. Have no idea how to fix it. Is possible to use other software to solve similar problems?
Thank you.
function PFR_MA_length
clear all; clc; close all;
function dCdt = df(t,C)
dCdt = zeros(N,2);
dCddt = [0; -vo*diff(C(:,1))./diff(V)-(-kM*C(2:end,1).*C(2:end,2)-kS*C(2:end,1))];
dCmdt = [0; -vo*diff(C(:,2))./diff(V)-(-kM*C(2:end,1).*C(2:end,2))];
dCdt(:,1) = dCddt;
dCdt(:,2) = dCmdt;
end
kM = 1;
kS = 0.5; % assumptions of the rate constants
C0 = [2, 2]; % assumptions of the entering concentration
vo = 2; % volumetric flow rate
volume = 20; % total volume of reactor, spacetime = 10
N = 100; % number of points to discretize the reactor volume on
init = zeros(N,2); % Concentration in reactor at t = 0
init(1,:) = C0; % concentration at entrance
V = linspace(0,volume,N)'; % discretized volume elements, in column form
tspan = [0 20];
[t,C] = ode23s(#(t,C) df(t,C),tspan,init);
end
'''
You can put a break point on the line that computes dCddt and observe that the size of the matrices C and V are different.
>> size(C)
ans =
200 1
>> size(V)
ans =
100 1
The element-wise divide operation, ./, between these two variables would then result in the error that you mentioned.
Per ode23s's help, the output of the call to dCdt = df(t,C) needs to be a vector. However, you are returning a matrix of size 100x2. In the next call to the same function, ode32s converts it to a vector when computing the value of C, hence the size 200x1.
In the GNU octave interpretation of Matlab behavior, one has to explicitly make sure that the solver only sees flat one-dimensional state vectors. These have to be translated forward and back in the application of the model.
Explicitly reading the object A as flat array A(:) forgets the matrix dimension information, these can be added back with the reshape(A,m,n) command.
function dCdt = df(t,C)
C = reshape(C,N,2);
...
dCdt = dCdt(:);
end
...
[t,C] = ode45(#(t,C) df(t,C), tspan, init(:));

Why is my third MATLAB function outputing only zeros when using ode45?

I need to model negative, positive and simple regulation of a gene for my systems biology class using MATLAB. The problem is that the functions for negative and simple regulation work but the positive regulation function is only outputting zeros.
My script is as follows:
% Simulation of simple regulation, negative autoregulation and positive
% autoregulation
% Define constants
global a b K n
a = 1;
b = 1;
K = 0.5;
n = 2; % Hill coefficient
% Simulation time
tspan = [0,10];
% Initial condition
X0 = 0;
% Run simulations
[t1,X1] = ode45(#autoregulation_f0,tspan,X0); % Simple regulation
[t2,X2] = ode45(#autoregulation_f1,tspan,X0); % Negative autoregulation
[t3,X3] = ode23(#autoregulation_f2,tspan,X0); % Positive autoregulation
% Plot results
figure;
plot(t1,X1,t2,X2,t3,X3);
legend('simple','negative','Location','southeast');
And my functions are:
function dxdt = autoregulation_f0(t,X)
global a b
dxdt = b - a*X;
end
function dxdt = autoregulation_f1(t,X)
global a b K n
dxdt = b/(1+(X^n)/(K^n)) - a*X;
end
function dxdt = autoregulation_f2(t,X)
global a b K n
dxdt = b*X.^n./(K.^n+X.^n) + a*X;
end
The third function "autoregulation_f2(t,X)" is the one that outputs zeros and therefore when plotting the graph I just get a straight line.
Does anyone know what could be causing this?
Thanks in advance!
It looks to be the correct result for the given function. Your provided dxdt has an X in every term. The initial X0=0 will result in dxdt=0, giving you no change in X. As a result you just end up with a flat line.

Solve matrix DAE system in matlab

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))

Differentiation from FFT finding extrema

I'm trying to find zeros of a function. See my code below.
Because fft expects a numerical array, I didn't define the symbolic function to use fzero.
However, this approach is not accurate and depend on step. Do you have a better idea?
step=2000;
x=0:pi/step:2*pi;
y= 4+5*cos(10*x)+20*cos(40*x)+cos(100*x);
fy = fft(y');
fy(1:8) =0;
fy(12:end-10)=0;
fy(end-6:end)=0;
ffy = ifft(fy);
t=diff(ffy);
x=0:pi/step:2*pi-pi/step;
plot(x,t)
indices= find(t<5e-4 & t>-5e-4);
You could proceed along the array t and look for points where the values change sign. That would indicate the presence of a zero.
Actaully, MATLAB's fzero function uses a similar method. You said you didn't use it because you required an array, rather than an anonymous function, but you could convert the array into an anonymous function using simple linear interpolation like so:
func = #(k) interp1(x,t,k); % value of t(x) interpolated at x=k
fzero(func,initial_value);
EDIT : Just to clarify what I mean. If you have an array t and you want to find its zeros...
f = 5; % frequency of wave in Hz
x = 0:0.01:1; % time index
t = cos( 2*pi*f*x ); % cosine wave of frequency f
zeroList = []; % start with an empty list of zeros
for i = 2:length(t) % loop through the array t
current_t = t(i); % current value of t
previous_t = t(i-1); % previous value of t
if current_t == 0 % the case where the zero is exact
newZero = x(i);
zeroList = [zeroList,newZero];
elseif current_t*previous_t < 0 % a and b have opposite sign if a*b is -ve
% do a linear interpolation to find the zero (solve y=mx+b)
slope = (current_t-previous_t)/(x(i)-x(i-1));
newZero = x(i) - current_t/slope;
zeroList = [zeroList,newZero];
end
end
figure(1); hold on;
axis([ min(x) max(x) -(max(abs(t))) (max(abs(t))) ]);
plot(x,t,'b');
plot(x,zeros(length(x)),'k-.');
scatter(zeroList,zeros(size(zeroList)),'ro');
The zeros I get are correct:

Matlab find the best constants for a fitting model

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.