# Programmatically Illustrating Permutations

Here's an illustration of what's known as an expansion permutation (the top half). It just means that instead of going straight through (like the bottom half aka. the identity permutation), the signal paths get shuffled around, and some inputs split and lead to multiple outputs (4 inputs ↦ 6 outputs).

Here's the code for this one:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows,positioning}

\begin{document}
\begin{tikzpicture}[>=latex']

\tikzset{cross line/.style={preaction={draw=white, -, shorten >=1pt, shorten <=1pt, line width=3.33pt}}}
\tikzstyle{invisible_block} = [draw=none, minimum size=0.0mm, text centered, text width=1.9em]
\tikzstyle{bit} = [fill,shape=rectangle, minimum size=0.5mm, inner sep=0pt]

\newcount\u

%% Define all nodes:
\foreach \i in {0,...,7} {
\u=\i
\def\j{\number\u}
%% 8 invisible boxes
\node[invisible_block, xshift=\i*12mm]
(S\j) {};
%% 6 above (inputs)
\foreach \m in {0,...,5}  {
\node[bit,xshift=1mm,yshift=0.2mm,right=1.25*\m mm of S\j.north west] (S\j in\m) {};
\node[bit,above=15mm of S\j in\m] (S\j inmid\m) {};
\node[bit,above=4mm of S\j in\m] (S\j inclose\m) {};
}
%% 4 on top (inputs)
\foreach \m in {1,...,4}  {
\node[xshift=1mm,yshift=-0.2mm,right=1.25*\m mm of S\j.south west] (S\j out\m) {};
\node[bit,above=4mm of S\j inmid\m] (S\j inext\m) {};
}
}

%% crossed connections
\foreach \i in {1,...,7} {
\u=\i
\def\j{\number\u}

\draw[-,very thin] (S\j inext1) -- (S\i inmid5);
\draw[cross line,-,very thin] (S\i inext4) -- (S\j inmid0);
}

%% vertical connections
\foreach \i in {0,...,7} {
\u=\i
\def\j{\number\u}

\foreach \m in {0,...,5}  {
\draw[-,very thin] (S\j inclose\m) -- (S\j in\m);
\draw[-,densely dotted,thin,color={black!40!white}] (S\j inclose\m) -- (S\j inmid\m);
}
\foreach \m in {1,...,4}  {
\draw[-,very thin] (S\j inmid\m) -- (S\j inext\m);
}
}

%% big loopy horizontal cables
%% nodes (invisible)
\node[coordinate,right=2mm of S8inmid5] (afterS8) {};
\node[coordinate,right=4mm of S8inmid5] (afterS8b) {};
\node[coordinate, left=2mm of S1inmid0] (beforeS1) {};
\node[coordinate, left=4mm of S1inmid0] (beforeS1b) {};
%% lines (visible)
\draw[cross line,-,very thin] (S1inext1.south west) to[out=200, in=120] (beforeS1b) to[out=300,in=270,distance=7mm] (afterS8) to[out=90,in=90,distance=1.8mm] (S8inmid5.north);
\draw[cross line,-,very thin] (S8inext4.south east) to[out=330, in=60] (afterS8b) to[out=240,in=270,distance=10mm] (beforeS1) to[out=90,in=90,distance=1.8mm] (S1inmid0.north);

%%% long horizontal chassis bars / lines/ edges
\draw[-,thin] (S1inext1.north west)   -- node[above, near start] {} (S8inext4.north east);
\draw[-,thin] (S1inmid0.south west)   -- (S8inmid5.south east);
\draw[-,thin] (S1inclose0.north west) -- (S8inclose5.north east);
\draw[-,thin] (S1in0.south west)      -- (S8in5.south east);

\end{tikzpicture}
\end{document}


It has a nice predictable, seamless, recurring pattern. Not all permutations are like that. Here's an image of one I found on Wikipedia. Here's a table showing which nodes are mapped to which.

      01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
:: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: ::
09 17 23 31 13 28 02 18 24 16 30 06 26 20 10 01 08 14 25 03 04 29 11 19 32 12 22 07 05 27 15 21


My question is: how can we conveniently incorporate this data into the code in order to illustrate in the same style (or similar). It doesn't necessarily have to be this set of data, that's just the set of data I'm working with at the moment. Rather than having to manually re-arrange the connections every time I'm working with a new permutation I'd like to be able to simply feed it comma separated values, an array of tuples, data from a spreadsheet, or something of that nature.

It probably sounds complicated, but really all it has to do is connect the dots when presented with a set of values.

The end result might look something like this:

I started to add them all manually like:

\draw[-,very thin] (S1inclose0.south east) -- (S3in1.north west);
\draw[-,very thin] (S1inclose2.south east) -- (S4in1.north west);


Then decided there had to be a better way and this is as far as I got:

\foreach \x in {1,...,8} {
\foreach \y in {1,...,4} {
\draw[-,very thin] (S\x inclose\y.south east) -- ( ??? );


This connects the bullets according to the permutation. It just draws nodes labelled 01,02,... in the top row and then nodes labelled p(01),p(02),... in the bottom row, where p(x) is the permutation of x. In the second run it connects x on the top with p(x) on the bottom. All you need to do is to say

\pic{perms={09,17,23,31,13,28,02,18,24,16,30,06,26,20,10,01,08,14,25,03,04,29,11,19,32,12,22,07,05,27,15,21}};


and TikZ will do the rest. You are not limited to 32 entries. (This version assumes that you always have two digits and want to fill up with 0s. If you want different conventions, this will be easy to accomplish. Also one could add pgf keys that control the dimensions of the graph, but all of this requires some input from your side.)

\documentclass[tikz,border=3.14mm]{standalone}
\tikzset{cross line/.style={preaction={draw=white, -, shorten >=1pt, shorten
<=1pt, line width=1.6pt}}}
\begin{document}

\begin{tikzpicture}[pics/perms/.style={code={
\foreach \XX [count=\YY] in {#1}
{\node[blullet] (T\YY) at (\YY,5) {\ifnum\YY<10 0\fi\YY};
\node[blullet] (B\the\numexpr\XX) at (\YY,-5) {\XX};}
\foreach \XX [count=\YY] in {#1}
{\draw[cross line] (T\YY) -- (B\YY);}
}},blullet/.style={circle,fill=blue,text=white,text width={width("33")},
font=\sffamily,align=center},scale=0.5,transform shape]
\pic{perms={09,17,23,31,13,28,02,18,24,16,30,06,26,20,10,01,08,14,25,03,04,29,11,19,32,12,22,07,05,27,15,21}};
\end{tikzpicture}
\end{document}


\documentclass[tikz,border=3.14mm]{standalone}
\tikzset{cross line/.style={preaction={draw=white, -, shorten >=1pt, shorten
<=1pt, line width=1.6pt}}}
\begin{document}

\begin{tikzpicture}[pics/perms/.style={code={
\foreach \XX [count=\YY] in {#1}
{\node[blullet] (T\YY) at ({\YY+int((\YY-1)/4)},10) {};
\node[blullet] (M\YY) at ({\YY+int((\YY-1)/4)},5) {\ifnum\YY<10 0\fi\YY};
\node[blullet] (B\the\numexpr\XX) at ({\YY+int((\YY-1)/4)},-5) {\XX};
\xdef\Ymax{\YY}}
\foreach \XX [count=\YY] in {#1}
{\draw[cross line] (M\YY) -- (B\YY);
\ifnum\YY>1
\ifnum\YY<\Ymax
\pgfmathtruncatemacro{\YYp}{\YY+ifthenelse(int(\YY/4)==\YY/4,1,0)
-ifthenelse(int((\YY-1)/4)==(\YY-1)/4,1,0)}
\pgfmathtruncatemacro{\YYm}{\YY-ifthenelse(int((\YY+1)/4)==(\YY+1)/4,1,0)}
\draw[cross line] (T\YYp) -- (M\YY);
\else
\draw (T1.-135) to[out=-135,in=180] ([yshift=-2cm]M1.south)
-- ([yshift=-2cm]M\Ymax.south) to[out=0,in=-45] (M\Ymax);
\fi
\else
\draw (M1.-135) to[out=-135,in=180] ([yshift=-1cm]M1.south)
-- ([yshift=-1cm]M\Ymax.south) to[out=0,in=-45] (T\Ymax);
\fi
}
}},blullet/.style={circle,fill=blue,text=white,text width={width("33")},
font=\sffamily,align=center},scale=0.5,transform shape]
\pic{perms={09,17,23,31,13,28,02,18,24,16,30,06,26,20,10,01,08,14,25,03,04,29,11,19,32,12,22,07,05,27,15,21}};
\end{tikzpicture}
\end{document}


• This is great by itself, but I'm trying to create the third image (the one below it). I'll try to adapt the technique so I can integrate it into my code. I think you might be too good at this. I don't know what bits I need and which bits I don't. Jul 11 '19 at 8:38
• can i extend the black line without extending the white part? Jul 11 '19 at 9:02
• @tjt263: No, you cannot. But you can modify the argument of shorten in the definition of the lines. You could even change both occurrences of 1pt to #1 and draw the lines with \draw[cross line=5pt]. Jul 11 '19 at 9:27
• @oerpli It's okay, it was just a tiny aesthetic thing I was fussing over. I better actually get it working with my code first. Jul 11 '19 at 9:38
• Maybe my comment was a bit weirdly phrased. You can do it, the comment describes how to do it. I adapted my answer (see below) to incorporate this. Though I have no idea for what purpose you would use it. Jul 11 '19 at 9:41

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{shapes.geometric, calc}

\tikzset{,
,   cross line/.style={preaction={draw=white, -, shorten >=#1, shorten
<=#1, line width=2.5pt}, line width=1.5}
,square/.style={regular polygon,regular polygon sides=4}
,sqnode/.style={square,fill=black, text=white,align=center,inner sep=2pt}
}

\newcommand{\perms}[3]{
\foreach \XX [count=\YY] in {#1} {
\node[sqnode](#2-\the\numexpr\XX) at ($(top) + (\XX,\yDist)$) {};
\coordinate (last) at ($(top) + (\YY,\yDist)$); % to connect first with last
\node[sqnode](#3-\the\numexpr\XX) at ($(top) + (\YY,0)$) {};
\draw[cross line=5pt] (#3-\the\numexpr\XX.center) -- (#2-\the\numexpr\XX.center);
}
\node[sqnode] (last) at (last) {};
\draw[ultra thick] (#2-1.north west) -- (last.north east);    % connect top line
\draw[ultra thick]($(#2-1.south west) - (0,\yDist)$) -- ($(last.south east) - (0,\yDist)$);
}

\begin{document}
\begin{tikzpicture}[scale=1.5,transform shape]

\def\yDist{5} % distance between top and bottom row

\coordinate (top) at (0,0); % location of top left node
\perms{1,2,4,5,3,6,7,8,9,10}{T1}{B1} % T1 = name of top row, T2 = name of bottom row
\coordinate (top) at (0,-8); % location of top left node
\perms{5,4,3,2,1,6,7,8,9,10}{T2}{B2} % T1 = name of top row, T2 = name of bottom row

\foreach \XX [count=\YY] in {1,2,4,5,3,6,7,8,9,10} { % has to be same permutation as top one
\draw[dotted, very thick] (B1-\XX.south) -- (T2-\YY.north);    % connect top line
}

\end{tikzpicture}
\end{document}


If you need the special spacings between groups of elements as in one of your pictures, adapt the at (\XX, and at (\YY) part of these two lines.

    \node$blullet$    (B\the\numexpr\XX) at (\XX,\yDist) {};
\node$blullet$    (T\the\numexpr\XX) at (\YY,0) {};


Basically you have to apply the following function:

f(x) = x + floor(x/4)* 0.5

Add a space of 0.5 after every group of 4 dots. I am not sure how to achieve these kinds of calculations in tikz coordinates though.

I tried to implement the groupings with the math library.:

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{shapes.geometric, calc}
\usetikzlibrary{math}

\tikzset{,
,   cross line/.style={preaction={draw=white, -, shorten >=#1, shorten
<=#1, line width=2.5pt}, line width=1}
,square/.style={regular polygon,regular polygon sides=4}
,sqnode/.style={square,fill=black, text=white,align=center,inner sep=2pt}
}

\newcommand{\perms}[3]{
\foreach \XX [count=\YY] in {#1} {
\tikzmath{
integer \m;
real \tx;
\offset = 1; % extra distance between groups
\m1 = ((\XX - 1) * 0.25); % size of groups (0.25 => 4)
\m2 = ((\YY - 1) * 0.25); % same
\tx1 = \XX + \m1 * \offset; % calculate transformed x coord
\tx2 = \YY + \m2 * \offset;
};
\node[sqnode](#2-\the\numexpr\XX) at ($(top) + (\tx{1},\yDist)$) {};
\coordinate (last) at ($(top) + (\tx{1},\yDist)$); % to connect first with last
\node[sqnode](#3-\the\numexpr\XX) at ($(top) + (\tx{2},0)$) {};
\draw[cross line=5pt] (#3-\the\numexpr\XX.center) -- (#2-\the\numexpr\XX.center);
}
\node[sqnode] (last) at (last) {};
\draw[ultra thick] (#2-1.north west) -- (last.north east);    % connect top line
\draw[ultra thick]($(#2-1.south west) - (0,\yDist)$) -- ($(last.south east) - (0,\yDist)$);
}

\begin{document}
\begin{tikzpicture}[scale=1.0,transform shape]

\def\yDist{5} % distance between top and bottom row

\coordinate (top) at (0,0); % location of top left node
\perms{1,2,3,5,4,6,8,7,9,10}{T1}{B1} % T1 = name of top row, T2 = name of bottom row
\coordinate (top) at (0,-8); % location of top left node
\perms{4,3,2,1,6,5,8,7,9,10}{T2}{B2} % T1 = name of top row, T2 = name of bottom row

\foreach \XX [count=\YY] in {1,2,3,5,4,6,8,7,9,10} { % has to be same permutation as top one
\draw[dotted, very thick] (B1-\XX.south) -- (T2-\YY.north);    % connect top line
}

\end{tikzpicture}
\end{document}


• That would probably be fine if I was starting from scratch. I can't seem to integrate it into my existing code, because the whole thing's one big lump issued as an argument when calling \begin{tikzpicture}[...]. Does that make sense? I already spent a bunch of time getting everything else the way I like it. I already have all the nodes coordinated and ready to go, shapes and bullets set, etc. It's a lot more complex than what you see here. I've stripped it down to it's bare bones to create an MWE. Jul 11 '19 at 10:08
• I tried to guess what you want and adapted it. Not sure, if I guessed correctly. You could move some parts into the macro (e.g. the definition of the (top) coordinate which is currently outside. Also not sure how flexibly you want to connect two permutations. If it is always top and bottom row, you could modify the macro accordingly. Jul 11 '19 at 11:07
• @tjt263: I added the grouping - I think most parts of your desired picture are there now - except the very custom edges that go around the image. I am not sure, if there is a general programmatic way to implement this. Adding additional outputs should be straightforward, just add them as seperate lines (e.g. \draw[cross line=5pt](B1-1) -- (T1-5);) Jul 12 '19 at 8:46