how to optimize layout in graphviz to remove unecessary edges intersections (crossings)? - graph

I am preparing the automaitc documentation of DB relations. The tool is graphviz. Problem I have is that the placement of nodes on the output image is not opptimal and there are many unecessary intersection of edges.
Is there any method to perform optimazation of the graph so the result will have minumum edges intersetction (crossing)?
digraph structs {
node [shape=Mrecord];
overlap="false";
splines="true";
layout=sfdp;
rankdir=LR;
ttype[label="::: ttype :::|<id>id|<table_name>table_name|<type_name>type_name|<synopsis>synopsis"];
tevents[label="::: tevents :::|<id>id|<id_tcases>id_tcases|<id_ttype>id_ttype|<synopsis>synopsis|<expiredate>expiredate|<open>open"];
toperationlog[label="::: toperationlog :::|<id>id|<executiondate>executiondate|<executiontime>executiontime|<query>query|<id_tusers>id_tusers"];
tdocuments[label="::: tdocuments :::|<id>id|<id_tcases>id_tcases|<id_ttype>id_ttype|<path>path|<creationdate>creationdate"];
tcustomers_cases[label="::: tcustomers_cases :::|<id_tcustomers>id_tcustomers|<id_tcases>id_tcases"];
tcases[label="::: tcases :::|<id>id|<creationdate>creationdate|<incomingdate>incomingdate|<clousuredate>clousuredate|<synopsis>synopsis|<notes>notes|<id_ttype>id_ttype|<id_tusers>id_tusers"];
tusers[label="::: tusers :::|<id>id|<username>username|<password>password|<firstname>firstname|<lastname>lastname|<role_id>role_id"];
tcustomers[label="::: tcustomers :::|<id>id|<firstname>firstname|<lastname>lastname|<email>email|<phone>phone|<mobile>mobile|<address>address"];
tevents:id_tcases -> tcases:id [arrowhead="none"];
tevents:id_ttype -> ttype:id [arrowhead="none"];
toperationlog:id_tusers -> tusers:id [arrowhead="none"];
tdocuments:id_tcases -> tcases:id [arrowhead="none"];
tdocuments:id_ttype -> ttype:id [arrowhead="none"];
tcustomers_cases:id_tcustomers -> tcustomers:id [arrowhead="none"];
tcustomers_cases:id_tcases -> tcases:id [arrowhead="none"];
tcases:id_ttype -> ttype:id [arrowhead="none"];
tcases:id_tusers -> tusers:id [arrowhead="none"];
}

Setting remincross to true will cause cross minimization to run a second time, which should improve the look of the graph by reducing the number of edge crosses.

Related

How to show arrows symmetrically for self-loops in a dot graph

I am working with a DFA which I have described in the dot file format. One of the nodes in my DFA has two self-loops, which I originally described with the following way
digraph {
rankdir=LR;
a -> a [color=blue]
a -> a [color=green]
}
Which produces this output when I run dot graph.gv -Tpng
For symmetry reasons, I'd like to have the blue and the green arrows on opposite sides of the node. I can modify the headport and tailport of the green arrow to force it to start from the bottom of the node as follows:
digraph {
rankdir=LR;
a -> a [color=blue]
a:sw -> a:se [color=green]
}
Unfortunately, that produces an absolutely wonky output:
What's the correct way of getting the two arrows to be symmetrically opposite each other on the node?
I doubt you have a lot of control over the edge layout. But you can experiment with the headport attribute values. Here are some examples.
Example 1
digraph {
rankdir=LR;
a:n -> a [color=blue]
a:s -> a [color=green headport=center]
}
View in editor
Example 2
digraph {
rankdir=LR;
a:ne -> a [color=blue]
a:sw -> a [color=green headport=center]
}
View in editor
Other layouts
You can also experiment with different layouts. Here are some options for the circo layout.
Example 3
digraph {
rankdir=LR;
layout="circo"
a:n -> a:_ [color=blue headport=n]
a:s -> a:_ [color=green headport=s dir=back]
}
View in editor
Example 4
digraph {
rankdir=LR;
layout="circo"
a:nw -> a:w [color=blue headport=_]
a:se -> a:e [color=green headport=_]
}
View in editor

Graphviz Composite Structure Diagram

Is it possible to generate with Graphviz / dot a UML composite structure diagram like the one below?
By using (open) box arrowheads https://graphviz.gitlab.io/doc/info/arrows.html, this gets pretty close:
digraph boxed {
graph [splines=ortho nodesep =.75]
node [shape=box]
edge [dir=both arrowtail=obox arrowhead=obox]
{ rank=same
x1 -> x2 -> x3
}
x1:s -> y1:n
}

How to combine list elements and find the price of largest combination

I have a class that holds details of a particular item like the following:
Detail.class
Long detailsId;
Integer price;
List<Long> stackableDetails;
/*getters and setters*/
Now, I have a sample dataset like the following:
DetailId Price StackableDetails
------------------------------------------
1011 4$ 1012, 1014
1012 6$ 1011,1013
1013 10$ 1012
1014 8$ 1011
This data set maps to List sampleDetails.
Now, based on the stackableDetails information, I have to combine the details and pick the combination having the max price from it.
For eg,
In the data set available, the possible combinations would be
1011,1012,1014 - 4+6+8 = 18$
1012,1011,1013 - 6+4+10 = 20$
1013,1012 - 10+6 = 16$
1014,1011 - 8+4 = 12$
Now the combination of details 1012,1011,1013 yields 20$, so I fetch this combination and add this in my result list. How can I achieve this in java8.
Any help appreciated. Thanks!
Well, first it's a bit misleading. In your question you say pick the combination having the least price from it, but then later (and your comments) you actually provide the samples that yields the max result.
Assuming you need the max result, you could use this:
long maxPrice = list
.stream()
.map(d -> Stream.concat(Stream.of(d.getDetailsId()), d.getStackableDetails().stream()))
.map(s -> s.reduce(0L, (left, right) -> left +
list.stream()
.filter(dt -> dt.getDetailsId().equals(right))
.findAny()
.get()
.getPrice()))
.max(Comparator.naturalOrder())
.orElse(0L);
System.out.println(maxPrice); // 20
EDIT
Well you want to compare by max price, but output set the make this price. The only thing I could come up with is to put them into a TreeMap, but that is not very readable IMHO. Also, there is the case when you have entries that collide - they have the same max price. This example simply takes the last one in encounter order.
List<Long> highest = list
.stream()
.map(d -> Stream.concat(Stream.of(d.getDetailsId()), d.getStackableDetails().stream()).collect(Collectors.toList()))
.collect(Collectors.toMap(s -> s.stream().reduce(0L,
(left, right) -> left + list.stream().filter(dt -> dt.getDetailsId().equals(right)).findAny().get().getPrice()),
s -> s.stream().collect(Collectors.toList()),
(left, right) -> right,
TreeMap::new))
.lastEntry().getValue();
EDIT2
Map<List<Long>, Long> map = list
.stream()
.map(d -> Stream.concat(Stream.of(d.getDetailsId()), d.getStackableDetails().stream()).collect(Collectors.toList()))
.collect(Collectors.toMap(
s -> s.stream().collect(Collectors.toList()),
s -> s.stream().reduce(0L,
(left, right) -> left + list.stream().filter(dt -> dt.getDetailsId().equals(right)).findAny().get().getPrice()),
(left, right) -> right));
Looking at Eugenes answer I thought that should work also a bit shorter... Here is another variant using flatMap and mapToDouble and the DoubleStream's sum and max:
double maxPrice = details.stream()
.mapToDouble(detail -> Stream.concat(Stream.of(detail.getDetailsId()),
detail.getStackableDetails().stream())
.flatMap(detailId -> details.stream()
.filter(candidateDetail -> detailId.equals(candidateDetail.getDetailsId())))
.map(Detail::getPrice)
.mapToDouble(value -> /* your transformation function */ (double) value)
.sum()
)
.max()
.orElse(0.0);
I wonder, why you might only sum up the details and its immediate stackable details. It sounds like a recursive problem to me, but on the other side, you probably know, what you need :-)
Regarding your other question: you may just want to replace the transformation step into the following:
.mapToDouble(Double::parseDouble)
Note that you can exchange double/mapToDouble, etc. to whatever suits you the most. sum() however is only available to the three primitive streams: IntStream, LongStream and DoubleStream.

Graphviz / dot label overlap with edge

I have a label that barely touch the edge, but there were plenty of space round it, is there anyway to make it not touching the edge in Graphiz? This is minor issue, but I have to redraw in powerpoint if cannot resolve by coding as my collaborator is very unhappy about this.. (Or I may export to JPEG and make changing in paintbrush)...
And we have to do this in black and white only so changing color would not help :(
minimal example: (the whole diagram is much more complex, and I have to put A E B in the same rank)
digraph "md" {
rankdir=TB;
size="8,8";
node [fontname="Helvetica" fontsize=10 shape=box];
edge [fontname="Helvetica" fontsize=10];
center=1;
{rank=min "A"}
{rank=min "B"}
{rank=min "E"}
"A" -> "B" [label="0.55***", dir=both];
"E" -> "B" [label="0.22" labeldistance="2"];
}
For the records - making the example minimal
digraph {
{ rank=same A -> B -> C }
A -> C [label=AC]
}
gives
using xlabel instead of label may help (but seems to have side effects)
digraph {
{ rank=same A -> B -> C }
A -> C [xlabel=AC]
}
gives

Tighten the dot graph making it more symmetric

I compile the following graph with: dot graph.dot -Tpdf -ograph.pdf, producing:
The result is okay, but the state transition look a lot like a spagetthi monster and I have no clue what I could do to approach this problem. I tried other layouts: twopi, neato, etc. Are there parameters which allow it to force the graph to look a little bit more symmetric? Because the overall picture is okay.
To me it seems like the edges use the minimal space available to frame the edge descriptions, maybe this is the problem?
Is my graph design flawed? Should I write the different state transitions on one edge, using \n to separate the different transitions?
digraph finite_state_machine {
rankdir=LR;
edge [fontsize=26];
node [shape = doublecircle, width=2.0, fontsize=24, fixedsize=true,style=filled, colorscheme=spectral5]; New [fillcolor=3] Terminated [fillcolor=5];
node [shape = circle, width=2.0, fontsize=24, fixedsize=true, colorscheme=spectral5]; Runnable [fillcolor=4] Waiting [fillcolor=2] "Timed\nWaiting" [fillcolor=2] Blocked [fillcolor=1];
New -> Runnable [ label = "Thread.start" ];
Runnable -> Waiting [ label = "Object.wait" ];
Runnable -> Waiting [ label = "Thread.sleep" ];
Runnable -> Waiting [ label = "LockSupport.park" ];
Waiting -> Blocked [ label = "Reacquire monitor lock after\nleaving Object.wait" ]
Waiting -> Blocked [label = "Spurious wake-up"]
"Timed\nWaiting" -> Blocked [ label = "Reaquire monitor lock after\n leaving Object.wait" ]
"Timed\nWaiting" -> Terminated [ label = "Exception" ]
"Timed\nWaiting" -> Blocked [ label = "Spurious wake-up" ]
Runnable -> "Timed\nWaiting" [ label = "Object.wait" ];
Runnable -> Blocked [ label = "Contended Monitor\nEnter" ];
Blocked -> Runnable [ label = "Contended Monitor\nEntered" ];
Runnable -> Terminated [ label = "Thread finishes\nexecution" ]
Runnable -> Terminated [ label = "Exception" ]
Waiting -> Runnable [ label = "Object.notify\nObject.notifyAll" ]
Waiting -> Terminated [ label = "Exception" ]
"Timed\nWaiting" -> Runnable [ label = "Object.notify\nObject.notifyAll" ]
}
I don't think your design is flawed, I think it's ok. The dot syntax is readable and therefore maintainable, and the result is what an automatically generated graph looks typically alike.
Of course you can add small corrections to make this particular graph better (or at least different). Some of them may prove difficult to implement if for example the source for your graph is generated by an application. Here are some ideas:
To get the layout to be more symmetric, you may try to align the nodes Waiting and Terminated as well as Timed Waiting and Blocked by setting their group attribute to the same value (group=a and group=b).
It worked fine for Waiting and Teminated, but not so well for Timed Waiting and Blocked - probably because there are two edges between those nodes.
You may try to straighten them out by picking one of the edges which links them and set its weight attribute to a high value.
Other than that, I think the graph looks nicer over all because the edges are smoother and have less unnecessary curves, especially - but not only - between Runnable and Waiting.
The spaghetti effect is due to the splines - maybe it looks less spaghetti without splines? I tried by adding splines=compound or splines=ortho (same result):
The graph uses slightly less vertical space. It's not spaghetti, but it's not better in my opinion...
You may also try splines=compound without the group attributes, this should make the graph a little more compact (bug not necessarily prettier). Or simply play around with the weight of the edges to straighten out particularly unpleasant edges.
In some cases, concentrate can clear up graphs with lots of parallel edges - in this case it doesn't really help.

Resources