from __future__ import print_function
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from datetime import date
date.today()

author = "kyubyong. https://github.com/Kyubyong/tensorflow-exercises"

tf.__version__

np.__version__

"""
Explanation: Variables
End of explanation
"""


w = tf.Variable(1.0, name="weight")
with tf.Session() as sess:
    sess.run(w.initializer)
    print(sess.run(w))

"""
Explanation: NOTE on notation
_x, _y, _z, ...: NumPy 0-d or 1-d arrays
_X, _Y, _Z, ...: NumPy 2-d or higer dimensional arrays
x, y, z, ...: 0-d or 1-d tensors
X, Y, Z, ...: 2-d or higher dimensional tensors

Variables
Q0. Create a variable w with an initial value of 1.0 and name weight.
Then, print out the value of w.
End of explanation
"""


# Create a variable w.
w = tf.Variable(1.0, name="Weight")

# Q. Add 1 to w and assign the value to w.
assign_op = w.assign(w + 1.0)
# Or assign_op = w.assign_add(1.0)
# Or assgin_op = tf.assgin(w, w + 1.0)

with tf.Session() as sess:
    sess.run(w.initializer)
    for _ in range(10):
        print(sess.run(w), "=>", end="")
        sess.run(assign_op)
        


"""
Explanation: Q1. Complete this code.
End of explanation
"""


w1 = tf.Variable(1.0)
w2 = tf.Variable(2.0)
w3 = tf.Variable(3.0)

out = w1 + w2 + w3

# Q. Add an Op to initialize global variables.
init_op = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init_op) # Initialize all variables.
    print(sess.run(out))


"""
Explanation: Q2. Complete this code.
End of explanation
"""


V = tf.Variable(tf.truncated_normal([1, 10]))
# Q3. Initialize `W` with 2 * W
W = tf.Variable(V.initialized_value() * 2.0)

# Q4. Add an Op to initialize global variables.
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op) # Initialize all variables.
    _V, _W = sess.run([V, W])
    print(_V)
    print(_W)
    assert np.array_equiv(_V * 2.0, _W)

# Compare the value of `W` with Z = 2 * V. 

"""
Explanation: Q3-4. Complete this code.
End of explanation
"""


g = tf.Graph()
with g.as_default():
    W = tf.Variable([[0,1],[2,3]], name="Weight", dtype=tf.float32)
    # Q5. Print the name of `W`.
    print("Q5.", W.name)
    # Q6. Print the name of the op of `W`.
    print("Q6.", W.op.name)
    # Q7. Print the data type of `w`.
    print("Q7.", W.dtype)
    # Q8. Print the shape of `w`.
    print("Q8.", W.get_shape().as_list())
    # Q9. Print the rank (or ndims) of `W`.
    print("Q9.", W.get_shape().ndims)
    # Q10. Check if the graph of `W` is the same as `g`.
    print("Q10.", W.graph == g)


"""
Explanation: Q5-8. Complete this code.
End of explanation
"""


tf.reset_default_graph()

w1 = tf.Variable(1.0, name="weight1")
w2 = tf.Variable(2.0, name="weight2", trainable=False)
w3 = tf.Variable(3.0, name="weight3")

with tf.Session() as sess:
    # Q11. Initialize the variables w1 and w2.
    sess.run(tf.variables_initializer([w1, w2]))
    
    # Q12. Print the name of all global variables 
    for v in tf.global_variables():
        print("global variable =>", v.name)
    
    # Q13. Print the name of all trainable variables
    for v in tf.trainable_variables():
        print("trainable_variable =>", v.name)
    
        


"""
Explanation: Variable helper functions
Q11-15. Complete this code.
End of explanation
"""


tf.reset_default_graph()
w = tf.Variable(0.2, 'weight') # <- This is what we want to find. The true value is 2.
x = tf.random_uniform([1])
y = 2. * x # Let's pretend we don't know the coefficient 2 here.
y_hat = w * x
loss = tf.squared_difference(y, y_hat)
train_op = tf.train.GradientDescentOptimizer(0.001).minimize(loss)

init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    saver = tf.train.Saver()
    for step in range(1, 10001):
        sess.run(train_op)
        
        import os
        if not os.path.exists('model'): os.mkdir('model') # Let's make a folder in order not to discrupt our current folder.
            
        if step % 1000 == 0:
            print(sess.run(w), "=>", end="")
            
            # Q14. Save the checkpoint file, giving it a base name of `model/my-model`
            save_path = saver.save(sess, 'model/my-model', global_step=step)
            print("Saved successfully")
    
    print(os.listdir('model'))
    # Q15. Find the filename of the latest saved checkpoint file.
    # And if it exists, restore it.
    ckpt = tf.train.latest_checkpoint('model')
    print(ckpt)
    if ckpt is not None:
        saver.restore(sess, ckpt)
        print("Restored succesfully!")
    
    


"""
Explanation: Saving and Restoring Variables
Q14-15. This is a simple example code to find the coefficient of a linear function. (Here y = 2x).
Complete the code.
End of explanation
"""


g = tf.Graph()
with g.as_default():
    with tf.variable_scope("foo"):
        v = tf.get_variable("vv", [1,])  # v.name == "foo/vv:0"
        
    #Q. Get the existing variable `v`
    with tf.variable_scope("foo", reuse=True):
        v1 = tf.get_variable("vv")  # The same as v above.

assert v1 == v    

"""
Explanation: Sharing Variables
Q16. Complete this code.
End of explanation
"""


with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v = tf.get_variable("vv", [1])
        print("v.name =", v.name)

"""
Explanation: Q17. Predict the result of this code.
End of explanation
"""


value = [0, 1, 2, 3, 4, 5, 6, 7]
# Q. Create an initializer with `value`.
init = tf.constant_initializer(value)

tf.reset_default_graph()
x = tf.get_variable('x', shape=[2, 4], initializer=init)

with tf.Session() as sess:
    sess.run(x.initializer)
    print("x =\n", sess.run(x))


"""
Explanation: Q18. Complete this code.
End of explanation
"""


# Q. Create an initializer with a normal distribution of mean equals 0 and standard deviation equals 2.
init = tf.random_normal_initializer(mean=0, stddev=2)

tf.reset_default_graph()
x = tf.get_variable('x', shape=[10, 1000], initializer=init)

with tf.Session():
    x.initializer.run()
    _x = x.eval()
    print("Make sure the mean", np.mean(_x), "is close to 0" )
    print("Make sure the standard deviation", np.std(_x), "is close to 2" )
    


"""
Explanation: Q19. Complete this code.
End of explanation
"""


# Q. Create an initializer with a truncated normal distribution of mean equals 0 and standard deviation equals 2.
init = tf.truncated_normal_initializer(mean=0, stddev=2)

tf.reset_default_graph()
x = tf.get_variable('x', shape=[1000,], initializer=init)

with tf.Session():
    x.initializer.run()
    _x = x.eval()
    plt.scatter(np.arange(1000), _x)
    _avg = np.array([np.mean(_x)] * 1000)
    _std = np.array([np.std(_x)] * 1000)
    plt.plot(np.arange(1000), _avg, 'r-')
    plt.plot(np.arange(1000), _avg + 2*_std, 'g-')
    plt.plot(np.arange(1000), _avg - 2*_std, 'k-')
    plt.legend(['mean', 'upper 2*std', 'lower 2*std'])
    plt.show()
# Note that few data points exceed the bounaries.


"""
Explanation: Q20. Complete this code.
End of explanation
"""


# Q. Create an initializer with a random normal distribution of minimum 0 and maximum 1.
init = tf.random_uniform_initializer(0, 1)

tf.reset_default_graph()
x = tf.get_variable('x', shape=[5000,], initializer=init)

with tf.Session():
    x.initializer.run()
    _x = x.eval()
    count, bins, ignored = plt.hist(_x, 20, normed=True)
    plt.plot(bins, np.ones_like(bins), linewidth=2, color='r')
    plt.show()
# Note that few data points exceed the bounaries.


# Check other initializers such as zeros_initializer, ones_initializer, or orthogonal_initializer, as well.

"""
Explanation: Q21. Complete this code.
End of explanation
"""


tf.reset_default_graph()
print("Of course, there're no variables since we reset the graph. See", tf.global_variables())
with tf.Session() as sess:
    # Q. Import the saved graph of `model/my-model-10000`.
    new_saver = tf.train.import_meta_graph('model/my-model-10000.meta')
    
    new_saver.restore(sess, 'model/my-model-10000')
    
    for v in tf.global_variables():
        print("Now we have a variable", v.name)


"""
Explanation: Exporting and Importing Meta Graphs
Q22. Complete the code. Make sure you've done questions 14-15.
End of explanation
"""


