import numpy as np
import tensorflow as tf
from matplotlib import pylab as plt
%matplotlib inline

m = 4500 
M = 4 
a = -10
b = 10

x_grid = np.linspace(a, b, m, endpoint=True)
sess = tf.Session()
x = tf.placeholder(tf.double)
trial_func = [tf.sin(x), tf.cos(x), tf.sin(2*x), tf.cos(2*x)]

alpha = tf.Variable(1, dtype=tf.double)
sess.run(tf.initializers.global_variables(), {x:x_grid})
alpha_loc = tf.placeholder(tf.double)
ass_alpha = tf.assign(alpha, alpha_loc)
y_set = [1*tf.sin(x), 0.1*tf.cos(x), tf.sin(2*x), alpha*tf.sigmoid(x), -3.1*tf.cos(2*x)]


A = tf.transpose(trial_func)
A_T = trial_func
y_0 = tf.reduce_sum(input_tensor=y_set, axis=0)
y = tf.expand_dims(y_0, -1)
omega = tf.matmul(tf.matmul(tf.matrix_inverse(tf.matmul(A_T, A)), A_T), y)
regression_fit = tf.matmul(tf.transpose(trial_func), omega)
noninvariance_factor = (1 / m) * tf.reduce_sum(tf.square(y - regression_fit))

def plot_all(x_in,title):
    fig = plt.figure(figsize=(20,10))
    func_set_matrix = sess.run(trial_func, {x:x_in})
    plt.title(title, fontsize = 16)
    plt.grid(True)
    for i in range(np.array(trial_func).shape[0]):
        plt.plot(x_in, func_set_matrix[i])
    plt.plot(x_in, sess.run(y_0, {x:x_in}), '--')
    plt.plot(x_in, sess.run(regression_fit, {x:x_in}), '--')

noninv_list = []
i_list = []
local_alpha = 1e-5
x_grid_obs = np.linspace(a-5, b+5, 10000, endpoint=True)
for i in range(15):
    sess.run(ass_alpha, {alpha_loc:local_alpha})
    noninv_list.append(sess.run(noninvariance_factor, {x:x_grid}))
    i_list.append(local_alpha)
    
    if i==14:
        title = r'$\alpha$'+'='+ str(local_alpha)
        plot_all(x_grid_obs, title)
    if i==5:
        title = r'$\alpha$'+'='+ str(local_alpha)
        plot_all(x_grid_obs, title)
    if i==0:
        title = r'$\alpha$'+'='+ str(local_alpha)
        plot_all(x_grid_obs, title)
    local_alpha += 5
    

"""
Explanation: Исследование поведения предложенного функционала. Часть I.
Возьмем в качестве базисных функций $\cos(x)$, $\sin(x)$, $\cos(2x)$, $\sin(2x)$.
Стоит посмотреть, как будет вести себя предлженная мера неинвариантности при использовании её для простых известных функций и простого оператора, например: $\frac{d^2 f}{dx^2}$
Однако, в качестве первого теста можно проверить, как будет вести себя значение этой меры если в качестве функции, к которой производится аппроксимация будет браться заданная комбинация функций, входящих в набор, затем та же комбинация с добавкой чего-то явно выходящего за пределы линейной оболочки исходного набора. Для этого берется сигмоида, умноженная на параметр. Результатом будет график зависимости меры неивариантности от значения этого параметра.
End of explanation
"""


fig = plt.figure(figsize=(20,10))
plt.grid(True)
plt.title('Noninvariance index', fontsize=26)
plt.xlabel(r'$\alpha$', fontsize=20)
plt.ylabel('J', fontsize=20)
plt.plot(i_list, noninv_list, 'rx--')

"""
Explanation: ---------------------------------------------
Видно, что для достаточно большого $\alpha$ аппроксимируемая функция явно выходит за пределы линейной оболочки исходного набора. Это можно было бы строго показать, учитывая переодичность всех функций исходного набора(который имеет конечный размер) и непериодичность сигмоиды. При достаточно маленьком $\alpha$ мера неивариантости набора ожидаемо должна быть малой. (Да, тут нужно всегда оговариваться, что пока речь не идет о работе с оператором. Пока что просто представляем, что есть некоторый оператор, одной из комбинаций образов базиса которого аппроксимируемая функция и является.)
---------------------------------------------
End of explanation
"""


