Consider the following polynomial:
$$ p(x) = x^3 + (C_b+K_a)x^2 - (C_aK_a + K_w)x - K_aK_w $$
Where:
- $x, C_a, C_b$ are concentrations, positive real numbers typically within $[10^{-7};1]$. The unknown $x$ is the proton concentration and $C_a, C_b$ are supposed to be constant;
- $K_a$ is acid/base dissociation constant, typically within $[10^{-12},10^{-2}]$;
- $K_w$ is the water ionic product constant which is about $10^{-14}$.
I expect this polynomial to have at least a positive real solution because proton concentration physically exist.
My goal is to find the real positive solution to the equation $p(x)=0$ using some numerical method (Newton, etc. or even Cardano formulae), but I face some normalization problem because of different magnitudes between constants or monomials.
For a typical setup:
$$K_a = 10^{-4.75},\quad C_a = 0.1,\quad C_b = 0.2$$
Polynomial coefficients widely varies in magnitude:
$$ a_3=1.0000,\quad a_2=0.2000,\quad a_1=-1.7792\cdot 10^{-11},\quad a_0=-1.7783\cdot 10^{-24} $$
The discriminant of this polynomial for this setup is about $\Delta = 5.6905\cdot 10^{-26}$ which is really small, it could be anything: zero, positive or negative, who knows?
I have used both float and fixed decimal (with 40 decimals) arithmetics to compute those quantities. They slightly differ with the arithmetic kind, but the scaling problem remains. I think my problem is about numerical stability and normalization.
Wolfram seems to be able to solve it when I use symbolic values (answer is correct) instead of coefficients decimal development (answer is too small).
I am not asking for a complete solution, rather for some hints or references to a method for roots finding for this kind of ill-scaled polynomials. So my questions are:
- How can I accurately solve this kind of polynomial with a numerical method?
- What kind of normalization must I apply before solving it?
- Is there a specific numerical method for this class of problem?
Nota
The polynomial $p(x)$ comes from the definition of the acid/base equilibrium constant:
$$ K_a = x\frac{b}{a}, \quad K_w = xy $$
Where matter amount and charge balances have been injected:
$$ C_a + C_b = a + b,\quad b + y = x + C_b $$
Update
I have managed to find a credible root using numpy.roots and decimal which complies with Wolfram Alpha. The following Python 3 code:
import numpy as np
import decimal
Ka = decimal.Decimal("10")**decimal.Decimal("-4.75")
Kw = decimal.Decimal("1e-14")
Ca = decimal.Decimal("0.1")
Cb = decimal.Decimal("0.2")
coefs = [decimal.Decimal(1), (Cb+Ka), -(Ca*Ka + Kw), -Ka*Kw]
r0 = np.roots(coefs)
Returns:
array([-2.00026673e-01, 8.89021156e-06, -9.99999983e-14])
I am still interested to know if there are other care to take in order to properly solve this kind of problems.
QBCsolver, using a C++ code that usesdoublearithmetic enhanced by the use of FMA (fused multiply-add):-2.0000000008896002e-1, -9.9837370721755525e-14, 8.9059837331107879e-11– njuffa Jul 28 '18 at 19:40