Difference in calculating eigenvectors wih Java and Python
Asked Answered
P

2

6

As a current task, I need to calculate eigenvalues and eigenvectors for a 120*120 matrix. For start, I tested those calculations on a simple 2 by 2 matrix in both Java (Apache Commons Math library) and Python 2.7 (Numpy library). I have a problem with eigenvector values not matching, as show below :

//Java
import org.apache.commons.math3.linear.EigenDecomposition;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;

public class TemporaryTest {

  public static void main(String[] args) {

    double[][] testArray = {{2, -1}, {1, 1}};
    RealMatrix testMatrix = MatrixUtils.createRealMatrix(testArray);
    EigenDecomposition decomposition = new EigenDecomposition (testMatrix);
    System.out.println("eigenvector[0] = " + decomposition.getEigenvector(0));
    System.out.println("eigenvector[1] = " + decomposition.getEigenvector(1));
}

Output of eigenvectors are shown as {real_value + imaginary_value; real_value + imaginary_value}:

//Java output
eigenvector[0] = {-0.8660254038; 0}
eigenvector[1] = {0.5; 1}

Same code in Python, but using Numpy library:

# Python
import numpy as np
from numpy import linalg as LA

w, v = LA.eig(np.array([[2, -1], [1, 1]]))
print (v[:, 0])
print (v[:, 1])

Output of eigenvectors in Python are shown similarly, [real+imag real+imag]:

[ 0.35355339+0.61237244j  0.70710678+0.j        ]
[ 0.35355339-0.61237244j  0.70710678-0.j        ]

My concern is, why are those vectors different ? Is there something that I am missing ? Ty for any kind of help or advice

Psychosomatic answered 15/11, 2016 at 13:6 Comment(6)
Those vectors in the Java output are not eigenvectors. getEigenvector(i) returns a RealVector, but your matrix has complex eigenvalues and eigenvectors. I don't know how the Apache Commons Math Library represents a complex eigenvector; hopefully someone familiar with the library will help you translate the real values returned by the Java functions into the actual complex eigenvectors.Enumeration
I agree, but as you can see, even the real values do not match :-/Psychosomatic
The java ones aren't even normalised..Wrigley
By any chance is your 120*120 matrix symmetric? If so, you won't have to deal with the problem of the EigenDecomposition methods returning only real values.Enumeration
Is there a way to check symmetry in Python ?Psychosomatic
You could use something like np.allclose(A, A.T). But you may know from the problem itself if the matrix should be symmetric. For example, a covariance matrix is symmetric by construction; there would be no need to check it in your code (except maybe to check for bugs in the code that generates the matrix).Enumeration
E
2

In Apache Commons Math 3, EigenDecomposition accepts nonsymmetric matrices, but it returns results using the classes RealVector and RealMatrix. To get the actual complex results, you have to combine the appropriate real results into complex conjugate pairs.

In the case of the eigenvectors, you got:

eigenvector[0] = {-0.8660254038; 0}
eigenvector[1] = {0.5; 1}

Both those vectors are associated with the complex conjugate pair of eigenvalues getRealEigenvalue(0) + getImagEigenvalue(0)*i and getRealEigenvalue(1) + getImagEigenvalue(1)*i, but those vectors are not the actual eigenvectors. The actual eigenvectors are the complex conjugate pairs eigenvector[0] + eigenvector[1]*i and eigenvector[0] - eigenvector[1]*i.

Those vectors still don't "match" the results returned by numpy, but that is because the two libraries have not used the same normalization. Eigenvectors are not unique; an eigenvector multiplied by any nonzero scalar (including a complex scalar) is still an eigenvector. The only difference between the Java result and the numpy result is a scalar multiplier.

For convenience, I'll convert the floating point values to their exact values. That is, -0.8660254038 is the floating point approximation of -sqrt(3)/2. The Java math library is giving the following eigenvectors:

 [-sqrt(3)/2 + (1/2)*i]    and    [-sqrt(3)/2 - (1/2)*i]
 [      0    +     1*i]           [      0    -     1*i]

If you multiply the first eigenvector by -(sqrt(2)/2)*i and the second by (sqrt(2)/2)*i, you'll get the eigenvectors that were return by numpy.

Here's an ipython session with that calculation. v1 and v2 are the vectors shown above.

In [20]: v1 = np.array([-np.sqrt(3)/2 + 0.5j, 1j])

In [21]: v1
Out[21]: array([-0.8660254+0.5j,  0.0000000+1.j ])

In [22]: v2 = np.array([-np.sqrt(3)/2 - 0.5j, -1j])

In [23]: v2
Out[23]: array([-0.8660254-0.5j,  0.0000000-1.j ])

Multiply v1 by -(sqrt(2)/2)*i to get the first eigenvector returned by numpy.linalg.eig:

In [24]: v1*(-np.sqrt(2)/2*1j)
Out[24]: array([ 0.35355339+0.61237244j,  0.70710678-0.j        ])

Multiply v2 by (sqrt(2)/2)*i to get the second eigenvector returned by numpy.linalg.eig:

In [25]: v2*(np.sqrt(2)/2*1j)
Out[25]: array([ 0.35355339-0.61237244j,  0.70710678+0.j        ])

For convenience, here's a repeat of the numpy calculation. The columns of evecs are the eigenvectors.

In [28]: evals, evecs = np.linalg.eig(a)

In [29]: evecs
Out[29]: 
array([[ 0.35355339+0.61237244j,  0.35355339-0.61237244j],
       [ 0.70710678+0.j        ,  0.70710678-0.j        ]])
Enumeration answered 17/11, 2016 at 20:47 Comment(9)
First, thank you so much for your detailed answer :) Second, I hope you don't mind if I take this step by step, so I can understand everything. My current questions is - if eigenvector[0] and eigenvector[1] have two entries both (e.g. -0.8660254038; 0), where do you use the second entry (zero), and how did you come up with the "i" and "-i" (in the answer, after converting float points) ? P.S. Also, I ran another example with {{2, -2}, {1, 1}} and by following your procedure, cannot get the right values :(Psychosomatic
"...where do you use the second entry (zero)" Because eigenvector[0][1] is 0, the second entry of the actual eigenvectors are both pure imaginary; the real part is 0. I edited the ASCII representation of the eigenvectors to expliclty include "0 + ..."; that 0 is eigenvector[0][1].Enumeration
"... following your procedure, cannot get the right values" You can't use the same constants (±sqrt(2)/2) as the scalar multiples. For each eigenvector (more precisely, for each eigenvector associated with a simple eigenvalue), there will be some constant that you can use to map the java result to the numpy result.Enumeration
Is there a possibility to find that scalar ? Sorry for my lack of knowledge with linear algebra, the concept is new to me, and ive struck a lot of deadlines until nowPsychosomatic
Why do you need it? As long as you are getting a correct eigenvector, the scaling doesn't really matter.Enumeration
For testing purposes - so i can match results with Python. Unfortunately, I have zero support in team and cannot validate the results, but the Python app is tested and calculations are correct.Psychosomatic
Let us continue this discussion in chat.Psychosomatic
@Psychosomatic In the end, how did u calculate the actual eigenvectors? Do u have an impelemantation? tyPyxidium
I have managed to get the eigenvectors in both Python and Java, but I am still not sure how to obtain similar data for identical matricesPsychosomatic
N
1

I don't think you'll be able to make it work. Here is why:

As of 2.0, this class supports only symmetric matrices, and hence computes only real realEigenvalues. This implies the D matrix returned by getD() is always diagonal and the imaginary values returned getImagEigenvalue(int) and getImagEigenvalues() are always null. (c) EigenDecomposition JavaDoc

Nag answered 17/11, 2016 at 19:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.