5

There exists an elaborate thread about how to draw rooted trees in LaTeX, e.g. for use in natural language applications. There exist packages other than TikZ to do this, like qtree or forest. However, a common theme in all these trees is that the leaves eventually join together in one overarching root. There are applications where this isn't the case; one example is hierarchical segmentation like byte-pair encoding. Another could be cutting across an agglomerative clustering hierarchy.

The net result is that you get multiple trees, but the leaves of one tree must be aligned and the leaves of different trees must also be aligned. Here is a hypothetical example I drew in Paint for aggregating the letters of discombobulate, ending at a segmentation discom+bobulate:

BPE tokenisation tree for discombobulate

How would one reproduce this in LaTeX, given a list of merges? There are several freedoms:

  • You are allowed to choose any specification format for the merges/tree(s). The list of merges comes from a Python program anyway, so it doesn't take much to adapt to the format you think is easiest. I have a programmatic representation of the tree, so it's trivial to convert it to something like Newick format which is almost literal forest code.
  • You are free to choose the the height at which a non-leaf node is placed, as long as it is 1. discretised and 2. above its constituents.
  • If you prefer right-angled branches like in a dendrogram, that is fine too.

There are several requirements I would like a solution to have, to make it sufficiently general:

  • Should allow more than two constituents for one node (e.g. "d", "i", "s" merge together immediately into "dis" without passing through "is"). Hence, please don't use a package that only allows drawing binary trees.
  • Should have some control over horizontal and vertical compactness, i.e. how closely packed the layers are and how far the leaves are from each other.
  • Should allow turning off intermediate node names.

The latter would produce something like the following image:

byte-pair encoding merge tree without intermediate nodes


Note to editors: a better title to this question is always appreciated. I don't like the title I came up with.

3 Answers 3

4

By use of forest package:

  • with forked edge:
\documentclass[margin=3mm, varwidth]{standalone}
\usepackage[edges]{forest}

\begin{document}
    \begin{figure}[ht]
\forestset{
    LT/.style = {% Linguistic tree
delay={where content={}{shape=coordinate}{}},
where n children=0{tier=word, baseline}{},
    for tree={
   text height = 2ex,
   text depth  = 0.5ex,
    inner ysep = 0pt,
    inner xsep = 1pt,
        forked edge,    % for forked edge
         s sep = 1mm,   % sibling distance
          }}}
    
\begin{forest}  LT
[discom
    [
        [d]
            [
                [i]
                [s]
            ]
    ]
    [
        [c]
            [
                [o]
                [m]
            ]
    ]
]
\end{forest}
\quad
\begin{forest}  LT
[bubolate
    [
        [b]
            [
                [o]
                [b]
            ]
    ]
    [
        [
            [
                [u]
                [l]
            ]
        ]
        [
            [
                [a]
                [t]
            ]
            [e]
        ]
    ]
]
\end{forest}
    \end{figure}
\end{document}

enter image description here

  • as linguistic tree:
\documentclass[margin=3mm, varwidth]{standalone}
\usepackage[linguistics]{forest}

\begin{document}
    \begin{figure}[ht]
\forestset{
    LT/.style = {% Linguistic tree
delay={where content={}{shape=coordinate}{}},
where n children=0{tier=word, baseline}{},
    for tree={
   text height = 2ex,
   text depth  = 0.5ex,
    inner ysep = 0pt,
    inner xsep = 1pt,
         s sep = 1mm,   % sibling distance
          }}}
    
\begin{forest}  LT
[discom
    [
        [d]
            [
                [i]
                [s]
            ]
    ]
    [
        [c]
            [
                [o]
                [m]
            ]
    ]
]
\end{forest}
\quad
\begin{forest}  LT
[bubolate
    [
        [b]
            [
                [o]
                [b]
            ]
    ]
    [
        [
            [
                [u]
                [l]
            ]
        ]
        [
            [
                [a]
                [t]
            ]
            [e]
        ]
    ]
]
\end{forest}
    \end{figure}
\end{document}

enter image description here

Addendum:
In the case, when you like to have the same distance between letters at bottom of trees and between trees, you need first to define new command for this distance, for example

\tikz\pgfmathsetlength{\SD}{2mm}

and than replace

  • quad with \hskip \SD
  • s sep = ...˙with s sep = \SD + 1mm`

MWE:

\documentclass[margin=3mm, varwidth]{standalone}
\usepackage[linguistics]{forest}

\begin{document}
    \begin{figure}[ht]

\newcommand\SD{1 mm}        % <-------   
\forestset{
    LT/.style = {% Linguistic tree
delay={where content={}{shape=coordinate}{}},
where n children=0{tier=word, baseline}{},
    for tree={
   text height = 2ex,
   text depth  = 0.5ex, 
        draw,   % that distance are more evident/visible, remove in real document
    inner ysep = 0pt,
    inner xsep = 1pt,
         s sep = \SD + 1mm, % <------- 
          }}}
    
\begin{forest}  LT,
[discom
    [
        [d]
            [
                [i]
                [s]
            ]
    ]
    [
        [c]
            [
                [o]
                [m]
            ]
    ]
]
\end{forest}
\hskip \SD                  % <------- 
\begin{forest}  LT
[bubolate
    [
        [b]
            [
                [o]
                [b]
            ]
    ]
    [
        [
            [
                [u]
                [l]
            ]
        ]
        [
            [
                [a]
                [t]
            ]
            [e]
        ]
    ]
]
\end{forest}
    \end{figure}
\end{document}

enter image description here

For better visibiity

8
  • This is almost exactly what I'm looking for. Is it possible to have a single parameter that controls the sibling distance and the tree distance, in such a way that all the letters at the bottom have equal spacing regardless of whether they belong to different trees?
    – Mew
    Commented Jun 1, 2023 at 9:06
  • 1
    @Mew, do you mean that distance between trees is the same as between letters? Yes, this is possible. You need to define new command for distance between trees and change s sep to s sep =<defined new command> +1 mm. See addendum to answer (will appear soon)
    – Zarko
    Commented Jun 1, 2023 at 19:08
  • Sorry to revisit this answer, but I have another question: as I requested in my question, I would like a central control to turn on/off the display of intermediate node names, like in my first screenshot. The way you solve this currently is just to not have intermediate node names. Is there a way to only display the leaves and root but still have the intermediate labels present in the forest code?
    – Mew
    Commented Jun 4, 2023 at 11:28
  • @Mew, probably is. It is not clear (at least to me) what you mean with intermediate names. I suggest you that for this you ask new question where show what you after (switched on, switched of). BTW, please one problem per question :-)
    – Zarko
    Commented Jun 4, 2023 at 12:21
  • Well, that's the thing: that's exactly what I did in my question. My question shows the two images you request: one is with intermediate node names switched on, one is with those switched off.
    – Mew
    Commented Jun 4, 2023 at 13:17
2

This is based on Zarko's answer. We introduce a forest register called empty intermediates which controls whether intermediate nodes get their content typeset or whether they are left empty. This is set by default to not empty intermediates. Change to empty intermediates to flip the default. Note that one or the other of these is required, so don't delete this line altogether - you want the value to default one way or the other or forest will complain. The line declaring the new register is not sufficient as it doesn't set a default value.

You can then specify empty intermediates or not empty intermediates in individual trees' preambles to override the default. You can also alter the default at any point in the document, of course, by just using \forestset{}.

\documentclass[margin=3mm, varwidth]{standalone}
\usepackage[linguistics]{forest}
% seiliedig ar ateb Zarko https://tex.stackexchange.com/a/687344/ i gwestiwn Mew https://tex.stackexchange.com/q/684485/
\newcommand\SD{1 mm}
\forestset{%
  declare boolean register=empty intermediates,
  not empty intermediates,% default is to include node content if specified; delete the 'not' to flip the default or override in individual trees' preambles etc.
  LT/.style = {% Linguistic tree
    for tree={
      if n children=0{tier=word,baseline}{if={> {O_> R &}{level}{0}{empty intermediates}}{before typesetting nodes={shape=coordinate}}{delay={if content={}{shape=coordinate}{}}}},
      % adjust the following to make the nodes larger or smaller
%       text height = 1.5ex,
%       text depth=.25ex,
%       inner sep=0pt,
      s sep = \SD,
    }}}
\begin{document}
\begin{figure}[ht]
  \begin{forest}  
    LT,
    [discom
      [dis
          [d]
          [is
              [i]
              [s]
          ]
      ]
      [com
          [c]
          [om
              [o]
              [m]
          ]
      ]
    ]
  \end{forest}%
  \hskip \SD        
  \begin{forest}  
    LT
    [bubolate
      [bob
          [b]
          [ob
              [o]
              [b]
          ]
      ]
      [ulate
          [ul
              [u]
              [l]
          ]
          [ate
              [at
                  [a]
                  [t]
              ]
              [e]
          ]
      ]
    ]
  \end{forest}
\end{figure}
\end{document}

with not empty intermediates

Flipping the default:

with empty intermediates

4
  • 2
    +1! I learn again something new from you.
    – Zarko
    Commented Jul 1, 2023 at 8:11
  • 1
    @Zarko All I did was add a boolean ;). I started with your code! But creating new forest options and registers is incredibly useful. prooftrees uses a great many of them!
    – cfr
    Commented Jul 1, 2023 at 16:02
  • +1: So, declare boolean register, is something that is already predefined (offered) by the package? This seems very specific. Commented Jul 2, 2023 at 6:00
  • 1
    @Dr.ManuelKuehner Yes. Not sure what you mean about it being specific. forest offers a whole bunch of declare ... keys for both registers and options. So it isn't just boolean registers, if that's what you're asking?
    – cfr
    Commented Jul 4, 2023 at 14:34
1

Hm, don't really get your requirements: sounds like you look for some automations. If that's true, you'd need to think about how to control the output (as a function, e.g. f(p1, p2, .. pn))

Here's a manual, simple straight forward approach, where you can vary placement as needed:

result

\documentclass[10pt,border=3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}

\begin{tikzpicture}
    % lowest level 1
    \node (1-1)                 {d};
    \node (1-2) [right=of 1-1]  {i};    
    \node (1-3) [right=of 1-2]  {s};    
    \node (1-4) [right=of 1-3]  {c};    
    \node (1-5) [right=of 1-4]  {o};    
    \node (1-6) [right=of 1-5]  {m};    
    % level 2
    \node (2-2) [above=of 1-2]  {is};
    \node (2-5) [above=of 1-5]  {om};   
    % level 3
    \node (3-1) [above=of 2-2]  {dis};
    \node (3-5) [above=of 2-5]  {com};  
    % level 4
    \node (4) [above right=of 3-1]  {discom};
    
    % connections
    \draw (1-2) -- (2-2) -- (1-3);
    \draw (1-5) -- (2-5) -- (1-6);
    
    \draw (1-1) -- (3-1) -- (2-2);
    \draw (1-4) -- (3-5) -- (2-5);
    
    \draw (3-1) -- (4) -- (3-5);
\end{tikzpicture}

\begin{tikzpicture}
    % lowest level 1
    \node (1-1)                 {d};
    \node (1-2) [right=of 1-1]  {i};    
    \node (1-3) [right=of 1-2]  {s};    
    \node (1-4) [right=of 1-3]  {c};    
    \node (1-5) [right=of 1-4]  {o};    
    \node (1-6) [right=of 1-5]  {m};    
    % level 2
    \node (2-2) [above=of 1-2]  {};
    \node (2-5) [above=of 1-5]  {}; 
    % level 3
    \node (3-1) [above=of 2-2]  {};
    \node (3-5) [above=of 2-5]  {}; 
    % level 4
    \node (4) [above right=of 3-1]  {discom};
    
    % connections
    \draw (1-2) -- (2-2) -- (1-3);
    \draw (1-5) -- (2-5) -- (1-6);
    
    \draw (1-1) -- (3-1) -- (2-2);
    \draw (1-4) -- (3-5) -- (2-5);
    
    \draw (3-1) -- (4) -- (3-5);
\end{tikzpicture}

\end{document}

3
  • To clarify the requirements: with TikZ, the requirement about more than two children isn't difficult to satisfy, because you're explicitly drawing from point to point. Some packages might however only allow binary trees, for all I know. The second requirement would in your case come down to controlling the spacing of right of and above of. This aside: could you show your solution with multiple trees, like my example? Otherwise there's not much difference with the existing tree thread.
    – Mew
    Commented May 1, 2023 at 17:38
  • 1
    Please put this kind of explanation into your question, to make it easier for future readers to follow. // No.
    – MS-SPO
    Commented May 1, 2023 at 18:03
  • You have to @Mew a commentator, I think, if you want them to see it.
    – cfr
    Commented Jul 1, 2023 at 2:43

You must log in to answer this question.

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