Compute the eigenvectors in lapack using predetermined eigenvalues? - c

I have a rather unusual challenge for lapack, and I have spent hours searching for a solution to it.
I have a generalized eigenvalue problem of the traditional form (A - x B = 0). Normally I would use for instance ?hegvx or ?hegvd to calculate the eigenvalues and the eigenvectors.
However the challenge I am facing now, is now that I already know the eigenvalues from the construction of the problem, and therefore I need an efficient lapack routine for calculating the eigenvectors only?
Anyone got a hack for this?

Given the generalised eigenvalue problem
(A - y B) x = 0
And an eigenvalue yn:
(A - yn B) xn = 0
We know A, B and yn, so we can form a new matrix Cn
Cn = A - yn B
Cn xn = 0
You can solve this with any linear algebra solver individually for each eigenvalue. According to the LAPACK docs on linear equations, for a general matrix, double precision, you might use DGETRS
Edit for degenerate eigenvalues:
The null space of the matrix Cn is what we are solving for here (as MvG commented). If
Cn j = 0 and
Cn k = 0
(i.e. degenerate e-vals) then given jTk = 0 (both are still eigenvectors of the AB system) we can say
Call a row of Cn r:
r.k = r.k - jT.k = (r-jT).k
Thus forming a matrix J whose rows are each jT (there must be a name for this, but I don't know it):
(Cn - J) k = 0
Define
Dnj = Cn - J
And now solve for the new matrix Dnj. By construction this will be a new, orthogonal eigenvector of the original matrix with the same degenerate eigenvalue.

Related

doing algebra with an MxNx3 array using vectorization in python?

Suppose I have an MxNx3 array A, where the first two indexes refer to the coordinates a point, and the last index (the number '3') refers to the three components of a vector. e.g. A[4,7,:] = [1,2,3] means that the vector at point (7,4) is (1,2,3).
Now I need to implement the following operations:
Lx = D*ux - (x-xo)
Ly = D*uy + (y-yo)
Lz = D
where D, ux, uy, xo, yo are all constants that are already known. Lx, Ly and Lz are the three components of the vector at each point (x,y) (note: x is the column index and y is the row index respectively). The biggest problem is about the x-xo and y-yo, as x and y are different for different points. So how to carry out these operations for an MxNx3 array efficiently, using vectorized code or some other fast methods?
thanks
You could use the meshgrid function from numpy:
import numpy as np
M=10
N=10
D=1
ux=0.5
uy=0.5
xo=1
yo=1
A=np.empty((M,N,3))
x=range(M)
y=range(N)
xv, yv = np.meshgrid(x, y, sparse=False, indexing='ij')
A[:,:,0]=D*ux - (xv-xo)
A[:,:,1]=D*uy - (yv-yo)
A[:,:,2]=D
If you want to operate on the X and Y values, you should include them in the matrix (or in other matrix) instead of relying in their indexes.
For that, you could use some of range creation routines from Numpy, specially numpy.mgrid.

How to improve the execution time of this function?

Suppose that f(x,y) is a bivariate function as follows:
function [ f ] = f(x,y)
UN=(g)1.6*(1-acos(g)/pi)-0.8;
f= 1+UN(cos(0.5*pi*x+y));
end
How to improve execution time for function F(N) with the following code:
function [VAL] = F(N)
x=0:4/N:4;
y=0:2*pi/1000:2*pi;
VAL=zeros(N+1,3);
for i = 1:N+1
val = zeros(1,N+1);
for j = 1:N+1
val(j) = trapz(y,f(0,y).*f(x(i),y).*f(x(j),y))/2/pi;
end
val = fftshift(fft(val))/N;
l = (length(val)+1)/2;
VAL(i,:)= val(l-1:l+1);
end
VAL = fftshift(fft(VAL,[],1),1)/N;
L = (size(VAL,1)+1)/2;
VAL = VAL(L-1:L+1,:);
end
Note that N=2^p where p>10, so please consider the memory limitations while optimizing the code using ndgrid, arrayfun, etc.
FYI: The code intends to find the central 3-by-3 submatrix of the fftn of
fun=#(a,b) trapz(y,f(0,y).*f(a,y).*f(b,y))/2/pi;
where a,b are in [0,4]. The key idea is that we can save memory using the code above specially when N is very large. But the execution time is still an issue because of nested loops. See the figure below for N=2^2:
This is not a full answer, but some possibly helpful hints:
0) The trivial: Are you sure you need numerics? Can't you do the computation analytically?
1) Do not use function handles:
function [ f ] = f(x,y)
f= 1+1.6*(1-acos(cos(0.5*pi*x+y))/pi)-0.8
end
2) Simplify analytically: acos(cos(x)) is the same as abs(mod(x + pi, 2 * pi) - pi), which should compute slightly faster. Or, instead of sampling and then numerically integrating, first integrate analytically and sample the result.
3) The FFT is a very efficient algorithm to compute the full DFT, but you don't need the full DFT. Since you only want the central 3 x 3 coefficients, it might be more efficient to directly apply the DFT definition and evaluate the formula only for those coefficients that you want. That should be both fast and memory-efficient.
4) If you repeatedly do this computation, it might be helpful to precompute DFT coefficients. Here, dftmtx from the Signal Processing toolbox can assist.
5) To get rid of the loops, think about the problem not in the form of computation instructions, but a single matrix operation. If you consider your input N x N matrix as a vector with N² elements, and your output 3 x 3 matrix as a 9-element vector, then the whole operation you apply (numerical integration via trapz and DFT via fft) appears to be a simple linear transform, which it should be possible to express as an N² x 9 matrix.

Optimize parameters of a pairwise distance function in Matlab

This question is related to matlab: find the index of common values at the same entry from two arrays.
Suppose that I have an 1000 by 10000 matrix that contains value 0,1,and 2. Each row are treated as a sample. I want to calculate the pairwise distance between those samples according to the formula d = 1-1/(2p)sum(a/c+b/d) where a,b,c,d can treated as as the row vector of length 10000 according to some definition and p=10000. c and d are probabilities such that c+d=1.
An example of how to find the values of a,b,c,d: suppose we want to find d between sample i and bj, then I look at row i and j.
If kth entry of row i and j has value 2 and 2, then a=2,b=0,c=1,d=0 (I guess I will assign 0/0=0 in this case).
If kth entry of row i and j has value 2 and 1 or vice versa, then a=1,b=0,c=3/4,d=1/4.
The similar assignment will give to the case for 2,0(a=0,b=0,c=1/2,d=1/2),1,1(a=1,b=1,c=1/2,d=1/2),1,0(a=0,b=1,c=1/4,d=3/4),0,0(a=0,b=2,c=0,d=1).
The matlab code I have so far is using for loops for i and j, then find the cases above by using find, then create two arrays for a/c and b/d. This is extremely slow, is there a way that I can improve the efficiency?
Edit: the distance d is the formula given in this paper on page 13.
Provided those coefficients are fixed, then I think I've successfully vectorised the distance function. Figuring out the formulae was fun. I flipped things around a bit to minimise division, and since I wasn't aware of pdist until #horchler's comment, you get it wrapped in loops with the constants factored out:
% m is the data
[n p] = size(m, 1);
distance = zeros(n);
for ii=1:n
for jj=ii+1:n
a = min(m(ii,:), m(jj,:));
b = 2 - max(m(ii,:), m(jj,:));
c = 4 ./ (m(ii,:) + m(jj,:));
c(c == Inf) = 0;
d = 1 - c;
distance(ii,jj) = sum(a.*c + b.*d);
% distance(jj,ii) = distance(ii,jj); % optional for the full matrix
end
end
distance = 1 - (1 / (2 * p)) * distance;

Vector norm of an array of vectors in MATLAB

When calling norm on a matrix in MATLAB, it returns what's known as a "matrix norm" (a scalar value), instead of an array of vector norms. Is there any way to obtain the norm of each vector in a matrix without looping and taking advantage of MATLAB's vectorization?
You can compute the norm of each column or row of a matrix yourself by using element-wise arithmetic operators and functions defined to operate over given matrix dimensions (like SUM and MAX). Here's how you could compute some column-wise norms for a matrix M:
twoNorm = sqrt(sum(abs(M).^2,1)); %# The two-norm of each column
pNorm = sum(abs(M).^p,1).^(1/p); %# The p-norm of each column (define p first)
infNorm = max(M,[],1); %# The infinity norm (max value) of each column
These norms can easily be made to operate on the rows instead of the columns by changing the dimension arguments from ...,1 to ...,2.
From version 2017b onwards, you can use vecnorm.
The existing implementation for the two-norm can be improved.
twoNorm = sqrt(sum(abs(M).^2,1)); # The two-norm of each column
abs(M).^2 is going to be calculating a whole bunch of unnecessary square roots which just get squared straightaway.
Far better to do:
twoNorm = sqrt(
sum( real(M .* conj(M)), 1 )
)
This efficiently handles real and complex M.
Using real() ensures that sum and sqrt act over real numbers (rather than complex numbers with 0 imaginary component).
Slight addition to P i's answer:
norm_2 = #(A,dim)sqrt( sum( real(A).*conj(A) , dim) )
allows for
B=magic([2,3])
norm_2( B , 1)
norm_2( B , 2)
or as this if you want a norm_2.m file:
function norm_2__ = norm_2 (A_,dim_)
norm_2__ = sqrt( sum( real(A_).*conj(A_) , dim_) ) ;
end

Symmetrical matrix with numpy

from random import *
N = 100
gamma = 0.7
connect = zeros((N,N))
for i in range(N):
for j in range(i+1):
if random() < gamma:
connect[i,j] = 1
connect[j,i] = 1
else:
connect[i,j] = 0
connect[j,i] = 0
What I try to do is to create a symmetrical matrix, filled with zeros and ones (ones with a probability of 0.7).
Here is the double for loop, very inefficient...I shall make something with numpy, which I believe could speed up thing a great deal?
Does anyone know how to proceed?
Thank you very much!
You could use the numpy random module to generate random vectors, and use those vectors to seed the matrix. For example:
import numpy as np
N = 100
gamma = 0.7
connect = np.zeros((N,N),dtype=np.int32)
for i in range(0,N):
dval = np.diag((np.random.random_sample(size=(N-i))<gamma).astype(np.int32),i)
connect += dval
if (i>0):
connect += dval.T
does this diagonally using numpy.diag, but you could do it row-wise to assemble the upper or lower triangular portion, then use addition to form the symmetrical matrix. I don't have a feeling for which might be faster.
EDIT:
In fact this row wise version is about 5 times faster than the diagonal version, which I guess shouldn't be all that surprising given the memory access patterns it uses compared to diagonal assembly.
N = 100
gamma = 0.7
connect = np.zeros((N,N),dtype=np.int32)
for i in range(0,N):
rval = (np.random.random_sample(size=(N-i))<gamma).astype(np.int32)
connect[i,i:] = rval
connect += np.triu(connect,1).T
EDIT 2
This is even simpler and about 4 times faster than the row-wise version above. Here a triangular matrix is formed directly from a full matrix of weights, then added to its transpose to produce the symmetric matrix:
N = 100
gamma = 0.7
a=np.triu((np.random.random_sample(size=(N,N))<gamma).astype(np.int32))
connect = a + np.triu(a,1).T
On the Linux system I tested it on, version 1 takes about 6.5 milliseconds, version 2 takes about 1.5 milliseconds, version 3 takes about 450 microseconds.

Resources