import pytest
import numpy as np
from numpy.testing import assert_allclose

from keras import backend as K
from keras import activations


def get_standard_values():
    '''
    These are just a set of floats used for testing the activation
    functions, and are useful in multiple tests.
    '''
    return np.array([[0, 0.1, 0.5, 0.9, 1.0]], dtype=K.floatx())


def test_softmax():
    '''
    Test using a reference implementation of softmax
    '''
    def softmax(values):
        m = np.max(values)
        e = np.exp(values - m)
        return e / np.sum(e)

    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.softmax(x)])
    test_values = get_standard_values()

    result = f([test_values])[0]
    expected = softmax(test_values)
    assert_allclose(result, expected, rtol=1e-05)


def test_time_distributed_softmax():
    x = K.placeholder(shape=(1, 1, 5))
    f = K.function([x], [activations.softmax(x)])
    test_values = get_standard_values()
    test_values = np.reshape(test_values, (1, 1, np.size(test_values)))
    f([test_values])[0]


def test_softplus():
    '''
    Test using a reference softplus implementation
    '''
    def softplus(x):
        return np.log(np.ones_like(x) + np.exp(x))

    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.softplus(x)])
    test_values = get_standard_values()

    result = f([test_values])[0]
    expected = softplus(test_values)
    assert_allclose(result, expected, rtol=1e-05)


def test_softsign():
    '''
    Test using a reference softsign implementation
    '''
    def softsign(x):
        return np.divide(x, np.ones_like(x) + np.absolute(x))

    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.softsign(x)])
    test_values = get_standard_values()

    result = f([test_values])[0]
    expected = softsign(test_values)
    assert_allclose(result, expected, rtol=1e-05)


def test_sigmoid():
    '''
    Test using a numerically stable reference sigmoid implementation
    '''
    def ref_sigmoid(x):
        if x >= 0:
            return 1 / (1 + np.exp(-x))
        else:
            z = np.exp(x)
            return z / (1 + z)
    sigmoid = np.vectorize(ref_sigmoid)

    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.sigmoid(x)])
    test_values = get_standard_values()

    result = f([test_values])[0]
    expected = sigmoid(test_values)
    assert_allclose(result, expected, rtol=1e-05)


def test_hard_sigmoid():
    '''
    Test using a reference hard sigmoid implementation
    '''
    def ref_hard_sigmoid(x):
        '''
        Reference hard sigmoid with slope and shift values from theano, see
        https://github.com/Theano/Theano/blob/master/theano/tensor/nnet/sigm.py
        '''
        x = (x * 0.2) + 0.5
        z = 0.0 if x <= 0 else (1.0 if x >= 1 else x)
        return z
    hard_sigmoid = np.vectorize(ref_hard_sigmoid)

    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.hard_sigmoid(x)])
    test_values = get_standard_values()

    result = f([test_values])[0]
    expected = hard_sigmoid(test_values)
    assert_allclose(result, expected, rtol=1e-05)


def test_relu():
    '''
    Relu implementation doesn't depend on the value being
    a theano variable. Testing ints, floats and theano tensors.
    '''
    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.relu(x)])

    test_values = get_standard_values()
    result = f([test_values])[0]

    # because no negatives in test values
    assert_allclose(result, test_values, rtol=1e-05)


def test_elu():
    x = K.placeholder(ndim=2)
    f = K.function([x], [activations.elu(x, 0.5)])

    test_values = get_standard_values()
    result = f([test_values])[0]

    # because no negatives in test values
    assert_allclose(result, test_values, rtol=1e-05)

    negative_values = np.array([[-1, -2]], dtype=K.floatx())
    result = f([negative_values])[0]
    true_result = (np.exp(negative_values) - 1) / 2

    assert_allclose(result, true_result)


def test_tanh():
    test_values = get_standard_values()

    x = K.placeholder(ndim=2)
    exp = activations.tanh(x)
    f = K.function([x], [exp])

    result = f([test_values])[0]
    expected = np.tanh(test_values)
    assert_allclose(result, expected, rtol=1e-05)


def test_linear():
    '''
    This function does no input validation, it just returns the thing
    that was passed in.
    '''
    xs = [1, 5, True, None, 'foo']
    for x in xs:
        assert(x == activations.linear(x))


if __name__ == '__main__':
    pytest.main([__file__])

[(26, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (32, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (36, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (50, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (56, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (66, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (72, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (85, 'numpy.vectorize', 'np.vectorize', True, 'import numpy as np\n'), (87, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (93, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (108, 'numpy.vectorize', 'np.vectorize', True, 'import numpy as np\n'), (110, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (116, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (124, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (131, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (135, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (142, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (148, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (154, 'keras.backend.placeholder', 'K.placeholder', True, 'from keras import backend as K\n'), (155, 'keras.activations.tanh', 'activations.tanh', False, 'from keras import activations\n'), (156, 'keras.backend.function', 'K.function', True, 'from keras import backend as K\n'), (159, 'numpy.tanh', 'np.tanh', True, 'import numpy as np\n'), (160, 'numpy.testing.assert_allclose', 'assert_allclose', False, 'from numpy.testing import assert_allclose\n'), (174, 'pytest.main', 'pytest.main', False, 'import pytest\n'), (22, 'numpy.max', 'np.max', True, 'import numpy as np\n'), (23, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (14, 'keras.backend.floatx', 'K.floatx', True, 'from keras import backend as K\n'), (24, 'numpy.sum', 'np.sum', True, 'import numpy as np\n'), (27, 'keras.activations.softmax', 'activations.softmax', False, 'from keras import activations\n'), (37, 'keras.activations.softmax', 'activations.softmax', False, 'from keras import activations\n'), (39, 'numpy.size', 'np.size', True, 'import numpy as np\n'), (51, 'keras.activations.softplus', 'activations.softplus', False, 'from keras import activations\n'), (67, 'keras.activations.softsign', 'activations.softsign', False, 'from keras import activations\n'), (83, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (88, 'keras.activations.sigmoid', 'activations.sigmoid', False, 'from keras import activations\n'), (111, 'keras.activations.hard_sigmoid', 'activations.hard_sigmoid', False, 'from keras import activations\n'), (125, 'keras.activations.relu', 'activations.relu', False, 'from keras import activations\n'), (136, 'keras.activations.elu', 'activations.elu', False, 'from keras import activations\n'), (144, 'keras.backend.floatx', 'K.floatx', True, 'from keras import backend as K\n'), (146, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (170, 'keras.activations.linear', 'activations.linear', False, 'from keras import activations\n'), (48, 'numpy.ones_like', 'np.ones_like', True, 'import numpy as np\n'), (48, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (64, 'numpy.ones_like', 'np.ones_like', True, 'import numpy as np\n'), (64, 'numpy.absolute', 'np.absolute', True, 'import numpy as np\n'), (81, 'numpy.exp', 'np.exp', True, 'import numpy as np\n')]