1

I'm trying to make a dot product of an expression and it was supposed to be symmetric.

It turns out that it just isn't.

B is a 4D array which I must transpose its last two dimensions to become B^t.

D is a 2D array. (It's an expression of the Stiffness Matrix known to the Finite Element Method programmers)

The numpy.dotproduct associated with numpy.transpose and as a second alternative numpy.einsum (the idea came from this topic: Numpy Matrix Multiplication U*B*U.T Results in Non-symmetric Matrix) have already been used and the problem persists.

By the end of the calculations the product B^tDB is obtained and when it's verified if it really is symmetric by subtracting its transpose B^tDB, there is still a residue.

The Dot product or the Einstein Summation are used only over the dimensions of interest (last ones).

The question is: How can these residues be eliminated?

  • It would be easier to help you if you provided a [minimal, complete and verifiable example](https://stackoverflow.com/help/mcve). You say "there is still a residue", but you don't give the magnitude of the residue, the sizes of the arrays, etc. With floating point calculations, you can't expect exact results, so the residue might be within the expected tolerances of floating point calculations. – Warren Weckesser Apr 17 '18 at 23:30
  • short answer: use arbitrary precision math – tel Apr 18 '18 at 02:37
  • Another short answer: if it's *close* to symmetric, but off by machine precision, then just force it, `x = (x + x.T) / 2`, and be done with it. But do check this carefully, if the values are very small or very large, then very bad things can happen… – Ahmed Fasih Apr 18 '18 at 04:44
  • The last two dimensions of **B** is (3x18) and the dimension of **D** is (3x3). The magnitude of the residues variates from 1E-6 to 1E-9. My point being: if the calculations are meant to be symmetric, a multiplication of variables with floating points like x*y must be equal to y*x. And this is what I can't see when **B**^t**DB** is calculated... – Bernardo Opolski Apr 18 '18 at 12:15

1 Answers1

1

You need to use arbitrary precision floating point math. Here's how you can combine numpy and the mpmath package to define an arbitrary precision version of matrix multiplication (ie the np.dot method):

from mpmath import mp, mpf
import numpy as np

# stands for "decimal places". Larger values 
# mean higher precision, but slower computation
mp.dps = 75

def tompf(arr):
    """Convert any numpy array to one of arbitrary precision mpmath.mpf floats
    """
    if arr.size and not isinstance(arr.flat[0], mpf):
        return np.array([mpf(x) for x in arr.flat]).reshape(*arr.shape)
    else:
        return arr

def dotmpf(arr0, arr1):
    """An arbitrary precision version of np.dot
    """
    return tompf(arr0).dot(tompf(arr1))

As an example, if you then set up B, B^t, and D matrices as so:

bshape = (8,8,8,8)
dshape = (8,8)

B = np.random.rand(*bshape)
BT = np.swapaxes(B, -2, -1)

d = np.random.rand(*dshape)
D = d.dot(d.T)

then B^tDB - (B^tDB)^t will always have a non-zero value if you calculate it using the standard matrix multiplication method from numpy:

M = np.dot(np.dot(B, D), BT)
np.sum(M - M.T)

but if you use the arbitrary precision version given above it won't have a residue:

M = dotmpf(dotmpf(B, D), BT)
np.sum(M - M.T)

Watch out though. Calculations using arbitrary precision math run much slower than those done using standard floating point numbers.

tel
  • 13,005
  • 2
  • 44
  • 62
  • First of all: thanks for your help. This is a way of cutting through the problem, but not the reason which causes it... You've written that it will always return a residue when the numpy routines are used. But as I've responded on the other comment, when a calculation is done on the CPU, two variables with floating points multiplied like x*y should be the same as y*x, right? That's why I don't understand when the operation **B** ^t **DB** returns a non-symmetric array. – Bernardo Opolski Apr 18 '18 at 12:20
  • @BernardoOpolski That's not the question you originally asked. You should start a separate question and ask about that. – tel Apr 19 '18 at 07:41
  • 1
    @BernardoOpolski the short answer is that although `xy==yx` is guaranteed to be true in floating point arithmetic, `xyz==zyx` is not, in general, true. Multiplying three matrices can be thought of as a sum of such `xyz` terms. [This paper](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) is a good intro to the weird intricacies of floats. – tel Apr 19 '18 at 07:47
  • you're right. I must say that despite the fact that it in fact is not the question I originally asked, it sure answers my question. The article you sent is very enlightening. I thought that the problem was in some routine implemented by `numpy`. Nevertheless, based on the article it probably is a floating-point problem. According to the article and your comment, the second multiplication is somewhat disturbed by the results of rounding of the first one. Now I can justify the residues and deal with them knowing their cause of existence. Thank you very much! – Bernardo Opolski Apr 20 '18 at 11:34