from __future__ import division
from past.utils import old_div
from ._tools import *

def test_FANode():
    d = 10
    N = 5000
    k = 4

    mu = uniform((1, d))*3.+2.
    sigma = uniform((d,))*0.01
    #A = utils.random_rot(d)[:k,:]
    A = numx_rand.normal(size=(k,d))

    # latent variables
    y = numx_rand.normal(0., 1., size=(N, k))
    # observations
    noise = numx_rand.normal(0., 1., size=(N, d)) * sigma

    x = mult(y, A) + mu + noise

    fa = mdp.nodes.FANode(output_dim=k, dtype='d')
    fa.train(x)
    fa.stop_training()

    # compare estimates to real parameters
    assert_array_almost_equal(fa.mu[0,:], mean(x, axis=0), 5)
    assert_array_almost_equal(fa.sigma, std(noise, axis=0)**2, 2)
    # FA finds A only up to a rotation. here we verify that the
    # A and its estimation span the same subspace
    AA = numx.concatenate((A,fa.A.T),axis=0)
    u,s,vh = utils.svd(AA)
    assert sum(old_div(s,max(s))>1e-2)==k, \
           'A and its estimation do not span the same subspace'

    y = fa.execute(x)
    fa.generate_input()
    fa.generate_input(10)
    fa.generate_input(y)
    fa.generate_input(y, noise=True)

    # test that noise has the right mean and variance
    est = fa.generate_input(numx.zeros((N, k)), noise=True)
    est -= fa.mu
    assert_array_almost_equal(numx.diag(numx.cov(est, rowvar=0)),
                              fa.sigma, 3)
    assert_almost_equal(numx.amax(abs(numx.mean(est, axis=0)), axis=None), 0., 3)

    est = fa.generate_input(100000)
    assert_array_almost_equal_diff(numx.cov(est, rowvar=0),
                                   mdp.utils.mult(fa.A, fa.A.T), 1)

def test_FANode_indim():
    # FANode uses two slightly different initialization for input_dims
    # larger or smaller than 200
    x = numx_rand.normal(size=(5000, 10))
    mdp.nodes.FANode(output_dim=1)(x)
    x = numx_rand.normal(size=(5000, 500))
    mdp.nodes.FANode(output_dim=1)(x)

def test_FANode_singular_cov():
    x = numx.array([[ 1., 1., 0., 0., 0.],
                    [ 0., 1., 1., 0., 0.],
                    [ 0., 1., 0., 0., 0.],
                    [ 0., 1., 1., 0., 0.],
                    [ 0., 1., 0., 0., 1.],
                    [ 0., 1., 0., 1., 0.],
                    [ 0., 1., 0., 0., 0.],
                    [ 1., 1., 0., 0., 0.],
                    [ 1., 1., 0., 0., 0.],
                    [ 0., 1., 0., 1., 1.]])



    fanode = mdp.nodes.FANode(output_dim=3)
    fanode.train(x)
    # the matrix x is singular
    py.test.raises(mdp.NodeException, "fanode.stop_training()")

