Java: Trigonometry and double inaccuracy causing NaN - java

I have a distance formula using latitude and longitude:
distance = EARTH_MILES_RADIUS
* Math.acos(Math.sin(lat1 / RADIAN_CONV)
* Math.sin(lat2 / RADIAN_CONV)
+ Math.cos(lat1 / RADIAN_CONV)
* Math.cos(lat2 / RADIAN_CONV)
* Math.cos((lng2 - lng1) / RADIAN_CONV));
lat1,lng1,lat2,lng2 are double primitives. They come to me as double primitives and there is nothing I can do about it.
The problem is that when I have a pair of longitude or latitudes that are the same the formula sometimes returns NaN. I believe this is because I am taking the arc cosine of a number very slightly greater than 1, when in fact it should be exactly 1. I would probably have problems if the points were antipodal as well, where they might be slightly less than -1.
How can I best fix this problem?

If you are indeed calculating great circle distance as I think you are, you should use the Vincenty formula instead of what you have.
http://en.wikipedia.org/wiki/Great-circle_distance

Check for two very close (ideally equal) values in your code. For example:
boolean doubleApproxEqual(double a, double b) {
double PRECISION = 0.000001;
if (Math.abs(a-b) < PRECISION) //not sure what the name of the function is
//cannot be bothered to check
return true;
return false;
}
if you get a True, do cos(1.0) or whatever

Simply checking for equality was enough to fix my problem:
if (lat1 == lat2 && lng1 == lng2) ...

Related

Sigmoid function return NaN in Java

I am trying to create a logistic regression algorithm in java but when I calculate the logarithm of the likelihood it is always returning NaN. My method which calculates the logarithm looks like this :
//Calculate log likelihood on given data
private double getLogLikelihood(double cat, double[] x) {
return cat * Math.log(findProbability(x))
+ (1 - cat) * Math.log(1 - findProbability(x));
}
And the findProbability method is just take an instance from the dataset and returning the sigmoid funcion result which is between 0 and 1.
//Calculate the sum of w * x for each weight and attribute
//call the sigmoid function with that s
public double findProbability(double[] x){
double s = 0;
for(int i = 0; i < this.weights.length; i++){
if(i >= x.length) break;
s += this.weights[i] * x[i];
}
return sigmoid(s);
}
private double sigmoid(double s){
return 1 / (1 + Math.exp(-s));
}
Moreover, my starting weights are :
[-0.2982955509135178, -0.4984900460081106, -1.816880187922516, -2.7325608512266073, 0.12542715714800834, 0.1516078084483485, 0.27631147403449774, 0.1371611094778011, 0.16029832096058613, 0.3117065974657231, 0.04262385176091778, 0.1948263133838624, 0.10788353525185314, 0.770608588466501, 0.2697281907888033, 0.09920694325563077, 0.003224073601703939, 0.021573742410541247, 0.21528348692817675, 0.3275511757298476, -0.1500597314893408, -0.7221692528386277, -2.062544912370121, 1.4315146889363015, 0.2522133355419722, 0.23919315019065995, 0.3200037377021523, 0.059466770771758076, 0.04012493980772944, 0.2553236501265919]
Finally, an instance from my dataset is :[M,17.99,10.38,122.8,1001,0.1184,0.2776,0.3001,0.1471,0.2419,0.07871,1.095,0.9053,8.589,153.4,0.006399,0.04904,0.05373,0.01587,0.03003,0.006193,25.38,17.33,184.6,2019,0.1622,0.6656,0.7119,0.2654,0.4601,0.1189]
I tried to initialize the starting weightss with different random numbers but thats didnt solve the problem.
The arithematic is causing a rounding error leaving you with 1.
double b = 1 + Math.exp(-3522);
b will be equal to 1, because otherwise you will need too many sig figs. You'll have to approximate the value to keep the precision. 1/(1+s) ~= 1 - s; Which means you need to calculate log(1) and log(s).
edit: sorry, I made a mistake, it appears Math.exp(-3522) is evaluated as 0 after rounding. Ill leave this answer because Math.exp(-x) might be too small to add to 1, or it might just be zero.
NaN is a result of dividing by zero or calling Math.log on a non-positive number, so u should try and find where exactly this happens. I suggest debugging or adding code to print the values of which u take the logarithm/dividy by.
EDIT: it seems it is a rounding error: exp(-s) will return a result so small that added with 1 it will still remain 1. This causes the logarithm to return -Inf. I'd suggest u try and find a mathematical way to solve this by trying to perhaps to approximate the log of the exponential.
I found a solution to my problem so I post it here:
I added an overflow check:
private double sigmoid(double s){
if(s>20){
s=20;
}else if(s<-20){
s=-20;
}
double exp = Math.exp(s);
return exp/(1+exp);
}
Also changing 1/(1+Math.exp(s) to exp/(1+exp) proved to be more stable in small disturbances of inputs.

Sine approximation error in Java

I'm a bit annoyed with a method I wrote to approximate sine function in Java. Here it is, it's based on Taylor's series.
static double PI = 3.14159265358979323846;
static double eps = 0.0000000000000000001;
static void sin(double x) {
x = x % (2 * PI);
double term = 1.0;
double res = 0.0;
for (int i = 1; term > eps; i++) {
term = term * (x / i);
if (i % 4 == 1) res += term;
if (i % 4 == 3) res -= term;
}
System.out.println(sum);
}
For little values, I got very good approximation of sine, but for large values (e.g pow(10,22)), results seems very very wrong.
Here are the results :
sin(pow(10,22)) // 0.8740280612007599
Math.sin(pow(10,22)) // -0.8522008497671888
Does someone have an idea ? Thank you !
Best regards,
Be reassured that the Java sin function will be off too.
You problem is that the Taylor expansion for sin has a small radius of convergence and convergence is slow even if you're within that radius.
There are floating point considerations too: a floating point double gives you about 15 significant figures of accuracy.
So for large arguments for sin, the accuracy will deteriorate significantly especially given that sin is a periodic function:
sin(x + 2 * pi * n) = sin(x) for any integer n.
Your answer is incorrect for big numbers because you accumulate a lot of rounding errors due to double presentation. When the number is big, then your for loop will iterate a lot before the term becomes smaller than epsilon. In each iteration, a rounding error is accumulated. The result is a very big error in the final value. Read some nice reference on "Numerical Analysis". Anyway, Tylor's series approximate sin near 0, by definition. So, it is normal not to be correct for very big numbers.
The difference actually has nothing to do with the radius of convergence of the Taylor Series and has to do with double precision not being accurate enough to hold the precision required for such big numbers. The radius of the Taylor series for the sine function is infinity.
10^22 is approximately 2^73. Since the mantissa for a double precision number is 52 bits, consecutive values that can be stored with double precision format will be 2^21 apart from each other. Since an evaluation of the sine function requires more resolution than that, you won't be able to reliably get an answer.

Calculating Cos inverse to find angle degree

So, right now I am trying to calculate the angles of a right triangle using the inverse of Cosine. However, I don't really know how to do it. I know the equation, just not how to convert into code. The equation would be:: Cos-1(A/C); However, that does not seem to work in Java. I also tried
angleX = (int) Math.acos(sideC / sideA);
If sideC and sideA were integers, one would have integer division (2 / 3 == 0).
If you do not expect a result in radians, but degrees, a conversion is needed.
As double is an approximation, use round too.
if (sideA == 0) { ... }
angleX = (int) Math.round(
Math.toDegrees(Math.acos(((double)sideC) / sideA)));

dot product of two quaternion rotations

I understand that the dot (or inner) product of two quaternions is the angle between the rotations (including the axis-rotation). This makes the dot product equal to the angle between two points on the quaternion hypersphere.
I can not, however, find how to actually compute the dot product.
Any help would be appreciated!
current code:
public static float dot(Quaternion left, Quaternion right){
float angle;
//compute
return angle;
}
Defined are Quaternion.w, Quaternion.x, Quaternion.y, and Quaternion.z.
Note: It can be assumed that the quaternions are normalised.
The dot product for quaternions is simply the standard Euclidean dot product in 4D:
dot = left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w
Then the angle your are looking for is the arccos of the dot product (note that the dot product is not the angle): acos(dot).
However, if you are looking for the relative rotation between two quaternions, say from q1 to q2, you should compute the relative quaternion q = q1^-1 * q2 and then find the rotation associated withq.
Just NOTE: acos(dot) is very not stable from numerical point of view.
as was said previos, q = q1^-1 * q2 and than angle = 2*atan2(q.vec.length(), q.w)
Should it be 2 x acos(dot) to get the angle between quaternions.
The "right way" to compute the angle between two quaternions
There is really no such thing as the angle between two quaternions, there is only the quaternion that takes one quaternion to another via multiplication. However, you can measure the total angle of rotation of that mapping transformation, by computing the difference between the two quaternions (e.g. qDiff = q1.mul(q2.inverse()), or your library might be able to compute this directly using a call like qDiff = q1.difference(q2)), and then measuring the angle about the axis of the quaternion (your quaternion library probably has a routine for this, e.g. ang = qDiff.angle()).
Note that you will probably need to fix the value, since measuring the angle about an axis doesn't necessarily give the rotation "the short way around", e.g.:
if (ang > Math.PI) {
ang -= 2.0 * Math.PI;
} else if (ang < -Math.PI) {
ang += 2.0 * Math.PI;
}
Measuring the similarity of two quaternions using the dot product
Update: See this answer instead.
I assume that in the original question, the intent of treating the quaternions as 4d vectors is to enable a simple method for measuring the similarity of two quaternions, while still keeping in mind that the quaternions represent rotations. (The actual rotation mapping from one quaternion to another is itself a quaternion, not a scalar.)
Several answers suggest using the acos of the dot product. (First thing to note: the quaternions must be unit quaternions for this to work.) However, the other answers don't take into account the "double cover issue": both q and -q represent the exact same rotation.
Both acos(q1 . q2) and acos(q1 . (-q2)) should return the same value, since q2 and -q2 represent the same rotation. However (with the exception of x == 0), acos(x) and acos(-x) do not return the same value. Therefore, on average (given random quaternions), acos(q1 . q2) will not give you what you expect half of the time, meaning that it will not give you a measure of the angle between q1 and q2, assuming that you care at all that q1 and q2 represent rotations. So even if you only plan to use the dot product or acos of the dot product as a similarity metric, to test how similar q1 and q2 are in terms of the effect they have as a rotation, the answer you get will be wrong half the time.
More specifically, if you are trying to simply treat quaternions as 4d vectors, and you compute ang = acos(q1 . q2), you will sometimes get the value of ang that you expect, and the rest of the time the value you actually wanted (taking into account the double cover issue) will be PI - acos(-q1 . q2). Which of these two values you get will randomly fluctuate between these values depending on exactly how q1 and q2 were computed!.
To solve this problem, you have to normalize the quaternions so that they are in the same "hemisphere" of the double cover space. There are several ways to do this, and to be honest I'm not even sure which of these is the "right" or optimal way. They do all produce different results from other methods in some cases. Any feedback on which of the three normalization forms above is the correct or optimal one would be greatly appreciated.
import java.util.Random;
import org.joml.Quaterniond;
import org.joml.Vector3d;
public class TestQuatNorm {
private static Random random = new Random(1);
private static Quaterniond randomQuaternion() {
return new Quaterniond(
random.nextDouble() * 2 - 1, random.nextDouble() * 2 - 1,
random.nextDouble() * 2 - 1, random.nextDouble() * 2 - 1)
.normalize();
}
public static double normalizedDot0(Quaterniond q1, Quaterniond q2) {
return Math.abs(q1.dot(q2));
}
public static double normalizedDot1(Quaterniond q1, Quaterniond q2) {
return
(q1.w >= 0.0 ? q1 : new Quaterniond(-q1.x, -q1.y, -q1.z, -q1.w))
.dot(
q2.w >= 0.0 ? q2 : new Quaterniond(-q2.x, -q2.y, -q2.z, -q2.w));
}
public static double normalizedDot2(Quaterniond q1, Quaterniond q2) {
Vector3d v1 = new Vector3d(q1.x, q1.y, q1.z);
Vector3d v2 = new Vector3d(q2.x, q2.y, q2.z);
double dot = v1.dot(v2);
Quaterniond q2n = dot >= 0.0 ? q2
: new Quaterniond(-q2.x, -q2.y, -q2.z, -q2.w);
return q1.dot(q2n);
}
public static double acos(double val) {
return Math.toDegrees(Math.acos(Math.max(-1.0, Math.min(1.0, val))));
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
var q1 = randomQuaternion();
var q2 = randomQuaternion();
double dot = q1.dot(q2);
double dot0 = normalizedDot0(q1, q2);
double dot1 = normalizedDot1(q1, q2);
double dot2 = normalizedDot2(q1, q2);
System.out.println(acos(dot) + "\t" + acos(dot0) + "\t" + acos(dot1)
+ "\t" + acos(dot2));
}
}
}
Also note that:
acos is known to not be very numerically accurate (given some worst-case inputs, up to half of the least significant digits can be wrong);
the implementation of acos is exceptionally slow in the JDK standard libraries;
acos returns NaN if its parameter is even slightly outside [-1,1], which is a common occurrence for dot products of even unit quaternions -- so you need to bound the value of the dot product to that range before calling acos. See this line in the code above:
return Math.toDegrees(Math.acos(Math.max(-1.0, Math.min(1.0, val))));
According to this cheatsheet Eq. (42), there is a more robust and accurate way of computing the angle between two vectors that replaces acos with atan2 (although note that this does not solve the double cover problem either, so you will need to use one of the above normalization forms before applying the following):
ang(q1, q2) = 2 * atan2(|q1 - q2|, |q1 + q2|)
I admit though that I don't understand this formulation, since quaternion subtraction and addition has no geometrical meaning.

Math.atan() returning input

I'm having a problem with Math.atan returning the same value as the input.
public double inchToMOA( double in, double range){
double rangeIn = 36*range;
double atan = (in / rangeIn) * -1.0;
double deg = Math.atan(atan);
double moa = deg * 60;
return moa;
}
I had this all in one line, but I broke it down into different variables to see if I could find out why it wasn't working. if in = -10 and range = 300, then atan is about -.00094. The angle should be about -.053 degrees, but math.atan is returning -.00094, the same as the input.
Is my number too small for math.atan?
Inverse tangent is described here:
http://mathworld.wolfram.com/InverseTangent.html
I don't think your argument is the problem here.
You realize, of course, that computer trig functions deal in radians rather than degrees, right?
It might just be. If you look at the strict definition of the tangent function in mathematics what you see if that tan(x) = sin(x)/cos(x) for small values of "x"
lim x->0, sin(x) = x
lim x->0, cos(x) = 1
hence, you could see that lim x->0, tan(x) -> x meaning that it's inverse, arctan, returns the value it is given. As to the numerical accuracy of Math.atan I would think that the authors had gone to great lengths to ensure it's numerical accuracy.
There's nothing wrong with Math.atan. Its value is nearly 1:1 linear, intersecting the origin, for inputs close to zero. So the closer you are to zero the less change from the input there will be.

Categories