Numerically compute derivative of complex-valued function in MATLAB - matlab

I would like to compute the derivative of a complex-valued function (Holomorphic function) numerically in MATLAB.
I have computed the function in a grid on the complex plane, and I've tried to compute the derivative using the Cauchy–Riemann relations.
Given:
u = real(f), v = imag(f), x = real(point), y = imag(point)
The derivative should be given by: f' = du/dx + i dv/dx = dv/dy - i du/dy
where 'd' is the derivative operator.
I've tried the following code:
stepx = 0.01;
stepy = 0.01;
Nx = 2/stepx +1;
Ny = 2/stepy +1;
[re,im] = meshgrid([-1:stepx:1], [-1:stepy:1]);
cplx = re + 1i*im;
z = cplx.^3;
The derivative should be given by:
f1 = diff(real(z),1,2)/stepx +1i* diff(imag(z),1,2)/stepx;
or
f2 = diff(imag(z),1,1)/stepy - 1i* diff(real(z),1,1)/stepy;
But the two derivatives, which are suppose to be equal, do not match.
What am I doing wrong?
Let's count the number of elements which differs for less than stepx (assuming stepx = stepy):
lm = min(size(f1));
A = f1(1:lm,1:lm);
B = f2(1:lm,1:lm);
sum(sum(abs(A - B) <= stepx))
and using the fix proposed by #A. Donda
f1i = interp1(1 : Ny, f1, 1.5 : Ny);
f2i = interp1(1 : Nx, f2 .', 1.5 : Nx) .';
sum(sum(abs(f1i - f2i) <= stepx))
In the second case they all differ for less than stepx as it should be, while in the first case it's not true.

The problem is that using the two different expressions, discretized for use in Matlab, you are computing the approximate derivatives at different points in the complex plane. Let's say you are at imaginary value y and you compute the differences along the real axis x, then the ith difference estimates the derivative at (x(i) + x(i + 1))/2, i.e. at all the midpoints between two subsequent x-values. The other way around you estimate the derivative at a given x but at all the midpoints between two subsequent y-values.
This also leads to different sizes of the resulting matrices. Using the first formula you get a matrix of size 201x200, the other of size 200x201. That's because in the first variant there are 200 midpoints along x, but 201 y-values, and vice versa.
So the answer is, you are not doing anything wrong, you are just interpreting the result wrongly.
You can solve the problem by interpolating explicitly along the other dimension (the one not used for the derivative):
f1i = interp1(1 : Ny, f1, 1.5 : Ny);
f2i = interp1(1 : Nx, f2 .', 1.5 : Nx) .';
Where f1 is computed according to your first formula and f2 according to the second. Now both derivatives are evaluated at points that are midpoints along both dimensions, which is why both matrices are of size 200x200.
If you compare them now, you will see that they are identical up to numerical error (after all, diff computes only approximate derivatives and interp1 makes interpolation errors). For your stepsize, this error is maximally 1e-4, and it can be further reduced by using a smaller stepsize.

Related

Cross Correlation of two sequences with different length using convolution in Matlab

Assume that we have two simple sequences with different lengths:
x = rand(3,1);
y = rand(2,1);
I calculated the cross correlation between them and plot it such below:
r_1 = xcorr(x,(y));
tx = 1:length(x);
ty = 1:length(y);
tr = ceil(-(length(x)+length(y)-1)/2) : floor((length(x)+length(y)-1)/2);
subplot(2,2,1); stem(tr,r_1); title('XC');
I wanted to calculate the cross correlation using convolution and show that it's result is equal to the result when using xcorr(). But when I implemented it like this:
r_2 = conv(x,fliplr(y));
tx = 1:length(x);
ty = 1:length(y);
tr = ceil(-(length(x)+length(y)-1)/2) : floor((length(x)+length(y)-1)/2);
subplot(2,2,1); stem(tr,r_2); title('XC');
the length of r_1 and r_2 are different and
I got this error:
Error using stem (line 43)
X must be same length as Y.
Thanks for your helps.
There are three issues in your code:
You are applying fliplr to y, but y is a column vector, so you are not really flipping it. You should apply flipud; or flip if you want it to work for any vector in general.
From the documentation of xcorr:
C = xcorr(A,B) [...] > If Aand B are of different length, the shortest one is zero-padded.
So, to make the result of conv equal to that of xcorr you need to zero-pad the shorter vector.
In the general case, for column vectors x and y you can use [x; zeros(numel(y)-numel(x),1)] instead of x and [y; zeros(numel(x)-numel(y),1)] instead of y. This will extend the shorter vector and leave the other as is. Note that this works because, according to the documentation of zeros,
zeros(M,N,P,...) [...] The size inputs M, N, and P... should be nonnegative integers. Negative integers are treated as 0.
The correlation applies a complex conjugate to the second input. You should do the same with convolution. In your example it doesn't matter because inputs are real, but it should be done in general.
Combining the three items above:
r_1 = xcorr(x, y);
r_2 = conv([x; zeros(numel(y)-numel(x),1)], conj(flip([y; zeros(numel(x)-numel(y),1)])));
should give the same result.
Example:
x = [3.1; -4.3; 5.6];
y = [2.6; -8.4];
give
r_1 =
0.000000000000004
-26.040000000000006
44.180000000000000
-58.219999999999999
14.559999999999999
r_2 =
0
-26.040000000000003
44.180000000000000
-58.219999999999999
14.559999999999999
which are the same up to numerical precision errors.

How to do circular convolution between 2 functions with cconv?

I was asked to do circular convolution between two functions by sampling them, using the functions cconv. A known result of this sort of convolution is: CCONV( sin(x), sin(x) ) == -pi*cos(x)
To test the above I did:
w = linspace(0,2*pi,1000);
l = linspace(0,2*pi,1999);
stem(l,cconv(sin(w),sin(w))
but the result I got was:
which is absolutely not -pi*cos(x).
Can anybody please explain what is wrong with my code and how to fix it?
In the documentation of cconv it says that:
c = cconv(a,b,n) circularly convolves vectors a and b. n is the length of the resulting vector. If you omit n, it defaults to length(a)+length(b)-1. When n = length(a)+length(b)-1, the circular convolution is equivalent to the linear convolution computed with conv.
I believe that the reason for your problem is that you do not specify the 3rd input to cconv, which then selects the default value, which is not the right one for you. I have made an animation showing what happens when different values of n are chosen.
If you compare my result for n=200 to your plot you will see that the amplitude of your data is 10 times larger whereas the length of your linspace is 10 times bigger. This means that some normalization is needed, likely a multiplication by the linspace step.
Indeed, after proper scaling and choice of n we get the right result:
res = 100; % resolution
w = linspace(0,2*pi,res);
dx = diff(w(1:2)); % grid step
stem( linspace(0,2*pi,res), dx * cconv(sin(w),sin(w),res) );
This is the code I used for the animation:
hF = figure();
subplot(1,2,1); hS(1) = stem(1,cconv(1,1,1)); title('Autoscaling');
subplot(1,2,2); hS(2) = stem(1,cconv(1,1,1)); xlim([0,7]); ylim(50*[-1,1]); title('Constant limits');
w = linspace(0,2*pi,100);
for ind1 = 1:200
set(hS,'XData',linspace(0,2*pi,ind1));
set(hS,'YData',cconv(sin(w),sin(w),ind1));
suptitle("n = " + ind1);
drawnow
% export_fig(char("D:\BLABLA\F" + ind1 + ".png"),'-nocrop');
end

Inverted pendelum matrix derivative approximation

Here I've written a dynamic function as:
function dAx = dynamic(t,x)
global u;
g = 9.8;
l = 0.5;
m = 0.5;
h = 2;
dx(1,1) = x(2);
dx(2,1) = g/l*sin(x(1))-h/(m*l^2)*x(2)+1/(m*l)*cos(x(1))*u(1,1);
dx(3,1) = g*lcos(x(3))-u(2,1);
A = [x(1)*x(2)+10*x(1);10*x(2)-5*x(1);x(3)]
dx = 1e-3
dAx = [(((x(1)+dx)+(x(1)-dx))*((x(2)+dx)+(x(2)-dx)))/(2*dx)+(10*(x(1)+dx)+(x(1)-dx))/(2*dx);
((10*(x(2)+dx)+(x(2)-dx))-5*((x(1)+dx)+(x(1)-dx)))/(2*dx);
((x(3)+dx)+(x(3)-dx))/(2*dx)]; % dA/dx using central derivative method computation
Here there is a matrix A (3*1) and function out put is derivative of matrix A related to system states.
I've tried to use central difference method.Is my derivative matrix calculation correct?
#Lahidj dAx/dx is a matrix of the size of nA by nx. You compute this column by column. Take the first state (eg. x(1)). Increment and decrement it by a small value (dx), like 1e-3. Get the A vectors for increased and decreased values of alpha. Compute the difference between these two vectors and divide it by two times of dx. (A_plus - A_minus)/(2*dx). Repeat this for the rest of the states/columns, you would get dAx/dx.

numerical integration for Gaussian function - indefinite integral

My approach
fun = #(y) (1/sqrt(pi))*exp(-(y-1).^2).*log(1 + exp(-4*y))
integral(fun,-Inf,Inf)
This gives NaN.
So I tried plotting it.
y= -10:0.1:10;
plot(y,exp(-(y-1).^2).*log(1 + exp(-4*y)))
Then understood that domain (siginificant part) is from -4 to +4.
So changed the limits to
integral(fun,-10,10)
However I do not want to always plot the graph and then know its limits. So is there any way to know the integral directly from -Inf to Inf.
Discussion
If your integrals are always of the form
I would use a high-order Gauss–Hermite quadrature rule.
It's similar to the Gauss-Legendre-Kronrod rule that forms the basis for quadgk but is specifically tailored for integrals over the real line with a standard Gaussian multiplier.
Rewriting your equation with the substitution x = y-1, we get
.
The integral can then be computed using the Gauss-Hermite rule of arbitrary order (within reason):
>> order = 10;
>> [nodes,weights] = GaussHermiteRule(order);
>> f = #(x) log(1 + exp(-4*(x+1)))/sqrt(pi);
>> sum(f(nodes).*weights)
ans =
0.1933
I'd note that the function below builds a full order x order matrix to compute nodes, so it shouldn't be made too large.
There is a way to avoid this by explicitly computing the weights, but I decided to be lazy.
Besides, event at order 100, the Gaussian multiplier is about 2E-98, so the integrand's contribution is extremely minimal.
And while this isn't inherently adaptive, a high-order rule should be sufficient in most cases ... I hope.
Code
function [nodes,weights] = GaussHermiteRule(n)
% ------------------------------------------------------------------------------
% Find the nodes and weights for a Gauss-Hermite Quadrature integration.
%
if (n < 1)
error('There is no Gauss-Hermite rule of order 0.');
elseif (n < 0) || (abs(n - round(n)) > eps())
error('Given order ''n'' must be a strictly positive integer.');
else
n = round(n);
end
% Get the nodes and weights from the Golub-Welsch function
n = (0:n)' ;
b = n*0 ;
a = b + 0.5 ;
c = n ;
[nodes,weights] = GolubWelsch(a,b,c,sqrt(pi));
end
function [xk,wk] = GolubWelsch(ak,bk,ck,mu0)
%GolubWelsch
% Calculate the approximate* nodes and weights (normalized to 1) of an orthogonal
% polynomial family defined by a three-term reccurence relation of the form
% x pk(x) = ak pkp1(x) + bk pk(x) + ck pkm1(x)
%
% The weight scale factor mu0 is the integral of the weight function over the
% orthogonal domain.
%
% Calculate the terms for the orthonormal version of the polynomials
alpha = sqrt(ak(1:end-1) .* ck(2:end));
% Build the symmetric tridiagonal matrix
T = full(spdiags([[alpha;0],bk,[0;alpha]],[-1,0,+1],length(alpha),length(alpha)));
% Calculate the eigenvectors and values of the matrix
[V,xk] = eig(T,'vector');
% Calculate the weights from the eigenvectors - technically, Golub-Welsch requires
% a normalization, but since MATLAB returns unit eigenvectors, it is omitted.
wk = mu0*(V(1,:).^2)';
end
I've had success with transforming such infinite-bounded integrals using a numerical variable transformation, as explained in Numerical Recipes 3e, section 4.5.3. Basically, you substitute in y=c*tan(t)+b and then numerically integrate over t in (-pi/2,pi/2), which sweeps y from -infinity to infinity. You can tune the values of c and b to optimize the process. This approach largely dodges the question of trying to determine cutoffs in the domain, but for this to work reliably using quadrature you have to know that the integrand does not have features far from y=b.
A quick and dirty solution would be to look for a position, where your function is sufficiently small enough and then taking it as limits. This assumes that for x>0 the function fun decreases montonically and fun(x) is roughly the same size as fun(-x) for all x.
%// A small number
epsilon = eps;
%// Stepsize for searching bound
stepTest = 1;
%// Starting position for searching bound
position = 0;
%// Not yet small enough
smallEnough = false;
%// Search bound
while ~smallEnough
smallEnough = (fun(position) < eps);
position = position + stepTest;
end
%// Calculate integral
integral(fun, -position, position)
If your were happy with plotting the function, deciding by eye where you can cut, then this code will suffice, I guess.

Numerical derivative of a vector

I have a problem with numerical derivative of a vector that is x: Nx1 with respect to another vector t (time) that is the same size of x.
I do the following (x is chosen to be sine function as an example):
t=t0:ts:tf;
x=sin(t);
xd=diff(x)/ts;
but the answer xd is (N-1)x1 and I figured out that it does not compute derivative corresponding to the first element of x.
is there any other way to compute this derivative?
You are looking for the numerical gradient I assume.
t0 = 0;
ts = pi/10;
tf = 2*pi;
t = t0:ts:tf;
x = sin(t);
dx = gradient(x)/ts
The purpose of this function is a different one (vector fields), but it offers what diff doesn't: input and output vector of equal length.
gradient calculates the central difference between data points. For an
array, matrix, or vector with N values in each row, the ith value is
defined by
The gradient at the end points, where i=1 and i=N, is calculated with
a single-sided difference between the endpoint value and the next
adjacent value within the row. If two or more outputs are specified,
gradient also calculates central differences along other dimensions.
Unlike the diff function, gradient returns an array with the same
number of elements as the input.
I know I'm a little late to the game here, but you can also get an approximation of the numerical derivative by taking the derivatives of the polynomial (cubic) splines that runs through your data:
function dy = splineDerivative(x,y)
% the spline has continuous first and second derivatives
pp = spline(x,y); % could also use pp = pchip(x,y);
[breaks,coefs,K,r,d] = unmkpp(pp);
% pre-allocate the coefficient vector
dCoeff = zeroes(K,r-1);
% Columns are ordered from highest to lowest power. Both spline and pchip
% return 4xn matrices, ordered from 3rd to zeroth power. (Thanks to the
% anonymous person who suggested this edit).
dCoeff(:, 1) = 3 * coefs(:, 1); % d(ax^3)/dx = 3ax^2;
dCoeff(:, 2) = 2 * coefs(:, 2); % d(ax^2)/dx = 2ax;
dCoeff(:, 3) = 1 * coefs(:, 3); % d(ax^1)/dx = a;
dpp = mkpp(breaks,dCoeff,d);
dy = ppval(dpp,x);
The spline polynomial is always guaranteed to have continuous first and second derivatives at each point. I haven not tested and compared this against using pchip instead of spline, but that might be another option as it too has continuous first derivatives (but not second derivatives) at every point.
The advantage of this is that there is no requirement that the step size be even.
There are some options to work-around your issue.
First: you can make your domain larger. Instead of N, use N+1 gridpoints.
Second: depending on the end-point of interest, you can use
Forward difference: F(x + dx) - F(x)
Backward difference: F(x) - F(x - dx)