投稿者「mod_poppo」のアーカイブ

IIJmioの格安SIMを1ヶ月間使ってみた感想

IIJmioの格安SIMを買ってから1ヶ月が経ったので、実際に使って生活した感想を書く。

端末

結局Pocket WifiのGL02Pで使い続けている。前の記事でも書いたように、この端末はSIMフリーで、APNを設定すればドコモのMVNOでも使える。SIMの大きさは標準SIMだが、microSIMでもアダプタなしで騙し騙し使える。自分の生活圏では、ほとんど3Gしか掴まない。LTEの表示が出る場所(つまり、ドコモのLTEの1.7GHz帯の電波が飛んでいる)も一部にはあるようだ。

OLYMPUS DIGITAL CAMERAOLYMPUS DIGITAL CAMERAOLYMPUS DIGITAL CAMERAスクリーンショット 2014-11-06 0.17.43

利用状況

家にいる時は家の固定回線があり、大学にいる時は(建物によっては)学内無線LANが使えるので、モバイル回線にお世話になるのは、このどちらでもない場所となる。駅や電車内とか。ただし、電車通学ではないので通学中は使わない。

というわけなので、普段の生活の中で、家と大学だけで過ごす日はまったくモバイル回線を使わない。そういうわけで、日によってばらつきが大きく、(b-mobileやOCNにあるような)高速通信の容量が1日あたりで決まっているプランよりも、今回選んだ、高速通信の容量が1ヶ月あたりで決まっているプランの方が向いている。

利用量は多い日で100MBを超える程度。まったく使わない日も多いので、先月は一ヶ月の利用量が2GBに届かず、700MBくらい余った。なので、常に高速通信ONで問題なさそうである。まあ、これは外出先で容量を食うサービス等を利用していないからだろう。動画の視聴や、リモートデスクトップ、VNCはやっていない。外出先でのアプリのアップデートは避けたいと思っている。

今後

これからどうするか。先の記事にも書いたように、今持っている端末がアレなので、新しい端末を入手したい。iPhoneを買うか、Android端末か、モバイルルーターか。

自分の中ではNexus 5が有力候補だったが、調べたところFOMAプラスエリアに完全対応していないようで、そこが不安要素になっている。

あと、メインのドコモ回線をどうするかも悩む。今はFOMA契約で、ガラケーで使っているが、今後どうするか。

日記終わり。

自作PCにWindows 8.1を導入した

1年半前にパソコンを自作したのだが、今まではLinux (Arch Linux)だけを入れて使ってきた。だが、何となくWindows環境の必要性を感じたので、Windows 8.1 Pro (DSP版)を買ってきてインストールすることにした。無印じゃなくてProにしたのは、リモートデスクトップを使いたいというのが主な理由だ。

そのパソコンに入っているHDDは手持ちの物の再利用(前はMacBookに入っていたが、容量が狭くなり換装したので余った)で、潤沢に容量があるというわけではなかったので、HDDも新しいのを買った。

インストールだが、特に何の問題もなかった。DVDから起動して、(新しく導入した)HDDを選択して、おわり。Microsoftアカウントの名前はローマ字にしていたので、ホームディレクトリの名前はローマ字になった。

とりあえず導入したソフトウエアは

  • Firefox
  • Dropbox
  • iCloud for Windows
    • ついでにBonjourが入るので他の端末から ホスト名.local で参照できるようになる

で、明日以降もっとほかのソフトウエアとかも入れて環境を整えたい。

…と思ったが、Arch Linuxを起動できるか確認していない。Arch Linuxの時はrEFIndを使っていたが、WindowsのUEFI事情はよく分からない。明日以降なんとかしたい。Arch Wikiのこのへんが参考になるだろうか。

GIMPとゲームパッド on Linux

フリーのペイントソフト、GIMPをゲームパッドで操作したい。いわゆる「左手デバイス」とかいうの。まあ自分はそんなにお絵描きするわけじゃないんだけど。

ここの話はLinuxでの話で、WindowsとかOS Xとかは知らん。

手持ちのハードウエア

  • パソコン(自作のLinux機)
  • USBゲームパッド(エレコムのやつ)
  • ペンタブ(Wacom Bamboo CTH-661)(今回の主役ではない)PA250017

試した手順

1. メニューの「Edit > Preferences」を選択。

GIMP: Edit > Preferences

2. Preferences ダイアログの Input Devices > Input Controllers を選択

GIMP: Preferences > Input Controllers

3. Available Controllers の Linux Input を選択し、→ボタンで Active Controllers に追加。

GIMP: Configure Input Controller

4. Configure Input Controller ダイアログが出てくるので、Deviceとして PC Game Controller を選択。

GIMP: Configure Input Controller (PC Game Controller)これで、

  1. Grab eventをクリック
  2. ゲームパッドのボタンを押す
  3. Actionを割り当て

という感じでゲームパッドのボタンにGIMPの操作を割り当てることができる…と期待したがそうはいかなかった。ゲームパッドのボタンを押しても何も起こらない。Dump events…にチェックを入れてターミナルの出力を見ると、ボタンを押した事自体は認識されてはいるようだ。なぜだ。

GIMPのソースコードをいじる

しかたがないからGIMPのソースコードを見ていじくろう。GIMP Developer WikiHacking:Building/Linuxを参考にして、ソースコードをgitでチェックアウトする。

Linux Inputに対応するソースはどこだろう。gimp/modules/ が怪しい。見てみると、gimp/modules/controller-linux-input.c というソースファイルがある。これか。

ファイルの頭の方に static const LinuxInputEvent key_events[] という変数の定義がある。Configure Input Controllerダイアログに出てきたボタンの名前はこれか。ゲームパッドのキーを押したときにターミナルに出てくるログと、<linux/input.h> で定義されている定数の値を参考にしながら、BTN_*** に対応する定義を追加する。

差分としてはこんな感じになった。

diff --git a/modules/controller-linux-input.c b/modules/controller-linux-input.c
index 8759d10..de72332 100644
--- a/modules/controller-linux-input.c
+++ b/modules/controller-linux-input.c
@@ -81,8 +81,20 @@ static const LinuxInputEvent key_events[] =
{ BTN_GEAR_DOWN, "button-gear-down", N_("Button Gear Down") },
#endif
#ifdef BTN_GEAR_UP
-  { BTN_GEAR_UP,   "button-gear-up",   N_("Button Gear Up")   }
+  { BTN_GEAR_UP,   "button-gear-up",   N_("Button Gear Up")   },
#endif
+  { BTN_TRIGGER,   "button-trigger",   N_("Button Trigger")   },
+  { BTN_THUMB,     "button-thumb",     N_("Button Thumb")     },
+  { BTN_THUMB2,    "button-thumb2",    N_("Button Thumb 2")   },
+  { BTN_TOP,       "button-top",       N_("Button Top")       },
+  { BTN_TOP2,      "button-top2",      N_("Button Top 2")     },
+  { BTN_PINKIE,    "button-pinkie",    N_("Button Pinkie")    },
+  { BTN_BASE,      "button-base",      N_("Button Base")      },
+  { BTN_BASE2,     "button-base2",     N_("Button Base 2")    },
+  { BTN_BASE3,     "button-base3",     N_("Button Base 3")    },
+  { BTN_BASE4,     "button-base4",     N_("Button Base 4")    },
+  { BTN_BASE5,     "button-base5",     N_("Button Base 5")    },
+  { BTN_BASE6,     "button-base6",     N_("Button Base 6")    },
};

static const LinuxInputEvent rel_events[] =

あとは、さっきのWikiの手順にしたがってGIMPをビルドする。すると、さっきスクリーンショット付きで説明した手順で、ゲームパッドのボタンにGIMPの操作を割り当てることができる。

とりあえず気づいた点は、ボタンを押すときと離すときで2回操作が実行されるのと、ゲームパッドのボタンじゃなくて倒すやつには反応しないこと。既存のソースコードの見よう見まねじゃなくて、ちゃんと調べてコードを書かないといけない気がする。そもそもこのLinux Inputというやつはどのぐらい使われているんだろうか。これは意図した動作なのだろうか。

あと、自分の手持ちのゲームパッド1個で試したが、他のゲームパッドではどうなるのか。この辺の問題が解決したらパッチを投げるかもしれない。その前に開発コミュニティーの雰囲気とかつかんでおきたいが。パッチを投稿するにはBugzillaにBug(?)を登録してそれに添付する感じだろうか。この辺の手間を思うに、GitHubのPull Requestという仕組みは偉大だ。

ペンタブのパッド(ペンタブの感知するエリアの横についてるボタン)に対する操作も、Linux Inputで扱えそうな雰囲気を感じたが、Wacomのドライバと競合するのか知らないがなにかうまくいかない。

IIJmioの格安SIMを買った

いままでイーモバイル(最近Y! mobileになったけど)のLTE回線でPocket WiFiを使ってきたけど、その2年契約が切れるのを機に、最近流行の格安SIMを使ってみることにした。

ドコモのMVNOでいわゆる「格安SIM」を提供しているのはb-mobileをはじめとして数社あるが、今回選んだのはIIJmioのデータ専用プラン。これまで使ってきたイーモバイルのデータ専用回線から移行するので、音声通話やSMSは必要ないと判断した。IIJmioを選んだ理由は、

  • 最近増量されて一番安いプランの高速データ通信容量が2GBになった。他社と比べてお得?(そこまで徹底的に調べたわけではないけど)
  • 対応した端末を使えばIPv6が使える!(21世紀になって十数年も経ったんだからいい加減IPv6を使ってもいいよね?)

あたり。まあ2年縛りとかはない(IIJmioの場合は高々2ヶ月程度の最低利用期間があるけど)ので、他社のが良いと思ったら気軽に乗り換えられる。

購入方法は、家電量販店で「IIJmioウェルカムパック」を買ってウェブで登録することにした。物理的な店舗で買えば、SIMカードが送られてくるのを待つ必要がない。買って帰って登録すればすぐに使えるようになる。また、月額料金とは別に初期費用的な感じでSIMカードが3000円ちょいするが、量販店だと定価より安く買える可能性がある。ビックカメラで買えば無料でWi2の公衆無線LANが使えるらしいが、まあ別にいいかなと思って普段使っている量販店で買った。

端末は、とりあえず手持ちの端末で使うことにした。手持ちの端末は

  • ドコモの古いAndroidスマートフォン
    • 古いやつなのでLTE非対応。
    • MVNOのSIMだとテザリングができない。
    • SIMカードのサイズはmicroSIM。
  • Pocket WiFi GL02P
    • 今までよく知らなかったが、イーモバイルの端末(最近の物を除く)は基本的にSIMフリーらしい。なのでドコモのSIMカードを挿しても通信できる。
    • ただし、ドコモの一部周波数帯に非対応。山間部で強い味方となるFOMAプラスエリアも使えないらしい。
    • 他のモバイルルーターに対してこれといった優位点はない。自分の手持ちっていうだけ。
    • まるまる2年使っているわけなのでバッテリーがへたっている。モバイルブースターなしでは使えない状態。
      • SIMカードのサイズは標準SIMなので、microSIMとかnanoSIMで使う場合はアダプターが必要。

あたり。まあそのうち良さげなモバイルルーターとか買うと思う。タブレットを買う時にLTEモデルを買っておけば良かったかなあと思ったり。

IPv6を使うには、端末として

  • Nexus 5/7みたいな一部のスマートフォン・タブレット
    • SIMロックされたものを含む多くのスマートフォンが、IPv4とIPv6を選べないらしい。
  • モバイルルーターだと、NECのAterm MR03LN (および旧機種?のMR02LN)
    • Aterm MR03LNは現時点のSIMフリーなモバイルルーターとしてはトップクラスに評判がいいっぽい。
    • Aterm MR03LNはBluetooth PANによるテザリングができる。Bluetooth DUNと違って、iPad/iPod touchでも使える。(iPhoneはネットワークアクセスを提供する側の機能しか実装されていないので使えないらしい。参考
  • ドコモの一部の通信端末でも使えるらしい?参考(2年前の記事だけど)

が必要らしい。IIJmioによる接続確認端末の一覧がここにある。

下調べはこれぐらいにして、実際に使ってみた感想だが、特筆すべきところはなかった。普通に期待通りというか。GL02Pで電波の掴みが悪いのは周波数帯のせいなのか自分の家の電波環境が悪いのか。ドコモのスマートフォンでテザリングができないので、主にモバイルルーターで使うことになるだろうか。日記終わり。

Cabal sandboxのメモ

Cabalを使ってHaskellのパッケージ管理をやっていると、依存関係がもつれて酷い事になる。あるプロジェクトで使うパッケージと別のプロジェクトで使うパッケージが干渉したりとかなんかそういうアレだ。こういう時はsandboxを使うと良いらしい。マニュアル

自分のプロジェクトのディレクトリで

$ cabal sandbox init

を実行すれば、新しいsandboxができる。何かパッケージを入れようと思ったら、そのディレクトリで普通に

$ cabal install ほげほげ

を実行すればそのパッケージはそのディレクトリに入る。

sandbox内のパッケージを使ってghciを起動したい場合は、

$ cabal repl

を使う。

sandbox内のパッケージを使ってghcやrunghcなどのコマンドを叩きたい場合は、1.20.0で追加された(?)execコマンドを使うといいようだ。

$ cabal exec ghc -- --make hogehoge.hs

この記事はcabal-install 1.21.0.0の時点の情報をテキトーに調べた結果を書いたので、もしかしたらもっと正しい使い方とかあるのかもしれないが、そんなもん知らん。

(9月21日: typoを修正)

TypeScriptで組み込みオブジェクトを拡張できない

最近、自分で書くWebアプリ、ブラウザアプリには、JavaScriptの代替言語としてTypeScriptを使っている。

TypeScriptの気に入っている点は、

  • 静的型がついているので、変数名のタイポのような、初歩的だけど実行してみないとわからない面倒なバグをコンパイル時に検出できる
  • 文法やセマンティクスがほぼJavaScriptの上位互換なので、すでにJavaScriptを習得している身としては学習コストが低い
  • JavaScriptの資産を活用しやすい
  • 標準の型定義に入っていないブラウザの拡張API等にも、自分で型定義を書けば利用できる

あたりである。逆に、不満があるとすれば

  • セマンティクスがJavaScriptに沿ったものなので、演算子オーバーロードのようなJavaSciptに1:1で翻訳できない機能が使えない
  • 型クラスとかがない
  • ECMAScript 6で導入される予定の、letや分割代入やジェネレーターなどの、言語の文法に対する拡張に対応していない(新しいラムダ式のような一部の機能は既に導入されているので、将来的には入るかもしれないが)

あたりであろうか。まあ不満があったら他のaltJSを使えばいい話だが。

[2015年1月18日 追記] 以下の内容はTypeScript 1.4のリリースに伴い古くなりました。詳しくはこちら

ECMAScript 6で導入されるライブラリ関数は、一部は型宣言を書いてやることでTypeScriptからも使えるようになる。例えば、前の記事で Math.hypotMath.sinh などの関数に触れたが、それらは[sourcecode lang=”js”]
interface Math {
hypot(…values: number[]): number;
sinh(x: number): number;
}
[/sourcecode]のような宣言を書いておけば、コンパイルが通るようになる。まだこれらの関数を実装していないブラウザ用に、polyfill(shim)も書いておくか、読み込むといいだろう。

このほか、例えば、Array.prototype.find の型宣言は[sourcecode lang=”js”]
interface Array<T> {
find(callback: (element: T, index: number, array: T[]) => boolean, thisArg?: any): T;
}
[/sourcecode]というふうにできる。

ここまではいい。

JavaScriptのイディオムとして、配列ライクなオブジェクトを変換するのに、Array.prototype.slice が使われる。もちろんこのイディオムはTypeScriptでも使えるが、Array.prototype の型は Array<any> となっているので、要素の型に対する型チェックや型推論が働いてくれない。幸い、ECMAScript 6では、Array.from という関数が導入される予定なので、こいつの型を[sourcecode lang=”js”]
from<T>(arrayLike: {[n: number]: T; length: number}): T[];
[/sourcecode]のように宣言してやれば型推論とかが効いて便利なはずだ。

だがしかし。組み込みの Array オブジェクトの型は標準の lib.d.ts において[sourcecode lang=”js”]
declare var Array: {
new (arrayLength?: number): any[];

prototype: Array<any>;
}
[/sourcecode]のように宣言されている。こいつに from メソッドを追加しようとして自分のコードで[sourcecode lang=”js”]
declare var Array: {
from<T>(arrayLike: {[n: number]: T; length: number}): T[];
}
[/sourcecode]と書くとエラーが出る。既存のメソッドも書かないとダメかと思って[sourcecode lang=”js”]
declare var Array: {
new (arrayLength?: number): any[];

prototype: Array<any>;
from<T>(arrayLike: {[n: number]: T; length: number}): T[];
}
[/sourcecode]と書いてもエラーになる。モジュールならばどうかと思って[sourcecode lang=”js”]
module Array {
from<T>(arrayLike: {[n: number]: T; length: number}): T[];
}
[/sourcecode]と書いてもエラーになる。組み込みの Array オブジェクトの型は lib.d.ts で書かれたものから変更できないのだ。

だがちょっと待て。Math オブジェクトは自前で拡張できたではないか。これは何でだったかというと、Math オブジェクトは[sourcecode lang=”js”]
interface Math {

}
declare var Math: Math;
[/sourcecode]という風に定義されていて、Math インターフェースにメソッドを追加すれば Math オブジェクトを拡張できたのだ。Array も[sourcecode lang=”js”]
interface ArrayConstructor {
new (arrayLength?: number): any[];

prototype: Array<any>;
}
declare var Array: ArrayConstructor;
[/sourcecode]と定義されていれば良かったのに。Array だけではなくて、Object, Number, String 等の組み込みオブジェクトも同じ問題を抱えている。

ググってみたらすでに同じ問題で悩んでいる人がいた。

最初のStack Overflowの回答によると、「自前の lib.d.ts を用意しろ」らしい。つらい。

[2015年1月18日 追記] 以上の問題点はTypeScript 1.4のリリースにより解決しました。詳しくはこちら

滑らかな曲線をベジエ曲線で近似する

目次

ベジエ曲線とは

ベジエ曲線とはWikipedia(英語版)によると(本当はちゃんとした文献を当たるべきなのだろうが)、\(n+1\) 個の点 \(x_0, x_1, \dots, x_n\) が与えられた時に\[
B(t)=\sum_{i=0}^{n}\binom{n}{i}t^i(1-t)^{n-i}x_i, \quad 0\le t\le 1
\]で定まる曲線らしい。始点 (\(t=0\)) は \(x_0\)、終点(\(t=1\))は \(x_n\) である。\(n\) のことを次数という。

(1次の場合は単なる線分なので置いておいて)よく使われるのは2次の場合と3次の場合である。それぞれ\begin{align*}
\mathit{quadratic B\acute{e}zier}(t)&=(1-t)^2x_0+2t(1-t)x_1+t^2x_2, \\
\mathit{cubic B\acute{e}zier}(t)&=(1-t)^3x_0+3t(1-t)^2x_1+3t^2(1-t)x_2+t^3x_3
\end{align*}で与えられる。

2次の場合は始点 \(x_0\) と終点 \(x_2\) の他に1個の制御点 \(x_1\)、3次の場合は始点 \(x_0\)、終点 \(x_3\) の他に2個の制御点 \(x_1\), \(x_2\) によって決まる。制御点は始点及び終点における接線を与えるためにある。ここではベジエ曲線についてはこれ以上突っ込んだ事は取り扱わない。

こういう、「制御点を与えて曲線を描く」のは、ドロー系の描画ソフトウエアを使ったことのある方はおなじみだろう。SVGやPostScriptなどのベクター画像形式でも、ベジエ曲線は基本的な描画対象である。

プログラミングに関して言うと、大抵のグラフィックAPIには2次か3次のベジエ曲線を描画するAPIがある。例えば、HTML5 Canvasの場合

context.quadraticCurveTo(cpx, cpy, x, y) /* 2次 */
context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) /* 3次 */

というAPIがあり、Cocoaの場合は

NSBezierPath
-(void)curveToPoint:(NSPoint)aPoint controlPoint1:(NSPoint1) controlPoint2:(NSPoint)controlPoint2 /* 3次 */

というAPIがある。いずれも始点を指定する引数がないが、これらのAPIを使った時の始点は「現在の点の位置」になる。つまり、最後の描画操作における終点か、あるいはmoveToなどのAPIを使って指定した点である。

滑らかな曲線をベジエ曲線で近似する

さて、滑らかな曲線 \(f\colon[0,1]\to\mathbf{R}^2\) をベジエ曲線で近似するということを考えよう。動機としては例えば、プログラミングで曲線を描きたい時に、大抵のグラフィックスAPIには任意の曲線を描画するようなAPIはないので(2次か3次の)ベジエ曲線を使って近似する事になる。2次か3次と書いたが、以後3次のベジエ曲線で近似する事を考える。

続きを読む

ECMAScriptでのいくつかの数学関数の実装

C言語をはじめとする多くのプログラミング言語には、exp, log, sqrt などの指数、対数、平方根の関数や、 cos, sin, tan などの三角関数、cosh, sinh, tanh などの双曲線関数が備わっている。これらの数学関数はECMAScript (JavaScript)からは、Mathオブジェクト経由で Math.expMath.cos などのようにアクセスできる(が、現状ECMAScriptには双曲線関数はない)。

さて、C99でこれらに加えて、acosh, asinh, atanh などの逆双曲線関数や、expm1log1p などの「精度を意識した」関数などが追加された(expm1は\(\exp x-1\), log1p は \(\log(1+x)\) を計算する関数である)。これらの関数をECMAScriptで使うにはどうすればいいか?

ECMAScriptの次期標準であるECMAScript 6では、双曲線関数 Math.(cosh|sinh|tanh) や、C99で追加された関数に対応する Math.(acosh|asinh|atanh)Math.expm1, Math.log1p が入るらしい(Firefoxなどでは既に実装されている)。しかし、ECMAScript 6に非対応の環境では、これらの関数は自前で実装するしかない。

幸い、双曲線関数は指数関数で書ける。よって、例えば Math.sinh はECMAScriptに既に存在する Math.exp 関数を使って次のように書ける:

[sourcecode lang=”js”]
Math.sinh = Math.sinh || function(x) {
return (Math.exp(x)-Math.exp(-x))/2;
};
[/sourcecode]

Mozillaのサイトにも代替コード(Polyfill)として同じようなコードが載っている。めでたしめでたし。

といいたいところだが、精度を気にする場合はそうはいかない。例えば、\(x\) にとても小さい数、例えば \(x=10^{-20}\) を代入してみるとどうなるだろうか。\(\sinh\) のテーラー展開\[
\sinh x=x+\frac{x^3}{3!}+\cdots
\]を考えると(\(x^3\) は微小なので無視して)値はおおよそ \(x=10^{-20}\) と一致するはずである。この場合、ECMAScriptの「数」はIEEE754の64bit浮動小数点数で、精度は53bitしかないため、\(x\) が十分に小さければ \(x+\frac{x^3}{3!}+\dots\) は完全に \(x\) と一致する。

一方、指数関数のテーラー展開は\[
\exp x=1+x+\frac{x^2}{2!}+\cdots
\]で、今書いたのと同じ理由で Math.exp(x)Math.exp(-x) は完全に 1 と等しくなる。従って、さっき実装したオレオレ Math.sinh は0を返す。情報落ちだか桁落ちだか忘れたが、なんかそういう現象が起こっている。これは理想的とはいえない。ちなみに、C言語の sinh はこの場合「正しい」 \(10^{-20}\) を返す。

同様に、Math.expm1 を次のように愚直に実装しても、同じ問題が発生する。

[sourcecode lang=”js”]
Math.expm1 = Math.expm1 || function(x) {
return Math.exp(x)-1;
};
[/sourcecode]

では、どうすれば \(x\) が小さい時でも「正しい」答えを出す expm1 関数を作れるか?(双曲線関数よりも指数関数の方が単純なので、以後こちらを議論する)

まあ単純な答えとしては、 \(x\) が小さいときに関数のテイラー展開を使って多項式として計算してやれば良い。さっきのように \(x=10^{-20}\) とかだと1次近似で \(x\) そのものを返してやればよかったが、この近似は \(x\) がどのぐらい小さければ問題ないのか?あるいは、近似の次数をどのぐらい増やしてやれば誤差はどれぐらい小さくなるか?

仮に、\(\exp x\) を \(n\) 次の多項式で近似するとしよう。すると、真の値(無限級数で表される)との差は\[
\left\lvert(\text{差})\right\rvert=\left\lvert\exp x-1-\sum_{k=1}^{n}\frac{x^k}{k!}\right\rvert=\left\lvert\sum_{k=n+1}^{\infty}\frac{x^k}{k!}\right\rvert
\]となる。これを適当に評価してやれば良い。実際にやってみると、例えば次のようになる。\begin{align*}
\left\lvert(\text{差})\right\rvert=\left\lvert\sum_{k=n+1}^{\infty}\frac{x^k}{k!}\right\rvert
&\le\sum_{k=n+1}^{\infty}\frac{\lvert x\rvert^k}{k!} \\
&=\frac{\lvert x\rvert^{n+1}}{(n+1)!}\sum_{k=0}^{\infty}\frac{\lvert x\rvert^k}{(n+2)\cdot\dots\cdot(n+k+1)} \\
&\le\frac{\lvert x\rvert^{n+1}}{(n+1)!}\sum_{k=0}^{\infty}\frac{1}{(n+2)^k} \quad (\left\lvert x\right\rvert <1\text{を仮定}) \\
&\le\frac{\left\lvert x\right\rvert^{n+1}}{(n+1)!}\frac{1}{1-\frac{1}{n+2}} \\
&\le\frac{\left\lvert x\right\rvert^{n+1}}{(n+1)!}\frac{n+2}{n+1}=\frac{(n+2)\left\lvert x\right\rvert^{n+1}}{(n+1)(n+1)!}
\end{align*}
では、どういうときに \(n\) 次の多項式近似の値を使ってよいかというと、近似値 \(\sum_{k=1}^{n}\frac{x^k}{k!}\) の大きさに対してこの \(\left\lvert(\text{差})\right\rvert\) の大きさが(精度が53bitの浮動小数点数の場合は) \(2^{-53}\) よりも小さければ確実に問題ない(十分条件)。つまり\begin{align*}
\left\lvert\frac{(\text{差})}{\sum_{k=1}^{n}\frac{x^k}{k!}}\right\rvert<2^{-53}
\end{align*}だ。この左辺をもうちょっと分かりやすい形で評価すると
\begin{align*}
\left\lvert\frac{(\text{差})}{\sum_{k=1}^{n}\frac{x^k}{k!}}\right\rvert&\le\left\lvert\frac{(\text{差})}{x}\right\rvert \\
&=\left\lvert\frac{1}{x}\frac{(n+2)\left\lvert x\right\rvert^{n+1}}{(n+1)(n+1)!}\right\rvert \\
&=\frac{(n+2)\left\lvert x\right\rvert^n}{(n+1)(n+1)!}
\end{align*}
となる(書いた後に気付いたが、最初の行の評価が \(x<0\) のときにマズい気がする)。つまり、\(n\) 次近似を使っても良い十分条件として\[
\frac{(n+2)\left\lvert x\right\rvert^n}{(n+1)(n+1)!}<2^{-53},
\]すなわち\[
\left\lvert x\right\rvert<\sqrt[n]{\frac{(n+1)(n+1)!}{n+2}2^{-53}}
\]が得られる。

例えば、\(n=3\) として右辺を計算すると \(1.286976\ldots\times 10^{-5}\) が得られるので、さっきのオレオレ Math.expm1 の実装は

[sourcecode lang=”js”]
Math.expm1 = Math.expm1 || function(x) {
if (Math.abs(x) <= 1.28e-5) {
return x*(1+x/2*(1+x/3));
} else {
return Math.exp(x)-1;
}
};
[/sourcecode]

のように改良できる。

まあ、改良したと言っても所詮お遊びで、実際のC言語のライブラリの実装はこんな適当なコードよりもちゃんとやっているので、真面目にやりたい人はC言語のライブラリの実装を移植しよう。

Math.expm1 の他に、Math.log1p, Math.sinh, Math.tanh, Math.asinh, Math.atanh あたりの関数を実装する時にも同じような(小手先の)テクニックが使える。

ところで、MDNに載っている Math.tanh のPolyfillに問題があるのを見つけた。MDNのPolyfillでは xInfinity かどうかを関数の最初でチェックしているが、xInfinity でなくても Math.exp(x)Infinity になる場合を考慮していない。その結果、Math.tanh(x) に 1 を返してほしいのに実際には NaN が返ってくる場合がある。

最後に全く関係ない話だが、プログラミング言語Luaは標準ライブラリに math.sinh などの双曲線関数を計算する関数(中ではC言語のライブラリ関数を呼び出す)を持っている。しかし、どうやら将来的にはこれらの双曲線関数を標準ライブラリから外すようだ(次期バージョン5.3でdeprecated扱い)。理由として作者は「あまり使われていない」「必要なら mathx などの外部ライブラリを使えば良い」としている(この記事に書いた理由により、「math.exp で実装できる」は理由にならない)。ECMAScript と Lua はよく似ていると言われる事が多いが、一方がWebの世界のアセンブラとなるべく数学関数・浮動小数点数関係の関数を追加しようとしているのに対し、もう一方はそれを削減しようとしているのは対照的だと思った。(まあ筆者自身はLuaを活発に触っていたのは5.1時代の事で、5.2が出て以降はあまり触っていないので、ぶっちゃけどうでもいいのだが)

八元数について

最近、八元数を勉強しなければならないという電波を受信したので、とりあえず八元数のさわりだけ勉強する事にした。

複素数をさらに拡張したような数の体系として、八元数の前に四元数がある。四元数は3次元や4次元の回転(特殊直交行列; \(SO(3)\),\(SO(4)\))と深い関わりがあるので、以前から(高校生の頃に)勉強して知っていた。四元数の積はノルムを保つため、単位四元数の積によって3次元球面 \(S^3\) にリー群の構造が入る。

八元数も、積がノルムを保つように定義されている。しかし積が結合的ではないため、群にはならない。いまいち勉強するモチベーションが起こらなかったのもその辺に理由がある気がする。

今参照している本はJohn H. ConwayとDerek A. SmithのOn Quaternions and Octonions: Their Geometry, Arithmetic, and Symmetryという本である。

複素数は1個(\(i\))、四元数は3個(\(i,j,k\))の直交する虚数単位があったが、八元数にはそれが7個ある。それを \(i_0,\dots,i_6\) で表す事にしよう。これに、「実数方向」の基底 \(1\) を加えると、八元数の \(\mathbf{R}\) 上線形空間としての基底 \(1,i_0,\dots,i_6\) ができる。

この基底を使うと、任意の八元数 \(x\) は次のように書ける:\[x=x_\infty+x_0i_0+x_1i_1+x_2i_2+x_3i_3+x_4i_4+x_5i_5+x_6i_6\]ただし、\(x_*\) は実数である。ノルムは普通の実数のノルム\[\left\lVert x\right\rVert^2=x_\infty^2+\sum_{n=0}^6 x_n^2\]とする。八元数の乗法をうまいこと定めてやると、2つの八元数 \(x\),\(y\) の積がノルムを保つ\[\left\lVert x\cdot y\right\rVert=\left\lVert x\right\rVert\cdot\left\lVert y\right\rVert\]ようにできる。どう定めるかというと、1以外の基底 \(i_n\) について\begin{align*}
i_n^2&=-1, \\
i_{n+1}i_{n+2}&=i_{n+4}=-i_{n+2}i_{n+1}, \\
i_{n+2}i_{n+4}&=i_{n+1}=-i_{n+4}i_{n+2}, \\
i_{n+4}i_{n+1}&=i_{n+2}=-i_{n+1}i_{n+4}
\end{align*}となるようにするらしい。ここで、\(n\) は整数を動き、添字は0から6に収まるように適宜 mod 7 で考える。

ということなのだが、この積の定義で本当にいいのか?本の記述を読み違えたとかいう可能性はないか?不安なので、積がノルムを保存することを確かめることにしよう。

続きを読む

ベクトル束のテンソル積

ベクトル束のテンソル積には2種類ある。

一つ目。\(E\to M\), \(F\to N\) をそれぞれ \(K\)-ベクトル束とする。このとき、\[\bigsqcup_{(x,y)\in{M\times N}} {E_x\otimes F_y}\]は \(M\times N\) 上のベクトル束となる。底空間は直積 \(M\times N\) となる。

二つ目。\(E\to M\), \(F\to M\) をそれぞれ \(K\)-ベクトル束とする。一つ目の場合は底空間が異なる場合も許したが、ここでは底空間は同じものを考えることに注意しよう。このとき、\[\bigsqcup_{x\in M} {E_x\otimes F_x}\]は \(M\) 上のベクトル束となる。底空間は \(M\) のままである。

最初に読んだ本では二つ目のやつを \(E\otimes F\) で表していたが、今読んでいる本は一つ目のやつを \(E\otimes F\) と書いて、二つ目のやつは \(E\mathop{\hat{\otimes}}F\) で書いていた。これに気づかないで読み進めたためにひどい目にあった(ちゃんと注意して読めという話だが)。

個人的には、二つ目の方を \(\otimes\) で書くのがいいかなあと思うが(この分野の最近の傾向を知らないのでアレだが)、じゃあ一つ目のテンソル積を書く必要があるときはどうするの、という話だ。どうしよう。