I have 2 vectors and a scalar:
grid which is (N x 1);
Value which is (N x 1);
sval which is (1,1);
If I want to interpolate sval on grid I know that I can simply do:
intervalue = interp1(grid, Value, sval, 'PCHIP');
What if now I want the derivatives, i.e. the slope of the function Value at that particular point sval?
As mentioned in the comments, you can approximate the derivative by forward finite difference approximation:
slope = diff(Value) ./ diff(grid);
Alternatively:
slope = gradient(Value(1:end-1),grid);
This is a simple method of numerical differentiation. For a detailed guide on numerical differentiation in MATALB, see this answer.
Here is an example of the finite difference method with the desired interpolation:
% Define function y = x^3
grid = 1:100;
Value = grid .^ 3;
% Approximate derivative via the local slope
slope = diff(Value) ./ diff(grid);
% Or: slope = gradient(Value(1:end-1),grid);
slope_grid = grid(1:end-1);
% Interpolate derivative
sval = 33.5;
sval_slope = interp1(slope_grid, slope, sval, 'PCHIP');
We can visualize the result:
figure;
plot(grid, 3*grid.^2)
hold on
plot(slope_grid, slope)
legend('Reference', 'Approximation', 'Location', 'NorthWest')
Related
Interpolate the Runge function of Example 10.6 at Chebyshev points for n from 10 to 170
in increments of 10. Calculate the maximum interpolation error on the uniform evaluation
mesh x = -1:.001:1 and plot the error vs. polynomial degree as in Figure 10.8 using
semilogy. Observe spectral accuracy.
The runge function is given by: f(x) = 1 / (1 + 25x^2)
My code so far:
x = -1:0.001:1;
n = 170;
i = 10:10:170;
cx = cos(((2*i + 1)/(2*(n+1)))*pi); %chebyshev pts
y = 1 ./ (1 + 25*x.^2); %true fct
%chebyshev polynomial, don't know how to construct using matlab
yc = polyval(c, x); %graph of approx polynomial fct
plot(x, yc);
mErr = (1 / ((2.^n).*(n+1)!))*%n+1 derivative of f evaluated at max x in [-1,1], not sure how to do this
%plotting stuff
I know very little matlab, so I am struggling on creating the interpolating polynomial. I did some google work, but I was confused with the current functions as I didn't find one that just simply took in points and the polynomial to be interpolated. I am also a bit confused in this case of whether I should be doing i = 0:1:n and n=10:10:170 or if n is fixed here. Any help is appreciated, thank you
Since you know very little about MATLAB, I will try explain everything step by step:
First, to visualize the Runge function, you can type:
f = #(x) 1./(1+25*x.^2); % Runge function
% plot Runge function over [-1,1];
x = -1:1e-3:1;
y = f(x);
figure;
plot(x,y); title('Runge function)'); xlabel('x');ylabel('y');
The #(x) part of the code is a function handle, a very useful feature of MATLAB. Notice the function is properly vecotrized, so it can receive as an argument a variable or an array. The plot function is straightforward.
To understand the Runge phenomenon, consider a linearly spaced vector of [-1,1] of 10 elements and use these points to obtain the interpolating (Lagrange) polynomial. You get the following:
% 10 linearly spaced points
xc = linspace(-1,1,10);
yc = f(xc);
p = polyfit(xc,yc,9); % gives the coefficients of the polynomial of degree 10
hold on; plot(xc,yc,'o',x,polyval(p,x));
The polyfit function does a polynomial curve fitting - it obtains the coefficients of the interpolating polynomial, given the poins x,y and the degree of the polynomial n. You can easily evaluate the polynomial at other points with the polyval function.
Obseve that, close to the end domains, you get an oscilatting polynomial and the interpolation is not a good approximation of the function. As a matter of fact, you can plot the absolute error, comparing the value of the function f(x) and the interpolating polynomial p(x):
plot(x,abs(y-polyval(p,x))); xlabel('x');ylabel('|f(x)-p(x)|');title('Error');
This error can be reduced if, instead of using a linearly space vector, you use other points to do the interpolation. A good choice is to use the Chebyshev nodes, which should reduce the error. As a matter of fact, notice that:
% find 10 Chebyshev nodes and mark them on the plot
n = 10;
k = 1:10; % iterator
xc = cos((2*k-1)/2/n*pi); % Chebyshev nodes
yc = f(xc); % function evaluated at Chebyshev nodes
hold on;
plot(xc,yc,'o')
% find polynomial to interpolate data using the Chebyshev nodes
p = polyfit(xc,yc,n-1); % gives the coefficients of the polynomial of degree 10
plot(x,polyval(p,x),'--'); % plot polynomial
legend('Runge function','Chebyshev nodes','interpolating polynomial','location','best')
Notice how the error is reduced close to the end domains. You don't get now that high oscillatory behaviour of the interpolating polynomial. If you plot the error, you will observe:
plot(x,abs(y-polyval(p,x))); xlabel('x');ylabel('|f(x)-p(x)|');title('Error');
If, now, you change the number of Chebyshev nodes, you will get an even better approximation. A little modification on the code lets you run it again for different numbers of nodes. You can store the maximum error and plot it as a function of the number of nodes:
n=1:20; % number of nodes
% pre-allocation for speed
e_ln = zeros(1,length(n)); % error for the linearly spaced interpolation
e_cn = zeros(1,length(n)); % error for the chebyshev nodes interpolation
for ii=1:length(n)
% linearly spaced vector
x_ln = linspace(-1,1,n(ii)); y_ln = f(x_ln);
p_ln = polyfit(x_ln,y_ln,n(ii)-1);
e_ln(ii) = max( abs( y-polyval(p_ln,x) ) );
% Chebyshev nodes
k = 1:n(ii); x_cn = cos((2*k-1)/2/n(ii)*pi); y_cn = f(x_cn);
p_cn = polyfit(x_cn,y_cn,n(ii)-1);
e_cn(ii) = max( abs( y-polyval(p_cn,x) ) );
end
figure
plot(n,e_ln,n,e_cn);
xlabel('no of points'); ylabel('maximum absolute error');
legend('linearly space','chebyshev nodes','location','best')
I am trying to fit a 3D surface polynomial of n-degrees to some data points in 3D space. My system requires the surface to be monotonically increasing in the area of interest, that is the partial derivatives must be non-negative. This can be achieved using Matlab's built in lsqlin function.
So here's what I've done to try and achieve this:
I have a function that takes in four parameters;
x1 and x2 are my explanatory variables and y is my dependent variable. Finally, I can specify order of polynomial fit. First I build the design matrix A using data from x1 and x2 and the degree of fit I want. Next I build the matrix D that is my container for the partial derivatives of my datapoints. NOTE: the matrix D is double the length of matrix A since all datapoints must be differentiated with respect to both x1 and x2. I specify that Dx >= 0 by setting b to be zeroes.
Finally, I call lsqlin. I use "-D" since Matlab defines the function as Dx <= b.
function w_mono = monotone_surface_fit(x1, x2, y, order_fit)
% Initialize design matrix
A = zeros(length(x1), 2*order_fit+2);
% Adjusting for bias term
A(:,1) = ones(length(x1),1);
% Building design matrix
for i = 2:order_fit+1
A(:,(i-1)*2:(i-1)*2+1) = [x1.^(i-1), x2.^(i-1)];
end
% Initialize matrix containing derivative constraint.
% NOTE: Partial derivatives must be non-negative
D = zeros(2*length(y), 2*order_fit+1);
% Filling matrix that holds constraints for partial derivatives
% NOTE: Matrix D will be double length of A since all data points will have a partial derivative constraint in both x1 and x2 directions.
for i = 2:order_fit+1
D(:,(i-1)*2:(i-1)*2+1) = [(i-1)*x1.^(i-2), zeros(length(x2),1); ...
zeros(length(x1),1), (i-1)*x2.^(i-2)];
end
% Limit of derivatives
b = zeros(2*length(y), 1);
% Constrained LSQ fit
options = optimoptions('lsqlin','Algorithm','interior-point');
% Final weights of polynomial
w_mono = lsqlin(A,y,-D,b,[],[],[],[],[], options);
end
So now I get some weights out, but unfortunately they do not at all capture the structure of the data. I've attached an image so you can just how bad it looks. .
I'll give you my plotting script with some dummy data, so you can try it.
%% Plot different order polynomials to data with constraints
x1 = [-5;12;4;9;18;-1;-8;13;0;7;-5;-8;-6;14;-1;1;9;14;12;1;-5;9;-10;-2;9;7;-1;19;-7;12;-6;3;14;0;-8;6;-2;-7;10;4;-5;-7;-4;-6;-1;18;5;-3;3;10];
x2 = [81.25;61;73;61.75;54.5;72.25;80;56.75;78;64.25;85.25;86;80.5;61.5;79.25;76.75;60.75;54.5;62;75.75;80.25;67.75;86.5;81.5;62.75;66.25;78.25;49.25;82.75;56;84.5;71.25;58.5;77;82;70.5;81.5;80.75;64.5;68;78.25;79.75;81;82.5;79.25;49.5;64.75;77.75;70.25;64.5];
y = [-6.52857142857143;-1.04736842105263;-5.18750000000000;-3.33157894736842;-0.117894736842105;-3.58571428571429;-5.61428571428572;0;-4.47142857142857;-1.75438596491228;-7.30555555555556;-8.82222222222222;-5.50000000000000;-2.95438596491228;-5.78571428571429;-5.15714285714286;-1.22631578947368;-0.340350877192983;-0.142105263157895;-2.98571428571429;-4.35714285714286;-0.963157894736842;-9.06666666666667;-4.27142857142857;-3.43684210526316;-3.97894736842105;-6.61428571428572;0;-4.98571428571429;-0.573684210526316;-8.22500000000000;-3.01428571428571;-0.691228070175439;-6.30000000000000;-6.95714285714286;-2.57232142857143;-5.27142857142857;-7.64285714285714;-2.54035087719298;-3.45438596491228;-5.01428571428571;-7.47142857142857;-5.38571428571429;-4.84285714285714;-6.78571428571429;0;-0.973684210526316;-4.72857142857143;-2.84285714285714;-2.54035087719298];
% Used to plot the surface in all points in the grid
X1 = meshgrid(-10:1:20);
X2 = flipud(meshgrid(30:2:90).');
figure;
for i = 1:4
w_mono = monotone_surface_fit(x1, x2, y, i);
y_nr = w_mono(1)*ones(size(X1)) + w_mono(2)*ones(size(X2));
for j = 1:i
y_nr = w_mono(j*2)*X1.^j + w_mono(j*2+1)*X2.^j;
end
subplot(2,2,i);
scatter3(x1, x2, y); hold on;
axis tight;
mesh(X1, X2, y_nr);
set(gca, 'ZDir','reverse');
xlabel('x1'); ylabel('x2');
zlabel('y');
% zlim([-10 0])
end
I think it may have something to do with the fact that I haven't specified anything about the region of interest, but really I don't know. Thanks in advance for any help.
Alright I figured it out.
The main problem was simply an error in the plotting script. The value of y_nr should be updated and not overwritten in the loop.
Also I figured out that the second derivative should be monotonically decreasing. Here's the updated code if anybody is interested.
%% Plot different order polynomials to data with constraints
x1 = [-5;12;4;9;18;-1;-8;13;0;7;-5;-8;-6;14;-1;1;9;14;12;1;-5;9;-10;-2;9;7;-1;19;-7;12;-6;3;14;0;-8;6;-2;-7;10;4;-5;-7;-4;-6;-1;18;5;-3;3;10];
x2 = [81.25;61;73;61.75;54.5;72.25;80;56.75;78;64.25;85.25;86;80.5;61.5;79.25;76.75;60.75;54.5;62;75.75;80.25;67.75;86.5;81.5;62.75;66.25;78.25;49.25;82.75;56;84.5;71.25;58.5;77;82;70.5;81.5;80.75;64.5;68;78.25;79.75;81;82.5;79.25;49.5;64.75;77.75;70.25;64.5];
y = [-6.52857142857143;-1.04736842105263;-5.18750000000000;-3.33157894736842;-0.117894736842105;-3.58571428571429;-5.61428571428572;0;-4.47142857142857;-1.75438596491228;-7.30555555555556;-8.82222222222222;-5.50000000000000;-2.95438596491228;-5.78571428571429;-5.15714285714286;-1.22631578947368;-0.340350877192983;-0.142105263157895;-2.98571428571429;-4.35714285714286;-0.963157894736842;-9.06666666666667;-4.27142857142857;-3.43684210526316;-3.97894736842105;-6.61428571428572;0;-4.98571428571429;-0.573684210526316;-8.22500000000000;-3.01428571428571;-0.691228070175439;-6.30000000000000;-6.95714285714286;-2.57232142857143;-5.27142857142857;-7.64285714285714;-2.54035087719298;-3.45438596491228;-5.01428571428571;-7.47142857142857;-5.38571428571429;-4.84285714285714;-6.78571428571429;0;-0.973684210526316;-4.72857142857143;-2.84285714285714;-2.54035087719298];
% Used to plot the surface in all points in the grid
X1 = meshgrid(-10:1:20);
X2 = flipud(meshgrid(30:2:90).');
figure;
for i = 1:4
w_mono = monotone_surface_fit(x1, x2, y, i);
% NOTE: Should only have 1 bias term
y_nr = w_mono(1)*ones(size(X1));
for j = 1:i
y_nr = y_nr + w_mono(j*2)*X1.^j + w_mono(j*2+1)*X2.^j;
end
subplot(2,2,i);
scatter3(x1, x2, y); hold on;
axis tight;
mesh(X1, X2, y_nr);
set(gca, 'ZDir','reverse');
xlabel('x1'); ylabel('x2');
zlabel('y');
% zlim([-10 0])
end
And here's the updated function
function [w_mono, w] = monotone_surface_fit(x1, x2, y, order_fit)
% Initialize design matrix
A = zeros(length(x1), 2*order_fit+1);
% Adjusting for bias term
A(:,1) = ones(length(x1),1);
% Building design matrix
for i = 2:order_fit+1
A(:,(i-1)*2:(i-1)*2+1) = [x1.^(i-1), x2.^(i-1)];
end
% Initialize matrix containing derivative constraint.
% NOTE: Partial derivatives must be non-negative
D = zeros(2*length(y), 2*order_fit+1);
for i = 2:order_fit+1
D(:,(i-1)*2:(i-1)*2+1) = [(i-1)*x1.^(i-2), zeros(length(x2),1); ...
zeros(length(x1),1), -(i-1)*x2.^(i-2)];
end
% Limit of derivatives
b = zeros(2*length(y), 1);
% Constrained LSQ fit
options = optimoptions('lsqlin','Algorithm','active-set');
w_mono = lsqlin(A,y,-D,b,[],[],[],[],[], options);
w = lsqlin(A,y);
end
Finally a plot of the fitting (Have used a new simulation, but fit also works on given dummy data).
This is my code for finding the centered coefficients for lagrange polynomial interpolation:
% INPUT
% f f scalar - valued function
% interval interpolation interval [a, b]
% n interpolation order
%
% OUTPUT
% coeff centered coefficients of Lagrange interpolant
function coeff = lagrangeInterp (f, interval , n)
a = interval(1);
b = interval(2);
x = linspace(a,b,n+1);
y = f(x);
coeff(1,:) = polyfit(x,y,n);
end
Which is called in the following script
%Plot lagrangeInterp and sin(x) together
hold on
x = 0:0.1*pi:2*pi;
for n = 1:1:4
coeff = lagrangeInterp(#(x)sin(x),[0,2*pi],n);
plot(x,polyval(coeff,x,'-'));
end
y = sin(x);
plot(x,y);
legend('1st order','2nd order','3rd order','4th order','sin(x)');
To check for stability I would like to perturb the function (eg g(x) = f(x) + epsilon). How would I go about this?
Well, a little trick for you.
You know randn([m,n]) in matlab generate a m*n random matrix. The point is to generate a random vector, and interp1 to a function of x. Like this:
x = linspace(a,b,n+1); % Your range of input
g = #(ep,xx)f(xx)+interp1(x,ep*randn([length(x),1]),xx);
The stairstep-look of the graph is unintentional. When I plot the same-sized vectors Arb and V (plot (Arb, V, 'r')) I get the following graph:
enter image description here
To make it smoother, I tried using 1-D data interpolation, interp1, as follows:
xq = 0:0.001:max(Arb);
Vq = interp1 (Arb, V, xq);
plot (xq, Vq);
However, I get this error message:
Error using interp1>reshapeAndSortXandV (line 416)
X must be a vector.
Error in interp1 (line 92)
[X,V,orig_size_v] = reshapeAndSortXandV(varargin{1},varargin{2})
interp1 will use linear interpolation on your data so it won't help you much. One thing you can try is to use a cubic spline with interp1 but again given the nature of the data I don't think it will help much. e.g.
Alternative 1. Cubic Spline
xq = 0:0.001:max(Arb);
Vq = interp1 (Arb, V, xq, 'spline');
plot (xq, Vq);
Alternative 2. Polynomial fit
Another alternative you can try is, polynomial interpolation using polyfit. e.g.
p = polifit(Arb, V, 2); % I think a 2nd order polynomial should do
xq = 0:0.001:max(Arb);
Vq = polyval(p, xq);
plot (xq, Vq);
Alternative 3. Alpha-beta filter
Last but not least, another thing to try is to use an smoothing alpha-beta filter on your V data. One possible implementation can be:
% x is the input data
% a is the "smoothing" factor from [0, 1]
% a = 0 full smoothing
% a = 1 no smoothing
function xflt = alphaBeta(x, a)
xflt = zeros(1,length(x));
xflt(1) = x(1);
a = max(min(a, 1), 0); % Bound coefficient between 0 and 1;
for n = 2:1:length(x);
xflt(n) = a * x(n) + (1 - a) * xflt(n-1);
end
end
Usage:
plot (Arb, alphaBeta(V, 0.65), 'r')
clc
clear all
x= -2:0.1:4;
y= (3*x.^3)-(26*x)+10;
y1= diff(y)./diff(x);
y2= diff(y1)./diff(x);
plot(x,y,'R')
hold on
plot(x,y1,'G')
hold on
plot(x,y2,'B')
Is this right way to find second derivative?
It's not finding the second derivative and giving an error that matrix dimensions must agree in line 6.
You have values y that correspond to the values x. While calculating the approximation to the first derivatives y1 of y, you use finite differences. These kind of finite differences approximate the derivative best at the points inbetween x. This means your calculation of the approximation of the second derivative should involve (x(1:end-1)+x(2:end))/2:
x1 = (x(1:end-1) + x(2:end))/2;
y2 = diff(y1) / diff(x1);
Note that these approximations y2 best approximate the second derivative at the positions x2 = (x1(1:end-1) + x1(2:end))/2.
All in all your code might look like this:
%// Clear command window, delete all variables, close all figures
clc;
clear all;
close all;
%// Define function
f = #(x) (3*x.^3)-(26*x)+10;
%// Define positions
x = -2:0.1:4;
%// Calculate f(x)
y= f(x);
%// Calculate approximation to first derivative and their positions
x1 = (x(1:end-1) + x(2:end))/2;
y1 = diff(y) ./ diff(x);
%// Calculate approximation to second derivative and their positions
x2 = (x1(1:end-1) + x1(2:end))/2;
y2 = diff(y1) ./ diff(x1);
%// Plot
plot(x, y, 'R',...
x1,y1,'G',...
x2,y2,'B');
legend('f','f''', 'f''''');