How to draw weighted convex hull in Matlab - matlab

Suppose I have a Matlab array PE of size Ex1. The elements of PE are between 0 and 1 and they sum up to one.
Take another array PEY of size ExY. The elements of PEY are one or zero. Moreover, for each row, there exists at least a one. Y=3.
For example
clear
E=4;
Y=3;
PE=[1/2; 1/6; 1/6; 1/6];
PEY=[1 0 0; 0 1 1; 1 1 1; 0 1 1];
Now, consider the simplex with vertices (1,0,0), (0,1,0), and (0,0,1)
patch([0 0 1],[0 1 0],[1 0 0],[0.8 0.8 0.8]);
axis equal
axis([0 1 0 1 0 1])
view(120,30)
I want to draw a convex subset A of such simplex. A is constructed as follows.
STEP 1: We construct an Ex1 cell array PEY_expanded such that, for each e-th row of PEY that has more than a 1, we write down all admissible 1xY vectors containing just a 1 and stack them in PEY_expanded{e}.
PEY_expanded=cell(E,1);
for e=1:E
if isequal(PEY(e,:),[1 1 1])
PEY_expanded{e}=[1 0 0; 0 1 0; 0 0 1];
elseif isequal(PEY(e,:),[1 1 0])
PEY_expanded{e}=[1 0 0; 0 1 0];
elseif isequal(PEY(e,:),[1 0 1])
PEY_expanded{e}=[1 0 0; 0 0 1];
elseif isequal(PEY(e,:),[0 1 1])
PEY_expanded{e}=[0 1 0; 0 0 1];
else
PEY_expanded{e}=PEY(e,:);
end
end
STEP 2: Take Cartesian product PEY_expanded{1} x PEY_expanded{2} x ... PEY_expanded{E} and get PEY_cartesian.
Note: the code below is specific for E=4 and not general
size_PEY_expanded=zeros(E,1);
for e=1:E
size_PEY_expanded(e)=size(PEY_expanded{e},1);
end
[a,b,c,d]=ndgrid(1: size_PEY_expanded(1),1: size_PEY_expanded(2),...
1: size_PEY_expanded(3), 1: size_PEY_expanded(4));
PEY_Cartesian= [PEY_expanded{1}(a,:),PEY_expanded{2}(b,:),...
PEY_expanded{3}(c,:), PEY_expanded{4}(d,:)];
PEY_Cartesian_rearranged=cell(prod(size_PEY_expanded),1);
for i=1:prod(size_PEY_expanded)
PEY_Cartesian_rearranged{i}=[PEY_Cartesian(i,1:3); PEY_Cartesian(i,4:6);...
PEY_Cartesian(i,7:9); PEY_Cartesian(i,10:end)];
end
PEY_Cartesian=PEY_Cartesian_rearranged;
STEP 3: For each possible cell of PEY_Cartesian, for y=1,...,Y, weight PEY_Cartesian{i}(e,y) by PE(e) and then sum across e.
PY=zeros(prod(size_PEY_expanded),Y);
for i=1:prod(size_PEY_expanded)
for y=1:Y
temp=0;
for e=1:E
temp=temp+PE(e)*PEY_Cartesian{i}(e,y);
end
PY(i,y)=temp;
end
end
STEP 4: Draw the region A that is the convex hull of the rows of PY (black region in the picture)
%Need https://fr.mathworks.com/matlabcentral/fileexchange/37004-suite-of-functions-to-perform-uniform-sampling-of-a-sphere
close all
patch([0 0 1],[0 1 0],[1 0 0],[0.8 0.8 0.8]);
axis equal
axis([0 1 0 1 0 1])
view(120,30)
hold on
T = delaunayTriangulation(PY);
K = convexHull(T);
patch('Faces',K,'Vertices',T.Points,'FaceColor','k','edgecolor','k');
hold on
QUESTION:
The algorithm above is unfeasible for large E. In my actual case I have E=216, for example. In particular, step 2 is unfeasible.
Could you suggest an easier way to proceed? Given that A is a convex region, maybe there is some shortcut I'm unable to see.

Related

Left circular shift in Matlab for each row of a matrix of a different number of positions

I have a matrix A in Matlab of dimension mxn composed of zeros and ones, and a matrix J of dimension mx1 reporting some integers from {1,...,n}.
I want to construct a matrix B of dimension mxn such that for
(1) B(1,:)=A(1,:)
(2) for i=2,...,m, B(i,:) is obtained by shifting A(i,:) LEFT circular of a number of positions equal to (J(1)-1)+ (J(2)-1)+...+ (J(i-1)-1)
This code does what I want
m=4;
n=5;
A=[1 0 1 1 0; ...
0 1 0 0 1; ...
1 1 0 0 0; ...
0 0 0 0 1;
J=[2;1;5;8];
B=zeros(m,n);
B(1,:)=A(1,:);
foridx=cumsum(J); %mx1
shift=foridx-(1:1:m).'; %mx1
v=shift(1:m-1); %(m-1)x1
for i=2:m
B(i,:)=(circshift((A(i,:)).', -v(i-1),1)).';
end
I would like to avoid the final loop. I like the answer here by Divakar but it is for a right circular shift.
Arelevant=A(2:end,:);
idx0 = mod(bsxfun(#plus,n-v(:),1:n)-1,n);
out = Arelevant(bsxfun(#plus,(idx0*(m-1)),(1:(m-1))'));
B(2:end,:)=out;
Could you help me to have something similar for a left circular shift?
Here is a little trick to perform this operation without loop.
I use an fft in order to shift each line.
m=4;
n=5;
A=[1 0 1 1 0; ...
0 1 0 0 1; ...
1 1 0 0 0; ...
0 0 0 0 1;]
J=[2;1;5;8];
foridx=cumsum(J); %mx1
shift=foridx-(1:1:m).'; %mx1
v=[0;shift(1:m-1)]; %(m-1)x1
L=real(round(ifft(fft(A,[],2) .* exp(2i*pi/n*v*(0:m)) ,[],2))) %left shift
R=real(round(ifft(fft(A,[],2) .* exp(-2i*pi/n*v*(0:m)) ,[],2))) %right shift

How to add legend in a highlighted graph?

I want to add legend in a graph G according to different highlighted edges. Is it possible to do it with only one graph G?
Here is a toy example to play with. I have a plot G.
adj =[0 0 1 1 1; % adjacency matrix
1 0 1 0 1;
0 1 0 1 1;
1 1 1 0 1;
0 0 1 0 0]
G = digraph(adj);
I highlighted all edges with 3 colors according to types of edges. 3 types of edges indicate there are 3 different relation between nodes in my case.
This is how I highlighted all edges:
M(:,:,1)=[0 0 1 0 0;1 0 0 0 1;0 0 0 0 0;1 0 0 0 0;0 0 1 0 0];
M(:,:,2)=[0 0 0 1 0; 0 0 1 0 0;0 1 0 0 1;0 0 0 0 0;0 0 0 0 0];
M(:,:,3)=[0 0 0 0 1; 0 0 0 0 0; 0 0 0 1 0;0 1 1 0 1;0 0 0 0 0];
The difficulty in my problem is that I have to remove vertices whose out-degree is less than some integel (say it's 2). Thus I can't plot 3 graphs independently.
rmvNode=find(outdegree(G)<2); % outdegree is the reason why single G is neccesary
adj(rmvNode,:)=[]; adj(:,rmvNode)=[];
M(:,rmvNode,:)=[]; M(rmvNode,:,:)=[];
G=digraph(adj);
Then we can plot it.
for k=1:3 %Looping depending on the third dimension
[r,c]= find(M(:,:,k)); %Finding non-zero elements
s{k}=r; t{k}=c;
end
h=plot(G);
highlight(h,s{1},t{1},'EdgeColor','r');
highlight(h,s{2},t{2},'EdgeColor','g');
highlight(h,s{3},t{3},'EdgeColor','b');
My ideal situation would be a legend like this: assign red edges to label 'type 1', assign blue edges to 'type 2', and assign green ones to 'type 3'. I want something like this:
Once more: I can't plot 3 graphs independently according to 3 pages in M, combine 3 plots together and then add a legend. Because as you can see, outdegree requires a whole graph G as input, it's not viable to divide G into G1, G2 and G3.
One way would be to manipulate the legend function by adding an invisible plot like this:
%put this at the end of your code
hold on; %to retain current plot
ax=plot(NaN,NaN,'r',NaN,NaN,'g',NaN,NaN,'b'); %plotting invisible points of desired colors
legend(ax,'Type 1','Type 2','Type 3'); %adding the legend
which gives:

How to address positions of marks from simple matrix data in the boxes between axis lines?

What is the simplest way to create the following plot in Matlab from the data of the matrix "positions" containing the value 1 for black marks and - 1 for white marks?
positions=[0 1 0 0 0 0; 0 -1 0 0 0 0; 0 1 0 0 0 0; 1 -1 1 1 -1 0]
There you go:
% //Data
positions = [0 1 0 0 0 0; 0 -1 0 0 0 0; 0 1 0 0 0 0; 1 -1 1 1 -1 0];
S = 50; %// circle size. Adjust manually
%// Preparation
[M N] = size(positions);
hold on
%// Plot filled circles
[ip jp] = find(flipud(positions)>0);
plot(jp-.5,ip-.5,'ko','markersize',S,'markerfacecolor','k')
%// Plot empty circles
[in jn] = find(flipud(positions)<0);
plot(jn-.5,in-.5,'ko','markersize',S)
%// Plot grid lines
plot([0 N],(1:M).'*[1 1],'k');
plot((1:N).'*[1 1],[0 M],'k');
%// Set tick labels
set(gca,'xtick',.5:N,'ytick',.5:M)
set(gca,'xticklabel',char((1:N).'+'A'-1),'yticklabel',char((1:M).'+'0'))
%// Set axis size and aspect
axis equal
axis([0 N 0 M])
set(gca,'ticklength',[0 0]) %// no visible ticks

Debugging 24 symmetric operators

I have written the following code to calculate the disorientation between two points in a large dataset using 24 crystal symmetry operators. The code seems to work fine though the end result is not right. I have a huge dataset containing the euler angles for each points. I find the misorientation between 1st point and 2nd point and then between 2 and 3rd and so on.
I want the end result to contain the corresponding misorientaion angles for each two data points. Following is the full code for your better understanding of my requirement.
LA=[phi1 phi phi2];
function gi=get_gi(pvec)
%pvec is a 1x3 vector of [phi1 phi phi2]
g_11=((cosd(pvec(1)).*cosd(pvec(3)))-(sind(pvec(1)).*sind(pvec(3)).*cosd(pvec(2))));
g_12=((sind(pvec(1)).*cosd(pvec(3)))+(cosd(pvec(1)).*sind(pvec(3)).*cosd(pvec(2))));
g_13= (sind(pvec(3)).*sind(pvec(2)));
g_21 =((-cosd(pvec(1)).*sind(pvec(3)))-(sind(pvec(1)).*cos(pvec(3)).*cos(pvec(2))));
g_22 = ((-sin(pvec(1)).*sind(pvec(3)))+(cosd(pvec(1)).*cosd(pvec(3)).*cosd(pvec(2))));
g_23 = (cosd(pvec(3)).*sind(pvec(2)));
g_31 = (sind(pvec(1)).* sind(pvec(2)));
g_32 = -cosd(pvec(1)).* sind(pvec(2));
g_33 = cosd(pvec(2));
gi =[g_11 g_12 g_13;g_21 g_22 g_23;g_31 g_32 g_33];
f = [1 1 1 -1 1 -1 -1 -1 1 1 -1 -1];
l= [1 1 1];
for i=1:3:10
l1= [f(i) 0 0;0 f(i+1) 0;0 0 f(i+2)];
l2= [0 f(i) 0;0 0 f(i+1);f(i+2) 0 0];
l3= [0 0 f(i);f(i+1) 0 0;0 f(i+2) 0];
l4= -[0 0 f(i);0 f(i+1) 0;f(i+2) 0 0];
l5= -[0 f(i) 0;f(i+1) 0 0;0 0 f(i+2)];
l6= -[f(i) 0 0;0 0 f(i+1);0 f(i+2) 0];
l=[l;l1;l2;l3;l4;l5;l6];
end
k=1;
t=1;
for m=1:(length(a)-1)
for i=2:3:71
for j=2:3:71
g_r= (get_gi(LA(m,:)*l(i:i+2,1:3))*(inv(get_gi(LA(m+1,:)))*inv(l(j:j+2,1:3))));
tr(k)= g_r(1,1) + g_r(2,2) +g_r(3,3);
angle(k) = real(acosd((tr(k)-1)/2));
k=k+1;
end
angle(angle==0)=360;
del_theta=min(angle)
del(t)=del_theta;
t=t+1;
end

3 dimensional matrices

I have a 3D 4-by-4-by-4 matrix and I consider it circular (so a 4th element of row closes with the 1st and same goes for columns and pages).
we know that in 3D each point has exactly 26 neighbors which can be stated as (i, j, k-1) (i, j, k+1) etc. but I am not sure how to make matlab know that a (i,j, k-1) neighbor of a point (1,1,1) is not (1,1,0) but (as it is circular) (1,1,4) as well as that point (2,4,3)'s neighbor (i,j+1,k) is not (2,5,3) but (2,1,3). In other words HOW DO I MAKE IT CIRCULAR?
Thank you
MATLAB does not have built-in facilities for this, but you can use the mod (modulus) function when accessing your matrix to achieve the effect you want. To illustrate this on a vector:
v=[1 2 3];
i=5;
result=v(mod(i-1, length(v))+1);
% assigns 2 to 'result'
You'll probably want to write a function that encapsulates the "circular" matrix access so that you have to do the index computations in only one place.
The idea is to use the MOD function as #MartinB explained. Here some code to efficiently compute the neighbors of each point in your 4x4x4 cube:
%# generate X/Y/Z coordinates of each point of the 4x4x4 cube
sz = [4 4 4]; %# size of the cube along each dimension
[X Y Z] = ndgrid(1:sz(1),1:sz(2),1:sz(3));
coords = [X(:) Y(:) Z(:)];
%# generate increments to get the 26 neighbors around a 3D point
[X Y Z] = ndgrid([-1 0 1], [-1 0 1], [-1 0 1]);
nb = [X(:) Y(:) Z(:)];
nb(ismember(nb,[0 0 0],'rows'),:) = []; %# remove the row [0 0 0]
%# for each 3D point, compute its neighbors
allNeighbors = zeros([size(nb,1) 3 size(coords,1)]);
szMod = repmat(sz, [size(nb,1) 1]);
for i=1:size(coords,1)
cc = bsxfun(#plus, nb, coords(i,:)); %# find 26 neighbors of coords(i,:)
cc = mod(cc-1,szMod)+1; %# wrap around circularly
allNeighbors(:,:,i) = cc; %# save them for later processing
end
The order of the neighbors generated is as follows:
>> nb
nb =
-1 -1 -1 %# read as: (i-1,j-1,k-1)
0 -1 -1 %# read as: (i,j-1,k-1)
1 -1 -1 %# ...
-1 0 -1
0 0 -1
1 0 -1
-1 1 -1
0 1 -1
1 1 -1
-1 -1 0
0 -1 0
1 -1 0
-1 0 0
1 0 0
-1 1 0
0 1 0
1 1 0
-1 -1 1
0 -1 1
1 -1 1
-1 0 1
0 0 1
1 0 1
-1 1 1
0 1 1
1 1 1