How explicitly does Matlab solve the rightmost equation in c1, specifically ((x-1)\y)?
I am well aware what happens when you use a matrix, e.g. A\b (where A is a matrix and b is a column vector), but what happens when you use backslash on two vectors with equal rows?
The problem:
x = (1:3)';
y = ones(3,1);
c1 = ((x-1)\y) % why does this =0.6?
You're doing [0;1;2]\[1;1;1]. Essentially x=0.6 is the least-squares solution to
[0;1;2]*x=[1;1;1]
The case you have from the documentation is the following:
If A is a rectangular m-by-n matrix with m ~= n, and B is a matrix with m rows, then A\B returns a least-squares solution to the system of equations A*x= B.
(Specifically, you have m=3, n=1).
A = (1:3).' - 1; % = [0;1;2]
B = ones(3,1); % = [1;1;1]
x = A\B; % = 0.6
Algebraically, it's easy to see this is the solution to the least-squares minimisation
% Calculate least squares
leastSquares = sum( ((A*x) - B).^2 )
= sum( ([0;1;2]*x - [1;1;1]).^2 )
= sum( [-1; x-1; 2x-1].^2 )
= 1 + (x-1)^2 + (2x-1)^2
= 1 + x^2 - 2*x + 1 + 4*x^2 - 4*x + 1
= 5*x^2 - 6*x + 3
% Minimum least squares -> derivative = 0
d(leastSquares)/dx = 10*x - 6 = 0
10*x = 6
x = 0.6
I have no doubt that MATLAB uses a more sophisticated algorithm to come to the same conclusion, but this lays out the mathematics in a fairly plain way.
You can see experimentally that there is no better solution by testing the following for various values of x... 0.6 gives the smallest error:
sum( ([0;1;2]*x - [1;1;1]).^2 )
Related
I am given a set of points (p1,q1) (p2,q2) ... (p20,q20) which satisfy the function q = 1/(ap + b)^2 except that one of these does not satisfy the given relation. The values of a and b are not given to me. All I have with me is two inputs p and q as arrays. I need to find the index of the point which does not satisfy the given relation.
The way I proceeded to solve is to find the values of a and b using the first two pairs (p1,q1) and (p2,q2) and check if the remaining points satisfy the function for the solved values of a and b. The results will be stored in a logical matrix. I wish to make use of the logical matrix to pick out the odd pair, but unable to proceed further.
Specifically, the challenge is to make use of vectorization in MATLAB to find the odd point, instead of resorting to for-loops. I think that I will have to first search for the only logical zero in any of the row. In that case, the column index of that zero will fetch me the odd point. But, if there are more than one zeros in all 4 rows, then the odd point is either of the first two pairs. I need help in translating this to efficient code in MATLAB.
Please note that vectors p and q have been named as x and y in the below code.
function [res, sol] = findThePair(x, y)
N = length(x);
syms a b
vars = [a,b];
eqns = [y(1) - 1/(a*x(1) + b)^2 == 0; y(2) - 1/(a*x(2) + b)^2];
[solA, solB] = solve(eqns,vars);
sol = [double(solA) double(solB)]; %solution of a & b (total 4 possibilites)
xTest = x(3:end); % performing check on remaining points
yTest = y(3:end);
res = zeros(4, N-2); % logical matrix to store the results of equality check
for i = 1:4
A = sol(i,1); B = sol(i, 2);
res(i, :) = [yTest == 1./(A*xTest + B).^2]; % perform equality check on remaining points
end
Let's do some maths up front, to avoid needing loops or vectorisation. At most this leaves us with half a dozen function evaluations, and we only need 5 points.
q = 1 / (a*p + b)^2
% ->
sqrt(q) * ( a*p + b ) = 1
% ->
a = ( 1 - b*sqrt(q) ) / ( p * sqrt(q) )
% Sub in some points (1 and 2) ->
a1 = ( 1 - b*sqrt(q1) ) / ( p1 * sqrt(q1) )
a2 = ( 1 - b*sqrt(q2) ) / ( p2 * sqrt(q2) )
% a1 and a2 should be the same ->
( 1 - b*sqrt(q1) ) * ( p2 * sqrt(q2) ) = ( 1 - b*sqrt(q2) ) * ( p1 * sqrt(q1) )
% Rearrange ->
b = ( p2*sqrt(q2) - p1*sqrt(q1) ) / ( (p2-p1)*sqrt(q1)*sqrt(q2) )
We have two unknowns, a and b. All we need are two points to create simultaneous equations. I'll use the following logic
Choose (pm, qm) and (pn, qn) with any m ~= n.
Calculate a and b using the above equation.
test whether (pr, qr) fits with the calculated a and b.
If it fits, we know all three of these must be on the curve, and we have a and b.
If it doesn't fit, we know either point m, n, or r is the outlier. Return to step (1) with two other points, the calculated a and b must be correct, as we've not fitted to the outlier.
Here is some code to implement this:
% Random coeffs, keep things unknown
a = rand*10;
b = rand*10;
% Set up our data
p = 1:20;
q = 1 ./ (a*p + b).^2;
% Create an outlier
q( 3 ) = q( 3 ) + 1;
% Steps as described
% 1.
p1 = p(1); p2 = p(2);
q1 = q(1); q2 = q(2);
% 2.
bGuess = ( p2*sqrt(q2) - p1*sqrt(q1) ) / ( (p2-p1)*sqrt(q1)*sqrt(q2) );
aGuess = ( 1 - bGuess*sqrt(q1) ) / ( p1 * sqrt(q1) );
% 3.
p3 = p(3);
q3Guess = 1 / ( aGuess*p3 + bGuess )^2;
tol = 1e-7; % Use tolerance rather than == comparison to avoid float issues
if abs( q3Guess - q(3) ) < tol
% success
aFit = aGuess;
bFit = bGuess;
else
% p1, p2 or p3 is an outlier! Repeat using other points
% If there's known to be only one outlier, this should give the result
p1 = p(4); p2 = p(5);
q1 = q(4); q2 = q(5);
bFit = ( p2*sqrt(q2) - p1*sqrt(q1) ) / ( (p2-p1)*sqrt(q1)*sqrt(q2) );
aFit = ( 1 - bFit*sqrt(q1) ) / ( p1 * sqrt(q1) );
end
% Validate
fprintf( 'a is valid: %d, b is valid: %d\n', abs(a-aFit)<tol, abs(b-bFit)<tol )
I don't really understand how you were trying to solve this and what do syms (i.e. symbolic variables) have to do with this, so I'll show you how I would solve this problem.
Since we're essentially looking for an outlier, we might as well convert the problem to something that's easier to work with. For this reason, instead of using q as-is, I'm going to invert it: this way, we'd be dealing with an equation of a parabola - which is easy.
Next, knowing that our points should lie on a parabola, we can fit the equation of the parabola (or equivalently - find the coefficients of the polynomial that describes the relation of the input to the output). The polynomial is a^2*x^2+2*a*b*x+b^2, and so the coefficients are {a^2, 2*a*b, b^2}.
Since the majority of the points (19 out of 20) lie on the same parabola, the outlier will always have a larger error, which would make it stand out, no matter how close it is to the parabola (within the limitations of machine precision) - you can see an extreme example of this in the code below.
Fitting of a parabola is performed using polynomial interpolation (see also: Vandermonde matrix).
function I = q55241683()
%% Generate the ground truth:
TRUE_A = 2.3;
TRUE_B = -pi;
IDX_BAD = 5;
p = 1:0.04:1.76;
q = (TRUE_A * p + TRUE_B).^-2;
q(IDX_BAD) = (1-1E-10)*q(IDX_BAD); % notice just how close this is to being valid
%% Visualize dataset:
% figure(); plot(p,q.^-1);
%% Solve
I = findThePair(p, q.^-1);
%% Test
if IDX_BAD == I
disp('Great success!');
else
disp('Complete failure!');
end
end
function I = findThePair(x,y)
% Fit a parabola to {x vs. y^-1}
P = x(:).^(2:-1:0)\y(:); %alternatively: P = polyfit(x,y.^-1,2)
% Estimate {a,b} (or {-a,-b})
est_A = sqrt(P(1));
est_B = P(2)/(2*est_A);
% Compute the distances of the points from the fit (residuals), find the biggest:
[~,I] = max( abs(y - (est_A*x + est_B).^2) );
end
How can I find all the points where Gaussian mixture distributions intersect in MATLAB?
The generic category of your question is finding the intersection of two curves, which is a manageable but non-trivial task (the hardest part is making sure you catch all the intersections).
But your problem is very specific: you're looking for the intersection of two Gaussians. This is very good: we both have an analytic formula for your function, and it's guaranteed that there are exactly two intersections unless some of your parameters are the same.*
Let's assume that your distributions are characterized by means mu1, mu2 and scale sigma1, sigma2. Then your Gaussians at position x are defined by the function
1/sqrt(2*pi*sigma^2) * exp(-(x-mu)^2/2/sigma^2)
Turns out that we can fully solve this equation for x on paper:
1/sqrt(2*pi*sigma1^2)*exp(-(x-mu1)^2/2/sigma1^2) == 1/sqrt(2*pi*sigma2^2)*exp(-(x-mu2)^2/2/sigma2^2)
sigma2/sigma1 == exp((x-mu1)^2/2/sigma1^2) * exp(-(x-mu2)^2/2/sigma2^2)
log(sigma2/sigma1) == (x-mu1)^2/2/sigma1^2) - (x-mu2)^2/2/sigma2^2
which results in the parabolic equation ax^2 + bx + c == 0 where
a = 1/(2*sigma1^2) - 1/(2*sigma2^2);
b = mu2/(sigma2^2) - mu1/(sigma1^2);
c = mu1^2/(2*sigma1^2) - mu2^2/(2*sigma2^2) - log(sigma2/sigma1);
It can be easily proved that the D = b^2 - 4 a c discriminant is non-negative, so indeed the equation has two real roots when the parameters aren't degenerate. So the two intersection points are, with the above definitions,
D = b^2 - 4 * a * c;
x1 = (-b + sqrt(D))/(2*a);
x2 = (-b - sqrt(D))/(2*a);
Using two gaussians with the pseudo-random parameters:
% define parameters and Gaussian
mu1=1; sigma1=3; mu2=2; sigma2=4;
% intersections
a = 1/(2*sigma1^2) - 1/(2*sigma2^2);
b = mu2/(sigma2^2) - mu1/(sigma1^2);
c = mu1^2/(2*sigma1^2) - mu2^2/(2*sigma2^2) - log(sigma2/sigma1)
D = b^2 - 4 * a * c;
x1 = (-b + sqrt(D))/(2*a);
x2 = (-b - sqrt(D))/(2*a);
Just to prove that the above intersection points are correct:
>> f = #(x,mu,sigma) 1/sqrt(2*pi*sigma^2) * exp(-(x-mu).^2/2/sigma^2);
>> f(x1,mu1,sigma1) - f(x1,mu2,sigma2)
ans =
2.7756e-17
>> f(x2,mu1,sigma1) - f(x2,mu2,sigma2)
ans =
1.0408e-17
The above means that the values of the two Gaussians at points x1 and x2 are equal to one another within machine precision, which is as good as any numerical answer will get.
*I originally claimed that we always have two intersections as long as the Gaussians aren't exactly the same. Clearly if both the means and the variances are the same we have two degenerate curves and intersections become meaningless. But as Cris Luengo pointed out in a comment, it might happen that there's only one intersection: when the variances are the same and the means are different (i.e. we have two curves of the exact same shape shifted along x). In this case a=0, consequently the corresponding equation is b*x + c == 0, giving us x0 = -c/b for the intersection. So a more exact (but a bit pseudo-codey) answer (given a, b and c) is
if a == 0 % or allow some tolerance... <=> sigma1 == sigma2
if b == 0 % or allow some tolerance... <=> mu1 == mu2
% degenerate curves: a == b == c == 0, f1(x)==f2(x) for all x
disp('curves are degenerate...')
else
% single intersection: mu1 ~= mu2
x1 = -c/b;
end
else
% two intersections; both parameters are different
D = b^2 - 4 * a * c;
x1 = (-b + sqrt(D))/(2*a);
x2 = (-b - sqrt(D))/(2*a);
I've had problems with my code as I've tried to make an integral compute, but it will not for the power, P2.
I've tried using anonymous function handles to use the integral() function on MATLAB as well as just using int(), but it will still not compute. Are the values too small for MATLAB to integrate or am I just missing something small?
Any help or advice would be appreciated to push me in the right direction. Thanks!
The problem in the code is in the bottom of the section labelled "Power Calculations". My integral also gets quite messy if that makes a difference.
%%%%%%%%%%% Parameters %%%%%%%%%%%%
n0 = 1; %air
n1 = 1.4; %layer 1
n2 = 2.62; %layer 2
n3 = 3.5; %silicon
L0 = 650*10^(-9); %centre wavelength
L1 = 200*10^(-9): 10*10^(-9): 2200*10^(-9); %lambda from 200nm to 2200nm
x = ((pi./2).*(L0./L1)); %layer phase thickness
r01 = ((n0 - n1)./(n0 + n1)); %reflection coefficient 01
r12 = ((n1 - n2)./(n1 + n2)); %reflection coefficient 12
r23 = ((n2 - n3)./(n2 + n3)); %reflection coefficient 23
t01 = ((2.*n0)./(n0 + n1)); %transmission coefficient 01
t12 = ((2.*n1)./(n1 + n2)); %transmission coefficient 12
t23 = ((2.*n2)./(n2 + n3)); %transmission coefficient 23
Q1 = [1 r01; r01 1]; %Matrix Q1
Q2 = [1 r12; r12 1]; %Matrix Q2
Q3 = [1 r23; r23 1]; %Matrix Q3
%%%%%%%%%%%% Graph of L vs R %%%%%%%%%%%
R = zeros(size(x));
for i = 1:length(x)
P = [exp(j.*x(i)) 0; 0 exp(-j.*x(i))]; %General Matrix P
T = ((1./(t01.*t12.*t23)).*(Q1*P*Q2*P*Q3)); %Transmission
T11 = T(1,1); %T11 value
T21 = T(2,1); %T21 value
R(i) = ((abs(T21./T11))^2).*100; %Percent reflectivity
end
plot(L1,R)
title('Percent Reflectance vs. wavelength for 2 Layers')
xlabel('Wavelength (m)')
ylabel('Reflectance (%)')
%%%%%%%%%%% Power Calculation %%%%%%%%%%
syms L; %General lamda
y = ((pi./2).*(L0./L)); %Layer phase thickness with variable Lamda
P1 = [exp(j.*y) 0; 0 exp(-j.*y)]; %Matrix P with variable Lambda
T1 = ((1./(t01.*t12.*t23)).*(Q1*P1*Q2*P1*Q3)); %Transmittivity matrix T1
I = ((6.16^(15))./((L.^(5)).*exp(2484./L) - 1)); %Blackbody Irradiance
Tf11 = T1(1,1); %New T11 section of matrix with variable Lambda
Tf2 = (((abs(1./Tf11))^2).*(n3./n0)); %final transmittivity
P1 = Tf2.*I; %Power before integration
L_initial = 200*10^(-9); %Initial wavelength
L_final = 2200*10^(-9); %Final wavelength
P2 = int(P1, L, L_initial, L_final) %Power production
I've refactored your code
to make it easier to read
to improve code reuse
to improve performance
to make it easier to understand
Why do you use so many unnecessary parentheses?!
Anyway, there's a few problems I saw in your code.
You used i as a loop variable, and j as the imaginary unit. It was OK for this one instance, but just barely so. In the future it's better to use 1i or 1j for the imaginary unit, and/or m or ii or something other than i or j as the loop index variable. You're helping yourself and your colleagues; it's just less confusing that way.
Towards the end, you used the variable name P1 twice in a row, and in two different ways. Although it works here, it's confusing! Took me a while to unravel why a matrix-producing function was producing scalars instead...
But by far the biggest problem in your code is the numerical problems with the blackbody irradiance computation. The term
L⁵ · exp(2484/L) - 1
for λ₀ = 200 · 10⁻⁹ m will require computing the quantity
exp(1.242 · 10¹⁰)
which, needless to say, is rather difficult for a computer :) Actually, the problem with your computation is two-fold. First, the exponentiation is definitely out of range of 64 bit IEEE-754 double precision, and will therefore result in ∞. Second, the parentheses are wrong; Planck's law should read
C/L⁵ · 1/(exp(D) - 1)
with C and D the constants (involving Planck's constant, speed of light, and Boltzmann constant), which you've presumably precomputed (I didn't check the values. I do know choice of units can mess these up, so better check).
So, aside from the silly parentheses error, I suspect the main problem is that you simply forgot to rescale λ to nm. Changing everything in the blackbody equation to nm and correcting those parentheses gives the code
I = 6.16^(15) / ( (L*1e+9)^5 * (exp(2484/(L*1e+9)) - 1) );
With this, I got a finite value for the integral of
P2 = 1.052916498836486e-010
But, again, you'd better double-check everything.
Note that I used quadgk(), because it's one of the better ones available on R2010a (which I'm stuck with), but you can just as easily replace this with integral() available on anything newer than R2012a.
Here's the code I ended up with:
function pwr = my_fcn()
% Parameters
n0 = 1; % air
n1 = 1.4; % layer 1
n2 = 2.62; % layer 2
n3 = 3.5; % silicon
L0 = 650e-9; % centre wavelength
% Reflection coefficients
r01 = (n0 - n1)/(n0 + n1);
r12 = (n1 - n2)/(n1 + n2);
r23 = (n2 - n3)/(n2 + n3);
% Transmission coefficients
t01 = (2*n0) / (n0 + n1);
t12 = (2*n1) / (n1 + n2);
t23 = (2*n2) / (n2 + n3);
% Quality factors
Q1 = [1 r01; r01 1];
Q2 = [1 r12; r12 1];
Q3 = [1 r23; r23 1];
% Initial & Final wavelengths
L_initial = 200e-9;
L_final = 2200e-9;
% plot reflectivity for selected lambda range
plot_reflectivity(L_initial, L_final, 1000);
% Compute power production
pwr = quadgk(#power_production, L_initial, L_final);
% Helper functions
% ========================================
% Graph of lambda vs reflectivity
function plot_reflectivity(L_initial, L_final, N)
L = linspace(L_initial, L_final, N);
R = zeros(size(L));
for ii = 1:numel(L)
% Transmission
T = transmittivity(L(ii));
% Percent reflectivity
R(ii) = 100 * abs(T(2,1)/T(1,1))^2 ;
end
plot(L, R)
title('Percent Reflectance vs. wavelength for 2 Layers')
xlabel('Wavelength (m)')
ylabel('Reflectance (%)')
end
% Compute transmittivity matrix for a single wavelength
function T = transmittivity(L)
% Layer phase thickness with variable Lamda
y = pi/2 * L0/L;
% Matrix P with variable Lambda
P1 = [exp(+1j*y) 0
0 exp(-1j*y)];
% Transmittivity matrix T1
T = 1/(t01*t12*t23) * Q1*P1*Q2*P1*Q3;
end
% Power for a specific wavelength. Note that this function
% accepts vector-valued wavelengths; needed for quadgk()
function pwr = power_production(L)
pwr = zeros(size(L));
for ii = 1:numel(L)
% Transmittivity matrix
T1 = transmittivity(L(ii));
% Blackbody Irradiance
I = 6.16^(15) / ( (L(ii)*1e+9)^5 * (exp(2484/(L(ii)*1e+9)) - 1) );
% final transmittivity
Tf2 = abs(1/T1(1))^2 * n3/n0;
% Power before integration
pwr(ii) = Tf2 * I;
end
end
end
I know that MATLAB works better when most or everything is vectorized. I have two set of vectors X and T. For every vector x in X I want to compute:
this is because I want to compute:
which can be easily expressed as MATLAB linear algebra operations as I wrote above with a dot product. I am hoping that I can speed this up by having those vectors, instead of computing each f(x) with a for loop. Ideally I could have it all vectorized and compute:
I've been think about this for some time now, but it doesn't seem to be a a nice way were a function takes two vectors and computes the norm between each one of them, with out me having to explicitly write the for loop.
i.e. I've implemented the trivial code:
function [ f ] = f_start( x, c, t )
% Computes f^*(x) = sum_i c_i exp( - || x_i - t_i ||^2)
% Inputs:
% x = data point (D x 1)
% c = weights (K x 1)
% t = centers (D x K)
% Outputs:
% f = f^*(x) = sum_k c_k exp( - || x - t_k ||^2)
[~, K] = size(t);
f = 0;
for k=1:K
c_k = c(k);
t_k = t(:, k);
norm_squared = norm(x - t_k, 2)^2;
f = f + c_k * exp( -1 * norm_squared );
end
end
but I was hoping there was a less naive way to do this!
I think you want pdist2 (Statistics Toolbox):
X = [1 2 3;
4 5 6];
T = [1 2 3;
1 2 4;
7 8 9];
result = pdist2(X,T);
gives
result =
0 1.0000 10.3923
5.1962 4.6904 5.1962
Equivalently, if you don't have that toolbox, use bsxfun as follows:
result = squeeze(sqrt(sum(bsxfun(#minus, X, permute(T, [3 2 1])).^2, 2)));
Another method just for kicks
X = [1 2 3;
4 5 6].';
T = [1 2 3;
1 2 4;
7 8 9].';
tT = repmat(T,[1,size(X,2)]);
tX = reshape(repmat(X,[size(T,2),1]),size(tT));
res=reshape(sqrt(sum((tT-tX).^2)).',[size(T,2),size(X,2)]).'
If I have a matrix A and I want to evaluate x' * A * x for multiple values of x, how can I vectorize this?
(I could do X' * A * X and take the diagonal, but this is clearly inefficient.)
One way to think about it is that you are trying to take a bunch of dot products between the vectors in X and the vectors in AX. Matlab has a function for that:
N = 10; % number of x's
M = 100; % length of x's
X = rand(M,N);
A = rand(M, M);
% way 1
way1 = diag(X' * A * X);
% way 2
way2 = dot(X, A*X)';
% compare
[way1 way2]
How about this?
sum((A*X).*X,1)
Or, if you are dealing with complex values,
sum((A*X).*conj(X),1)
Check:
>> A = rand(4,4);
>> X = rand(4,3);
>> sum((A*X).*X,1)
ans =
5.4755 2.6205 3.4803
>> diag(X'*A*X)
ans =
5.4755
2.6205
3.4803
This could be one approach, though not sure if this would be more efficient than the direct matrix multiplication + diag based approach -
%// Perform X'*A equivalent multiplication
mult1 = bsxfun(#times,permute(X,[1 3 2]),A)
%// Perform rest of the equivalent multiplication
mult2 = bsxfun(#times,mult1,permute(X,[3 1 2]))
%// Perform the summations required to reduce to desired output's size
out = sum(reshape(mult2,[],size(X,2)),1)
You can re-arrange the multiplications a bit -
mult1 = bsxfun(#times,permute(X,[1 3 2]),permute(X,[3 1 2]))
mult2 = bsxfun(#times,mult1,A)
out = sum(reshape(mult2,[],size(X,2)),1)
Or merge the ending bsxfun(#times and sum with a bit more simplified and maybe more efficient version -
mult1 = bsxfun(#times,permute(X,[1 3 2]),permute(X,[3 1 2]))
out = reshape(permute(mult1,[3 1 2]),size(X,2),[])*A(:)
Or simplify it further to make it a one-liner that uses minimal of tools and could be the most efficient of the lot! -
out = reshape(bsxfun(#times,X.',permute(X,[2 3 1])),[],numel(A))*A(:)
In numpy, you can do np.einsum('ij,jk,ki->i',np.transpose(X),A,X).