Generating a grid in matlab with a general number of dimensions - matlab

Problem
I have a vector w containing n elements. I do not know n in advance.
I want to generate an n-dimensional grid g whose values range from grid_min to grid_max and obtain the "dimension-wise" product of w and g.
How can I do this for an arbitrary n?
Examples
For simplicity, let's say that grid_min = 0 and grid_max = 5.
Case: n=1
>> w = [0.75];
>> g = 0:5
ans =
0 1 2 3 4 5
>> w * g
ans =
0 0.7500 1.5000 2.2500 3.0000 3.7500
Case: n=2
>> w = [0.1, 0.2];
>> [g1, g2] = meshgrid(0:5, 0:5)
g1 =
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
g2 =
0 0 0 0 0 0
1 1 1 1 1 1
2 2 2 2 2 2
3 3 3 3 3 3
4 4 4 4 4 4
5 5 5 5 5 5
>> w(1) * g1 + w(2) * g2
ans =
0 0.1000 0.2000 0.3000 0.4000 0.5000
0.2000 0.3000 0.4000 0.5000 0.6000 0.7000
0.4000 0.5000 0.6000 0.7000 0.8000 0.9000
0.6000 0.7000 0.8000 0.9000 1.0000 1.1000
0.8000 0.9000 1.0000 1.1000 1.2000 1.3000
1.0000 1.1000 1.2000 1.3000 1.4000 1.5000
Now suppose a user passes in the vector w and we do not know how many elements (n) it contains. How can I create the grid and obtain the product?

%// Data:
grid_min = 0;
grid_max = 5;
w = [.1 .2 .3];
%// Let's go:
n = numel(w);
gg = cell(1,n);
[gg{:}] = ndgrid(grid_min:grid_max);
gg = cat(n+1, gg{:});
result = sum(bsxfun(#times, gg, shiftdim(w(:), -n)), n+1);
How this works:
The grid (variable gg) is generated with ndgrid, using as output a comma-separated list of n elements obtained from a cell array. The resulting n-dimensional arrays (gg{1}, gg{2} etc) are contatenated along the n+1-th dimension (using cat), which turns gg into an n+1-dimensional array. The vector w is reshaped into the n+1-th dimension (shiftdim), multiplied by gg using bsxfun, and the results are summed along the n+1-th dimension.
Edit:
Following #Divakar's insightful comment, the last line can be replaced by
sz_gg = size(gg);
result = zeros(sz_gg(1:end-1));
result(:) = reshape(gg,[],numel(w))*w(:);
which results in a significant speedup, because Matlab is even better at matrix multiplication than at bsxfun (see for example here and here).

Related

Solve System of Linear Equations in MatLab with Matrix of Arbitrary Size for Finite Difference Calculation

I am trying to write a script in MatLab R2016a that can solve a system of linear equations that can have different sizes depending on the values of p and Q.
I have the following equations that I am trying to solve, where h=[-p:1:p]*dx. Obviously, there is some index m where h=0, but that shouldn't be a problem.
I'm trying to write a function where I can input p and Q and build the matrix and then just solve it to get the coefficients. Is there a way to build a matrix using the variables p, Q, and h instead of using different integer values for each individual case?
I would use bsxfun(in recent matlab versions this function may be implented to the interpreter, I don't know for sure):
p = 4;
Q = 8;
dx = 1;
h = -p:p*dx
Qvector = [Q,1:Q-1]'
Matrix = bsxfun(#(Qvector, h)h.^(Qvector)./factorial(Qvector), Qvector, h)
Output:
h =
-4 -3 -2 -1 0 1 2 3 4
Qvector =
8
1
2
3
4
5
6
7
Matrix =
1.6254 0.1627 0.0063 0.0000 0 0.0000 0.0063 0.1627 1.6254
-4.0000 -3.0000 -2.0000 -1.0000 0 1.0000 2.0000 3.0000 4.0000
8.0000 4.5000 2.0000 0.5000 0 0.5000 2.0000 4.5000 8.0000
-10.6667 -4.5000 -1.3333 -0.1667 0 0.1667 1.3333 4.5000 10.6667
10.6667 3.3750 0.6667 0.0417 0 0.0417 0.6667 3.3750 10.6667
-8.5333 -2.0250 -0.2667 -0.0083 0 0.0083 0.2667 2.0250 8.5333
5.6889 1.0125 0.0889 0.0014 0 0.0014 0.0889 1.0125 5.6889
-3.2508 -0.4339 -0.0254 -0.0002 0 0.0002 0.0254 0.4339 3.2508

Normalize each slice of a 3D matrix

How do I normalize each slice of a 3D matrix? I tried like this:
a=rand(1,100,3481);
a= (a - min(a)) ./ (max(a)-min(a)); %
By right each slice of matrix should ranges from 0 to 1. But that is not the case, I don't find 1 in some of the slices. As I inspected, min(a) and max(a) returned the respective value in 3D. Thus it should be of no issue using the code above. Is there something I missed for 3D matrix? Thanks in advance!
We need to find the minimum and maximum values for each of those 2D slices and then we can use bsxfun to do those operations in a vectorized manner with help from permute to let the singleton dims align properly to let bsxfun do its broadcasting job (or use reshape there).
Hence, the implementation would be -
mins = min(reshape(a,[],size(a,3)));
maxs = max(reshape(a,[],size(a,3)));
a_offsetted = bsxfun(#minus, a, permute(mins,[1,3,2]));
a_normalized = bsxfun(#rdivide, a_offsetted, permute(maxs-mins,[1,3,2]))
Sample input, output -
>> a
a(:,:,1) =
2 8 2 2
8 3 8 2
a(:,:,2) =
8 1 1 5
4 9 8 6
a(:,:,3) =
7 9 3 5
6 2 6 5
a(:,:,4) =
9 3 4 9
7 1 9 9
>> a_normalized
a_normalized(:,:,1) =
0 1.0000 0 0
1.0000 0.1667 1.0000 0
a_normalized(:,:,2) =
0.8750 0 0 0.5000
0.3750 1.0000 0.8750 0.6250
a_normalized(:,:,3) =
0.7143 1.0000 0.1429 0.4286
0.5714 0 0.5714 0.4286
a_normalized(:,:,4) =
1.0000 0.2500 0.3750 1.0000
0.7500 0 1.0000 1.0000
My option would be without reshaping as it is sometimes bit difficult to understand. I use min max with the dimension you want want to use for normalization with repmat to clone...:
a=rand(1,100,3481);
a_min2 = min(a,[],2);
a_max2 = max(a,[],2);
a_norm2 = (a - repmat(a_min2,[1 size(a,2) 1]) ) ./ repmat( (a_max2-a_min2),[1 size(a,2) 1]);
or if normalization on 3rd dim...
a_min3 = min(a,[],3);
a_max3 = max(a,[],3);
a_norm3 = (a - repmat(a_min3,[1 1 size(a,3)]) ) ./ repmat( (a_max3-a_min3),[1 1 size(a,3)]);

Blockdiagonal variation grid

I have the feeling I am missing something intuitive in my solution for generating a partially varied block-diagonal grid. In any case, I would like to get rid of the loop in my function (for the sake of challenge...)
Given tuples of parameters, number of intervals and percentage variation:
params = [100 0.5 1
24 1 0.9];
nint = 1;
perc = 0.1;
The desired output should be:
pspacegrid(params,perc,nint)
ans =
90.0000 0.5000 1.0000
100.0000 0.5000 1.0000
110.0000 0.5000 1.0000
100.0000 0.4500 1.0000
100.0000 0.5000 1.0000
100.0000 0.5500 1.0000
100.0000 0.5000 0.9000
100.0000 0.5000 1.0000
100.0000 0.5000 1.1000
21.6000 1.0000 0.9000
24.0000 1.0000 0.9000
26.4000 1.0000 0.9000
24.0000 0.9000 0.9000
24.0000 1.0000 0.9000
24.0000 1.1000 0.9000
24.0000 1.0000 0.8100
24.0000 1.0000 0.9000
24.0000 1.0000 0.9900
where you can see that the variation occurs at the values expressed by this mask:
mask =
1 0 0
1 0 0
1 0 0
0 1 0
0 1 0
0 1 0
0 0 1
0 0 1
0 0 1
1 0 0
1 0 0
1 0 0
0 1 0
0 1 0
0 1 0
0 0 1
0 0 1
0 0 1
The function pspacegrid() is:
function out = pspacegrid(params, perc, nint)
% PSPACEGRID Generates a parameter space grid for sensitivity analysis
% Size and number of variation steps
sz = size(params);
nsteps = nint*2+1;
% Preallocate output
out = reshape(permute(repmat(params,[1,1,nsteps*sz(2)]),[3,1,2]),[],sz(2));
% Mask to index positions where to place interpolated
[tmp{1:sz(2)}] = deal(true(nsteps,1));
mask = repmat(logical(blkdiag(tmp{:})),sz(1),1);
zi = cell(sz(1),1);
% LOOP per each parameter tuple
for r = 1:sz(1)
% Columns, rows, rows to interpolate and lower/upper parameter values
x = 1:sz(2);
y = [1; nint*2+1];
yi = (1:nint*2+1)';
z = [params(r,:)*(1-perc); params(r,:)*(1+perc)];
% Interpolated parameters
zi{r} = interp2(x,y,z, x, yi);
end
out(mask) = cat(1,zi{:});
I think I got it, building off your pre-loop code:
params = [100 0.5 1
24 1 0.9];
nint = 1;
perc = 0.1;
sz = size(params);
nsteps = nint*2+1;
% Preallocate output
out = reshape(permute(repmat(params,[1,1,nsteps*sz(2)]),[3,1,2]),[],sz(2));
%Map of the percentage moves
[tmp{1:sz(2)}] = deal(linspace(-perc,perc,nint*2+1)');
mask = repmat(blkdiag(tmp{:}),sz(1),1) + 1; %Add one so we can just multiply at the end
mask.*out
So instead of making your mask replicate the ones I made it replicate the percentage moves each element makes which is a repeating pattern, the basic element is made like this:
linspace(-perc,perc,nint*2+1)'
Then it's as simple as adding 1 to the whole thing and multiplying by your out matrix
I tested it as follows:
me = mask.*out;
you = pspacegrid(params, perc, nint);
check = me - you < 0.0001;
mean(check(:))
Seemed to work when I fiddled with the inputs. However I did get an error with your function, I had to change true(...) to ones(...). This might be because I'm running it online which probably uses Octave rather than Matlab.

how to delete decimals in Matlab matrix

I have an output like
a = [1 1.4 2.45 2.22; 2 3 4.2 1]
and I need the output to be like
[1 1 2 2; 2 3 4 1]
I don't want to round it.
fix maybe OK.
If you have both positive and negative numbers, and you just want to delete decimals, fix is a good choice.
b=[1 1.4 2.45 2.22; 2 3 -4.2 1]
b =
1.0000 1.4000 2.4500 2.2200
2.0000 3.0000 -4.2000 1.0000
fix(b)
ans =
1 1 2 2
2 3 -4 1
Use fix rather than round, e.g.
octave-3.4.0:1> a = [1 1.4 2.45 2.22; 2 3 4.8 1]
a =
1.0000 1.4000 2.4500 2.2200
2.0000 3.0000 4.8000 1.0000
octave-3.4.0:2> b = fix(a)
b =
1 1 2 2
2 3 4 1

Inserting rows into matrix matlab

I have a ~ 100000/2 matrix. I'd like to go down the columns, average each vertically adjacent value, and insert that value in between the two values. For example...
1 2
3 4
4 6
7 8
would become
1 2
2 3
3 4
3.5 5
4 6
5.5 7
7 8
I'm not sure if there is a terse way to do this in matlab. I took a look at http://www.mathworks.com/matlabcentral/fileexchange/9984 but it seems to insert all of the rows in a matrix into the other one at a specific point. Obviously it can still be used, but just wondering if there is a simpler way.
Any help is appreciated, thanks.
Untested:
% Take the mean of adjacent pairs
x_mean = ([x; 0 0] + [0 0; x]) / 2;
% Interleave the two matrices
y = kron(x, [1;0]) + kron(x_mean(1:end-1,:), [0;1]);
%# works for any 2D matrix of size N-by-M
X = rand(100,2);
adjMean = mean(cat(3, X(1:end-1,:), X(2:end,:)), 3);
Y = zeros(2*size(X,1)-1, size(X,2));
Y(1:2:end,:) = X;
Y(2:2:end,:) = adjMean;
octave-3.0.3:57> a = [1,2; 3,4; 4,6; 7,8]
a =
1 2
3 4
4 6
7 8
octave-3.0.3:58> b = (circshift(a, -1) + a) / 2
b =
2.0000 3.0000
3.5000 5.0000
5.5000 7.0000
4.0000 5.0000
octave-3.0.3:60> reshape(vertcat(a', b'), 2, [])'(1:end-1, :)
ans =
1.0000 2.0000
2.0000 3.0000
3.0000 4.0000
3.5000 5.0000
4.0000 6.0000
5.5000 7.0000
7.0000 8.0000