DockerでSageMathを使ってみた

# コンテナを起動する

イメージをプルします。

```
docker pull sagemath/sagemath
```

DockerHub による SageMath イメージの公式ドキュメントはこちらです。

https://hub.docker.com/r/sagemath/sagemath/

コンテナを起動します。`-it` はコンテナ内でインタラクティブな bash シェルを作成するオプションです。

```
docker run -it sagemath/sagemath:latest /bin/bash
```

オプションの詳細に関する DockerHub 公式ドキュメントはこちらです。

https://docs.docker.jp/engine/reference/commandline/run.html#ruby-pseudo-tty-name-it

このコマンドの実行後、以下のように sage ユーザーでコンテナに入ることができます。

```
sage@a5eac0d438c4:~$
```

`sage` コマンドで SageMath を起動します。

```
sage@a5eac0d438c4:~$ sage
----------------------------------------------------------------------
| SageMath version 9.1, Release Date: 2020-05-20                     |
| Using Python 3.7.3. Type "help()" for help.                        |
----------------------------------------------------------------------
```

例）$\mathbb{Q}$ 上の多項式環

```
sage: R.<x> = PolynomialRing(QQ)
sage: R
Univariate Polynomial Ring in x over Rational Field
sage: f = x^2 - 1
sage: f.factor()
(x - 1) * (x + 1)
```

`exit` で対話モードを終了します。

```
sage: exit
```

# ファイルの入出力

ファイルを作成するために `vim` コマンドをインストールします。

```
sage@a5eac0d438c4:~$ sudo apt-get update
sage@a5eac0d438c4:~$ sudo apt-get install vim
```

自分のプログラムコードを保存するディレクトリを作成し、sample.sage というファイルを作成します。

```
sage@a5eac0d438c4:~$ mkdir mysrc
sage@a5eac0d438c4:~$ cd mysrc/
sage@a5eac0d438c4:~/mysrc$ vim sample.sage
```

10までの素数を output.txt ファイルに出力するプログラムを作成します。

```python:sample.sage
file = open('output.txt', 'w')
for i in range(10):
    if is_prime(i):
        file.write(f'{i}は素数です。\n')
file.close()
```

`sage` コマンドで sagemath を起動します。

```
sage@a5eac0d438c4:~/mysrc$ sage
```

実行するには以下のように `load(ファイル名)` を実行します。

```
sage: load('sample.sage')
```

実行が完了したら一旦 sage を終了します。

```
sage: exit
```

output.txt ファイルを確認します。

```
sage@a5eac0d438c4:~/mysrc$ cat output.txt
2は素数です。
3は素数です。
5は素数です。
7は素数です。
```

なお、sage を起動してからでもディレクトリ移動などのコマンドは実行できます。

```
sage: cd mysrc/
/home/sage/mysrc
sage: pwd
'/home/sage/mysrc'
sage: ls
sample_M23.sage
```

# sage でコマンドを実行する

以下のコマンドで、`sage` コマンドで対話形式に入る前にコマンドを実行できます。

```
sage -c "load('sample.sage')"
```

もしくは次のように実行します。

```
sage sample.sage
```

`Permission denied` というエラーが発生する場合は、`sudo` を使用します。

```
sudo sage sample.sage
```

# 文字列の多項式から定義した環の多項式に変換する

例えばファイルから読み込んだ多項式の型は文字列です。これをそのまま因数分解することはできません。実行してみます。

```
sage: f = 'x^2 - 1'
sage: f
'x^2 - 1'
sage: f.factor()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-12862fc134be> in <module>()
----> 1 f.factor()

AttributeError: 'str' object has no attribute 'factor'
```

次のようにキャストすることで因数分解が可能です。

```
sage: R.<x> = PolynomialRing(QQ)
sage: cast_f = R(f)
sage: cast_f.factor()
(x - 1) * (x + 1)
```

# docker run で sage ファイルを実行する

カレントディレクトリに docker_test というディレクトリを作成し、その直下に Dockerfile を作成します。

```
mkdir docker_test
cd docker_test
vi Dockerfile
```

以下の Dockerfile を作成します。Dockerfile の CMD で、コンテナ起動時にコマンドを実行できます。

```Dockerfile:Dockerfile
FROM sagemath/sagemath

COPY ./src /mysrc

CMD sage -c "load('/src/sample.sage')"
```

docker_test/src には、ファイル sample.sage を格納しておきます。

```python:sample.sage
import datetime

file = open('/src/output.txt', 'w')
for i in range(10):
    if is_prime(i):
        file.write(f'{i}は素数です。\n')

file.write(str(datetime.datetime.now()) + '\n')
file.close()
```

以下のようなディレクトリ構成になります。

```
docker_test
    |--- Dockerfile
    |--- src
          |--- sample.sage
```

ビルドを実行します。

```
docker build -t my_sage .
```

実行結果を書き出したファイル output.txt をローカルに保持するために、以下のように `-v` オプションを付与して実行します。これはデータ・ボリュームとしてホスト上のディレクトリをマウントするコマンドです。

```
docker run -v /home/ec2-user/docker_test/src:/src my_sage
```

実行後、docker_test/src/output.txt には以下のような出力があります。

```
2は素数です。
3は素数です。
5は素数です。
7は素数です。
2021-09-14 12:54:08.261853
```

# ライブラリのインストール

SageMath は Python を使用しているため、必要なライブラリのインストールは `pip3` コマンドを用いることができます。Dockerfile でライブラリのインストールを行う際に注意することを記載します。

Dockerfile では以下のように `RUN` コマンドによるライブラリのインストールが可能です。

```Dockerfile:Dockerfile
RUN pip3 install boto3
```

しかし、Docker ビルド時に以下のようなエラーが発生します。

```
/bin/sh: 1: pip3: not found
```

これは `pip3` のパスが見つからないためです。以下のように絶対パスで `pip3` コマンドを実行すれば、正常にライブラリのインストールが行われます。

```Dockerfile:Dockerfile
RUN /home/sage/sage/local/var/lib/sage/venv-python3.10.5/bin/pip3 install boto3
```

絶対パスを見つけるには以下の手順を実行します。

１. コンテナを起動する

```
docker run -it sagemath/sagemath:latest /bin/bash
```

２. `which` コマンドで `pip3` コマンドの絶対パスを確認する 

```
sage@d4ef529b4505:~$ which pip3
/home/sage/sage/local/var/lib/sage/venv-python3.10.5/bin/pip3
```

# 最後に

こちらにサンプルを置きました。

https://github.com/quryu-1/my-sage-docker/tree/main

# 参考記事

https://hub.docker.com/r/sagemath/sagemath/

プログラミング
https://doc.sagemath.org/html/ja/tutorial/programming.html

https://doc.sagemath.org/html/ja/tutorial/tour_polynomial.html