I'd like to find a vectorized way to calculate the cumulative sums of a vector, but with upper and lower limits.
In my case, the input only contains 1's and -1's. You can use this assumption in your answer. Of course, a more general solution is also welcome.
For example:
x = [1 1 1 1 -1 -1 -1 -1 -1 -1];
upper = 3;
lower = 0;
s = cumsum(x) %// Ordinary cumsum.
s =
1 2 3 4 3 2 1 0 -1 -2
y = cumsumlim(x, upper, lower) %// Cumsum with limits.
y =
1 2 3 3 2 1 0 0 0 0
^ ^
| |
upper limit lower limit
When the cumulative sum reaches the upper limit (at the 3rd element), it won't increase anymore. Likewise, when the cumulative sum reaches the lower limit (at the 7th element), it won't decrease anymore. A for-loop version would be like this:
function y = cumsumlim(x, upper, lower)
y = zeros(size(x));
y(1) = x(1);
for i = 2 : numel(x)
y(i) = y(i-1) + x(i);
y(i) = min(y(i), upper);
y(i) = max(y(i), lower);
end
end
Do you have any ideas?
This is a somewhat hackish solution, but perhaps worth mentioning.
You can do the sum using a signed integer data type, and exploit the inherent limits of that data type. For this to work, the input needs to be converted to that integer type and multiplied by the appropiate factor, and an initial offset needs to be applied. The factor and offset are chosen as a function of lower and upper. After cumsum, the multiplication and offset are undone to obtain the desired result.
In your example, data type int8 suffices; and the required factor and offset are 85 and -128 respectively:
x = [1 1 1 1 -1 -1 -1 -1 -1 -1];
result = cumsum([-128 int8(x)*85]); %// integer sum, with factor and initial offset
result = (double(result(2:end))+128)/85; %// undo factor and offset
which gives
result =
1 2 3 3 2 1 0 0 0 0
I won't provide you with a magic vectorized way to do this, but I'll provide you with some data that probably will help you get on with your work.
Your cumsumlim function is very fast!
tic
for ii = 1:100
y = cumsumlim(x,3,0);
end
t = toc;
disp(['Length of vector: ' num2str(numel(x))])
disp(['Total time for one execution: ' num2str(t*10), ' ms.'])
Length of vector: 65000
Total time for one execution: 1.7965 ms.
I really doubt this is your bottleneck. Have you tried profiling the code?
Related
I have a Matlab code, which use fmincon with some constraints. So that I am able to modify the code I have thought about whether the line position within the condition matrix A makes a difference
I set up a test file so I can change some variables. It turns out that the position of the condition is irrelevant for the result, but the number of rows in A and b plays a role. I´m suprised by that because I would expect that a row with only zeros in A and b just cancel out.
fun = #(x)100*(x(2)-x(1)^2)^2 + (1-x(1))^2;
options1 = optimoptions('fmincon','Display','off');
A=zeros(2,2); %setup A
A(2,2)=1; %x2<0
b=[0 0]'; %setup b
x = fmincon(fun,[-1,2],A,b,[],[],[],[],[],options1);x
%change condition position inside A
A=zeros(2,2);
A(1,2)=1; %x2<0
b=[0 0]';
x = fmincon(fun,[-1,2],A,b,[],[],[],[],[],options1);x
% no change; the position doesn´t influence fmincon
%change row size of A
A=zeros(1,2);
A(1,2)=1; %x2<0
b=[0]';
x = fmincon(fun,[-1,2],A,b,[],[],[],[],[],options1);x
%change in x2
%increase size of A
A=zeros(10,2);
A(1,2)=1; %x2<0
b=[0 0 0 0 0 0 0 0 0 0]';
x = fmincon(fun,[-1,2],A,b,[],[],[],[],[],options1);x
%change in x2
Can someone explain to me why fmincon is influenced by the row number? What is the "right" rownumber in A and b? The number of variables or the number of conditions?
EDIT
For reasons of completeness:
I agree that different values are possible because of the iteration process. Nevertheless I can find situations where the difference is bigger than the tolerance:
Added +log(x(2) to the function:
fun = #(x)100*(x(2)-x(1)^2)^2 + (1-x(1))^2+log(x(3));
options1 = optimoptions('fmincon','Display','off');
options = optimoptions('fmincon')
A=zeros(2,3); %setup A
A(2,3)=1; %x2<0
b=[0 0]'; %setup b
x = fmincon(fun,[-1,2,1],A,b,[],[],[],[],[],options1);x
%change row size of A
A=zeros(1,3);
A(1,3)=1; %x2<0
b=[0]';
x = fmincon(fun,[-1,2,1],A,b,[],[],[],[],[],options1);x
%change in x2
%increase size of A
A=zeros(10,3);
A(1,3)=1; %x2<0
b=[0 0 0 0 0 0 0 0 0 0]';
x = fmincon(fun,[-1,2,1],A,b,[],[],[],[],[],options1);x
%change in x2
x =
-0.79876 **0.49156** 2.3103e-11
x =
-0.79921 0.49143 1.1341e-11
x =
-0.80253 **0.50099** 5.8733e-12
Matlab support told me that the A matrix should not have more rows than conditions. Each condition makes it more difficult for the algorithm.
Note that fmincom doesn't necessarily give the exact solution but a good approximation of the solution according to a certain criteria.
The difference in results are plausible since fminconis an iterative algorithm and these matrix multiplications (even if there are mainly zeros) will eventually end with different results. Matlab will actually do these matrix multiplications until he finds the best result. So these results are all correct in the sense they are all close to the solution.
x =
0.161261791015350 -0.000000117317860
x =
0.161261791015350 -0.000000117317860
x =
0.161261838607809 -0.000000077614999
x =
0.161261877075196 -0.000000096088746
The difference in your results is around 1.0e-07 which is decent result considering you don't specify stopping criteria. You can see what you have by default with the command
options = optimoptions('fmincon')
My result is
Default properties:
Algorithm: 'interior-point'
CheckGradients: 0
ConstraintTolerance: 1.0000e-06
Display: 'final'
FiniteDifferenceStepSize: 'sqrt(eps)'
FiniteDifferenceType: 'forward'
HessianApproximation: 'bfgs'
HessianFcn: []
HessianMultiplyFcn: []
HonorBounds: 1
MaxFunctionEvaluations: 3000
MaxIterations: 1000
ObjectiveLimit: -1.0000e+20
OptimalityTolerance: 1.0000e-06
OutputFcn: []
PlotFcn: []
ScaleProblem: 0
SpecifyConstraintGradient: 0
SpecifyObjectiveGradient: 0
StepTolerance: 1.0000e-10
SubproblemAlgorithm: 'factorization'
TypicalX: 'ones(numberOfVariables,1)'
UseParallel: 0
For example, I can reach closer results with the option:
options1 = optimoptions('fmincon','Display','off', 'OptimalityTolerance', 1.0e-09);
Result is
x =
0.161262015455003 -0.000000000243997
x =
0.161262015455003 -0.000000000243997
x =
0.161262015706777 -0.000000000007691
x =
0.161262015313928 -0.000000000234186
You can also try and play with other criteria MaxFunctionEvaluations, MaxFunctionEvaluations etc to see if you can have even closer results...
I have a matrix suppX in Matlab with size GxN and a matrix A with size MxN. I would like your help to construct a matrix Xresponse with size GxM with Xresponse(g,m)=1 if the row A(m,:) is equal to the row suppX(g,:) and zero otherwise.
Let me explain better with an example.
suppX=[1 2 3 4;
5 6 7 8;
9 10 11 12]; %GxN
A=[1 2 3 4;
1 2 3 4;
9 10 11 12;
1 2 3 4]; %MxN
Xresponse=[1 1 0 1;
0 0 0 0;
0 0 1 0]; %GxM
I have written a code that does what I want.
Xresponsemy=zeros(size(suppX,1), size(A,1));
for x=1:size(suppX,1)
Xresponsemy(x,:)=ismember(A, suppX(x,:), 'rows').';
end
My code uses a loop. I would like to avoid this because in my real case this piece of code is part of another big loop. Do you have suggestions without looping?
One way to do this would be to treat each matrix as vectors in N dimensional space and you can find the L2 norm (or the Euclidean distance) of each vector. After, check if the distance is 0. If it is, then you have a match. Specifically, you can create a matrix such that element (i,j) in this matrix calculates the distance between row i in one matrix to row j in the other matrix.
You can treat your problem by modifying the distance matrix that results from this problem such that 1 means the two vectors completely similar and 0 otherwise.
This post should be of interest: Efficiently compute pairwise squared Euclidean distance in Matlab.
I would specifically look at the answer by Shai Bagon that uses matrix multiplication and broadcasting. You would then modify it so that you find distances that would be equal to 0:
nA = sum(A.^2, 2); % norm of A's elements
nB = sum(suppX.^2, 2); % norm of B's elements
Xresponse = bsxfun(#plus, nB, nA.') - 2 * suppX * A.';
Xresponse = Xresponse == 0;
We get:
Xresponse =
3×4 logical array
1 1 0 1
0 0 0 0
0 0 1 0
Note on floating-point efficiency
Because you are using ismember in your implementation, it's implicit to me that you expect all values to be integer. In this case, you can very much compare directly with the zero distance without loss of accuracy. If you intend to move to floating-point, you should always compare with some small threshold instead of 0, like Xresponse = Xresponse <= 1e-10; or something to that effect. I don't believe that is needed for your scenario.
Here's an alternative to #rayryeng's answer: reduce each row of the two matrices to a unique identifier using the third output of unique with the 'rows' input flag, and then compare the identifiers with singleton expansion (broadcast) using bsxfun:
[~, ~, w] = unique([A; suppX], 'rows');
Xresponse = bsxfun(#eq, w(1:size(A,1)).', w(size(A,1)+1:end));
I want to find the minimum amount of lag between two vector , I mean the minimum distance that something is repeated in vector based on another one
for example for
x=[0 0 1 2 2 2 0 0 0 0]
y=[1 2 2 2 0 0 1 2 2 2]
I want to obtain 4 for x to y and obtain 2 for y to x .
I found out a finddelay(x,y) function that works correctly only for x to y (it gives -4 for y to x).
is there any function that only give me lag based on going to the right direction of the vector? I will be so thankful if you'd mind helping me to get this result
I think this may be a potential bug in finddelay. Note this excerpt from the documentation (emphasis mine):
X and Y need not be exact delayed copies of each other, as finddelay(X,Y) returns an estimate of the delay via cross-correlation. However this estimated delay has a useful meaning only if there is sufficient correlation between delayed versions of X and Y. Also, if several delays are possible, as in the case of periodic signals, the delay with the smallest absolute value is returned. In the case that both a positive and a negative delay with the same absolute value are possible, the positive delay is returned.
This would seem to imply that finddelay(y, x) should return 2, when it actually returns -4.
EDIT:
This would appear to be an issue related to floating-point errors introduced by xcorr as I describe in my answer to this related question. If you type type finddelay into the Command Window, you can see that finddelay uses xcorr internally. Even when the inputs to xcorr are integer values, the results (which you would expect to be integer values as well) can end up having floating-point errors that cause them to be slightly larger or smaller than an integer value. This can then change the indices where maxima would be located. The solution is to round the output from xcorr when you know your inputs are all integer values.
A better implementation of finddelay for integer values might be something like this, which would actually return the delay with the smallest absolute value:
function delay = finddelay_int(x, y)
[d, lags] = xcorr(x, y);
d = round(d);
lags = -lags(d == max(d));
[~, index] = min(abs(lags));
delay = lags(index);
end
However, in your question you are asking for the positive delays to be returned, which won't necessarily be the smallest in absolute value. Here's a different implementation of finddelay that works correctly for integer values and gives preference to positive delays:
function delay = finddelay_pos(x, y)
[d, lags] = xcorr(x, y);
d = round(d);
lags = -lags(d == max(d));
index = (lags <= 0);
if all(index)
delay = lags(1);
else
delay = lags(find(index, 1)-1);
end
end
And here are the various results for your test case:
>> x = [0 0 1 2 2 2 0 0 0 0];
>> y = [1 2 2 2 0 0 1 2 2 2];
>> [finddelay(x, y) finddelay(y, x)] % The default behavior, which fails to find
% the delays with smallest absolute value
ans =
4 -4
>> [finddelay_int(x, y) finddelay_int(y, x)] % Correctly finds the delays with the
% smallest absolute value
ans =
-2 2
>> [finddelay_pos(x, y) finddelay_pos(y, x)] % Finds the smallest positive delays
ans =
4 2
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I need to create all 0,1 NxN matrices with zero diagonal. Matrices must be symmetrical. In every column and row must be at least one 1. Any ideas that can help?
getting all possible matrices of that form
The idea is that each matrix of this type with size NxN is defined by its upper diagonal values. Therefore, iterating over all the possible patterns for the upper diagonal part, and coping these values to the lower diagonal will do the trick.
Code example:
%defines N
N = 3;
%calculates degree of freedom
nValuesToRand = ((N*N) - N)/2;
%generate all possible binary patterns of size nValuesToRand
B = dec2bin(0:2^nValuesToRand - 1);
%masks of lower and upper diagonal - will be used later on
upperTriagonalMask = logical(triu(ones(N,N)) - eye(N));
lowerTriagonalMask = logical(tril(ones(N,N)) - eye(N));
%generates a new cell to hold the matrices
allMatrices = cell(size(B,1),1);
%iterates over all possible patterns
for i=1:size(B,1)
%generates a new matrix
mat = zeros(N,N);
%initializes its upper diagonal according to the binary pattern
mat(upperTriagonalMask) = logical(B(i,:)- 48);
%copies the upper triagonal to the lower triagonal (for symmetricality)
upperTriagonalTransposed = triu(mat)';
mat(lowerTriagonalMask) = upperTriagonalTransposed(lowerTriagonalMask);
%ignores illegal Matrices
if sum(sum(mat,2)==0)>0
continue;
end
%saves mat in the cell
allMatrices{i} = mat;
end
%cleanes cell
allMatrices = allMatrices(~cellfun(#isempty, allMatrices));
Random matrix generation
generating all possible matrices for large N values is computationally hard.
If you want to generate a matrix randomly, you can try the following approach:
%Dimension size
N = 6;
%Probability for appearance of 0
P = 0.5;
%A mask of the lower diagonal, to be used later on
lowerTriagonalMask = logical(tril(ones(N,N)));
%initializes the matrix
mat = zeros(N,N);
%runs the loop as long as the matrix is not valid
while (sum(mat,2)==0)>0
%defines a random binary matrix
mat = rand(N,N) > P;
%zero out the diagonal values
mat(logical(eye(N))) = 0;
%copies the upper triagonal to the lower triagonal (for symmetricality)
upperTriagonalTransposed = triu(mat)';
mat(lowerTriagonalMask) = upperTriagonalTransposed(lowerTriagonalMask);
end
%testing
issymmetric(double(mat))
mat
result:
ans =
1
mat =
0 1 1 1 0 0
1 0 1 1 1 1
1 1 0 0 0 1
1 1 0 0 0 1
0 1 0 0 0 1
0 1 1 1 1 0
Can anybody help me create a simple pseudo-random sequence of +-1 integers with length 1000 using Matlab?
I.e. a sequence such as
-1 -1 1 1 -1 -1 1 -1 -1 -1 1 1 1 1 -1 -1 -1 -1 1
I tried using this code below but this is the RANGE -1 to 1, which includes 0 values. I only want -1 and 1. Thanks
x = randi([-1 1],1000,1);
You can try generating a random sequence of floating point numbers from [0,1] and any values less than 0.5 set to -1, and anything larger set to 1:
x = rand(1000,1);
ind = x >= 0.5;
x(ind) = 1;
x(~ind) = -1;
Another suggestion I have is to use the sign function combined with randn so that we can generate both positive and negative numbers. sign generates values that are either -1, 0, 1 depending on the sign of the input. If the input is negative, the output is -1, +1 when positive and 0 when 0. You could do an additional check where any values that are output to 0, set them to -1 or 1:
x = sign(randn(1000,1));
x(x == 0) = 1;
One more (inspired by Luis Mendo) would be to have a vector of [-1,1] and use randi to generate a sequence of either 1 or 2, then use this and sample into this vector:
vec = [-1 1];
x = vec(randi(numel(vec), 1000, 1));
This code can be extended where vec can be anything you want, and we can sample from any element in vec to produce a random sequence of values (observation made by Luis Mendo. Thanks!).
Some alternatives:
x = 2*randi(2, 1000, 1)-3; %// generate 1 and 2 values, and transform to -1 and 1
x = 2*(rand(1, 1000, 1)<=.5)-1; %// similar to Rayryeng's answer but in one step
x = randsample([-1 1], 1000, true); %// sample with replacement from the set [-1 1]
Simply user randsrc function.
It will generate random sequences of 1 and -1.
For example
out = randsrc(2,3)
out =
-1 -1 -1
1 -1 1
Thanks for these many helpful answers. I figure this topic might be general enough it may well deserve a comparison.
In my setup ( Windows8.4 x64 i74820k cpu and with R2014a) the fastest version is consistently:
x=2*round(rand(L,1))-1;
Being half an order of magnitude faster than the slowest solution. Hope this helps.
comparison:
figure comparing execution times for pseudo-random sign generation
code:
L=[];
for expon=0:6
for mant=1:9
L=cat(1,L,mant*power(10,expon));
end
end
clear expon mant
t1=zeros(length(L),1);
x=2*round(rand(L(1),1))-1;
for li=1:length(L)
tic,
x=2*round(rand(L(li),1))-1;
t1(li)=toc;
end
t2=zeros(length(L),1);
x=(rand(L(1),1)>0.5)*2-1;
for li=1:length(L)
tic,
x=(rand(L(li),1)>0.5)*2-1;
t2(li)=toc;
end
t3=zeros(length(L),1);
x=(randi([0,1],L(1),1)>0.5)*2-1;
for li=1:length(L)
tic,
x=(randi([0,1],L(li),1)>0.5)*2-1;
t3(li)=toc;
end
t4=zeros(length(L),1);
x=rand(L(1),1);ind=x>=0.5;x(ind)=1;x(~ind)=-1;
for li=1:length(L)
tic,
x=rand(L(li),1);
ind=x>=0.5;
x(ind)=1;
x(~ind)=-1;
t4(li)=toc;
end
t5=zeros(length(L),1);
x=sign(randn(L(1),1));
for li=1:length(L)
tic,
x=sign(randn(L(li),1));
x(x==0)=1;
t5(li)=toc;
end
t6=zeros(length(L),1);
vec = [-1 1];
x=vec(randi(numel(vec),L(1),1));
for li=1:length(L)
tic,
x=vec(randi(numel(vec),L(li),1));
t6(li)=toc;
end
t7=zeros(length(L),1);
x=2*randi(2,L(1),1)-3;
for li=1:length(L)
tic,
x=2*randi(2,L(li),1)-3;
t7(li)=toc;
end
t8=zeros(length(L),1);
x=randsample([-1 1],L(1),true);
for li=1:length(L)
tic,
x=randsample([-1 1],L(li),true);
t8(li)=toc;
end
clear x vec ind li
figure,
loglog(L,[t1 t2 t3 t4 t5 t6 t7 t8],'.-','linewidth',2)
grid on
grid minor
title('Generating pseudo-random sequence +1/-1')
ylabel('Exec. Time [s]')
xlabel('Output Vector Length')
T{1}='x=2*round(rand(L(1),1))-1';
T{2}='x=(rand(L(1),1)>0.5)*2-1';
T{3}='x=(randi([0,1],L(1),1)>0.5)*2-1';
T{4}='x=rand(L(1),1);ind=x>=0.5;x(ind)=1;x(~ind)=-1';
T{5}='x=sign(randn(L(1),1))';
T{6}='vec=[-1 1];x=vec(randi(numel(vec),L(1),1))';
T{7}='x=2*randi(2,L(1),1)-3';
T{8}='x=randsample([-1 1],L(1),true)';
legend(T,'location','northwest')
x = rand(N,1);
y = sign(x-0.5);