I'm trying to display "cylindrical" digraphs (in left-to-right format), so that they can be drawn on a cylinder. The graphs consist of successive columns of nodes. The rightmost column of nodes (depicted with dashed lines) are stand-ins for the leftmost column of nodes--so that the graph could be printed out, rolled into a cylinder, and with the dashed nodes aligned exactly "underneath" the first column's nodes, it shows a continuous (cylindrical) loop-graph.
I've had some success using "rank" to get the columnar node alignments, and "group" to suggest horizontal node alignment between 1st and last columns. (This Graphviz Dot vertical alignment of nodes was particularly helpful.)

The problem is, I don't want the first and last column's nodes all splayed out vertically (see pic); there should be no more vertical separation between 1st col's nodes (ditto for last col's nodes) than between nodes in any other node column. But the respective 1st/last col nodes must remain horizontally aligned. (I suppose it's trying to leave the horizontal invisible edges in node pairs 1->15 and 13->16 unobstructed by intermediary nodes; but I don't need them unobstructed. Nodes 1 and 13 (and 15 and 16) should have about the same vertical separation as between (say) nodes 5 and 14.)
TL;DR version
- the graph "flows" left to right, in a sequence of "node columns" (fwiw all edges are pointing rightward, and always only go one step, from column j to column j+1)
- the rightmost column needs to be a "duplicate" of the leftmost column (reflecting the cylindrical wraparound)--it should look like a copy-and-paste of the leftmost column, at the same vertical height
- this has to work for more complicated graphs too (like this one; the leftmost and rightmost columns's nodes line up fairly well, but I'm concerned about outliers), and has to come from an automated process (no fine-tuning by hand)
digraph {
node[label=""];
rankdir="LR"
1->8[label="c"]
13->8[label="a"]
8->12[label="b"]
12->10[label="a"]
10->5[label="a"]
10->14[label="b"]
5->2[label="a"]
5->9[label="b"]
14->9[label="a"]
2->4[label="b"]
9->4[label="a"]
// "wraparound" nodes
node[label="", style="dashed"];
4->15[label="a"] // since "a" connected 4 to 1, 15 must be in the same group as 1 (subs for 1)
node[label="", style="dashed"];
4->16[label="c"]
{rank=same; 1[group=g1]; 13[group=g2]}
{rank=same; 8}
{rank=same; 12}
{rank=same; 10}
{rank=same; 5, 14}
{rank=same; 2, 9}
{rank=same; 4}
// new nodes also need to have same rank; groups should pair corresponding nodes, w/ correct edge letter
{rank=same; 15[group=g1]; 16[group=g2]}
edge[style=invis];
1->15
13->16
}


