シンギュラリティは遠い

人に理解されないというのは大変な苦しみです。 しかし、いざ理解されれば惨めです。

シンギュラリティは遠い

色んな人を傷つけてきたし、それと同じくらい色んな人に傷つけられてきた気がする。もうどの一人をとっても顔さえハッキリ思い出せない。

昔は、人から嫌な目に合わされる度に、なるべくこいつが碌でもない人生を送ってほしいとか、俺はこいつより偉大になってやるとか思ってた気がする。 今はもうそんなに子供じゃないし、人との間には決して乗り越えられない壁があることを知っているし、その壁の向こうで何が起きても、もはや俺の知るところではない。

周りの人に期待せず淡白に付き合ってると、感情があんまり動かないので思い出も残らないし、本当に時間が経つのが早い。 最近はもう、興味のない動画を倍速で回すみたいに日々が過ぎていく。

それで良いのかもしれない。 今はただ、いち早く、滞りなく、全て過ぎ去って終わりへと至ってほしい。

Intel® Math Kernel Libraryを導入してFortranから利用する

この記事の概要

Intel® oneAPIという高性能な数値計算ツールキットがある。 以前は結構な値段だったらしいが、昨年あたりから無料で使えるようになった (Intel® oneAPI FAQ)。

oneAPIにはプロファイラーとか深層学習とか色々便利なツールが含まれている。そのうちの一つのMath Kernel Library(mkl)は、LAPACK95やFFTなど高速な数値計算処理を提供する。 LAPACK95のようなライブラリを自力でダウンロード・ビルド・リンクまでするのはかなり手間だが、これからはoneAPIで一括でもってこられる。

そういうわけでこの記事では、oneAPIのインストール手順を紹介し、使用例としてIntel® Fortran Compiler(ifort) からIntel® Math Kernel Libraryの手続きを呼び出して実行する方法を説明する。

Intel® oneAPI Toolkit のインストール

私はUbuntu上にインストールしたので以下のコマンドで。

おおむね公式ドキュメント( APT - Intel® oneAPI Toolkits Installation Guide for Linux* OS ) の通り。

wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \
| gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null

echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list

sudo apt update

# basekitにはMath Kernel Libraryが含まれる
sudo apt install intel-basekit

# hpckitにはFortran Compilerが含まれる
sudo apt install intel-hpckit

# 環境変数の設定はsetvars.shがよしなにやってくれる
echo "source /opt/intel/oneapi/setvars.sh" >> zsh.rc

最後の行について、setvars.shをShellの起動時に毎回実行させると結構起動がおそくなるので、 alias activate-oneapi="source /opt/intel/oneapi/setvars.sh"などとしてもいいかもしれない。

インストールが完了したことの確認。

$ ifort --version
ifort (IFORT) 2021.5.0 20211109
Copyright (C) 1985-2021 Intel Corporation.  All rights reserved.

$ mkl_help
oneAPI MKL vars.sh Syntax:
    ...

線型方程式系を解く

mklに含まれるlapack95を使って線型方程式系を解いてみる。

コード

! solve_linear_equation.f90
program main
    use lapack95, only: getrf, getrs

    implicit none

    integer, parameter :: N = 3
    integer :: i
    integer :: ipiv(N) ! pivot indices
    real :: a(N,N) ! matrix A
    real :: b(N) ! vector b

    a(:, :) = reshape( &
        [2., -1., 1., 1., 1., 2., 1., -1., 3.], &
        [3, 3] &
    )

    b(:) = [2, 3, -10]

    print *, "A:"
    do i=1, N
        print '(3f6.2)', a(i, :)
    end do

    print *, "b:"
    print '(3f6.2)', b(:)

    ! LU factorization
    call getrf(a, ipiv)
    ! solve linear equations with an LU-factored matrix
    call getrs(a, ipiv, b)

    print *, "x:"
    print '(3f6.2)', b(:)
end program main

各手続の取説はこの辺を見ると良いgetrs - Developer Reference for Intel® oneAPI Math Kernel Library - Fortran

lapack95の手続は総称名になっていて、 例えばgetrfは引数が単精度ならsgetrfに、 倍精度ならdgetrfに勝手にリダイレクトしてくれるので型を気にせず使える。 また、純粋手続になっているのでpure functionの中でも使える。 総じて非常に取り回しが良い。

Ref: Intel® MKL Fortran 95 Interfaces for LAPACK Routines vs. Netlib Implementation

コンパイル

# compile
ifort -c solve_linear_equations.f90 -c -qmkl
# link
ifort solve_linear_equations.o -lmkl_lapack95_lp64 -qmkl

-qmklオプションを付けることでコンパイラが lapack95等のmklモジュールを参照してくれる。

Ref: qmkl, Qmkl - Intel® oneAPI DPC++/C++ Compiler Developer Guide and Reference

が、どうやらモジュールはライブラリの実体を含んでおらず インターフェイスを提供するのみのようで、リンク時には 使用するモジュールに合わせて-lmkl_lapack95_lp64のようにリンクさせてやる必要がある。

結果

$ ./a.out
 A:
  2.00  1.00  1.00
 -1.00  1.00 -1.00
  1.00  2.00  3.00
 b:
  2.00  3.00-10.00
 x:
  3.00  1.00 -5.00

尚、MacOSでは実行時にDYLD_LIBRARY_PATHの指定が必要だった。

export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}:/opt/intel/oneapi/mkl/latest/lib"

メモ(MacOS標準アプリ)の中身をプレーンテキストファイルに出力する

概要と結果

MacOS標準アプリの『メモ (Notes)』の中身を移行しようと思ったら、ファイル実体がSQLiteのバイナリデータだった。

このバイナリを以下の通りまあまあ許容できる範囲でプレーンテキスト化することに成功した。

f:id:tatsuzaki98:20210517225234p:plain
Before(メモアプリの画面)
f:id:tatsuzaki98:20210517225301p:plain
After(プレーンテキスト化した。末尾にASCII記号が混入してしまう。)

以下のリポジトリで公開している。(かなりやっつけ仕事で書いているので万が一使う場合は自己責任で)

github.com

バイナリデータの正体はMacBinary Archive

SQLiteのBlobをファイルとして保存したところ、MacBinary Archiveというファイルらしい。

f:id:tatsuzaki98:20210517225851p:plain
メモのファイル実体

MacOSなら標準のArchive Utilityで解凍できるが、Terminalからひらく場合は単に

$ open file

とすれば良い。

バイナリデータの解凍先はutf-8と非utf-8が混在する

バイナリデータを解凍したは良いものの、以下のようにutf-8と非utf-8のコードが混在している。書式などのデータだろうか。

f:id:tatsuzaki98:20210517230450p:plain
解凍したblob

ここまで来たらあとはutf-8の仕様に沿った部分だけdecodeしてやればよい。

utf-8を16進数文字列として読み込んだ時、ascii文字は20から7e、非ascii文字はe.[8-b].[8-b].である。

以下のような正規表現で無理矢理取り出す。

# python3
re.findall(r'(e. [8-b]. [8-b].) | ([2-7][0-f])', hex_str)

ここまでの処理をPython3とMakefileでまとめたので、詳細は冒頭のリポジトリを参照のこと。

以上。

素のJavaScript (ES6)でTodoアプリを作る、ついでにReactのrenderっぽい動きを再現する

完成品

f:id:tatsuzaki98:20210307001639g:plain
完成品

コード全文

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <script>
      const state = {
        todoList: [],
        formText: '',
      };

      const render = () => {
        document.getElementById('main').innerHTML = `
          <div>
            <div>
              <input
                type="text"
                onchange="handleEvents({type: 'INPUT_TEXT', event})"
                value="${state.formText}"
              />
              <input type="submit" onclick="handleEvents({type: 'SUBMIT'})"/>️
            </div>
            <hr/>
            <table id="table">
              ${state.todoList.length > 0 ? `
                <thead>
                  <tr>
                    <th/>
                    <th>memo</th>
                    <th>deadline</th>
                    <th/>
                    <th/>
                  </tr>
                </thead>
              ` : ``}
              <tbody>
                ${state.todoList.map((todo, key) => (`
                    <tr>
                      <td>
                        <button onclick="handleEvents({key: ${key}, type: 'CHECK'})">
                          check
                        </button>
                        <span>${todo.isChecked ? '✅' : ''}</span>
                      </td>
                      <td><span>${todo.text}</span></td>
                      <td>
                        <input
                          type="date"
                          onblur="handleEvents({key: ${key}, type: 'CHANGE_DATE', event})"
                          value="${todo.deadlineDate}"
                        />
                      </td>
                      <td>
                        <input
                          type="time"
                          onchange="handleEvents({key: ${key}, type: 'CHANGE_TIME', event})"
                          value="${todo.deadlineTime}"
                        />
                      </td>
                      <td>
                        <button onclick="handleEvents({key: ${key}, type: 'DELETE'})">
                          delete
                        </button>
                      </td>
                    </tr>
                `)).join('')}
              </tbody>
            </table>
            <hr/>
            <div>
              <button onclick="handleEvents({type: 'SORT'})">sort by deadline</button>
            </div>
          </div>
        `;
      };

      const handleEvents = (action) => {
        switch (action.type) {
          case 'INPUT_TEXT':
            state.formText = action.event.target.value;
            break;
          case 'SUBMIT':
            document.activeElement.blur();
            if (state.formText === '') {
              break;
            };
            const date = new Date();
            state.todoList.push({
              text: state.formText,
              isChecked: false,
              createdAt: date.getTime(),
              deadlineDate: '',
              deadlineTime: '',
            });
            state.formText = '';
            break;
          case 'CHECK':
            state.todoList[action.key] = {
              ...state.todoList[action.key],
              isChecked: state.todoList[action.key].isChecked ? false : true,
            };
            break;
          case 'DELETE':
            state.todoList.splice(action.key, 1);
            break;
          case 'CHANGE_DATE':
            state.todoList[action.key].deadlineDate = action.event.target.value;
            break;
          case 'CHANGE_TIME':
            state.todoList[action.key].deadlineTime = action.event.target.value;
            break;
          case 'SORT':
            const parseDateTime = (todo) => (
              todo.deadlineDate ?
                new Date(`${todo.deadlineDate} ${todo.deadlineTime}`) :
                new Date(todo.createdAt)
            );
            state.todoList.sort((before, after) => parseDateTime(before) - parseDateTime(after));
          default:
            break;
        }
        render();
      };

      window.onload = render;
    </script>
  </head>
  <body>
    <div id='main'></div>
  </body>
</html>

説明

表題の通りCDNフレームワーク等は使っていない。

最近はhtmlを書くだけでブラウザが勝手にカレンダーとか時計とかを表示してくれるので素晴らしい。

表題に「Reactのrenderっぽい動き」と書いたが、要点は三つで

  • state: Todoリストなどの状態を保持しておく
  • render: stateを画面に反映させる
  • handleEvents: ユーザーの操作を受け取ってstateを更新し、再度renderする

ということ。

Reactのrenderはもちろんもっと複雑にできているが、状態管理の設計の練習くらいにはなっただろうと思う。

TypeScriptでFirestoreにgetOrCreateをやる

Django REST Framework使った後だとgetOrCreateが欲しくなる。
実際はDocumentSnapshot.updateとかが便利すぎるので使い道はほぼ無い。

const db = firebase.firestore();

const fsGetOrCreate = (docPath: string, initialData: any): Promise<DocumentSnapshot> => {
  return new Promise(async (resolve, reject) => {
    const ref = db.doc(docPath);
    return ref.get()
        .then(async (snap) => {
          if (snap.exists) {
            resolve(snap);
          } else {
            await ref.create(initialData);
            const snap = await ref.get();
            return resolve(snap);
          }
        })
        .catch((error) => reject(error));
  });
};

人の本性と玉ねぎの皮の話

「人の本性は玉ねぎの皮のように何重にも覆い隠されていて、皮を一枚一枚剥くと本性が出てくる」という話を聞いた。しかしこの例え話は、甚だ実態とかけ離れているように思う。なぜなら、玉ねぎを包む一枚一枚の皮こそが、玉ねぎそのものだからである。

玉ねぎの皮を剥けば本当の姿が出てくるのか

良い玉ねぎとは何であるか。あるいは、悪い玉ねぎとは何であるか。例えば、外側が腐っている玉ねぎでも、内側が新鮮ならば良い玉ねぎなのか。逆に、外側の艶が良い玉ねぎも、内側が普通であれば並の玉ねぎなのか。
これらは本当に意味のない議論である。玉ねぎの質は、その内側の性質のみによっても外側の性質のみによっても評価されるものではない。玉ねぎを形成する一枚一枚の皮の全てが、玉ねぎの『本質』そのものなのである。

人の本性について

我々はいつも、他人について、或いは自分自身についてさえも、何か外側からは見えない素晴らしい本性があるはずだ、と期待している。しかしながら、人の本性は実に、外側によく現れている。日常の中の一つ一つの言動こそがその人の性質を形成するほとんど全てなのである。それらを剥がして奥を調べたところで、一体その人の本当の姿など何処にあろうか。
例えば、いつもは温厚な態度の男が、何気ない行き違いで激昂し、暴力を振るったとする。この男の本性は何であろうか。いつもの温厚さが本性なのか、激昂した様が本性なのか、あるいはそれよりも奥に別の本性が眠っているのか。実際のところ、彼の本性は「いつもは温厚だが激昂すると暴力を振るう」という、ただそれだけのことである。もちろんこれが彼の全てではないし、しかし確実に彼の一部である。このように、本性とはその人の在り方一つ一つが重なり合ってできているのではないか。
いずれにせよ、これは本性ではない、これもまた本性ではない、と思いなして、玉ねぎの皮を剥くように人の内側に神秘性を期待していても、実態からは離れるばかりである。

玉ねぎは内側から成長する

さて、玉ねぎの例えになぞらえて、もう一つ面白い話がある。それは、玉ねぎは内側から成長していて、その核心に至っては、常に新しい皮が生まれているという事実である。人の本性は実にこれに似ているではないか。これがその人の本性だと突き止めても、来週には全く違う形に変わっているかもしれない。そして、その変化が最も激しく起きている部分は、その人の最も深奥なのであって、他人からは決して見えない。
そうであるならば、他人について「この人の内側に眠る本性は何か」と考えるのは、実に空虚なことではないか。もっとも重要な部分は時事刻々と変化するのに、それについて一時的に知ったところで、一体どれほどの意味があろうか。むしろ、期待していた相手の本性との矛盾に悩まされるだけなのである。

GithubのPushログをGoogle Spreadsheetsに記録する

Google Apps Scriptを記述する

Google Spreadsheetsを新規作成します。SpreadsheetのIDとシートの名前を控えておきます。スクリプトからシートを呼び出すときに必要になります。

f:id:tatsuzaki98:20201006011929p:plain
Spreadsheet IDとSheet Nameを控えておく

上のメニューバーから『ツール→スクリプトエディタ』を開きます。

f:id:tatsuzaki98:20201006012529p:plain
スクリプトを記入する画面

スクリプトエディタでGithub Webhookから送られてくる処理をSpreadsheetに記述する処理を書きます。
一番簡単なスクリプトを下に載せておきます。

const doPost = (e) => {
  const dataString = e.postData.getDataAsString();
  const data = JSON.parse(dataString);
  edit(data);
  return;
};

const edit = (data) => {
  const commits = data["commits"];
  const sheet = SpreadsheetApp
      .openById(YOUR_SPREADSHEET_ID_HERE)
      .getSheetByName(YOUR_SHEET_NAME_HERE);
  sheet
      .getRange(sheet.getLastRow()+1, 1, commits.length, 5)
      .setValues(commits.map((commit, key) => ([
        commit["id"],
        commit["message"],
        commit["author"]["name"],
        commit["timestamp"],
        commit["url"],
      ])));
};

スクリプト中のSpreadsheet IDとSheet Nameは各自のものに差し替えておいてください。

スクリプトウェブアプリケーションとして導入する

上のメニューから『公開→ウェブアプリケーションとして導入』を選択します。

f:id:tatsuzaki98:20201006013146p:plain
Webアプリとして導入

who has access to the app:のところをanyone, even anonymousに変更しておきます。
Github Webhookのリクエストを通すためです。

Deployを押すと、「スプレッドシートスクリプトによって操作されますが許可しますか」的なことを聞かれるので構わず続行します。

f:id:tatsuzaki98:20201006013813p:plain
初見でこのページ見るとビビる

これで導入は完了です。

『公開→ウェブアプリケーションとして導入』のところに『Current web app URL』が表示されているはずです。
"https://script.google.com/macros/s/{YOUR_APPS_ID}/exec"の形式のやつです。
このURLに、ためしにbash curlでそれっぽいデータをPOSTリクエストしてみます。

curl \
  -X POST \
  -H 'Content-Type: application/json' \
  -d '{"commits": [{"id": "1", "message": "hello.", "author": {"name": "me"}, "timestamp": "2020/10/10", "url": "example.com"}]}' \
  'https://script.google.com/macros/s/AKfycbw6crKJ9ZJErVurbDI5ZkeZut3a-lh8MULELKR1EM3k_DfNrpQ/exec'

下のようなメッセージが返ってくるはずです。

f:id:tatsuzaki98:20201006015208p:plain
POSTリクエストが完了した

何も返されませんでしたというエラーが出ていますが、これはdoPost関数の返り値をしていないからで、今回の場合は問題になりません。

○行目でエラーが生じました、的なメッセージが出た場合は、スクリプトの該当箇所を修正したのちに、改めて『ウェブアプリケーションとして導入』からProject VersionをNewにして更新するとうまくいくようです。

f:id:tatsuzaki98:20201006015638p:plain
spreadsheetにも反映されている

Spreadsheetにも無事反映されています。

Github WebhookにスクリプトのURLを貼る

自分のレポジトリのページで、設定→Webhookを開きます。URLはhttps://github.com/{USER_NAME}/{REPOSITORY_NAME}/settings/hooksです。

f:id:tatsuzaki98:20201006020112p:plain
Webhookを追加するページ

『Add Webhook』を開き、『Payload URL』に先ほどの『Current web app URL』を貼り付けます。また、『Content type』を『application/json』に変更しておきます。

f:id:tatsuzaki98:20201006021135p:plain
Add Webhook

これで全ての準備は完了です。
試しになにかPushして、Spreadsheetに反映されるか見てみましょう。

f:id:tatsuzaki98:20201006021538p:plain
ok

無事成功しました。
POSTリクエストの中身は、先ほどのGithubのWebhookを設定するページの下の方から確認することができます。

f:id:tatsuzaki98:20201006022232p:plain
POSTリクエストの中身

うまくいかなかった場合はここを確認しつつ試行錯誤してみてください。
以上です。