Replace the diagonal of matrix - matlab

I am interesting to replace the diagonal of matrix D to 1,2,3,4.
This is matrix D:
A=[1,2,3,4,2,3,4,5; 3,4,5,6,4,5,6,7];
D=[A;A];
D=[D D]; % size of matrix [4x16] %

To set the main diagonal to integers starting a 1 and incrementing by 1:
D(eye(4)==1) = 1:4
Or to generalize it:
n = min(size(D));
D(eye(n)==1) = 1:n;
note here that the ==1 is to convert the output of eye(n), the identity matrix, to type logical.
EDIT:
This is just a guess at what you mean by all the diagonals but here goes:
n = size(D,1);
m = size(D,2);
I = repmat(eye(min([n,m])), ceil(n/m), ceil(m/n));
I = I(1:n, 1:m)==1
d = repmat(1:min([n,m]), 1, max([ceil(n/m), ceil(m/n)]));
d = d(1:max(m,n));
D(I) = d

Related

Tensor multiplication w/o looping in Matlab

I have a 3d array A, e.g. A=rand(N,N,K).
I need an array B s.t.
B(n,m) = norm(A(:,:,n)*A(:,:,m)' - A(:,:,m)*A(:,:,n)','fro')^2 for all indices n,m in 1:K.
Here's the looping code:
B = zeros(K,K);
for n=1:K
for m=1:K
B(n,m) = norm(A(:,:,n)*A(:,:,m)' - A(:,:,m)*A(:,:,n)','fro')^2;
end
end
I don't want to loop through 1:K.
I can create an array An_x_mt of size NK x NK s.t.
An_x_mt equals A(:,:,n)*A(:,:,m)' for all n,m in 1:K by
An_x_mt = Ar*Ac_t;
with
Ac_t=reshape(permute(A,[2 1 3]),size(A,1),[]);
Ar=Ac_t';
How do I create an array Am_x_nt also of size NK x NK s.t.
Am_x_nt equals A(:,:,m)*A(:,:,n)' for all n,m in 1:K
so that I could do
B = An_x_mt - Am_x_nt
B = reshape(B,N,N,[]);
B = reshape(squeeze(sum(sum(B.^2,1),2)),K,K);
Thx
For those who can't/won't use mmx and want to stick to pure Matlab code, here's how you could do it. mat2cell and cell2mat functions are your friends:
[N,~,nmat]=size(A);
Atc = reshape(permute(A,[2 1 3]),N,[]); % A', N x N*nmat
Ar = Atc'; % A, N*nmat x N
Anmt_2d = Ar*Atc; % An*Am'
Anmt_2d_cell = mat2cell(Anmt_2d,N*ones(nmat,1),N*ones(nmat,1));
Amnt_2d_cell = Anmt_2d_cell'; % ONLY products transposed, NOT their factors
Amnt_2d = cell2mat(Amnt_2d_cell); % Am*An'
Anm = Anmt_2d - Amnt_2d;
Anm = Anm.^2;
Anm_cell = mat2cell(Anm,N*ones(nmat,1),N*ones(nmat,1));
d = cellfun(#(c) sum(c(:)), Anm_cell); % squared Frobenius norm of each product; nmat x nmat
Alternatively, after computing Anmt_2d_cell and Amnt_2d_cell, you could convert them to 3d with the 3rd dimension encoding the (n,m) and (m,n) indices and then do the rest of the computations in 3d. You would need the permn() utility from here https://www.mathworks.com/matlabcentral/fileexchange/7147-permn-v-n-k
Anmt_3d = cat(3,Anmt_2d_cell);
Amnt_3d = cat(3,Amnt_2d_cell);
Anm_3d = Anmt_3d - Amnt_3d;
Anm_3d = Anm_3d.^2;
Anm = squeeze(sum(sum(Anm_3d,1),2));
d = zeros(nmat,nmat);
nm=permn(1:nmat, 2); % all permutations (n,m) with repeat, by-row order
d(sub2ind([nmat,nmat],nm(:,1),nm(:,2))) = Anm;
For some reason, the 2nd option (3D arrays) is twice faster.
Hopes this helps.

How do I compute the left null space for a matrix over GF(2) in MATLAB?

Let's say I have a matrix over GF(2) , i.e. a binary matrix. Now how do I go about computing the left null space of the given matrix over the finite field of 2?
Does MATLAB provide an in-built function for this?
I don't know of Matlab packages for linear algebra in finite space, but I programmed a simple
function that calculates LU-factorizations of matrices modulo a prime p (for example, 2):
function [L,D,U,rows,cols] = ModLU(A,p)
%
% LU-factorization of A, modulo p:
% A(rows,cols) - mod(L * diag(D)*U,p)
%
[m,n] = size(A);
% inverses in mod-p:
% mod(k*invp(k+1)) = 0 if k==0; 1 otherwise
invp = 2:p-2;
for i = 2:p-2; invp = mod(invp.*[2:p-2],p); end
invp = [0,1,invp,p-1];
% Initialize outputs:
L = eye(m); U = A;
rows = 1:m;
cols = 1:n;
% Sweep
for i = 1:m
% Pivoting
[row,col] = find(U(i:end,:));
if isempty(row); break; end
row = row(1)+i-1; col = col(1);
r = 1:m; r(i) = row; r(row) = i;
c = 1:n; c(i) = col; c(col) = i;
ri = rows(i); rows(i) = rows(row); rows(row)=ri;
ci = cols(i); cols(i) = cols(col); cols(col)=ci;
rinv = 1:m; rinv(r) = 1:m;
U = U(r,c); L=L(r,r);
% Gaussian elimination
L(i+1:end,i ) = mod(invp(U(i,i)+1) * U(i+1:end,i),p);
U(i+1:end,i:end) = mod(U(i+1:end,i:end) + (p-L(i+1:end,i)) * U(i,i:end),p);
end
% Factorize diagonal
D = zeros(m,1); D(1:min(m,n)) = diag(U);
U = mod(diag(invp(D+1)) * U,p );
Also, for an upper triangular matrix with ones on the diagonal, a function that calculates
the right-null space modulo p:
function N = NullPU(U,p)
% for an upper triangular matrix, calculate a base for the null space modulo p:
% U * N = 0
n = size(U,2);
rank = size(find(diag(U)),1);
A = U(1:rank,:);
for i=rank:-1:2
A(1:i-1,:) = mod(A(1:i-1,:) + (p-1) * A(1:i-1,i) * A(i,:),p);
end
N = [mod(p-A(:,rank+1:end),p); eye(n-rank)];
These functions are simply combined into a function that calculates the null space of
matrix A, modulo p:
function N = NullP(A,p)
% Calculate a basis for the null space of A, modulo p:
% mod(A*N,p) = 0
[L,D,U,rows,cols] = ModLU(A,p);
N = NullPU(U,p);
N(cols,:) = N;
Note that this function calculates a base for the right null space of A, modulo p. The left
null space is found using
N = NullP(A',p)';

Performance of using a matrix as vector index

In my code I have a slow part of which the idea can be summarized in the following short example:
A = randi(10,5); %Random 5×5 matrix containing integers ranging from 0 to 10
B = rand(10,1); %Random 10×1 vector containing values ranging from 0 to 1
C = B(A); %New 5×5 matrix consisting of elements from B, indexed using A
In my case, the matrix A is sized 1000×1000, B is a 500×1 vector and C is also 1000×1000. Given that this 3rd line is in a for loop, where A is constant and B is updated every iteration, how can I further improve speed performance? According to the profile viewer 75% of code execution is at this single line. As expected, using a for loop for this operation is much slower (10x for a 1000×1000 matrix):
AA = A(:); %Convert matrix to vector
for k=1:length(AA) %Loop through this vector and use it as index
D(k) = B(AA(k));
end
E = reshape(D,5,5); %Reshape vector to matrix of 5x5
Any ideas to optimize this?
Edit: Script used to measure performance:
N = 1500;
A = randi(500,N);
AA = A(:);
D = zeros(N,N);
B = rand(500,1);
f1 = #() VectorIndex(A,B);
timeit(f1,1)
f2 = #() LoopIndex(AA,B,N);
timeit(f2,1)
function C = VectorIndex(A,B)
C = B(A);
end
function D = LoopIndex(AA,B,N)
D = zeros(N,N);
for k=1:length(AA)
D(k) = B(AA(k));
end
D = reshape(D,N,N);
end

Smarter way to generate a matrix of zeros and ones in Matlab

I would like to generate all the possible adjacency matrices (zero diagonale) of an undirected graph of n nodes.
For example, with no relabeling for n=3 we get 23(3-1)/2 = 8 possible network configurations (or adjacency matrices).
One solution that works for n = 3 (and which I think is quite stupid) would be the following:
n = 3;
A = [];
for k = 0:1
for j = 0:1
for i = 0:1
m = [0 , i , j ; i , 0 , k ; j , k , 0 ];
A = [A, m];
end
end
end
Also I though of the following which seems to be faster but something is wrong with my indexing since 2 matrices are missing:
n = 3
C = [];
E = [];
A = zeros(n);
for i = 1:n
for j = i+1:n
A(i,j) = 1;
A(j,i) = 1;
C = [C,A];
end
end
B = ones(n);
B = B- diag(diag(ones(n)));
for i = 1:n
for j = i+1:n
B(i,j) = 0;
B(j,i) = 0;
E = [E,B];
end
end
D = [C,E]
Is there a faster way of doing this?
I would definitely generate the off-diagonal elements of the adjacency matrices with binary encoding:
n = 4; %// number of nodes
m = n*(n-1)/2;
offdiags = dec2bin(0:2^m-1,m)-48; %//every 2^m-1 possible configurations
If you have the Statistics and Machine Learning Toolbox, then squareform will easily create the matrices for you, one by one:
%// this is basically a for loop
tmpcell = arrayfun(#(k) squareform(offdiags(k,:)),1:size(offdiags,1),...
'uniformoutput',false);
A = cat(2,tmpcell{:}); %// concatenate the matrices in tmpcell
Although I'd consider concatenating along dimension 3, then you can see each matrix individually and conveniently.
Alternatively, you can do the array synthesis yourself in a vectorized way, it's probably even quicker (at the cost of more memory):
A = zeros(n,n,2^m);
%// lazy person's indexing scheme:
[ind_i,ind_j,ind_k] = meshgrid(1:n,1:n,1:2^m);
A(ind_i>ind_j) = offdiags.'; %'// watch out for the transpose
%// copy to upper diagonal:
A = A + permute(A,[2 1 3]); %// n x n x 2^m matrix
%// reshape to n*[] matrix if you wish
A = reshape(A,n,[]); %// n x (n*2^m) matrix

How to vectorize a sum of dot products between a normal and array of points

I need to evaluate following expression (in pseudo-math notation):
∑ipi⋅n
where p is a matrix of three-element vectors and n is a three-element vector. I can do this with for loops as follows but I can't figure out
how to vectorize this:
p = [1 1 1; 2 2 2];
n = [3 3 3];
s = 0;
for i = 1:size(p, 1)
s = s + dot(p(i, :), n)
end
Why complicate things? How about simple matrix multiplication:
s = sum(p * n(:))
where p is assumed to be an M-by-3 matrix.
I think you can do it with bsxfun:
sum(sum(bsxfun(#times,p,n)))
----------
% Is it the same for this case?
----------
n = 200; % depending on the computer it might be
m = 1000*n; % that n needs to be chosen differently
A = randn(n,m);
x = randn(n,1);
p = zeros(m,1);
q = zeros(1,m);
tic;
for i = 1:m
p(i) = sum(x.*A(:,i));
q(i) = sum(x.*A(:,i));
end
time = toc; disp(['time = ',num2str(time)]);