luacasパッケージを使ってLaTeX文書内で数式処理をする

# 0. はじめに

この記事は[物工/計数 Advent Calender 2022](https://adventar.org/calendars/7701)の17日目の記事です。
この記事のテーマは、先日2022/11/06に[CTAN(Comprehensive TeX Archive Network)](https://ctan.org/)に追加されたばかりのLaTeXパッケージであるluacasパッケージについてです。
luacasパッケージは、簡単にいうと**LaTeX文書内で数式処理をするためのパッケージ**です(CAS: Computer Algebra System)。例えば微積分なんかをやってくれます。
luacasの公式ドキュメントにおいては次のように書かれています。
> The package luacas allows for symbolic computation within LaTeX.

https://ctan.org/pkg/luacas

# 1. luacasを使うための準備

:::note info
luacasの使用例を早く見たい人はここは飛ばして[2. 使ってみる](#2-使ってみる)を見てください
:::

luacasパッケージは
- LuaLaTeXでのみ利用できる（pLaTeXやupLaTeXでは利用できない)
    - 自分がどのエンジンでLaTeXを使っているのかわからないという方もしれませんが、少なくとも`jsarticle`などを使っていればまあ(u)pLaTeXだと思います。
- ごく最近発表されたパッケージである

という2点から、使うまでに少し準備が必要です。
人によってはこの準備にちょっと時間かかるかもしれません。

## LuaLaTeXでのビルド環境を整える

自分なりの設定をしたい人は勝手に自分でやってください。以下はVSCodeでLaTeX Workshopを使ってる人向けの説明です。
ここでは[cluttex](https://blog.miz-ar.info/2018/10/cluttex-release/)というLaTeX処理の自動化ツールを使って環境構築します(楽なので)。
1. `settings.json`を開く
2. `"latex-workshop.latex.tools"`に以下を追加
    <details><summary>追記内容</summary>
    biberじゃなくてbibtexが良い人とかはcluttexの仕様見ながらよしなに書き換えてください

    ```json:settings.json
    {
      "name": "cluttex",
      "command": "cluttex",
      "args": [
        "--engine=lualatex",
        "--makeindex=upmendex",
        "--biber=biber",
        "-synctex=1",
        "-shell-escape",
        "%DOC%"
      ],
    }
    ```
    </details>

3. `"latex-workshop.latex.recipes"`の**一番上に**以下を追加(一番上に追加したレシピがデフォルトのビルドレシピとなります）

    <details><summary>追記内容</summary>

    ```json:settings.json
    {
      "name": "cluttex",
      "tools": [
        "cluttex",
      ]
    }
    ```
    </details>

4. プリアンプルなどをよしなに書き換える
    LuaLaTeXにおいては`jsarticle`とかが使えなくなるので、よしなに書き換えます。
    <details><summary>ちなみに僕がよく使うプリアンプルはこんな感じです</summary>

    LuaLaTeXは(u)pLaTeXと違い、PDFを生成する際にdviファイルを経由しないため、graphicxなどのインポートの際に`dvipdfmx`などのドライバを指定する必要がなくなります。
    ```latex
    \documentclass{jlreq}

    \usepackage{amsmath,amssymb}
    \usepackage{physics}
    \usepackage{graphicx}
    \usepackage{float}
    \usepackage{enumitem}
    
    \usepackage[no-math]{luatexja-fontspec}
    \setmainfont[Ligatures=TeX]{TeXGyreTermes}
    \setsansfont[Ligatures=TeX]{TeXGyreHeros}
    \setmainjfont[BoldFont=HiraMinProN-W6]{HiraMinProN-W3}
    \setsansjfont[BoldFont=HiraKakuProN-W6]{HiraKakuProN-W3}
    
    
    \begin{document}
    ```
    </details>

## (TeX Liveを使っている人向け)TeX Liveをupdateし、luacasを入手
ターミナルで
```shell:shell
> sudo tlmgr update --self --all
```
を実行することでTeX Liveをupdateできます。久々にupdateする場合だと結構時間かかると思います。
```shell:shell
> kpsewhich luacas.sty
/usr/local/texlive/2022/texmf-dist/tex/lualatex/luacas/luacas.sty
```
こんな感じで`luacas.sty`が見つかってくれればOKです。
overleafとか使ってる方はluacasパッケージのページからstyファイルを入手すればいいんじゃないですかね。

# 2. 使ってみる

`\usepackage{luacas}`をプリアンプルに追加して始めましょう。

## 使い方

`\begin{CAS}...\end{CAS}`の中に記述し、　`\print{}`によって出力します。
`\begin{CAS}...\end{CAS}`の中での定義は文書内でグローバルなもののようです。
```latex
\begin{CAS}
    vars('x')
    f = x^2 + 3*x + 2
\end{CAS}
\[ f(x)=\print{f} \]
```
出力
![スクリーンショット 2022-12-09 17.27.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/e7c37352-562c-afdd-e8f4-44efa9a7244e.png)


## 基本の関数

### 計算結果を簡単にする： `f:autosimplify()` `\print*{}` `f:simplify()`
```latex:luacas_example.tex
\begin{CAS}
	vars('x')
	f = (1 - x + 0*x)*(1 + 1*x)
\end{CAS}
\[　\print{f}=\print{f:autosimplify()} = \print{f:simplify()}　\]
```
出力
![スクリーンショット 2022-12-09 17.29.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/de0f64f7-c4a3-1a26-5b74-2ee625c41d7b.png)
- `\print*{f}`は`\print{f:autosimplify()}`と等価です。ですので簡単な`\print*{f}`の方をメインで使うようになるのではないかと思います。
- `f:autosimplify()`と`f:simplify()`は別のコマンドです。`f:simplify()`は出てくる項の数を極力少なくするように変形するらしく、こちらの方がやや強いコマンドなのではないかと思います（詳細はよくわからない）。
- 降べきの順になってほしいけど、デフォルトでは昇べきの順になるようです。簡単に変更する方法あるんですかね。

### 展開する： `f:expand()`
```latex:luacas_example.tex
\begin{CAS}
    vars('x')
	f = (x + 1)^2*(x - 3) + (2*x^3 + 5*x + 1)
\end{CAS}
\[ \print{f}=\print{f:expand()} \]
```
出力
![スクリーンショット 2022-12-09 17.38.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/9d93037c-8408-a341-9396-cc11dfd96d0c.png)

### 因数分解(or素因数分解)する： `factor(f)`
```latex:luacas_example.tex
\begin{CAS}
    vars('x')
	f = (x^2 + 5*x + 26)*(x^2 + 5*x + 24) + 1
	a = 12512
\end{CAS}
\begin{align*}
	\print{f} & =\print{factor(f:expand())} \\
	\print{a} & =\print{factor(a)}
\end{align*}
```
出力
![スクリーンショット 2022-12-09 17.43.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/9ef5c283-1ecb-4ef8-1650-e3122c67baeb.png)
- 整式に対して適用すると因数分解され、整数に対して適用すると素因数分解されるようです。
- aのように変数に数値を代入する場合は色々気をつけることがある的なことが[公式ドキュメント](https://ftp.yz.yamagata-u.ac.jp/pub/CTAN/macros/luatex/latex/luacas/doc/luacas.pdf)に書いてあったのですが、よくわからなかったので気になる方は公式ドキュメントを読んでください。


### 代入する： `substitute({[x]=a},f)`
```latex:luacas_example.tex
\begin{CAS}
    vars('x')
	f = x^4 + sqrt(2)*x^2 + 3*x + 5
	subs = {[x]=1+sqrt(3)}
	g = x^2 + x + 1
\end{CAS}
\begin{align*}
	f(x)          & =\print{f}                               \\
	f(1+\sqrt{3}) & =\print*{substitute(subs,f):simplify()}  \\
	g(x)          & =\print{g}                               \\
	g(f(x))       & =\print{substitute({[x]=f},g)}           \\
	              & =\print*{substitute({[x]=f},g):expand()}
\end{align*}
```
出力
![スクリーンショット 2022-12-09 17.55.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/7b5954d9-76a6-8f1d-6d20-5cad06ddddb5.png)
- `{[x]=hoge}`みたいな代入内容を`\begin{CAS}...\end{CAS}`の中に書くか`\print{}`の中に書くかで微妙に挙動が違うことがあるのが気になります（詳細はよくわかっていないが、おそらく前述のようにluacasの数値の扱いにちょっとクセがあることが影響しているのではないかと思う）。
- 数値だけでなく文字を代入することもできます(g(f(x))参照)。


## さまざまな演算

### 微分する： `diff(f,x)`
```latex:luacas_example.tex
\begin{CAS}
	vars('x','y')
	f = sin(x^2+x-1)
	fx = diff(f, x)
	g = 3*x*y - x^2*y
	gxy = diff(g, x, y)
	h = arctan(y/x)
\end{CAS}
\begin{align*}
	\print{fx}                              & =\print*{fx}        \\
	\print{gxy}                             & =\print*{gxy}       \\
	\pdv{}{x}\qty(\arctan\qty(\frac{y}{x})) & =\print*{diff(h,x)}
\end{align*}
```
出力
![スクリーンショット 2022-12-09 18.44.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/bfde4985-77b0-9b8b-9af9-d75c0e3ceefa.png)
- gやhのような2変数関数に対しても使えます。`diff(g,x,y)`とすると$\frac{\partial^2g}{\partial y\partial x}$となります。
- 2変数関数に対し1つの変数で微分すると偏微分を計算しますが、そのままprintしたときの微分記号は$\frac{\partial}{\partial x}$ではなく$\frac{d}{dx}$でした。
- `\qty`や`\pdv`はphysicsパッケージのコマンドです。

### 積分する： `int(f,x,a,b)`
```latex:luacas_example.tex
\begin{CAS}
    vars('x')
	f = cos(x)/(sin(x)*(sin(x)+1))
	intf = int(f, x)
	g = sin(x)*cos(x)/(1+sin(x))
	intg = int(g, x, 0, pi/2)
\end{CAS}
\begin{align*}
	\print{intf} & =\print*{intf}+C \\
	\print{intg} & =\print*{intg}
\end{align*}
```
出力
![スクリーンショット 2022-12-09 18.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/64683fe0-9edf-7933-ae94-9b048170bb7b.png)
- 引数にa,bを入れるとaからbまでの定積分、入れなければ原始関数(積分定数なし)が出力されます。
- 原始関数が求まらない積分などは基本できなさそうです。

### 部分分数展開する： `parfrac(num,den)`
```latex:luacas_example.tex
\begin{CAS}
    vars('x')
	num = 3*x^2 - 9*x + 7
	den = x^4 - 7*x^3 + 18*x^2 - 20*x + 8
	p = parfrac(num, den)
\end{CAS}
\[ \print{num/den}=\print*{p} \]
```
出力
![スクリーンショット 2022-12-09 18.59.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/74937bce-ac2e-3dfa-98a0-fad4ca843bb6.png)

### 方程式の根を求める：　`roots(f)`
```latex:luacas_example.tex
\begin{CAS}
    vars('x')
	f = x^6 + 3*x^5 + 6*x^4 + 7*x^3 + 6*x^2 +3*x + 2
	r = roots(f)
\end{CAS}
$\print{f}=0$の解は
\[\lprint{r}\]
```
出力
![スクリーンショット 2022-12-09 20.15.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/8bde492c-38e0-d17b-5738-c71351e5a92c.png)

- `r[1]`のようにすることで各要素にアクセスすることもできます（indexは1スタート）。

### xについて解く： `eq:solvefor(x)`
```latex:luacas_example.tex
\begin{CAS}
	vars('x','y','z')
	lhs = e^(x^2*y)
	rhs = z + 1
	eq = Equation(lhs, rhs)
	eqx = eq:solvefor(x)
\end{CAS}
\[\print{eq}\iff\print{eqx}\]
```
出力
![スクリーンショット 2022-12-09 20.19.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/d69fcd33-dd23-d5b6-6b3e-89124632a1ad.png)

### グラフをプロットする：　`\fetch{f}`(pgfplotsパッケージを利用)
```latex:luacas_example.tex
% 以下をプリアンプルに追加
% \usepackage{pgfplots}

\begin{CAS}
    vars('x')
	f = e^x
\end{CAS}
\[f(x)=\print{f}\]
\begin{figure}[H]
	\centering
	\begin{tikzpicture}
		\begin{axis}[legend pos = north west, xlabel=$x$, ylabel=$y$]
			\addplot[domain=-3:3,samples=100]{\fetch{f}};
			\addlegendentry{$f(x)$};
		\end{axis}
	\end{tikzpicture}
\end{figure}
```
出力
![スクリーンショット 2022-12-09 20.54.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/72be18e7-06aa-e91d-1afd-024f75b2e7c8.png)
- pgfplots側の問題っぽいですが、$\ln(x)$とかは描画できませんでした。
- どうやらそのまま$\sin(x)$などを描画しようとするとxが度数法で見られるようです。

## 使用例: 関数の増減を調べる（関数定義・微分・根の計算・グラフの描画）
以下はluacasを使って書いてみました。
![スクリーンショット 2022-12-09 21.00.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958276/a4d80958-24b8-63f4-712b-12586c90bff2.png)

<details><summary>TeXコード（長いので折りたたみ）</summary>

```latex:luacas_example.tex
\begin{CAS}
	vars('x')
	f = x^3-5*x^2+2*x+1
	df = diff(f,x)
	r = roots(df)
\end{CAS}

$f(x)=\print{f}$とする．$x$で微分すると
\[f'(x)=\print*{df}\]
ここで，$f'(x)=0$の解は$\displaystyle x=\print*{r[1]},\,\print*{r[2]}$である．
よって極大値と極小値はそれぞれ
\begin{align*}
	f\qty(\print*{r[1]}) & =\print{substitute({[x]=r[1]},f)}            \\
	                     & =\print{substitute({[x]=r[1]},f):simplify()} \\
	f\qty(\print*{r[1]}) & =\print{substitute({[x]=r[2]},f)}            \\
	                     & =\print{substitute({[x]=r[2]},f):simplify()}
\end{align*}
となる．よってグラフは次のとおり．
\begin{figure}[H]
	\centering
	\begin{tikzpicture}
		\begin{axis}[legend pos = north west, xlabel=$x$, ylabel=$y$]
			\addplot[domain=-4:7,samples=100]{\fetch{f}};
			\addlegendentry{$f(x)$};
		\end{axis}
	\end{tikzpicture}
\end{figure}
```

</details>

# 3. おわりに
luacasパッケージの紹介でした。LuaLaTeXは処理系にプログラミング言語Luaを組み込んでいるわけですが、それによってこんなにいろんな計算ができるというのは驚きですね。

このluacasパッケージが実際どの程度便利なのかは私もよくわからないですが（そんな使ってないので）、たとえそこまで便利じゃないと感じても面白ければまあ良いじゃんというのが私の考えです。

LuaLaTeXは他にも色々な可能性を秘めているLaTeXエンジンだと思うので、皆さんも是非LuaLaTeXの動向をチェックしてみてください。

最後になりましたが、明日以降の[物工/計数 Advent Calender 2022](https://adventar.org/calendars/7701)の記事もお楽しみに。過去記事も色々面白いのあるのでぜひ見ていってください。