Matlab: How to solve the system of nonlinear equations with additional parameters? - matlab

I would like to create a function that finds the parameters p and q of Bass diffusion model, given the data of two time periods.
The model (equation) is the following:
n(T) = p*m + (q-p)*n(T-1) + q/m*n(T-1)^2
where
n(T) = number of addoptions occuring in period T
n(T-1) = number of cumulative adoptions that occured before T
p = coefficient of innovation
q = coefficient of imitation
m = number of eventual adopters
for example if m = 3.000.000
and the data for the years below is the following:
2000: n(T) = 820, n(T-1) = 0
2005: n(T) = 25000, n(T-1) = 18000
then the following equation system has to be solved (in order to determine the values of p and q):
p*m + (q-p)*0 + q/3.000.000 * 0^2 == 820
p*m + (q-p)*18000 + q/3.000.000 * 18000^2 == 25000
By following Matlab documentation I tried to create a function Bass:
function F = Bass(m, p, q, cummulativeAdoptersBefore)
F = [p*m + (q-p)*cummulativeAdoptersBefore(1) + q/m*cummulativeAdoptersBefore(1).^2;
p*m + (q-p)*cummulativeAdoptersBefore(2) + q/m*cummulativeAdoptersBefore(2).^2];
end
Which should be used in fsolve(#Bass,x0,options) but in this case m, p, q, cummulativeAdoptersBefore(1), and cummulativeAdoptersBefore(2) should be given in x0 and all variables would be considered as unknown instead of just the latter two.
Does anyone know how to solve the system of equations such as above?
Thank you!

fsolve() seeks to minimize the function you supply as argument. Thus, you have to change your equations to
p*m + (q-p)*0 + q/3.000.000 * 0^2 - 820 == 0
p*m + (q-p)*18000 + q/3.000.000 * 18000^2 - 25000 == 0
and in Matlab syntax
function F = Bass(m, p, q, cumulativeAdoptersBefore, cumulativeAdoptersAfter)
F = [p*m + (q-p)*cumulativeAdoptersBefore(1) ...
+ q/m *cumulativeAdoptersBefore(1).^2
- cumulativeAdoptersAfter(1);
p*m + (q-p)*cumulativeAdoptersBefore(2) ...
+ q/m *cumulativeAdoptersBefore(2).^2
- cumulativeAdoptersAfter(2)];
end
Note: There is a typo in your Bass function (multiplication instead of sum).
Now you have a function, which takes more parameters than there are unkowns.
One option is to create an anonymous function, which only takes the unknowns as arguments and to fix the other parameters via a closure.
To fit the unkowns p and q, you could use something like
cumulativeAdoptersBefore = [0, 1800];
cumulativeAdoptersAfter = [820, 25000];
m = 3e6;
x = [0, 0]; %# Probably, this is no good starting guess.
xopt = fsolve(#(x) Bass(m, x(1), x(2), cumulativeAdoptersBefore, cumulativeAdoptersAfter), x0);
So fsolve() sees a function taking only a single argument (a vector with two elements) and it also returns a vector value.

Related

I keep getting an error trying implementing midpoint method solving ODE

I am trying to implement Midpoint formulas y[n+1/2] = y[n] + h/2 f (x[n], y[n]) and y[n+1] = y[n] + h *f (x[n] + h/2, y[n + 1/2])
so it solves ODE using midpoint method.
My function is
function [ x, y ] = Midpoint_ODE ( f, xRange, yInitial, numSteps )
% f = name of file with function
% xrange Interval
% x(1) first meaning of x
% x(2) second meaning of x
x=zeros(numSteps+1,1);
x(1) = xRange(1);
h = ( xRange(2) - xRange(1) ) / numSteps; % calculated step size
y(1,:) = transpose(yInitial);
for n = 1 : numSteps
y(n+0.5,:)= (y(n) + (h * 0.5)*(transpose(feval( f, x(n), y(n)))));
y(n+1,:) = y(n,:) + h * transpose(feval(f, x(n)+ (h/2), y(n+0.5,:))); %evaluating the function
end
But I get an error :
**Index in position 1 is invalid. Array indices must be positive integers or logical values.
Error in Midpoint_ODE (line 11)Index in position 1 is invalid. Array indices must be positive integers or logical values.
Error in Midpoint_ODE (line 11)**
I checked it a couple of times, and can't get what's wrong and if I missed some logical piece.
You do not need to keep the half-step value. Thus the easiest is to not have in in the list of values
for n = 1 : numSteps
yhalfstep = (y(n,:) + (h * 0.5)*(transpose(feval( f, x(n), y(n,:)))));
y(n+1,:) = y(n,:) + h * transpose(feval( x(n)+ (h/2), yhalfstep));
end
Also remember that in matlab and similar, a single-index access to a multi-dimensional array gives back the element of the flattened array (column first). That is, in a=[ 1,2;3,4;5,6] you get from a(3) the number 5 as the 3rd element in the first column, while a(3,:) gives the 3rd row [5,6].

Series expansion of a function about infinity - how to return coefficients of series as a Matlab array?

This question is connected to this one. Suppose again the following code:
syms x
f = 1/(x^2+4*x+9)
Now taylor allows the function f to be expanded about infinity:
ts = taylor(f,x,inf,'Order',100)
But the following code
c = coeffs(ts)
produces errors, because the series does not contain positive powers of x (it contains negative powers of x).
In such a case, what code should be used?
Since the Taylor Expansion around infinity was likely performed with the substitution y = 1/x and expanded around 0, I would explicitly make that substitution to make the power positive for use on coeffs:
syms x y
f = 1/(x^2+4x+9);
ts = taylor(f,x,inf,'Order',100);
[c,ty] = coeffs(subs(ts,x,1/y),y);
tx = subs(ty,y,1/x);
The output from taylor is not a multivariate polynomial, so coeffs won't work in this case. One thing you can try is using collect (you may get the same or similar result from using simplify):
syms x
f = 1/(x^2 + 4*x + 9);
ts = series(f,x,Inf,'Order',5) % 4-th order Puiseux series of f about 0
c = collect(ts)
which returns
ts =
1/x^2 - 4/x^3 + 7/x^4 + 8/x^5 - 95/x^6
c =
(x^4 - 4*x^3 + 7*x^2 + 8*x - 95)/x^6
Then you can use numden to extract the numerator and denominator from either c or ts:
[n,d] = numden(ts)
which returns the following polynomials:
n =
x^4 - 4*x^3 + 7*x^2 + 8*x - 95
d =
x^6
coeffs can then be used on the numerator. You may find other functions listed here helpful as well.

Matlab - Unexpected Results from Differential Equation Solver Ode45

I am trying to solve a differential equation with the ode solver ode45 with MATLAB. I have tried using it with other simpler functions and let it plot the function. They all look correct, but when I plug in the function that I need to solve, it fails. The plot starts off at y(0) = 1 but starts decreasing at some point when it should have been an increasing function all the way up to its critical point.
function [xpts,soln] = diffsolver(p1x,p2x,p3x,p1rr,y0)
syms x y
yp = matlabFunction((p3x/p1x) - (p2x/p1x) * y);
[xpts,soln] = ode45(yp,[0 p1rr],y0);
p1x, p2x, and p3x are polynomials and they are passed into this diffsolver function as parameters.
p1rr here is the critical point. The function should diverge after the critical point, so i want to integrate it up to that point.
EDIT: Here is the code that I have before using diffsolver, the above function. I do pade approximation to find the polynomials p1, p2, and p3. Then i find the critical point, which is the root of p1 that is closest to the target (target is specified by user).
I check if the critical point is empty (sometimes there might not be a critical point in some functions). If its not empty, then it uses the above function to solve the differential equation. Then it plots the x- and y- points returned from the above function basically.
function error = padeapprox(m,n,j)
global f df p1 p2 p3 N target
error = 0;
size = m + n + j + 2;
A = zeros(size,size);
for i = 1:m
A((i + 1):size,i) = df(1:(size - i));
end
for i = (m + 1):(m + n + 1)
A((i - m):size,i) = f(1:(size + 1 - i + m));
end
for i = (m + n + 2):size
A(i - (m + n + 1),i) = -1;
end
if det(A) == 0
error = 1;
fprintf('Warning: Matrix is singular.\n');
end
V = -A\df(1:size);
p1 = [1];
for i = 1:m
p1 = [p1; V(i)];
end
p2 = [];
for i = (m + 1):(m + n + 1)
p2 = [p2; V(i)];
end
p3 = [];
for i = (m + n + 2):size
p3 = [p3; V(i)];
end
fx = poly2sym(f(end:-1:1));
dfx = poly2sym(df(end:-1:1));
p1x = poly2sym(p1(end:-1:1));
p2x = poly2sym(p2(end:-1:1));
p3x = poly2sym(p3(end:-1:1));
p3fullx = p1x * dfx + p2x * fx;
p3full = sym2poly(p3fullx); p3full = p3full(end:-1:1);
p1r = roots(p1(end:-1:1));
p1rr = findroots(p1r,target); % findroots eliminates unreal roots and chooses the one closest to the target
if ~isempty(p1rr)
[xpts,soln] = diffsolver(p1x,p2x,p3fullx,p1rr,f(1));
if rcond(A) >= 1e-10
plot(xpts,soln); axis([0 p1rr 0 5]); hold all
end
end
I saw some examples using another function to generate the differential equation but i've tried using the matlabFunction() method with other simpler functions and it seems like it works. Its just that when I try to solve this function, it fails. The solved values start becoming negative when they should all be positive.
I also tried using another solver, dsolve(). But it gives me an implicit solution all the time...
Does anyone have an idea why this is happening? Any advice is appreciated. Thank you!
Since your code seems to work for simpler functions, you could try to increase the accuracy options of the ode45 solver.
This can be achieved by using odeset:
options = odeset('RelTol',1e-10,'AbsTol',1e-10);
[T,Y] = ode45(#function,[tspan],[y0],options);

Matlab - "Could not extract differential variables to solve for" in dsolve

I am trying to solve a RLC circuit as a symbolic differential equation in MATLAB using dsolve, the equation being
U(t) = L*Q''(t) + R*Q'(t) + (1/C)*Q(t)
with the initial conditions
Q(0) = 0
Q'(0) = 0
and
U(t) = 10*sin(2*t)
L = 1
R = 0
C = 1/4
While this works ...
When I implement it explicitly (and using strings) as
Q = dsolve('D2Q(t) + 4*Q(t) = 10*sin(2*t)', 'DQ(0)=0, Q(0)=0');
Q = simplify(Q);
I'll get
Q =
5 sin(2 t) 5 t cos(2 t)
---------- - ------------
4 2
which is correct.
... this does not.
For purely esoteric reasons I tried computing it directly using symbolic equations, as the documentation on dsolve stated it could be done.
So starting with
syms L R C t Q(t)
U = sym('10')*sin(sym('2')*t)
DEQ = L*diff(Q(t),t,2) + R*diff(Q(t),t) + (1/C)*Q(t)
DEQ = subs(DEQ, [L R C], [sym('1'), sym('0'), sym('1/4')])
eqn = (U == DEQ)
I receive
eqn =
10*sin(2*t) == 4*Q(t) + diff(Q(t), t, t)
Which is correct. If I now feed it into dsolve though, using
Q = dsolve(eqn, ...
Q(t) == 0, ...
diff(Q(t),t) == 0);
Matlab throws the error
Error using symengine (line 58)
Could not extract differential variables to solve for. Use 'solve' or 'vpasolve'
to compute the solutions of non-differential equations.
Why is that?
It looks like you're using sym/diff and symfuns incorrectly. Q(t) is what is referred to as an arbitrary (help sym/diff uses the term "abstract" instead) symbolic function, i.e., a function with no definition. Your function's name is Q (think of it as a function handle) and it is represented by the abstract formula Q(t), which just means that it's a function of t. When you want to take the derivative of an abstract function, pass in the name of the function - in your case, Q (the online documentation makes this slightly clearer, but not really). When you want evaluate the function use the formula, e.g., Q(0), the output of which is a sym rather than a symfun.
Here is how I might write the code for your second case:
syms L R C t Q(t)
U = 10*sin(2*t); % No need to wrap integer or exactly-represenable values in sym
dQ = diff(Q,t);
d2Q = diff(dQ,t);
DEQ = L*d2Q + R*dQ + Q/C;
DEQ = subs(DEQ, {L, R, C}, {1, 0, 1/4});
eqn = (U == DEQ);
Q = dsolve(eqn, Q(0) == 0, dQ(0) == 0);
Q = simplify(Q)
which returns
Q =
(5*sin(2*t))/4 - (5*t*cos(2*t))/2
You also forgot to evaluate your initial conditions at zero in the second case so I fixed that too. By the way, in current versions of Matlab you should be using the pure symbolic form for symbolic math (as opposed to strings).

plot a nonlinear function matlab

I'm very new to Matlab and have problem plotting this nonlinear 2D function graph using Matlab.
a lot of errors generated after the below is run.
fun1 = 20 + 10 + 15;
fun2 = 20 + (x * 0.00125 ) + 15;
fun3 = (x * 0.0025) + 15;
fplot(fun1,[0 8000])
fplot(fun2,[8000 16000])
fplot(fun2,[16000 positive infinity])
I appreciate a lot to your efforts and kindness for replying my question
Best Regards
Your first three expressions do not define functions. Please read the documentation about the correct syntax.
fun1 = #(x)(20 + 10 + 15);
First create a file fun.m which contains your function definition
function y = fun(x)
if x < 8000
y = 20 + 10 + 15;
elseif x < 16000
y = 20 + (x * 0.00125) + 15;
else
y = x * 0.0025 + 15;
end
end
Then you can plot it with
fplot(#fun, [0 24000])
which results in
If you do some reading in fplot you will find out that
for fplot(fun,limits)
fun must be
The name of a function
A string with variable x that may be passed to eval, such as 'sin(x)', 'diric(x,10)', or '[sin(x),cos(x)]'
A function handle
so in your case you need to change all of you fun to strings just add ' before and after the expression
as for the last line change it to be
fplot(fun2,[16000 inf])
although i don't think this would give you any good results