OCD cosmetics.
[dagnn.git] / README.md
index 3b8d274..fa77a7e 100644 (file)
--- a/README.md
+++ b/README.md
+# Introduction #
 
-This package implements a new module nn.DAG which inherits from nn.Container and allows to combine modules in an arbitrary graph without cycle.
+This package implements a new module nn.DAG for the [torch framework](https://torch.ch),
+which inherits from [nn.Container](https://github.com/torch/nn/blob/master/Container.lua) and allows to combine modules in an
+arbitrary [Directed Acyclic Graph (DAG).](https://en.wikipedia.org/wiki/Directed_acyclic_graph)
 
-#Example#
+## Example ##
 
-The typical use is:
+A typical use would be:
 
-```Lua
+```lua
 model = nn.DAG()
 
 a = nn.Linear(100, 10)
 b = nn.ReLU()
 c = nn.Linear(10, 15)
-d = nn.Linear(10, 15)
-e = nn.CMulTable()
-f = nn.Linear(15, 15)
+d = nn.CMulTable()
+e = nn.Linear(15, 15)
 
-model:addEdge(a, b)
-model:addEdge(b, c)
-model:addEdge(b, d)
-model:addEdge(c, e)
-model:addEdge(d, e)
-model:addEdge(d, f)
+model:connect(a, b)
+model:connect(b, nn.Linear(10, 15), nn.ReLU(), d)
+model:connect(b, c)
+model:connect(c, d)
+model:connect(c, nn.Mul(-1), e)
 
 model:setInput(a)
-model:setOutput({ e, f })
+model:setOutput({ d, e })
 
-input = torch.Tensor(300, 100):uniform()
-output = model:updateOutput(input):clone()
+input = torch.Tensor(30, 100):uniform()
+output = model:updateOutput(input)
 
 ```
 
 which would encode the following graph
 
-                       +--> c ----> e -->
-                      /            /
-                     /            /
-    input --> a --> b ----> d ---+          output
-                             \
+                 +- Linear(10, 10) -> ReLU ---> d -->
+                /                              /
+               /                              /
+    --> a --> b -----------> c --------------+
                               \
-                               +--> f -->
+                               \
+                                +-- Mul(-1) --> e -->
 
-#Input and output#
+and run a forward pass with a random batch of 30 samples.
 
-If a node has a single successor, its output is sent unchanged as input to that successor. If it has multiple successors, the outputs are collected into a table, and the table is used as input to the successor node. The indexes of the outputs in that table reflects the order in which they appear in the addEdge commands.
+Note that DAG:connect allows to add a bunch of edges at once. This is
+particularly useful to add anonymous modules which have a single
+predecessor and successor.
 
-The expected input (respectively the produced output) is a nested table of inputs reflecting the structure of the nested table of modules provided to DAG:setInput (respectively DAG:setOutput)
+# Usage #
 
-So for instance, in the example above, the DAG expects a tensor as input, since it is the input to the module a, and its output will is a table composed of two tensors, corresponding to the outputs of e and f respectively.
+## Input and output ##
 
-*Francois Fleuret, Jan 12th, 2017*
+The DAG can deal with modules which take as input and produce as
+output tensors and nested tables of tensors.
+
+If a node has a single predecessor, the output of the latter is taken
+as-is as the input to the former. If it has multiple predecessors, all
+the outputs are collected into a table, and the table is used as
+input. The indexes of the outputs in that table reflect the
+chronological order in which the edges where created in the
+DAG:connect() commands.
+
+The input to the DAG (respectively the produced output) is a nested
+table of inputs reflecting the structure of the nested table of
+modules given as argument to DAG:setInput (respectively DAG:setOutput)
+
+So for instance, in the example above, the model expects a tensor as
+input, since it is the input to the module a, and its output is a
+table composed of two tensors, corresponding to the outputs of d and e
+respectively.
+
+## Functions ##
+
+### nn.DAG() ###
+
+Create a new empty DAG, which inherits from nn.Container.
+
+### nn.DAG:connect(module1, module2 [, module3, [...]]) ###
+
+Add new nodes corresponding to the modules passed as arguments if they
+have not been already added in a previous call. Add edges between
+every two nodes associated to two successive modules in the
+arguments.
+
+Calling this function with n > 2 arguments is strictly equivalent to
+calling it n-1 times on the pairs of successive arguments.
+
+Accepting more than two arguments allows in particular to add
+anonymous modules, which are not associated to variables. In principle
+the only ones that have to be non-anonymous are those that have more
+than one successor/predecessor and/or are inputs/outputs.
+
+### nn.DAG:setInput(i) ###
+
+Define the content and structure of the input. The argument should be
+either a module, or a (nested) table of modules. The input to the DAG
+should be a (nested) table of inputs, with the corresponding
+structure.
+
+### nn.DAG:setOutput(o) ###
+
+Similar to DAG:setInput().
+
+### nn.DAG:print() ###
+
+Print the list of nodes.
+
+### nn.DAG:saveDot(filename) ###
+
+Save a dot file to be used by the Graphviz set of tools for graph
+visualization. This dot file can than be used for instance to produce
+a pdf file such as [this one](https://fleuret.org/git-extract/dagnn/graph.pdf) with
+
+```
+dot graph.dot -T pdf -o graph.pdf
+```
+
+### nn.DAG:setLabel(module, name) ###
+
+Add a label to the given module, that will be used for DAG:print() and DAG:saveDot()