(-8)^(-2/3) returns wrong result - matlab

I'm using Matlab R2020b (Mac OS 12.0.1).
When I enter (-8)^(-2/3), it returns:
ans =
-0.1250 - 0.2165i
Shouldn't it be 0.2500, instead?

Raising negative numbers to fractional powers is a complex multi-valued operation. MATLAB is simply picking one of the solutions for you. E.g., for the solutions to (-8)^(1/3). start with the roots to the following polynomial equation:
x^3+8=0
Using MATLAB for this:
>> roots([1 0 0 8])
ans =
-2.0000 + 0.0000i
1.0000 + 1.7321i
1.0000 - 1.7321i
Then raising this result to the -2 power yields:
>> ans.^-2
ans =
0.2500 + 0.0000i
-0.1250 - 0.2165i
-0.1250 + 0.2165i
MATLAB happened to give you the second solution above for the (-8)^(-2/3) calculation.
BOTTOM LINE: Whenever you are dealing with complex multi-valued operations, if you want specific results you will need to account for that in your code, because MATLAB might pick something else.

Depends on the order of calculation.
(-8)^(-2/3) means divide -2 by 3 and then raise -8 to that power.
But if you do ((-8)^(-2))^(1/3) or nthroot((-8)^(-2),3) instead, you'll get 0.25.

Related

Index of the number with the lowest real part, of a complex number

I have a problem with some complex numbers, which I rarely work with.
My problem is that I have an array with made by
cos^(-1)(x) where x can be values smaller or larger than |1|, thereby getting some complex numbers, I need the index of the number with the lowest real part.
the data could look like
[0 + 0.37i, 0 + 0.18i, 0.2 + 0.0i, 0.3 + 0.0i, 0.4 + 0.0i]
so I need the index of 0.2 + 0.0i
what I have tried so far is
[val_min_x,idx_min_x]=min(real(x)>0))
since I need the smallest value of the real part larger then zero.
But this wont work and I guess its because real(x>0) gives out true or false. And then taking the minimum of that just gives the index of the first zero.
Any suggestions to solve this without an if statement?
First convert all 0s to 1s, so that min() will not find them (by addition of 1). Then keep everything else untouched (by addition of 0). A boolean trick will keep everything in one line for brevity,
Thanks #CG
% example input
A=[0 + 0.37i, 0 + 0.18i, 0.2 + 0.0i, 0.3 + 0.0i, 0.4 + 0.0i]
% leveraging maximum cos(x)<=1 and boolean true being cast to 1
[~, idx] = min(real(A)+(real(A)==0))
A =
Columns 1 through 3
0.0000 + 0.3700i 0.0000 + 0.1800i 0.2000 + 0.0000i
Columns 4 through 5
0.3000 + 0.0000i 0.4000 + 0.0000i
idx =
3

Using Matlab's backslash operator to invert sparse matrices is leading to some entries being rounded down to zero

I am using Matlab's backslash operator to solve a system of equations written as two matrices M1 and M2. These two matrices are square and tridiagonal, and so I have defined them as sparse. For example, with the dimensions of each being 5x5, they are defined as follows, with the values in each entry being dependent on some constant a:
N = 5;
a = 1e10;
M1 = spdiags([-a*ones(N,1)... % Sub diagonal
(1 + 2*a)*ones(N,1)... % Main Diagonal
-a*ones(N,1)],... % Super diagonal
-1:1,N,N);
M2 = spdiags([+a*ones(N,1)...
(1 - 2*a)*ones(N,1)...
+a*ones(N,1)],...
-1:1,N,N);
M_out = M1\M2;
So for example, M1 looks like the following in full form:
>> full(M1)
ans =
1.0e+10 *
2.0000 -1.0000 0 0 0
-1.0000 2.0000 -1.0000 0 0
0 -1.0000 2.0000 -1.0000 0
0 0 -1.0000 2.0000 -1.0000
0 0 0 -1.0000 2.0000
Now, if I examine the number of non-zero entries in the result M_out, then I can see they are all non-zero, which is fine:
>> nnz(M_out)
ans =
25
The problem is that I also need to do this for larger values of the constant a. However, if, for example, a=1e16 instead, then the off-diagonal entries of M_out are automatically set to zero, presumably because they have become too small:
>> nnz(M_out)
ans =
5
Is there a better way in Matlab of going about this problem of inverting sparse matrices? Or am I using the backslash operator in the wrong way?
If the size of your matrices doesn't grow too much, I recommend doing a full symbolic computation:
N = 5;
syms a
M1 = diag(-a*ones(N-1,1),-1) + diag((1 + 2*a)*ones(N,1),0) + diag(-a*ones(N-1,1),+1);
M2 = diag(+a*ones(N-1,1),-1) + diag((1 - 2*a)*ones(N,1),0) + diag(+a*ones(N-1,1),+1);
M_out = M1\M2;
M_num_1e10 = subs(M_out,a,1e10);
M_num_1e16 = subs(M_out,a,1e16);
vpa(M_num_1e10)
vpa(M_num_1e16)
In that case, you will need the Symbolic Math Toolbox. If you don't have it, I think you should considerer migrating to Python and work with SymPy.
EDIT:
Considering the way you defined your problem, you need extended precision for your computations. The double precision isn't enough. For example, in double precision (1e16+1) has to be rounded to (1e16), in other words (1e16+1)-(1e16) is equal to zero. So your problem starts in the main diagonal of your matrices. MATLAB only provides extended precision through its symbolic toolbox.
If you want to stick with double precision, you may extend the double precision yourself relying on the so called double-double arithmetic. I say that you will have to do it by yourself because I don't think there is a open source double-double library for MATLAB.

Wrong std and mean values then expected

I have a matrix X with 3 columns. For the porous of the question X=randn(5,3).
I want to normalize the columns of X S.T. each column will have a 0 mean and a 1 std. I was using the following code:
X=(X-mean(X))./std(X);
I am getting an std of 1. My mean, however, is a very small value close to 0 but not essential 0. I tried playing a bit with the numbers to find an explanation:
X=1:15;
X=reshape(X,[5 3]);
mean(X-mean(X));
Which gives me 0 value for each column.
X=1:15;
X=reshape(X,[5 3]);
mean((X-mean(X))./std(X));
Which does not. But 0/anything is still 0. what am I missing?
Why am I not getting 0 values?
Are the values I am getting good enough for a pre-clustering algorithm normalization?
Here is a version that does what I think you're trying to do... you need to replicate the matrix because X-mean(X) isn't valid (if you're using the standard implementation)-- you can't subtract a 1x3 from a 5x3.
r = 5; c = 3;
X=randn(r,c);
Xm=repmat(mean(X),r,1);
Xstd = repmat(std(X),r,1);
Xn = (X-Xm)./Xstd;
mean(Xn)
std(Xn)
For me this prints out
ans =
1.0e-16 *
-0.6661 0 0.4441
ans =
1.0000 1.0000 1.0000
Which seems like exactly what you're looking for... note the 1e-16 multiplier on the mean values... this is essentially 0, with some floating point error.

integration of a list of functions in Matlab

How can I use integral over a list of functions. The code below doesn't work:
integral(#(v) exp(-v*[1 2 3]),0,100)
exp(-v*[1 2 3]) is the list of functions.
The code is inside another function with the vector [1 2 3] as input.
You need to used the name-value pair ('ArrayValued',true) for non-scalar functions (this is because for scalar integrands, the function passes vectors of integration points to the integrand function in conjunction with bsxfun for speed).
>> integral(#(v) exp(-v*[1 2 3]),0,100,'ArrayValued',true)
ans =
1.0000 0.5000 0.3333
I'd also note that integral can handle Inf as an upper-bound if that is what 100 is approximating:
>> integral(#(v) exp(-v*[1 2 3]),0,Inf,'ArrayValued',true)
ans =
1.0000 0.5000 0.3333
While the outputs look similar, this one is accurate to machine-precision.

How can I vectorize the entropy calculation?

I was trying to the entropy for every column, the matrix looks like this:
0.5 0.3333 0.2
0 0.3333 0.4
0.5 0.3333 0.4
Every column add up to one, however, there's some zeros in the matrix, so if I just log2(arr(i,:)), there will be an -Inf in the result so the whole thing won't work
In practice I have a huge matrix, so I want the program to run fast, is there a work around?
Here's my solution, does it works as fast as p .* log2(p)?
log2p = log2(p);
log2p(log2p==-Inf)=0;
entropy = entropy - p .* log2p;
In MATLAB 0^0 is equal to 1. And since log2(1)==0, You can use this and rewrite your entropy function as
p.*log2(p) = log2(p.^p)
Then for your example we get
>> log2(p.^p)
ans =
-0.5000 -0.5283 -0.4644
0 -0.5283 -0.5288
-0.5000 -0.5283 -0.5288
Use isinf
log2p = log2(p);
log2p( isinf(log2p) ) = 0;
entrpoy = -sum( p.*log2p , 1 )
You could use isnan in combination with the fact that 0*-inf==NaN:
E = p.*log2(p);
valid = ~isnan(E);
entropy(valid) = entropy(valid) - E(valid);
clear E valid
As found here, this should work without warnings if you have MATLAB newer than R2007a.
Use eps:
eps is minimum representable number in float so you will have your results without changing much(almost infinitesmall change).
log2(p)
ans =
-1.0000 -1.5851 -2.3219
-Inf -1.5851 -1.3219
-1.0000 -1.5851 -1.3219
log2(p+eps)
ans =
-1.0000 -1.5851 -2.3219
-52.0000 -1.5851 -1.3219
-1.0000 -1.5851 -1.3219
p2=p+eps;
entropy=-sum(p2.*log2(p2),1)
entropy =
1.0000 1.5849 1.5219