16

A similar question is here. If we give all the data of the vertices's coordinates and the graph's adjacency matrix, how to plot it with tikz, pstrick or other tool in tex?

here are the data of adjacency matrix and coordinates

Coordinates:{{0.809,0.588},{0.309,0.951},{-0.309,0.951},{-0.809,0.588},{-1.,0.},{-0.809,-0.588},{-0.309,-0.951},{0.309,-0.951},{0.809,-0.588},{1.,0.}}

Adjacency matrix: {{0,1,0,0,1,0,1,0,0,1},{1,0,1,0,0,1,0,1,0,0},{0,1,0,1,0,0,1,0,1,0},{0,0,1,0,1,0,0,1,0,1},{1,0,0,1,0,1,0,0,1,0},{0,1,0,0,1,0,1,0,0,1},{1,0,1,0,0,1,0,1,0,0},{0,1,0,1,0,0,1,0,1,0},{0,0,1,0,1,0,0,1,0,1},{1,0,0,1,0,1,0,0,1,0}}

Sometimes the number of data is too large to put the data in the main tex file. It would be better to import the data from external files. Here is a large file of data.

  • What do you mean by "all the data of the vertices' coordinates"? It sounds like your looking at a specific matrix rather than an arbitrary matrix. The adjacency matrix is all that's needed to construct the graph. – DJP Sep 20 '14 at 6:23
  • Thanks! When we plot a graph, we plot each point according to its coordinate and then link them according to the adjacency matrix. So, coordinates of vertex are also needed. – user25607 Sep 20 '14 at 7:16
  • 1
    Please post a sample of the adjacency matrix here and not in a link. – Peter Grill Sep 24 '14 at 4:53
  • @PeterGrill The adjacency matrix is too large. – user25607 Sep 24 '14 at 7:30
  • 1
    Then make it smaller. Make it a minimal working example. – Sean Allred Sep 24 '14 at 12:05
16
+100

A simple solution requiring only TikZ

\documentclass[tikz]{standalone}

\begin{document}
\begin{tikzpicture}[scale=2,vertex/.style={draw,circle}, arc/.style={draw,thick,->}]
    \foreach [count=\i] \coord in {(0.809,0.588),(0.309,0.951),(-0.309,0.951),(-0.809,0.588),(-1.,0.),(-0.809,-0.588),(-0.309,-0.951),(0.309,-0.951),(0.809,-0.588),(1.,0.)}{
        \node[vertex] (p\i) at \coord {\i};
    }

    \foreach [count=\r] \row in {{0,1,0,0,1,0,1,0,0,1},{1,0,1,0,0,1,0,1,0,0},{0,1,0,1,0,0,1,0,1,0},{0,0,1,0,1,0,0,1,0,1},{1,0,0,1,0,1,0,0,1,0},{0,1,0,0,1,0,1,0,0,1},{1,0,1,0,0,1,0,1,0,0},{0,1,0,1,0,0,1,0,1,0},{0,0,1,0,1,0,0,1,0,1},{1,0,0,1,0,1,0,0,1,0}}{
        \foreach [count=\c] \cell in \row{
            \ifnum\cell=1%
                \draw[arc] (p\r) edge (p\c);
            \fi
        }
    }
\end{tikzpicture}
\end{document}

This can obviously be wrapped into a macro accepting the adjacency matrix as argument.

The same idea could be used to generate the edges in a graph description parseable by the PGF3 graph library (requires LuaTeX).

Here's a "macroed" version handling the weighted case with customisable styles:

\documentclass[tikz]{standalone}

\newcommand{\graphfromadj}[3][arc/.try]{
    \foreach [count=\r] \row in {#3}{
        \foreach [count=\c] \cell in \row{
            \ifnum\cell=1%
                \draw[arc/.try=\cell, #1] (#2\r) edge (#2\c);
            \fi
        }
    }
}

\newcommand{\weigthgraphfromadj}[3][draw,->]{
    \foreach [count=\r] \row in {#3}{
        \foreach [count=\c] \cell in \row{
            \if0\cell%
            \else
                \draw[arc/.try=\cell, #1] (#2\r) edge node[arc label/.try=\cell]{\cell} (#2\c);
            \fi
        }
    }
}

\begin{document}
\begin{tikzpicture}[scale=5,
    vertex/.style={draw,circle},
    arc/.style={draw=blue!#10,thick,->},
    arc label/.style={fill=white, font=\tiny, inner sep=1pt}
    ]
    \foreach [count=\i] \coord in {(0.809,0.588),(0.309,0.951),(-0.309,0.951),(-0.809,0.588),(-1.,0.),(-0.809,-0.588),(-0.309,-0.951),(0.309,-0.951),(0.809,-0.588),(1.,0.)}{
        \node[vertex] (p\i) at \coord {\i};
    }

    \weigthgraphfromadj[bend left=10]{p}{{0,5,0,0,1,0,5,0,0,5},{2,0,1,0,0,5,0,2,0,0},{0,5,0,2,0,0,2,0,5,0},{0,0,7,0,5,0,0,2,0,5},{7,0,0,7,0,5,0,0,1,0},{0,5,0,0,2,0,5,0,0,1},{2,0,5,0,0,1,0,5,0,0},{0,7,0,5,0,0,2,0,1,0},{0,0,5,0,7,0,0,5,0,1},{5,0,0,5,0,1,0,0,1,0}}
\end{tikzpicture}
\end{document}

preview

To handle self-loops a simple if could be used, and proper styling could adjust the settings (even per-node):

\newcommand{\graphfromadj}[3][]{
    \foreach [count=\r] \row in {#3}{
        \foreach [count=\c] \cell in \row{
            \ifnum\cell>0%
                \ifnum\c=\r%
                    \draw[arc/.try=\cell] (#2\r) edge[loop arc/.try=\r] (#2\c);
                \else
                    \draw[arc/.try=\cell, #1] (#2\r) edge (#2\c);
                \fi
            \fi
        }
    }
}

Detecting bi-directional edges and drawing them differently is more difficult with this approach.

Importing from an external file

Here's a possible approach using catchfile which assumes the data is in the file demo.dat

\documentclass[tikz]{standalone}
\usepackage{catchfile}

\newcommand{\graphfromadj}[3][]{
    \foreach [count=\r] \row in #3{
        \foreach [count=\c] \cell in \row{
            \ifnum\cell>0%
                \ifnum\c=\r%
                    \draw[arc/.try=\cell] (#2\r) edge[loop arc/.try=\r] (#2\c);
                \else
                    \draw[arc/.try=\cell, #1] (#2\r) edge (#2\c);
                \fi
            \fi
        }
    }
}

\CatchFileDef{\mymat}{demo.dat}{\endlinechar=-1 }

\begin{document}
\begin{tikzpicture}[
        scale=5,
        vertex/.style={draw,circle},
        arc/.style={draw=blue,thick,->},
        arc label/.style={fill=white, font=\tiny, inner sep=1pt},
        loop arc/.style={in=20,out=70,loop,min distance=.8mm}
    ]

    \foreach [count=\i] \coord in {(0.809,0.588),(0.309,0.951),(-0.309,0.951),(-0.809,0.588),(-1.,0.),(-0.809,-0.588),(-0.309,-0.951),(0.309,-0.951),(0.809,-0.588),(1.,0.)}{
        \node[vertex] (p\i) at \coord {\i};
    }

    \graphfromadj[bend left=10]{p}{\mymat}
\end{tikzpicture}
\end{document}
  • This was my first thought (and almost exactly the same code). But self-loops don't work (although they aren't required by the OPs example) and without considerable extra work it isn't possible to optimise two "uni-directional" edges into a single "bi-directional" edge. – Mark Wibrow Sep 25 '14 at 9:15
  • 1
    @MarkWibrow Very good points! However people looking for a solution handling simple cases could find this useful? – Bordaigorl Sep 25 '14 at 9:23
  • @MarkWibrow I added support for self-loops. bi-directional edges are still treated as two separate edges. – Bordaigorl Sep 25 '14 at 9:41
  • 1
    What about showing us what you are trying in a new question so I can give you a solution there? – Bordaigorl Sep 25 '14 at 11:49
  • 1
    @Bordaigorl I agree, the simple version is certainly useful. Unfortunately, posters' are rarely satisfied with the simple solution and (as in this question) sequentially (or randomly) update their requirements. – Mark Wibrow Sep 25 '14 at 12:01
9

enter image description here

Consider using Asymptote (part of TeXLive distribution), it is perfectly suited for such tasks. Here is a brief MWE to draw wiki example with added loop to the node 5. This code use three main inputs: adjacency matrix adj, a list of coordinates pair[] vcenter and a list of self-loops directions (in degrees) real[] SelfLoopDir.

// gmx.asy
//
settings.tex="pdflatex";
size(4cm);
import graph;
import fontsize;

defaultpen(fontsize(9pt));

texpreamble("\usepackage{lmodern}");

pair[] vcenter={
(120,130),
(60,250),
(100,380),
(230,360),
(200,220),
(340,430),
};

typedef int[][]Matrix;

Matrix adj={
{1, 1,  0,  0,  1,  0,},
{1, 0,  1,  0,  1,  0,},
{0, 1,  0,  1,  0,  0,},
{0, 0,  1,  0,  1,  1,},
{1, 1,  0,  1,  1,  0,},
{0, 0,  0,  1,  0,  0,},
};

real[] SelfLoopDir={-50,0,0,0,124,0};

int n=vcenter.length;

assert(n==adj.length && n==adj[0].length && n==SelfLoopDir.length,"Inconsistent input data. ");

real nodeR=40;
guide nodeShape=scale(nodeR)*unitcircle;

guide loop=(0,0){dir(-60)}..(nodeR*1.8,0)
           ..{dir(180+60)}cycle;

pen edgePen=orange+1bp;
pen nodeFgPen=deepblue+0.8bp;
pen nodeBgPen=lightgreen+0.8bp;

void drawNode(pair c){

  filldraw(shift(c.x,c.y)*nodeShape,nodeBgPen,nodeFgPen);
}

void drawEdge(int i, int j){ 
  pair p=vcenter[i], q=vcenter[j]; 
  if(i==j){
    draw(shift(p.x,p.y)*rotate(SelfLoopDir[i])*loop, edgePen);
  }else {
    draw(p--q, edgePen);
  }
}

void drawEdges(Matrix A){
  for(int i=0;i<n;++i){
    for(int j=0;j<=i;++j){
      if(A[i][j]>0){
        drawEdge(i,j);
      }
    }
  }
};

drawEdges(adj);

for(int i=0;i<vcenter.length;++i){
  drawNode(vcenter[i]);
}

for(int i=0;i<n;++i){
  label("$n_{"+string(i+1)+"}$",vcenter[i]);
}

Process this code with asy gmx.asy, it will run pdflatex to make all the labels and combine them along with the graphics into gmx.pdf.

The code can me modified and enhanced in many ways, for example to read the data from file or to make a special class to draw the thing.

  • Thanks so much! This is just what I want. Could you enhance it as much as possible? This is an excellent answer. – user25607 Sep 22 '14 at 14:09
  • @user25607: Well, it can be modified ad infinitum. Do you need something specific to start with? – g.kov Sep 22 '14 at 15:47
  • For example to read the data from file or to make a special class to draw the thing. – user25607 Sep 23 '14 at 1:33
  • @user25607 It would have really helped the question in general if you had provided a proper MWE that included this input format… there are so many ways the data could be represented – Sean Allred Sep 24 '14 at 3:15
  • How to import the data of coordinates and adjacency matrix from external files? – user25607 Sep 25 '14 at 9:45
8

This is a sagetex approach, which gives you access to a computer algebra system, Sage, plus the Python language. There are two ways to use this package: install Sage on your computer and integrate it with LaTeX. Not such a problem in Linux but maybe troublesome with other operating systems. The second way is to sign up for the free SageMath Cloud account which has everything set up for you. All you'll have to do is copy/paste the code below to get up and running. Modifying the code wouldn't be difficult but there's a ton of documentation for Sage/graphs/LaTeX to wade through depending on how particular you are on the output. I've put some key links below.

Your comment (above) indicated you needed the coordinates in order to "plot each point according to its coordinate and then link them according to the adjacency matrix". Using Sage, those coordinates aren't necessary. The Graph Format section gives you 6 ways of getting a graph into Sage. I'll use a matrix and for the second graph I'll take advantage of Sage's extensive graph theory knowledge to get the Petersen graph.

\documentclass{article}
\usepackage{xcolor}
\usepackage{fullpage}% to get the URL in the margins
\usepackage{sagetex}
\usepackage{tikz}
\usepackage{tkz-graph,tkz-berge}
\usetikzlibrary{arrows,shapes}
\begin{document}
\begin{sagesilent}
M = Matrix([(-1,0,0,0,1,0,0,0,0,0,-1,0,0,0,0), \
(1,-1,0,0,0,0,0,0,0,0,0,-1,0,0,0),(0,1,-1,0,0,0,0,0,0,0,0,0,-1,0,0), \
(0,0,1,-1,0,0,0,0,0,0,0,0,0,-1,0),(0,0,0,1,-1,0,0,0,0,0,0,0,0,0,-1), \
(0,0,0,0,0,-1,0,0,0,1,1,0,0,0,0),(0,0,0,0,0,0,0,1,-1,0,0,1,0,0,0), \
(0,0,0,0,0,1,-1,0,0,0,0,0,1,0,0),(0,0,0,0,0,0,0,0,1,-1,0,0,0,1,0), \
(0,0,0,0,0,0,1,-1,0,0,0,0,0,0,1)])
g = Graph(M)
g.set_pos(g.layout_circular())
g.set_latex_options(graphic_size=(4,4), tkz_style = 'Custom',vertex_size = 0.2,    edge_thickness = 0.04, edge_color = 'black',vertex_labels=False)
\end{sagesilent}
The work done in \textbf{sagesilent} is invisible to us. When we're 
ready to view the graph we can insert it as follows:\\
\begin{center}
\sage{g}
\end{center}
Of course, you can alter the size of the figure by adjusting the 
numbers in \verb!graphic_size=(4,4)! to a different dimension. 
Likewise, other parameters can be adjusted above. There is an 
extensive list of plotting options. See the Sage URL:
\begin{verbatim}
http://www.sagemath.org/doc/reference/plotting/sage/graphs/graph_plot.html
\end{verbatim}
\begin{sagesilent}
from sage.graphs.graph_latex import check_tkz_graph
check_tkz_graph() # random - depends on TeX installation
h = graphs.PetersenGraph()
h.set_latex_options(graphic_size=(4.3,4.3), tkz_style = 'Art',vertex_size = 0.2, edge_thickness = 0.04,vertex_labels=False)
\end{sagesilent}
\begin{center}
\sage{h}
\end{center}
\end{document}

Here's the output from Sagemath Cloud:enter image description here

Notice a several things. First, using the matrix approach, graph g was set using a circular layout, so I didn't need the points you mentioned above. There are of course, other layout settings. Second, you can set latex options for the output. Plot options are mentioned here Third, Sage has knowledge of a wide variety of graph structures. To get the well known Petersen graph, all I do is define graph h to be the Petersen graph and Sage handles the placement of vertices by itself. You could have forced a circular layout of Petersen's graph if you were interested in showing a different looking graph which is isomorphic to it. Fourth, notice I specified tkz_style = 'Art' and got graph output which is using the LaTeX package tkz-graph. Sage has lots of LaTeX support.

Using sagetex gets us away from a pure LaTeX approach but gives a quick way of efficiently turning out all sorts of graphs. So this approach isn't using the point plotting approach you asked for but hopefully you can see the benefits of using Sage: imagine the extra difficulty you'd have of setting the points for plotting the Petersen graph in the standard representation shown or, in fact, any graph with a lot of vertices. AskSage is a place to go if you have questions using Sage.

Regarding your comment for an example where the user specifies the coordinates you can try:

\documentclass{article}
\usepackage{xcolor}
\usepackage{fullpage}% to get the URL in the margins
\usepackage{sagetex}
\usepackage{tikz}
\usepackage{tkz-graph,tkz-berge}
\usetikzlibrary{arrows,shapes}
\begin{document}
\begin{sagesilent}
M = [[0,1,1,1,1], [1,0,1,1,1],[1,1,0,1,1],[1,1,1,0,1],
[1,1,1,1,0]]
vertices = ['A','B','C','D','E']

N = 5
output = ""
output += r"\begin{tikzpicture}"
output += r"\GraphInit[vstyle=Classic]"

#Create the vertices
for p in range(0,N):
    output += r"\Vertex[x=0,y=0,Lpos=-180]{A}"
    output += r"\Vertex[x=2,y=0,Lpos=-90]{B}"
    output += r"\Vertex[x=2,y=2,Lpos=90]{C}"
    output += r"\Vertex[x=1,y=4,Lpos=-180]{D}"
    output += r"\Vertex[x=5,y=1,Lpos=0]{E}"

#Create the edges
for i in range(0,N):
    for j in range(i,N):
        if M[i][j]==1:
            output += r"\Edge(%s)(%s)"%(vertices[i],vertices[j])
output += r"\end{tikzpicture}"
\end{sagesilent}
If you want to control the positioning, then there is no 
need to work with either a (Sage) matrix or a (Sage) graph structure.
Just specify the position of the vertices along with the 
label position just read throught the top half of the matrix
(since the matrix of every graph is symmetric).
\begin{center}
\sagestr{output}
\end{center}
Sage gives you the flexibility to choose the approach that you
think is best.
\end{document}

The output is:enter image description here

  • Thanks! I know something about sage. In fact, I have draw 120-cell in Sage and Mathematica. But the former is not as better as the later. So I want to give the coordinates manually. Sometimes there maybe no existing layouts is what I want. – user25607 Sep 21 '14 at 1:36
  • 1
    I would imagine that the Python which comes with Sage could help with that (like the answer to the question you linked to). – DJP Sep 21 '14 at 15:34
  • Could you give an example? – user25607 Sep 24 '14 at 1:23
  • OK: Example appended to previous answer. Python looping to figure out which edges are needed. – DJP Sep 24 '14 at 4:34
  • Thanks! Though this works on sagemath-cloud, but I can not make it on my local computer. I failed to configure sagetex. – Eden Harder Sep 24 '14 at 13:21
8

Possibly not robust for "serious business" and it does not handle self-loops very well (you have to specify a number greater than one that corresponds to a predefined "loop" style - some examples are given). In addition, "edge" styles are provided to customise non-loop edges.

Also it requires lualatex. By specifying coordinates using braces rather than parentheses, it is trivial to convert data structures into lua tables.

Also keys are provided to load the require data from files.

Note that the graph drawing library circular provides automatic layouts (e.g., simple necklace layout) than means that the required circular arrangement of nodes could be drawn without specific coordinates as shown in the second (blue) graph (although note that the nodes are in a different order):

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{graphs,graphdrawing,arrows.meta}

\usegdlibrary{circular}
\tikzset{%
  edge 1/.style={>=Stealth},
  loop 1/.style={},
  loop 2/.style={loop above},
  loop 3/.style={loop below},
  loop 4/.style={loop left},
  loop 5/.style={loop right},
} 
\def\luafiletomacro#1#2{%
  \edef#2{%
    \directlua{%
      file = io.open("#1", "r")
      data = file:read("*all")
      file:close()
      tex.print(data)
    }%
  }%
}
\tikzgraphsset{% 
  n/.store in=\n,
  n = 1,
  adjacency matrix/.store in=\tikzadjacencymatrix,
  adjacency matrix from file/.code={\luafiletomacro{#1}{\tikzadjacencymatrix}},
  vertices/.store in=\tikzvertices,
  vertices={},  
  vertices from file/.code={\luafiletomacro{#1}{\tikzvertices}},
  declare={adjacency graph}{[
    /utils/exec={\edef\adjacencygraph{%
      \directlua{%
         local i, j, n, v
         local vertices = {\tikzvertices}
         local matrix = {\tikzadjacencymatrix}
         local graph_spec = ""
         n = 0
         for i, vertex in pairs(vertices) do
            x = vertex[1]
            y = vertex[2]
            n = n + 1
            graph_spec = graph_spec .. " " .. n .. 
              "[at={(" .. x .. "," .. y .. ")}];"
         end
         if n == 0 then
           n = \n\space
           for i = 1,n do
              if i > 1 then
                graph_spec = graph_spec .. ","
              end
              graph_spec = graph_spec .. " " .. i 
           end
         end
         graph_spec = graph_spec .. ";"
         for i = 1,n do
           for j = 1,i do
             v = matrix[i][j]
             if v > 0 then
               if i == j then
                 graph_spec = graph_spec .. " " .. i .. 
                   " ->[/tikz/loop " .. v .. "/.try]" .. i .. "; "
               else
                 if matrix[j][i] == 1 then
                   graph_spec = graph_spec .. " " .. i .. 
                     " <->[/tikz/edge " .. v .. "/.try]" .. j .. "; "
                 else
                   graph_spec = graph_spec .. " " .. i .. 
                     " ->[/tikz/edge " .. v .. "/.try]" .. j .. "; "
                 end
               end
             end
           end
         end
         tex.print(graph_spec)  
      }%    
    }},
    parse/.expand once=\adjacencygraph
  ]}%  
}
\begin{document}
\begin{tikzpicture}[x=2cm,y=2cm]
\graph [nodes={circle, draw}, no placement] {

   adjacency graph[
     vertices={{0.809,0.588},{0.309,0.951},{-0.309,0.951},{-0.809,0.588},
       {-1.,0.},{-0.809,-0.588},{-0.309,-0.951},{0.309,-0.951},
       {0.809,-0.588},{1.,0.}},
     adjacency matrix={%
      {0,1,0,0,1,0,1,0,0,1},
      {1,0,1,0,0,1,0,1,0,0},
      {0,1,0,1,0,0,1,0,1,0},
      {0,0,1,0,1,0,0,1,0,1},
      {1,0,0,1,0,1,0,0,1,0},
      {0,1,0,0,1,0,1,0,0,1},
      {1,0,1,0,0,1,0,1,0,0},
      {0,1,0,1,0,0,1,0,1,0},
      {0,0,1,0,1,0,0,1,0,1},
      {1,0,0,1,0,1,0,0,1,0}
     }];
};

\tikzset{shift=(270:2), edge 1/.style={draw=blue}}
\graph [nodes={circle, draw}, simple necklace layout, node distance=1.25cm] {

    adjacency graph[n=10, 
      adjacency matrix={%
       {0,1,0,0,1,0,1,0,0,1},{1,0,1,0,0,1,0,1,0,0},{0,1,0,1,0,0,1,0,1,0},
       {0,0,1,0,1,0,0,1,0,1},{1,0,0,1,0,1,0,0,1,0},{0,1,0,0,1,0,1,0,0,1},
       {1,0,1,0,0,1,0,1,0,0},{0,1,0,1,0,0,1,0,1,0},{0,0,1,0,1,0,0,1,0,1},
       {1,0,0,1,0,1,0,0,1,0}
      }];
 };

\end{tikzpicture}

\end{document}

enter image description here

  • Thanks! But I can not run it in my texlive with lualatex, though I download the needed pgflibrarygraphdrawing.code.tex and tikzlibrarygraphdrawing.code.tex. – user25607 Sep 24 '14 at 13:12
  • @user25607 you need much more than those two files. Rather than downloading individual files, it would be easier to upgrade to PGF 3.0 manually or simply to upgrade to the latest texlive release. – Mark Wibrow Sep 24 '14 at 15:12
  • How to import the data of coordinates and adjacency matrix from external files? – user25607 Sep 25 '14 at 9:45
  • For example, we just put the datas in your example in vert.tex and adj.tex – user25607 Sep 25 '14 at 10:20
  • @user25607 it is trivial to load the data into a macro as my updated answer shows. – Mark Wibrow Sep 25 '14 at 11:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.