Matlab command unique with matrices - matlab

Suppose I have a matrix B of size a x b and a cell C of size ax1. Each C{a} may is a matrix with potentially different sizes across a.
I would like to create a vector idx of size ax1 containing the integers from 1 to at most a, where idx(j)=idx(h) if
B(j,:)=B(h,:);
C{j}=C{h};
If I had only 1., I would do
[~,~,idx] = unique(B,'rows', 'stable');
I don't know how to introduce 2. Could you help?
Here's a code to construct an example of C
clear
rng default
u0grid_temp=0;
u1grid_temp=randn(3,1);
u2grid_temp=randn(3,1);
[ca, cb, cc] = ndgrid(u0grid_temp, u1grid_temp, u2grid_temp);
u0grid=ca(:);
u1grid=cb(:);
u2grid=cc(:);
sg=size(u0grid,1);
n_U_sets=3;
Ugrid=cell(1,n_U_sets);
n_columns_Ujgrid=5;
Ugrid_all=zeros(sg,n_columns_Ujgrid*n_U_sets);
for g=1:sg
Ugrid{1}(g,:)=[u0grid(g)-u1grid(g) Inf -Inf 0 3];
Ugrid{2}(g,:)=[u0grid(g)-u2grid(g) Inf -Inf 0 3];
Ugrid{3}(g,:)=[Inf -Inf u2grid(g)-u1grid(g) 0 3];
Ugrid_all(g,:)=[Ugrid{1}(g,:) Ugrid{2}(g,:) Ugrid{3}(g,:)];
end
s_id_gridreduced_2=zeros(1, n_U_sets);
for j=1:n_U_sets
s_id_gridreduced_2(:,j)=size(Ugrid{j},2);
end
sU_id_gridreduced_2=prod(s_id_gridreduced_2);
indices_pairs = pairIndices(sU_id_gridreduced_2);
D=cell(sg,1);
for g=1:sg
vectors = cellfun(#(x) {x(g,:)}, Ugrid);
T_temp = cell(1,n_U_sets);
[T_temp{:}] = ndgrid(vectors{:});
T_temp = cat(n_U_sets+1, T_temp{:});
T = reshape(T_temp,[],n_U_sets);
D_temp=[T(indices_pairs(:,1),:) T(indices_pairs(:,2),:)];
D1=D_temp(sum(D_temp(:,1:3)<=D_temp(:,4:6),2)==3, :);
D2_temp=D_temp((sum(D_temp(:,1:3)>D_temp(:,4:6),2)==3) |... %>,>,>
(sum(D_temp(:,1:3)>D_temp(:,4:6),2)==2 & sum(D_temp(:,1:3)==D_temp(:,4:6),2)==1) | ... %>,>,=
(sum(D_temp(:,1:3)>D_temp(:,4:6),2)==1 & sum(D_temp(:,1:3)==D_temp(:,4:6),2)==2), :); %>,=,=
D2=[D2_temp(:,4:6) D2_temp(:,1:3)];
D{g}=[D1;D2];
end
C=cell(1,sg);
for g=1:sg
C{g}=NaN(size(D{g},1),1);
for r=1:size(D{g},1)
if round(D{g}(r,1),8)>=round(D{g}(r,5)+D{g}(r,6),8) || round(D{g}(r,4),8)<=round(D{g}(r,2)+D{g}(r,3),8)
C{g}(r)=1;
else
C{g}(r)=0;
end
end
end

To compute condition 2 you can use a nested loop to compare arrays with isequal:
out = zeros(numel(C), 1);
k = 0;
for i = 1:numel(C)
if out(i)
continue;
end
k = k + 1;
out(i) = k;
for j = i + 1 : numel(C)
if out(j)
continue;
end
if isequal(C{[i, j]})
out(j) = k;
end
end
end
Use unique to combine condition 1 and condition 2:
[~, ~, result] = unique ([idx(:) out(:)], 'rows', 'stable');

Related

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 code a vectorized expression that depends on other vectorized expression?

If for example I have three expressions: A, B and C as follows:
A(i+1) = A(i) + C(i).k
B(i+1) = B(i) + A(i).h
C(i+1) = A(i) + B(i)
where k and h are some constants and m and n is the desired size of C. i is the previous obtained value, i+1 is the next value. Now, if I use for loop, then I can code it as:
A(1)= 2;
B(1)= 5;
C(1)= 3;
for i=1:10
A(i+1) = A(i) + C(i)*2;
B(i+1) = B(i) + A(i)*3;
C(i+1) = A(i) + B(i);
end
And it works just fine. But I want to code it in a vector form, as in without having to use a loop. But the problem is I do not know how to get around the dependency of:
A on its previous value and previous C value
B on it previous values and previous C value of A
C on the previous values of A and B
Here's a matrix-based way to obtain the n-th value of the [A;B;C] vector. I wouldn't exactly call it vectorization, but this could speed things up considerably for you:
[A,B,C] = deal(zeros(11,1));
A(1)= 2;
B(1)= 5;
C(1)= 3;
%% // Original method
for k=1:10
A(k+1) = A(k) + C(k)*2;
B(k+1) = B(k) + A(k)*3;
C(k+1) = A(k) + B(k);
end
%% // Matrix method:
%// [ A ] [1 0 2][ A ]
%// | B | = |3 1 0|| B |
%// [ C ] [1 1 0][ C ]
%// i+1 i
%//
%// [ A ] [1 0 2][ A ] [1 0 2] ( [1 0 2][ A ] )
%// | B | = |3 1 0|| B | = |3 1 0| * ( |3 1 0|| B | )
%// [ C ] [1 1 0][ C ] [1 1 0] ( [1 1 0][ C ] )
%// i+2 i+1 i
%// Thus, this coefficient matrix taken to the n-th power, multiplied by the input
%// vector will yield the values of A(n+1), B(n+1), and C(n+1):
M = [1 0 2
3 1 0
1 1 0];
isequal(M^10*[A(1);B(1);C(1)],[A(11);B(11);C(11)])
In reality you can use M to the appropriate power (positive or negative) to obtain any [A,B,C]n from any [A,B,C]k ...
First, forgive me for abusing Matlab syntax for expressing mathematical stuff.
Consider following code, where we do exactly the same as in your example. Note that A,B,C are the rows of X.
X = zeros(3,N+1);
X(:,1) = [2,5,3];
M= [1,0,2;3,1,0;1,1,0];
for i=1:N
X(:,i+1) = M*X(:,i);
end
This is just a matrix vector notation of the above code. I think it is even slower. Note that we could also compute: X(:,i+1) = M^i * X(:,1) which is even slower.
Notice that we can use the eigenvalue decomposition:
[V,D] = eigs(M);
X(:,i+1) = [V*D*inv(V)]^i * X;
Therefore
X(:,i+1) = V*D^i*inv(V) * X;
So V*D^i*inv(V) is an explicit formula for the i+1th term of X. I suggest computing those analytically, and plug the formula you get into your code again.
EDIT: I wrote some code that should be close to analyitcally solving the system, you can compare the runtimes. It seems in the end preallocation with your first method is still the fastest IF you need ALL the terms. If you only need one of them, my suggested method is certainly quicker.
clear;clc
N = 10000000;
tic
A(1)= 2;
B(1)= 5;
C(1)= 3;
A = zeros(1,N+1);
B=A;C=A;
for i=1:N
A(i+1) = A(i) + C(i)*2;
B(i+1) = B(i) + A(i)*3;
C(i+1) = A(i) + B(i);
end
toc
tic
X = zeros(3,N+1);
X(:,1) = [2,5,3];
M= [1,0,2;3,1,0;1,1,0];
for i=1:N
X(:,i+1) = M*X(:,i);
end
toc
tic
M= [1,0,2;3,1,0;1,1,0];
[V,D]=eig(M);
v=0:N;
d=diag(D);
B=bsxfun(#power,repmat(d,1,N+1),v);
Y=bsxfun(#times,V * B, V \[2;5;3]);
toc
tic
M= [1,0,2;3,1,0;1,1,0];
[V,D]=eig(M);
v=0:N;
d=diag(D);
Y = ones(3,N+1);
for i=1:N
Y(:,i+1) = d.*Y(:,i);
end
Y=bsxfun(#times,V * B, V \[2;5;3]);
toc

How to oppositely order two vectors in Matlab?

I have the code below for oppositely ordering two vectors. It works, but I want to specify the line
B_diff(i) = B(i) - B(i+1);
to hold true not just for only
B_diff(i) = B(i) - B(i+1); but for
B_diff(i) = B(i) - B(i+k); where k can be any integer less than or equal to n. The same applies to "A". Any clues as to how I can achieve this in the program?
For example, I want to rearrange the first column of the matrix
A =
1 4
6 9
3 8
4 2
such that, the condition should hold true not only for
(a11-a12)(a21-a22)<=0;
but also for all
(a11-a13)(a21-a23)<=0;
(a11-a14)(a21-a24)<=0;
(a12-a13)(a22-a23)<=0;
(a12-a14)(a22-a24)<=0; and
(a13-a14)(a23-a24)<=0;
## MATLAB CODE ##
A = xlsread('column 1');
B = xlsread('column 2');
n = numel(A);
B_diff = zeros(n-1,1); %Vector to contain the differences between the elements of B
count_pos = 0; %To count the number of positive entries in B_diff
for i = 1:n-1
B_diff(i) = B(i) - B(i+1);
if B_diff(i) > 0
count_pos = count_pos + 1;
end
end
A_desc = sort(A,'descend'); %Sort the vector A in descending order
if count_pos > 0 %If B_diff contains positive entries, divide A_desc into two vectors
A_less = A_desc(count_pos+1:n);
A_great = sort(A_desc(1:count_pos),'ascend');
A_new = zeros(n,1); %To contain the sorted elements of A
else
A_new = A_desc; %This is then the sorted elements of A
end
if count_pos > 0
A_new(1) = A_less(1);
j = 2; %To keep track of the index for A_less
k = 1; %To keep track of the index for A_great
for i = 1:n-1
if B_diff(i) <= 0
A_new(i+1) = A_less(j);
j = j + 1;
else
A_new(i+1) = A_great(k);
k = k + 1;
end
end
end
A_diff = zeros(n-1,1);
for i = 1:n-1
A_diff(i) = A_new(i) - A_new(i+1);
end
diff = [A_diff B_diff]
prod = A_diff.*B_diff
The following code orders the first column of A opposite to the order of the second column.
A= [1 4; 6 9; 3 8; 4 2]; % sample matrix
[~,ix]=sort(A(:,2)); % ix is the sorting permutation of A(:,2)
inverse=zeros(size(ix));
inverse(ix) = numel(ix):-1:1; % the un-sorting permutation, reversed
B = sort(A(:,1)); % sort the first column
A(:,1)=B(inverse); % permute the first column according to inverse
Result:
A =
4 4
1 9
3 8
6 2

Jacobi solver going into an infinite loop

I can't seem to find a fix to my infinite loop. I have coded a Jacobi solver to solve a system of linear equations.
Here is my code:
function [x, i] = Jacobi(A, b, x0, TOL)
[m n] = size(A);
i = 0;
x = [0;0;0];
while (true)
i =1;
for r=1:m
sum = 0;
for c=1:n
if r~=c
sum = sum + A(r,c)*x(c);
else
x(r) = (-sum + b(r))/A(r,c);
end
x(r) = (-sum + b(r))/A(r,c);
xxx end xxx
end
if abs(norm(x) - norm(x0)) < TOL;
break
end
x0 = x;
i = i + 1;
end
When I terminate the code it ends at the line with xxx
The reason why your code isn't working is due to the logic of your if statements inside your for loops. Specifically, you need to accumulate all values for a particular row that don't belong to the diagonal of that row first. Once that's done, you then perform the division. You also need to make sure that you're dividing by the diagonal coefficient of A for that row you're concentrating on, which corresponds to the component of x you're trying to solve for. You also need to remove the i=1 statement at the beginning of your loop. You're resetting i each time.
In other words:
function [x, i] = Jacobi(A, b, x0, TOL)
[m n] = size(A);
i = 0;
x = [0;0;0];
while (true)
for r=1:m
sum = 0;
for c=1:n
if r==c %// NEW
continue;
end
sum = sum + A(r,c)*x(c); %// NEW
end
x(r) = (-sum + b(r))/A(r,r); %// CHANGE
end
if abs(norm(x) - norm(x0)) < TOL;
break
end
x0 = x;
i = i + 1;
end
Example use:
A = [6 1 1; 1 5 3; 0 2 4]
b = [1 2 3].';
[x,i] = Jacobi(A, b, [0;0;0], 1e-10)
x =
0.048780487792648
-0.085365853612062
0.792682926806031
i =
20
This means it took 20 iterations to achieve a solution with tolerance 1e-10. Compare this with MATLAB's built-in inverse:
x2 = A \ b
x2 =
0.048780487804878
-0.085365853658537
0.792682926829268
As you can see, I specified a tolerance of 1e-10, which means we are guaranteed to have 10 decimal places of accuracy. We can certainly see 10 decimal places of accuracy between what Jacobi gives us with what MATLAB gives us built-in.

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)]);