Introduction

This package implements a new module nn.DAG for the torch framework, which inherits from nn.Container and allows to combine modules in an arbitrary Directed Acyclic Graph (DAG).

Example

A typical use would be:

model = nn.DAG()

a = nn.Linear(100, 10)
b = nn.ReLU()
c = nn.Linear(10, 15)
d = nn.CMulTable()
e = nn.Linear(15, 15)

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({ d, e })

input = torch.Tensor(30, 100):uniform()
output = model:updateOutput(input)

which would encode the following graph

             +- Linear(10, 10) -> ReLU ---> d -->
            /                              /
           /                              /
--> a --> b -----------> c --------------+
                          \
                           \
                            +-- Mul(-1) --> e -->

and run a forward pass with a random batch of 30 samples.

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.

Usage

Input and output

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 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()