Find the minimum of a multi-variable function - matlab

Question: Find the minimum of f(x,y)=x^2+y^2-2*x-6*y+14 in the window [0,2]×[2,4] with increment 0.01 for x and y.
My approach: Find the first partial derivatives fx and fy. The critical points satisfy the equations fx(x,y) = 0 and fy(x,y) = 0 simultaneously. find the second order partial derivatives fxx(x,y), fyy(x,y) and fxy(x,y) in order to find D.
clc
clear all
syms x y
fun=x^2+y^2-2*x-6*y+14;
fx=diff(fun,x);
fy=diff(fun,y);
pt=solve(fx==0,fy==0);
sol = struct2array(pt)
fxx=diff(fx,x);
fyy=diff(fy,y);
fxy=diff(fx,y);
D=subs(fxx,[x y],[1 3])*subs(fyy,[x y],[1 3])-(subs(fxy,[x y],[1 3]))^2
fxx_val=subs(fxx,[x y],[1 3])
minimum_value=subs(fun,[x y],[1 3])
Am I doing the right thing about what the question asked? Besides what about the window and increment mentioned that question. Any hints or solution will be appreciated .
Thanks in advance .

Use function evaluation optimization method instead of gradient
Please read through the code
f = #(x,y)x.^2+y.^2-2.*x-6.*y+14;
% x range
x_lb = 0;
x_ub = 2;
% y range
y_lb = 2;
y_ub = 4;
step = 0.01;
% lower bound of x, initial guess as xmin
xmin = x_lb;
% lower bound of y, initial guess as ymin
ymin = y_lb;
% f at the lower bounds, initial fmin
fmin = f(xmin, ymin);
for x = x_lb:step:x_ub
for y = y_lb:step:y_ub
% function evaluation
fval = f(x, y);
%replace fmin if the newly evaluated f is less than the actual fmin
if fval < fmin
fmin = fval;
% save current x and y where f is minimum
xmin = x;
ymin = y;
end
end
end
Solution
xmin = 1;
ymin = 3;
fmin = 4;

I would suggest to make use of Matlab's capabilities to compute with matrices. Then, no loop is required.
% your function, look up anonymous functions
func = #(x,y) x.^2 + y.^2 - 2.*x - 6.*y + 14;
% get matrices for you x- and y-window
[xg, yg] = meshgrid(0:.01:2, 2:0.01:4);
% compute all in one call
result = func(xg,yg);
% find total minimum
minimum = min(result(:));
% find the index of the (first) minimum, for other equations, there might
% be more than one
ind = find(result==minimum, 1);
% Output the result
fprintf('The minimum (%d) is located at x: %d, y: %d.\n', minimum, xg(ind), yg(ind));

Related

Negative values obtained in the solution of the 1D advection-dispersion equation using FD method

I am trying to solve the 1D ADE
This is my code so far:
clc; clear; close all
%Input parameters
Ao = 1; %Initial value
L = 0.08; %Column length [m]
nx = 40; %spatial gridpoints
dx = L/nx; %Length step size [m]
T = 20/24; %End time [days]
nt = 100; %temporal gridpoints
dt = T/nt; %Time step size [days]
Vel = dx/dt; %Velocity in each cell [m/day]
alpha = 0.002; %Dispersivity [m]
De = alpha*Vel; % Dispersion coeff. [m2/day]
%Gridblocks
x = 0:dx:L;
t = 0:dt:T;
%Initial and boundary conditions
f = #(x) x; % initial cond.
% boundary conditions
g1 = #(t) Ao;
g2 = #(t) 0;
%Initialization
A = zeros(nx+1, nt+1);
A(:,1) = f(x);
A(1,:) = g1(t);
gamma = dt/(dx^2);
beta = dt/dx;
% Implementation of the explicit method
for j= 1:nt-1 % Time Loop
for i= 2:nx-1 % Space Loop
A(i,j+1) = (A(i-1,j))*(Vel*beta + De*gamma)...
+ A(i,j)*(1-2*De*gamma-Vel*beta) + A(i+1,j)*(De*gamma);
end
% Insert boundary conditions for i = 1 and i = N
A(2,j+1) = A(1,j)*(Vel*beta + De*gamma) + A(2,j)*(1-2*De*gamma-Vel*beta) + A(3,j)*(De*gamma);
A(nx,j+1) = A(nx-1,j)*(Vel*beta + 2*De*gamma) + A(nx,j)*(1-2*De*gamma-Vel*beta)+ (2*De*gamma*dx*g2(t));
end
figure
plot(t, A(end,:), 'r*', 'MarkerSize', 2)
title('A Vs time profile (Using FDM)')
xlabel('t'),ylabel('A')
Now, I have been able to solve the problem using MATLAB’s pdepe function (see plot), but I am trying to compare the result with the finite difference method (implemented in the code above). I am however getting negative values of the dependent variable, but I am not sure what exactly I could be doing wrong. I therefore will really appreciate if anyone can help me out here. Many thanks in anticipation.
PS: I can post the code I used for the pdepe if anyone would like to see it.

How to evaluate function of two variables with different x and y vectors

I've been trying to evaluate a function in matlab. I want my x vector to go from 0 to 1000 and my y vector to go from 0 to 125. They should both have a length of 101.
The equation to be evaluated is z(x,y) = ay + bx, with a=10 and b=20.
a = 10;
b = 20;
n = 101;
dx = 10; % Interval length
dy = 1.25;
x = zeros(1,n);
y = zeros(1,n);
z = zeros(n,n);
for i = 1:n;
x(i) = dx*(i-1);
y(i) = dy*(i-1);
for j = 1:n;
z(i,j) = a*dy*(j-1) + b*dx*(j-1);
end
end
I get an answer, but I don't know if I did it correctly with the indices in the nested for loop?
See MATLAB's linspace function.
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
z=a*y+b*x;
This is easier and takes care of the interval spacing for you. From the linspace documentation,
y = linspace(x1,x2,n) generates n points. The spacing between the points is (x2-x1)/(n-1).
Edit:
As others have pointed out, my solution above makes a vector, not a matrix which the OP seems to want. As #obchardon pointed out, you can use meshgrid to make that 2D grid of x and y points to generate a matrix of z. Updated approached would be:
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
[X,Y] = meshgrid(x,y);
z=a*Y+b*X;
(you may swap the order of x and y depending on if you want each variable along the row or column of z.)

Optim-nonlinear equation in matlab code

I updated the question to clarify it more. Here is a graph:
For the curve in the attached photo, I hope to draw the curve. I have its equation and it is after simplification will be like this one
% Eq-2
(b*Y* cos(v) + c - k*X*sin(v))^2 + ...
sqrt(k*X*(cos(v) + 1.0) + b*Y*sin(v))^2) - d = 0.0
Where:
v = atan((2.0*Y)/X) + c
and b, c, d and k are constants.
from the attached graph,
The curve is identified in two points:
p1 # (x=0)
p2 # (y=0)
I a new on coding so accept my apologize if my question is not clear.
Thanks
So, after your edit, it is a bit more clear what you want.
I insist that your equation needs work -- the original equation (before your edit) simplified to what I have below. The curve for that looks like your plot, except the X and Y intercepts are at different locations, and funky stuff happens near X = 0 because you have numerical problems with the tangent (you might want to reformulate the problem).
But, after checking your equation, the following code should be helpful:
function solve_for_F()
% graininess of alpha
N = 100;
% Find solutions for all alphae
X = zeros(1,N);
options = optimset('Display', 'off');
alpha = linspace(0, pi/2, N);
x0 = linspace(6, 0, N);
for ii = 1:numel(alpha)
X(ii) = fzero(#(x)F(x, alpha(ii)), x0(ii), options);
end
% Convert and make an X-Y plot
Y = X .* tan(alpha);
plot(X, Y,...
'linewidth', 2,...
'color', [1 0.65 0]);
end
function fval = F(X, alpha)
Y = X*tan(alpha);
% Please, SIMPLIFY in the future
A = 1247745517111813/562949953421312;
B = 4243112111277797/4503599627370496;
V = atan2(2*Y,X) + A;
eq2 = sqrt( (5/33*( Y*sin(V) + X/2*(cos(V) + 1) ))^2 + ...
(5/33*( Y*cos(V) - X/2* sin(V) ))^2 ) - B;
fval = eq2;
end
Results:
So, I was having fun with this (thanks for that)!
Different question, different answer.
The solution below first searches for the constants causing the X and Y intercepts you were looking for (p1 and p2). For those constants that best fit the problem, it makes a plot, taking into account numerical issues.
In fact, you don't need eq. 1, because that's true always for any curve -- it's just there to confuse you, and problematic to use.
So, here it is:
function C = solve_for_F()
% Points of interest
px = 6;
py = 4.2;
% Wrapper function; search for those constants
% causing the correct X,Y intercepts (at px, py)
G = #(C) abs(F( 0, px, C)) + ... % X intercept at px
abs(F(py, 0, C)); % Y intercept at py
% Initial estimate, based on your original equation
C0 = [5/33
1247745517111813/562949953421312
4243112111277797/4503599627370496
5/66];
% Minimize the error in G by optimizing those constants
C = fminsearch(G, C0);
% Plot the solutions
plot_XY(px, py, C);
end
function plot_XY(xmax,ymax, C)
% graininess of X
N = 100;
% Find solutions for all alphae
Y = zeros(1,N);
X = linspace(0, xmax, N);
y0 = linspace(ymax, 0, N);
options = optimset('Display', 'off',...,...
'TolX' , 1e-10);
% Solve the nonlinear equation for each X
for ii = 1:numel(X)
% Wrapper function for fzero()
fcn1 = #(y)F(y, X(ii), C);
% fzero() is probably the fastest and most intuitive
% solver for this problem
[Y(ii),~,flag] = fzero(fcn1, y0(ii), options);
% However, it uses an algorithm that easily diverges
% when the function slope is large. For those cases,
% solve with fminsearch()
if flag ~= 1
% In this case, the minimum of the absolute value
% is searched for (which should be zero)
fcn2 = #(y) abs(fcn1(y));
Y(ii) = fminsearch(fcn2, y0(ii), options);
end
end
% Now plot the X,Y solutions
plot(X, Y,...
'linewidth', 2,...
'color', [1 0.65 0]);
xlabel('X'), ylabel('Y')
axis([0 xmax+.1 0 ymax+.1])
end
function fval = F(Y, X, C)
% Unpack constants
b = C(1); d = C(3);
c = C(2); k = C(4);
% pre-work
V = atan2(2*Y, X) + c;
% Eq. 2
fval = sqrt( (b*Y*sin(V) + k*X*(cos(V) + 1))^2 + ...
(b*Y*cos(V) - k*X* sin(V) )^2 ) - d;
end

Implement finite difference method in matlab

I am trying to implement the finite difference method in matlab. I did some calculations and I got that y(i) is a function of y(i-1) and y(i+1), when I know y(1) and y(n+1). However, I don't know how I can implement this so the values of y are updated the right way. I tried using 2 fors, but it's not going to work that way.
EDIT
This is the script and the result isn't right
n = 10;
m = n+1;
h = 1/m;
x = 0:h:1;
y = zeros(m+1,1);
y(1) = 4;
y(m+1) = 6;
s = y;
for i=2:m
y(i) = y(i-1)*(-1+(-2)*h)+h*h*x(i)*exp(2*x(i));
end
for i=m:-1:2
y(i) = (y(i) + (y(i+1)*(2*h-1)))/(3*h*h-2);
end
The equation is:
y''(x) - 4y'(x) + 3y(x) = x * e ^ (2x),
y(0) = 4,
y(1) = 6
Thanks.
Consider the following code. The central differential quotient is discretized.
% Second order diff. equ.
% y'' - 4*y' + 3*y = x*exp(2*x)
% (y(i+1)-2*y(i)+y(i-1))/h^2-4*(y(i+1)-y(i-1))/(2*h) + 3*y(i) = x(i)*exp(2*x(i));
The solution region is specified.
x = (0:0.01:1)'; % Solution region
h = min(diff(x)); % distance
As said in my comment, using this method, all points have to be solved simultaneously. Therefore, above numerical approximation of the equation is transformed in a linear system of euqations.
% System of equations
% Matrix of coefficients
A = zeros(length(x));
A(1,1) = 1; % known solu for first point
A(end,end) = 1; % known solu for last point
% y(i) y'' y
A(2:end-1,2:end-1) = A(2:end-1,2:end-1)+diag(repmat(-2/h^2+3,[length(x)-2 1]));
% y(i-1) y'' -4*y'
A(1:end-1,1:end-1) = A(1:end-1,1:end-1)+diag(repmat(1/h^2+4/(2*h),[length(x)-2 1]),-1);
% y(i+1) y'' -4*y'
A(2:end,2:end) = A(2:end,2:end)+diag(repmat(1/h^2-4/(2*h),[length(x)-2 1]),+1);
With the rhs of the differential equation. Note that the known values are calculated by 1 in the matrix and the actual value in the solution vector.
Y = x.*exp(2*x);
Y(1) = 4; % known solu for first point
Y(end) = 6; % known solu for last point
y = A\Y;
Having an equation to approximate the first order derivative (see above) you can verify the solution. (note, ddx2 is an own function)
f1 = ddx2(x,y); % first derivative (own function)
f2 = ddx2(x,f1); % second derivative (own function)
figure;
plot(x,y);
saveas(gcf,'solu1','png');
figure;
plot(x,f2-4*f1+3*y,x,x.*exp(2*x),'ko');
ylim([0 10]);
legend('lhs','rhs','Location','nw');
saveas(gcf,'solu2','png');
I hope the solution shown below is correct.

How can I (efficiently) compute a moving average of a vector?

I've got a vector and I want to calculate the moving average of it (using a window of width 5).
For instance, if the vector in question is [1,2,3,4,5,6,7,8], then
the first entry of the resulting vector should be the sum of all entries in [1,2,3,4,5] (i.e. 15);
the second entry of the resulting vector should be the sum of all entries in [2,3,4,5,6] (i.e. 20);
etc.
In the end, the resulting vector should be [15,20,25,30]. How can I do that?
The conv function is right up your alley:
>> x = 1:8;
>> y = conv(x, ones(1,5), 'valid')
y =
15 20 25 30
Benchmark
Three answers, three different methods... Here is a quick benchmark (different input sizes, fixed window width of 5) using timeit; feel free to poke holes in it (in the comments) if you think it needs to be refined.
conv emerges as the fastest approach; it's about twice as fast as coin's approach (using filter), and about four times as fast as Luis Mendo's approach (using cumsum).
Here is another benchmark (fixed input size of 1e4, different window widths). Here, Luis Mendo's cumsum approach emerges as the clear winner, because its complexity is primarily governed by the length of the input and is insensitive to the width of the window.
Conclusion
To summarize, you should
use the conv approach if your window is relatively small,
use the cumsum approach if your window is relatively large.
Code (for benchmarks)
function benchmark
clear all
w = 5; % moving average window width
u = ones(1, w);
n = logspace(2,6,60); % vector of input sizes for benchmark
t1 = zeros(size(n)); % preallocation of time vectors before the loop
t2 = t1;
th = t1;
for k = 1 : numel(n)
x = rand(1, round(n(k))); % generate random row vector
% Luis Mendo's approach (cumsum)
f = #() luisMendo(w, x);
tf(k) = timeit(f);
% coin's approach (filter)
g = #() coin(w, u, x);
tg(k) = timeit(g);
% Jubobs's approach (conv)
h = #() jubobs(u, x);
th(k) = timeit(h);
end
figure
hold on
plot(n, tf, 'bo')
plot(n, tg, 'ro')
plot(n, th, 'mo')
hold off
xlabel('input size')
ylabel('time (s)')
legend('cumsum', 'filter', 'conv')
end
function y = luisMendo(w,x)
cs = cumsum(x);
y(1,numel(x)-w+1) = 0; %// hackish way to preallocate result
y(1) = cs(w);
y(2:end) = cs(w+1:end) - cs(1:end-w);
end
function y = coin(w,u,x)
y = filter(u, 1, x);
y = y(w:end);
end
function jubobs(u,x)
y = conv(x, u, 'valid');
end
function benchmark2
clear all
w = round(logspace(1,3,31)); % moving average window width
n = 1e4; % vector of input sizes for benchmark
t1 = zeros(size(n)); % preallocation of time vectors before the loop
t2 = t1;
th = t1;
for k = 1 : numel(w)
u = ones(1, w(k));
x = rand(1, n); % generate random row vector
% Luis Mendo's approach (cumsum)
f = #() luisMendo(w(k), x);
tf(k) = timeit(f);
% coin's approach (filter)
g = #() coin(w(k), u, x);
tg(k) = timeit(g);
% Jubobs's approach (conv)
h = #() jubobs(u, x);
th(k) = timeit(h);
end
figure
hold on
plot(w, tf, 'bo')
plot(w, tg, 'ro')
plot(w, th, 'mo')
hold off
xlabel('window size')
ylabel('time (s)')
legend('cumsum', 'filter', 'conv')
end
function y = luisMendo(w,x)
cs = cumsum(x);
y(1,numel(x)-w+1) = 0; %// hackish way to preallocate result
y(1) = cs(w);
y(2:end) = cs(w+1:end) - cs(1:end-w);
end
function y = coin(w,u,x)
y = filter(u, 1, x);
y = y(w:end);
end
function jubobs(u,x)
y = conv(x, u, 'valid');
end
Another possibility is to use cumsum. This approach probably requires fewer operations than conv does:
x = 1:8
n = 5;
cs = cumsum(x);
result = cs(n:end) - [0 cs(1:end-n)];
To save a little time, you can replace the last line by
%// clear result
result(1,numel(x)-n+1) = 0; %// hackish way to preallocate result
result(1) = cs(n);
result(2:end) = cs(n+1:end) - cs(1:end-n);
If you want to preserve the size of your input vector, I suggest using filter
>> x = 1:8;
>> y = filter(ones(1,5), 1, x)
y =
1 3 6 10 15 20 25 30
>> y = (5:end)
y =
15 20 25 30