Add a unique nodes ordering in a graph
This is a proposal to add a well-specified and unique nodes ordering method in a graph (inputs ordering for a given node is already present).
One of the objective is to define a consistant and unique node inputs ordering for any sub-graph. This would allow for example to canonically handle the connections ordering for matching sub-graphs. General topological sorting algorithms garantees uniqueness only for a DAG forming a Hamiltonian path (i.e. no parallel branch). However, in our case, I thing this could be extended to any Aidge graph because 1) inputs (and outputs) are ordered, meaning parallel branches can be ordered and 2) cycles in the graph should only exist through a specific statefull operator making cycle easily breakable from the point of view of topological sorting.
Advantages:
- Do not rely on user's construction order;
- Is consistant also for generated graphs;
- Possibility to automatically generate meaningful and even predictable unique names for the nodes.
Implementation proposal
Each node stores an ID that can be set within a set of nodes (or a GraphView) by the ordering method (Q: store the ID internally in each node or in a separate structure?). Then, a getSortedInputs()
method allows to get the GraphView inputs by correct order. This method may be implemented with std::sort()
on a pair of node ID and input ID with a custom compare function.
We could use the Longest-Path algorithm or the Coffman–Graham algorithm for the ordering method. Nodes at identical level would then be ordered by highest child level and, if not enough, childs input ranking (for example), in order to garantee uniqueness.
Examples of automatic connections mapping deduction
In these examples, the inputs are ordered and mapping between topologies can be made by connecting inputs of identical rank.
PaddedConv meta-op
flowchart LR
I{"I [1]"} -->|1| Pad("Pad [2]")
Pad --> Conv("Conv [5]")
W{"W [3]"} -->|2| Conv
B{"B [4]"} -->|3| Conv
flowchart LR
I{"I [1]"} -->|1| PaddedConv("PaddedConv [4]")
W{"W [2]"} -->|2| PaddedConv
B{"B [3]"} -->|3| PaddedConv
FC equivalence
flowchart LR
I{"I [1]"} -->|1| Mul("Mul [3]")
W{"W [2]"} -->|2| Mul
Mul --> Add("Add [5]")
B{"B [4]"} -->|3| Add
flowchart LR
I -->|"[1].1 = 1"| Mul("Mul [1]")
W -->|"[1].2 = 2"| Mul
B -->|"[2].1 = 3"| Add("Add [2]")
Mul -->|"[2].2 = 4"| Add
FC
operator, the role of I
and W
should be symmetrical!)
flowchart LR
W -->|"[1].1 = 1"| Mul("Mul [1]")
I -->|"[1].2 = 2"| Mul
B -->|"[2].2 = 3"| Add("Add [2]")
Mul -->|"[2].1 = 4"| Add
flowchart LR
I -->|"[1].1 = 1"| FC("FC [1]")
W -->|"[1].2 = 2"| FC
B -->|"[1].3 = 3"| FC