I'm using Graphviz to represent arrays, using subgraphs and record nodes:
subgraph cluster_array
{
label="my array"
Array [shape="record", label="A | B | C | D"]
Array
}
I would like to add external indices for each array elements, mapping 0 -> A, 1 -> B and so on.
I want to achieve a result similar to:
I've searched online and tried using xlabel but couldn't find a way to correctly add a label for each record element. I've also tried making the indices part of the label, and moving the label with lp, but it seems to have no effect on record nodes.
Is it possible to add external element labels to record nodes using GraphViz?
Not a real answer to your question (which, I think, would be "no") but a workaround that may give you what you want. I use a "parallel" record node with no borders (or paper color borders, to be exact), located very close and connected by an invisible edge:
digraph so
{
subgraph cluster0
{
rank = same{ Array notes }
color = white;
Array [ shape = record, label = "{ A | B | C | D }"] ;
notes [ shape = record, color = white, label = "{ a_1 | b_2 | c_3 | d_4 }" ];
Array -> notes[ style = invis ];
}
nodesep = .0;
X -> Array -> Z;
}
which yields
Related
DISCLAIMER : I am French and so I am sorry in advance for my poor english. Please be nice, thank you very much.
So I have multiple files and graphs with different direction (rankdir). I must merge them to have one big coherent graph.
There is a part on the bottom with the classic toptobottom direction :
strict digraph G {
compound = true
ranksep = "1 equally"
node [shape=box, style=rounded]
render [label="Rendu de l'image"]
subgraph cluster_controle {
label = "Contrôle qualité"
{
conformite[label="Conforme à la réalité : simulation visuelle"]
mesure[label="Mesures factuelles"]
fidelite[label="Fidélité de l'image"]
perf[label="Performance"]
nettete[label="Netteté de l'image"]
nettete -> mesure
fidelite -> mesure
perf -> mesure
mesure -> conformite
}
}
render -> perf [lhead=cluster_controle]
}
Now I have two other parts : one that must go on the left of the main graph, and the other on the right.
Left :
strict digraph O {
compound = true
ranksep = "1 equally"
rankdir=LR
node [shape=box, style=rounded]
subgraph cluster_opti {
label = "Optimisation du calcul"
tracking [label="Eye-tracking"]
{
rank=same
fovea [label="Fovea rendering"]
load [label="Equilibrage de charge"]
denoiser [label="Denoiser"]
}
ia_denoise [label="IA"]
fovea -> tracking
load -> tracking
denoiser -> tracking
ia_denoise -> denoiser
}
}
Right part :
strict digraph A {
compound = true
ranksep = "1 equally"
rankdir=RL
node [shape=box, style=rounded]
subgraph cluster_precalcul {
label = "Anticipation"
precalcul [label="Précalcul des images"]
{
rank=same
ia_mouv [label="IA (mouvements)"]
caching [label="Caching"]
}
ia_mouv -> precalcul
caching -> precalcul
}
}
I tried gvpack -u but it does not do what I want. The direction (rankdir) of the lef and right part are not taken in account. I also tried gvpack -g or -n but there is the following error :
Error: node render in graph G has no position
Error loading layout info from graph G
And now my questions are :
Is it possible to merge them like I want to in one file ?
If yes, how to please ?
If it is not possible, do I really have to do it in Inkscape by hand ? sad face
try:
-array to combine as graphs (not clusters or nodes)
_i to combine the files in the order on the command line (not based on size)
3 to request 3 "columns" of graphs (not a 2x2 grid)
gvpack -array_i3 part*dot | neato -n2 -Tpng
[the gvpack man page can help if you need to alter justification or margins]
Giving:
In the graph below, how do I move the ClassB subgraph to the right of ClassA aligned at the top? I.e. I want to increase the rank of the nodes in ClassB to 3.
I guess it might be possible using invisible dummy nodes, but I can't figure it out. Also I'm hoping there's a less "ad hoc" solution.
digraph G {
graph [rankdir=LR];
0 -> 1 -> 2 -> 3;
subgraph cluster_SEM_SAD_analysis {
graph [label="main"];
main [label="main"];
}
subgraph cluster_ClassA {
graph [label="ClassA"];
ClassA__method1 [label="method1"];
ClassA__method2 [label="method2"];
}
subgraph cluster_ClassB {
graph [label="ClassB"];
ClassB__method1 [label="method1"];
ClassB__method2 [label="method2"];
}
main -> ClassA__method1;
ClassA__method1 -> ClassB__method1;
ClassA__method1 -> ClassA__method2;
ClassA__method1 -> ClassB__method2
}
You need to tell graphviz that you want the nodes in the Class B cluster on the level below method2 of Class 1. You achieve that by introducing an invisble edge between them. This is not "ad hoc", but inherent graphviz logic.
Add, as a last line of your code
ClassA__method2 -> ClassB__method1[ style = invis, weight = 100 ];
and you get
which is probably what you want. Aligning the third cluster at the top is achieved by the weight = 100 element.
I'm starting working with graphviz and I have problem with creating new nodes with this same label.
For example for word "sentence" I would like to create graph with 8 nodes:
s -> e -> n -> t -> e -> n -> c -> e
Now I'm receiving graph with only 5 nodes (one "e" instead of 3 and one "n" instead of 2). I need to create more nodes with this same label (value).
Example of my problem may be this image http://rdftwig.sourceforge.net/paper/diagrams/bfsdeep.png where there are 2 nodes with value "C", "E" and "D".
Is it possible? If it is possible how can I access in my example with word "sentence" first, second or third "e" node?
You could define your nodes explicitly and set the label for them. Then each node has an unique id, but can have the same labels. Consider this example:
strict graph G {
1 [label="A"];
2 [label="B"];
3 [label="B"];
4 [label="A"];
1 -- 2;
2 -- 3;
3 -- 4;
}
which will output (with dot):
It might sound wired, but just put a whitespace at the end of the character that repeated will solve the problem.
Has the following problem got a name and is there an algorithm to solve it? :
Given a graph, either directed or not, find all the paths which satisfy the specification given by
a list of exact nodes, or
'*?' which denotes just 'any node or no node at all', or
'*{n}' which denote 'any n consecutively connected nodes'
e.g.
A -> B -> *? -> D which results in ABXD and ABYD and ABD etc.
or
A -> *{1} -> D -> *? -> E which results in ABXDZE and ABYDZE and ABDZE etc. etc.
thanks
p.s.
Does anyone know a graph library doing this either in R or perl or C?
I dunno any library for that, but you have to separate this in two part :
the user query parsing
The algorithm to find what you are looking for
For the parsing, i let you find what you need to do (using parsing library or by your self)
Concerning the algorithm part i suggest you to define a special structure (like a linked list) for representing you query, in which each element can either denote a real node, x number of node, or unlimited number of nodes.
The only problem on your algorithm is to find all path from a node A to a node B, using an unlimited number or a limited number of intermediate nodes. You can do this by using dynamic programming, or a search algorithm such as DFS or BFS.
What I did at the end was:
The problem is to find all paths of length N between 2 nodes. Cycles are excluded.
read the data in as an edgelist, e.g. pairs of from->to nodes (names of nodes are assumed to be unique)
create a hashtable (or unordered_map in boost and stl, c++) of node names as keys and a hashtable as a value.
this second hashtable will contain all nodes the first node leads to as keys.
For example
A->B
A->C
B->D
C->E
E->D
the resultant data structure holding the input data in perl notation looks like this after reading in all the data as an 'edgelist':
my %hash = (
'A' => {'B' => 1, 'C' => 1},
'B' => {'D' => 1},
'C' => {'E' => 1},
'E' => {'D' => 1},
);
finding if a pair of nodes is DIRECTLY connected can be done roughly as (perl):
sub search {
my ($from,$to) = #_;
if( $to eq '*' ){ return defined($x=$hash{$from}) ? [keys $hash{$from}] : [] }
return defined($x=$hash{$from}) && defined($x{$to}) ? [$to] : []
}
In the above function there is provision to return all the nodes a 'from' node is connected to, by setting $to to '*'. The return is an array ref of nodes connected directly to the $from parameter.
Searching for the path between two nodes requires using the above function recursively.
e.g.
sub path {
my ($from,$to, $hops, $save_results) = #_;
if( $hops < 0 ){ return 0 }
$results = search($from, '*');
if( "".#$results == 0 ){ return 0 }
$found = 0;
foreach $result (#$results){
$a_node = new Tree::Nary($result);
if( path($result, $to, $hops-1, $a_node) == 1 ){
$save_results->insert($save_results, -1, $a_node);
$found = 1;
}
}
return $found;
}
It's ok to use recursion if the depth is not too much (i.e. $hops < 6 ?) because of stack overflow [sic].
The most tricky part is to read through the results and extract the nodes for each path. After a lot of deliberation I decided to use a Tree::Nary (n-ary tree) to store the results. At the end we have the following tree:
|-> B -> D
A -> |-> C -> E -> D
In order to extract all the paths, do:
find all the leaf nodes
start from each leaf node moving backwards via its parent to the root node and saving the node name.
The above was implemented using perl, but have also done it in C++ using boost::unordered_map for hashtable. I haven't yet added a tree structure in then C++ code.
Results: for 3281415 edges and 18601 unique nodes, perl takes 3 mins to find A->'*'->'*'->B. I will give an update on the C++ code when ready.
I updated the question with graphics and more details. Thanks to marapet, without the hack I couldn´t have generated the desired results as images.
why does this code produce this graph?
digraph {
rankdir = TB;
1 -> 2 -> 3 -> 1;
}
How can I get graphviz/dot to produce a clockwise direction like this?
Update
This is the final graph I want to generate (afaik logically correct this way)
digraph {
rankdir = TB
start -> 1
1 -> 2 -> 3 -> 1
3 -> end
3 -> increment
end -> product
{rank = same; 2; 3; increment}
{rank = same; end; product}
}
Which produces this result
While I want this
Thanks
Why does this code produce this graph?
A directed graph puts its nodes on different ranks depending on their relations. Since 1 points to 2, it must be above 2, and since 2 points to 3 it gets to be above 3.
But since 3 also points to 1, the circle is completed - any of the 3 nodes could be on top. Graphviz simply puts the first mentioned node on top. Therefore, if you write instead:
2 -> 3 -> 1 -> 2;
node 2 will be on top, and when using
3 -> 1 -> 2 -> 3;
node 3 will be the top node.
Probably the layout engine neato would be more appropriate for this graph, producing a graph with a clockwise direction:
If you absolutely must use the dot layout engine, the following dot code
digraph {
rankdir = TB;
1 -> 2;
3 -> 2 [dir=back];
3 -> 1;
{rank=same; 2; 3;}
}
produces the desired output by changing the edge 2->3 into 3->2 and at the same time inverting the direction of the arrow.
Or, an other variant of the same technique, easier to explain: We reverse the order of all arrows (1->3->2->1), but display them backwards (dir=back), and force node 2 and 3 to be on the same rank:
rankdir = TB;
edge[dir=back];
1 -> 3 -> 2 -> 1;
{rank=same; 2;3;}
This hack yields the following result: