C++用のGnuplotラッパーライブラリを一般公開することにした。

 タイトルのとおりである。ADAPT-GPM2という名前のライブラリだ。 github.com

 C++によるグラフ描画のためのライブラリは意外と見つかる。CERN開発の統計解析ライブラリであるROOT、マックス・プランク研究所開発のDISLIN、その他PGPLOTやPLplot、比較的小規模なところで言えばQCustomPlot、Qtそれ自体にも多少の可視化機能はある。変わったところではPythonのMatplotlibをC++から扱えるようにするMatplotlib-cppなんてものもある。しかしいずれも調べ試してみた限り、非常に使いづらいか機能が不足するものばかりで、結局どれも導入を断念した。どのみちラッパーを書かなければならないのなら、Gnuplotが一番楽だった。
 そんなわけで、GnuplotC++から利用したいという要望はそれなりに多い。しかしC++からのインターフェイスとして公開されているライブラリは、まあ、残念ながら大半はゴミのようなものばかりである。GitHubなどで使い物になるライブラリが公開されていないかと必死に探し回ったのだが、残念ながら私が見つけた限り役に立たないものばかりだった。敢えて言うならgnuplot-cppは比較的まともではあったが、私の欲する機能がない。特にカラーマップに対応していないのは致命的だった。gnuplot-iostreamは悪くないが、boost依存は受け入れがたいし、Gnuplotの悪しき仕様から逃れられていない。それ以外は、わざわざ言及するほどの価値さえない。
 こんな有様なので、自分で作るしかなかったのだ。

 このライブラリは、極力Gnuplotの存在を隠蔽するよう作られている。利用者はGnuplotのコマンドを直接叩く必要はないし、一部の番号指定が必要な点の形状などを除けば、Gnuplotを一切知らなくても使うことができる。Gnuplotをインストールし、そのパスさえ指定すればよい。あの悪名高きusingコマンドなどよりもずっと分かりやすくなっているはずだ、と私は勝手に思っている。例えば2次元のグラフを描く場合は次のように使う。

#include <ADAPT/GPM2/GPMCanvas.h>
#include <random>

using namespace adapt::gpm2;

int main()
{
    std::string norm = std::to_string(250. / std::sqrt(2 * 3.1415926535));
    std::string equation = norm + "*exp(-x*x/2)";

    std::random_device rd;
    std::mt19937_64 mt(0);
    std::normal_distribution<> nd(0., 1.);
    std::vector<double> x1(32, 0);
    std::vector<double> y1(32, 0);
    std::vector<double> e1(32);
    for (int i = 0; i < 1000; ++i)
    {
        double x = nd(mt);
        if (x < -4.0 || x >= 4.0) continue;
        ++y1[static_cast<size_t>(std::floor(x / 0.25) + 16)];
    }
    for (int i = 0; i < 32; ++i)
    {
        x1[i] = i * 0.25 - 4. + 0.125;
        e1[i] = std::sqrt(y1[i]);
    }

    GPMCanvas2D g("example_2d.png");
    g.ShowCommands(true);
    g.SetTitle("example\\_2d");
    g.SetXRange(-4.0, 4.0);
    g.SetXLabel("x");
    g.SetYLabel("y");
    g.PlotPoints(equation, plot::title = "mu = 0, sigma = 1",
                 plot::style = Style::lines).
        PlotPoints(x1, y1, plot::xerrorbar = 0.125, plot::yerrorbar = e1,
                   plot::title = "data", plot::color = "black",
                   plot::style = Style::points, plot::pointtype = 7, plot::pointsize = 0.5);
    return 0;
}

f:id:thayakawa:20200710205855p:plain  尤も、私が研究のために欲しいと思った機能しか実装していないので、機能的に十分ではないし開発途中である。テストも不十分だ。また私のプログラミングに関する知識は中途半端であるし、Gnuplotも実はあまり使いこなせていない。したがって、おそらくバグも多い。仮にこのライブラリの致命的なバグによって損失を負ったとしても、私は一切関与しないし責任を取らない。むしろ誰かにデバッグしてほしい。

 GPM2がなぜ2なのかといえば、その前身であるGPMが存在するからだ。こちらは設計が大変汚らしいので公開する気はない。
 GPMおよびGPM2は私が研究に利用するため個人的に開発しているADAPTというライブラリの一部である。ADAPTはその動作上はややデータベースに似たところのある、統計解析やデータ加工、2Dならびに3D可視化を目的としたライブラリである。一般には非公開で、研究室関係者にのみ利用を許可している。オープンソースで一般公開しようかと考えたことはあるが、そんな需要があるかは非常に疑わしいしメリットもないので、今のところは非公開としている。階層モデルに関係モデル風のJOIN機能をくっつけたややこしいデータモデルを理解した上で一億件のデータ分析に利用しようなんて考える人間は、私個人以外には滅多にいないだろう。そんな需要があるのならとっくに誰かが作って世間に広めているはずだ。
 ADAPTの一部のため、ADAPT内のヘッダーを多少含んでいる。sprintfのようにstd::stringをフォーマットするFormat.hやキーワード引数を擬似再現するKeywordArgs.hなどだ。ADAPT自体は本来非公開だが、GPM2やその他のヘッダーは非公開にするほど重要なアルゴリズムを含んでいるわけでもないお遊びコード、便利機能の集まりなので、公開したところで別に問題ない。

 既にスクリプト言語を用いたデータ解析が主流となっている現代、敢えてC++Gnuplotの組み合わせを使いたがる人間がどれほどいるのかは不明だ。しかしMatplotlibの遅さにうんざりしている人、コアプログラムをC++で書いていていちいち他言語からグラフ化するのが面倒な人は、世界中を探せば多少はいるかもしれない。そんな極めてニッチな需要に対して、本ライブラリは多少の役割を果たせるかもしれない。……いや尤も、見つけてさえもらえないのがオチだろうとは思うけれども。