Create a "pyramid" matrix - arrays

Say I'm given a symmetric row vector with an odd length where each element is smaller than the next one in the first half of the vector and each element is bigger than the next one in the second half and the middle element is the biggest. (e.g [1 2 3 2 1] or [10 20 50 20 10]).
I want to create a square matrix where this row vector is its middle row and the equivalent column vector (v') is its middle column and each other row or column is a reduced version of the given vector according to the middle element in this row or column. And when there are no more "original elements" we put 0.
Examples:
if v = [1 2 3 2 1] we get
0 0 1 0 0
0 1 2 1 0
1 2 3 2 1
0 1 2 1 0
0 0 1 0 0
if v = [3 5 3] we get
0 3 0
3 5 3
0 3 0
What I did so far: I managed to create a matrix with v as the middle row and v' as the middle column with this code I wrote:
s = length(vector);
matrix= zeros(s);
matrix(round(s/2),:) = vector;
matrix(:, round(s/2)) = vector';
but got stuck with assigning the other values.

A more hands-on approach is to produce your matrix as a mosaic, starting from a hankel matrix. For performance comparison, here's a version using the same format as #Divakar's solution:
function out=pyramid_hankel(v)
%I suggest checking v here
%it should be odd in length and a palindrome
i0=ceil(length(v)/2);
v2=v(i0:end);
Mtmp=hankel(v2);
out=zeros(length(v));
out(i0:end,i0:end)=Mtmp;
out(1:i0-1,i0:end)=flipud(Mtmp(2:end,:));
out(:,1:i0-1)=fliplr(out(:,i0+1:end));
>> pyramid_hankel([1 2 3 2 1])
ans =
0 0 1 0 0
0 1 2 1 0
1 2 3 2 1
0 1 2 1 0
0 0 1 0 0
For v=[1 2 3 2 1] the starting block is hankel([3 2 1]), which is
ans =
3 2 1
2 1 0
1 0 0
From here it should be clear what's happening.

Here's one approach -
function out = pyramid(v)
hlen = (numel(v)+1)/2;
updown_vec = [1:(numel(v)+1)/2 (numel(v)-1)/2:-1:1];
upper_part = cumsum(bsxfun(#le,(hlen:-1:1)',updown_vec)); %//'
out = [upper_part ; flipud(upper_part(1:end-1,:))];
out = changem(out,v,updown_vec);
Here's another approach, sort of simpler maybe -
function out = pyramid_v2(v)
hlen = (numel(v)+1)/2;
updown_vec = [1:(numel(v)+1)/2 (numel(v)-1)/2:-1:1];
mask = bsxfun(#le,([hlen:-1:1 2:hlen])',updown_vec); %//'
M = double(mask);
M(hlen+1:end,:) = -1;
out = changem(cumsum(M).*mask,v,updown_vec);
Sample runs -
>> v = [1 2 3 2 1];
>> pyramid(v)
ans =
0 0 1 0 0
0 1 2 1 0
1 2 3 2 1
0 1 2 1 0
0 0 1 0 0
>> v = [3 5 3];
>> pyramid(v)
ans =
0 3 0
3 5 3
0 3 0
>> v = [99,3,78,55,78,3,99];
>> pyramid(v)
ans =
0 0 0 99 0 0 0
0 0 99 3 99 0 0
0 99 3 78 3 99 0
99 3 78 55 78 3 99
0 99 3 78 3 99 0
0 0 99 3 99 0 0
0 0 0 99 0 0 0

Here's another approach:
v = [1 2 3 2 1]; %// symmetric, odd size
m = (numel(v)-1)/2;
w = [0 v(1:m+1)];
t = abs(-m:m);
result = w(max(m+2-bsxfun(#plus, t, t.'),1));

Related

MATLAB : How to construct block bi-diagonal matrix from input arguments [duplicate]

I have a matrix B and I want to obtain a matrix C of dimension (L+k)*m by L*n. L and k are input values. B0 , B1 , ... , Bk has size m by n.
For example :
If I have a matrix B = [1 1 ; 1 1 ; 1 1] with B0 = [1 1], B1 = [1 1] and B2 = [1 1], and each B0 , B1 , B2 of dimension 1 by 2 with k = 2 and L = 4.
Then the matrix C obtained is given by C = [1 1 0 0 0 0 0 0 ; 1 1 1 1 0 0 0 0 ; 1 1 1 1 1 1 0 0 ; 0 0 1 1 1 1 1 1 ; 0 0 0 0 1 1 1 1 ; 0 0 0 0 0 0 1 1] and of dimension
6 by 8.
I would like to generalize my program for any size matrix B.
My program solves the problem for B = [1 1 ; 1 1 ; 1 1] with m = 1 , n = 2 , k = 2 and L = 4.
My code :
clc;
clear;
k = 2;
L = 4;
B = [1 1 ; 1 1 ; 1 1];
B0 = [1 1];
B1 = [1 1];
B2 = [1 1];
m = size(B0,1);
n = size(B0,2);
c = [B ; zeros(size(B))];
C = zeros((L+k)*m,L*n);
for i = 1:L
C(:,2*i-1:2*i) = circshift(c,i-1,1);
end
Result : C =
1 1 0 0 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 0 0 1 1
I have difficulties to generalize for any given matrix B and for any value of k and L.
Any suggestions?
B = [1 2 3; 4 5 6; 7 8 9; 100 110 120; 130 140 150; 160 170 180]; % input
L = 4; % input
k = 2; % input
m = size(B,1)/(k+1); % obtain m
n = size(B,2); % obtain n
C = zeros((L-1)*m+1, (L-1)*n+1); % initiallize result
C(1:((L-1)*m+1)*n+m:end) = 1; % each 1 marks the upper-left corner for a copy of B
C = conv2(C, B); % insert copies of B, extending size
The second-to-last line uses linear indexing. The last line applies two-dimensional convolution. The result in this example is
B =
1 2 3
4 5 6
7 8 9
100 110 120
130 140 150
160 170 180
C =
1 2 3 0 0 0 0 0 0 0 0 0
4 5 6 0 0 0 0 0 0 0 0 0
7 8 9 1 2 3 0 0 0 0 0 0
100 110 120 4 5 6 0 0 0 0 0 0
130 140 150 7 8 9 1 2 3 0 0 0
160 170 180 100 110 120 4 5 6 0 0 0
0 0 0 130 140 150 7 8 9 1 2 3
0 0 0 160 170 180 100 110 120 4 5 6
0 0 0 0 0 0 130 140 150 7 8 9
0 0 0 0 0 0 160 170 180 100 110 120
0 0 0 0 0 0 0 0 0 130 140 150
0 0 0 0 0 0 0 0 0 160 170 180
In addition to #LuisMendo's answer:
If size(B,1)/(k+1) do not produce an integer, then his solution can fail.
I would suggest to create the C matrix with sparse():
n = size(B,2)
C = full(sparse(1:k:k*L,1:n:n*L,1))
So the whole code become:
B = ones(7,3); % An input matrix that will produce an error with the linear indexing solution.
L = 6; % number of repetition
k = 2; % row-shift
n = size(B,2); % obtain n
C = full(sparse(1:k:k*L,1:n:n*L,1)); % Create C using full(sparse())
C = conv2(C, B) % insert copies of B, extending size
(all credits goes to LuisMendo for the idea)
This solution as #obchardon's answer uses sparse but unlike the #LuisMendo's answer doesn't use conv2. Indices of blocks are computed and used in sparse to form the desired matrix.
[row col] = find(true(size(B)));
ROW = row + (0:L-1)*k;
COL = col + (0:L-1)*size(B,2);
C = sparse (ROW, COL, repmat (B, 1, L));

Expanding a matrix diagonally with Matlab

I have a matrix B and I want to obtain a matrix C of dimension (L+k)*m by L*n. L and k are input values. B0 , B1 , ... , Bk has size m by n.
For example :
If I have a matrix B = [1 1 ; 1 1 ; 1 1] with B0 = [1 1], B1 = [1 1] and B2 = [1 1], and each B0 , B1 , B2 of dimension 1 by 2 with k = 2 and L = 4.
Then the matrix C obtained is given by C = [1 1 0 0 0 0 0 0 ; 1 1 1 1 0 0 0 0 ; 1 1 1 1 1 1 0 0 ; 0 0 1 1 1 1 1 1 ; 0 0 0 0 1 1 1 1 ; 0 0 0 0 0 0 1 1] and of dimension
6 by 8.
I would like to generalize my program for any size matrix B.
My program solves the problem for B = [1 1 ; 1 1 ; 1 1] with m = 1 , n = 2 , k = 2 and L = 4.
My code :
clc;
clear;
k = 2;
L = 4;
B = [1 1 ; 1 1 ; 1 1];
B0 = [1 1];
B1 = [1 1];
B2 = [1 1];
m = size(B0,1);
n = size(B0,2);
c = [B ; zeros(size(B))];
C = zeros((L+k)*m,L*n);
for i = 1:L
C(:,2*i-1:2*i) = circshift(c,i-1,1);
end
Result : C =
1 1 0 0 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 0 0 1 1
I have difficulties to generalize for any given matrix B and for any value of k and L.
Any suggestions?
B = [1 2 3; 4 5 6; 7 8 9; 100 110 120; 130 140 150; 160 170 180]; % input
L = 4; % input
k = 2; % input
m = size(B,1)/(k+1); % obtain m
n = size(B,2); % obtain n
C = zeros((L-1)*m+1, (L-1)*n+1); % initiallize result
C(1:((L-1)*m+1)*n+m:end) = 1; % each 1 marks the upper-left corner for a copy of B
C = conv2(C, B); % insert copies of B, extending size
The second-to-last line uses linear indexing. The last line applies two-dimensional convolution. The result in this example is
B =
1 2 3
4 5 6
7 8 9
100 110 120
130 140 150
160 170 180
C =
1 2 3 0 0 0 0 0 0 0 0 0
4 5 6 0 0 0 0 0 0 0 0 0
7 8 9 1 2 3 0 0 0 0 0 0
100 110 120 4 5 6 0 0 0 0 0 0
130 140 150 7 8 9 1 2 3 0 0 0
160 170 180 100 110 120 4 5 6 0 0 0
0 0 0 130 140 150 7 8 9 1 2 3
0 0 0 160 170 180 100 110 120 4 5 6
0 0 0 0 0 0 130 140 150 7 8 9
0 0 0 0 0 0 160 170 180 100 110 120
0 0 0 0 0 0 0 0 0 130 140 150
0 0 0 0 0 0 0 0 0 160 170 180
In addition to #LuisMendo's answer:
If size(B,1)/(k+1) do not produce an integer, then his solution can fail.
I would suggest to create the C matrix with sparse():
n = size(B,2)
C = full(sparse(1:k:k*L,1:n:n*L,1))
So the whole code become:
B = ones(7,3); % An input matrix that will produce an error with the linear indexing solution.
L = 6; % number of repetition
k = 2; % row-shift
n = size(B,2); % obtain n
C = full(sparse(1:k:k*L,1:n:n*L,1)); % Create C using full(sparse())
C = conv2(C, B) % insert copies of B, extending size
(all credits goes to LuisMendo for the idea)
This solution as #obchardon's answer uses sparse but unlike the #LuisMendo's answer doesn't use conv2. Indices of blocks are computed and used in sparse to form the desired matrix.
[row col] = find(true(size(B)));
ROW = row + (0:L-1)*k;
COL = col + (0:L-1)*size(B,2);
C = sparse (ROW, COL, repmat (B, 1, L));

MATLAB find first elements in columns of array

Within the context of writing a certain function, I have the following example matrix:
temp =
1 2 0 0 1 0
1 0 0 0 0 0
0 1 0 0 0 1
I want to obtain an array whose each element indicates the number of the element out of all non-zero elements which starts that column. If a column is empty, the element should correspond to the next non-empty column. For the matrix temp, the result would be:
result = [1 3 5 5 5 6]
Because the first non-zero element starts the first column, the third starts the second column, the fifth starts the fifth column and the sixth starts the sixth column.
How can I do this operation for any general matrix (one which may or may not contain empty columns) in a vectorized way?
Code:
temp = [1 2 0 0 1 0; 1 0 0 0 0 0; 0 1 0 0 0 1]
t10 = temp~=0
l2 = cumsum(t10(end:-1:1))
temp2 = reshape(l2(end)-l2(end:-1:1)+1, size(temp))
result = temp2(1,:)
Output:
temp =
1 2 0 0 1 0
1 0 0 0 0 0
0 1 0 0 0 1
t10 =
1 1 0 0 1 0
1 0 0 0 0 0
0 1 0 0 0 1
l2 =
1 1 1 1 1 2 2 2 2 2 2 2 3 3 4 4 5 6
temp2 =
1 3 5 5 5 6
2 4 5 5 6 6
3 4 5 5 6 6
result =
1 3 5 5 5 6
Printing values of each step may be clearer than my explanation. Basically we use cumsum to get the IDs of the non-zero elements. As you need to know the ID before reaching the element, a reversed cumsum will do. Then the only thing left is to reverse the ID numbers back.
Here's another way:
temp = [1 2 0 0 1 0; 1 0 0 0 0 0; 0 1 0 0 0 1]; % data
[~, c] = find(temp); % col indices of nonzero elements
result = accumarray(c, 1:numel(c), [], #min, NaN).'; % index, among all nonzero
% values, of the first nonzero value of each col; or NaN if none exists
result = cummin(result, 'reverse'); % fill NaN's using backwards cumulative maximum

Matlab: vectorize assignment of values in matrix based on index

Apologies in advance if this question is a duplicate, or if the solution to this question is very straightforward in Matlab. I have a M x N matrix A, a 1 x M vector ind, and another vector val. For example,
A = zeros(6,5);
ind = [3 4 2 4 2 3];
val = [1 2 3];
I would like to vectorize the following code:
for i = 1 : size(A,1)
A(i, ind(i)-1 : ind(i)+1) = val;
end
>> A
A =
0 1 2 3 0
0 0 1 2 3
1 2 3 0 0
0 0 1 2 3
1 2 3 0 0
0 1 2 3 0
That is, for row i of A, I want to insert the vector val in a certain location, as specificied by the i'th entry of ind. What's the best way to do this in Matlab without a for loop?
It can be done using bsxfun's masking capability: build a mask telling where the values will be placed, and then fill those values in. In doing this, it's easier to work with columns instead of rows (because of Matlab's column major order), and transpose at the end.
The code below determines the minimum number of columns in the final A so that all values fit at the specified positions.
Your example applies a displacement of -1 with respect to ind. The code includes a generic displacement, which can be modified.
%// Data
ind = [3 4 2 4 2 3]; %// indices
val = [1 2 3]; %// values
d = -1; %// displacement for indices. -1 in your example
%// Let's go
n = numel(val);
m = numel(ind);
N = max(ind-1) + n + d; %// number of rows in A (rows before transposition)
mask = bsxfun(#ge, (1:N).', ind+d) & bsxfun(#le, (1:N).', ind+n-1+d); %// build mask
A = zeros(size(mask)); %/// define A with zeros
A(mask) = repmat(val(:), m, 1); %// fill in values as indicated by mask
A = A.'; %// transpose
Result in your example:
A =
0 1 2 3 0
0 0 1 2 3
1 2 3 0 0
0 0 1 2 3
1 2 3 0 0
0 1 2 3 0
Result with d = 0 (no displacement):
A =
0 0 1 2 3 0
0 0 0 1 2 3
0 1 2 3 0 0
0 0 0 1 2 3
0 1 2 3 0 0
0 0 1 2 3 0
If you can handle a bit of bsxfun overdose, here's one with bsxfun's adding capability -
N = numel(ind);
A(bsxfun(#plus,N*[-1:1]',(ind-1)*N + [1:N])) = repmat(val(:),1,N)
Sample run -
>> ind
ind =
3 4 2 4 2 3
>> val
val =
1 2 3
>> A = zeros(6,5);
>> N = numel(ind);
>> A(bsxfun(#plus,N*[-1:1]',(ind-1)*N + [1:N])) = repmat(val(:),1,N)
A =
0 1 2 3 0
0 0 1 2 3
1 2 3 0 0
0 0 1 2 3
1 2 3 0 0
0 1 2 3 0

Select n elements in matrix left-wise based on certain value

I have a logical matrix A, and I would like to select all the elements to the left of each of my 1s values given a fixed distant. Let's say my distance is 4, I would like to (for instance) replace with a fixed value (saying 2) all the 4 cells at the left of each 1 in A.
A= [0 0 0 0 0 1 0
0 1 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 1 0 1]
B= [0 2 2 2 2 1 0
2 1 0 0 0 0 0
0 0 0 0 0 0 0
2 2 2 2 2 2 1]
In B is what I would like to have, considering also overwrting (last row in B), and cases where there is only 1 value at the left of my 1 and not 4 as the fixed searching distance (second row).
How about this lovely one-liner?
n = 3;
const = 5;
A = [0 0 0 0 0 1 0;
0 1 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 0 0 1 0 1]
A(bsxfun(#ne,fliplr(filter(ones(1,1+n),1,fliplr(A),[],2)),A)) = const
results in:
A =
0 0 5 5 5 1 0
5 1 0 0 0 0 0
0 0 0 0 0 0 0
0 5 5 5 5 5 1
here some explanations:
Am = fliplr(A); %// mirrored input required
Bm = filter(ones(1,1+n),1,Am,[],2); %// moving average filter for 2nd dimension
B = fliplr(Bm); %// back mirrored
mask = bsxfun(#ne,B,A) %// mask for constants
A(mask) = const
Here is a simple solution you could have come up with:
w=4; % Window size
v=2; % Desired value
B = A;
for r=1:size(A,1) % Go over all rows
for c=2:size(A,2) % Go over all columns
if A(r,c)==1 % If we encounter a 1
B(r,max(1,c-w):c-1)=v; % Set the four spots before this point to your value (if possible)
end
end
end
d = 4; %// distance
v = 2; %// value
A = fliplr(A).'; %'// flip matrix, and transpose to work along rows.
ind = logical( cumsum(A) ...
- [ zeros(size(A,1)-d+2,size(A,2)); cumsum(A(1:end-d-1,:)) ] - A );
A(ind) = v;
A = fliplr(A.');
Result:
A =
0 2 2 2 2 1 0
2 1 0 0 0 0 0
0 0 0 0 0 0 0
2 2 2 2 2 2 1
Approach #1 One-liner using imdilate available with Image Processing Toolbox -
A(imdilate(A,[ones(1,4) zeros(1,4+1)])==1)=2
Explanation
Step #1: Create a morphological structuring element to be used with imdilate -
morph_strel = [ones(1,4) zeros(1,4+1)]
This basically represents a window extending n places to the left with ones and n places to the right including the origin with zeros.
Step #2: Use imdilate that will modify A such that we would have 1 at all four places to the left of each 1 in A -
imdilate_result = imdilate(A,morph_strel)
Step #3: Select all four indices for each 1 of A and set them to 2 -
A(imdilate_result==1)=2
Thus, one can write a general form for this approach as -
A(imdilate(A,[ones(1,window_length) zeros(1,window_length+1)])==1)=new_value
where window_length would be 4 and new_value would be 2 for the given data.
Approach #2 Using bsxfun-
%// Paramters
window_length = 4;
new_value = 2;
B = A' %//'
[r,c] = find(B)
extents = bsxfun(#plus,r,-window_length:-1)
valid_ind1 = extents>0
jump_factor = (c-1)*size(B,1)
extents_valid = extents.*valid_ind1
B(nonzeros(bsxfun(#plus,extents_valid,jump_factor).*valid_ind1))=new_value
B = B' %// B is the desired output

Resources