Numerically stable evaluation of sqrt(x+a) - sqrt(x) - math

Is there an elegant way of numerically stable evaluating the following expression for the full parameter range x,a >= 0?
f(x,a) = sqrt(x+a) - sqrt(x)
Also is there any programming language or library that does provide this kind of function? If yes, under what name? I have no specific problem using the above expression right now, but encountered it many times in the past and always thought that this problem must have been solved before!

Yes, there is! Provided that at least one of x and a is positive, you can use:
f(x, a) = a / (sqrt(x + a) + sqrt(x))
which is perfectly numerically stable, but hardly worth a library function in its own right. Of course, when x = a = 0, the result should be 0.
Explanation: sqrt(x + a) - sqrt(x) is equal to (sqrt(x + a) - sqrt(x)) * (sqrt(x + a) + sqrt(x)) / (sqrt(x + a) + sqrt(x)). Now multiply the first two terms to get sqrt(x+a)^2 - sqrt(x)^2, which simplifies to a.
Here's an example demonstrating the stability: the troublesome case for the original expression is where x + a and x are very close in value (or equivalently when a is much smaller in magnitude than x). For example, if x = 1 and a is small, we know from a Taylor expansion around 1 that sqrt(1 + a) should be 1 + a/2 - a^2/8 + O(a^3), so sqrt(1 + a) - sqrt(1) should be close to a/2 - a^2/8. Let's try that for a particular choice of small a. Here's the original function (written in Python, in this case, but you can treat it as pseudocode):
def f(x, a):
return sqrt(x + a) - sqrt(x)
and here's the stable version:
def g(x, a):
if a == 0:
return 0.0
else:
return a / ((sqrt(x + a) + sqrt(x))
Now let's see what we get with x = 1 and a = 2e-10:
>>> a = 2e-10
>>> f(1, a)
1.000000082740371e-10
>>> g(1, a)
9.999999999500001e-11
The value we should have got is (up to machine accuracy): a/2 - a^2/8 - for this particular a, the cubic and higher order terms are insignificant in the context of IEEE 754 double-precision floats, which only provide around 16 decimal digits of precision. Let's compute that value for comparison:
>>> a/2 - a**2/8
9.999999999500001e-11

Related

Why is my approximation of the Gamma function not exact?

So I'm setting out to recreating some math functions from the math library from python.
One of those functions is the math.gamma-function. Since I know my way around JavaScript I thought I might try to translate the JavaScript implementation of the Lanczos approximation on Rosetta code into Applescript code:
on gamma(x)
set p to {1.0, 676.520368121885, -1259.139216722403, 771.323428777653, -176.615029162141, 12.507343278687, -0.138571095266, 9.98436957801957E-6, 1.50563273514931E-7}
set E to 2.718281828459045
set g to 7
if x < 0.5 then
return pi / (sin(pi * x) * (gamma(1 - x)))
end if
set x to x - 1
set a to item 1 of p
set t to x + g + 0.5
repeat with i from 2 to count of p
set a to a + ((item i of p) / (x + i))
end repeat
return ((2 * pi) ^ 0.5) * (t ^ x + 0.5) * (E ^ -t) * a
end gamma
The required function for this to run is:
on sin(x)
return (do shell script "python3 -c 'import math; print(math.sin(" & x & "))'") as number
end sin
All the other functions of the Javascript implementation have been removed to not have too many required functions, but the inline operations I introduced produce the same result.
This Javascript-code works great when trying to run it in the browser-console, but my Applescript implementation doesn't produce answers anywhere near the actual result. Is it because...
...I implemented something wrong?
...Applescript doesn't have enough precision?
...something else?
You made two mistakes in your code:
First of all, the i in your repeat statement starts at 2 rather than 1, which is fine for (item i of p), but it needs to be subtracted by 1 in the (x + i).
Secondly, in the code (t ^ x + 0.5) in the return statement, the t and x are being calculated first since they are exponents and then added to 0.5, but according to the JavaScript implementation the x and 0.5 need to be added together first instead.

sympy: rootfinding of parameterized function in range

I'm having trouble to find the root of a parameterized quintic polynome. Background is: I want to find the parameter s_f such that for any given parameter d_f, the curvature of the polynomial is smaller than a threshold (yeah .. sounds complex, but the math is rather straight);
# define quintic polynomial (jerk-minimized trajectory)
# see http://courses.shadmehrlab.org/Shortcourse/minimumjerk.pdf
s = symbols('s', real=True, positive=True)
s_f = symbols('s_f', real=True, positive=True, nonzero=True)
d_0 = 0
d_f = symbols('d_f', real=True, positive=True, nonzero=True)
d_of_s = d_0 + (d_f - d_0) * ( 10*(s/s_f)**3 - 15*(s/s_f)**4 + 6*(s/s_f)**5 )
display(d_of_s)
# define curvature of d_of_s
# see https://en.wikipedia.org/wiki/Curvature#In_terms_of_a_general_parametrization
y = d_of_s
dy = diff(d_of_s, s)
ddy = diff(dy, s)
x = s
dx = diff(x, s) # evaluates to 1
ddx = diff(dx, s) # evaluates to 0
k = (dx*ddy - dy*ddx) / ((dx*dx + dy*dy)**Rational(3,2))
# the goal is to find s_f for any given d_f, such that k(s) < some_threshold
# strategy: find the roots of the derivative of k in the range of sāˆˆ[0, s_f]
dk = diff(k, s)
dk = simplify(dk)
display(dk)
# now solve
res = solveset(dk, s, Interval(0, s_f).intersection(S.Reals))
display(res)
The function dk(s, d_f, s_f) has two root in the interval sāˆˆ[0, s_f], however solveset returns this:
ConditionSet(s, Eq(5400*d_f**2*s**4*(-s**2 + 2*s*s_f - s_f**2)*(2*s**2 - 3*s*s_f + s_f**2)**2 + (900*d_f**2*s**4*(s**2 - 2*s*s_f + s_f**2)**2 + s_f**10)*(6*s**2 - 6*s*s_f + s_f**2), 0), Interval(0, s_f))
.. which is afaik equivalent to: I can't solve this, we got infinite number of results. Well, this is true for the function in general. limit(dk, s, -oo) and limit(dk, s, +oo) is zero. But since I stated the domain interval, why am I not getting the two roots I'm expecting? I'd also expect to get a more granular result:
set containing the roots for s < 0;
set containing the roots for s > s_f
the two roots when sāˆˆ[0, s_f]
I started with solve() and a lot of different assumptions on my symbols. I get different results for different assumptions, but no combination seems to yield what I need. When I state no assumptions, I get back a set with a huge condition and 8 roots, that don't seem real or correct. In general, the constraints are:
- all symbols are real
- s_f > 0
- d_f > 0
- s āˆˆ [0, s_f] (domain range .. the polynomial is only evaluated in this interval)
I guess the problem is that I'm not setting up my solveset correctly:
how to specify that s_f and d_f are real? afaik the symbol
assumptions are ignored when using solveset?
how to specify intervals
and assumptions on other multivariate functions, i.e. other symbols
than the domain one?
This is what d_of_s look like for s_f = 1, d_f = 1
And this is what dk(s) looks like (I plotted outside the domain range to visualize the problem).
Substitute in the known values of d_f and s_f and use real_roots to find the real roots of the numerator of dk. Keep the ones that have a value in the range of interest:
>>> s_fi = 3
>>> [i for i in real_roots(dk.subs(d_f,2).subs(s_f, s_fi).as_numer_denom()[0])
... if 0 <= i.n(2) <= s_fi]
[CRootOf(800*s**10 - 12000*s**9 + 74000*s**8 - 240000*s**7 + 432000*s**6 - 410400*s**5
+ 162000*s**4 - 4374*s**2 + 13122*s - 6561, 1), CRootOf(800*s**10 - 12000*s**9 +
74000*s**8 - 240000*s**7 + 432000*s**6 - 410400*s**5 + 162000*s**4 - 4374*s**2 +
13122*s - 6561, 2)]
I kept the CRootOf instances because they can be computed to arbitrary precision, e.g. to 3 digits:
>>> [i.n(3) for i in _]
[0.433, 2.57]

solving equations and using values obtained in other calculations - SAGE

the function solve() in SAGE returns symbolic values for the variables i solve the equations for. for e.g:
sage: s=solve(eqn,y)
sage: s
[y == -1/2*(sqrt(-596*x^8 - 168*x^7 - 67*x^6 + 240*x^5 + 144*x^4 - 60*x - 4) + 8*x^4 + 11*x^3 + 12*x^2)/(15*x + 1), y == 1/2*(sqrt(-596*x^8 - 168*x^7 - 67*x^6 + 240*x^5 + 144*x^4 - 60*x - 4) - 8*x^4 - 11*x^3 - 12*x^2)/(15*x + 1)]
My problem is that i need to use the values obtained for y in other calculations, but I cannot assign these values to any other variable. Could someone please help me with this?
(1) You should visit ask.sagemath.org, the Stack Overflow-like forum for Sage users, experts, and developers! </plug>
(2) If you want to use the values of a solve() call in something, then it's probably easiest to use the solution_dict flag:
sage: x,y = var("x, y")
sage: eqn = x**4+5*x*y+3*x-y==17
sage: solve(eqn,y)
[y == -(x^4 + 3*x - 17)/(5*x - 1)]
sage: solve(eqn,y,solution_dict=True)
[{y: -(x^4 + 3*x - 17)/(5*x - 1)}]
This option gives the solutions as a list of dictionaries instead of a list of equations. We can access the results like we would any other dictionary:
sage: sols = solve(eqn,y,solution_dict=True)
sage: sols[0][y]
-(x^4 + 3*x - 17)/(5*x - 1)
and then we can assign that to something else if we like:
sage: z = sols[0][y]
sage: z
-(x^4 + 3*x - 17)/(5*x - 1)
and substitute:
sage: eqn2 = y*(5*x-1)
sage: eqn2.subs(y=z)
-x^4 - 3*x + 17
et cetera. While IMHO the above is more convenient, you could also access the same results without solution_dict via .rhs():
sage: solve(eqn,y)[0].rhs()
-(x^4 + 3*x - 17)/(5*x - 1)
If you have unknown number of variables you can use **kwargs to pass data calculated with solve to next expressions. Here's example:
B_set is an list of variables and its filled in runtime so i dont know
names of variables and their quantity at the time of writing the code
solution = solve(system_of_equations, B_set)[0]
pretty_print(solution)
For example this gives me:
result of solving system of equations
I cant use this answer for further calculations so lets convert it to usable form
solution = {str(elem.lhs()): elem.rhs() for elem in solution}
Which gives us:
result converted to dictionary with string keys
Then we just pass this as **kwargs
approximation = approximation_function(**solution)
pretty_print(approximation)
And that converts this:
approximation without values from solution
Into this:
approximation with values from solution
Note: if you use dictionary output from solve() you still need to convert keys to string.

equivalent expressions

I'm trying to figure out an equivalent expressions of the following equations using bitwise, addition, and/or subtraction operators. I know there's suppose to be an answer (which furthermore generalizes to work for any modulus 2^a-1, where a is a power of 2), but for some reason I can't seem to figure out what the relation is.
Initial expressions:
x = n % (2^32-1);
c = (int)n / (2^32-1); // ints are 32-bit, but x, c, and n may have a greater number of bits
My procedure for the first expression was to take the modulo of 2^32, then try to make up the difference between the two modulo's. I'm having trouble on this second part.
x = n & 0xFFFFFFFF + difference // how do I calculate difference?
I know that the difference n%(2^32)-n%(2^32-1) is periodic (with a period of 2^32*(2^32-1)), and there's a "spike up' starting at multiples of 2^32-1 and ending at 2^32. After each 2^32 multiple, the difference plot decreases by 1 (hopefully my descriptions make sense)
Similarly, the second expression could be calculated in a similar fashion:
c = n >> 32 + makeup // how do I calculate makeup?
I think makeup steadily increases by 1 at multiples of 2^32-1 (and decreases by 1 at multiples of 2^32), though I'm having troubles expressing this idea in terms of the available operators.
You can use these identities:
n mod (x - 1) = (((n div x) mod (x - 1)) + ((n mod x) mod (x - 1))) mod (x - 1)
n div (x - 1) = (n div x) + (((n div x) + (n mod x)) div (x - 1))
First comes from (ab+c) mod d = ((a mod d) (b mod d) + (c mod d)) mod d.
Second comes from expanding n = ax + b = a(x-1) + a + b, while dividing by x-1.
I think I've figured out the answer to my question:
Compute c first, then use the results to compute x. Assumes that the comparison returns 1 for true, 0 for false. Also, the shifts are all logical shifts.
c = (n>>32) + ((t & 0xFFFFFFFF) >= (0xFFFFFFFF - (n>>32)))
x = (0xFFFFFFFE - (n & 0xFFFFFFFF) - ((c - (n>>32))<<32)-c) & 0xFFFFFFFF
edit: changed x (only need to keep lower 32 bits, rest is "junk")

WolframAlpha: Solve Multiple Functions

I'm trying to use WolframAlpha to solve for a variable.
I have
u(k, r) = (900-3k)r^(k-1)
and
s(n, r) = sum u(k, r), k=1 to n
and I want to solve for r with
s(5000, r) = -600000000000
I've tried various incantations, but can't seem to get it working. I can't even get s defined to evaluate it.
If you care, it is to solve this problem : http://projecteuler.net/index.php?section=problems&id=235
WARNING: Spoiler below!
You should ask WA to FullSimplify the expression of s(n,r) after you substitute u(k,r) into it. It should give
(3 (299 - 300 r + r^n (-299 + n + 300 r - n r)))/(-1 + r)^2
Solving the final equality is then just finding the root of a (high degree) polynomial:
299 + 200000000000 (-1 + r)^2 + (4701 - 4700 r) r^5000 == 300 r
where r != 1 since that was a pole of the original expression. Note that r must be positive so that the positive quadratic gets negated by the high-degree term. Plotting the function shows that It is positive for r < 1, and negative for r >~ 1, so the solution is somewhere past r=1. Now change variables so that x = r-1 and look near x=0:
200000000000 x^2 + (1 + x)^5000 (1 - 4700 x) - 1 - 300 x == 0
This should be enlightnening:
Plot[200000000000 x^2 + (1 + x)^5000 (1 - 4700 x) - 1 - 300 x, {x, 0, 0.003}]
Using FindRoot with a good guess gives x=0.002322108633 or r=1.002322108633.
The WA commands follow.
First I used
FullSimplify[Sum[(900-3k)r^(k-1),{k,1,n]]
Then you would have to retype the expression it spits out:
Plot[(3 (299 - 300 r + r^5000 (-299 + 5000 + 300 r - 5000 r)))/(-1 + r)^2 + 6000000000,{r,-2,2}]
At this point I manually replaced r with x+1:
Plot[200000000000 x^2 + (1 + x)^5000 (1 - 4700 x) - 1 - 300 x, {x, 0, 0.003}]
And solving for the root:
FindRoot[200000000000 x^2 + (1 + x)^5000 (1 - 4700 x) - 1 - 300 x, {x, 0.0023}]
Which doesn't give enough precision, and this is as far as you can go using only WA. You can try to subtract off the first few digits that WA gives you, and do another substitution with y = x + 0.00232211 to get the next few digits, but that is too tedious for me to try.

Resources