|
5 | 5 | Graph Structures
|
6 | 6 | ================
|
7 | 7 |
|
| 8 | +Theano represents mathematical computations as graphs. These graphs |
| 9 | +are formed of interconnected :ref:`apply` and :ref:`result` nodes, |
| 10 | +which are standard types of objects. They are respectively associated |
| 11 | +to *function application* and *data*. Two additional structures are |
| 12 | +used by Theano in order to represent the operations carried in the |
| 13 | +computations and the data types that are processed. Operations are |
| 14 | +represented :ref:`op` instances and data types are represented by |
| 15 | +:ref:`type` instances. Here is a piece of code and a diagram showing |
| 16 | +the structure built by that piece of code. This should help you |
| 17 | +understand how all these things play together: |
| 18 | + |
| 19 | + |
| 20 | +----------------------- |
| 21 | + |
| 22 | +**Code** |
| 23 | + |
| 24 | +.. code-block:: python |
| 25 | + |
| 26 | + x = dmatrix('x') |
| 27 | + y = dmatrix('y') |
| 28 | + z = x + y |
| 29 | + |
| 30 | +**Diagram** |
| 31 | + |
| 32 | +.. image:: apply.png |
| 33 | + |
| 34 | +----------------------- |
| 35 | + |
| 36 | +Arrows represent references to the Python objects pointed at. The blue |
| 37 | +box is an :ref:`apply` node. Red boxes are :ref:`result` nodes. Green |
| 38 | +circles are :ref:`Ops <op>`. Purple boxes are :ref:`Types <type>`. |
| 39 | + |
| 40 | +When we create :ref:`Results <result>` and then :ref:`apply` |
| 41 | +:ref:`operations <op>` to them to make more Results, we build a |
| 42 | +bi-partite, directed, acyclic graph. Results point to the Apply nodes |
| 43 | +representing the function application producing them via their |
| 44 | +``owner`` field. These Apply nodes point in turn to their input and |
| 45 | +output Results via their ``inputs`` and ``outputs`` fields. |
| 46 | + |
| 47 | +The ``owner`` field of both ``x`` and ``y`` point to ``None`` because |
| 48 | +they are not the result of another computation. If they were the |
| 49 | +result of another computation, they would point to another blue box |
| 50 | +like ``z`` does, and so on. |
| 51 | + |
| 52 | + |
| 53 | +An explicit example |
| 54 | +=================== |
| 55 | + |
| 56 | +In this example we will see in turn a short example where the graph |
| 57 | +construction is hidden behind the standard interface's syntactic |
| 58 | +shortcuts and then the same example but rolled out so that the graph |
| 59 | +construction is made explicit. |
| 60 | + |
| 61 | + |
| 62 | +**Short example** |
| 63 | + |
| 64 | +This is what you would normally type: |
| 65 | + |
| 66 | +.. code-block:: python |
| 67 | + |
| 68 | + from theano.tensor import * |
| 69 | + |
| 70 | + # create 3 Results with owner = None |
| 71 | + x = matrix('x') |
| 72 | + y = matrix('y') |
| 73 | + z = matrix('z') |
| 74 | + |
| 75 | + # create 2 Results (one for 'e', one intermediate for y*z) |
| 76 | + # create 2 Apply instances (one for '+', one for '*') |
| 77 | + e = x + y * z |
| 78 | + |
| 79 | + |
| 80 | +**Long example** |
| 81 | + |
| 82 | +This is what you would type to build the graph explicitly: |
| 83 | + |
| 84 | +.. code-block:: python |
| 85 | + |
| 86 | + from theano.tensor import * |
| 87 | + |
| 88 | + # We instantiate a type that represents a matrix of doubles |
| 89 | + float64_matrix = Tensor(dtype = 'float64', # double |
| 90 | + broadcastable = (False, False)) # matrix |
| 91 | + |
| 92 | + # We make the Result instances we need. |
| 93 | + x = Result(type = float64_matrix, name = 'x') |
| 94 | + y = Result(type = float64_matrix, name = 'y') |
| 95 | + z = Result(type = float64_matrix, name = 'z') |
| 96 | + |
| 97 | + # This is the Result that we want to symbolically represents y*z |
| 98 | + mul_result = Result(type = float64_matrix) |
| 99 | + assert mul_result.owner is None |
| 100 | + |
| 101 | + # We instantiate a symbolic multiplication |
| 102 | + node_mul = Apply(op = mul, |
| 103 | + inputs = [y, z], |
| 104 | + outputs = [mul_result]) |
| 105 | + assert mul_result.owner is node_mul and mul_result.index == 0 # these fields are set by Apply |
| 106 | + |
| 107 | + # This is the Result that we want to symbolically represents x+(y*z) |
| 108 | + add_result = Result(type = float64_matrix) |
| 109 | + assert add_result.owner is None |
| 110 | + |
| 111 | + # We instantiate a symbolic addition |
| 112 | + node_add = Apply(op = add, |
| 113 | + inputs = [x, mul_result], |
| 114 | + outputs = [add_result]) |
| 115 | + assert add_result.owner is node_add and add_result.index == 0 # these fields are set by Apply |
| 116 | + |
| 117 | + e = add_result |
| 118 | + |
| 119 | + # We have access to x, y and z through pointers |
| 120 | + assert e.owner.inputs[0] is x |
| 121 | + assert e.owner.inputs[1] is mul_result |
| 122 | + assert e.owner.inputs[1].owner.inputs[0] is y |
| 123 | + assert e.owner.inputs[1].owner.inputs[1] is z |
| 124 | + |
| 125 | + |
| 126 | +Note how the call to ``Apply`` modifies the ``owner`` and ``index`` |
| 127 | +fields of the :ref:`Results <result>` passed as outputs to point to |
| 128 | +itself and the rank they occupy in the output list. This whole |
| 129 | +machinery builds a DAG (Directed Acyclic Graph) representing the |
| 130 | +computation, a graph that theano can compile and optimize. |
| 131 | + |
| 132 | + |
| 133 | +Graph Structures |
| 134 | +================ |
| 135 | + |
| 136 | +The following section outlines each type of structure that may be used |
| 137 | +in a Theano-built computation graph. The following structures are |
| 138 | +explained: :ref:`apply`, :ref:`constant`, :ref:`op`, :ref:`result` and |
| 139 | +:ref:`type`. |
| 140 | + |
8 | 141 |
|
9 | 142 | .. index::
|
10 | 143 | single: Apply
|
|
0 commit comments