This post is not a repost, which is related solely to mpmath. But this is mainly related to the comparison between different dot product algorithms.
I have coded an accurate dot product in python in order to compute ill-conditioned dot products.
x = np.array([32888447.473062776 ,-254.18174014817862 ,-0.027520952868535176 ,0.0 ,-63157.14459198614 ,6.547216534966907 ,0.0 ,-10637698.845199142])
y = np.array([3900984.764412485 ,603797189919.5646 ,-116303296140.4418 ,0.0 ,-202.0980635978269 ,-6.596018188968733 ,0.0 ,-2366458.6427612416])
To check the validity of the algorithm, I compare it with a naive dot product, and the exact answer (DotExact) which is computed by mpmath library.
The problem is that, depending on how the DotExact is coded, it can agree with the accurate dot product until ~16 digits, or disagrees with accurate dot product and perform the same as the naive case. The Dot Exact is computed as
mp.dps = 64
def DotExact(x,y):
n=np.int64(x.shape[0])
sum = mp.mpf(0.)
for i in range(n):
sum += mp.mpf(x[i]) * mp.mpf(y[i]) # 1ST this agrees with Dot2K
# sum += mp.mpf(str(x[i])) * mp.mpf(str(y[i])) # 2ND this does NOT agree with
return sum
We expect the naive dot product to be wrong, but does anyone here know why the accurate product algorithm only agrees with the Exact summation performed by 1st, but not 2nd? For your info, I also provide here the results of the dot products based on DotExact_1st
Condition number = 19.765763843548616
Dot Exact by Mpmath = 0.7289845395392482978061485937582
Naive Dot = 0.73046875 agree digits at -2.6912228296231142978105880502362
Accurate Dot = 0.7289845395392482 agree digits at -16.13278356042518496483357616017
Edit1:
Here is the algorithm for the accurate dot product.
import pyfma
def TwoProdFMA(a,b):
x = a*b
y = pyfma.fma(a, b, -x)
# y = 0
return x,y
def TwoSumP(a,b):
pi = (a + b);
z = (pi - a);
q = (a - (pi-z) + (b-z));
return pi, q
def Dot2s_f64(x,y):
# accurate dot product
N = x.shape[0]
p,s = TwoProdFMA(x[0],y[0]);
for i in range(1,N):
h, r = TwoProdFMA(x[i],y[i]);
p, q = TwoSumP(p,h);
s += (q+r);
return p+s