11

Are there any LaTeX package that draws/plots/generates a matrix from a "graph" description (node relations). Any type of description/language could be useful. The trick is to generate automaticly a matrix from a description of "relations".

For example, from the following "mathematical" relation:

(1, 1)
(1, 2)
(1, 5)
(2, 1)
(2, 3)
(2, 5)
(3, 2)
(3, 4)
(4, 3)
(4, 5)
(4, 6)
(5, 1)
(5, 2)
(5, 4)
(6, 4)

which represents the next graph:

An example graph

to get the following matrix representation of this relation:

Matrix representation

Are there packages available for LaTeX to accomplish this type of "translation"? (from the mathematical relation to the matrix representation of its corresponding graph).

2
  • 1
    Could you be a little more specific as to what description language is?
    – Scott H.
    Commented Nov 9, 2012 at 3:37
  • I'have edited the ask. Perhaps now I explain it better.
    – ABu
    Commented Nov 9, 2012 at 10:35

3 Answers 3

10

Here is one way to do it (with much room for improvement). Given a list of node relations:

\newcommand{\NodeRelations}{%
    (1,1),
    (1,2),
    (1,5),
    (2,1),
    (2,3),
    (2,5),
    (3,2),
    (3,4),
    (4,3),
    (4,5),
    (4,6),
    (5,1),
    (5,2),
    (5,4),
    (6,4)
}%

you use the MyMatrix environment and pass in dummy data:

\begin{MyMatrix}{\NodeRelations}
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
\end{MyMatrix}

which gets replaced with a 1 or 0:

enter image description here

Code:

\documentclass{article}
\usepackage{pgffor}
\usepackage{xstring}
\usepackage{collcell}
\usepackage{etoolbox}

\newcounter{RowCount}
\newcounter{ColCount}

\newcommand*{\NextColumn}[1]{%
    \addtocounter{ColCount}{1}%
    \IfIsInNodeRelations{\arabic{ColCount}}{\arabic{RowCount}}{1}{0}%
}
\newcommand*{\FirstColumn}[1]{%
    \setcounter{ColCount}{0}%
    \addtocounter{RowCount}{1}%
    \NextColumn{#1}%
}


\newtoggle{IsInNodeRelations}
\newcommand*{\IfIsInNodeRelations}[4]{%
    \global\togglefalse{IsInNodeRelations}%
    \foreach \Node in \NodeRelations {%
        \StrBetween{\Node}{(}{,}[\x]
        \StrBetween{\Node}{,}{)}[\y]
        \IfEq{\x}{#1}{%
            \IfEq{\y}{#2}{%
                \global\toggletrue{IsInNodeRelations}%
                \breakforeach%
            }{}%
        }{}%
    }%
    \iftoggle{IsInNodeRelations}{#3}{#4}
}

\newcommand{\NodeRelations}{%
    (1,1),
    (1,2),
    (1,5),
    (2,1),
    (2,3),
    (2,5),
    (3,2),
    (3,4),
    (4,3),
    (4,5),
    (4,6),
    (5,1),
    (5,2),
    (5,4),
    (6,4)
}%

\newcommand*{\ExtractedX}{}%
\newcommand*{\ExtractedY}{}%
\def\ExtractXY(#1,#2){%
    \xdef\ExtractedX{#1}%
    \xdef\ExtractedY{#2}%
}%

\newenvironment{MyMatrix}[1]{%
    % #1 = List of node relations
    \newcolumntype{F}{>{\collectcell\FirstColumn}c<{\endcollectcell}}%
    \newcolumntype{C}{>{\collectcell\NextColumn}c<{\endcollectcell}}%
    \setcounter{RowCount}{0}
    \setcounter{ColCount}{0}
    $\left[\begin{array}{F C C C C C}%
}{%
    \end{array}\right]$%
}%

\begin{document}


\begin{MyMatrix}{\NodeRelations}
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
    - & - & - & - & - & - \\
\end{MyMatrix}
\end{document}
1
  • Wow! Thank you so much. You, and in general users in this forum, impress me.
    – ABu
    Commented Nov 9, 2012 at 16:03
9

Looks like Peter has you covered, so here's a LuaLaTeX option just for comparison. You can pass the pairs directly, or store them in a macro and pass that. I left the option of using whichever array environment you prefer (i.e. bmatrix, array, pmatrix etc.) I'm certain that my Lua could be improved, but this does the trick anyway :)

enter image description here

\documentclass{article}
\usepackage{luacode}
\usepackage{mathtools}

\begin{luacode*}
function adjmat(lst)
    local edges = {}
    local n = 0
    for hit in string.gmatch(lst,"%b()") do
        for r,c in string.gmatch(hit, "%((.+),(.+)%)") do
            edges[tonumber(r)] = edges[tonumber(r)] or {}
            edges[tonumber(r)][tonumber(c)] = 1
            n = math.max(n,r,c)
        end
    end
    for i = 1,n do
        local mat = ""
        edges[i] = edges[i] or {}
        for j = 1,n do
            edges[i][j] = edges[i][j] or 0
            mat = mat..edges[i][j].."&" 
        end
        tex.sprint(string.sub(mat,1,-2))
        tex.print("\\\\")
    end
end
\end{luacode*}

\def\adjmat#1{\directlua{adjmat("\luatexluaescapestring{#1}")}}

\begin{document}

\def\mymattwo{(1, 1),(1, 2),(1, 5),(2, 1),(2, 3),(2, 5),(3, 2),(3, 4),(4, 3),(4, 5),(4, 6),(5, 1),(5, 2),(5, 4),(6, 4)}

\[
    \begin{bmatrix}
        \adjmat{(1,1),(3,2),(4,5),(1,1),(2,1)}
    \end{bmatrix}
\]

\[
    \begin{bmatrix}
        \adjmat\mymattwo
    \end{bmatrix}
\]

\end{document}
4

Here's an expl3 implementation:

\documentclass{article}
\usepackage{xparse,amsmath}
\ExplSyntaxOn
\NewDocumentCommand{\definegraph}{ o m }
 {
  \IfNoValueTF{#1}
   {
    \graphs_define:nn { default } { #2 }
   }
   {
    \msg_term:n { I'm~(re)defining~graph~#1 }
    \graphs_define:nn { #1 } { #2 }
   }
 }

\tl_new:N \l__graphs_temp_tl
\seq_new:N \l__graphs_temp_seq
\int_new:N \l__graphs_count_int
\prop_new:N \g_graphs_list_prop

\cs_new_protected:Npn \graphs_define:nn #1 #2
 {
  % remove spaces
  \tl_set:Nn \l__graphs_temp_tl { #2 }
  \tl_remove_all:Nn \l__graphs_temp_tl { ~ }
  % store the list of edges
  \prop_gput:NnV \g_graphs_list_prop { #1 edges } \l__graphs_temp_tl
  % get the number of nodes
  \tl_remove_all:Nn \l__graphs_temp_tl { ( }
  \tl_remove_all:Nn \l__graphs_temp_tl { ) }
  \tl_replace_all:Nnn \l__graphs_temp_tl { , } { ; }
  \seq_set_split:NnV \l__graphs_temp_seq { ; } \l__graphs_temp_tl
  \seq_remove_duplicates:N \l__graphs_temp_seq
  % store the number of nodes
  \prop_gput:Nnx \g_graphs_list_prop { #1 length } { \int_to_arabic:n { \seq_count:N \l__graphs_temp_seq } }
 }

\NewDocumentCommand{\adjacencymatrix} { O{default} }
 {
  \graphs_build_adjmatr:n { #1 }
 }
\cs_new:Npn \graphs_build_adjmatr:n #1
 {
  % retrieve the list of nodes
  \prop_get:NnN \g_graphs_list_prop { #1 edges } \l__graphs_temp_tl
  % split the list into a sequence
  \seq_set_split:NnV \l__graphs_temp_seq { ; } \l__graphs_temp_tl
  % retrieve the number of nodes
  \int_set:Nn \l__graphs_count_int { \prop_get:Nn \g_graphs_list_prop { #1 length } }
  % start building the matrix
  \tl_clear:N \l__graphs_temp_tl
  % the outer loop indexes the rows, the inner loop the columns
  \int_step_inline:nnnn { 1 } { 1 } { \l__graphs_count_int }
   {
    \int_step_inline:nnnn { 1 } { 1 } { \l__graphs_count_int }
     { \__graphs_put_coeff:nn { ##1 } { ####1 } }
    \tl_put_right:Nn \l__graphs_temp_tl { \\ }
   }
  \begin{bmatrix}\l__graphs_temp_tl\end{bmatrix}
 }
% An auxiliary function for the lower level code:
% add 1 if the edge is listed, add 0 otherwise;
% then place the & if not in the last column.
\cs_new:Npn \__graphs_put_coeff:nn #1 #2
 {
  \seq_if_in:NnTF \l__graphs_temp_seq { (#1,#2) }
   { \tl_put_right:Nn \l__graphs_temp_tl { 1 } }
   { \tl_put_right:Nn \l__graphs_temp_tl { 0 } }
  \int_compare:nT { #2 < \l__graphs_count_int }
   { \tl_put_right:Nn \l__graphs_temp_tl { & } }
 }

\ExplSyntaxOff

\begin{document}

\definegraph{(1,2);(1,3);(2,3);(4,5)}

$\adjacencymatrix$

\definegraph{(1,2);( 1,3) ;(2 , 3); ( 4 , 5)} % spaces are insignificant

$\adjacencymatrix$

\definegraph[IK]{
    (1,1);
    (1,2);
    (1,5);
    (2,1);
    (2,3);
    (2,5);
    (3,2);
    (3,4);
    (4,3);
    (4,5);
    (4,6);
    (5,1);
    (5,2);
    (5,4);
    (6,4)
}

$\adjacencymatrix[IK]$
\end{document}

You can define and store any number of graph specifications. It would be possible to use a comma also to separate edge specifications, but I believe it's better to keep a distinction (more structure = more information).

enter image description here

1
  • Note that as of September 2014, \prop_get:Nn has been renamed to \prop_item:Nn
    – egreg
    Commented Sep 17, 2014 at 17:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .