When and how to do zero-padding for a discrete convolution? - matlab

I want to take the discrete convolution of two 1-D vectors. The vectors correspond to intensity data as a function of frequency. My goal is to take the convolution of one intensity vector B with itself and then take the convolution of the result with the original vector B, and so on, each time taking the convolution of the result with the original vector B. I want the final result to have the same length as the original vector B.
I am starting with a code in IDL that I am trying to modify to MATLAB. The relevant part of the code reads:
for i=1,10 do begin
if i lt 2 then A=B else A=Rt
n=i+1
Rt=convol(A,B,s,center=0,/edge_zero)
...
endfor
which I have rewritten in MATLAB
for i = 2:11
if i < 3
A = B; % indices start at 0, not 1
else
A = Rt;
end
n = i + 1;
% Scale by 1/s
Rt = (1/s).*conv(A,B);
...
end
But I am not sure how to incorporate the zero-padding that uses the edge_zero option. In IDL, the convolution calculates the values of elements at the edge of the vector as if the vector were padded with zeroes. The optional third option for the convolution function in MATLAB includes the option 'same', which returns the central part of the convolution of the same size as u for conv(u,v), but that doesn't seem to be the correct way about this problem. How do I do an analogous zero-padding in MATLAB?

Here is a code I needed for my doctoral research that I know does the zero padding correctly. I hope it helps.
function conv_out=F_convolve_FFT(f,g,dw,flipFlag),
if(nargin<4), flipFlag==0; end;
% length of function f to be convolved, initialization of conv_out, and padding p
N=length(f); conv_out=zeros(1,N); p=zeros(1,N);
% uncomment if working with tensor f,g's of rank 3 or greater
% f=F_reduce_rank(f); g=F_reduce_rank(g);
% padding. also, this was commented out: EN=length(fp);
fp = [f p]; gp = [g p];
% if performing convolution of the form c(w) = int(f(wp)g(w+wp),wp) = int(f(w-wp)gbar(wp),wp) due to reverse-order domain on substitution
if(flipFlag==1), gp=fliplr(gp); end;
% perform the convolution. You do NOT need to multiply the invocation of "F_convolve_FFT(f,g,dw,flipFlag)" in your program by "dx", the finite-element.
c1 = ifft(fft(fp).*fft(gp))*dw;
% if performing "reverse" convolution, an additional circshift is necessary to offset the padding
if(flipFlag==1), c1=circshift(c1',N)'; end;
% offset the padding by dNm
if(mod(N,2)==0), dNm=N/2; elseif(mod(N,2)==1), dNm=(N-1)/2; end;
% padding. also, this was commented out: EN=length(fp);
conv_out(:)=c1(dNm+1:dNm+N);
return

Related

Finite difference scheme in Matlab

I am trying to implement a finite difference scheme for KdV equation in MATLAB, and I have most of the code ready, except for approximation at the first level using initial condition. It was suggested I use Euler's method to obtain 'u' at m=1, and then use the scheme for m>=2.
How does one apply Euler's method in this context? Even just a general answer for approximation at the first level would be appreciated.
I am including my code for reference
close all
clear
clc
% Generating grid with n points, with the space between two points being
%(x2-x1)/(n-1)
x = linspace(-5,5,1001);
N=1001;
h=x(2)-x(1); % grid size
dt=0.05;
%Soliton initial condition
Am=8; %Amplitude
mu=sqrt(Am/2);
x0=-15;
c=1;
syms H(x)
H(x)=piecewise(x < 0,0,x > 0,1);
u= Am*(sech(mu*(x'-x0))).^2+c^2*H(x);
% Creating a matrix A - First order
A = diag(ones(N-1,1),1)-diag(ones(N-1,1),-1);
Cvector = zeros(N, 1);
Cvector(end) = 1;
u_ic = Cvector;
% First order finite difference scheme
diff_first=A*u/(2*h)+1/(2*h)*u_ic;
% Weighted average matrix for the term 'u'
A_w = diag(ones(N-1,1),1)+diag(ones(N-1,1),-1)+diag(ones(N,1));
diff_w=2*A_w*u +2*u_ic;
% Matrix multiplication of first derivative and weighted average for 6uu_x
diff_middle=diff_first.*diff_w;
% Creating a Third Order Matrix
r = zeros(1,N);
r(2:3) = [-2,1];
c = -r;
A_third = toeplitz(c,r);
% Difference scheme for third order term
diff_third=A_third*u/(h*h*h)-1/(h*h*h)*u_ic;
%Computing finite difference method
u = u - 2*dt*diff_middle-dt*diff_third;
plot(u)

Parity check matrix of LDPC encoder and decoder in Matlab

MATLAB provides powerful LDPC encoder and decoder objects in the latest versions. However the parity check matrix H, with dimension (N-K) by N, needs to satisfy the following condition:
"The last N−K columns in the parity check matrix H must be an invertible matrix in GF(2)"
Indeed, this condition is not easy to be satisfied for most LDPC codes, although we know that there is at least one (N-M) by (N-M) invertible sub-block in the parity check matrix H, if H is with a full rank.
I want to know that, if there exists a fast algorithm or a MATLAB function, which can find out an invertible sub-block in H provided H is with a full rank. So that we can use the MATLAB objects and Simulink blocks conveniently.
I tried repermuting the columns of H matrix until it matches the Malab
% Programmer: Taha Valizadeh
% Date: September 2016
%% Column Permutation
% Permute columns of a binary Matrix until the rightmost square matrix is
% invertible over GF(2)
% matrix dimensions:
[~, n] = size(H);
% Initialization
HInvertible = H;
PermutorIndex = 1:n;
flag = true;
counter = 0;
% Initial Report
disp('Creating a ParityCheck matrix which is suitable for MATLAB COMM Tollbox')
% Permute columns
while flag
% Check if the rightmost square matrix is invertible over GF(2)
try
EncoderObject = comm.LDPCEncoder(sparse(HInvertible));
% Check if new matrix works
fprintf(['ParityCheck Matrix become suitable for Matlab LDPC Encoder ',...
'after ',num2str(counter),' permutations!\n'])
flag = false; % Break the loop
catch
% Choose different columns for the rightmost part of matrix
counter = counter+1; %Permutation Counter
PermutorIndex = randperm(n);
HInvertible = H(:,PermutorIndex);
end
end

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.

Unexpected result with DFT in MATLAB

I have a problem when calculate discrete Fourier transform in MATLAB, apparently get the right result but when plot the amplitude of the frequencies obtained you can see values very close to zero which should be exactly zero. I use my own implementation:
function [y] = Discrete_Fourier_Transform(x)
N=length(x);
y=zeros(1,N);
for k = 1:N
for n = 1:N
y(k) = y(k) + x(n)*exp( -1j*2*pi*(n-1)*(k-1)/N );
end;
end;
end
I know it's better to use fft of MATLAB, but I need to use my own implementation as it is for college.
The code I used to generate the square wave:
x = [ones(1,8), -ones(1,8)];
for i=1:63
x = [x, ones(1,8), -ones(1,8)];
end
MATLAB version: R2013a(8.1.0.604) 64 bits
I have tried everything that has happened to me but I do not have much experience using MATLAB and I have not found information relevant to this issue in forums. I hope someone can help me.
Thanks in advance.
This will be a numerical problem. The values are in the range of 1e-15, while the DFT of your signal has values in the range of 1e+02. Most likely this won't lead to any errors when doing further processing. You can calculate the total squared error between your DFT and the MATLAB fft function by
y = fft(x);
yh = Discrete_Fourier_Transform(x);
sum(abs(yh - y).^2)
ans =
3.1327e-20
which is basically zero. I would therefore conclude: your DFT function works just fine.
Just one small remark: You can easily vectorize the DFT.
n = 0:1:N-1;
k = 0:1:N-1;
y = exp(-1j*2*pi/N * n'*k) * x(:);
With n'*k you create a matrix with all combinations of n and k. You then take the exp(...) of each of those matrix elements. With x(:) you make sure x is a column vector, so you can do the matrix multiplication (...)*x which automatically sums over all k's. Actually, I just notice, this is exactly the well-known matrix form of the DFT.

Filter image that contains NaNs in Matlab?

I have a 2d array (doubles) representing some data, and it has a bunch of NaNs in it. The contour plot of the data looks like this:
All of the white spaces are NaNs, the gray diamond is there for reference, and the filled contour shows the shape of my data. When I filter the data with imfilt, the NaNs significantly chew into the data, so we end up with something like this:
You can see that the support set is significantly contracted. I can't use this, as it has chewed into some of the more interesting variations on the edges (for reasons specific to my experiments, those edges are important).
Is there a function to filter within an island of NaNs that treats edges similar to edges of rectangular filtering windows, instead of just killing the edges? Sort of like an nanmean function, except for convolving images?
Here is my filter code:
filtWidth = 7;
imageFilter=fspecial('gaussian',filtWidth,filtSigma);
%convolve them
dataFiltered = imfilter(rfVals,imageFilter,'symmetric','conv');
and the code for plotting the contour plot:
figure
contourf(dataFiltered); hold on
plot([-850 0 850 0 -850], [0 850 0 -850 0], 'Color', [.7 .7 .7],'LineWidth', 1); %the square (limits are data-specific)
axis equal
There is some code at the Mathworks file exchange (ndanfilter.m) that comes close to what I want, but I believe it only interpolates NaNs that are sprinkled on the interior of an image, not data showing this island-type effect.
Note: I just found nanconv.m, which does exactly what I want, with a very intuitive usage (convolve an image, ignoring NaN, much like nanmean works). I've made this part of my accepted answer, and include a comparison to the performance of the other answers.
Related questions
Gaussian filtering a image with Nan in Python
The technique I ended up using was the function nanconv.m at Matlab's File Exchange. It does exactly what I was looking for: it runs the filter in a way that ignores the NaNs just the way that Matlab's built-in function nanmean does. This is a hard to decipher from the documentation of the function, which is a tad cryptic.
Here's how I use it:
filtWidth = 7;
filtSigma = 5;
imageFilter=fspecial('gaussian',filtWidth,filtSigma);
dataFiltered = nanconv(data,imageFilter, 'nanout');
I'm pasting the nanconv function below (it is covered by the BSD license). I will post images etc when I get a chance, just wanted to post what I ended up doing for anyone curious about what I did.
Comparison to other answers
Using gnovice's solution the results look intuitively very nice, but there are some quantitative blips on the edges that were a concern. In practice, the extrapolation of the image beyond the edges led to many spuriously high values at the edges of my data.
Using krisdestruction's suggestion of replacing the missing bits with the original data, also looks pretty decent (especially for very small filters), but (by design) you end up with unfiltered data at the edges, which is a problem for my application.
nanconv
function c = nanconv(a, k, varargin)
% NANCONV Convolution in 1D or 2D ignoring NaNs.
% C = NANCONV(A, K) convolves A and K, correcting for any NaN values
% in the input vector A. The result is the same size as A (as though you
% called 'conv' or 'conv2' with the 'same' shape).
%
% C = NANCONV(A, K, 'param1', 'param2', ...) specifies one or more of the following:
% 'edge' - Apply edge correction to the output.
% 'noedge' - Do not apply edge correction to the output (default).
% 'nanout' - The result C should have NaNs in the same places as A.
% 'nonanout' - The result C should have ignored NaNs removed (default).
% Even with this option, C will have NaN values where the
% number of consecutive NaNs is too large to ignore.
% '2d' - Treat the input vectors as 2D matrices (default).
% '1d' - Treat the input vectors as 1D vectors.
% This option only matters if 'a' or 'k' is a row vector,
% and the other is a column vector. Otherwise, this
% option has no effect.
%
% NANCONV works by running 'conv2' either two or three times. The first
% time is run on the original input signals A and K, except all the
% NaN values in A are replaced with zeros. The 'same' input argument is
% used so the output is the same size as A. The second convolution is
% done between a matrix the same size as A, except with zeros wherever
% there is a NaN value in A, and ones everywhere else. The output from
% the first convolution is normalized by the output from the second
% convolution. This corrects for missing (NaN) values in A, but it has
% the side effect of correcting for edge effects due to the assumption of
% zero padding during convolution. When the optional 'noedge' parameter
% is included, the convolution is run a third time, this time on a matrix
% of all ones the same size as A. The output from this third convolution
% is used to restore the edge effects. The 'noedge' parameter is enabled
% by default so that the output from 'nanconv' is identical to the output
% from 'conv2' when the input argument A has no NaN values.
%
% See also conv, conv2
%
% AUTHOR: Benjamin Kraus (bkraus#bu.edu, ben#benkraus.com)
% Copyright (c) 2013, Benjamin Kraus
% $Id: nanconv.m 4861 2013-05-27 03:16:22Z bkraus $
% Process input arguments
for arg = 1:nargin-2
switch lower(varargin{arg})
case 'edge'; edge = true; % Apply edge correction
case 'noedge'; edge = false; % Do not apply edge correction
case {'same','full','valid'}; shape = varargin{arg}; % Specify shape
case 'nanout'; nanout = true; % Include original NaNs in the output.
case 'nonanout'; nanout = false; % Do not include NaNs in the output.
case {'2d','is2d'}; is1D = false; % Treat the input as 2D
case {'1d','is1d'}; is1D = true; % Treat the input as 1D
end
end
% Apply default options when necessary.
if(exist('edge','var')~=1); edge = false; end
if(exist('nanout','var')~=1); nanout = false; end
if(exist('is1D','var')~=1); is1D = false; end
if(exist('shape','var')~=1); shape = 'same';
elseif(~strcmp(shape,'same'))
error([mfilename ':NotImplemented'],'Shape ''%s'' not implemented',shape);
end
% Get the size of 'a' for use later.
sza = size(a);
% If 1D, then convert them both to columns.
% This modification only matters if 'a' or 'k' is a row vector, and the
% other is a column vector. Otherwise, this argument has no effect.
if(is1D);
if(~isvector(a) || ~isvector(k))
error('MATLAB:conv:AorBNotVector','A and B must be vectors.');
end
a = a(:); k = k(:);
end
% Flat function for comparison.
o = ones(size(a));
% Flat function with NaNs for comparison.
on = ones(size(a));
% Find all the NaNs in the input.
n = isnan(a);
% Replace NaNs with zero, both in 'a' and 'on'.
a(n) = 0;
on(n) = 0;
% Check that the filter does not have NaNs.
if(any(isnan(k)));
error([mfilename ':NaNinFilter'],'Filter (k) contains NaN values.');
end
% Calculate what a 'flat' function looks like after convolution.
if(any(n(:)) || edge)
flat = conv2(on,k,shape);
else flat = o;
end
% The line above will automatically include a correction for edge effects,
% so remove that correction if the user does not want it.
if(any(n(:)) && ~edge); flat = flat./conv2(o,k,shape); end
% Do the actual convolution
c = conv2(a,k,shape)./flat;
% If requested, replace output values with NaNs corresponding to input.
if(nanout); c(n) = NaN; end
% If 1D, convert back to the original shape.
if(is1D && sza(1) == 1); c = c.'; end
end
One approach would be to replace the NaN values with nearest-neighbor interpolates using scatteredInterpolant (or TriScatteredInterp in older MATLAB versions) before performing the filtering, then replacing those points again with NaN values afterward. This would be akin to filtering a full 2-D array using the 'replicate' argument as opposed to the 'symmetric' argument as a boundary option for imfilter (i.e. you're replicating as opposed to reflecting values at the jagged NaN boundary).
Here's what the code would look like:
% Make your filter:
filtWidth = 7;
imageFilter = fspecial('gaussian', filtWidth, filtWidth);
% Interpolate new values for Nans:
nanMask = isnan(rfVals);
[r, c] = find(~nanMask);
[rNan, cNan] = find(nanMask);
F = scatteredInterpolant(c, r, rfVals(~nanMask), 'nearest');
interpVals = F(cNan, rNan);
data = rfVals;
data(nanMask) = interpVals;
% Filter the data, replacing Nans afterward:
dataFiltered = imfilter(data, imageFilter, 'replicate', 'conv');
dataFiltered(nanMask) = nan;
Okay without using your plot function, I can still give you a solution. What you want to do is find all the new NaN's and replace it with the original unfiltered data (assuming it is correct). While it's not filtered, it's better than reducing the domain of your contour image.
% Toy Example Data
rfVals= rand(100,100);
rfVals(1:2,:) = nan;
rfVals(:,1:2) = nan;
% Create and Apply Filter
filtWidth = 3;
imageFilter=fspecial('gaussian',filtWidth,filtWidth);
dataFiltered = imfilter(rfVals,imageFilter,'symmetric','conv');
sum(sum(isnan( dataFiltered ) ) )
% Replace New NaN with Unfiltered Data
newnan = ~isnan( rfVals) & isnan( dataFiltered );
dataFiltered( newnan ) = rfVals( newnan );
sum(sum(isnan( rfVals) ) )
sum(sum(isnan( dataFiltered ) ) )
Detect new NaN using the following code. You can also probably use the xor function.
newnan = ~isnan( rfVals) & isnan( dataFiltered );
Then this line sets the indices in dataFiltered to the values in rfVals
dataFiltered( newnan ) = rfVals( newnan );
Results
From the lines printed in the console and my code, you can see that the number of NaN in dataFiltered is reduced from 688 to 396 as was the number of NaN in rfVals.
ans =
688
ans =
396
ans =
396
Alternate Solution 1
You can also use a smaller filter near the edges by specifying a smaller kernel and merging it after, but if you just want valid data with minimal code, my main solution will work.
Alternate Solution 2
An alternate approach is to pad/replace the NaN values with zero or some constant you want so that it will work, then truncate it. However for signal processing/filtering, you will probably want my main solution.
Want to improve this post? Add citations from reputable sources by editing the post. Posts with unsourced content may be edited or deleted.
nanfilter does exactly the same thing with nanconv when filtering as long as the filter is the same. If you get the nan values before you use nanfilter and then add the back to the after-filtered matrix, you will get the same result with what you get from nanconv with the option 'nanout', as long as you use the same filter.