integration of a list of functions in Matlab - 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.

Related

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

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.

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.

Write function for bsxfun with if/else

In my code, I need to divide each values of a matrix by the values of another. I could use A./B but some elements in B are 0. I know that if B(i,j) = 0 so A(i,j) = 0 too and I want to have 0/0 = 0. So I wrote a function div and I use bsxfun but I don't have 0, I have NaN :
A = [1,0;1,1];
B = [1,0;1,2];
function n = div(a,b)
if(b==0)
n = 0;
else
n = a./b;
end
end
C = bsxfun(#div,A,B);
Why not just replace the unwanted values after?
C=A./B;
C(A==0 & B==0)=0;
You could do C(isnan(C))=0;, but this will replace all NaN, even the ones not created by 0/0. If zeros always happen together then just C(B==0)=0; will do
If you know your non-zero values in B are never smaller than a very small number eps (for example 1e-300), a simple trick is to add eps to B. All non-zero values are unchanged, while all zero values become eps. When dividing 0/eps you get the wished result.
The reason this is happening is because bsxfun doesn't process the arrays element-wise. Consequently, your function doesn't get two scalars in. It is actually called only once. Your if statement does not work for non-scalar values of b.
Replacing bsxfun with arrayfun will call your function with scalar inputs, and will yield the expected result:
>> C = arrayfun(#div,A,B)
C =
1.0000 0
1.0000 0.5000
Nonetheless, either of the other two answers will be more efficient:
>> C = A./B;
>> C(B==0) = 0 % Ander's answer
C =
1.0000 0
1.0000 0.5000
or
C = A./(B+eps) % user10259794's answer
C =
1.0000 0
1.0000 0.5000

Matlab - Compare vectors with setdiff while ignoring precision below eps

Given two vectors containing numerical values, say for example
a=1.:0.1:2.;
b=a+0.1;
I would like to select only the differing values. For this Matlab provides the function setdiff. In the above example it is obvious that setdiff(a,b) should return 1. and setdiff(b,a) gives 2.1. However, due to computational precision (see the questions here or here) the result differs. I get
>> setdiff(a,b)
ans =
1.0000 1.2000 1.4000 1.7000 1.9000
Matlab provides a function which returns a lower limit to this precision error, eps. This allows us to estimate a tolerance like tol = 100*eps;
My question now, is there an intelligent and efficient way to select only those values whose difference is below tol? Or said differently: How do I write my own version of setdiff, returning both values and indexes, which includes a tolerance limit?
I don't like the way it is answered in this question, since matlab already provides part of the required functionality.
Introduction and custom function
In a general case with floating point precision issues, one would be advised to use a tolerance value for comparisons against suspected zero values and that tolerance must be a very small value. A little robust method would use a tolerance that uses eps in it. Now, since MATLAB basically performs subtractions with setdiff, you can use eps directly here by comparing for lesser than or equal to it to find zeros.
This forms the basis of a modified setdiff for floating point numbers shown here -
function [C,IA] = setdiff_fp(A,B)
%//SETDIFF_FP Set difference for floating point numbers.
%// C = SETDIFF_FP(A,B) for vectors A and B, returns the values in A that
%// are not in B with no repetitions. C will be sorted.
%//
%// [C,IA] = SETDIFF_FP(A,B) also returns an index vector IA such that
%// C = A(IA). If there are repeated values in A that are not in B, then
%// the index of the first occurrence of each repeated value is returned.
%// Get 2D matrix of absolute difference between each element of A against
%// each element of B
abs_diff_mat = abs(bsxfun(#minus,A,B.')); %//'
%// Compare each element against eps to "negate" the floating point
%// precision issues. Thus, we have a binary array of true comparisons.
abs_diff_mat_epscmp = abs_diff_mat<=eps;
%// Find indices of A that are exclusive to it
A_ind = ~any(abs_diff_mat_epscmp,1);
%// Get unique(to account for no repetitions and being sorted) exclusive
%// A elements for the final output alongwith the indices
[C,IA] = intersect(A,unique(A(A_ind)));
return;
Example runs
Case1 (With integers)
This will verify that setdiff_fp works with integer arrays just the way setdiff does.
A = [2 5];
B = [9 8 8 1 2 1 1 5];
[C_setdiff,IA_setdiff] = setdiff(B,A)
[C_setdiff_fp,IA_setdiff_fp] = setdiff_fp(B,A)
Output
A =
2 5
B =
9 8 8 1 2 1 1 5
C_setdiff =
1 8 9
IA_setdiff =
4
2
1
C_setdiff_fp =
1 8 9
IA_setdiff_fp =
4
2
1
Case2 (With floating point numbers)
This is to show that setdiff_fp produces the correct results, while setdiff doesn't. Additionally, this will also test out the output indices.
A=1.:0.1:1.5
B=[A+0.1 5.5 5.5 2.6]
[C_setdiff,IA_setdiff] = setdiff(B,A)
[C_setdiff_fp,IA_setdiff_fp] = setdiff_fp(B,A)
Output
A =
1.0000 1.1000 1.2000 1.3000 1.4000 1.5000
B =
1.1000 1.2000 1.3000 1.4000 1.5000 1.6000 5.5000 5.5000 2.6000
C_setdiff =
1.2000 1.4000 1.6000 2.6000 5.5000
IA_setdiff =
2
4
6
9
7
C_setdiff_fp =
1.6000 2.6000 5.5000
IA_setdiff_fp =
6
9
7
For Tolerance of 1 epsilon This should work:
a=1.0:0.1:2.0;
b=a+0.1;
b=[b b-eps b+eps];
c=setdiff(a,b)
The idea is to expand b to include also its closest values.

Matlab log(1) is not always zero

I am developing an image processing application for object detection.
At some point I am using log of the generalized eigenvalues vector of two square covariance matrices.
Assume that I have a 9x9 covariance matrix a.
a = rand(9, 9)%just generating random matrix for testing problem easily
b = eig(a, a)%generalized eigenvalues vector containing nine values equal to 1
%so we have b = [1.000, 1.000, 1.000 ... (9 times)]
c = log(b(:)) %we know b contains values of 1. and log(1) is 0.
Even though we know and can debug to see that b contains elements with value 1 and log(1) is 0, content of c is:
1.0e-014 *
0.0222
0.1110
0.0222
0.0222
-0.0777
0
0.0222
0.0888
0
This in my case.
Anyone knows why doesn't c have values of 0?
Thanks.
As #OliCharlesworth commented the values of b aren't really 1. I did the exact same as you did and got the following for b:
b =
1.0000
1.0000
1.0000
1.0000
1.0000
1.0000
1.0000
1.0000
1.0000
But when I opened b up in the variable explorer I got the following:
You'll see that there's only really one value of 1 and not 1.000 meaning that there are some trailing values that aren't shown in MatLab. Hence you would get the following for c:
c =
1.0e-15 *
0.2220
-0.4441
0.2220
-0.2220
0.2220
0
0.2220
-0.1110
-0.1110
Notice the 1.0e-15 that's where trailing values are being found.
Trailing zeros after a number, like
1.0000
indicates, that it is not exactly this value.
Try
format long
to see trailing digits up to 15.
If this isn't exactly enough, try out the print command
sprintf('%.50f',b(1))
to see 50 trailing digits of the value b(1).
Thus, the numbers are not exactly 1, neither log becomes exactly 0.