シンギュラリティは遠い
人に理解されないというのは大変な苦しみです。 しかし、いざ理解されれば惨めです。
シンギュラリティは遠い
色んな人を傷つけてきたし、それと同じくらい色んな人に傷つけられてきた気がする。もうどの一人をとっても顔さえハッキリ思い出せない。
昔は、人から嫌な目に合わされる度に、なるべくこいつが碌でもない人生を送ってほしいとか、俺はこいつより偉大になってやるとか思ってた気がする。 今はもうそんなに子供じゃないし、人との間には決して乗り越えられない壁があることを知っているし、その壁の向こうで何が起きても、もはや俺の知るところではない。
周りの人に期待せず淡白に付き合ってると、感情があんまり動かないので思い出も残らないし、本当に時間が経つのが早い。 最近はもう、興味のない動画を倍速で回すみたいに日々が過ぎていく。
それで良いのかもしれない。 今はただ、いち早く、滞りなく、全て過ぎ去って終わりへと至ってほしい。
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のバイナリデータだった。
メモの中身移行しようと思ったら実体ファイルSQLite Blobでクs…うんち!!!!!!!! pic.twitter.com/oqeR08R7bl
— 上位者アメンドーズ (@tatsuzaki98) 2021年5月14日
このバイナリを以下の通りまあまあ許容できる範囲でプレーンテキスト化することに成功した。
以下のリポジトリで公開している。(かなりやっつけ仕事で書いているので万が一使う場合は自己責任で)
バイナリデータの正体はMacBinary Archive
SQLiteのBlobをファイルとして保存したところ、MacBinary Archiveというファイルらしい。
MacOSなら標準のArchive Utilityで解凍できるが、Terminalからひらく場合は単に
$ open file
とすれば良い。
バイナリデータの解凍先はutf-8と非utf-8が混在する
バイナリデータを解凍したは良いものの、以下のようにutf-8と非utf-8のコードが混在している。書式などのデータだろうか。
ここまで来たらあとは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っぽい動きを再現する
完成品
コード全文
<!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>
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とシートの名前を控えておきます。スクリプトからシートを呼び出すときに必要になります。
上のメニューバーから『ツール→スクリプトエディタ』を開きます。
スクリプトエディタで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は各自のものに差し替えておいてください。
スクリプトをウェブアプリケーションとして導入する
上のメニューから『公開→ウェブアプリケーションとして導入』を選択します。
who has access to the app:のところをanyone, even anonymousに変更しておきます。
Github Webhookのリクエストを通すためです。
Deployを押すと、「スプレッドシートがスクリプトによって操作されますが許可しますか」的なことを聞かれるので構わず続行します。
これで導入は完了です。
『公開→ウェブアプリケーションとして導入』のところに『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'
下のようなメッセージが返ってくるはずです。
何も返されませんでしたというエラーが出ていますが、これはdoPost関数の返り値をしていないからで、今回の場合は問題になりません。
○行目でエラーが生じました、的なメッセージが出た場合は、スクリプトの該当箇所を修正したのちに、改めて『ウェブアプリケーションとして導入』からProject VersionをNewにして更新するとうまくいくようです。
Spreadsheetにも無事反映されています。
Github WebhookにスクリプトのURLを貼る
自分のレポジトリのページで、設定→Webhookを開きます。URLはhttps://github.com/{USER_NAME}/{REPOSITORY_NAME}/settings/hooksです。
『Add Webhook』を開き、『Payload URL』に先ほどの『Current web app URL』を貼り付けます。また、『Content type』を『application/json』に変更しておきます。
これで全ての準備は完了です。
試しになにかPushして、Spreadsheetに反映されるか見てみましょう。
無事成功しました。
POSTリクエストの中身は、先ほどのGithubのWebhookを設定するページの下の方から確認することができます。
うまくいかなかった場合はここを確認しつつ試行錯誤してみてください。
以上です。