# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Explanation: Copyright 2017 Google LLC.
End of explanation
"""


import tensorflow as tf

"""
Explanation: # Concepts de programmation de TensorFlow
Objectifs de formation :
  * Découvrir les bases du modèle de programmation TensorFlow, en particulier les concepts suivants :
    * Les Tensors
    * Les opérations
    * Les graphes
    * Les sessions
  * Développer un programme TensorFlow simple permettant de créer un graphe par défaut, ainsi qu'une session pour exécuter ce graphe
Remarque : Veuillez lire attentivement ce didacticiel. Le modèle de programmation TensorFlow diffère sans doute de ceux que vous avez pu rencontrer jusqu'à maintenant. Il peut donc ne pas être aussi intuitif que vous pouvez l'imaginer.
## Présentation des concepts
Le terme TensorFlow est dérivé du mot Tensors, qui désigne des tableaux de dimension arbitraire. TensorFlow permet d'utiliser des Tensors avec un nombre très élevé de dimensions. Ceci étant, vous utiliserez le plus souvent un ou plusieurs des Tensors de faible dimension ci-après :

Un scalaire est un tableau à zéro dimension (Tensor d'ordre 0). Exemple : \'Salut\' ou 5.
Un vecteur est un tableau à une dimension (Tensor d'ordre 1). Exemple : [2, 3, 5, 7, 11] ou [5].
Une matrice est un tableau à deux dimensions (Tensor d'ordre 2). Exemple : [[3.1, 8.2, 5.9][4.3, -2.7, 6.5]].

Vous pouvez créer, supprimer et manipuler les Tensors au moyen d'opérations. Dans un programme TensorFlow standard, les lignes de code correspondent essentiellement à des opérations.
Un graphe TensorFlow (aussi appelé graphe de calcul ou graphe de flux de données) désigne la représentation graphique d'une structure de données. De nombreux programmes TensorFlow sont constitués d'un seul graphe, mais il est tout à possible d'en créer plusieurs. Les nœuds du graphe représentent des opérations, tandis que les arêtes représentent des Tensors. Les Tensors passent d'un nœud à l'autre et subissent à chaque fois une opération. Le Tensor de sortie d'une opération devient souvent le Tensor d'entrée de l'opération suivante. TensorFlow repose sur un modèle d'exécution paresseux : les nœuds ne sont calculés qu'en cas de nécessité, en fonction des besoins des nœuds associés.
Les Tensors sont enregistrés dans le graphe en tant que constantes ou variables. Comme vous pouvez l'imaginer, les constantes correspondent aux Tensors de valeur fixe. Les variables, elles, désignent les Tensors de valeur variable. Ce qui vous aura peut-être échappé, en revanche, c'est que les constantes et les variables s'ajoutent aux autres opérations du graphe. Ainsi, une constante est une opération renvoyant systématiquement la même valeur de Tensor, et une variable une opération renvoyant le Tensor qui lui a été affecté.
Pour définir une constante, vous devez utiliser l'opérateur tf.constant et transmettre sa valeur. Par exemple :
x = tf.constant([5.2])
De la même façon, le code suivant permet de créer une variable :
y = tf.Variable([5])
Vous pouvez également créer la variable, puis lui affecter une valeur (une valeur par défaut doit être définie) :
y = tf.Variable([0])
  y = y.assign([5])
Après avoir défini plusieurs constantes ou variables, vous pouvez les combiner avec d'autres opérations (par exemple, tf.add). Lors de son évaluation, tf.add appelle les opérations tf.constant ou tf.Variable afin d'obtenir leur valeur, puis renvoie un nouveau Tensor correspondant à la somme de ces valeurs.
Les graphes doivent être exécutés dans une session TensorFlow, qui maintient leur état :
with tf.Session() as sess:
  initialization = tf.global_variables_initializer()
  print(y.eval())
Les opérations tf.Variable doivent être initialisées explicitement en appelant tf.global_variables_initializer au début de la session, comme illustré ci-dessus.
Remarque : Les graphes d'une session peuvent être exécutés sur plusieurs machines (à condition d'exécuter le programme sur un framework de calcul distribué). Pour en savoir plus, consultez la page Distributed TensorFlow (Programme TensorFlow distribué).
Résumé
Le processus de programmation TensorFlow se divise en deux grandes étapes :
1. Assemblage des constantes, variables et opérations sur un graphe
2. Évaluation de ces constantes, variables et opérations au sein d'une session
## Créer un programme TensorFlow simple
Voyons comment coder un programme TensorFlow simple afin d'ajouter deux constantes. 
### Définir des déclarations import
Comme pour la quasi-totalité des programmes Python, la première étape consiste à définir des déclarations import.
Évidemment, ces déclarations varient en fonction des fonctions auquel le programme TensorFlow doit accéder. Tout programme TensorFlow doit au moins contenir la déclaration import tensorflow :
End of explanation
"""


from __future__ import print_function

import tensorflow as tf

# Create a graph.
g = tf.Graph()

# Establish the graph as the "default" graph.
with g.as_default():
  # Assemble a graph consisting of the following three operations:
  #   * Two tf.constant operations to create the operands.
  #   * One tf.add operation to add the two operands.
  x = tf.constant(8, name="x_const")
  y = tf.constant(5, name="y_const")
  sum = tf.add(x, y, name="x_y_sum")


  # Now create a session.
  # The session will run the default graph.
  with tf.Session() as sess:
    print(sum.eval())

"""
Explanation: N'oubliez pas d'exécuter le bloc de code qui précède (les déclarations import).
Autres déclarations import courantes :
import matplotlib.pyplot as plt # Visualisation d'un ensemble de données.
import numpy as np              # Bibliothèque numérique Python de niveau inférieur.
import pandas as pd             # Bibliothèque numérique Python de niveau supérieur.
Le modèle TensorFlow propose un graphe par défaut. Nous vous recommandons cependant de créer votre propre graphe pour faciliter le suivi de son état (vous pouvez utiliser un graphe différent par cellule, par exemple).
End of explanation
"""


# Create a graph.
g = tf.Graph()

# Establish our graph as the "default" graph.
with g.as_default():
  # Assemble a graph consisting of three operations. 
  # (Creating a tensor is an operation.)
  x = tf.constant(8, name="x_const")
  y = tf.constant(5, name="y_const")
  sum = tf.add(x, y, name="x_y_sum")
  
  # Task 1: Define a third scalar integer constant z.
  z = tf.constant(4, name="z_const")
  # Task 2: Add z to `sum` to yield a new sum.
  new_sum = tf.add(sum, z, name="x_y_z_sum")

  # Now create a session.
  # The session will run the default graph.
  with tf.Session() as sess:
    # Task 3: Ensure the program yields the correct grand total.
    print(new_sum.eval())

"""
Explanation: ## Exercice : Ajouter une troisième opérande
Modifiez le code ci-dessus pour ajouter trois entiers, au lieu de deux :

Définissez un troisième entier scalaire de type constante (z), auquel vous affectez la valeur 4.
Ajoutez z à l'opération sum pour réaliser une nouvelle addition.

Astuce : Pour en savoir plus sur la signature de la fonction, consultez la documentation de l'API relative à tf.add().

Exécutez de nouveau le bloc de code modifié. Obtenez-vous le bon total ?

### Solution
Cliquez ci-dessous pour afficher la solution.
End of explanation
"""


