わらばんし仄聞記

南の国で引きこもってるWeb屋さん

raspberry pi3への64bit版opensuseインストールとwifiの有効化

この記事は以前に書いた
Installing 64bit openSUSE to raspberry pi 3 - わらばんし仄聞記
After installing 64bit openSUSE to raspberry pi 3 - わらばんし仄聞記
を日本語化して整理したものです。

追記 2017/02/03
元々英語版の記事を書いていた時に使用したOSイメージである openSUSE-Tumbleweed-ARM-JeOS-raspberrypi3_aarch64.aarch64-2016.10.15-Build6.1.raw.xz での手順となります。その後この日本語版を書く際に使用してみたこの時点の最新版である openSUSE-Tumbleweed-ARM-JeOS-raspberrypi3.aarch64-2017.01.31-Build1.1.raw.xz でも試してみた所、この手順ではwlan0を認識してくれませでした。原因は調査中です。

追記 2017/02/05
Tumbleweed版ではなくLeap42.2版のJeOSイメージでは現時点での最新版で動作したとのことです。b4_dooms_dayさん、検証ありがとうございました!
その情報を元に自分の手元でも試してみた所、本記事本文のやり方でSDカードへの書き込み後、raspi3へセットしての最初の起動時はwlan0を何も設定していなくても認識していました。ですが、再起動したらwlan0が現れなくなり、その後は下で記した手順を経たら認識するようになりました。

はじめに

raspberry pi3を買ってみたものの、下調べが足りずにその時点でraspbianが64bit対応してなかったことを把握してませんでした。とはいえ、折角の64bitマシンなのにOSは32bitで動かすなんて悔しすぎる!
というわけで、raspberry pi3向けのイメージを探し求めた結果、openSUSEならば対応しているのを見つけ、これで動かしてやろうと思った次第です。
但し、自分が試した段階ではopenSUSEがinstallした直後の状態ではwifi用のNICを認識してくれず、これまたraspberry pi3なのにwifi使えないなんて悔しすぎる!ということでそこら辺まで動作するようにした際のメモです。
尚、SDカードに書き込みをしたりするのに使ってるのもopenSUSEを入れてあるラップトップだったりします。

続きを読む

After installing 64bit openSUSE to raspberry pi 3

Below are what I did after installing this OS to raspberry pi 3.

update 2017/02/03
About wifi device recognition, this was work on old OS image (openSUSE-Tumbleweed-ARM-JeOS-raspberrypi3_aarch64.aarch64-2016.10.15-Build6.1.raw.xz) as of wrote this article.
Latest OS image doesn’t recognize it by following steps.

update 2017/02/05
Latest version of Leap42.2 (as of 2017/02/05. file name is openSUSE-Leap42.2-ARM-JeOS-raspberrypi3.aarch64-2017.02.02-Build1.2.raw.xz) recognize wlan0 device by following article. Thank you b4_dooms_day san for validation!

extend main system partition

main partition of /dev/mmcblk0p2 had only 1.3GB of partition

nohostname:~ # df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
devtmpfs       devtmpfs  389M     0  389M   0% /dev
tmpfs          tmpfs     393M     0  393M   0% /dev/shm
tmpfs          tmpfs     393M  352K  393M   1% /run
tmpfs          tmpfs     393M     0  393M   0% /sys/fs/cgroup
/dev/mmcblk0p2 ext4      1.3G  1.2G     0 100% /
/dev/mmcblk0p1 vfat      200M  3.2M  197M   2% /boot/efi
tmpfs          tmpfs      79M     0   79M   0% /run/user/0

This isn’t enough so extend this partition.

nohostname:~ # fdisk /dev/mmcblk0                                                                                                   

Welcome to fdisk (util-linux 2.28.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/mmcblk0: 29.7 GiB, 31914983424 bytes, 62333952 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x495bfe7e

Device         Boot    Start      End  Sectors   Size Id Type
/dev/mmcblk0p1          2048   411651   409604   200M  c W95 FAT32 (LBA)
/dev/mmcblk0p2        413696 61304040 60890345    29G 83 Linux
/dev/mmcblk0p3      61304832 62332199  1027368 501.7M 82 Linux swap / Solari

This partition seems have 29GB of space. so I executed command such as below

# resize2fs /dev/mmcblk0p2 29G

wifi device recognition

I referred to following links

TUMBLEWEED Wifi (Broadcom 43438) not work on Raspberry Pi 3
Re: [opensuse-arm] Still no wlan on Raspberry 3

As of now, the distributed opensuse image for raspberry pi3 doesn’t recognize wifi device by default. I could recognize wifi device by process that was written in the bottom one of above two links. And I setup wlan0 by yast after that.

Running /usr/sbin/install-brcmfmac command may output warning message /usr/sbin/install-brcmfmac: line 4: warning: command substitution: ignored null byte in input because /sys/firmware/devicetree/base/compatible include null byte character.

Installing 64bit openSUSE to raspberry pi 3

I got raspberry pi 3 but I found out after got it that raspbian doesn't support aarch64. So I looked for distribution that works with 64bit linux on this raspberry pi and I have found opensuse fullfill my demand for now.

Official page is HCL:Raspberry Pi3 - openSUSE.

This official page uses serial port and HDMI display in step 3 and 4 but I didn't use these.

First, copy the OS image to SD card according to step 1 and 2. After this, you get a SD card that opensuse was installed. Next, mount this SD card on another linux OS. In this case, I used my laptop which was installed opensuse. In my case, SD card is mounted to /dev/mmcblk0 so I executed command like below.

# mount /dev/mmcblk0p2 /mnt

second partition was system partition so I mount it to /mnt and edit some files. As of now, I aim to just work and accessible through ssh so I don't care security. Because of this, I stopped firewall and start sshd like below.

# echo "service sshd start" >> /mnt/etc/init.d/after.local
# echo "systemctl stop SuSEfirewall2" >> /mnt/etc/init.d/after.local

And edit /etc/ssh/sshd_config for permit login with root user and password authentication.

--- /etc/ssh/sshd_config.org    2016-10-17 18:47:58.143853990 +0200
+++ /etc/ssh/sshd_config        2016-10-16 11:25:12.849113103 +0200
@@ -46,7 +46,7 @@
 # Authentication:
 
 #LoginGraceTime 2m
-#PermitRootLogin yes
+PermitRootLogin yes
 #StrictModes yes
 #MaxAuthTries 6
 #MaxSessions 10
@@ -74,7 +74,7 @@
 #IgnoreRhosts yes
 
 # To disable tunneled clear text passwords, change to no here!
-#PasswordAuthentication no
+PasswordAuthentication yes
 #PermitEmptyPasswords no
 
 # Change to no to disable s/key passwords

After boot raspberry pi 3, it will make connection with DHCP so we need to find IP of the raspberry pi. You can find it by nmap command such as

$ nmap -p22 192.168.1.0/24

Please replace local IP for your environment. After this, you need to boot raspberry pi 3 and access with root user. If you can find the row that is "22/tcp open ssh" you can access by ssh to it raspberry pi.

Nmap scan report for 192.168.1.6
Host is up (0.0068s latency).
PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: B8:27:EB:FD:AB:BE (Raspberry Pi Foundation)

KDEにて指定したvirtual desktop上にアプリケーションを自動起動

最近、メインマシンのOSをopenSUSEのTumbleweedに変えました。 デスクトップ環境はKDEを使用してます。バージョンは以下の通り。

$ kstart --version
Qt: 4.8.7
KDE Development Platform: 4.14.14
KStart: 4.14.12

以前使ってたバージョンでは、前回終了時に起動してたアプリケーションが次回起動時に元のvirtual desktop上で起動されてるのが良かったんだけど、なんか今回からそうなってはくれない。コンソールなんかはどうせ毎回確実に開くのに、逐一起動するのはめんどい。あとフルスクリーンにしたりと。

単純に、起動時に自動的にコマンドを実行させるには [System Settings] -> [Workspace] -> [Startup and Shutdown] から、Autostartに設定を追加してやればいい。

f:id:warabanshi21:20151227151540p:plain

ただし、これだと何処のvirtual desktopに起動するかは指定出来てないので、適当な所に起動されてしまう。それに、通常のwindowで開かれるので、full screenにしたりするのまだめんどい。

この問題を解消するにはコマンド部分を修正して、kstart経由で実行すると良い。

$ kstart --desktop 1 --fullscreen konsole

こんな感じ。これを先ほどのcommandフォームに指定してやればOK。

参考:autostart application on specific virtual desktop • KDE Community Forums

初心者の為の線形代数勉強会(2)

前回に続き、初心者のための線形代数勉強会(3) - connpass に参加してきました。今回は第二章の以下の節をやりました

  • 2.4 行列の転置と共役
  • 2.5 行列の分割
  • 2.6 行列の線形写像

前回のエントリは下記の通りです

初心者の為の線形代数勉強会(1) - わらばんし仄聞記

2.4 行列の転置と共役

基本的には教科書参照。転置行列、対称行列、交代行列についての説明。任意の正方行列が1つの対称行列と1つ交代行列の和として一意に表せることなどについてが前半部分。この辺りは教科書に書かれてる事そのままなので、特筆する事は無し。
命題2.4.2で転置についての性質が示されていたが、\({}^t\!(AB)={}^t\!B{}^t\!A\)ってなんか群の性質でも見かけたような気が・・・。

後半は実行列、複素行列、実ベクトル、複素ベクトル、複素共役行列、随伴行列(エルミート共役)などについて。
この節については教科書を参照すれば事足りるかと思うので、ここでは触れません。

2.5 行列の分割

例 2.5.1

\((m,n)\)型行列\(A\)の各列および各行をベクトルと見なして

\begin{align*} A = \begin{pmatrix}\boldsymbol{a_1} & \boldsymbol{a_2} & \cdots & \boldsymbol{a_n} \end{pmatrix} = \begin{pmatrix}\boldsymbol{a'_1} \\ \boldsymbol{a'_2} \\ \vdots \\ \boldsymbol{a'_n} \end{pmatrix} \end{align*}

で表す事がある。とのことですが、確かに文と式をちゃんと読めばその通りなんですが、初見殺しな感じがしますね。要は

f:id:warabanshi21:20150320012752j:plain

こんな\((m,n)\)型行列について、例えば各列、つまり青枠の要素をそれぞれ1つの列ベクトルとして考えると、列ベクトルが並んだ\((1, n)\)型の行列と見なせますね。それが

\begin{align*} A = \begin{pmatrix}\boldsymbol{a_1} & \boldsymbol{a_2} & \cdots & \boldsymbol{a_n} \end{pmatrix} \end{align*}

なわけですね。同様のことが行についても考えられます。

例 2.5.2

\((m,n)\)型行列\(A\)、\((n,r)\)型行列\(B\)をそれぞれ行ベクトルと列ベクトルで表して

\begin{align*} A = \begin{pmatrix}\boldsymbol{a'_1} \\ \boldsymbol{a'_2} \\ \vdots \\ \boldsymbol{a'_n} \end{pmatrix},~~ B = \begin{pmatrix}\boldsymbol{b_1} & \boldsymbol{b_2} & \cdots & \boldsymbol{b_r} \end{pmatrix} \end{align*}

とすれば

\begin{align*} AB = \begin{pmatrix} \boldsymbol{a'_1}\boldsymbol{b_1} & \boldsymbol{a'_1}\boldsymbol{b_2} & \dots & \boldsymbol{a'_1}\boldsymbol{b_r} \\ \boldsymbol{a'_2}\boldsymbol{b_1} & \boldsymbol{a'_2}\boldsymbol{b_2} & \dots & \boldsymbol{a'_2}\boldsymbol{b_r} \\ \vdots & \vdots & \ddots& \vdots \\ \boldsymbol{a'_m}\boldsymbol{b_1} & \boldsymbol{a'_m}\boldsymbol{b_2} & \dots & \boldsymbol{a'_m}\boldsymbol{b_r} \end{pmatrix} \end{align*}

で表せ、これは各項がスカラーとなっている。
一方、\(A, B\)をそれぞれ列ベクトルと行ベクトルで

\begin{align*} A = \begin{pmatrix}\boldsymbol{a_1} & \boldsymbol{a_2} & \cdots & \boldsymbol{a_n} \end{pmatrix},~~ B = \begin{pmatrix}\boldsymbol{b'_1} \\ \boldsymbol{b'_2} \\ \vdots \\ \boldsymbol{b'_n} \end{pmatrix} \end{align*}

とすれば

\begin{align*} AB = \boldsymbol{a}_1\boldsymbol{b}'_1 + \boldsymbol{a}_2 \boldsymbol{b}'_2 + \dots + \boldsymbol{a}_n\boldsymbol{b}'_n \end{align*}

で表せ、この場合は各項が\((m, r)\)型の行列となっている。つまり、結果の見た目がかなり異なっているが、実質はどちらも\((m, r)\)型の行列となっている。

例 2.5.8

\(A\)、\(B\)ともに、行、列どちらも\(r\)個と\(s\)個の要素に分割されたブロックを持つ(\(A_{11}\)が\((r, r)\)型、\(A_{12}\)が\((r, s)\)型という具合。\(B\)についても同様)、例2.5.7の行列

\begin{align*} A = \begin{pmatrix} A_{11} & A_{12} \\ O & A_{22}\end{pmatrix}, ~~~ B = \begin{pmatrix} B_{11} & B_{12} \\ B_{21} & B_{22}\end{pmatrix} \end{align*}

について、\(A_{12} = O\)とするということで、つまり\(A\)の非対角成分は0となる。このとき、\(AB = BA = I_n~~~(n=r+s)\)であるとするということなので、要は\(B = A^{-1}\)である場合の\(B\)の各成分がどうなるかということですね。ということで、\(B\)の各成分を求めていきます。
\(I_n\)はまた、\(A, B\)を分割したように\(r\)個の要素と\(s\)個の要素でブロックに分けると

\begin{align*} I_n = \begin{pmatrix} I_r & O \\ O & I_s\end{pmatrix} \end{align*}

と表せる。一方、\(A\)と\(B\)について、この積\(AB\)は

\begin{align*} AB = \begin{pmatrix} A_{11}B_{11}+A_{12}B_{21} & A_{11}B_{12}+A_{12}B_{22} \\ A_{22}B_{21} & A_{22}B_{22}\end{pmatrix} \end{align*}

となり、題意の条件\(A_{12}=O\)より

\begin{align*} AB = \begin{pmatrix} A_{11}B_{11} & A_{11}B_{12} \\ A_{22}B_{21} & A_{22}B_{22}\end{pmatrix} \end{align*}

である。\(AB\)と\(I_n\)をそれぞれ比較して

\begin{align*} A_{11}B_{11} = B_{11}A_{11} = I_r,\,\,A_{22}B_{22} = B_{22}A_{22} = I_s,\,\,A_{11}B_{12} = O,\,\,A_{22}B_{21} = O \end{align*}

なので、\(B_{11} = A_{11}^{-1}\)、\(B_{22} = A_{22}^{-1}\)である。
また、\(A\)が正則行列の場合、\(AX = O\)ならば\(X = O\)であるから、\(A_{11}B_{12} = O\)より\(B_{12} = O\)。同様にして、\(B_{21} = O\)である。よって

\begin{align*} B = \begin{pmatrix} B_{11} & B_{12} \\ B_{21} & B_{22}\end{pmatrix} = \begin{pmatrix} A_{11}^{-1} & O \\ O & A_{22}^{-1}\end{pmatrix} = A^{-1} \end{align*}

以降、一般の場合についての記述は教科書参照。

2.6 行列と線形写像

例 2.6.1

\(\boldsymbol{x} \in \boldsymbol{R}^n\)の\(\boldsymbol{x}\)とは、要は\((n, 1)\)型の列ベクトルであるから、\(A\)が\((m, n)\)型行列の場合

\begin{align*} f(\boldsymbol{x}) = A\boldsymbol{x} \end{align*}

とすると、\(A\boldsymbol{x}\)は\((m, 1)\)型の列ベクトルとなり、\(\boldsymbol{R}^n\)から\(\boldsymbol{R}^m\)への線形写像となっている。この\((m, 1)\)型の列ベクトルを記述してみると

\begin{align*} A\boldsymbol{x} = \begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{1n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix} = \begin{pmatrix} \sum_{k=1}^{n} a_{1k}x_k \\ \sum_{k=1}^{n} a_{2k}x_k \\ \vdots \\ \sum_{k=1}^{n} a_{mk}x_k \end{pmatrix} \end{align*}

となる。

基本ベクトル

\(\boldsymbol{R}^{n}\)のベクトル(=\((n, 1)\)型の列ベクトル)について、各\(j\,(1 \leq j \leq n)\)に対して\(j\)番目の成分が1で、他の成分が全て0であるベクトルを\(\boldsymbol{e}_j\)とする。

\begin{align*} \boldsymbol{e_1} = \begin{pmatrix} 1 \\ 0 \\ \vdots \\ 0 \end{pmatrix},~~~ \boldsymbol{e_2} = \begin{pmatrix} 0 \\ 1 \\ \vdots \\ 0 \end{pmatrix},~~~ \ldots \boldsymbol{e_n} = \begin{pmatrix} 0 \\ 0 \\ \vdots \\ 1 \end{pmatrix} \end{align*}

各成分の単位元みたいな感じですね。

写像\(f: \boldsymbol{R}^n \to \boldsymbol{R}^m\)が線形写像である場合

\begin{eqnarray} f(x) & = & f(x_1\boldsymbol{e}_1 + x_2\boldsymbol{e}_2 + \ldots + x_n\boldsymbol{e}_n) \\ & = & f(x_1\boldsymbol{e}_1) + f(x_2\boldsymbol{e}_2) + \ldots + f(x_n\boldsymbol{e}_n) \\ & = & x_1f(\boldsymbol{e}_1) + x_2f(\boldsymbol{e}_2) + \ldots + x_nf(\boldsymbol{e}_n) \\ & = & \begin{pmatrix} f(\boldsymbol{e}_1) & f(\boldsymbol{e}_2) & \ldots & f(\boldsymbol{e}_n) \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix} \end{eqnarray}

なので、\(f(\boldsymbol{x}) = A\boldsymbol{x}\)であるとき、\(A = \begin{pmatrix} f(\boldsymbol{e}_1) & f(\boldsymbol{e}_2) & \ldots & f(\boldsymbol{e}_n) \end{pmatrix}\)と表せる。

これをふまえて問 2.11の(1)を考えると

\begin{align*} f\begin{pmatrix} x_1 \\ x_2 \end{pmatrix} = \begin{pmatrix} 2x_1+3x_2 \\ x_1-5x_2 \\ 7x_1+6x_2 \end{pmatrix} \ldots (*) \end{align*}

について、線形写像\(f\)に対応する行列を求めろということなので、求める行列を\(A\)とすると、左辺は

\begin{eqnarray} f\begin{pmatrix} x_1 \\ x_2 \end{pmatrix} & = & \begin{pmatrix} f(\boldsymbol{e}_1) & f(\boldsymbol{e}_2) \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \end{pmatrix} \\ & = & \begin{pmatrix} f\begin{pmatrix} 1 \\ 0 \end{pmatrix} & f\begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \end{pmatrix} \end{eqnarray}

となるので

\begin{align*} A = \begin{pmatrix} f\begin{pmatrix} 1 \\ 0 \end{pmatrix} & f\begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} \end{align*}

である。なので、\(A\)の各成分を式\((*)\)に当てはめてみると

\begin{align*} f\begin{pmatrix} 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 2 \\ 1 \\ 7 \end{pmatrix}, \,\,\, f\begin{pmatrix} 0 \\ 1 \end{pmatrix} = \begin{pmatrix} 3 \\ -5 \\ 6 \end{pmatrix} \end{align*}

よって

\begin{align*} A = \begin{pmatrix} 2 & 3 \\ 1 & -5 \\ 7 & 6 \end{pmatrix} \end{align*}

初心者の為の線形代数勉強会(1)

はじめに

初心者のための線形代数勉強会(2) - connpassに参加してきました。
次の本を教科書として、線形代数を学んでいこうという内容の会になります。

Amazon.co.jp: 線形代数学講義 改訂版: 対馬 龍司: 本

機械学習や画像処理、量子力学をはじめとして、なんらかを真面目に取り組もうとすると当然のように出てくるのがこの線形代数ですね。自分は不真面目な学生だったので、当時にちゃんと修めてなかった為、今になってよく見かけるそれらを理解する事が出来ませんでした。
正直なところ、今更やるのは遅きに失しているんじゃないかとも思いますが、きっと今やらなかったら5年、10年後にまた、あのときやっておけば・・・と思うのが必至なので今からでもやります。

「やればわかる。やらなければ一生わからん!」ってやつですね。

さて、今回既に第2回ですが、第1回は線形代数本編というよりもその準備として、集合、複素数、ベクトルといった辺りをおさらいした回でした。今回は第2章の行列からスタートしました。

基本的には教科書通りに進んでいるので、そちらを読めば問題ないです。ですが、会の中で気になった点や教科書に書いてなかったけど述べられた点などについてちょっとメモっておこうかと。
なお、本記事の内容は「自分が現時点でこの様に理解した」という事なので、厳密に正しいかといったことの保証は一切ありません。

第2章 行列

まずは行列の話です。数学Cをちゃんと高校でやった人は復習になると思います。自分は高校では教わりませんでしたが・・・。

2.1 2次のベクトルと行列

序盤は定義について述べてるだけなので、特に問題なし。2.1.2節、線形変換から見てみます。まずは初っぱなにこんな風な説明があります。

直行座標の与えられた平面を\( {R}^2 \)で表す。\( {R}^2 \)から \( {R}^2 \)自身への写像\(f:{R}^2 \to {R}^2 \)で定数項を含まない1次式で表されるものを\({R}^2\)の線形変換という。

いきなりこれだけ言われたら、何なの?って感じになりますね。その気持ちを堪えて続きを見てみると

\begin{align*} f \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} ax + by \\ cx + dy \end{pmatrix} \end{align*}

「線形変換」とは、この様に表される写像のことである。と述べられています。つまり、左辺の\(x\)と\(y\)の列ベクトルを線形変換した結果が右辺の\(ax+by\)と\(cx+dy\)の列ベクトルということですね。積の定義より、計算結果が右辺となるような行列の積は

\begin{align*} \begin{pmatrix} a & b \\ c & d \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} \end{align*}

ですね。並べて書くと

\begin{align*} f \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} ax + by \\ cx + dy \end{pmatrix} = \begin{pmatrix} a & b \\ c & d \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} \end{align*}

つまり

\begin{align*} f = \begin{pmatrix} a & b \\ c & d \end{pmatrix} \end{align*}

です。さて、第1章で集合論をやったときにこんな図が出てきたと思います。

f:id:warabanshi21:20150223000920j:plain

集合\(X\)から写像\(f\)を通じて、集合\(Y\)上へ像を作ってる図ですね。今回、線形変換を施すにあたって使用している、\(f\)と等しい\(a,b,c,d\)による行列は、この様に集合で論じられていた写像の具体的な実装の一例となっています。
他の分野をかじっているとき、「この行列を演算子として見る」といった様な事をしばしば聞いていたんですが、今まで全くイメージが湧いていませんでした。この線形変換のように、行列を写像として扱うこともできるからそのような表現をされていたという事で、やっと腑に落ちました。

例 2.1.6

例として、次のような図が提示されています

f:id:warabanshi21:20150223003154j:plain

先のことを踏まえてこの例を見てみると、行列の積によって関数合成が成り立っている事を確かめられます。\(h(\mathbf x)\)は

\begin{eqnarray} h(\mathbf x) = f(g(\mathbf x))\\ = f \circ g(\mathbf x) \end{eqnarray}

と表せ、各写像に対応する行列を\(F, G, H\)とすると

\begin{align*} F = \begin{pmatrix} 1 & 0 \\ 0 & -1\end{pmatrix}, G = \begin{pmatrix} -1 & 0 \\ 0 & 1\end{pmatrix}, H = \begin{pmatrix} -1 & 0 \\ 0 & -1\end{pmatrix} \end{align*}

としてこの例では指定されているので、先の関数合成より

\begin{align*} f \circ g(\mathbf x) = FG\mathbf x = \begin{pmatrix} 1 & 0 \\ 0 & -1\end{pmatrix}\begin{pmatrix} -1 & 0 \\ 0 & 1\end{pmatrix}\mathbf x = \begin{pmatrix}-1 & 0 \\ 0 & -1\end{pmatrix} = H\mathbf x = h(\mathbf x) \end{align*}

これより、行列が関数として扱えている事がわかる。

命題 2.1.9

2つの列ベクトル\(\mathbf a = \begin{pmatrix} a \\ c\end{pmatrix}, b = \begin{pmatrix} b \\ d\end{pmatrix}\)によって作られる平行四辺形の面積\(S\)は行列\(A = (\mathbf a \mathbf b) = \begin{pmatrix} a & b \\ c & d\end{pmatrix}\)の行列式\(\Delta\)の絶対値\(\mid\Delta\mid = \mid ad - bc \mid\)に等しい

教科書の方では内積つかったり云々してますが、この時点では教科書中には内積やらは出てきてないのでそれらを使わない方向で説明。次のような図で考える。

f:id:warabanshi21:20150223014412j:plain

空白部分が求めたい面積S。こんな感じで座標に当てはめてみると、平行四辺形を含む大枠の矩形の面積は\( (a+b)(c+d) \)で表される。あとは縦線、横線が引かれている領域をそこから引けばいい。
引く対象の面積はいずれも三角形と台形の面積なので、容易に求められる。これらの和は

\begin{align*} \frac{ac}{2} + \frac{bd}{2} + \frac{(c+(c+d))b}{2} + \frac{(b+(a+b))c}{2}\\ = \frac{1}{2}(ac + bd + 2bc + bd + ac + 2bc)\\ = ac + bd + 2bc \end{align*}

これを先程の矩形の面積から引けばよいので

\begin{align*} (a+b)(c+d) - (ac + bd + 2bc) = ad - bc \end{align*}

以上より、命題が示された。

例 2.1.11

平面上の点\((x,y)\)を原点を中心に反時計回りに\(\alpha\)回転した点\((x', y')\)に移す写像を\(f:{R}^2 \to {R}^2 \)とする。\((x,y)\)と\((x',y')\)を複素平面上で考えると、それらの間の関係は定理1.2.6により

\begin{eqnarray} x' + iy' & = & (\cos \alpha + i\sin \alpha)(x+iy)\\ & = & (x\cos \alpha - y\sin \alpha) + i(x\sin \alpha + y\cos \alpha) \end{eqnarray}

と、教科書の方には定理1.2.6よりこの様な式を導出してますが、複素平面極座標を用いてお手軽に導出してみます。

図にして表すと次のような図で、左が\(x,y\)平面上で\(\alpha\)回転させたイメージ。それを複素平面上の極座標で考えたイメージが右の図で、座標\((x,y)\)を\(z_1 = re^{i\theta_1}\)。また、\(\alpha\)と同じ角度の\(\theta_2\)を考え、\(z_2 = re^{i\theta_2}\)とすると、これらの積\(z_1 z_2\)が\(z'\)となる。

f:id:warabanshi21:20150224014940j:plain

式にしてみると

\begin{align*} z_1 \cdot z_2 = r_1 r_2 e^{i(\theta_1 + \theta_2)} \end{align*}

となり、長さ\(r\)は\(z_1\)のものだけあればよく、\(z_2\)については回転分だけが欲しいので\(\mid z_2 \mid = 1\)つまり\(r_2 = 1\)とすると

\begin{align*} z_1 \cdot z_2 = r_1 e^{i(\theta_1 + \theta_2)} \end{align*}

となるので、回転を単なるかけ算として扱うことが出来る。
さて、\(z = x + iy\)だったので、複素平面上でここから\(\alpha\)回転させるには\(e^{i\alpha}\)を掛ければよいので

\begin{eqnarray} z' & = & e^{i\alpha}(x + iy)\\ & = & (\cos \alpha + i\sin \alpha)(x + iy)\\ & = & \cos \alpha \cdot x - \sin \alpha \cdot y + i(\sin \alpha \cdot x + \cos \alpha \cdot y)\\ \end{eqnarray}

であるから、つまり

\begin{align*} \begin{pmatrix} x' \\ y'\end{pmatrix} = \begin{pmatrix}\cos \alpha \cdot x - \sin \alpha \cdot y \\ \sin \alpha \cdot x + \cos \alpha \cdot y\end{pmatrix} = \begin{pmatrix}\cos \alpha & -\sin \alpha \\ \sin \alpha & \cos \alpha\end{pmatrix}\begin{pmatrix}x \\ y\end{pmatrix} \end{align*}

として回転を表せる

2.2 行列の定義と演算

いままで見てたのは2行2列程度の行列でしたが、ここでもっと一般的な行列について見ることとなります。\(m\)行\(n\)列の行つのことを\((m, n)\)型行列とも言うそうです。
\((m, n)\)型の行列\(A\)と\((n, r)\)型の行列\(B\)についてはそれらの行列で積を求め、\((m, r)\)型の行列を導く事が出来ます。これはつまり\((m, n)\)型の行列(\(m \neq n\))は\(n\)次元から\(m\)次元への写像を表していると考えることが出来るためで、これはそもそも異なる次元が結果として出てくるので単位行列(恒等写像\(I\))は存在しない。

p.42 2.3.5

行列のトレースについて

\begin{align*} tr AB = tr BA \end{align*}

とある。これはあくまで行列の積を二分割して入れ替える事が出来るというだけであり、自由に入れ替えていいというわけではない事に注意。

\begin{align*} ○tr ABCD = tr BCDA \\ ×tr ABCD = tr ADCB \end{align*}

p.42 2.3.2 掃き出し法による逆行列の計算

やり方については教科書を参照。

ここで行われている行基本変形の操作は、その操作に対応する正則行列を左から掛ける事に対応している。つまり、行列\(A\)に対して行基本変形逆行列を求めるという操作は、\(m\)回目に行基本変形で掛ける正則行列を\(R_m\)として、n回の操作で終わる場合

\begin{align*} R_n \cdots R_1A = I \end{align*}

と表せる。さて行列\(A\)について、式(2.32)、(2.33)より

\begin{align*} A\mathbf x = \mathbf y \Leftrightarrow \mathbf x = A^{-1}\mathbf y \end{align*}

であるから、先程の行基本変形より

\begin{align*} \mathbf x = I \cdot \mathbf x = R_n \cdots R_1A\mathbf x = R_n \cdots R_1\mathbf y \\ \end{align*}

となるので、\(\mathbf x = A^{-1}\mathbf y\)より

\begin{align*} R_n \cdots R_1 = A^{-1} \end{align*}

である。ちなみに列基本変形では右から\(R_m\)を掛けていくのに相当するので、行基本変形と同様には扱えない。

Javaバイトコードの読み方

先日、Javaクラスファイル入門 - connpassにてバイトコードの読み方について話してきたので、折角だからこちらにも書いてみます。
内容的にはあくまで入門ということで、サンプルとして取り上げたコードを読むために必要な範囲の知識のみ、加えてあまり厳密な事までには言及していません。とりあえずこんな感じで動いてるという雰囲気を掴む事を主眼に起きました。厳密な定義なんてわざわざ自分が言わなくても仕様書見れば書いてありますしね。

Javaバイトコードとは?

通常、コンピュータを動かすための命令は高級言語がなんであれ、最終的にはアセンブラ、ひいては機械語となって処理されます。
Javaでは様々なプラットフォーム上で動作するJava仮想マシンを用意し、その仮想マシン上で動作する、先述のアセンブラの位置に該当する言語を解釈して実行します。大雑把に言ってしまえば、それがバイトコードということですね。

バイトコードを学ぶ意義としてもアセンブラと同様の事が言えます。最終的にどういう挙動になるのかを理解することは、より上位のコードを書く上でも助けになるでしょう。
私よりもちゃんとしたエンジニアのお言葉を聞きたい人は下記のIBM developer worksの記事を読むといいと思います。

Java bytecode:

Javaバイトコードの閲覧方法

javapコマンドを使います。この辺りは同勉強会で自分よりも先に、バイナリの解析について講師をしていただいた虎塚さんの記事を見ていただいた方が早いので、そちらに譲ります。

実例その1

以下のコードについて考えます

このコードをコンパイルして、javapにかけると次の様なコードが出力されます

Constant poolはひとまず置いておきましょう。定数やらが定義されているところです。本稿で着目すべき対象はその下、ブラケットで囲まれている箇所になります。抜き出すと以下の通り。

前半部分がコンストラクタ、後半部分がmain関数のバイトコードに相当します。javaのコードにはコンストラクタを書いていませんでしたが、このバイトコードから自動的に補完されていることがわかりますね。
さて、更にmain関数のみに着目して、そのバイトコード本体を見てみましょう。それだけを抜き出したものが次のコードです。

左の番号は行番号ではなく、実際にクラスファイル上でバイトコードが含まれている構造の中で何バイト目の位置にあるかを表したものです。
さて、これらの処理について概要を見てみます。詳細については後述するなりしますので、一旦は意味不明な言葉が出てきても突き進んでください。 また、以降で述べる命令についての正確な定義はJava Virtual Machine Specification (PDF)を参照下さい。

getstatic命令

getstatic命令はクラスのstaticフィールドを取得します。今回のコードではConstant poolの#2に該当する部分、java/lang/System.out:Ljava/io/PrintStream;より、java/lang/System.outを取得しています。

ldc命令

実行時コンスタントプールから項目をpushします。何処にpushするのかは後述。今回のコードでは#3、つまり文字列helloをpushする事を意味します。

invokevirutal命令

クラスに基づくディスパッチを行い、インスタンスメソッドを起動します。今回のコードでは#4、つまり java/io/PrintStream.println:(Ljava/lang/String;)V の型に一致するインスタンスメソッドを実行します。つまりはSystem.out.println(String)を実行することになります。

return命令

メソッドからvoidをreturnします。main関数の定義より、返値はvoidである必要があるのでこの命令が実行されます。

以上より、このmain関数では

  1. 使用するフィールド(System.out)の取得
  2. 引数(文字列hello)を渡す
  3. 関数(println)の実行
  4. voidをreturn

という手順で動作していることがバイトコードから読み取れます。

もうちょっと踏み込んでみる

さて、今の例では何をやっているかに軽く触れただけで、細かいところはスルーしてきました。ここで今のコードを理解するのに必要な最低限の事柄に触れておきます。

Java仮想マシンスタック

Java仮想マシンはプログラム実行時に必要なデータを保持する領域をいくつか持ちます。先程の処理を理解する為に、Java仮想マシンスタックについて見ていきます。

このスタックはスレッド作成と同時に作られる領域であり、フレーム(後述)を保持するためのものです。各スレッド毎にこのスタックを保持することになります。

f:id:warabanshi21:20141225231012j:plain

こんな感じですね。各スレッド毎のスタックは独立しています。

フレーム

スタックに積まれている、このフレームというもの。これらはメソッド毎に作成され、そのメソッドのローカル変数やオペランドスタック(後述)などを保持します。メソッドの定義が同じものであっても、メソッドの実行毎にそれぞれがフレームを持つことになります。再帰処理などで同じ定義のメソッドを呼んでも状態がそれぞれ独立して保持されているのはこのためですね。

f:id:warabanshi21:20141225232716j:plain

オペランドスタック

位置づけとしては前述の図の通り、フレームの中にあります。このスタックの役割は色々ありますが、ここで注目するのは

といった所です。例として、バイトコードのiadd命令を見てみましょう。この命令の概要は

  1. オペランドスタックから値を2つpopしてくる
  2. それら2値の和を取る
  3. 結果をオペランドスタックへpushする

という具合です。オペランドスタックの状況を図を交えて見てみます。

まず、スタックに和を計算する対象の2値、aとbが入っている状態とします。iadd命令はこれらをpopします。

f:id:warabanshi21:20141225233534j:plain

iaddがpopした2値の和を計算します。popされているのでスタックは空になっています。

f:id:warabanshi21:20141225233652j:plain

計算結果をpushします。この処理を経て、オペランドスタックには計算結果の値が1つだけ積まれている状態になります。

f:id:warabanshi21:20141225233757j:plain

ローカル変数配列

ローカル変数配列はローカルで使われる変数を保持するのに使われる他、状況によってはメソッド起動時のパラメーター引き渡しに使われます。例えばクラスメソッドの起動では、配列のindexが0の位置から連続したローカル変数として引き渡される事になります。

例えば、クラスXのメソッドAから、このクラスのクラスメソッドBを引数 x,y,z を渡して呼び出すとします。適当に書くならこんな感じでしょうか

class X {
    public static void A () {
        X.B(x, y, z);
    }

    private static void B(int x, int y, int z) {
        return x + y + z;
    }
}

このとき、Bのフレームにはローカル変数配列のindexが0の位置から順にx, y, zが入っている状態として、Bが開始されることになります。

最後に

一応、勉強会の時はもう一歩複雑な例について、スタックの様子も見ながらバイトコードをトレースして挙動を観察すると言うことをやりました。それについては省きます。スタック周りがわかってれば、あとは仕様書でコードの挙動を追えばそれでわかる範囲のものですから。

コードだけ載せておきますと、こんな感じで変数の定義と計算、クラスメソッドの呼び出しが含まれるものになります。興味ある人は実際にコンパイルしてクラスファイルをjavapにかけてみるといいと思います。そんなに長くもないので、軽い気持ちで出来る練習になるかと。

バイトコードの命令について、詳細な仕様はOracleの公式ドキュメントで、"The Java Virtual Machine Specification, Java SE 8 Edition"の方を閲覧してもらえばよいと思います。命令の一覧はchapter6にあります。フレームやらオペランドスタックやらの話はchapter2の2.6です。