Find Intersection Point between Two Gaussians
Hi there,
I'm trying to find the intersection point between two Gaussians. To do this I'm using the method described here and here where such a point can be deduced via solving a quadratic equation whose coefficients are defined in terms of the mean and standard deviation of each Gaussian. I coded this in but I'm not getting the expected answers.
Here's the code that I'm using:
Variable mu1,mu2,SD1,SD2 //mu1 and mu2 are the means of Gaussian 1 and 2 respectively
//SD1 and SD2 are the standard deviations of Gaussian 1 and 2 respectively
Variable A = -1/(2*SD1^2) + 1/(2*SD2^2)
Variable B = -mu2/(SD2^2) + mu1/(SD1^2)
Variable C = -mu1^2 /(2*SD1^2) + mu2^2 / (2*SD2^2) + log(SD2/SD1)
Make/O/N=3 coefWave = {A,B,C}
Wave coefWave
Variable x = poly(coefWave,x)
FindRoots/P=coefWave
print A,B,C
print x
Wave W_polyRoots
print W_polyRoots
End
I've played around with the poly and FindRoots operations but I'm still not getting the expected answer. When I use mu1 = 2.5 , mu2 = 5.0 , SD1 = 1.0 and SD2 = 1.0 as my input parameters (same values used in the linked references), I find that the resulting value is x=0 as the intersection point, however, I'm supposed to be getting a value of x = 3.75
Any ideas?
Thanks!
(deleted)
....just realised my response was not correct!
September 17, 2019 at 11:29 pm - Permalink
Try this as a starter:
Variable mu1,mu2,SD1,SD2 //mu1 and mu2 are the means of Gaussian 1 and 2 respectively
//SD1 and SD2 are the standard deviations of Gaussian 1 and 2 respectively
Variable A = -1/(2*SD1^2) + 1/(2*SD2^2)
Variable B = -mu2/(SD2^2) + mu1/(SD1^2)
Variable C = -mu1^2 /(2*SD1^2) + mu2^2 / (2*SD2^2) + ln(SD2/SD1)
Make/O/N=3 coefWave = {C,B,A}
Wave coefWave
make/O/D/N=201 wY
SetScale/P x -100,1,"", wY
wY = poly(coefWave,x)
FindLevel wY,0
print A,B,C
print V_LevelX
End
I don't know whether the log should be base e or base 10 - I changed it to the natural log in the code above.
When I run this with intGauss(2.5,5,1,1) I get 3.75.
Hope this helps.
Kurt
September 18, 2019 at 03:00 am - Permalink
In reply to Try this as a starter: … by KurtB
Sorry, I tried again with Find Roots - simply reversing the coefficients appears to do the trick:
Variable mu1,mu2,SD1,SD2 //mu1 and mu2 are the means of Gaussian 1 and 2 respectively
//SD1 and SD2 are the standard deviations of Gaussian 1 and 2 respectively
Variable A = -1/(2*SD1^2) + 1/(2*SD2^2)
Variable B = -mu2/(SD2^2) + mu1/(SD1^2)
Variable C = -mu1^2 /(2*SD1^2) + mu2^2 / (2*SD2^2) + log(SD2/SD1)
Make/O/N=3 coefWave = {C,B,A}
Wave coefWave
FindRoots /P=coefWave
print A,B,C
Wave W_polyRoots
print W_polyRoots
End
Running this with intGauss(2.5,5,1,1) gives the output W_polyRoots[0]= {cmplx(3.75,0)}.
Do check on the log base though before you use this in a real application!
Regards,
Kurt
September 18, 2019 at 03:50 am - Permalink
You need to reverse the order of the coefficients in coefWave
(in the code in the original post - I didn't notice that Kurt's replies had already corrected that!)
September 20, 2019 at 04:24 am - Permalink
I suggest that a completely analytic solution is possible if the baselines are the same: ln(Gaussian1)=ln(Gaussian2) produces a quadratic equation in 'x'. Why not solve it directly?
Vmmr5596 seems to be implying that the amplitudes and base lines are the same, and uses an unfortunate example with equal standard deviations. This gives a simple solution where the intersection is the average of the means, (mu1+mu2)/2 = 3.75, a degenerate double-valued root.
The quadratic method also allows for unequal Gaussian amplitudes, means, and standard deviations, but the general case may cause the roots to be complex (no real-valued intersection, easily testable), or have two real values (sometimes degenerate).
September 21, 2019 at 04:49 am - Permalink
@s.r.chinn
surely FindRoots /P=coefWave does implement the analytic solution for a quadratic?
September 22, 2019 at 12:40 pm - Permalink
@tony:
I don't know what WaveMetrics does under the hood here. FindRoots should also have tolerance flag to ensure that precision requirements are maintained (done implicitly if the standard quadratic eqn solution with default DP variables is used).
@others:
The log() in above posts should be interpreted as IP natural logarithm ln(), since ln's of an exponential are used in the underlying derivation. I don't know the cribbed Python syntax for log().
The poster probably should have mentioned up front that the Normal function ( a special case of a Gaussian) is used.
Note that two solutions will be possible. You might want to add a search range to FindRoots if you know which single root is desired. Picture a tall skinny peak vs a laterally displaced shallow peak.The choice of computation method is entirely up to the user. My personal preference is the algebraic method.
September 23, 2019 at 05:13 am - Permalink
@tony For a taste of how the quadratic formula can go wrong, see this: https://people.eecs.berkeley.edu/~wkahan/Qdrtcs.pdf
I'm checking the source to see if there's a test to use the quadratic formula; I don't think there is.
September 23, 2019 at 10:48 am - Permalink
So is findroots using this surprisingly complicated formula, just in case the quadratic is ill-conditioned? That would explain why it's 10 times slower than using the high-school version of the formula to calculate real roots (with a check for degeneracy). I thought that maybe it was extra overhead of allocating all the variables and wave, or maybe that findroots really was using a numerical algorithm.
September 23, 2019 at 11:24 am - Permalink
I don't know why it would be slow- a check of the source shows that the code I'm using (which isn't mine :) is using a careful implementation of the quadratic formula for degree 2. The code goes the route of finding the better-conditioned root using the quadratic formula and then using C/A to get the other root. And there is a comment, "COMPUTE DISCRIMINANT AVOIDING OVERFLOW". Clearly, this used to be FORTRAN code :)
September 23, 2019 at 11:45 am - Permalink
What about using the analytical equation for the derivative of the Gaussian and finding the point(s) where that curve is disjoint in its slope? Would this be more robust and/or faster?
Can a simple algebra operation be done in FFT space?
September 23, 2019 at 02:10 pm - Permalink
In reply to I don't know why it would be… by johnweeks
then the speed difference has to be the cost of doing it right!
Variable mu1,mu2,SD1,SD2 //mu1 and mu2 are the means of Gaussian 1 and 2 respectively
//SD1 and SD2 are the standard deviations of Gaussian 1 and 2 respectively
Variable A = -1/(2*SD1^2) + 1/(2*SD2^2)
Variable B = -mu2/(SD2^2) + mu1/(SD1^2)
Variable C = -mu1^2 /(2*SD1^2) + mu2^2 / (2*SD2^2) + log(SD2/SD1)
Make/free/N=3 coefWave = {C,B,A}
variable t1, t2
t1=startMSTimer
FindRoots /P=coefWave
t1=stopMSTimer(t1)
t2=startMSTimer
variable root1, root2
// naively use simple 'high school' quadratic root equation without
// worrying about rounding errors when roots are uncomfortably close
// to one another
if(a==0) // not a quadratic - only one root
root1=-c/b
root2=root1
else
root1=(-b-sqrt(b^2-4*a*c))/(2*a)
root2=(-b+sqrt(b^2-4*a*c))/(2*a)
endif
t2=stopMSTimer(t2)
Wave W_polyRoots
print W_polyRoots, root1, root2
print t1/t2
End
September 24, 2019 at 06:04 am - Permalink
I just looked again at the code. It is originally FORTRAN that was run through f2c sometime probably in the previous millennium. The f2c-generated code appears to be grossly inefficient. I will look into doing something about that.
September 24, 2019 at 01:19 pm - Permalink