I'm new to Matlab and struggling with understanding the concept of array notation (coming from a Perl background).
Let's assume we have two random vectors (X,Y) which are coordinates in 2D (range -r ... r) and we want to find out which points (x, y) lie within a circle with radius r. This would be our setup:
n = 100000000; % point number
r = 1; % circle size
X = (rand(1,n) .* 2*r) - r; % generate coordinates (-r .. r)
Y = (rand(1,n) .* 2*r) - r;
As I understood, Matlab is able to do almost anything fully vectorized on the way to classify the coordinates (in C):
C = - ... % negate sign, 1: within, 0,-1: outside
sign( ... % -1: within, 0,1: outside
(X.^2 + Y.^2) - r^2 ... % calculate distance
);
Now I would like to retain only the values for points within the circle and therefore drop all -1 (former >=0 values) values from C, which could be done by:
C(C < 0) = 0;
I found no obvious way to include the latter expression with the former into a single expression, because I did not find out how the temporary array which is about to be created and modified by the distance-, sign-, and negation operators can be "addressed" to do another "step" with C(C < 0) = 0. Would this be possible at all?
Edit:
According to Dan's comment below, I could simplify the whole expression to:
C = ( (X.^2 + Y.^2) - r^2 ) < 0;
which is exactly what I had looked for. Thank you very much! I didn't think this is possible in Matlab ...
In your specific case I think you could just equate it to 1:
C = -sign((X.^2 + Y.^2) - r^2) == 1;
or
C = -sign((X.^2 + Y.^2) - r^2) > 0;
because what your output is in the end is just a matrix of 1s and 0s so you might as well take advantage of Matlab's logical datatype
But addressing the temporary matrix is not really the way to do things in Matlab. Normally you would just leave it as two lines of code. But if you really really want to, I think you can use the subsref function to do it.
Related
I have a matlab program with 5 nested
for
loops and a
if
condition like this:
for x0=1:N
for y0=1:N
for k=1:N
for x1=1:N
for y1=1:N
if ~((y1-x1>N/2)||(x1-y1>N/2)) && ~((y0-x0>N/2)||(x0-y0>N/2))
A(x0,y0)=A(x0,y0)+2^(k*((x0-y0)+(x1-y1)))*B(x1,y1)
end
end
end
end
end
end
where A and B are two matrices. How can I make this program run faster?
I've tried to use meshgrid but it seems doesn't work because there's a
if
condition.
Lets be smart about loops and conditions first, as you are using the loop indices as condition variables.
We start with
~(y1-x1>N/2)||(x1-y1>N/2), or way clearer, abs(y1-x1)<N/2.
Instead of having an if condition, why not enforce y1 to be in range, always?
The last loop can be written as y1=max(x1-N/2,1):min(x1+N/2,N), and thus the entirety of the first part of the if condition is not needed. We can do the same for the other variables, of course:
for x0=1:N
for y0=max(x0-N/2,1):min(x0+N/2,N)
for k=1:N
for x1=1:N
for y1=max(x1-N/2,1):min(x1+N/2,N)
A(x0,y0)=A(x0,y0)+2^(k*((x0-y0)+(x1-y1)))*B(x1,y1)
end
end
end
end
end
Now, for clarity, lets reshuffle and vectorize that k. There is no need for it to be the middle loop, in fact, its only feature as the middle loop is to confuse the person reading the code. But aside from that, there is no need for it to be a loop either.
k=1:N;
for x0=1:N
for y0=max(x0-N/2,1):min(x0+N/2,N)
for x1=1:N
for y1=max(x1-N/2,1):min(x1+N/2,N)
A(x0,y0)=A(x0,y0)+sum(2.^(k*((x0-y0)+(x1-y1))))*B(x1,y1)
end
end
end
end
Now, is this faster?
No. MATLAB is really good at optimizing your code, so it is not faster. But at least its way way clearer, so I guess you got that going for you. But you need it faster! Well.... I am not sure you can. You have a 5 nested loops, that is just super slow. I don't think you can meshgrid this, even without the conditions, because you intermingle all loops. meshgrid is good when well, you do operations on a mesh grid, but in your case you use all x1,y1 for every x0,y0 and thus its not a mesh operation.
Here is a vectorized solution:
x0 = (1:N).';
y0 = 1:N;
x1 = (1:N).';
y1 = 1:N;
k = reshape(1:N, 1, 1, N);
conditiona = ~((y0-x0 > N/2) | (x0-y0 > N/2));
conditionb = ~((y1-x1 > N/2) | (x1-y1 > N/2));
a = 2 .^ (k .* (x0-y0)) .* conditiona;
b = 2 .^ (k .* (x1-y1)) .* B .* conditionb;
bsum = squeeze(sum(sum(b, 1) ,2));
A = A + reshape(reshape(a, [] , N) * bsum ,N ,N);
Note that two 3D arrays a and b are created that may/may not require a lot of memory. In such a case you need to loop over k. For example in the first iteration set k to 1:5. In the second iteration set it to 6:10 and so on. You need to addv the result of each iteration to the previous iteration to form the final A.
Explanation
This function can be vectorized by implicit expansion (that is more efficient than using meshgrid) and using element-wise operators like .^ and .* instead of ^ and * operators. As a result a 5D array is created (because we have 5 loop variables) that should be summed over 3-5th dimensions to produce the final 2D matrix. However that may require a lot of memory. Another point is that functions that contains the sum of products usually can be written as efficient matrix multiplication.
The expression:
2^(k*((x0-y0)+(x1-y1)))*B(x1,y1);
can be written as:
2 .^ (k .* (x0-y0)) .* 2 .^ (k .* (x1-y1)) .* B(x1, y1)
------- a -------- ------------- b ---------------
that is the multiplication of two sub-expressions that each has 3 dimensions, because each contains just 3 loop variables. So the 5D problem is reduced to 3D.
The if condition has also two sub-expressions that each can be multiplied by a and b sub-expressions:
conditiona = ~((y0-x0 > N/2) | (x0-y0 > N/2));
conditionb = ~((y1-x1 > N/2) | (x1-y1 > N/2));
a = 2 .^ (k .* (x0-y0)) .* conditiona;
b = 2 .^ (k .* (x1-y1)) .* B .* conditionb;
A for loop can be formed just by using two loop variables x0 and y0:
for x0=1:N
for y0=1:N
A(x0,y0)=A(x0,y0)+ sum(sum(sum(a(x0,x0, :) .* b, 3), 2), 1);
%or simply A(x0,y0)=A(x0,y0)+ sum(a(x0,x0, :) .* b, "all");
end
end
That can be simplified to the following loop by precomputing sum of b:
bsum = sum(sum(b, 1) ,2);
% bsum = sum(b ,[1, 2]);
for x0=1:N
for y0=1:N
A(x0,y0)=A(x0,y0)+ sum(a(x0,x0, :) .* bsum, 3);
% or as vector x vector multiplication
% A(x0,y0)=A(x0,y0)+ squeeze(a(x0,x0, :)).' * squeeze(bsum);
end
end
Here the loop can be prevented by using the matrix x vector multiplication:
A = A + reshape(reshape(a, [] , N) * bsum ,N ,N);
Update this solution may not be faster under Matlab, because the execution engine can optimise the loops in the original code. (It does provide a speedup under Octave.)
One trick to deal with if statements within loops is to turn the if statement (or part of it) into a logical matrix. You can then multiply the logical matrix elementwise by the matrix of values you are adding in each step. A false value will evaluate to zero and will not change the result.
This only works if each element can be computed independently of the others.
It will generally make the actual calculation line slower, but in Matlab this is often outweighed by the huge speed improvement from the the removal of the for loops.
In your example, we can use this idea with meshgrid to remove the loops over x0 and y0.
The calculation line needs to become an elementwise matrix caluclation, so elementwise operators .*, .^ and | replace *, ^ and |.
% Warning: Y0 and X0 are swapped in this example
[Y0, X0] = meshgrid(1:N,1:N);
% Create a logical matrix which represents part of the if statement
C = ~((Y0-X0>N/2) | (X0-Y0>N/2));
for k=1:N
for x1=1:N
for y1=1:N
if ~((y1-x1>N/2)||(x1-y1>N/2))
% Multiply by C elementwise
A = A + C.*2.^(k*((X0-Y0)+(x1-y1)))*B(x1,y1);
end
end
end
end
You could even take this concept further and remove more loops using multidemensional ndgrids, but it becomes more complex (you have to start summing over dimensions) and the multidimensional arrays become unwieldy if N is large.
Note: be careful with index order. meshgrid defines y as rows and x as columns, so matrix indexing is A(y,x) but you are using A(x,y). So to make your example work I've switched x and y in the output of meshgrid.
I have inequalities with two unknown variables. So how could I assume one variable with different values and get the others?
For instance: -15<10*x+2*y<20.
How could I assume x=2, 3, and so on, and then find answer of (y) depending on the value of (x)?
I have been trying to apply the assume and find commands, but unfortunately, I could not. So I hope anyone could help me, please.
Looking forward to hearing from you.
I am new to Matlab, so I have been trying to apply solve, assume, and find commands
clear all;
clc;
syms x y real;
z=solve(-15<10*x+2*y,[x y])
b=solve(10*x+2*y<20,[x y])
yinterval = [ z,b]
I expect the output: to assume x=different numbers and then y= be a list of possible results depending on the value of x
Thanks,
For each value of x, technically there are infinite values of y that satisfy those equations, so for my solution, I assumed x and y were integer values. As well, it appears that you want to give the program a set of x values and have it calculate y values for each x value. Instead of using the solve command, we can simply use a couple of loops to find all satisfactory integer values of y for each value of x.
To start, we need to make a results matrix to store each x,y pair that satisfies the equations you've given. This is called pre-allocation, as we're pre-allocating the space needed to store our answers. Using the equations, we can deduce that there will be 17 satisfactory y values per x. So, our first two lines of code will be initializing the desired x-values and the results matrix:
xVec = 1:5; %x-vector, change this to whatever x-values you want to test
results = zeros(length(xVec)*14, 2); %results matrix
Note: If you decide to iterate x or y by a value different than +1 (more on that later), you'll need to come up with a different method of creating this results matrix. You could also just not pre-allocate the results matrix, but your code will run slower as the size of the results matrix will be changing on each loop.
Next are the loops. Admittedly, this is not the most elegant solution, but it'll get the job done. First, we need an index to keep up with where we are in our results matrix. This is pretty easy, we'll just call it index and start at 1 (since MATLAB indexes from 1 in matrices. Remember that!):
index = 1; %index for results matrix
Next, we need to loop through each value in our x-vector. Simply use a for loop:
for x = xVec
...
For each value of x, there is a minimum value of y. This value can be solved for in
-15 < 10*x + 2*y --> -14 = 10*x + 2*y_min
So, simply solving for y gives us our next line of code:
y = -7 - 5*x; %solving for y
Note: each time we iterate x in our for loop, a new starting value of y will be calculated.
Finally, we need to loop through values of y that still satisfy the inequalities given. This is performed through use of a while loop:
while 10*x + 2*y > -15 && 10*x + 2*y < 20
...
Note: && is the 'and' statement while using loops. You can't use a single equation for this (i.e. you can't say something like -15 < x < 20, you have to split them up using &&).
Since we solved for the first value of y, we can go ahead and record the current x and y values in our results matrix:
results(index, :) = [x, y]; %storing current x- and y-values
Then, we need to iterate y, as otherwise we'd be stuck in this while-loop forever.
y = y + 1;
Note: You can iterate this y-value with whatever amount you want. I chose to iterate by 1 each time, as I assumed you wanted to find integer values. Just change the +1 to whatever value you want.
Finally, we iterate our index, so that the next pair of x,y values that satisfy our equations don't overwrite our previous solutions.
index = index + 1;
All that's left is to close our loops and run! As I said, this isn't the most efficient solution, so I wouldn't use this for large amounts of x- and y-values. As well, like with iterating the y-values, the x-values can have any 'step-size' you want. As it's coded currently, it jumps +1 between each x, but changing the xVec input to any vector will still work (ex. xVec = 1:0.1:5; iterates the x-value by +0.1 each step instead of +1).
Here's the code all together, sans comments (since I wrote the comments while making the above code snippets):
xVec = 1:5;
results = zeros(length(xVec)*14, 2);
index = 1;
for x = xVec
y = -7 - 5*x;
while 10*x + 2*y > -15 && 10*x + 2*y < 20
results(index, :) = [x, y];
y = y + 1;
index = index + 1;
end
end
Let me know if you have any questions!
I have a (transition) function defined by a matrix say P=[0.75,0.25;0.25,0.75] and I have a vector say X=[1,2,1] then i would like to find P(1,2)*P(2,1). How is the easiest way to generalise this? I tried creating a function handle for P(i,j) and then X_temp=[X(1:end-1);X(2:end)], using the function of each column and finally using the product function, but it seems a lot more comprehensive than it has to be.
The X i want to use is 1000 dimensional and P is 3x3 and I would have to repeat it a lot of times so speed I think will matter.
You can use sub2ind to get your relevant P values:
Ps = P(sub2ind(size(P), X(1:end-1), X(2:end)))
Now just multiply them all together:
prod(Ps)
EDIT:
For function handles you had the right idea, just make sure that you function itself handles vectors. For example lets say your function f(i,j) = i + j, I'm going to assume it's actually f(x) = x(1) + x(2) but I want it to handle many xs at once sof(x) = x(:,1) + x(:,2):
f = #(x)(x(:,1) + x(:,2))
f([X(1:end-1)', X(2:end)'])
OR
f = #(ii, jj)(ii + jj)
f(X(1:end-1)', X(2:end)') %//You don't actually need the transposes here anymore
just note that you need to use element wise operators such as .*, ./ and .^ etc instead of *, /,^...
I have N lines that are defined by a y-intercept and an angle, q. The constraint is that all N lines must intersect at one point. The equations I can come up with to eventually get the constraint are these:
Y = tan(q(1))X + y(1)
Y = tan(q(2))X + y(2)
...
I can, by hand, get the constraint if N = 3 or 4 but I am having trouble just getting one constraint if N is greater than 4. If N = 3 or 4, then when I solve the equations above for X, I get 2 equations and then can just set them equal to each other. If N > 4, I get more than 2 equations that equal X and I dont know how to condense them down into one constraint. If I cannot condense them down into one constraint and am able to solve the optimization problem with multiple constraints that are created dynamically (depending on the N that is passed in) that would be fine also.
To better understand what I am doing I will show how I get the constraints for N = 3. I start off with these three equations:
Y = tan(q(1))X + y(1)
Y = tan(q(2))X + y(2)
Y = tan(q(3))X + y(3)
I then set them equal to each other and get these equations:
tan(q(1))X + y(1) = tan(q(2))X + y(2)
tan(q(2))X + y(2) = tan(q(3))X + y(3)
I then solve for X and get this constraint:
(y(2) - y(1)) / (tan(q(1)) - tan(q(2))) = (y(3) - y(2)) / (tan(q(2)) - tan(q(3)))
Notice how I have 2 equations to solve for X. When N > 4 I end up with more than 2. This is OK if I am able to dynamically create the constraints and then call an optimization function in MATLAB that will handle multiple constraints but so far have not found one.
You say the optimization algorithm needs to adjust q such that the "real" problem is minimized while the above equations also hold.
Note that the fifth Euclid axoim ensures that all lines will always intersect with all other lines, unless two qs are equal but the corresponding y0s are not. This last case is so rare (in a floating point context) that I'm going to skip it here, but for added robustness, you should eventually include it.
Now, first, think in terms of matrices. Your constraints can be formulated by the matrix equation:
y = tan(q)*x + y0
where q, y and y0 are [Nx1] matrices, x an unknown scalar. Note that y = c*ones(N,1), e.g., a matrix containing only the same constant. This is actually a non-linear constraint -- that is, it cannot be expressed as
A*q <= b or A*q == b
with A some design matrix and b some solution vector. So, you'll have to write a function defining this non-linear constraint, which you can pass on to an optimizer like fmincon. From the documentation:
x = fmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon) subjects the
minimization to the nonlinear inequalities c(x) or equalities ceq(x)
defined in nonlcon. fmincon optimizes such that c(x) ≤ 0 and ceq(x) =
0. If no bounds exist, set lb = [] and/or ub = [].
Note that you were actually going in the right direction. You can solve for the x-location of the intersection for any pair of lines q(n),y0(n) and q(m),y0(m) with the equation:
x(n,m) = (y0(n)-y0(m)) / (q(m)-q(n))
Your nonlcon function should find x for all possible pairs n,m, and check if they are all equal. You can do this conveniently something like so:
function [c, ceq] = nonlcon(q, y0)
% not using inequalities
c = -1; % NOTE: setting it like this will always satisfy this constraint
% compute tangents
tanq = tan(q);
% compute solutions to x for all pairs
x = bsxfun(#minus, y0, y0.') ./ -bsxfun(#minus, tanq, tanq.');
% equality contraints: they all need to be equal
ceq = diff(x(~isnan(x))); % NOTE: if all(ceq==0), converged.
end
Note that you're not actually solving for q explicitly (or need the y-coordinate of the intersection at all) -- that is all fmincon's job.
You will need to do some experimenting, because sometimes it is sufficient to define
x = x(~isnan(x));
ceq = norm(x-x(1)); % e.g., only 1 equality constraint
which will be faster (less derivatives to compute), but other problems really need
x = x(~isnan(x));
ceq = x-x(1); % e.g., N constraints
or similar tricks. It really depends on the rest of the problem how difficult the optimizer will find each case.
I have created a position matrix which I am happy with, and for each position (or element) of this matrix, I want to calculate the positional distance between all other positions in the matrix. This way i can obtain the direction each other element is from another. I have tried to do this in the following way:
pos = [X(:),Y(:),Z(:)];
for j = 1:length(pos)
for i = 1:length(pos)
vecdir(i,:,:) = pos(i,:,:) - pos(j,:,:);
end
v(i) = {vecdir};
i = i+1;
end
where each cell holds the positional distance per position in the position matrix. v(i) only seems to store the last calculation (i.e. all cells are empty apart from the last cell which holds the correct information for the last position on the position matrix.). Where am I going wrong here? Also, if there is a more efficient way of doing this then I'd like to know, as I know storing and accessing cell arrays slows programs down a lot.
They're is always pdist2:
dist = pdist2(pos,pos);
which gives the norm of the distance vectors.
In case you also need the distance vectors I'd use something like this:
N = size(pos,1);
v = arrayfun(#(ii) bsxfun(#minus,pos,pos(ii,:)),1:N,'uni',false)
which returns a Nx1 cell array, each cell containing the distance vector of pos(ii,:) to the other positions.
Your code seems to do the same, altough there are some errors; I think you intended to do the following:
N = size(pos,1);
v = cell(N,1);
for j = 1:N
for i = 1:N
vecdir(i,:) = pos(i,:) - pos(j,:);
end
v{j} = vecdir;
end
What are these statements doing at the end of your loop over j ?
v(i) = {vecdir};
i = i+1;
As I read your code, these always set v(length(pos)) to {vecdir} and then add 1 to i. This updated value of i is never used before it is reset (to 1) at the next go round the inner loop.
I can't say that the rest of your code is OK, I'm not sure I follow your question entirely, but these bits smell a bit fishy.
x = repmat(X(:), 1, numel(X));
y = repmat(Y(:), 1, numel(Y));
z = repmat(Z(:), 1, numel(Z));
dst = sqrt((x - x') .^ 2 + (y - y') .^ 2 + (z - z') .^ 2);