現在、MacPortsを使って

sudo port install maxima
すると、下のようなエラーになって失敗してしまう。(macOS Sierra 10.12.4で確認)
../../doc/info//errormessages.texi:296: `Warning messages' has no Up field (perhaps incorrect sectioning?).
../../doc/info//errormessages.texi:162: Prev field of node `Operators of arguments must all be the same' not pointed to.
../../doc/info//errormessages.texi:152: This node (Only symbols can be bound) has the bad Next.
../../doc/info//errormessages.texi:152: Next field of node `Only symbols can be bound' not pointed to (perhaps incorrect sectioning?).
../../doc/info//errormessages.texi:204: This node (out of memory) has the bad Prev.
../../doc/info//errormessages.texi:8: `Error messages' has no Up field (perhaps incorrect sectioning?).
makeinfo: Removing output file `/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_macports_release_tarballs_ports_math_maxima/maxima/work/maxima-5.39.0/doc/info/maxima.info' due to errors; use --force to preserve.
make[3]: *** [maxima.info] Error 1

原因は、makeinfoのバージョンが古いからのようだ。
http://maxima-discuss.narkive.com/KeLR9qhb/can-t-install-5-39
に、makeinfoのバージョンが4.xだとこうなることが書かれている。 実際、macOS Sierraで makeinfo --version すると、(GNU texinfo) 4.8と表示される。

そこで、

sudo port install texinfo
(GNU texinfo 6.3のmakeinfoがインストールされる)してから再度
sudo port install maxima
すると、上記のエラーは出なくなった。Activationの段階で
--->  Installing maxima @5.39.0_3+xmaxima
--->  Activating maxima @5.39.0_3+xmaxima
Error: Failed to activate maxima: Image error: /opt/local/bin/maxima already exists and does not belong to a registered port.  Unable to activate port maxima. Use 'port -f activate maxima' to force the activation.
というエラーになるが、メッセージの通り、
sudo port -f activate maxima
とすると、一応wxMaximaと併用しても問題なく動いた。

2ヶ月前にCarbon EmacsのPython環境を整備したばかりなのだが、MacのOSを10.7.5から10.12.4にバージョンアップすると、Carbon Emacsがまともに動かなくなってしまった。起動はするのだが、すぐに固まってしまう。しかも、その後distnotedというプロセスが全てのCPUを奪い続けて、Macが激重になる。
この現象はOS X 10.9+Emacs 24.3で起こるらしく、Carbon Emacs(Emacs 22)でも起こったという情報は見つけられなかったが、おそらく同じ問題である。どちらかと言うとOSのバグなのだが、現時点ではEmacsを24.4以降にバージョンアップする以外に解決方法が見当たらない。

その為、長年お世話になったCarbon Emacsを手放し、Emacsの最新版である25.2を使うことにした。

EmacsWikiのPython Programming In EmacsのページにはPythonの開発環境を便利にする方法が色々書かれているが、筆者は高機能なPythonの開発環境を使いたい時はSpyderを使っており、EmacsのPython環境はPython.elでPython3が使えて、もう少し便利な補完が効けば十分なので、Jediだけを追加インストールすることにした。

Emacs 25.2にbuilt-inのPython.elはPython3に対応しているのだが、MacPortsでpython35をインストールして、M-x customize-group -> pythonして"Python Shell Interpreter"をpython3にすると、Warningが出まくり、妙に不安定だった。M-x list-packagesしてpython packageを0.25.2にバージョンアップすると少し改善したが、完全には直らなかった。
さらに、Jediをインストールすると、これまた不安定で、時々補完候補が出る前にEmacsが固まった。(C-gで抜けることはできる)

Emacs 24の最新版である24.5.1ではこの問題は起こらなかったので、当面、EmacsでPythonのコードを書く時は24.5.1を使うことにした。

Jediのインストールは、

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)
としてpackage.elにMELPAのリポジトリを登録し、M-x list-packagesしてjediをインストールし、Jedi.elのドキュメントに従ってセットアップした。

なお、Emacs25でjediをインストールすると、Emacs24で"Symbol's function is void: cl-struct-define"というエラーになった。これはjediに限らず、よくあることだそうで、Emacsの24と25を併用する場合、package.elで何かをインストールするならEmacs24でする方が良さそうだ。

さて、JediはAutoCompleteとCompanyModeの両方に対応しているが、package.elで"jedi"をインストールするとAutoCompleteが使われる。"company-jedi"をインストールするとCompanyModeが使われるのだが、"jedi"と"company-jedi"の両方をインストールすると、AutoCompleteが優先される。特にAutoCompleteに不満は無く、和製なので贔屓したいが、色々読んでいると、世界的には、日本でも現在は、CompanyModeの方が人気があるようなので、何が良いのかを知るために、これからしばらくはCompanyModeを積極的に使うべく、両方をインストールしてCompanyModeをデフォルトにすることに決めた。

しかし、.emacsや.emacs.d/init.elでCompanyModeを優先する適当な方法がわからなかった。Python modeにしてM-x auto-complete-modeとすればAutoCompleteのON/OFFが切り替わり、OFFだとCompanyModeが使われるのだが、Emacs Lispで(auto-complete-mode)としてもON/OFFが切り替わらず、auto-complete-mode関数にはOFFにする引数も無いのである。auto-complete-mode関数のソースコードを見ると、"(if auto-complete-mode ..."とあるので、

(setq auto-complete-mode nil)
(auto-complete-mode)
とすれば良さそうに思ったが、これでもOFFにならない。
但し、(setq auto-complete-mode nil)すると、即座にAutoCompleteがOFFになる。このことを使って、試行錯誤の末、.emacs等に
(add-hook 'python-mode-hook 'jedi:setup)
よりも前のどこかに
(add-hook 'python-mode-hook
(lambda () (setq auto-complete-mode nil)))
と書けば、Python mode開始時にAutoCompleteがOFFになり、CompanyModeが使われることがわかった。

ROC曲線を理解する

2値の予測(判別、識別、...)に用いる特徴量の良し悪しを評価する1つの方法として、ROC曲線というものがある。
実際は正で予測も正であるデータの数をTP(True Positive)、
実際は負で予測も負であるデータの数をTN(True Negative)、
実際は負で予測は正であるデータの数をFP(False Positive)、
実際は正で予測は負であるデータの数をFN(False Negative)、
と呼ぶ時、
TPR(True Positive Ratio)=TP/(TP+FN)を縦軸、
FPR(False Positive Ratio)=FP/(FP+TN)を横軸、
としたグラフである。

ROC曲線の例
ROC curve sample

ROC曲線は、必ず(0,0)から始まって(1,1)で終わる。
特徴量が全く予測の役に立たない、ランダムな値であれば、TPR=FPRの線になる。
ROC曲線より下の面積、AUR(Area Under ROC curve)(または単にAUC(Area Under the Curve))が大きいほど、特徴量の値の全域に渡って良い特徴量だとされる。 理想的な特徴量だと、ROC曲線はFPR=0とTPR=1の線になる。

ROC曲線は機械学習で識別器の評価によく用いられるらしいので、とりあえず覚えておこうと思ったのだが、筆者はこれの理解にえらく苦労したので、調べたことや考えたことをメモする。
統計学の検定と同様、こういう確率と論理を組み合わせたものは、人によって向き不向きがあるのだと思いたい。

TP,TN,FP,FNの関係を再度整理すると、次のようになる。

予測

TPFN (Type II error)
FP (Type I error)TN
PやNは予測がPositiveかNegativeかであり、TやFはそれが正解かどうかである。
FPは誤検出のことであり、統計学の検定でも使われる「第一種の過誤」(検定では帰無仮説を棄却できないのに棄却する条件に誤ってヒットしてしまうこと)である。
FNは検出不能であり、「第二種の過誤」(検定では帰無仮説が誤りなのに棄却する条件にヒットしないこと)である。

予測の精度に関する尺度としては、Accuracy, Presicision, Recall, F値があり、それぞれ次のように定義される。
Accuracy = (TP+TN) / (TP+TN+FP+FN)
予測の正解率。
Precision = TP / (TP+FP)
Positiveと予測される中の正解率。
Recall = TP / (TP+FN)
実際にPositiveの内、Positiveと予測される割合。検出力、Sensitivity。
F値(F-measure, F1 score) = ((Precision-1 + Recall-1)/2)-1
PrecisionとRecallの調和平均。統計学のF分布に従うF値とは関係ない。
PrecisionとRecallはトレードオフの関係にあるので、それらをバランス良く合成した尺度。

予測の精度はAccuracyで評価するのが簡単だが、実際の正のデータ数と負のデータ数に偏りがあると、データ数が少ない方の正解率が低くても、データ数が多い方の正解率が高ければAccuracyが高くなってしまうので、Accuracyだけでは適切に評価できない。
そのような場合にPrecisionやRecallが用いられるが、これらは一般に特徴量の閾値によってトレードオフの関係があり、セットで評価しないといけないので、単純比較には向かない。そこで用いられるスカラー値が、F値や、ROC曲線のAUCである。
Precision-Recall曲線のAUCも使われることがあるが、Presicionは実際の正のデータの割合に依存するので、正のデータの割合が同じでないと比較には使えない。実際の正のデータの割合が極端に小さい場合など、Precisionが大きな意味を持つ場合にはPrecision-Recall曲線が用いられる。

ROC曲線は、TPR=TP/(TP+FN)とFPR=FP/(FP+TN)のグラフである。TPRを陽性率、FPRを偽陽性率と呼ぶこともある。TPRはRecallと同じである。FPRはfall-out(副産物)と呼ばれることもある。

次の図の3つのROC曲線が、正のデータと負のデータがどのように分布する特徴量に対応するかを考えてみる。
ROC curve sample 2

例えば、次のような分布になる特徴量だと、青いROC曲線になる。
distribution of totally independent feature
横軸は特徴量、縦軸は赤い部分が正のデータの分布、青い部分が負のデータの分布を表している。このグラフでは、正のデータも負のデータも一様分布している。閾値tより右ならPositive、左ならNegativeと予測する時、tを右端から左に動かすと、TPRもFPRも0から1に向かって増大するが、常にTPR=FPRである。正のデータと負のデータの割合はグラフの形状には関係しない。

次のような、正のデータと負のデータが完全に分かれる理想的な特徴量だと、緑のROC曲線になる。
distribution of ideal feature
tを右端から左に動かすと、FPR=0のままTPRが0から1に変化し、青のゾーンに入ると、FPRが0から1に変化する。

次のような分布だと、赤いROC曲線になる。
distribution of which makes ROC curve perfect arc
tを右端から左へ動かすと、FPRよりもTPRの方が早く上昇する。なるべく正のデータと負のデータの分布が分離している良い特徴量ほどFPRが上昇する前にTPRが上昇するので、AUCが大きくなることがわかる。

AUCはどれくらいだと良いか、という基準は一般的なものも色々あるようだが、大体、最低0.7は無いと有効ではないとされるようである。

なお、特徴量の最良の閾値(cut-off)はROC曲線の(0,1)に最も近い点とする、という方法を複数の箇所で目にしたが、明確な理論的根拠がある訳ではなく、必ずしもそれに限定されないようである。そもそも、(0,1)に最も近いというのがユークリッド距離で良いのかどうかがわからない。

この前、pandasを使っていて、次のような感じの、関連する2つのテーブル、access_logとchoice_logがある時に、結合したテーブルを作らずにchoice毎のtimestampの最小値を求めたかったのだが、どう書けば良いのかわからなかった。

import pandas as pd
import numpy as np
access_log = pd.DataFrame({'session': [100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
                           'timestamp': [314, 159, 265, 358, 979, 323, 846, 264, 338, 327]})
choice_log = pd.DataFrame({'session': [100, 100, 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109],
                           'choice':  ['A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E']})
>>> access_log
   session  timestamp
0      100        314
1      101        159
2      102        265
3      103        358
4      104        979
5      105        323
6      106        846
7      107        264
8      108        338
9      109        327
>>> choice_log
   choice  session
0       A      100
1       B      100
2       C      101
3       D      102
4       E      102
5       A      103
6       B      104
7       C      104
8       D      105
9       E      106
10      A      106
11      B      107
12      C      108
13      D      108
14      E      109
>>> 

結合テーブルを作るなら、次のように書ける。

merged = choice_log.merge(access_log, on='session', how='left')
result = merged.groupby('choice')['timestamp'].min()
>>> result
choice
A    314
B    264
C    159
D    265
E    265
Name: timestamp, dtype: int64
>>> 

実際にあったテーブルは巨大で、他にも列がたくさんあり、単純に結合テーブルを作るとRAMが足りなくなってメモリスワップが多発したので、結合テーブルを作らずにこれと同じことがしたかったのだが、その書き方がわからなかった。
結局access_log.set_index('session').to_dict()のようにして一時的なdictを作って、スワップを多発させながら処理してしまった。
それが心残りだったので、改めてpandasのドキュメントを拾い読みしながら方法を探してみた。

  1. 単純に、別のテーブルを参照する関数をSeries.mapに渡す
    def session_to_timestamp(session_series):
        return session_series.map(lambda x: access_log[access_log.session==x]['timestamp'].iat[0])
    
    result = choice_log.groupby('choice').agg(lambda x: session_to_timestamp(x).min())
    

    Seriesの先頭の要素を取り出す方法には、.iat[0]の他に.iloc[0]や.values[0]などがあり、筆者が試した所values[0]の方が速かったりしたが、pandasのドキュメントに書かれているのはilocとiatなので、ここでは添字が整数なら高速なiatを用いた。

  2. リスト内包表現(list comprehension)で別のテーブルを参照する
    def session_to_timestamp(session_series):
        return [access_log[access_log.session==x]['timestamp'].iat[0] for x in session_series]
    
    result = choice_log.groupby('choice').agg(lambda x: min(session_to_timestamp(x)))
    

    リストにはminメソッドが無いので、session_to_timestamp(x).min()とはできない。

  3. isinを使ったBoolean Indexingで別の表のサブセットを得る
    def session_to_timestamp(session_series):
        return access_log[access_log.session.isin(session_series.values)]['timestamp']
    
    result = choice_log.groupby('choice').agg(lambda x: session_to_timestamp(x).min())
    
  4. 別の表からSeriesを作ってSeries.mapに渡す
    def session_to_timestamp(session_series):
        return session_series.map(access_log.set_index('session').timestamp)
    
    result = choice_log.groupby('choice').agg(lambda x: session_to_timestamp(x).min())
    

    一見シンプルで美しそうだが、set_index()はコピーを返すので、timestampの一時的なdictを作るのと変わらない。しかも、グループ数だけ新たなテーブルを作るので、無駄である。

これらの処理時間を色々測ってみたが、2つのテーブルのサイズやグループの数によって変わり、どう比較すれば良いかわからなかったので、省略する。
大まかな傾向としては、1.と2.の処理時間はchoice_logのサイズに依存し、3.と4.の処理時間はsession_logのサイズに依存するようだった。4.はset_indexした中間テーブルを事前に作っておくと高速化するが、それでも、大抵の場合3.が一番速かった。
いずれの方法も最速になる場合があるようなので、場合毎に色々試してみるしかなさそうである。

肝心のメモリ使用量は、適当な測り方がわからなかった。
そもそも、スワップしながらの処理時間が問題だったので、単純にメモリ使用量では測れないと思う。

他にも、以下のような方向で書き方を考えてみたが、うまくできなかった。

  • groupbyでaggregateでなくtransformしてmin()
    transformする時に別のテーブルを参照することを考えたが、transformするとgroup解除されてしまうので、使えなかった。transformする時にmin()するのなら、min()した値を増殖させるだけ無駄なので、確実にaggregateの方が効率が良い。
  • pandas.DataFrame.lookupを使う
    引数としてindexしか受けられないので、使えなかった。
  • pandas.DataFrame.joinを使う
    pandas.DataFrame.mergeを使うのと変わらなかった。

そもそも、lambda式を使わずに書く方法は無いのだろうか。
teratailとかStack Overflowとかで聞いた方が早いか。

ちょっと前に、はやりのAIのプログラミングでよく使われるPythonとNumPyとPandasを使い始めたのだが、PandasでCSVファイルを読み込んで、各ラベルの出現回数を得る為にNumPyのbincountメソッドを使うと、次のようなエラーが出て、困った。

TypeError: Cannot cast array data from dtype('int64') to dtype('int32') according to the rule 'safe'
その時使った環境は、Windows 7(32bit版)+Python 3.5.2(Anaconda 4.1.1 (32-bit))である。Mac OS Xでは出なかった。

これは、32bitのPythonを使っていると起こることらしい。例えば、32bit Pythonで次のプログラムを実行すると、同じエラーが出る。

import numpy as np
import pandas as pd
import io

data = np.random.randint(10, size=30)
buf = io.StringIO("\n".join(str(x) for x in data))
df = pd.read_csv(buf, header=None)
x = df[0].values
print(x)
print(np.bincount(x))	#Error on 32bit Python
これは、Pandasが32bit Pythonでもint64を使うのが原因のようである。

このエラーを回避するには、bincountに渡すデータの型をint32にすれば良い。

print(np.bincount(x.astype('int32')))	#also OK on 32bit Python
出力例
[1 0 6 7 1 7 7 6 9 5 6 2 8 0 7 6 7 8 9 7 8 9 8 0 2 1 2 6 0 3]
[4 3 3 1 0 1 5 6 4 3]

または、np.unique(return_counts=True)を使う方法があり、こちらの方が、大抵の場合はnp.bincountを使うよりも好ましいとされるようである。

print(np.unique(x, return_counts=True))
出力例
(array([0, 1, 2, 3, 5, 6, 7, 8, 9]), array([4, 3, 3, 1, 1, 5, 6, 4, 3]))
確かに、np.unique(return_counts=True)の方が、データに負の値があっても使えるし、大きな値が混ざってても配列が巨大にならないので、安全そうである。

なお、今動作しているPythonが32bitか64bitかを判定する方法は、いくつかあるようである。 例1

import platform
platform.architecture()
出力(上がMacOSX、下がWin32)
('64bit', '')
('32bit', 'WindowsPE')
例2
import sys
"%x" % sys.maxsize
出力(上がMacOSX、下がWin32)
'7fffffffffffffff'
'7fffffff'
例3(platform.architecture()の実装にも使われている方法)
import struct
struct.calcsize("P") * 8
出力(上がMacOSX、下がWin32)
64
32

Carbon EmacsにPython 3環境導入

筆者はMacで未だにEmacs 22ベースのCarbon Emacsを多用している。Emacs 24ベースのCocoa Emacsもインストールはしているのだが、色々な環境をCarbon Emacsに作ってしまっているので、移行するのが億劫なのである。

最近、Python 3を使い始めたのだが、Carbon Emacsのpython.elはPython 2にしか対応していないので、何とかPython 3に対応させる方法は無いかと探した結果、
http://www.loveshack.ukfsn.org/emacs/
から
python.el
emacs.py
sym-comp.el
の3つをダウンロードして、Emacs.app/内に置けば良いことがわかった。(python.elとemacs.pyは既にあるものを置換、sym-comp.elはpython.elと同じディレクトリに追加)
そして、Emacsを立ち上げて、M-x customize-groupとし、
"Python Default Version" = 3
"Python Python Command" = python3
にすれば完成である。

これで、C-c TABとM-TABを駆使すればシンボルの補完ができて、まあまあコーディングが楽になるのだが、やっぱり補完機能は便利にしたいので、
Pythonの補完をEmacsでシンプルに最小労力で手早く使えるようにする - 牌語備忘録 -pygo
を参考にして、auto-complete.el + ac-python.elをインストールした。
auto-complete.elは、Emacs 22で動作実績のある、auto-complete-1.3.1.tar.bz2をどこかから入手した。

そして、.emacsに次の4行を書けば完成である。

(require 'auto-complete)
(require 'auto-complete-config)
(ac-config-default)
(require 'ac-python)

これで、python-modeにして、import mathと書いてC-c TABして、math.と打つと、math.sqrtやらmath.piやらが補完候補として自動的に現れるようになった。

対石田流の定跡で悩む

筆者は石田流が苦手である。後手になって、初手から▲7六歩△3四歩▲7五歩とされると、これは大変なことになったな、と思ってしまう。同じぐらいの実力(アマ三段程度)同士だと、石田流にされると3局に2局は負けてると思う。
先週は将棋大会があり、石田流対策としては、▲7六歩△3四歩▲7五歩には△4二玉、下図の局面から仕掛けるなら△9二飛▲8五桂△8二飛▲8六歩△8四飛、の2つだけを覚えて臨んだら、予選の1局目から▲7六歩△3四歩▲7五歩とされ、次の図の局面を迎えてしまった。

このような局面から△9二飛▲8五桂△8二飛▲8六歩△8四飛と飛車を端に振ってすぐ戻して浮くのは、過去に何かの本で読んだもので、多分定跡だと思う。もし△9二飛▲8五桂△8二飛の次に▲8六飛とされたらどうすればいいんだろう、と不安に思っていたが、他に何も思い付かなかったので、定跡と信じて△9二飛、▲8五桂、△8二飛と手早く指した。

すると、ノータイムで実際に▲8六飛とされてしまい、長考に沈んでしまった。
後で調べると、ここまでは2004年のNHK杯の三浦−行方戦と全く同じ進行であり、その対局ではやはり▲8六歩と指されていた。他にも部分的に同じ形から△9二飛▲8五桂△8二飛としたプロの棋譜を4つぐらい見つけたが、次の手は全て▲8六歩△8四飛だったし、激指14で分析しても▲8六飛は評価が低いので、良くない手なのだと思うが、これの咎め方がよくわからない。

次に何をやっても▲7三桂成がある。それには△8六飛(次図)とする一手だが、▲同角でも△7三桂の後に先攻されてすぐ桂損を回復されるし、▲6三成桂と踏み込まれる手も気になる。

筆者の実戦は、▲8六飛の後、△7四歩▲7三桂成△8六飛▲同角△7三桂▲7四歩△同銀▲7一飛(次図)と先着され、△6二金と受けたら▲9一飛成から大暴れされ、なす術無く、十数手後には激指の先手の評価値が+1000という必敗の形勢になってしまった。

激指によると、△6二金が悪手で、ここまで来たら桂取りが銀取りにならないように△6三銀と戻るのが良いらしいが、そこまで後手を引いて駒損を回復されて龍まで作られてしまうのでは、何をやってるのかわからないと思う。
ただ、△7四歩では△6五歩の方が良さそうである。▲7三桂成と歩を取られるのなら逃げておこうと思って△7四歩と避けたのだが、△7三桂と取り返した後に▲7四歩と取る手が桂取りの先手になるし、△7四歩が無くても△7三桂には▲7四歩と突き出す所なので、意味が無かった。▲8六飛の後、△6五歩▲7三桂成△8六飛▲同角△7三桂▲7四歩△同銀▲7一飛△5五角というのが激指の推奨手順である。

激指はこれで-400(後手少しリード)くらいの評価を示しているが、筆者にはこれで後手が指せる理由がわからない。次に△3六桂とやっても▲3九玉と引かれるので攻め方がわからないし、やはり▲9一飛成から暴れられて、筆者の実力ではその勢いを止められないように思う。
ただ、もし△5五角に▲5六歩とされれば、△3六桂で一気に後手勝勢になりそうである。△3六桂に▲3九玉だと△6六角が王手で危険なので▲1七玉しか無いが、△1五歩とすれば受けが難しいと思う。

▲5五歩なら△1六歩▲2六玉△2八桂成である。
こうなることを期待して、▲8六飛には△6五歩とするべきなのだろうか。

筆者はpsvn.elを6年くらい愛用している。仕事でSubversionを使ってるのだが、Subversionのクライアントソフトとして、周囲がTortoiseSVNを使う中、一人で頑にpsvn.elを使っている。マウス操作やトラックパッド操作が必要なく、全てがキーボード操作で完結するのが、個人的に気持ち良いのである。

最近、仕事で使うSubversionのバージョンが1.6から1.8に変わり、それまで使っていた、2009年のバージョンのpsvn.elが動かなくなった。Subversion 1.7でワーキングコピーの形式が大幅に変わったからである。

そこで、Subversion 1.7にも対応している、最新のpsvn.el(2015-07-20版)をダウンロードして使い始めた所、ワーキングコピーのルートディレクトリにチェックアウトしたワーキングコピーでM-x svn-statusすると、

Wrong type argument: stringp, nil
というエラーが出て使えなかった。つまり、
svn co (URL1) aaa
cd aaa
svn co (URL2) bbb
とした時のディレクトリbbbの下でM-x svn-statusができなかった。
svn coの代わりに、svn propsetでsvn:externalsを指定してbbbを取得しても同じエラーになった。
ディレクトリaaaの下でsvn switchして別のワーキングコピーを取得した場合は同じ問題が起こらないのだが、あいにくURL1とURL2とでSubversionのリポジトリが異なる為に、svn switchでbbbを取得することができないのである。

これが筆者としては非常に困ったので、自力で修正してみた。
変更箇所をdiff形式で示す。

修正案1

--- psvn.el.org	2015-07-20, 21:42:00
+++ psvn.el	2016-09-30
@@ -6063,6 +6063,7 @@
         ;; it doesn't, e.g we reached / already.
         (setq parent (expand-file-name (concat wc-root "..")))
         (or (and (< (length parent) (length wc-root))
+                 (not (file-exists-p (concat wc-root (svn-wc-adm-dir-name))))  ;; stop if .svn or equivalent exists
                  (svn-status-base-dir-1 (expand-file-name (concat wc-root ".."))))
             wc-root)))))
svn-status-base-dir-1関数の中で、ワーキングコピーのルートディレクトリを探すのに、できるだけ上位のディレクトリを探すようで、上に辿る途中にエラーになるので、.svnを発見したらそれ以上遡らないようにするものである。

修正案2

--- psvn.el.org	2015-07-20, 21:42:00
+++ psvn.el	2016-10-10
@@ -3084,7 +3084,7 @@
     (let ((svn-process-buffer-name "*svn-info-output*"))
       (when (get-buffer svn-process-buffer-name)
         (kill-buffer svn-process-buffer-name))
-      (svn-run nil t 'parse-info "info" ".")
+      (svn-run nil t 'parse-info "info" default-directory)
       (svn-status-parse-info-result)))
   (unless (eq arg t)
     (svn-status-update-buffer)))
原因がよくわからないが、途中にワーキングコピーのルートディレクトリがあると、そこから1つ上に遡った時に、カレントディレクトリがdefault-directoryでなく、もう1つ上のディレクトリにずれてしまうようで、カレントディレクトリがワーキングコピー中でないと svn info . がエラーになる(*)ので、 svn info する時に常にdefault-directoryを使うように変えるものである。

どちらも適切な修正かどうかはわからないが、とりあえず筆者の環境(Linux + Subversion 1.8 + Emacs 2.4)では標記の問題は解決した。

被写界深度というのは、レンズを通して見た映像の、ピントが合っているように見える、奥行き方向の範囲のことである。
昨年、フォトマスター検定という試験を受ける為に勉強した中で、被写界深度を求める式が何故このようになるのかがわからず、気になっていた。
先月、フォトマスター検定について講義する機会があったので、そのネタとして、被写界深度の式を導いてみた。


図1

まず、被写界深度が何故存在するかというと、ピントが合っていない距離の点は点でなく丸になって映る(ボケる)が、その丸(錯乱円)が十分に小さいと、ほとんどボケてるように見えないからである。
その十分に小さい錯乱円の直径=許容ボケをδとすると、フィルム(デジカメだとセンサー)上のδに対応して、光軸上のフィルム/センサーの前後に、その範囲に焦点を結ぶとピントが合って見える範囲=焦点深度が存在する。

図2

この焦点深度の幅を2εとすると、F値(絞り値)=レンズ焦点距離(f)÷レンズ口径(Φ)を使って、f >> εなのでε/δ = F、すなわちε = Fδと近似できる。
この焦点深度に対応する、被写体側の範囲が、被写界深度である。

ここで、「ガウスの結像公式」(以下、単に「結像公式」)というのを用いる。結像公式とは、凸レンズからa離れた位置から発せられる光が、凸レンズを通って反対側のb離れた位置に焦点を結ぶ時、aとbの関係を、レンズ焦点距離fを用いて、
\frac{1}{f}=\frac{1}{a}+\frac{1}{b}, Gaussian form of the lens equation
と表すものである。ちなみに、レンズの焦点距離とは、無限遠から来る光が焦点を結ぶ位置のことである。(上式でa=∞とするとb=fとなる)

この結像公式を用いて、焦点深度と被写界深度の関係を式にする。
図1のように、sをレンズから合焦位置までの距離、snを被写界の近点、sfを被写界の遠点、tをレンズからフィルム/センサーまでの距離とすると、

となる。これらの連立方程式を、tを消去して、snとsfについて解く。
(1)よりt=sf/(s-f)が得られるので、これを(2),(3)に代入すると、
s_n=\frac{(t+\epsilon)f}{(t+\epsilon)-f}=\frac{(\frac{sf}{s-f}+\epsilon)f}{(\frac{sf}{s-f}+\epsilon)-f}=\frac{(sf+\epsilon(s-f))f}{f^2+\epsilon(s-f)}
s_f=\frac{(t-\epsilon)f}{(t-\epsilon)-f}=\frac{(\frac{sf}{s-f}-\epsilon)f}{(\frac{sf}{s-f}-\epsilon)-f}=\frac{(sf-\epsilon(s-f))f}{f^2-\epsilon(s-f)}
が得られる。
ここで、s,f>>εなので、s±ε≈s, f±ε≈f, sf±ε2≈sfと近似できることを用いて、簡略化する。
s_n=\frac{(sf+\epsilon(s-f))f}{f^2+\epsilon(s-f)}=\frac{((s-\epsilon)(f+\epsilon)+\epsilon^2))f}{f(f-\epsilon)+\epsilon s}\approx\frac{sf^2}{f^2+\epsilon s}
s_f=\frac{(sf-\epsilon(s-f))f}{f^2-\epsilon(s-f)}=\frac{((s+\epsilon)(f-\epsilon)+\epsilon^2))f}{f(f+\epsilon)-\epsilon s}\approx\frac{sf^2}{f^2-\epsilon s}

これにε=Fδを代入すれば、被写界深度の近点と遠点を求める式になるのだが、フォトマスター検定の参考書では過焦点距離を用いた式になっているので、もう少し変形を進める。
過焦点距離とは、被写界深度の遠点が無限遠に届く、最短の撮影距離である。焦点深度と結像公式を用いて被写界深度の近点、遠点と同じ要領で、過焦点距離Hについても式を立てると、

となる。下の式からt=f+εが得られ、それを用いて上の式をHについて解くと、
H=\frac{tf}{t-f}=\frac{(f+\epsilon)f}{\epsilon}\approx\frac{f^2}{\epsilon}=\frac{f^2}{F\delta}
すなわち過焦点距離=レンズ焦点距離2÷(絞り値×許容ボケ)と求まる。これを用いてsn, sfをさらに整理する。
s_n\approx\frac{sf^2}{f^2+\epsilon s}=\frac{sf^2}{f^2+\frac{f^2}{H}s}=\frac{Hs}{H+s}
s_f\approx\frac{sf^2}{f^2-\epsilon s}=\frac{sf^2}{f^2-\frac{f^2}{H}s}=\frac{Hs}{H-s}

従って、
被写界近点=(過焦点距離×合焦距離)÷(過焦点距離+合焦距離)
被写界遠点=(過焦点距離×合焦距離)÷(過焦点距離−合焦距離)
である。

なお、過焦点距離の計算には許容ボケが必要になるが、許容ボケは通常、フィルムやセンサーの対角線長÷1300、が用いられる。
35mmフィルムなら、√(362+242)/1300≒0.03328 (≒1/30) mm、
フォーサーズ/マイクロフォーサーズなら√(17.32+132)/1300≒0.01665 (≒1/60) mm、である。