Consider I have a transfer function (polynomial fraction)
```
a_1*s^n + a_2*s^{n-1} + ... + a_{n-1}*s^1 + a_n
---------------------------------------------
s^N + b_1*s^{N-1} + ... + b_{N-1}*s^1 + b_N
```
where the denominator order `N` is greater than the numerator order `n`.
I would like to find a robust way to transform it to a matrix `\StateMat` representing the phase variable form of the state-space to have
```
\edef\StateMat{%
{ 0 , 1 , 0 , 0 , ... , 0 },%
{ 0 , 0 , 1 , 0 , ... , 0 },%
{ 0 , 0 , 0 , 1 , ... , 0 },%
% .........................
{ 0 , 0 , 0 , ... , 0 , 1 },%
{ -b_N , -b_{N-1} , ... , -b_1 },%
{ a_n , a_{n-1} , ... , 0 }%
}
```
where the matrix is square of dimensions `N+1`*`N+1`.
The expected input from my side is two matrices of the coefficients
```
\edef\NumCoeffMat{%
{ a_1 , a_2 , ... , a_n }%
}
```
and
```
\edef\DenCoeffMat{%
{ b_1 , b_2 , ... , b_N }%
}
```
---
# Edit (after marmot's answer)
This problem is a part of converting a transfer function into a state space representation for drawing the signal flow graphs introduced here https://topanswers.xyz/tex?q=1568#a1818
## Full MWE
```
\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{calc,decorations.markings,positioning,arrows.meta}
\usepackage{pgffor}
\makeatletter
\pgfkeys{/matrix games/.cd,
count/.code={\c@pgf@counta0\relax
\pgfkeys{/matrix games/step counta/.list/.expanded={#1}}%
\edef\pgfmathresult{\the\c@pgf@counta}%
},step counta/.code={\advance\c@pgf@counta by1\relax},
cells/.code={\pgfmathparse{#1}},nrows/.initial=5,ncols/.initial=5,
create matrix/.code={\edef\pgf@util@tempa{}%
\foreach \i in {1,...,\pgfkeysvalueof{/matrix games/nrows}}%
{\foreach \j in {1,...,\pgfkeysvalueof{/matrix games/ncols}}{%
\pgfkeys{/matrix games/cells}%
\ifnum\j=1\relax
\xdef\pgf@util@tempb{\pgfmathresult}%
\else
\xdef\pgf@util@tempb{\pgf@util@tempb,\pgfmathresult}%
\fi}%
\ifnum\i=1\relax
\xdef\pgf@util@tempa{{\pgf@util@tempb}}%
\else
\xdef\pgf@util@tempa{\pgf@util@tempa,{\pgf@util@tempb}}%
\fi}%
\let#1\pgf@util@tempa
}}
\makeatother
\begin{document}
\edef\NumCoeffMat{{2,24,34}}%
\edef\DenCoeffMat{{10,31,30}}%
\pgfkeys{/matrix games/.cd,count=\NumCoeffMat}%
\pgfmathtruncatemacro\myN{\pgfmathresult+1}% now \myN is 1 + the length of \NumCoeffMat
\pgfkeys{/matrix games/.cd,count=\DenCoeffMat}%
\pgfmathtruncatemacro\myM{\pgfmathresult}% now \myM is the length of \DenCoeffMat
\pgfkeys{/matrix games/.cd,ncols=\myN,nrows=\numexpr\myN+1,
cells/.code={
\pgfmathtruncatemacro{\icase}{(\i==\myN?1:0)+(\i==\myN+1?2:0)}%
\ifcase\icase% first case: zero unless \i+1=\j
\pgfmathparse{int(\i+1==\j?1:0)}%
\or% second case
\ifnum\j>\numexpr\myN-1\relax% subcase a) \j >=\myN
\pgfmathparse{1}%
\else% subcase b) \j <\myN (fill in \NumCoeffMat reversed)
\pgfmathparse{int(\NumCoeffMat[\myN-\j-1])}%
\fi
\or% third case
\ifnum\j<\numexpr\myM+1\relax% subcase a) \j <=\myM (fill in \DenCoeffMat reversed)
\pgfmathparse{int(\DenCoeffMat[\j-1])}%
\else% subcase b) \j > \myN (fill in zeros)
\pgfmathparse{int(0)}%
\fi
\fi},
create matrix=\StateMat}%
\begin{tikzpicture}[node distance = 15 mm,
amark/.style={
decoration={
markings,
mark=at position {0.5} with {
\arrow{stealth},
}
},
postaction={decorate,edge label={#1}}
}, % make the mark an entry of the \StateMat matrix
pmark/.code n args={2}{%
\pgfmathtruncatemacro{\myi}{int({\StateMat}[\numexpr#1][\numexpr#2])}%
\tikzset{suppress if 0=\myi,edge label={$\myi$},
amark}% <- changed
},
terminal/.style 2 args={draw,alias=ln,circle,inner sep=2pt,label={#1:#2}},
% semicircle path
semicircle/.style={to path={let \p1=($(\tikztotarget)-(\tikztostart)$)
in \ifdim\x1>0pt
(\tikztostart.north) arc[start angle=180,end angle=0,radius=0.5*\x1]
\else
(\tikztostart.south) arc[start angle=0,end angle=-180,radius=-0.5*\x1]
\fi
\tikztonodes}},
semicircle'/.style={to path={let \p1=($(\tikztotarget)-(\tikztostart)$)
in \ifdim\x1>0pt
(\tikztostart.south) arc[start angle=-180,end angle=0,radius=0.5*\x1]
\else
(\tikztostart.north) arc[start angle=0,end angle=180,radius=-0.5*\x1]
\fi
\tikztonodes}},
add key if/.code n args={3}{\pgfmathtruncatemacro{\itest}{(#1?0:1)}%
\ifcase\itest
\tikzset{#2}%
\else
\tikzset{#3}%
\fi
},suppress if 0/.style={add key if={#1==0}{opacity=0}{}}
]
\pgfmathtruncatemacro{\dimy}{dim({\StateMat})} % number of rows
\pgfmathtruncatemacro{\dimx}{dim({\StateMat}[0])} % number of columns
% create the graph
\path % R node
node[terminal={left}{$R(S)$},alias={X-\dimy}] (R) {}
% loop over matrix entries
foreach \Y [evaluate=\Y as \X using {int(\dimy-\Y)}]
in {1,...,\numexpr\dimy-1}
{node[right=of ln,terminal={below right}{% sX_i node
$sX_{\X}(s)$}](sX-\X){}
node[right=of ln,terminal={below right}{% X_i node
$X_{\X}(s)$}](X-\X){}
% ege from sX_i to X_i
(sX-\X) edge[amark,edge label={$\frac{1}{s}$}] (X-\X)
% edge from X_{i+1} to X_i (R had an alias)
(X-\the\numexpr\X+1) edge[pmark={\X-1}{\X}] (sX-\X)
% semicircle edge from X_i to sX_i
(X-\X) edge[semicircle,pmark={\X-1}{\X-1}] (sX-\X)
}
node[right=of ln,terminal={right}{$Y(s)$},alias=sX-\dimy](Y){}
(X-1) edge[pmark={\dimy-1}{0}] (Y)
% we are now done with the horizontal part,
% and have also dealt with the entries on the diagonal and
% the {i-1},i entries
% now we deal with the matrix entries
foreach \X in {1,...,\the\numexpr\dimx-1}
{% all edges that end in Y (excluding the horizontal one)
\ifnum\X>\numexpr1\relax
(X-\X) edge[pmark={\dimx-1}{\X-1},semicircle] (Y)
\fi
% all edges that start at R (excluding the horizontal one)
\ifnum\X<\numexpr\dimx-1\relax
(R) edge[pmark={\X-1}{\dimx-1},semicircle] (sX-\X)
\fi
% remaining semicircles
foreach \Y in {\the\numexpr\X+1,...,\dimy}
{% backwards pointing semicircles
\unless\ifnum\X\Y=1\dimy
(X-\X) edge[pmark={\Y-1}{\X-1},semicircle] (sX-\Y)
\fi
% forward pointing semicircles
\pgfextra{\pgfmathtruncatemacro{\itest}{(\Y<\dimy&&\Y-\X>1?1:0)}}
\ifnum\itest=\numexpr1\relax
(X-\Y) edge[swap,pmark={\X-1}{\Y-1},semicircle'] (sX-\X)
\fi
}
};
\end{tikzpicture}
\end{document}
```Not sure if this qualifies for an answer. It uses global macros (though hidden ones) etc. This can all be avoided with the `/.list` key handler but the overhead might be too confusing without an explicit manula so this has to wait.
However, I tried to add some explanations for what is going on so that one can apply this to similar scenarios. There are two steps:
1. count the entries of the two lists `\NumCoeffMat` and `\DenCoeffMat`. This can be done with the `/.list` key handler without introducing explicit global macros (thus falsifying the claims that the Ti*k*Z `foreach` cannot do that, even nesting is fine).
2. use this information to fill the matrix programmatically. (With the `/.list` key handler one can even build LaTeX matrices with `&` and so on, but spelling this out is not relevant for this post.)
```
\documentclass{article}
\usepackage{pgffor}
\makeatletter
\pgfkeys{/matrix games/.cd,
count/.code={\c@pgf@counta0\relax
\pgfkeys{/matrix games/step counta/.list/.expanded={#1}}%
\edef\pgfmathresult{\the\c@pgf@counta}%
},step counta/.code={\advance\c@pgf@counta by1\relax},
cells/.code={\pgfmathparse{#1}},nrows/.initial=5,ncols/.initial=5,
create matrix/.code={\edef\pgf@util@tempa{}%
\foreach \i in {1,...,\pgfkeysvalueof{/matrix games/nrows}}%
{\foreach \j in {1,...,\pgfkeysvalueof{/matrix games/ncols}}{%
\pgfkeys{/matrix games/cells}%
\ifnum\j=1\relax
\xdef\pgf@util@tempb{\pgfmathresult}%
\else
\xdef\pgf@util@tempb{\pgf@util@tempb,\pgfmathresult}%
\fi}%
\ifnum\i=1\relax
\xdef\pgf@util@tempa{{\pgf@util@tempb}}%
\else
\xdef\pgf@util@tempa{\pgf@util@tempa,{\pgf@util@tempb}}%
\fi}%
\let#1\pgf@util@tempa
}}
\makeatother
\begin{document}
\edef\NumCoeffMat{{1,7,2}}%
\edef\DenCoeffMat{{9,26,24}}%
\pgfkeys{/matrix games/.cd,count=\DenCoeffMat}%
\pgfmathtruncatemacro\myN{\pgfmathresult}% now \myN is the length of \NumCoeffMat
\pgfkeys{/matrix games/.cd,count=\NumCoeffMat}%
\pgfmathtruncatemacro\myM{\pgfmathresult}% now \myM is the length of \DenCoeffMat
\pgfkeys{/matrix games/.cd,ncols=\numexpr\myN+1,nrows=\numexpr\myN+1,
cells/.code={% we now tell the matrix what to do depending on the row index \i
% and the column index \j
% there are three cases:
% 1. \i<\myN (the diagonal block)
% 2. \i=\myN (the row filled with \NumCoeffMat reversed)
% 3. \i=\myN+1 (the row filled with \DenCoeffMat)
\pgfmathtruncatemacro{\icase}{(\i==\myN?1:0)+(\i==\myN+1?2:0)}%
\ifcase\icase% first case: zero unless \i+1=\j
\pgfmathparse{int(\i+1==\j?1:0)}%
\or% second case
\ifnum\j>\numexpr\myN\relax% subcase a) \j >=\myN
\pgfmathparse{1}%
\else% subcase b) \j <\myN (fill in \NumCoeffMat reversed)
\pgfmathparse{int(-1*(\DenCoeffMat[\myN-\j]))}%
\fi
\or% third case
\ifnum\j<\numexpr\myM+1\relax% subcase a) \j <=\myM (fill in \DenCoeffMat reversed)
\pgfmathparse{int(\NumCoeffMat[\myM-\j])}%
\else% subcase b) \j > \myN (fill in zeros)
\pgfmathparse{int(0)}%
\fi
\fi},
create matrix=\StateMat}%
\StateMat
\typeout{\StateMat}
\end{document}
```
Of course this can be used in other codes.
```
\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{calc,decorations.markings,positioning,arrows.meta}
\makeatletter
\pgfkeys{/matrix games/.cd,
count/.code={\c@pgf@counta0\relax
\pgfkeys{/matrix games/step counta/.list/.expanded={#1}}%
\edef\pgfmathresult{\the\c@pgf@counta}%
},step counta/.code={\advance\c@pgf@counta by1\relax},
cells/.code={\pgfmathparse{#1}},nrows/.initial=5,ncols/.initial=5,
create matrix/.code={\edef\pgf@util@tempa{}%
\foreach \i in {1,...,\pgfkeysvalueof{/matrix games/nrows}}%
{\foreach \j in {1,...,\pgfkeysvalueof{/matrix games/ncols}}{%
\pgfkeys{/matrix games/cells}%
\ifnum\j=1\relax
\xdef\pgf@util@tempb{\pgfmathresult}%
\else
\xdef\pgf@util@tempb{\pgf@util@tempb,\pgfmathresult}%
\fi}%
\ifnum\i=1\relax
\xdef\pgf@util@tempa{{\pgf@util@tempb}}%
\else
\xdef\pgf@util@tempa{\pgf@util@tempa,{\pgf@util@tempb}}%
\fi}%
\let#1\pgf@util@tempa
}}
\makeatother
\begin{document}
\edef\DenCoeffMat{{9,26,24}}%
\edef\NumCoeffMat{{1,7,2}}%
\pgfkeys{/matrix games/.cd,count=\DenCoeffMat}%
\pgfmathtruncatemacro\myN{\pgfmathresult}% now \myN is the length of \NumCoeffMat
\pgfkeys{/matrix games/.cd,count=\NumCoeffMat}%
\pgfmathtruncatemacro\myM{\pgfmathresult}% now \myM is the length of \DenCoeffMat
\pgfkeys{/matrix games/.cd,ncols=\numexpr\myN+1,nrows=\numexpr\myN+1,
cells/.code={% we now tell the matrix what to do depending on the row index \i
\pgfmathtruncatemacro{\icase}{(\i==\myN?1:0)+(\i==\myN+1?2:0)}%
\ifcase\icase% first case: zero unless \i+1=\j
\pgfmathparse{int(\i+1==\j?1:0)}%
\or% second case
\ifnum\j>\numexpr\myN\relax%
\pgfmathparse{1}%
\else%
\pgfmathparse{int(-1*(\DenCoeffMat[\myN-\j]))}%
\fi
\or% third case
\ifnum\j<\numexpr\myM+1\relax%
\pgfmathparse{int(\NumCoeffMat[\myM-\j])}%
\else%
\pgfmathparse{int(0)}%
\fi
\fi},
create matrix=\StateMat}%
\let\mmat\StateMat
\typeout{\StateMat}
\begin{tikzpicture}[node distance = 15 mm,
amark/.style={
decoration={
markings,
mark=at position {0.5} with {
\arrow{stealth},
}
},
postaction={decorate,edge label={#1}}
}, % make the mark an entry of the \mmat matrix
pmark/.code n args={2}{%
\pgfmathtruncatemacro{\myi}{int({\mmat}[\numexpr#1][\numexpr#2])}%
\tikzset{suppress if 0=\myi,edge label={$\myi$},
amark}% <- changed
},
terminal/.style 2 args={draw,alias=ln,circle,inner sep=2pt,label={#1:#2}},
% semicircle path
semicircle/.style={to path={let \p1=($(\tikztotarget)-(\tikztostart)$)
in \ifdim\x1>0pt
(\tikztostart.north) arc[start angle=180,end angle=0,radius=0.5*\x1]
\else
(\tikztostart.south) arc[start angle=0,end angle=-180,radius=-0.5*\x1]
\fi
\tikztonodes}},
semicircle'/.style={to path={let \p1=($(\tikztotarget)-(\tikztostart)$)
in \ifdim\x1>0pt
(\tikztostart.south) arc[start angle=-180,end angle=0,radius=0.5*\x1]
\else
(\tikztostart.north) arc[start angle=0,end angle=180,radius=-0.5*\x1]
\fi
\tikztonodes}},
add key if/.code n args={3}{\pgfmathtruncatemacro{\itest}{(#1?0:1)}%
\ifcase\itest
\tikzset{#2}%
\else
\tikzset{#3}%
\fi
},suppress if 0/.style={add key if={#1==0}{opacity=0}{}}
]
\pgfmathtruncatemacro{\dimy}{dim({\mmat})} % number of rows
\pgfmathtruncatemacro{\dimx}{dim({\mmat}[0])} % number of columns
\typeout{\dimy,\dimx}
% create the graph
\path % R node
node[terminal={left}{$R(S)$},alias={X-\dimy}] (R) {}
% loop over matrix entries
foreach \Y [evaluate=\Y as \X using {int(\dimy-\Y)}]
in {1,...,\numexpr\dimy-1}
{node[right=of ln,terminal={below right}{% sX_i node
$sX_{\X}(s)$}](sX-\X){}
node[right=of ln,terminal={below right}{% X_i node
$X_{\X}(s)$}](X-\X){}
% ege from sX_i to X_i
(sX-\X) edge[amark,edge label={$\frac{1}{s}$}] (X-\X)
% edge from X_{i+1} to X_i (R had an alias)
(X-\the\numexpr\X+1) edge[pmark={\X-1}{\X}] (sX-\X)
% semicircle edge from X_i to sX_i
(X-\X) edge[semicircle,pmark={\X-1}{\X-1}] (sX-\X)
}
node[right=of ln,terminal={right}{$Y(s)$},alias=sX-\dimy](Y){}
(X-1) edge[pmark={\dimy-1}{0}] (Y)
% we are now done with the horizontal part,
% and have also dealt with the entries on the diagonal and
% the {i-1},i entries
% now we deal with the matrix entries
foreach \X in {1,...,\the\numexpr\dimx-1}
{% all edges that end in Y (excluding the horizontal one)
\ifnum\X>\numexpr1\relax
(X-\X) edge[pmark={\dimx-1}{\X-1},semicircle] (Y)
\fi
% all edges that start at R (excluding the horizontal one)
\ifnum\X<\numexpr\dimx-1\relax
(R) edge[pmark={\X-1}{\dimx-1},semicircle] (sX-\X)
\fi
% remaining semicircles
foreach \Y in {\the\numexpr\X+1,...,\dimy}
{% backwards pointing semicircles
\unless\ifnum\X\Y=1\dimy
(X-\X) edge[pmark={\Y-1}{\X-1},semicircle] (sX-\Y)
\fi
% forward pointing semicircles
\pgfextra{\pgfmathtruncatemacro{\itest}{(\Y<\dimy&&\Y-\X>1?1:0)}}
\ifnum\itest=\numexpr1\relax
(X-\Y) edge[swap,pmark={\X-1}{\Y-1},semicircle'] (sX-\X)
\fi
}
};
\end{tikzpicture}
\end{document}
```
