# Source code for PyMca5.tests.PCAToolsTest

    #/*##########################################################################
    #
    # The PyMca X-Ray Fluorescence Toolkit
    #
    # Copyright (c) 2004-2014 European Synchrotron Radiation Facility
    #
    # This file is part of the PyMca X-ray Fluorescence Toolkit developed at
    # the ESRF by the Software group.
    #
    # Permission is hereby granted, free of charge, to any person obtaining a copy
    # of this software and associated documentation files (the "Software"), to deal
    # in the Software without restriction, including without limitation the rights
    # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    # copies of the Software, and to permit persons to whom the Software is
    # furnished to do so, subject to the following conditions:
    #
    # The above copyright notice and this permission notice shall be included in
    # all copies or substantial portions of the Software.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    # THE SOFTWARE.
    #
    #############################################################################*/
    __author__ = "V. Armando Sole - ESRF Data Analysis"
    __contact__ = "sole@esrf.fr"
    __license__ = "MIT"
    __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
    import unittest
    import numpy
    import numpy.linalg
    try:
        import mdp
        MDP = True
    except:
        # MDP can give very weird errors
        MDP = False

    class testPCATools(unittest.TestCase):
        def testPCAToolsImport(self):
            from PyMca5.PyMcaMath.mva import PCATools

        def testPCAToolsCovariance(self):
            from PyMca5.PyMcaMath.mva.PCATools import getCovarianceMatrix
            x = numpy.array([[0.0,  2.0,  3.0],
                             [3.0,  0.0, -1.0],
                             [4.0, -4.0,  4.0],
                             [4.0,  4.0,  4.0]])
            nSpectra = x.shape[0]

            # test just multiplication
            tmpArray = numpy.dot(x.T, x)
            for force in [True, False]:
                pymcaCov, pymcaAvg, nData = getCovarianceMatrix(x,
                                                                force=force,
                                                                center=False)
                self.assertTrue(numpy.allclose(tmpArray, pymcaCov * (nData - 1)))

            # calculate covariance using numpy
            numpyCov = numpy.cov(x.T)
            numpyAvg = x.sum(axis=0).reshape(-1, 1) / nSpectra
            tmpArray = x.T - numpyAvg
            numpyCov2 = numpy.dot(tmpArray, tmpArray.T) / nSpectra
            numpyAvg = numpyAvg.reshape(1, -1)

            # calculate covariance using PCATools and 2D stack
            # directly and dynamically loading data
            for force in [False, True]:
                pymcaCov, pymcaAvg, nData = getCovarianceMatrix(x,
                                                                force=force,
                                                                center=True)

                self.assertTrue(numpy.allclose(numpyCov, pymcaCov))
                self.assertTrue(numpy.allclose(numpyAvg, pymcaAvg))
                self.assertTrue(nData == nSpectra)

            # calculate covariance using PCATools and 3D stack
            # directly and dynamically loading data
            x.shape = 2, 2, -1
            for force in [False, True]:
                pymcaCov, pymcaAvg, nData = getCovarianceMatrix(x,
                                                                force=force,
                                                                center=True)

                self.assertTrue(numpy.allclose(numpyCov, pymcaCov))
                self.assertTrue(numpy.allclose(numpyAvg, pymcaAvg))
                self.assertTrue(nData == nSpectra)

        def testPCAToolsPCA(self):
            from PyMca5.PyMcaMath.mva.PCATools import numpyPCA
            x = numpy.array([[0.0,  2.0,  3.0],
                             [3.0,  0.0, -1.0],
                             [4.0, -4.0,  4.0],
                             [4.0,  4.0,  4.0]])

            # that corresponds to 4 spectra of 3 channels
            nSpectra = x.shape[0]

            # calculate eigenvalues and eigenvectors with numpy
            tmpArray = numpy.dot(x.T, x)/(nSpectra - 1)
            numpyEigenvalues, numpyEigenvectors = numpy.linalg.eigh(tmpArray)

            # sort from higher to lower
            idx = list(range(numpyEigenvalues.shape[0]-1, -1 , -1))
            numpyEigenvalues = numpy.take(numpyEigenvalues, idx)
            numpyEigenvectors = numpyEigenvectors[:, ::-1].T

            # now use PyMca
            # centering has to be false to obtain the same results
            for force in [True, False]:
                images, eigenvalues, eigenvectors = numpyPCA(x,
                                                             ncomponents=x.shape[1],
                                                             force=force,
                                                             center=False,
                                                             scale=False)
                self.assertTrue(numpy.allclose(eigenvalues, numpyEigenvalues))
                self.assertTrue(numpy.allclose(eigenvectors, numpyEigenvectors))

            # test with a different shape
            x.shape = 2, 2, -1
            for force in [True, False]:
                images, eigenvalues, eigenvectors = numpyPCA(x,
                                                             ncomponents=3,
                                                             force=force,
                                                             center=False,
                                                             scale=False)
                self.assertTrue(numpy.allclose(eigenvalues, numpyEigenvalues))
                self.assertTrue(numpy.allclose(eigenvectors, numpyEigenvectors))

        if MDP:
            def testPCAToolsMDP(self):
                from PyMca5.PyMcaMath.mva.PCATools import getCovarianceMatrix, numpyPCA
                x = numpy.array([[0.0,  2.0,  3.0],
                                 [3.0,  0.0, -1.0],
                                 [4.0, -4.0,  4.0],
                                 [4.0,  4.0,  4.0]])

                # use mdp
                pcaNode = mdp.nodes.PCANode()
                pcaNode.train(x)
                pcaNode.stop_training()
                pcaEigenvectors = pcaNode.v.T

                # and compare with PyMca
                for force in [True, False]:
                    images, eigenvalues, eigenvectors = numpyPCA(x,
                                                            ncomponents=x.shape[1],
                                                            force=force,
                                                            center=True,
                                                            scale=False)

                    # the eigenvalues must be the same
                    self.assertTrue(numpy.allclose(eigenvalues, pcaNode.d))
                    # the eigenvectors can be multiplied by -1
                    for i in range(x.shape[1]):
                        if (eigenvectors[i,0] >= 0 and pcaEigenvectors[i,0] >=0) or\
                           (eigenvectors[i,0] <= 0 and pcaEigenvectors[i,0] <=0):
                            # both same sign
                            self.assertTrue(numpy.allclose(eigenvectors[i],
                                                           pcaEigenvectors[i]))
                        else:
                            self.assertTrue(numpy.allclose(-eigenvectors[i],
                                                           pcaEigenvectors[i]))

    def getSuite(auto=True):
        testSuite = unittest.TestSuite()
        if auto:
            testSuite.addTest(\
                unittest.TestLoader().loadTestsFromTestCase(testPCATools))
        else:
            # use a predefined order
            testSuite.addTest(testPCATools("testPCAToolsImport"))
            testSuite.addTest(testPCATools("testPCAToolsCovariance"))
            testSuite.addTest(testPCATools("testPCAToolsPCA"))
            if MDP:
                testSuite.addTest(testPCATools("testPCAToolsMDP"))
        return testSuite

    def test(auto=False):
        unittest.TextTestRunner(verbosity=2).run(getSuite(auto=auto))

    if __name__ == '__main__':
        test()