Natural cubic spline function in MATLAB - matlab

I am trying to write a MATLAB function which interpolates data points in X to create a natural cubic spline, similar to interp1 but without using interp1. The function takes inputs vector x and c (from the system Ac=Y) and vector X of data points that I want to interpolate.
My function is almost complete, I have put the system in matrix form, found the y values, coefficients a,b,c and d but I do not know how to evaluate the X values to get my estimated value Y.
For example, this is what I have at the moment:
%cubic spline interpolation
n = length(x);
N = length(X);
Y = zeros(size(X));
for i = 1:n-1
for j = 1:N
while x(i) <= X(j) && x(i+1) >= X(j)
Y(j) = a(i)*(X(j)^3) + b(i)*(X(j)^2) + c(i)*X(j) + d(i);
break
end
end
end
My question is why does this not work? I know interp1 does not find the natural spline but I am very new to MATLAB so I was just using this built-in function as a reference as to how the graph should look and my function is just completely wrong. I hope that makes some sense. Any help would be great.

Related

Create arrays based on a different array

% create variable x with array of all necessary values
x=linspace(0.1,13,50);
for i=x
% create equation to determine y
y=(sqrt(2.*i)*4*i.^3)/(4.*i+7.^(i/10));
%create equation to determine z
z=log10(2.*i+5)+(4.*i+exp(i))/(2./3+4.*i.^2);
end
Using Matlab and im trying to use values from my x array to create two arrays, y and z, im pretty new to matlab and im struggling, thanks.
The problem in you code is that you did not use for loop properly. You can run through the index of x, then assign x(i) to a new variable k in each iteration, i.e.,
x=linspace(0.1,13,50);
for k = 1:length(x)
i = x(k);
% create equation to determine y
y(k) =(sqrt(2.*i)*4*i.^3)/(4.*i+7.^(i/10));
%create equation to determine z
z(k) =log10(2.*i+5)+(4.*i+exp(i))/(2./3+4.*i.^2);
end
Since MATLAB is able to vectorize the operations, so you are recommended to do it like below to speed up (for loop in MATLAB is expensive)
x = linspace(0.1,13,50);
y = (sqrt(2*x).*4.*x.^3)./(4*x+7^(x/10));
z = log10(2*x+5)+(4*x+exp(x))./(2/3 + 4*x.^2);
Remarks: you should be careful about the difference between .* and *, or ./ and /, where * and / are not element-wise operations.
Method 1:
you can initialize y and z into empty arrays and just append the corresponding result at the end:
% create variable x with array of all necessary values
x=linspace(0.1,13,50);
y=[];
z=[];
for i=x
% create equation to determine y
y(end+1)=(sqrt(2.*i)*4*i.^3)/(4.*i+7.^(i/10));
%create equation to determine z
z(end+1)=log10(2.*i+5)+(4.*i+exp(i))/(2./3+4.*i.^2);
end
This approach can prove to be poor in terms of efficiency, since the size of y and z changes dynamically.
Method 2:
If you still want to use a for loop, it is better to preallocate memory for y and z and iterate the indices of x, something like:
% create variable x with array of all necessary values
x=linspace(0.1,13,50);
% Memory allocation
y = zeros(1, length(x));
z = zeros(1, length(x));
for i = 1 : length(x)
% create equation to determine y
y(i)=(sqrt(2.*x(i)*4*x(i).^3)/(4.*x(i)+7.^(x(i)/10));
%create equation to determine z
z(i)=log10(2.*x(i)+5)+(4.*x(i)+exp(x(i)))/(2./3+4.*x(i).^2);
end
Method 3 (preferred one):
It is generally more efficient to vectorize your implementations. In your case you could use something like:
x = linspace(0.1,13,50);
y = (sqrt(2.*x)*4*.*x.^3) ./ (4*x + 7.^(x./10));
z = log10(2*x+5) + (4*x + exp(x)) ./ (2/3 + 4*x.^2);

Why can't I use 'scatter3' here?

[X,Y] = meshgrid(-8:.5:8);
R = sqrt(X.^2 + Y.^2) + eps;
Z = sin(R)./R;
scatter3(X,Y,Z)
Error using scatter3 (line 64)
X, Y and Z must be vectors of the same length.
Matlab R2018b windows x64
As shown in the documentation, X, Y, Z must be vectors. (When you enter an article on mathworks from Googling, say, "matlab scatter3", you will first see the syntax for the function. Blue text means hyperlink. All the inputs are linked to the bottom of the page where their exact typing is defined.)
The reason is (probably) as follows.
As stated in the documentation, scatter3 puts circles (or other symbols of your choice if you modify the graphic object) on 3D coordinates of your choice. The coordinates are the ith element of X, Y, Z respectively. For example, the x-coordinate of the 10th point you wish to plot in 3D is X(10).
Thus it is not natural to input matrices into scatter3. If you know X(i), Y(i), Z(i) are indeed the coordinates you want to plot for all i, even though your X, Y, Z are not vectors for some reason, you need to reshape X, Y, Z.
In order to reshape, you can simply do scatter3(X(:), Y(:), Z(:)) which tells Matlab to read your arrays as a vectors. (You should look up in what order this is done. But it is in the intuitive way.) Or you can use reshape. Chances are: reshape is faster for large data set. But ofc (:) is more convenient.
The following should work:
[X,Y] = meshgrid(-8:.5:8);
R = sqrt(X.^2 + Y.^2) + eps;
Z = sin(R)./R;
X = X(:);
Y = Y(:);
Z = Z(:);
scatter3(X,Y,Z)
scatter3 needs vectors, not matrices as far as I can see here
this is my result:
If you want to use meshgrid without reshaping the matrices you have to use plot3 and the 'o' symbol. So you can get a similar result with:
plot3(X,Y,Z,'o')
EDIT:
A question that arose in association with this post was, which of the following methods is more efficient in terms of computation speed: The function reshape(X,[],1), suggested by me, or the simpler colon version X(:), suggested by #Argyll.
After timing the reshape function versus the : method, I have to admit that the latter is more efficient.
I added my results and the code I used to time both functions:
sizes = linspace(100,10000,100);
time_reshape = [];
time_col = [];
for i=1:length(sizes)
X = rand(sizes(i)); % Create random squared matrix
r = #() ResFcn(X);
c = #() ColFcn(X);
time_reshape = [time_reshape timeit(r)/1000] % Take average of 1000 measurements
time_col = [time_col timeit(c)/1000] % Take average of 1000 measurements
end
figure()
hold on
grid on
plot(sizes(2:end), time_col(2:end))
plot(sizes(2:end), time_reshape(2:end))
legend("Colon","Reshape","Location","northwest")
title("Comparison: Reshape vs. Colon Method")
xlabel("Length of squared matrix")
ylabel("Average execution time [s]")
hold off
function res = ResFcn(X)
for i = 1:1000 % Repeat 1000 times
res = reshape(X,[],1);
end
end
function res = ColFcn(X)
for i = 1:1000 % Repeat 1000 times
res = X(:);
end
end

Matlab: Computations with the translations of a function

Before the question, thank you for reading this post.
I have defined a real function, f= #(x) sin(x) + x.^2 and I want to store the N translates of f from 0 to N-1, in a vector w. I mean, I want to store the vector w=( sin(x) + x.^2 , ...., sin(x- (N-1)) + (x-(N-1)).^2)
I have a vector, v, of N real numbers.
Question: How to get the inner product, u, of v and w? I want to store it as a function because I want to calculate the FFT coefficients of u.
I'm trying to do this with a loop but I'm making a mess with the loop/s.
Would you help me, please?
Many thanks.
What I did with the help of #m7913d:
%%%code
N=10;
v=rand(1,N);%%vector
tras=1:N;
tsi=t.*ones(1,N);
%%ff(x)=sin(x)+x.^2
ff=sin(tsi+tras) +(tsi+tras).^2 ;
total=sum(ff.*v,2);
vpa(subs(total,t,0.1));%%check
Fs=100;% Sampling frequency
tt=-pi:1/Fs:pi;% Time vector
L=length(tt); % Signal length
X=double(subs(total, t,tt));
n=2^nextpow2(L);
coef=fft(X,n);
You can generate a cell array of functions as follows:
N = 10;
f= #(x) sin(x) + x.^2;
u = cell(N, 1);
v = rand(N, 1);
g = 0;
for i=0:(N-1)
u{i+1} = #(x) v(i+1)*f(x + i);
g = #(x) g(x) + u{i+1}(x);
end
Note that I calculated the u vector at once, without using the intermediate variable w.
Note that the calculations will be easier (and faster) if you immediately insert your desired x vector. In that case, you do not have to hassle with function handles.
The question is solved. Here is the code
%%%code
N=10;
v=rand(1,N);%%vector
tras=1:N;
tsi=t.*ones(1,N);
%%ff(x)=sin(x)+x.^2
ff=sin(tsi+tras) +(tsi+tras).^2 ;
total=sum(ff.*v,2);
vpa(subs(total,t,0.1));%%check
Fs=100;% Sampling frequency
tt=-pi:1/Fs:pi;% Time vector
L=length(tt); % Signal length
X=double(subs(total, t,tt));
n=2^nextpow2(L);
coef=fft(X,n);
Thanks to everyone for your help.

Calling generic functions on rows of a matrix

I'd like to compute kernel matrices efficiently for generic kernel functions in
Matlab. This means I need to compute k(x,y) for every row x of X
and every row y of Y. Here is some matlab code that computes what I'd
like, but it is rather slow,
function K=compute_kernel( k_func, X, Y )
m = size(X,1);
n = size(Y,1);
K = zeros(m,n);
for i = 1:m
for j = 1:n
K(i,j) = k_func(X(i,:)', Y(j,:)');
end
end
end
Is there any other approach to this problem, e.g. some bsxfun variant that
calls a function on every row taken from X and Y?
pdist2(X,Y, dist_func) unfortunately computes dist_func(X, Y(i,:)), instead of dist_func(X(i,:), Y(i,:)). So the actual function I need is,
function K=compute_kernel( k_func, X, Y )
% Woohoo! Efficient way to compute kernel
size(X)
size(Y)
m = size(X,1);
n = size(Y,1);
for i = 1:n
K(:,i) = pdist2( Y(i,:), X, k_func);
end
It's not as nice as just using pdist2, but is still much more efficient than the previous case.
Have you tired pdist2 with custom distance function?
P.S.
It is best not to use i and j as variables in Matlab

Auto-covariance and Cross Covariance Function in Matlab without using imbuilt functions

x and y are 1x100000 vectors.
I have calculated the mean and variance of x and y. When I want to calculate the autocovariance and cross covariance function the simulation lasts maybe 5 minutes because of my loops. It is not allowed to use xcorr, xcov, mean, cov, var etc.
Please help me.
Thanks in advance.
%%Mean of Vector x
Nx=length(x);
mx= sum(x)/Nx;
%%Mean of Vector y
Ny=length(y);
my=sum(y)/Ny;
%%Variance of x
varx=0;
for i=1:Nx
varx=varx+(abs(x(i)-mx)^(2));
end
varx=varx/Nx;
%%Variance of y
vary=0;
for j=1:Ny
vary=vary+(abs(y(j)-my)^(2));
end
vary=vary/Ny;
%%Auto-Covariance function of x
for k=1:Nx
Cxx(k)=0;
for i=1:(Nx-k+1)
Cxx(k)=Cxx(k)+(x(i+k-1)-mx)*conj((x(i)-my));
end
end
%%Auto-Covariance function of y
for s=1:Ny
Cyy(s)=0;
for j=1:(Ny-s+1)
Cyy(s)=Cyy(s)+(y(j+s-1)-my)*conj((y(j)-mx));
end
end
Use the fact that FFT(corr(x, y)) = FFT(x) * conj(FFTy)):
corrxy = ifft(fft(x) .* conj(fft(y)));
corrxy = [corrxy(end - length(x) + 2:end); corrxy(1:length(x))];
To get the cross-covariance just multiply the correlation by the standard deviations:
covarxy = corrxy * sqrt(varx) * sqrt(vary);
To get the autocovariance, compute the cross covariance between x and itself.
Doing a re-write of this code:
%%Auto-Covariance function of x
for k=1:Nx
Cxx(k)=0;
for i=1:(Nx-k+1)
Cxx(k)=Cxx(k)+(x(i+k-1)-mx)*conj((x(i)-my));
end
end
The following code takes out the inner for-loop:
% x is a [Nx x 1] vector (lets say Nx = 50)
Cxx = zeros(Nx,1); % [Nx x 1] vector of zeros
for k = 1:Nx,
a = (x(k:Nx) -mx); % If k=3, then x(3:50) and a is [Nx-k+1 x 1]
b = (x(1:Nx-k+1)-my); % If k=3, then x(1:48) and b is [Nx-k+1 x 1]
Cxx(k) = a'*conj(b); % Cxx(k) is always 1x1. (*) is a matrix multiply
end
Since x is a really large vector, and the way to take out the the last for-loop for k=1:Nx is to make a [Nx x Nx] matrix, I'm going to leave it at the above answer for now. Plus, if you have the parfor function in the Parallel Computing Toolbox then you can parallelize it to make it run even faster.