木構造(forest / qtree)

forest のマニュアルは、作者の告白から始まります——長年 qtreesynttree を有り難く使ってきたが、どの道具にも同じ不満があった。木が、ときどき横に広がりすぎる。この一点への苛立ちから生まれたのが、いま LaTeX で 木構造(構文木・ディレクトリ図・決定木・確率木) を描くときの第一候補です。とはいえ古参の qtreetikz-qtree も現役で、両者の 角括弧記法はよく似ていて、しかし決定的に一箇所だけ違います。その一箇所を知らずに移ると、エラーも出ないまま .S という名前のノードが生えます。このページは、その違いから使い分けまでを扱います。

ブラケット記法 — 座標をひとつも書かずに木を組む

木を描く道具がどれも 括弧の入れ子 を入力にしているのは、木そのものが再帰構造だからです。根があり、その下に子がぶら下がり、子がさらに孫を持つ——この入れ子をそのまま [ ] の入れ子で書けば、どのノードをどこに置くかという 座標の計算はすべて道具の側の仕事 になります。forest なら [S [NP] [VP]]qtree なら \Tree [.S [.NP ] [.VP ] ]。書いているのは配置ではなく 構造 です。

選択肢は実質三つです。TikZ と pgfkeys の上に築かれた forest(Sašo Živanović、TeX Live 2024 では v2.1.5)が、いちばん自由度が高く自動整列も効きます。言語学の構文木を最短で描く古典が qtree(v3.1b)と、その記法を TikZ で再実装した tikz-qtree(David Chiang、v1.2)。そして追加パッケージを増やしたくない数段の木なら、TikZ 本体の trees ライブラリchildnode)で足ります。以下、まず二つのブラケット記法の違いを片付けてから、それぞれを見ていきます。

[.S [.NP ]][S [NP]] の違い — qtree の点は forest では通じない

結論から言うと、qtree では先頭のピリオドが「これは枝分かれする節点だ」という印で、forest ではピリオドはただの文字です。qtree[.S ... ] は「S という非終端節点」を意味し、ピリオドの付かない裸の語は葉(終端)になります。forest にその約束はなく、[ の直後から次の [] までがまるごとラベルです。したがって forest[.S [.NP Kim ]] を渡すと——エラーは一切出ずに——ラベルが .S.NP Kim のノードが二つできあがります。Kim は葉にならず、.NP と同じ箱に入ってしまいます。

latex
% qtree / tikz-qtree: a leading dot marks a branching node,
% and a closing ] must be preceded by whitespace
\Tree [.S [.NP Kim ] [.VP [.V saw ] [.NP Lee ] ] ]

% forest: no dots, and every word is a node of its own
\begin{forest}
[S
  [NP [Kim]]
  [VP [V [saw]] [NP [Lee]]]
]
\end{forest}

逆方向の間違いはもっと派手に失敗します。qtree閉じ括弧 ] の直前に空白を要求 するので、[.NP Kim] のように詰めて書くと、! LaTeX Error: \begin{tabular} on input line 5 ended by \end{document}. という、表を一つも書いていないのに tabular の名を出す不可解なエラーになります。tikz-qtree に同じことをすると ! Emergency stop. で PDF すら出ません。forest に慣れた手が qtree の原稿を触るとき、まずここで転びます。要するに どちらの記法も相手の記法を静かには受け入れない ので、ファイルを移すときはパッケージと記法をセットで確認してください。

forest の自動レイアウトと、間隔を手で詰めたいとき

forest の中心的な機能は、冒頭で触れた「広がりすぎる木」への答えである 自動パッキング です。各ノードを、それが支配する部分木の実際の幅に応じて配置し、重ならない範囲でできるだけ寄せるので、込み入った木でもそのまま見られる形に落ち着きます。手で調整したいときは、木全体または部分木に for tree={...} を適用します。よく使うのは、兄弟どうしの横の間隔 s sep、段(レベル)間の縦の距離 l、ノードの中身の周りの余白 inner sep の三つです。

document.tex
\usepackage[linguistics]{forest}

\begin{forest}
for tree={s sep=10mm, inner sep=0, l=0}
[CP
  [C]
  [IP, for tree={s sep=20mm}
    [I, name=src]
    [VP
      [V]
      [DP, roof, name=tgt [the woman]]
    ]
  ]
]
\draw[->] (src) to[out=south west, in=south] (tgt);
\end{forest}

この短い例に、forest の作法がひととおり詰まっています。冒頭の for tree={...} が木全体の既定になり、[IP, for tree={s sep=20mm} のように ノードラベルの後にカンマを置く と、その部分木だけ設定を上書きできます。name= でノードに名前を付けておけば、木を閉じたあとに \draw[->] (src) to[...] (tgt); と書いて 移動を示す矢印 を引けます——forest 環境の中では TikZ の命令がそのまま使えるからです。ラベル自体にカンマを入れたいときは {NP, PP} のように 波括弧で囲んで レイアウト指示と区別します。同じ体裁を毎回書きたくなければ、プリアンブルで \forestset{default preamble={for tree={...}}} と既定を定義しておけます。

ここで最頻出の落とし穴をひとつ。forest のソースに空行を入れると解析に失敗します。 空行は TeX にとって段落の区切りなので、木の途中で構文解析が打ち切られ、! Paragraph ended before \forestOappto was complete. という、木とは無関係に見えるエラーが出ます。長い木を読みやすく整えたい気持ちは分かりますが、入れられるのは改行とインデントだけ、空行は不可 です。逆にインデントの深さは自由なので、階層はそちらで表現してください。

linguistics と edges — ライブラリを読まないと roof も folder も使えない

forest の機能の一部は ライブラリ に分かれていて、\usepackage[linguistics]{forest} のようにオプションとして読み込まないと使えません。とくに引っかかりやすいのが、語句の上に 三角形の「屋根」 をかぶせる roof です。これは核となる forest ではなく linguistics ライブラリの定義なので、素の \usepackage{forest} のまま [DP, roof [the woman]] と書くと ! Package pgfkeys Error: I do not know the key '/tikz/roof' で止まります。同じく、ディレクトリ図でおなじみの folderedges ライブラリ側の定義です。

ついでに、linguistics ライブラリが実際に何をしているかを押さえておくと迷いが減ります。第一に sn edges——forest の既定は枝を親と子の「境界どうし」で結びますが、これを 親の下端(south)から子の上端(north)へ 変えます。言語学の構文木で見慣れた形はこちらです。第二に ベースラインを根に合わせる こと。例文番号や訳の行と木の高さがそろうのはこの設定のおかげです。第三に c 統御(c-command)に関するノードウォーク の定義で、c-commandedc-commanders でその関係にあるノード群をまとめて指定できます。edges ライブラリのほうは folder のほか、枝を直角に折る forked edge / forked edges を提供します。

qtree と tikz-qtree — 構文木を一行で書く

構文木を一本さっと描くだけなら、\Tree 記法がいまも最短です。\usepackage{qtree} を読み込み、本文に \Tree [.S [.NP Kim ] [.VP [.V saw ] [.NP Lee ] ] ] と一行書けば木が出ます。中身は二人の仕事の合流で、木を描くマクロ本体は Jeffrey Mark Siskind の QobiTree、それを LaTeX から使いやすくしたフロントエンドと拡張が Alexis Dimitriadis によるものです。バージョン 3.0b(2005 年)以降は線を pict2e で引くようになったので、DVI/PostScript 経路でも PDF 直接出力でも同じ結果が得られます。

qtree には構文木のための小技がひととおり入っています。木の中では NP_iN^0自動的に数式モードの下付き・上付き に整い、X\1X$'$(X′)の略記です。語句の上に三角形をかぶせるには \qroof{out of style}.PP のように書きます(forest の roof と役割は同じですが綴りが違います)。木は既定で中央寄せされるので、左揃えにしたいときは \usepackage[nocenter]{qtree} を使います。

tikz-qtree(David Chiang)は、この \Tree 記法を TikZ の上で作り直した ものです。\usepackage{tikz}\usepackage{tikz-qtree} を並べて読み込めば、同じソースがそのまま通ります。作者自身が挙げている利点は三つで、TikZ 標準の木より衝突を避けて配置できることqtree に TikZ の機能(枝のラベル、ノード間の矢印)を足せること、そして pst-qtree と違って pdfTeX と XeTeX で使えること です。木の向きも変えられ、tikzpicture[grow'=right] を渡せば 左から右へ 伸びます。互換性は高いものの完全ではないので、複雑な木を移すときはコンパイルして目で確かめてください。

document.tex
\usepackage{tikz}
\usepackage{tikz-qtree}

% sideways tree: grow to the right instead of downwards
\begin{tikzpicture}[grow'=right]
  \Tree [.S [.NP Kim ] [.VP [.V saw ] [.NP Lee ] ] ]
\end{tikzpicture}

言語学以外の木 — ディレクトリ構成・決定木・確率木

forest は言語学専用の道具ではありません。ディレクトリ構成図 なら edges ライブラリの foldergrow'=east の組み合わせが定番で、フォルダらしい直角の枝で左から右へ広がります。決定木・確率木 なら、枝そのものに数値を載せたくなります——forest の枝は TikZ のパスなので、edge label={node[midway,above]{0.3}} のようにノードを載せれば確率や条件をそのまま書き込めます。forest 付属のマニュアルにも決定木の作例が収録されています。

document.tex
\usepackage[edges]{forest}

% a file hierarchy: right-angled branches, growing to the right
\begin{forest}
for tree={grow'=east, folder, s sep=1mm}
[project
  [src [main.tex] [chapters]]
  [figures [plot.pdf]]
]
\end{forest}

% a probability tree: labels ride on the edges
\begin{forest}
for tree={grow'=east, l sep=18mm, s sep=6mm, anchor=west}
[start
  [rain, edge label={node[midway,above,font=\scriptsize]{0.3}}]
  [dry,  edge label={node[midway,below,font=\scriptsize]{0.7}}]
]
\end{forest}

追加パッケージを一切増やしたくない数段の木なら、TikZ 本体の trees ライブラリ で足ります。ノードを置いたあとに child {...} を続けると、その中身が子として配置され、親から子への枝が自動で引かれます。child の中にはふつう node {...} を書き、さらに child を入れ子にして孫を作ります。配置を決めるのは二つの距離で、親の段と子の段の縦の隔たりが level distance、横に並ぶ子どうしの間隔が sibling distance——どちらも既定は 15 mm です。段ごとに変えたいときは level 1/.style={sibling distance=4cm} のように level ⟨n⟩/.style を使い、木の向きは [grow=right] などで、枝の形は edge from parent(直角に折るなら edge from parent fork down)で制御します。

latex
\usetikzlibrary{trees}

\begin{tikzpicture}[level distance=12mm, sibling distance=24mm]
  \node {root}
    child { node {a} }
    child { node {b}
      child { node {c} }
      child { node {d} }
    };   % one semicolon for the whole tree
\end{tikzpicture}

書くときの注意は セミコロン ; が木全体でひとつだけ\node ... ; の末尾に置かれることです。そして素の TikZ には forest のような自動パッキングがないので、子の本数や深さが増えると sibling distance を手で広げて重なりを避けることになります。数段の小さな木には十分ですが、「間隔を内容に合わせたい」と思った時点が forest に移る合図です。

どれを選ぶか

迷ったら 新しく描く木は forest です。自動レイアウトが効き、言語学から一般の木まで守備範囲が広く、装飾には TikZ の全機能がそのまま使えます。既存の \Tree 原稿を引き継ぐ、あるいは共著者が \Tree で書いてくるなら qtreetikz-qtree——出力の質と調整のしやすさでは tikz-qtree が上です。依存を一切増やしたくない数段の木 なら素の TikZ の child。四つとも CTAN にあり、foresttikz-qtreegraphics/pgf/contrib/ の下で TikZ/pgf に依存します。細部は手元で texdoc foresttexdoc qtreetexdoc tikz-qtree を引くのが確実です。

道具記法向いている用途
forest[S [NP] [VP]](点は付けない)新規の木全般。自動パッキング、言語学から決定木まで
qtree\Tree [.S [.NP Kim ] ](点と空白が必須)構文木を最短で。pict2e ベースで軽量
tikz-qtree\Tree(qtree と同じ)同じ記法を TikZ 品質で。枝のラベルや矢印も足せる
tikz trees library\node {r} child {node {a}};数段の小さな木。追加パッケージを増やしたくないとき

最後にひとつ実務的な話を。学会発表や講義スライドに構文木を載せたい、という需要はとても多いのですが、その場合は木を LaTeX 文書の中で完結させるのではなく、単独の画像として書き出して PowerPoint や Keynote に貼るほうが早いことがあります。foresttikz-qtree も TikZ の上にあるので、standalone クラスで包めばそのまま切り詰めた PDF や SVG になります。その手順は画像化のページにまとめてあります。