遊戯にっき

@nnt339

Julia + Jupyter + VSCode + Windows10 でNotebook to PDF

以前導入したJuliaJupyter環境をリセットして、再度セットアップしようとしたら死ぬほど詰まったのでメモ。

はじめに

グラフ描画と言えばPython+matplotlibが文献も豊富で重宝するが、科学計算という点ではJuliaの方が優れている。例えば、三角関数をプロットするだけを考えてもPythonではnumpyをインポートした上で、

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
plt.plot(x, y)

f:id:nnt339:20210615213749p:plain

と書かなければならないが、Juliaであれば、

using Plots
x = collect(0:0.01:2π)
y = sin.(x)
plot(x, y)

f:id:nnt339:20210615213623p:plain

で書けるのでちょっとしたグラフのレポートを作成する際に最適である。

理系学生はJuliaの導入を検討すべきだと思うし、普及させたいと考えているのでその一助となれば。

環境

  • OS=windows10
  • Julia-1.6.1
  • Jupyter Notebook(IJuliaからanacondaを通してインストール)
  • VSCode-1.58.0-insider
  • MikTeX-21.6
  • Inkscape-1.1

Juliaのインストール

julialang.org (accessd on 2021/6/15) 環境変数PATHを通しておくと、どこからでも起動できるので便利。

Juliaのパッケージ

コンソールからJuliaREPLを起動する。

$ julia
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.6.1 (2021-04-23)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |
julia>

]キーを入力してパッケージモードへ切り替えると、(@1.6) pkg>になる。 使い方はGoogle検索しても良いし、helpで確認しても良い。

データ分析の基本的なパッケージを入れる。

(@1.6) pkg>add IJulia  # Jupyter用のカーネル
(@1.6) pkg>add Plots  # グラフ描画ライブラリ
(@1.6) pkg>add DataFrames # pandasのようなもの

それぞれの入門的なものは本題ではないのでここでは無し。 インターネット上のドキュメントは充実している。

Jupyter Notebookのインストール

既にインストールされている場合は必要ないかもしれないが、今回の本題であるNotebook to PDF という点では若干環境が異なるので注意。

パッケージモードからbackspaceキーで戻って、

julia> using IJulia
julia> notebook()

anaconda経由でインストールするか聞かれるのでyesと。 ~\.julia\condaPythonipython等の必要なライブラリがインストールされてJupyter Notebookが起動する。

因みに~\はホームディレクトリのことでWindowsだとC:\users\username\

Jupyter NotebookVSCodeから使用するため今は閉じて良し。

VSCode Insidersのインストールと拡張機能

VSCode InsidersのNotebookのGUIが好みだったんだけど、最近VSCodeの仕様と統一されたっぽい?

code.visualstudio.com (accessd on 2021/6/15)

拡張機能Jupyterだけで良い。必要があればJuliaとかも。

github.com

github.com (accessd on 2021/6/15)

Notebookの作成とグラフの描画

# julia-sin.ipynb #
using Plots
x = collect(0:0.01:2π)
y = sin.(x)
plot(x, y)

カーネルJuliaを選択してノートブックを実行するとグラフが描画される。

Notebook to PDF

さて、作成したノートブックをPDFのレポートとして提出したいわけだが、ここが難所なのである。

VSCodeJupyter拡張にはExport asというオプションがあるが、準備なしにはそれは上手くいかない。環境ごとに問題が発生するせいでインターネットにもピンポイントな回答は少ない。

今回はWindows10という環境に絞ってこの問題を解決したいと思う。

Jupyter Interpreterの選択

Jupyter拡張機能を起動する用のPythonインタープリターを選択する。Pythonを複数インストールしている場合は正しく選択されていない可能性があるので要確認。

VSCodeで「ctrl+shift+P」でコマンドパレットを開いて、「Jupyter: Select interpreter to start Jupyter Server」から先ほどcondaでインストールしたPython~\.julia\conda\3\python.exe)を指定する。

MiKTeXのインストール

PDFに変換する際nbconvertというPythonのライブラリを通して一旦texに変換するわけだが、その際に必要なパッケージを管理するのにWindowsではMiKTeXというソフトを使う。 miktex.org (accessd on 2021/6/15)

念のためC:\Program Files\MiKTeX\miktex\bin\x64\へPATHが通っている確認する。

Inkscapeのインストール

グラフをPDF出力するのにnbconvertInkscapeを必要とする。

inkscape.org (accessd on 2021/6/15)

さて、ここが詰まりポイントである。通常インストーラーでインストールするとC:\Program Files\へインストールされるのだが、Program Filesのスペースのせいでnbconvertsvg2pdf.pyが機能しない。InkscapeをCドライブ直下などに配置しなおすことで対処可能。このとき、C:\Inkscape\binにPATHを通すのを忘れないようにする。

PDFの日本語出力

レポートを書いていてすぐに気が付くのが、日本語が出力されないことである。日本の大学で教授も日本人(たとえいくら国際学会で活躍していたとしても)となると名前ぐらいは漢字で書くのが普通だと思うので厄介な問題となる。

結論から言うと、~\.julia\conda\3\share\jupyter\nbconvert\templates\latex\index.tex.j2を次のように書き換える。

((=- Default to the notebook output style -=))
((*- if not cell_style is defined -*))
    ((* set cell_style = 'style_jupyter.tex.j2' *))
((*- endif -*))

((=- Inherit from the specified cell style. -=))
((* extends cell_style *))


%===============================================================================
% Latex Article
%===============================================================================

((*- block docclass -*))
\documentclass[xelatex,ja=standard]{bxjsarticle} # ここ(このコメントは削除すべし)
((*- endblock docclass -*))

もしファイルが見つからなければ、

$ cd ~\.julia\conda\3\Scripts
$ ./jupyter --path

で出力されたディレクトリを調べる。

最後に

どれも間接的にエラーとして出力はされてはいるんだけど、解決法は示してくれなくて相当手間取った。 例えば、「nbconvert is not installed」って出るけどpipで確認するとインストールされているから意味がわからないし、MiKTeXがインストールされていてパスも通っているのに「latex is not installed」って出るからイライラが募った。

特にInkscape周りはそもそもエラーが文字化けしてて何が問題なのかさっぱりだった。ディレクトリ名にスペース入れるのやめてもらえませんか?

mod_wsgiする際のちょっとした注意点

ちょっとしたメモです。

PythonWEBアプリケーションをApache2mod_wsgiでデプロイする際に

apt update 
apt install -y apache2
pip install mod_wsgi

だけでは、pipの所でエラーが出てしまい、Dockerイメージのビルドとかが失敗します。

Quick Installation Guide — mod_wsgi 4.8.0 documentation (accessed on 2021/6/13)

ただドキュメント読んでなかっただけなのですぐに解決しました。

apt update 
apt install -y apache2
apt install -y apache2-dev  # ←これが必要
pip install mod-wsgi

ドキュメントはよく読みましょう。

Docker + Selenium + arm64

RaspberryPiにubunbu入れてDockerでSeleniumしようとしたら、docker-seleniumのイメージがarm64に対応してなかったり、https://chromedriver.chromium.org/downloadsから落としてきたchromedriverが機能しなかったりで困り果てたので何とかSeleniumをするためのメモ。

コンテナの立ち上げ

適当なコンテナを起動する。

$ docker run -it --rm python:3.9.5-buster /bin/bash

以下はコンテナ内での作業

chromium-browser と chromium-chromedriverのインストール

launchpad.net

上記URLからchromium-browser(依存パーケージも必要)とchromium-chromedriver.debファイルを落としてくる。

# chrome.sh

# 依存パーケージ
wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-codecs-ffmpeg_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb
wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-codecs-ffmpeg-extra_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb

# chromium-browser
wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-browser_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb

# chromium-chromedriver
wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-chromedriver_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb

# 全てインストール
apt update
apt install -y ./chromium-codecs-ffmpeg_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb
apt install -y ./chromium-codecs-ffmpeg-extra_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb
apt install -y ./chromium-browser_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb
apt install -y ./chromium-chromedriver_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb

# Seleniumもインストール
pip install --upgrade pip
pip install selenium

サンプルスクリプト

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

def main():
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')
    options.add_argument('--no-sandbox') # これがないとエラーが出る
    driver = webdriver.Chrome(
        '/usr/lib/chromium-browser/chromedriver',
        options=options)

    driver.get('https://www.google.co.jp/')
    search = driver.find_element_by_name('q')
    search.send_keys('Python')
    search.send_keys(Keys.RETURN)

    time.sleep(3)
    driver.save_screenshot('search.png')

    driver.close()

if __name__ == '__main__':
    main()

f:id:nnt339:20210612132911p:plain

文字化けはその内直す。

Dockerfile

サンプル用のDockerfile

FROM python:3.9.5-buster

WORKDIR /opt/chrome
RUN wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-codecs-ffmpeg_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-codecs-ffmpeg-extra_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-browser_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && wget https://launchpad.net/~canonical-chromium-builds/+archive/ubuntu/stage/+files/chromium-chromedriver_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && apt update \
 && apt install -y ./chromium-codecs-ffmpeg_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && apt install -y ./chromium-codecs-ffmpeg-extra_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && apt install -y ./chromium-browser_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && apt install -y ./chromium-chromedriver_90.0.4430.93-0ubuntu0.16.04.1_arm64.deb \
 && apt clean \ 
 && rm -rf /var/lib/apt/lists/*

RUN pip install --upgrade pip \
 && pip install selenium

ENV PATH $PATH:/usr/lib/chromium-browser/chromedriver

おまけ:Windows10 + Docker + Selenium

Windows10でWSL使ってDockerを動かした環境でSeleniumする例。

# Dockerfile #
FROM python:3.9.5-buster
# chromium のインストール
RUN apt update \
 && apt install -y chromium \
 && apt clean \      # 掃除
 && rm -rf /var/lib/apt/lists/*

# chromedriver のダウンロード
WORKDIR /opt/chromedriver
RUN wget https://chromedriver.storage.googleapis.com/90.0.4430.24/chromedriver_linux64.zip \
 && unzip chromedriver_linux64.zip \
 && rm chromedriver_linux64.zip

# chromediriver にパスを通す
ENV PATH $PATH:/opt/chromedriver

# Selenium のインストール
RUN pip install --upgrade pip \
 && pip install selenium

CMD ["/bin/bash"]

chromedriverはchromiumのヴァージョンを確認してからhttps://chromedriver.chromium.org/downloadsから探してくる。linux64のchromedriverでOK。

RaspberryPi + Python3.9でmatplotlib/pandas

nnt339es.hatenablog.com

折角ビルドしたのにmatplotlibもpandasもpipできなかったので結局Docker。

$ curl -fsSL https://get.docker.com/ | bash

参考

docs.docker.jp (accessed on 2021/6/10)

Dockerfile

適当にDockerfile書いて終わり。

FROM python:3.9.5-buster as builder

WORKDIR /opt/app
COPY requirements.lock /opt/app
RUN pip install --upgrade pip \
 && pip install -r requirements.lock

FROM python:3.9.5-slim-buster as runner

WORKDIR /opt/app 


COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages


CMD ["/bin/bash"]

nnt339es.hatenablog.com

RaspberryPiへのPythonインストールメモ

RaspberryPiにUbuntu入れて適当にPythonインストールしてたらSSL関連でpipが詰まったのでそのメモ。

$ sudo apt update
# ビルド用ライブラリのインストール (これを忘れていた。)
$ sudo apt install build-essential libbz2-dev libdb-dev \
  libreadline-dev libffi-dev libgdbm-dev liblzma-dev \
  libncursesw5-dev libsqlite3-dev libssl-dev \
  zlib1g-dev uuid-dev tk-dev

# ソースのダウンロード
$ wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tar.xz

# 解凍
$ tar zxvf Python-3.9.5.tgz

$ cd Python-3.9.5
# ビルドコンフィグ
$ ./configure
# コンパイル
$ make
# インストール
$ sudo make install

# Version確認
$ python3.9 --version
# ワーキングディレクトリの作成
$ mkdir work
$ cd work

# venv環境作成
$ python3.9 -m venv venv

# venv環境起動
$ source venv/bin/activate

参考

www.python.jp

OpenGLで遊ぶ

OpenGLを触るきっかけがあったのでfreeglutで遊んでみた。

Homer Simpson

かわいい。

よく使ったオブジェクト

void gluSphere(
    GLUquadric *qobj, 
    GLdouble radius, 
    GLint slices, 
    GLint stacks
);
GLquadric *qobj

quadricオブジェクトのポインタ。 初学者なのでquadricオブジェクトが何か知らない。 gluNewQuadric()で全て誤魔化した。

GLdouble radius

半径。

GLint slices

経度の分割数。 数が大きいほど滑らか。

GLint stacks

緯度の分割数。

円筒

void gluCylinder(
    GLUquadric *qobj,
    GLdouble   baseRadius,
    GLdouble   topRadius,
    GLdouble   height,
    GLint      slices,
    GLint      stacks
);
GLquadric *qobj

quadricオブジェクトのポインタ。

GLdouble baseRadiius

底面の半径。

GLdouble topRadius

上面の半径。

GLdouble height

円筒の高さ

GLint slices

経度の分割数。

GLint stacks

緯度の分割数。

座標の平行移動

void glTranslated(
    GLdouble x, 
    GLdouble y, 
    GLdouble z
);
GLdouble x, y, z

平行移動するベクトル。

座標の回転

void glRotated(
    GLdouble angle,
    GLdouble x,
    GLdouble y,
    GLdouble z
);
GLdouble angle

回転する角度。

GLdouble x, y, z

回転軸ベクトル。

拡大・縮小

void glScaled(
    GLdouble x,
    GLdouble y,
    GLdouble z
);
GLdouble x, y, z

それぞれの方向に対する拡大率。

座標系の一時的な変更

void glPushMatrix(void);

座標系の保存。

void glPopMatrix(void);

座標系の元に戻す。

eg.

glPushMatrix();
    /* 座標系を変更する処理 */
    glTranslated(1.0, 0.0, 0.0); // ... (#)
    glSphere(glNewQuadric(), 1.0, 1.0, 3.0, 30, 30);
glPopMatrix();

/* ここでは(#)の処理が反映されない */
glSphere(glNewQuadric(), 1.0, 1.0, 3.0, 30, 30);

C言語だから仕方ないけど、Pythonなら絶対withステートメント使う。

色付け

void glMaterialfv(
    GLenum face, 
    GLenum pname, 
    const GLfloat *params
);
GLenum face

GL_FRONT: 表 GL_BACK: 裏 GL_FRONT_AND_BACK: 表と裏

GLenum pname

GL_DIFFUSE: 拡散反射 GL_SPECULAR: 鏡面反射 GL_AMBIENT: 環境光

const GLfloat *params

色を指定する。 (0.0, 1.0)のRGBと透過率(α値)で指定する。

// eg.
GLfloat red[] = {1.0, 0.0, 0.0, 1.0};

まだこんな少数の関数しか覚えてないけど、誤魔化しながらホーマー描けたので満足。

参考文献

docs.microsoft.com

(accessed on 2021/05/23)

#include <GL/glut.h>
#include <math.h>
#define PI2 (3.1415*2)

GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
GLfloat jeans[] = {0, 0.5, 0.7, 1.0};
GLfloat yellow[] = {1.0, 0.9, 0.0, 1.0};
GLfloat stubble[] = {0.596, 0.486, 0.165, 1.0};
GLfloat grey[] = {0.4, 0.4, 0.4, 1.0};
GLfloat black[] = {0, 0, 0, 1.0};

void Homer();
void head();
void body();
void arms();
void legs();

void eyes();
void mouse();
void nose();
void ears();
void hand(int isRight);


void Homer(void) {
    /* head */
    glPushMatrix();
        glTranslatef(0, 0.2, 2.2);
        glScalef(0.8, 0.8, 0.8);
        head();
    glPopMatrix();

    /* body */
    body();

    /* legs */
    glPushMatrix();
        glTranslated(0.0, 0.0, -2.5);
        legs();
    glPopMatrix();
}

void head(void) {
    GLdouble headHeight = 1.05;
    GLdouble headRadius = 0.63;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
    glPushMatrix();
        glRotatef(20, 1, 0, 0);
        glPushMatrix();
            gluCylinder(gluNewQuadric(), headRadius+0.02, headRadius, headHeight, 30, 10);
            glPushMatrix();
                glTranslated(0, 0.0, 0.01);
                glScalef(1, 1, 0.9);
                gluSphere(gluNewQuadric(), headRadius+0.02, 30, 30);
            glPopMatrix();
            glPushMatrix();
                glTranslated(0, 0, headHeight);
                glScalef(1, 1, 0.8);
                gluSphere(gluNewQuadric(), headRadius, 30, 30);
            glPopMatrix();
        glPopMatrix();

        /* hear side */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, black);
        int numSpike = 30;
        double step = PI2 / numSpike;
        GLdouble sideHearHeight = 0.45;
        GLdouble sideHearRadius = headRadius + 0.01;
        GLdouble sideHearWidth = 2.0;
        int start = 5, end = 23;
        glLineWidth(sideHearWidth);
        glBegin(GL_LINE_STRIP);
        for (int i = 0; i < numSpike; i++) {
            if (i < start) continue;
            if (i > end) continue;
            glVertex3d(sideHearRadius * sin(i * step), sideHearRadius * cos(i * step), sideHearHeight);
            glVertex3d(sideHearRadius * sin((i+0.5) * step), sideHearRadius * cos((i+0.5) * step), sideHearHeight+0.3);
        }
        glVertex3d(sideHearRadius * sin((end+1) * step), sideHearRadius * cos((end+1) * step), sideHearHeight);
        glEnd();
        
        /* hear top */
        GLdouble topHearHeight = headHeight + 0.3;
        GLdouble between = 0.1;
        for (int i = 0; i < 2; i++) {
            glPushMatrix();
                glTranslated(0, (i == 0)? between : -between, topHearHeight);
                hear();
            glPopMatrix();
        }
    glPopMatrix();

    /* eyes */
    GLdouble eyePos[] = {0, 0.4, 0.60};
    glPushMatrix();
        glTranslated(eyePos[0], eyePos[1], eyePos[2]);
        eyes();
    glPopMatrix();

    /* mouse */
    GLdouble mousePos[] = {0, 0.29, -0.08};
    glPushMatrix();
        glTranslated(mousePos[0], mousePos[1], mousePos[2]);
        mouse();
    glPopMatrix();

    /* nose */
    GLdouble nosePos[] = {0, 0.5, 0.36};
    glPushMatrix();
        glTranslated(nosePos[0], nosePos[1], nosePos[2]);
        nose();
    glPopMatrix();

    /* ears */
    GLdouble earPos[] = {0, 0, 0.27};
    glPushMatrix();
        glTranslated(earPos[0], earPos[1], earPos[2]);
        ears();
    glPopMatrix();
}


void body(void) {
    /* arms */
    GLdouble armPos[] = {0, 0, 1.55};
    glPushMatrix();
        glTranslated(armPos[0], armPos[1], armPos[2]);
        arms();
    glPopMatrix();

    glPushMatrix();
        /* waist */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, jeans);
        gluSphere(gluNewQuadric(), 1.0, 30, 30);
        
        /* chest */
        glTranslatef(0, 0, -0.18);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
        gluCylinder(gluNewQuadric(), 1.15, 0.4, 1.9, 30, 10);

        /* neck */
        glTranslated(0, 0, 1.7);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
        gluCylinder(gluNewQuadric(), 0.4, 0.4, 0.6, 30, 10);
    glPopMatrix();
}

void arms(void) {
    GLdouble sleeveLength = 0.5;
    GLdouble sleeveRadius = 0.4;
    GLdouble armLength = 1.0;
    GLdouble pos[3] = {0.4, 0, 0};
    GLfloat angle = -30;

    /* right arm */
    glPushMatrix();
        glTranslated(pos[0], pos[1], pos[2]);
        glRotatef(angle, 0, 1, 0);
        sleeve_and_arm(sleeveRadius, sleeveLength, armLength, 1);
    glPopMatrix();

    /* left arm */
    glPushMatrix();
        glTranslated(-pos[0], pos[1], pos[2]);
        glRotatef(-angle, 0, 1, 0);
        sleeve_and_arm(sleeveRadius, sleeveLength, armLength, 0);
    glPopMatrix();
}

void sleeve_and_arm(GLdouble sleeveRadius, GLdouble sleeveLength, GLdouble armLength, int isRight) {
    glPushMatrix();
        /* sleeve */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
        glTranslated(0, 0, -sleeveRadius);
        gluSphere(gluNewQuadric(), sleeveRadius, 30, 30);
        glTranslated(0, 0, -sleeveLength);
        gluCylinder(gluNewQuadric(), 0.4, 0.4, sleeveLength, 30, 30);

        /* arm */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
        glTranslated(0, 0, -armLength);
        gluCylinder(gluNewQuadric(), 0.3, 0.35, armLength+sleeveLength, 30, 30);

        /* hand */
        hand(isRight);
    glPopMatrix();
}

void hand(int isRight) {
    GLdouble thumbLength = 0.25;
    GLdouble indexLength = 0.36;
    GLdouble middleLength = 0.36;
    GLdouble pinkieLength = 0.35;
    double t = (isRight)? 1.0 : -1.0;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
    glPushMatrix();
        glScalef(1, 1, 0.6);
        gluSphere(gluNewQuadric(), 0.3, 30, 30);
    glPopMatrix();
    
    /* thumb */
    glPushMatrix();
        glRotatef(20*t, 0, 1, 0);
        glTranslated(-0.11*t, 0.16, -thumbLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.11, thumbLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* index */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.13*t, 0.12, -indexLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.12, indexLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* middle */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.17*t, -0.03, -middleLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.12, middleLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* pinlie */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.13*t, -0.14, -pinkieLength);
        gluCylinder(gluNewQuadric(), 0.09, 0.11, pinkieLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.09, 30, 30);   
    glPopMatrix();
}

void legs(void) {
    GLdouble legLength = 2.3;
    GLfloat pos[3] = {0.37, 0, 0};
    GLdouble legRadius = 0.5;
    for (int i = 0; i < 2; i++) {
        glPushMatrix();
            glMaterialfv(GL_FRONT, GL_DIFFUSE, jeans);
            glTranslatef((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            gluCylinder(gluNewQuadric(), legRadius, legRadius, legLength, 30, 10);
            
            /* shoe */
            glMaterialfv(GL_FRONT, GL_DIFFUSE, grey);
            glScalef(1, 1.4, 0.45);
            glTranslated(0, 0.1, -0.2);
            gluSphere(gluNewQuadric(), legRadius, 30, 30);
        glPopMatrix();
    }
}

void eyes(void) {
    GLdouble pos[3] = {0.24, 0, 0};
    GLdouble eyeSize = 0.24;
    
    glPushMatrix();
        glScalef(1, 0.8, 1);
        for (int i = 0; i < 2; i++) {
            glPushMatrix();
            glTranslated((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, white); // color
            gluSphere(gluNewQuadric(), eyeSize, 30, 30);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, black); // color
            glTranslated(0, eyeSize-0.01, 0);
            glScalef(1, 0.6, 1);
            gluSphere(gluNewQuadric(), 0.06, 30, 30);
            glPopMatrix();
        }
    glPopMatrix();
}

void mouse(void) {
    glMaterialfv(GL_FRONT, GL_DIFFUSE, stubble); // color
    glPushMatrix();
        glScalef(1, 1, 0.68);
        glTranslated(0, -0.12, 0.10);
        gluSphere(gluNewQuadric(), 0.6, 20, 20);
    glPopMatrix();

    glPushMatrix();
        glTranslated(0, -0.06, -0.04);
        glScalef(1, 1, 0.6);
        gluSphere(gluNewQuadric(), 0.5, 20, 20);
    glPopMatrix();
}

void nose(void) {
    GLdouble noseSize = 0.12;
    GLdouble noseHeight = 0.15;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow); // color
    glPushMatrix();
        glRotatef(90, -1, 0, 0);
        glPushMatrix();
            gluCylinder(gluNewQuadric(), noseSize, noseSize, noseHeight, 20, 20);
            glTranslated(0, 0, noseHeight);
            gluSphere(gluNewQuadric(), noseSize, 20, 20);
        glPopMatrix();
    glPopMatrix();
}

void ears(void) {
    GLdouble pos[3] = {0.61, -0.2, 0};
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow); // color
    for (int i = 0; i < 2; i++) {
        glPushMatrix();
            glTranslated((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            glRotatef(100, 0, (i==0)? 1 : -1, 0);
            glRotatef(30, -1, 0, 0);
            glutSolidTorus(0.089, 0.08, 30, 30);
        glPopMatrix();
    }
}

void hear() {
    int n = 30;
    double step = PI2 / n;
    GLdouble hearRadius = 0.4;
    GLdouble hearWidth = 2.0;
    glLineWidth(hearWidth);
    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < n; i++) {
        glVertex3d(hearRadius * cos(i * step), 0, hearRadius * sin(i * step));
    }
    glEnd();
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    Homer();
    glutSwapBuffers();
}


void init(void) {
    
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    GLfloat lightPos0[] = {-5, 15, 10, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);
    
    glEnable(GL_LIGHT1);
    GLfloat lightPos1[] = {5, 15, 10, 1};
    glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30.0, 1, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(8, 15, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

int main(int argc, char *argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(600, 600);
    glutCreateWindow("Homer Simpson");
    glutDisplayFunc(display);
    init();
    glutMainLoop();
    return 0;
}

pyYGOBot(ygo_agent)の今後

一人回しの時間がもったいないから機械学習によって一人回しと同等の知見が得られないかと画策しているpyYGOBotであるが、今後の作業について整理したいと思う。

パッケージ化

いわゆる”AI”と呼ばれる機械学習の部分をパッケージとして独立させることで、さまざまなアプリケーションに導入できるようにしたいと思う。

パッケージ化に伴い、名前をtf_agentに倣ってygo_agentに変更する。

パソコンに不慣れな人々もユーザー層として捉えるにはGUI・実行ファイル形式が不可欠だ。その際パッケージとして”AI”を組み込んだ方が互いに独立して開発・テストできるため生産性が上がるだろうという判断だ。

ドキュメントの整備

パッケージとして誰かに使ってもらうにはドキュメントが不可欠だ。

個人プロジェクトである以上モチベーションによって開発にブランクができてしまうため、あれば復帰する時に便利なはず。

元々可読性の高さを意識してコーディングしているため数か月経ってもコードは読めたが、それでも脳内にプログラムを構築するのに数時間はかかったためできれば節約したい。

テストコードの実装

設計図のないままリファクタリングするのに疲れた。というかテストするの自体疲れた。自動化して楽になりたい。

ドキュメントを整備する過程で設計が浮かびあがてくるはずだから、ドキュメントを書きながらテストコードを書きたい。

サーバー(ホスト)の実装

実質ADSを自作するということ。
もちろんGUIはなし。
サードパーティから独立できれば、頒布・導入・拡張がやりやすくなるはず。

Goで実装

いずれはGoで実装したいなあとかなんとか。

計算機の購入

現状のショボいビジネスノートじゃ学習がままならないので、並列計算機を実費で購入するか、あるいはクラウドファンディング等で資金を募るか。

そのためにも十分にテストされた機能するコードを完成させる必要があるなと。

おわりに

計画してから半年経つけど、まだ「とりあえず動くコード」しかできてなくて、AIの学習効率向上、精度改善みたいな段階にいけてないのが辛い。

「金銭でも名誉でもなんでも良いけど何か利益でんの?」とか考えだすと途端にモチベが落ちる。暇だからやっている。プログラマになるつもりはない。でも将来的(数年後)には職が欲しい。あほくさ。

春休みにはJulia習得して、暇つぶしに競技プログラミングやるときにはJulia使ってる。近い未来に数値シミュレーションするときはFortranではなく、きっとJulia使う。

JavaScriptきらいTypeScriptすき。
流行りのRustとRやって言語オタク極めるか?



何かに束縛されている。何だろう。おそらく将来に束縛されている。無垢なあの頃のように将来から解放されたい。とりあえず進んでおけば手が広いという数年後の自画像があるあの頃に。

これ以上選択を引き延ばせば専門オタクに何も勝てない虚無の存在になってしまうという恐怖と専門性に拘束されてしまう嫌悪が現代の若者の生きづらさではないか?