from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
tf.__version__

"""
Explanation: Try not to peek at the solutions when you go through the exercises. ;-)
First let's make sure this notebook works well in both Python 2 and Python 3:
End of explanation
"""


>>> graph = tf.Graph()
>>> with graph.as_default():
...     x = tf.Variable(100)
...     c = tf.constant(5)
...     increment_op = tf.assign(x, x + c)
...

>>> with tf.Session(graph=graph) as sess:
...     x.initializer.run()
...     print(x.eval())    # 100
...     for iteration in range(10):
...         increment_op.eval()
...     print(x.eval())    # 150
...

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


>>> graph = tf.Graph()
>>> with graph.as_default():
...     x = tf.Variable(100)
...     c = tf.constant(5)
...     increment_op = tf.assign(x, x + c)
...     init = tf.global_variables_initializer()
...

>>> with tf.Session(graph=graph) as sess:
...     init.run()
...     print(x.eval())    # 100
...     for iteration in range(10):
...         increment_op.eval()
...     print(x.eval())    # 150
...

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


>>> session1 = tf.Session(graph=graph)
>>> session2 = tf.Session(graph=graph)
>>> x.initializer.run(session=session1)
>>> x.initializer.run(session=session2)

>>> increment_op.eval(session=session1)

>>> x.eval(session=session1)

>>> x.eval(session=session2)

>>> session1.close()
>>> session2.close()

"""
Explanation: Variable State
End of explanation
"""


graph = tf.Graph()
with graph.as_default():
    x = tf.Variable(0.0)
    y = tf.Variable(1.0)
    add = tf.assign(x, x + y)
    divide = tf.assign(y, y / 2)
    init = tf.global_variables_initializer()

"""
Explanation: Exercise 2

In this exercise, we will use TensorFlow to compute $ 1 + \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \cdots $ by creating a simple graph then running it multiple times.
Think about how you would solve this problem (and if you are feeling confident enough, go ahead and implement your ideas), then follow the instructions below.
2.1) Create a graph with two variables $ x $ and $ y $, initialized to 0.0 and 1.0 respectively. Create an operation that will perform the following assignment: $ x \gets x + y $. Create a second operation that will perform the following assignment: $ y \gets \dfrac{y}{2} $.
2.2) Now create a Session(), initialize the variables, then create a loop that will run 50 times, and at each iteration will run the first assignment operation, then the second (separately). Finally, print out the value of $ x $. The result should be very close (or equal to) 2.0.
2.3) Try to run the assignment operations simultaneously. What happens to the result? Run your code multiply times: do the results vary? Can you explain what is happening?
2.5) Bonus question (if you have time): update you graph to define the second assignment ($y \gets \frac{y}{2}$) inside a tf.control_dependencies() block, to  guarantee that it runs after the first assignment ($ x \gets x + y$). Does this finally solve the problem?
Try not to peek at the solution below before you have done the exercise! :)

Exercise 2 - Solution
2.1)
End of explanation
"""


with tf.Session(graph=graph):
    init.run()
    for iteration in range(20):
        add.eval()
        divide.eval()
    result = x.eval()

print(result)

"""
Explanation: 2.2)
End of explanation
"""


with tf.Session(graph=graph) as sess:
    init.run()
    for iteration in range(20):
        sess.run([add, divide])
    result = x.eval()

result

"""
Explanation: 2.3)
End of explanation
"""


graph = tf.Graph()
with graph.as_default():
    x = tf.Variable(0.0)
    y = tf.Variable(1.0)
    add = tf.assign(x, x + y)
    with tf.control_dependencies([add]):
        divide = tf.assign(y, y / 2)
    init = tf.global_variables_initializer()

with tf.Session(graph=graph) as sess:
    init.run()
    for iteration in range(30):
        sess.run([add, divide])
    result = x.eval()

result

"""
Explanation: 2.4)
End of explanation
"""


