Impulse response function in matlab - matlab

There are examples for summation of a vector but not for matrix in Matlab. So please help solve the following:
How to write impulse response function in matlab?
I want program in Matlab for the equation:
hij(t) = ∑_(k=1)to n (φik*φjk*e-xwk*sin(wdk(t))/(M*wdk))
h is impulse response function
φ is mode shape
x is constant
wk is kth mode nat frequency
wdk is kth mode damped frequency
M is mass matrix.

Summing on a matrix, in general, looks like this:
>> A = randi(5,[3,6]) % Creating a random [3 x 6] integer matrix
A =
3 4 4 1 2 4
3 4 4 3 3 2
4 2 1 5 2 3
>> sum(A) % Sums on rows (dim=1 is default) so you get a [1 x 6] vector
ans =
10 10 9 9 7 9
>> sum(A,2) % Sums on columns (dim=2) so you get a [3 x 1] vector
ans =
18
19
17
And similarly if you had a 3D matrix V, then you could do sum(V,3) to sum on the slices.
If you want more specific help, please note the dimensions of each input (phi_i, phi_j, M, w, and wd)

Related

Merging sorted pairs

I have two (or more but if solved for two, it's solved for any number) 2-by-N matrices which represent points with an x (the first row) and y (the second row) coordinates. The points are always sorted in the increasing x coordinate. What I want to do is I want to merge these two matrices into one 3-by-N matrix so that if two points (one from each matrix) have the same x coordinate, they would form one column in the new matrix, the first row being the x coordinate and the second and third row being the two y coordinates. However, if there is a point in one matrix that has x coordinate different than all other points in the second matrix, I still want to have full 3-element column that is placed such that the x coordinates are still sorted and the missing value from the other matrix is replaced by the nearest value with lower x coordinate (or NaN if there is none).
Better to explain by example.
First matrix:
1 3 5 7 % x coordinate
1 2 3 4 % y coordinate
Second matrix:
2 3 4 7 8 % x coordinate
5 6 7 8 9 % y coordinate
Desired result:
1 2 3 4 5 7 8 % x coordinate
1 1 2 2 3 4 4 % y coordinate from first matrix
NaN 5 6 7 7 8 9 % y coordinate from second matrix
My question is, how can I do it effectively in matlab/octave and numpy? (Effectively because I can always do it "manually" with loops but that doesn't seem right.)
You can do it with interp1 and the keyword 'previous' for strategy (you can also choose 'nearest' if you do not care if it is larger or smaller) and 'extrap' for allowing extrapolation.
Define the matrices
a=[...
1 3 5 7;...
1 2 3 4];
b=[...
2 3 4 7 8;...
5 6 7 8 9];
Then find the interpolation points
x = unique([a(1,:),b(1,:)]);
And interpolate
[x ; interp1(a(1,:),a(2,:),x,'previous','extrap') ; interp1(b(1,:),b(2,:),x,'previous','extrap') ]
Timeit results:
I tested the algorithms on
n = 1e6;
a = cumsum(randi(3,2,n),2);
b = cumsum(randi(2,2,n),2);
and got:
Wolfie: 1.7473 s
Flawr: 0.4927 s
Mine: 0.2757 s
This verions uses set operations:
a=[...
1 3 5 7;...
1 2 3 4];
b=[...
2 3 4 7 8;...
5 6 7 8 9];
% compute union of x coordinates
c = union(a(1,:),b(1,:));
% find indices of x of a and b coordinates in c
[~,~,ia] = intersect(a(1,:),c);
[~,~,ib] = intersect(b(1,:),c);
% create output matrix
d = NaN(3,numel(c));
d(1,:) = c;
d(2,ia) = a(2,:);
d(3,ib) = b(2,:);
% fill NaNs
m = isnan(d);
m(:,1) = false;
i = find(m(:,[2:end,1])); %if you have multiple consecutive nans you have to repeat these two steps
d(m) = d(i);
disp(d);
Try it online!
Your example:
a = [1 3 5 7; 1 2 3 4];
b = [2 3 4 7 8; 5 6 7 8 9];
% Get the combined (unique, sorted) `x` coordinates
output(1,:) = unique([a(1,:), b(1,:)]);
% Initialise y values to NaN
output(2:3, :) = NaN;
% Add x coords from `a` and `b`
output(2, ismember(output(1,:),a(1,:))) = a(2,:);
output(3, ismember(output(1,:),b(1,:))) = b(2,:);
% Replace NaNs in columns `2:end` with the previous value.
% A simple loop has the advantage of capturing multiple consecutive NaNs.
for ii = 2:size(output,2)
colNaN = isnan(output(:, ii));
output(colNaN, ii) = output(colNaN, ii-1);
end
If you have more than 2 matrices (as suggested in your question) then I'd advise
Store them in a cell array, and loop over them to do the calls to ismember, instead of having one code line per matrix hardcoded.
The NaN replacement loop is already vectorised for any number of rows.
This is the generic solution for any number of matrices, demonstrated with a and b:
mats = {a, b};
cmats = horzcat(mats);
output(1, :) = unique(cmats(1,:));
output(2:numel(mats)+1, :) = NaN;
for ii = 1:size(mats)
output(ii+1, ismember(output(1,:), mats{ii}(1,:))) = mats{ii}(2,:);
end
for ii = 2:size(output,2)
colNaN = isnan(output(:,ii));
output(colNaN, ii) = output(colNaN, ii-1);
end

How to compute 1D convolution in Matlab?

Suppose I have 2 vectors, data vector:
x=[2 1 2 1]
and weights vector
y=[1 2 3]
I want Matlab to convolve these vectors in sense of 1D neural network, i.e. run y as window against x and compute convolutions:
If I run built-in function conv then I get
>> conv(x,y)
ans =
2 5 10 8 8 3
which contains correct values in the middle but has something unknown at margins. Manual for conv function looks completely different with what I want.
If I run
>> conv(x,y, 'same')
ans =
5 10 8 8
I also get something strange.
You were very close to solving it by specifying the 3rd input to conv, but instead of 'same' you should've used 'valid':
x = [2 1 2 1];
y = [1 2 3];
conv(x,y,'valid')
ans =
10 8
Just reverse the filter:
x = [2,1,2,1];
y = [1,2,3];
z = conv(x,flip(y),'valid');

How to create a polynomial that accepts vectors?

I have a problem with creating a polynomial function of arbitrary length in Matlab, that would work, when used with a vector as an argument.
I have to do an algorithm, that includes and returns a value of a polynomial.
Bellow is my code:
n = 4 % For simplicity, could be arbitrary positive integer
f = #(x) x.^[0:n] %Coefficients are 1 (for this example), if not, would be multiplied with vector of them
p = #(x) sum(f(x)) %My polynomial
>> p(5)
ans =
781
This goes as planed. But because I need a plot, I need my polynomial to be able to receive vectors of values and return them. But when I do this, an error pops up.
Example:
>>p([1 2 3 4])
Error using .^
Matrix dimensions must agree.
Error in #(x)x.^[0:n]
Error in #(x)sum(f(x))
What I want it to return is a vector of length 4 with values of my polynomial [p(1) p(2) p(3) p(4)]
I got around this by creating a values vector with a for loop, but am just wondering, is it possible to change my code, so this would work?
The problem can be easily fixed using a row and a column vector, instead of two row vectors:
p([1 2 3 4]')
and explicitly defining the dimension along which you want to take the summation:
p = #(x) sum(f(x), 2)
Explanation
Note that .^ is an element wise operation. p([1 2 3 4 5]) works, because both row vectors have the same size, but doesn't return the desired result, i.e. it calculates 1^0 + 2^1 + 3^2 + 4^3 + 5^4 = 701.
Matlab automatically expands (in pseudo matlab code)
[1 .^ [0 1 2 3 4]
2
3
4]
to
[1 1 1 1 .^ [0 1 2 3 4
2 2 2 2 0 1 2 3 4
3 3 3 3 0 1 2 3 4
4 4 4 4] 0 1 2 3 4]
Backward compatibility (2006-2016a)
The definition of f should be changed because matlab does not support automatic arithmetic expansion yet.
f = #(x) bsxfun(#power, x, 0:n);
Backward compatibility (1996-2005)
bsxfun didn't exist yet, so one should resort to repmat.

Error: chol: input matrix must be positive definite

How can I fix this error?
mvnpdf.m
% y = mvnpdf(x,mu,Sigma)
% Compute multivariate normal pdf for x given mean mu and covariance matrix
% sigma. The dimension of x is d x p, mu is 1 x p and sigma is p x p.
function pdf = mvnpdf(x,mu,sigma)
[d,p] = size(x);
% mu can be a scalar, a 1xp vector or a nxp matrix
if nargin == 1, mu = 0; end
if all(size(mu) == [1,p]), mu = repmat(mu,[d,1]); end
if nargin < 3
pdf = (2*pi)^(-p/2) * exp(-sumsq(x-mu,2)/2);
else
r = chol(sigma);
pdf = (2*pi)^(-p/2) * exp(-sumsq((x-mu)/r,2)/2) / prod(diag(r));
end
pdfdep.m
function pdfmx = pdfdep(train, test)
% computes probability density for all classes
% assuming feature independence
% train - train set; the first column contains label
% used to compute mean and variation for all classes
% test - test set (without labels)
% pdfmx - matrix of probability density for all classes
% class with label idx is stored in pdfmx(:,idx)
classnb = rows(unique(train(:,1)));
pdfmx = ones(rows(test), classnb);
for cl=1:classnb
clidx = train(:,1) == cl;
mu = mean(train(clidx,2:end));
sigma = cov(train(clidx,2:end));
pdfmx(:,cl) = mvnpdf(test, mu, sigma);
end
mat.txt
1 2 3 4 5 6 7 8
2 3 4 5 6 7 8 1
3 4 5 6 7 8 1 2
4 5 6 7 8 1 2 3
1 8 7 6 5 4 3 2
2 7 6 5 4 3 2 9
3 6 5 4 3 2 9 8
4 5 4 3 2 9 8 7
1 8 7 6 5 4 3 2
3 6 5 4 3 2 9 8
Error message:
>> mat2 = mat;
>> pdfdep(mat, mat2)
error: chol: input matrix must be positive definite
error: called from
mvnpdf at line 13 column 7
pdfdep at line 20 column 15
>>
The error is quite self explanatory
input matrix must be positive definite
Means that your matrix (sigma) is not positive definite, thus you cannot run cholesky decomposition on it. There are many ways used to estimate covariance in a nice manner, simply computing the empirical estimate (what you do by calling cov) does not work when your data is degenerated (it lies in low dimensional manifold). One of the simplest solutions is to use "pulled estimator" of form:
cov(X) + eps * I
instead of
cov(X)
thus just change
sigma = cov(train(clidx,2:end));
to incorporate this additional + eps * I (where I is identity matrix of appropriate dimension).

Multiplying matrix Matlab

I have a matrix M[1,98] and a matrix N[1,x], let's assume in this case x =16.
What I want is to multiply N by M , make the sum by element, and increment the matrix M. With the finality of getting an output of [1,98].
It's a bit confusing. An example:
M=[2 3 4 5 6 7]
N=[1 2 3]
it1=(2*1)+(3*2)+(4*3)+(5*0)+...=20
it2=(3*1)+(4*2)+(5*3)+(6*0)+...=26
it3=..
Output=[20 26 ... ... ... ...]
Like that until the end but considering the size of the matrix N variable. M has always the same size.
That's a convolution:
result = conv(M, N(end:-1:1), 'valid');
To achieve the result you want you need to flip the second vector and keep only the "valid" part of the convolution (no border effects).
In your example:
>> M = [2 3 4 5 6 7];
>> N = [1 2 3];
>> result = conv(M, N(end:-1:1), 'valid')
result =
20 26 32 38