Solving mathematical functions

I’ve been developing an app that will gather user input and ultimately calculate the value of Xo. I need swift to mutate values of Xo until my function F(Xo) = 0, with a tolerance of 0.00001. Both equations contain Xo and is setup like this: F(Xo) = Equation1 - Equation2 = 0. I’m fairly new to programming in swift and any help will be much appreciated.

Thanks,

The name for this type of problem, which has a multi-hundred-year(!) research history, is root finding. The full history is out of scope for the Swift forums, but the Wikipedia page is a pretty good start, and has good references. Here are a few highlights to consider:

If your function doesn't have special structure, but is differentiable, Newton's Method (and variants thereof) is the usual approach. If you'd like to learn about differentiable programming, and your problem falls into this class, experimenting with the Swift for TensorFlow branch may be interesting to you.

The secant method doesn't converge as quickly, but is easy to implement and does not require a derivative. It does have some other limitations, though.

If your function is continuous and has a single root in an interval, you can use bisection to find it. This is basically the most general method (i.e. has the fewest requirements), but it also converges only linearly.

2 Likes

The method I’d like to pursue is the Newton-Raphson convergence technique. My function is differentiable and Xo is my only variable. I’m just not sure how to write the code for this. I will begin researching TensorFlow.

Thank you

The basic iteration looks like this:

while abs(f(x)) > tolerance {
  x -= f(x) / df(x)
}

You should be aware that Newton’s method can fail to converge, even for a smooth, monotonic function with a single root, if the initial value is poorly chosen. For example, with arctan(x), an initial x-value of approximately 1.4 or greater will not converge.

Also, roots of order 2 or higher are slow to converge unless the algorithm is adjusted.

And finally, if your function is of the form f(x) = g(x) - h(x), and both g and h have large magnitude, then numerical precision could prevent the result from ever being within the tolerance of zero. (A similar issue applies to large-magnitude x-values.)

1 Like

You might be interested to try my library Numerical for this. Among other useful numerical methods it has root finding. It's setup as a Swift Package Manager package.

Let's take an example where you want to know the cube root of 30. In this case x^3 = 30, so the function you want to find a root of is f(x) = x * x * x - 30. In root finding it's useful to have a starting guess, so let's say your guess is 3.0.

If you know how to write your function but not its first derivative you may write:

let a = root(guess: 3.0) { x in x * x * x - 30 }

This uses Brent's method by default but you can specify secant or one of a few other methods if you know that is a better fit.

In this example we also know that the first derivative of f(x) is f'(x) = 3 * x * x. Now we can use Newton's method like so:

let b = root(guess: 3.0,
             f: { x in x * x * x - 30 }, 
             f1: { x in 3 * x * x })

We can also take it one step further in this example because we also know that the second derivative of our function is f''(x) = 6 * x. This lets us use Halley's method like so:

let c = rootSecondOrder(guess: 3.0,
                        f: { x in x * x * x - 30 },
                        f1: { x in 3 * x * x },
                        f2: { x in 6 * x })

There are a few other options in there as well allowing you to specify a one or two sided interval to restrict your search, a maximum number of iterations you can afford, and a tolerance you can accept.

If you try it out let me know if you have questions.

My two equations are:
w((cosh(Xo/w)-1) = Y + ((Xo-Xt)*(sinh(Xo/w)))

Everything is known except for Xo. Earlier it was setup to prepare for the Newton method, but I’m open to suggestions.

I haven’t tried yet, but you can probably solve that explicitly using the Lambert W function.

Not sure if that actually simplifies the computation though.

I suspect Nevin is right and you could take this further analytically. A tool like Wolfram Alpha might be able to help with that.

If you do want to attack it as a root finding problem, though, that's easy enough. You just subtract so that you are setting equal to zero: f(x) = Y + ((Xo-Xt)*(sinh(Xo/w))) - w((cosh(Xo/w)-1). Then the solution (for some arbitrarily chosen constant values) looks like:

let w = 3.0
let Y = 5.0
let Xt = 10.0
let r = root(guess: 2.0) { (Xo: Double) in 
    Y + (Xo - Xt) * (sinh(Xo / w)) - w * (cosh(Xo/w) - 1.0)
}
print(r) // 1.5587189470591787

I'd caution that attacking this equation with root finding is likely sensitive to your starting guess. It may have more than one (or fewer than one!) root depending on the constant values and it grows rapidly on both sides. Taking the derivatives wouldn't be too bad (again Wolfram Alpha or other tools could help) and could help with the second problem.