2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

数学の絵は、きれいなので誰も確かめない ―― 定理を門にして描く PoC シリーズ

2
Last updated at Posted at 2026-09-23

title: '数学の絵は、きれいなので誰も確かめない ―― 定理を門にして描く PoC シリーズ'
tags:

  • Python
  • NumPy
  • 数学
  • アルゴリズム
  • 画像処理
    public_private: true
    public_id: fe04f6eef40119913894

言語 / Language: 日本語 · English

数学の絵は、きれいなので誰も確かめない ―― 定理を門にして描く PoC シリーズ

回る円の連鎖が北斎の波を描く

↑ 腕が 1 → 4 → 16 → 90 → 600 → 4000 本と増えるにつれ絵が正体を現す。どの段でどこまで似るかは描く前に分かる。

図について: この記事が扱う 7 回ぶんの図は、下に全部出してあります(86 枚)。シリーズ外も含めた全 151 回・1165 枚は紙面の計測館に並べてあり、元のファイルはこちらにあります。

この記事について

位相彩色、ニュートンの吸引域、マンデルブロ集合、アポロニウスの円、葉序の螺旋、翼まわりの流れ。数学の図はきれいです。そしてきれいなものは、合っているかを誰も確かめません。

実装が少し間違っていても、出てくる絵はやはりきれいです。色は回るし、境界はフラクタルになるし、円は詰まります。間違いが絵に出ない——これがこの分野を書くときの難所です。

そこで描画コードを足すときの基準を 2 つに絞りました。

(i) 定理が門になるか、(ii) 既存の別の実装が真値になるか。
どちらも満たさないものは書かない。使った式で答え合わせをするのは禁止。

このシリーズは、その基準で作った描画を 1 本ずつ並べます。毎回、絵と一緒に成績表が出ます。コードは fullseye にあり、図はすべてスクリプト自身の出力です。

シリーズの現在地

回 何を描くか 門(真値の出どころ)
1 写真を一本の線にして回る円に描かせる パーセバルの等式 / 最小全域木という下界
2 複素平面を「面」で見る 偏角の原理 / Cayley 1879 / 主カージオイドの閉形式
3 定理が絵になる デカルトの円定理 / ファレイ隣接 / オイラーの公式 / モランの式
4 うなりは一つ ―― 干渉縞と印刷のモアレは同じ数学 閉形式の固有値 / ベッセルの零点 / 格子式 / モアレ周期の予言
5 絵では確かめられないもの ―― 力学系と極小曲面 行列指数関数 / トレース恒等式 / 厳密な分岐点 / H ≡ 0 という定義
6 目が嘘をつく絵を作って、測る側を採点する リュカの定理 / デカルトの円定理 / div(curl ψ) ≡ 0 / 総量保存
7 測定器のほうを健診する ―― キャリパーが外すのはどこで、なぜか 生成器が保証する真値 / 残差 rms / 想定幅の宣言 / 端の定義

第 1 回: 写真を一本の線にして、回る円に描かせる

手順

写真 → 点描 → 一本の閉じた線 → 等弧長に打ち直し → フーリエ係数 → 回る円で描き直し

写真の濃淡を点の密度に写し(重みつき Lloyd)、全部の点をちょうど 1 回ずつ通って戻る経路を作り(TSP)、等弧長に打ち直して複素フーリエ級数に載せます。閉じた曲線のフーリエ級数は回る腕の足し算なので、そのまま振り子の連鎖になります。

筋は TSP art(Kaplan & Bosch 2005)です。巡回路が閉じていることがフーリエ級数に載る条件なので、この 2 つはもともと噛み合っています。

言葉 かみくだくと
点描(stippling) 濃いところに点を多く置き、点の密度で階調を表す
重みつき Lloyd 点を「自分の縄張りの重心」へ繰り返し動かす手続き
最小全域木(MST) 全部の点を繋ぐ最短の木。閉じた巡回路はこれより短くなれない
パーセバルの等式 「総エネルギー = 係数の二乗和」。打ち切り誤差を先に計算できる

門と成績

北斎「神奈川沖浪裏」(202×300 画素)を 9,000 点で描いた実測です。真値に使ったのは下界・対照群・閉形式の 3 種類だけで、特別な道具は要りません。

主張 実測 真値の出どころ 対照群
点の密度が濃淡を追う 相関 0.9946 ランプ画像の帯ごとの暗さ 一様画像では等間隔(cv 0.13 対 0.37)
巡回路の質 MST 比 1.146 閉じた巡回路は MST より短くなれない 座標順に繋ぐと 41.4
濃淡の再現 相関 0.984 目標画像との直接比較 同数のランダム線は +0.013
ペン幅 0.900 px インク率 ≈ 線長 × ペン幅 / 面積 目標の 0.890 倍、不足 11 % が重なり
円の本数 K=64 で 99.0 % パーセバルの等式 予言と実測の差 7e-15 px
紙の上 15.57 m / 最短 8.6 分 G-code に変換して測る ―

元の版画と点描

一本の線と対照群

↑ 左が一筆書き(MST の 1.146 倍)、右が同じ点を座標順に繋いだ対照群(41.4 倍)。同じ点でも繋ぎ方でこれだけ違います。

★ 実装すると、ここで間違える

この筋道を自分で書く人が確実に踏む穴が 4 つあります。どれも例外は出ず、絵はそれらしく出るので、テストを書かないと気づけません。

1. 相関が高くても、密度は暗さに比例していない

「点の密度が濃淡を追う」を相関で測ると 0.99 を超えます。しかし相関は単調性しか見ていません。

重みつき Lloyd が作るのは重心ボロノイ分割です。Gersho の予想により、最適な点密度は重み ρ に対し √ρ に比例します(2 次元で ρ^(d/(d+2)))。つまり重みをそのまま渡すと、濃淡の半分しか濃さに出ません。

実際に指数(log 密度 を log 暗さ に回帰した傾き)を測ると 0.61 です。相関 0.9946 と指数 0.61 は矛盾しません——単調ではあるので相関は高く出ます。

濃淡に比例させたいなら、重みを二乗して渡してください。 実測で指数は 0.77 まで上がります(床と有限の点数のぶん、1 には届きません)。

2. フーリエに載せる前に、線が短くなっている

巡回路を等弧長に打ち直して標本の列にするとき、標本間隔が線分より粗いと角が切られ、線そのものが短くなります。載せる前の段階で情報が落ちるので、そのあとの「予言が当たった」は土台から崩れます。

線分が平均 2.08・中央 1.57 px の巡回路で測ると:

打ち直し点数 標本間隔 長さ保持 表現できる最短周期
16,384 1.140 px 0.935 2.28 px
32,768 0.570 px 0.967 1.14 px
65,536 0.285 px 0.984 0.57 px

間隔 1.14 px では密な領域を解像できず、線が 6.5 % 縮みます。誤差はきれいに 1/N で落ちるので、標本間隔が線分の中央値を下回るまで増やすのが目安です。

3. 点数が偶数だと、フーリエ係数を二重に数える

複素フーリエ係数を k = −N/2 … +N/2 で取り出すとき、N が偶数だと両端が同じ係数を指します。両方足すとパーセバルの等式が破れます(N=8 で 1.17e-01、N=512 で 7.5e-03)。

厄介なのは、対称性の高い形では気づけないことです。正方形の輪郭で検算すると、最高周波数の係数がたまたま 0 に近いので通ってしまいます。N を 8, 9, 16, 17, 64, 255, 256 と振って、係数の本数とパーセバルの両方を固定してください。

4. ペン幅は調整するものではなく、解くもの

線が重ならない範囲では インク率 ≈ 線長 × ペン幅 / 面積 が成り立つので、目標の濃さからペン幅を逆に解けます。

ただし 2 つ注意があります。ひとつは、線を整数画素の円板で塗るとペン幅が階段になること(0.5 / 1.0 / 1.5 px が同じ絵になります)。被覆率で塗れば連続なノブになります。

もうひとつは、解いた幅でも目標には届かないこと。実測でインク率は目標の 0.890 倍でした。合併は和より小さいので実測は必ず予言以下になり、その差 11 % が線の重なりの量そのものです。ここを誤差と見ずに「重なりの測定値」と読むのが正解です。

向かないこと

墨の線画には向きません。 重心ボロノイは必ず画面全体を埋めるので、暗い画素が数 % しかない絵では点の大半が白地に配られます。鳥獣戯画(暗い画素 5.5 %)で試すと元の絵が読めません。線画は細線化して線そのものを辿るべきで、それは別の道具です。

エッジに沿わせる項は入れませんでした。 試すとエッジ近傍を通る線の割合は 0.421 → 0.530 に上がりますが、濃淡の相関が 0.911 → 0.772、疎密比が 6.5 → 2.4 と両方落ちます。この絵ではエッジが既に暗いので、濃度の対比を上げるほうが両方良くなります(エッジの面積比は 0.235 なので、偶然に線が乗る割合が 0.235。濃淡だけで 0.475 なら既に 2 倍です)。

動かす

pip install -U fullseye
py -3.11 examples/poc_one_stroke_epicycles.py \
    --image my_photo.jpg --points 12000 --out out/mine

点の数は「画素数 × 点数」で効きます。まず --points 3000 で形を見てから増やすのが早いです。

import fullseye as fs

img  = fs.read_image("photo.png")
pts  = fs.ledger.stipple_points_from_image(img, 9000, gamma=1.6)  # 濃いところに点
tour = fs.ledger.stroke_tour_closed(pts)                           # 一本の閉じた線
err  = fs.ledger.stroke_tone_error(img, tour)                      # 何を保ったかを数で
print(err["corr"], err["length_px"])

この回が作った図は全部で 9 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 6 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

lloyd

↑ Lloyd's energy sum w|x-c(x)|^2 never goes up — that is a property of the algorithm that can be checked exactly, which is why it is the gate rather than the look…

tone

↑ the tone of a line drawing exists at a scale coarser than the line, so both pictures are compared after a Gaussian of sigma 6 px: correlation 0.984 against +0.0…

pen

↑ while the strokes do not overlap the ink fraction is length x width / area, so the width that reproduces the mean tone is solved in closed form (0.90 px here)

harmonics

↑ Parseval makes this a prediction, not a measurement: the squared error of the order-K reconstruction equals the energy of the coefficients above K, exactly

numbers

↑ every row is measured by an operator in this run; the harmonic rows are predicted in closed form before the drawing is made

harmonic table

↑ Parseval: the error above is the energy that was left out, exactly

第 2 回: 複素平面を「面」で見る

手順

有理関数 f(z) → 面の上で値を出す → 位相彩色 / 吸引域 / 脱出時間 → 絵とは別の真値で採点

複素解析の可視化は、曲線の上(積分・巻き数・ローラン係数・等角写像)と領域の上(値の場・位相彩色・吸引域)で別の道具が要ります。この回は後者です。

位相彩色・ニュートンの吸引域・脱出時間・翼まわりの流れ

↑ どれも「きれいだから正しい」が効かない絵。採点は全部この絵の外から来る。

門と成績

主張 測った量 実測 真値の出どころ
偏角の原理 巻き数 +3 / +1 / −2 既存 cplx_winding_number(窓の外は 0 = 対照群)
位相彩色から位数が読める 画素の RGB だけから色相の巻き数 1 / 2 / 3 / −2 偏角の原理
z²−1 の吸引域 厳密解との一致 262,144 画素すべて、未収束 0 Cayley 1879(2 つの半平面)
3 次の吸引域 同じ格子の境界画素 1,026 → 13,348(13 倍) Cayley が解けなかった側
主カージオイド 反復せずに内側と言える画素 反例 0 件、覆えたのは 90.1 % 閉形式(下界であることも数で出る)
翼まわりの場 コーシー–リーマン残差 2.06e-04 乱数 1.04 / 共役 2.000 が対照群
循環は経路に依らない 外周 / 内周 −3.0263 / −3.0263 コーシーの積分定理
クッタ条件 後縁の速度 22.9 → 22,020(循環 0 のとき) 対照群が発散する

おまけ: 揚力係数と薄翼理論の比は、迎角にも速さにもよらず厳密に a/b(円の半径と写像の特異点の距離の比)になります。実測のずれ 2.2e-16、厚みを 0 に近づけると薄翼理論に戻ります。厚みの効果が 1 つの数に落ちる、気持ちのよい結果です。

実装で踏む穴

既存 op が壊れて見えたが、壊れていたのは呼び方でした。 cplx_cr_residual が正則な場で 0.95、乱数で 1.04 を返し「判別していない」と結論しかけたところ、docstring に「行は虚部の増える向き」と書いてありました。こちらの格子は画像の並び(行が下向き)なので、op は共役の場を正しく測っていたのです。

規約違反は例外を出しません。もっともらしく別の量を返します。 なので食い違いは罠にせず門にしました —— 「そのまま渡すと 2」と「反転すると ≈ 0」を同じ検査に両方書いてあります。

なお「ちょうど 2」が出るのは中心差分が厳密になる 2 次以下の多項式のときだけで、一般の正則場では 2 から丸めの床の分だけ引かれます(実測 1.999951)。

向かないこと

脱出時間は反復回数の上限で切るので、境界の近くは常に「まだ分からない」が残ります(上限を上げると境界画素が増え続ける)。閉形式の内部判定は下界で、マンデルブロ集合の内部を全部は覆いません(実測 90.1 %)。位相彩色は位数を読めますが、零点の位置は読めません —— そちらは巻き数を数える op の仕事です。

最初の 1 本

import fullseye as fs

z = fs.cplx_plane_grid((-2.0, 1.0), (-1.5, 1.5), (512, 512))
basins = fs.cplx_newton_basins((1.0, 0.0, -1.0), shape=(512, 512))  # z^2 - 1
print("未収束:", int((basins < 0).sum()))          # Cayley の厳密解なら 0

この回が作った図は全部で 10 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 9 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

rational

↑ 有理関数 (z−z₁)(z−z₂)/(z−p) の位相彩色。

orders

↑ ★絵が定理を証明する側に回る。

boundary

↑ 2 次の境界は虚軸 1 本(513 画素の格子で 1026)。

mandelbrot

↑ ★★右の明るい部分は反復せずに内側と分かる領域 —— 固定点 z = (1−√(1−4c))/2 が吸引的(|2z*| < 1)なら軌道は決して 脱出しない、という閉形式。*

julia

↑ z → z² + c は |z| を 2 乗するだけなので、c = 0 では「|z| < 1 は 0 へ、|z| > 1 は無限へ」が厳密に言える。

flow

↑ 迎角 8 度のジューコフスキー翼まわりの非粘性流。

kutta

↑ 後縁は写像の特異点(dz/dζ = 0)なので、循環が正しくないと1/√距離 で発散する。

lift

↑ ★CL = 2π(a/b)sin(α+β)。

numbers

↑ どの行も「使った式」ではない真値で採点している: 既存 op (cplx_winding_number / cplx_cr_residual)、Cayley の定理、閉形式の内部判定、c=0 のジュリア集合、クッタ条件の対照群、薄翼理論との比。

第 3 回: 定理が絵になる

手順

曲率の四つ組 / ファレイの分母 / 黄金角 / IFS の写像 / 曲線の位数 → 絵
                                ↓
             絵を見ずに、定理の側から答え合わせをする

アポロニウスの円、フォードの円、測地ドーム、葉序の螺旋、IFS のアトラクタ、空間充填曲線。実装が少し間違っていても、円は詰まるし螺旋は回るしフラクタルはフラクタルに見えます。

アポロニウスの充填とフォードの円

門と成績

主張 測った量 実測 真値の出どころ
デカルトの円定理 (Σk)² − 2Σk² の相対ずれ 1e-13 以下(depth 4 の 164 円、接触 4 円の全組) 定理
整数充填 曲率の整数からのずれ 0 Lagarias–Mallows–Wilks
ファレイ隣接 接触と |ps − qr| = 1 の不一致 0 組(分母 12 の 47 円・1,081 組) 整数の等式
フォード円の個数 |F_n| 一致 1 + Σφ(k)(オイラーの関数)
測地ドーム 次数 5 の頂点 f = 1,2,3,4,6 すべて 12 個 V − E + F = 2
葉序 番号差の山がフィボナッチ 黄金角 7/7、137.0° は 2/7、90° も 2/7 フィボナッチ数列
IFS の次元 相似次元 vs 箱数え 1.5850(閉形式)/ 1.6164(箱数え) モランの式 Σrᵢᵈ = 1
空間充填曲線 4ⁿ 点をちょうど 1 回ずつ・隣は距離 1 4 種すべて成立 置換であること
局所性 k=32 の平均距離 ヒルベルト 6.38 / 走査線 16.07 √32 = 5.66 / 32

★ここがこの回の要点です。 測地ドームの次数は別実装の op(conngraph.graph_degree_table)に数えさせています。自分で数え直して自分と一致しても、何も確かめたことになりません。

デカルトの円定理も同じ構えです。生成は反射 k' = 2(k₁+k₂+k₃) − k₄ で行うので、接触を距離から探し直して定理に入れます —— そうすると中心の計算が正しいかまで一度に効きます。

そして整数充填。種 (−1, 2, 2, 3) から始めた充填はどこまで行っても曲率が整数です。実装が少しずれれば整数から外れます —— 絵では絶対に見えない種類の誤りがここで捕まります。

実装で踏む穴

検査そのものの欠陥を 2 件見つけました。

  1. pytest.raises(match="similarit") は op 名 ifs_similarity_dimension に当たって必ず通ります。 しかもプリセット名を間違えていて("fern"、正しくは "barnsley_fern")、「未知プリセットの拒否」を見て合格していました。「拒否した」だけを見て「なぜ拒否したか」を見ないとこうなります。
  2. 型を points で登録して型契約の門に捕まりました。points は (N,3) の 3 次元点で、2 次元の点列は pairs です。名前が似ているので取り違えやすい。

そして図が数値検査の盲点を暴きました。 測地ドームの図を三角形 1 枚ずつ焼いていたため、呼び出しごとに自分の外接矩形へ正規化され「各三角形が画面いっぱいに伸びた線の山」になっていました。検査は 20 件とも通っていたので、図を開くまで気づけません。

向かないこと

アポロニウスの充填は depth に対し円が 3 のべきで増えます(depth 10 で 118,100 個)。フォード円は分母の二乗で半径が縮むので、分母 30 を超えると多くが 1 画素を割ります。IFS のカオスゲームは点を打つので、細かい隙間は点数を増やさないと埋まりません(箱数えの次元が閉形式より上に出るのはそのため)。モランの式は相似写像にしか使えません —— アフィンだが相似でないバーンズリーのシダは拒否します。

最初の 1 本

import fullseye as fs

t = fs.ledger.circle_packing_apollonian(curvatures=(-1, 2, 2, 3), depth=4)
print(len(t["x"]), "円 / 曲率は整数のまま:",
      abs(t["curvature"] - t["curvature"].round()).max())

pts  = fs.ledger.phyllotaxis_pattern(n_points=800)
gaps = fs.ledger.neighbour_index_gaps(pts, k=6)
print("番号差の山:", gaps.argsort()[::-1][:5])      # 黄金角なら 8, 13, 21, ...

この回が作った図は全部で 11 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 10 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

apollonian counts

↑ 合計は 2·3^depth + 2。

ford

↑ フォードの円(分母 12 まで、47 円)。

dome

↑ 分割 f を 1, 2, 3, 4, 6 と上げても、次数 5 の頂点はちょうど 12 個から動かない(次数 6 だけが増える)。

dome degrees

↑ オイラーの公式 V − E + F = 2 の帰結。

phyllotaxis

↑ 3 枚とも螺旋に見える。

parastichy

↑ 黄金角の山は [34, 55, 89, 21, 13, 8, 144] —— すべてフィボナッチ数。

ifs

↑ モランの式 Σrᵢᵈ = 1 は写像の縮小率だけから d を出す。

curves

↑ order 5 で 4^5 = 1,024 点をちょうど 1 回ずつ通り、隣り合う点は必ず距離 1。

locality

↑ ヒルベルトは √k の近くを通り(k=32 で 6.38)、走査線はほぼ k に比例する(16.07)。

numbers

↑ どの行も「使った式」ではない真値で採点している: デカルトの円定理、整数充填、|ps − qr| = 1、オイラーの関数の和、オイラーの公式(既存 graph_degree_table が数えた)、フィボナッチ、モランの式と既存 fractal_dimension、置換であること。

第 4 回: うなりは一つ ―― 干渉縞と印刷のモアレは同じ数学

手順

膜の固有モード / 二重スリット / 回折格子      ┐
                                          ├→ どちらも「2 つの周波数ベクトルの差」
網点 2 版の重ね / 彫版線 / ハッチ / モザイク  ┘

二重スリットの縞と、2 版を重ねた網点のモアレは教科書では別の章に載っています。でも式は 1 本しかありません。 この回はその 1 本を両側から確かめます。

膜の固有モードと節線

↑ モードの形は符号つき([-1, 1])。節線の両側で位相が反転するので、image2d を名乗ると腹の位相が消える。

門と成績

主張 測った量 実測 真値の出どころ
矩形膜の固有値 π²(m²+n²) との最大差 0.00e+00 閉形式(縮退の重複まで)
節線の本数 (m,n)=(3,4) の縦 / 横 2 / 3 m−1 / n−1(整数)
円膜の節円 半径 一致 J₀ の零点 / k
二重スリット 縞間隔の実測 / λD/d 0.998〜1.003(4 設定) 作る op と別の op が測り返す
回折格子 既存 op で逆算した λ 550.000000 nm grating_wavelengths との往復
偶数次が消える 見えた次数 −3, −1, 0, 1, 3 矩形波のフーリエ係数が偶数調波で 0
3 次 / 1 次の強度比 比 0.1140 sinc(m/2)² → 1/9
モアレの周期 実測 / 描く前の予言 0.948〜0.986 閉形式 1/(2 f sin(Δ/2))
網点の濃度保存 4 周期の窓で平均した差 0.0517 捨てたのは階調だけ
彫版線 インク率と w/d の差 0.0000(4 段) 閉形式
ハッチの向き 縞 30° に対するずれ 0.3° 既存 structure_tensor_orientation
Lloyd エネルギー 反復 0 → 16 2,782,566 → 1,491,442 既存 stipple_energy が測る(単調減少)
セル平均 平均と最小点が食い違うセル 0 個(全数走査) L2 最適

気をつけること(2 つとも一度読み違えました)

うなりは低周波側にあります。 FFT の探す範囲を切らずに argmax を取ると、どの角度でも 16.7 px = スクリーンの周期 1/f そのものが返り、「op の予言と全然合わない」と読めます。

ハッチの線は高周波側にあります。 切らずに取ると、線ではなく「どこに墨を置いたか」の濃淡の包絡(元の絵と同じ 24 px)を拾い、向きが 90° ずれて見えます。

どちらも op は正しく、欠陥は測り方にありました。スペクトルには必ず複数の山があり、argmax は「一番強い山」であって「探している山」ではありません。しかも返ってくるのはもっともらしい数なので、例外も NaN も出ずに静かに別の量を答えます。

クラドニ図形についての訂正

よく見る cos·cos − cos·cos の式は、板ではなく膜の解です。実際のクラドニ板は重調和方程式に従う別物で、砂が節線に集まる絵は同じでも周波数比は合いません。ここでは膜と明記し、膜の真値(閉形式の固有値・ベッセル零点)だけで採点しています。

もう 1 つ。円膜の節円は J₀ の零点にあります。J′₀ の零点は腹の位置です —— 取り違えると「op が間違っている」と読み違えます(一度やりました)。

向かないこと

wave_two_slit はフラウンホーファー領域の遠視野で、近接場やフレネル領域は別の op の仕事です。膜のモードは矩形と円の解析解だけで、任意形状は解きません。wave_fringe_period は1 本の支配的な周期を返すので、複数の周期が重なった像では最大の山しか見ません。モアレの予言は同じ形のスクリーン 2 枚の場合で、3 版以上や点形状の違いによる 2 次のうなりは入りません。hatch_field は画素ごとに 1 つの向きしか持てないので、交差や分岐では向きが任意になります —— そこを信じてよいかを言うのは向きではなくコヒーレンスです。

最初の 1 本

import fullseye as fs

# 刷る前にモアレの周期が分かる
p = fs.ledger.halftone_moire_period(lpi_a=60.0, angle_a_deg=45.0,
                                    lpi_b=60.0, angle_b_deg=75.0, pixel_um=25.4)
print("モアレの周期 %.2f px / 向き %.1f 度" % (p["period_px"][0], p["angle_deg"][0]))

# 縞間隔は、作った op とは別の op が測り返す
img = fs.wave_two_slit(wavelength_nm=550.0, slit_sep_um=200.0,
                       distance_mm=200.0, shape=(64, 1024), pixel_um=5.0)
print("実測 %.2f px / lambda*D/d = %.2f px"
      % (fs.wave_fringe_period(img), 0.55 * 200e3 / (200.0 * 5.0)))

この回が作った図は全部で 13 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 12 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

nodal

↑ (m, n) モードの節線は縦 m−1 本・横 n−1 本。

fringes

↑ 縞間隔は λD/d。

fringe scaling

↑ 重なっていることが主張。

grating

↑ ★デューティ 50 % の矩形格子は偶数次が消える(矩形波のフーリエ係数が偶数調波で 0)。

moire

↑ 新聞の 15° / 45° / 75° は差が 30° で、うなりが最も細かくなる組み合わせ。

moire scaling

↑ ★FFT の探す範囲を切らないとスクリーン自身の山(どの角度でも 16.7 px = 1/f)を拾って「予言と全然合わない」と読める。

halftone tone

↑ 平均絶対差 0.0517。

engrave

↑ 被覆率は線幅 / 間隔 = w/d。

hatch

↑ 向きは既存 structure_tensor_orientation が決める(この族は構造テンソルを再実装しない)。

mosaic

↑ Lloyd 反復でセルが等エネルギーに近づく。

lloyd

↑ 下がり続けることが主張。

numbers

↑ どの行も「使った式」ではない真値で採点している: 膜の閉形式とベッセルの零点、λD/d を測り返す別の op、既存 grating_wavelengths との往復、描く前のモアレ予言、w/d の閉形式、既存 structure_tensor_orientation、既存 stipple_energy。

第 5 回: 絵では確かめられないもの ―― 力学系と極小曲面

手順

名前で呼んだベクトル場 → 積む → 断面・指数・分岐・次元
極小曲面の厳密なパラメータ表示 → メッシュ → 平均曲率を別の op に測らせる

ローレンツ・アトラクタの図は、積分器が 1 次でも 4 次でも蝶に見えます。 極小曲面の図は、平均曲率が 0 でなくてもきれいな曲面に見えます。この回は「見て分かる」が一切効きません。

ローレンツの軌道と分岐図

門と成績

主張 測った量 実測 真値の出どころ
RK4 の次数 刻み半分での誤差の比 16.0 / 16.0 / 16.0 expm(At)x₀ が厳密解
オイラーの次数(対照群) 同じ比 2.08 / 2.04 / 2.02 1 次
同じ刻みでの誤差 dt = 0.01 2.02e-02 対 3.33e-10 桁が違う
トレース恒等式 Σλ − (−(σ+1+β)) 2.57e-06 厳密(出し方と独立)
最大リアプノフ指数 λ₁ 0.9142 公表値 0.906
保存系(対照群) 調和振動子の Σλ 4.87e-15 0
1 本目の分岐点 r 2.999401 3(厳密)
2 本目の分岐点 r 3.449260 1+√6 = 3.449490
ファイゲンバウム δ (r₂−r₁)/(r₃−r₂) 4.7485 4.6692
場の発散 既存 piv_divergence vs tr(A) 4 通りすべて 1e-6 未満 線形系で厳密
場の渦度 既存 piv_vorticity vs A₁₀−A₀₁ 一致 線形系で厳密
管メッシュ 体積 / 2π²Rr² 0.99667 トーラスの解析解
管メッシュ 面積 / 4π²Rr 0.99928 同
極小曲面 |H| 中央値(4 種) 0.00002〜0.00014 0(定義そのもの)
対照群 単位球 / 円柱の |H| 1.00004 / 0.50000 1.0 / 0.5(極小ではない)
等長な曲げ 面積の振れ幅 17.6738〜17.6756 変わらない
ジャイロイド 体積比(48³/64³/96³) 0.499928 / 0.499981 / 0.499991 0.5(体心反転の対称性)

★採点者が別の族から来ます。 ode_vector_field_grid は flow2d を返すので、PIV(粒子画像流速計)族の既存 op —— piv_divergence / piv_vorticity —— がそのまま食います。あちらはこの族の実装を何も知らないので、「場が正しい」という主張の独立した採点者になります。線形系では発散が厳密に tr(A)、渦度が A₁₀ − A₀₁ です。

極小曲面は定義そのものが門になります。 「平均曲率 H が至るところ 0」という定義を、既存 vertex_curvature がそのまま測ります —— つまり「これは極小曲面だ」という主張を、作り方を知らない op が採点します。 対照群の単位球が 1.00004、半径 1 の円柱が 0.50000 に出るので、門が素通しでないことも同時に分かります。

残差を隠さない

2 か所とも、理由を書く前に正体を実測で特定しました。

(1) 分岐点は必ず少し手前に見えます。 分岐点での収束は幾何的でなく代数的なので、有限の burn-in では届きません(δ も 4.7485 対 4.6692 と 1.7 % 偏る)。だから門は「誤差が小さい」ではなく 「burn-in を伸ばすと置いていった分だけ縮む」 で置きました —— 2,000 → 20,000 で誤差が 10.9 倍縮みます。残差の正体が写像でなく測り方であることの証拠は、そちらです。

(2) 相関次元の偏りは「点数不足」ではありませんでした。 円 1.0061・カントール 0.6408(真値 log2/log3 = 0.6309)は合うのに、平面は 1.8789(真値 2)。最初に疑った点数は外れでした —— 400 → 1,500 → 3,000 点で 1.8825 / 1.8789 / 1.8709 と、むしろ下がります。

正体はべき乗則を見る半径の窓です。既定は対距離の 1〜25 パーセンタイルで、有界な集合ではその上端が境界に当たり、相関和が飽和して傾きが寝ます。窓を小さい r 側に寄せると 1.887 → 1.947 → 2.050 と真値に寄ります。

「有限標本だから」「数値誤差だから」は何にでも当てはまるので、書いた瞬間に調べるのをやめてしまいます。 当てはまる説明と正しい説明は別物で、正しい説明だけが「どのノブを回せば直るか」を教えます。この件では r_lo / r_hi であって max_points ではありませんでした。

ジャイロイドも同じ構えです。level = 0 で場は体心反転に対し奇なので体積比は対称性からちょうど 1/2(格子に依らない)。ですが Schoen のジャイロイドは H = 0 で、節面近似には残差が残ります(|H| / 主曲率スケール = 0.1250)。足場と絵として使うもので、極小性の主張ではありません。

実装で踏む穴

引数で戻り値の型が変わる関数は、型付きの台帳に載せられません。 gyroid_isosurface(..., thickness=...) は thickness を渡すと voxel、渡さないと mesh を返す設計でしたが、宣言できる出力型は 1 つなのでどちらを書いても嘘になります。2 つの op に分けました。

分けた副作用が利得になりました —— 固体形が scikit-image の依存から外れ、numpy だけの環境でも使えるようになったのです。

もう 1 つ。ode_vector_field_grid を最初 (H, W, 2) で返していて型検査に捕まりました。そちらのほうが自然に読めますが、flow2d は既存の型で既存の述語と既存の消費側があるので、転置を返すのは型の嘘です。読みやすさより既存の語彙に合わせた結果、下流の既存 op がそのまま繋がりました。

向かないこと

系は族名か係数配列で受け、callable は取りません —— 型付き台帳は入力を型で登録するので、関数は載らないのです。任意の常微分方程式を渡す口ではありません。リアプノフ指数は接流の解ける系だけで、遅延埋め込みからの推定は入りません。分岐図は 1 次元写像だけです。相関次元は答えが要るときは半径の窓を明示してください(上のとおり既定の窓は有界集合で低く出ます)。極小曲面は閉形式の 4 族 + ジャイロイドで、任意の境界に張るプラトー問題は解きません。管メッシュは自己交差を検出しません(曲率半径より太い管は自分を貫きます)。

最初の 1 本

import numpy as np
import fullseye as fs

# 積分器の次数を実証する(厳密解は expm(At)x0)
A, x0, T = np.array([0.0, 1.0, -1.0, 0.0]), np.array([1.0, 0.0]), 4.0
exact = np.array([np.cos(T), -np.sin(T)])
for dt in (0.04, 0.02, 0.01):
    st = fs.ode_flow_states("linear", A, x0, T, dt, "rk4")
    end = np.array([st["x0"][-1], st["x1"][-1]])
    print("dt = %.3f  誤差 %.3e" % (dt, np.linalg.norm(end - exact)))   # 半分で 1/16

# 「これは極小曲面だ」を、作り方を知らない op が採点する
V, F = fs.ledger.minimal_surface("catenoid", nu=90, nv=140, extent=1.2)
H = np.abs(np.asarray(fs.ledger.vertex_curvature((V, F))))
print("|H| 中央値 %.5f(対照群の単位球は 1.0)" % np.median(H[np.isfinite(H)]))

この回が作った図は全部で 11 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 10 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

rk4

↑ 厳密解は expm(At)x₀ なので、誤差は積分器の次数をそのまま出す。

lorenz

↑ ローレンツ・アトラクタの xz 断面。

lyapunov

↑ Σλ = -13.66666、閉形式 −(σ+1+β) = -13.66667(差 2.6e-06)。

dimension

↑ 円 1・平面 2・カントール log2/log3 = 0.6309。

field

↑ ode_vector_field_grid は flow2d((2, H, W) の (dy, dx))を返すので、piv_divergence / piv_vorticity / piv_flow_magnitude がそのまま使える。

poincare

↑ 同じ op で両方を測っている。

tube

↑ ローレンツの軌道を管メッシュに(MATLAB の tubeplot 相当、12510 頂点 / 25000 面)。

minimal

↑ 「これは極小曲面だ」という主張を、作り方を知らない op が採点する。

gyroid

↑ level = 0 で場は体心反転に対し奇なので、両側の体積はちょうど半分ずつ(実測 0.499928 / 0.499981 / 0.499991、格子に依らない)。

numbers

↑ どの行も絵からは読めない: expm との誤差の比、トレース恒等式、厳密な分岐点、既知の 3 つの次元、PIV 族の既存 op が測った tr(A)、トーラスの解析解、既存 vertex_curvature が測った H —— そして節面近似の残差は隠さずここに出してある。

第 6 回: 目が嘘をつく絵を作って、測る側を採点する

手順

錯視図を作る → その図が「否定している不変量」を数で返す → 既存の op に測らせる
無限に描き続ける系を回す → 絵ではなく恒等式で採点する
時間依存の量をすべて θ の関数にする → 継ぎ目が最初から存在しない動画になる

ここまでの 5 回は「描いた絵を、絵の外から採点する」話でした。第 6 回は向きが変わります —— 採点される側の絵を、こちらから作ります。

錯視を置くつもりでした。カフェウォールの目地は厳密な水平線で、ミュラー・リヤーの 2 本は厳密に等長で、チェッカーシャドウの 2 マスは浮動小数として同じ値 —— それを op に測らせれば「測る側は騙されない」と言える、と考えたからです。

錯視の章は落としました —— 測っても何も言えなかったから

言えませんでした。 検査が 7 件とも自分で置いた定数を読み返すだけだったからです。厳密に水平に引いた目地が水平だ(傾き 0.0e+00)、平坦に塗ったマスが平坦だ(ptp 0.0e+00)、同値に置いた 2 パッチが同値だ。間に変換が 1 つもありません。 錯視は人間の知覚の性質なので、測る側が騙される理由がそもそも無く、「測る側は騙されない」は発見ではなく同義反復です。絵の側も直線と矩形を引いているだけで、この箱の力を何も示していません。

残したのは 1 件だけ、測る側の偏りです。

ミュラー・リヤーの偏り

↑ 2 本の軸は矢羽根を外すと画素単位で同一(差 0)。ところが「行の黒い区間」を素朴に測ると 223 画素と 225 画素になります。しかも矢羽根が無くても 223 画素(真値 220)—— 反エイリアスの裾を「黒い画素」として数えているからで、これは錯視より前の素の欠陥です。

矢羽根の長さを振る

↑ ★ここでも外しました。「矢羽根が長いほど偏る」と読みましたが、20 px で 2 画素に達したあと 80 px まで動きません。偏りを作るのは長さではなく、端に裾が乗るかどうかだけでした。

止めるまで描き続けるものを、どう採点するか

8 ビットの一行プログラムから続く系譜 —— 10 PRINT のランダム迷路、基本セルオートマトン、ラングトンの蟻、カオスゲーム。これらは「いつ止めても途中」なので、絵では採点できません。

そこで、絵とは独立に成り立つ主張を持つものだけを入れました。

規則 90

↑ 規則 90 を中央 1 点から 200 行。第 n 行の第 k セルは二項係数 C(n,k) の偶奇に厳密に一致します —— パスカルの三角形を 2 で割った余り。絵を一切見ずに採点できる、という意味でこの族の代表例です。

規則の並べ比べ

↑ 始まりはどれも中央の 1 セルだけ。変えたのは 8 ビットの規則番号 1 つです。規則 30 は乱数生成に使われた歴史があり、規則 110 は万能計算ができることが証明されています。

ラングトンの蟻

↑ 規則は 2 つだけ(白なら右折して黒く塗る、黒なら左折して白く塗る)。最初の 1 万歩ほどは対称な模様、次に無秩序、そして突然「高速道路」に入り、以後は周期 104 で斜めに進み続けます。

蟻の成長曲線

↑ 黒マスの数を歩数に対して。前半は揺れますが、高速道路に入ると 12/104 = 0.1154 マス/歩の直線になります。絵を見なくても、傾きで「入った」が分かります。

アポロニウス

↑ 隙間に接する円を入れ続けるので終わりがありません。互いに接する 4 円の曲率はデカルトの円定理 (Σk)² = 2Σk² を厳密に満たします(残差 1.2e-16)—— 円の位置まで複素数の曲率中心で解けるので、当てはめではなく代数で描いています。 ★塗りの明るさは曲率(= 1/半径)—— 定理が試している量をそのまま色にしてあるので、絵の濃淡が主張と同じものを指しています。

カオスゲーム

↑ 賽を振って頂点へ半分ずつ寄るだけで、シェルピンスキーの三角形が出ます。明るさは点の密度(双一次で積み、asinh で暗部を伸ばしてあります)。描き方は飾りではありません —— 同じ点列でも、切り捨てて積んだ絵から測った箱数え次元は真値 log3/log2 から −0.032 ずれるのに、こう描くと −0.0030 で 10 倍正確になります。

バーンズリーのシダ

↑ バーンズリーのシダ —— 4 本のアフィン写像を確率で選んで回すだけ。明るさは密度で、正規化は最大値でなく分位点で行っています(根元の 1 点が飛び抜けるので、最大値で割ると他が全部潰れます)。

流れ場

↑ 場はポテンシャル ψ の回転として作ってあるので、発散が恒等的に 0(非圧縮、残差 7.0e-17)。流線が渦のまわりで閉じているのがその絵です —— 「それらしい雑音」で作った流れ場では、ここが湧き出しと吸い込みになります。 ★線の色と太さは流速 |∇ψ| —— 太さも濃さも一定だと、同じ場から描いても落書きにしかなりません。

反応拡散

↑ グレイ–スコット反応拡散。種は散らして 12 個置いてあります ——中央に 1 個だけだと、同じ歩数でも模様の被覆が 0.086 にしかならず、画面の大半が余白になります(散らすと 0.379)。★格子状に並べると格子そのものが絵に出るので、ずらして置きます。

プラズマ

10 PRINT とトルシェ

↑ 左は 10 PRINT CHR$(205.5+RND(1)); : GOTO 10 —— Commodore 64 の一行プログラム。右はトルシェ・タイルで、弧の端点が必ず辺の中点に来るので、どう組んでも曲線が途切れません。

時間軸で循環する画像

継ぎ目の無い循環動画

↑ 最後のコマから最初のコマへ戻るところに、切れ目がありません。

作り方が要点です。「最後に頭へ戻す」のではなく、時間依存の量をすべて θ の関数にして θ を 0→2π 回すと、t = T は t = 0 と同じ式になります —— 継ぎ目は編集で消すものではなく、最初から存在しません。

継ぎ目の比

↑ 継ぎ目の正解は 0 ではなく 1 です。 比 =(最後のまたぎの差)÷(ふつうのコマ間の差)。1 なら「最後のまたぎが、ほかのまたぎと見分けが付かない」。0 だと継ぎ目が無いのではなく、動きが止まっているという意味になります —— ここを取り違えると「完璧な結果」に見えます。

門と成績

主張 測った量 実測 真値の出どころ
カフェウォール 目地 8 本の傾き 0.0e+00 目地は水平線(構成)
ミュラー・リヤー 軸だけの図の 2 行 画素単位で同一 等長(構成)
同(素朴な計測) 矢羽根つきで行の黒い区間 223 vs 225 真値 220 ―― 測り方の欠陥
チェッカーシャドウ 2 マスの画素値の差 0.0e+00 影の係数 = 暗/明(厳密)
カニッツァ 錯覚輪郭上の ptp 0.0e+00 辺は描かれていない
同時対比 2 パッチの差 0.0e+00 同値(構成)
規則 90 第 n 行 vs C(n,k) mod 2 0.0e+00 リュカの定理
ラングトンの蟻 104 歩ごとの変位 0.0e+00 周期 104・斜め (2,2)
同 高速道路の正味増加 ちょうど 12 マス 整数(実測で確定)
アポロニウス (Σk)² − 2Σk² 1.19e-16 デカルトの円定理
同 生成した全円と親 3 円の接触 9.20e-07 接する(再帰で誤差が積もる)
流れ場 div(curl ψ) 6.95e-17 恒等的に 0
反応拡散 餌も死も 0 のときの総量 1.98e-16 保存(拡散は再分配)
プラズマ 4 隅の値 0.0e+00 中点変位は既存値を触らない
循環動画 継ぎ目の比(4 種) 0.997〜1.041 1(0 ではない)

検査が見つけたもの

4 件出ました。どれも絵を見ても気づけない型です。

  1. 二項係数を int64 で積んでいて、C(62,31)×31 で黙って溢れた。 リュカの定理の帰結「C(n,k) が奇 ⟺ (n & k) == k」に置き換えて、桁あふれの余地を消しました。
  2. チェッカーシャドウで対にするマスを間違えていた。 同じ明暗のマスを 2 つ選んでいて、差が 0.31 出ました。対にすべきは「影の外の暗マス」と「影の中の明マス」。絵は"それらしく"見えるので、数で測るまで気づけません。
  3. 「104 歩で 52 マス」と見当で書いた成長率が、実測 0.114/歩 と合わなかった。 測り直すと正味ちょうど 12 マス(整数)。周期の中で塗っては消すので、正味はずっと少ない。推測でなく実測が正しかったという、それだけの話です。
  4. ★アポロニウスで平方根の枝を選び損ねていた。 複素曲率中心には枝が 2 つあり、片方は「親 3 円に接しない円」を返します。主値だけを使っていたため、外円の外へ逃げる円の鎖が出ていました —— しかも絵としては「フラクタルっぽい」ので、画像を開いて見るまで分かりません。接することを数で確かめて枝を選ぶようにし、「外へ逃げた円が無い」を門にしました。

そのほかに、定数の配列でも std() は厳密に 0 にならない(平均を引く途中で 1 ulp 残る、実測 5.6e-17)という小さな発見がありました。「全部同じ値」を厳密に言いたいときは ptp(最大 − 最小)を使います。

速度も 1 件。素朴に全画面の距離場を primitive ごとに作っていたので、錯視 12 枚で 17.7 秒、アポロニウス 1 枚で 4.96 秒かかっていました。外接箱だけを触る方式にして 0.07 秒 / 0.03 秒(250 倍・165 倍)。PoC の所要時間は CI の合否そのものなので、これは配管ではなく門の問題です。

向かないこと

作品としての完成度には向きません。この族には主張が検算できる図しか入れていないので、美しいが検算できない図は、描けても入れていません。逆に言えば、ここにある絵は全部「なぜそれで正しいと言えるのか」に答えられます。

また、錯視の強さは測っていません(「どれくらい傾いて見えるか」は心理物理の話で、この族の守備範囲の外です)。測っているのは「傾いていないこと」だけです。

この回が作った図は全部で 16 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 1 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

numbers

↑ この PoC の主張は全部この表に入っています。

第 7 回: 測定器のほうを健診する ―― キャリパーが外すのはどこで、なぜか

手順

答えの分かっている絵(錯視)を作る → 産業用のサブピクセル測定器に測らせる
→ 真値と突き合わせる → 外れたら「なぜ外れたか」を突き止め、直せるか試す
→ 直らなかったものは、直らなかったと書く

第 6 回で「真値の付いた絵」が作れるようになりました。第 7 回はその絵の使い道です ―― 測られる側ではなく、測る側を採点します。

Fullseye には HALCON 流のサブピクセル測定器が入っています(measure_pos / measure_pairs / fuzzy_measure_pairing / apply_metrology_model)。工場で寸法を測るための道具です。この道具に、目が確実に外すと分かっている図を測らせるとどうなるか。

★この回で新しい op は 1 つも作っていません。作る側と測る側が同じ箱に入っているので、答え合わせが 1 本のスクリプトで閉じます。

まず、測定器は目より正しい

カフェウォールにキャリパーを当てる

↑ 青が与えた参照線、橙が測定器が法線方向に探して見つけたエッジ点 41 個。タイルは傾いて見えたままなのに、点は一直線に乗り(rms = 0.0)、当て直した直線の角度は厳密に 0.0 度。

エビングハウス

↑ 左の円は小さく、右の円は大きく見えます。円の計測オブジェクトが返した半径は 29.996525 px と 29.996525 px ―― 差は 2.5e-12 px。目が主張する差は、画像の側には存在しません。

エビングハウスの半径

↑ 縦軸を 0.2 px 幅まで拡大しても 2 点は重なったまま。どちらも指定の 30 px より 0.0035 px 小さいのですが、そのずれは両方に同じだけ乗るので、差を取ると消えます。

ところが、探針 1 枚では足りなかった

ここが、この回でいちばん学びになったところです。

最初、カフェウォールを 1 通りの設定(ずらし量 0.25)だけで測って、「測定器は錯視では決して動かない」と書きました。きれいな結論です。

そのあと、ずらし量 5 通り × 探索半幅 4 通りの 20 通り全部を測りました。

ずらし量 × 探索半幅の格子

↑ 探索半幅が目地の帯幅 8 px に届く 10 px と 16 px のとき、ずらし量 0.125 と 0.375 でだけ 0.1429 度傾きます。0.25 は、たまたま外れない側でした。

等倍で切り出す

↑ 何が起きているか。全体図では 8 px の飛びが潰れて見えないので、目地の周り 34 行だけを等倍で切り出しました。下の段では、点が帯の反対側にも乗っています ―― 探索範囲が向こう側の境界に届いているのです。

既定に見える 1 点だけで試すと、この欠陥は構造的に見つかりません。

外したことは、答えではなく残差に出る

そして、ここがいちばん実務に効くところです。

答えと残差

↑ 同じ 20 通りについて、答え(角度)と残差(rms)を重ねたもの。

外れたときの角度は 0.1429 度です。数字だけ見れば「ほぼ 0」で、そのまま通ってしまいます。ところが残差は 0.00 から 1.70 へ桁で動きます。しかも「角度 0 ⇔ rms 0」が 20 通り全部で一致しました。

門は答えではなく残差に置く。「当てた形が本当にそこにあったか」は、答えの側からは見えません。

測定器も外す。ただし目とは別の理由で

ミュラー・リヤーにキャリパーを当てる

↑ 上(矢羽根なし)は測定線上のエッジが 2 本、下(矢羽根あり)は 4 本。

矢羽根は「長く見せる」だけではありません。測定線の上に余計なエッジを置きます。測定器は隣り合う逆極性のエッジを対にするので、軸ではなく矢羽根のストロークを測ってしまいます(幅 5.701 px)。

目は「長く見える」と外し、測定器は「別の物を測る」と外す ―― 外れ方が違います。

4 通りの答え

↑ 同じ 1 本の軸が、測り方で 4 通りの答えになります。どれもバグではありません。

測り方を宣言すると、静かな誤りが見える拒否に変わる

適合度

↑ fuzzy_measure_pairing に「幅は 220 px のはず」と宣言して測らせたもの。矢羽根なしでは適合度 0.9988、矢羽根ありでは 0.0225。

これは「間違った値を返した」のではありません。「想定した幅の構造は見つからない」と数で申告している状態です。隣り合う逆極性の対しか候補にしないので、軸をまたぐ対はそもそも提案されません。

測り方を宣言した分だけ、失敗が静かな誤りから見える拒否に変わります。

3.777 px は錯視ではなく、端の定義だった

矢羽根が無い図でも、答えは 223.777 px で真値 220 と合いません。錯視のせいでしょうか。

端の定義

↑ 軸の長さを 140〜300 px に振ると、ずれは +3.777 px 一定(振れ幅 0.0000 px)。

比例していれば倍率の誤り、一定なら端の定義のずれです。軸は太さを持って描かれているので、「外側エッジ間の距離」は「線分の長さ」より線幅ぶん長い。引けば真値に戻ります。

動くのは答えではなく、証拠の数のほうだった

ツェルナー

↑ 収束して見える 7 本すべてが、測ると水平(最大 |傾き| 4.3e-04 度 = 幅 520 px を渡って 0.0039 px)。

傾きと点の数

↑ 傾きは 7 本とも 0 に重なって見分けが付きません。動いたのは採用されたエッジ点の数で、ハッチの向きが 1 本おきに反転するため 61 点中 13 と 19 を交互に取ります。

「測れた」と「よく測れた」は別の量です。前者だけ見ていると、この差は見えません。

直らなかったものは、直らなかったと書く

ポッゲンドルフ

↑ 帯の左右の 2 本は厳密に 1 本の直線です(帯で隠れているだけ)。橙が素朴なキャリパーの点、青が両エッジを対にして中心を取り直した線。

ここでは測定器が外します。共線のはずの 2 区間で、角度が 0.450 度違いました。

原因は突き止められました。極性が塊で交互しているのです(41 点が 9 個の塊、+18 / −23)。細い線には上側と下側の 2 つのエッジがあり、「線の位置」はどちらか(あるいは中心か)を言うまで定義されません。測定器はその 2 つを行き来していました。

残差

*↑ 両エッジを対にして中心を取ると、角度差は 0.450 → 0.115 度、残差は 2.07 → 0.10/0.39 まで下がります。**それでも 0 ではありません。*帯の縁の細い縦線と画像の端が測定線に掛かっており、そこまでは追い込んでいない ―― というのが正直な状態です。

絵を作り直したら、測れる真値が変わった

濃淡と色を付け直しただけで、絵から測った数が真値に近づきました。同じ点列・
同じ op・同じしきい値で、箱数え次元のずれが −0.032 → −0.0030(10 倍)。

原因は積み方でした。前の実装は .astype(np.int64) で切り捨てて整数画素に
積んでいた —— 四捨五入ですらないので、絵全体がちょうど半画素ずれます
(実測の重心差 −0.5009 / −0.4998 画素)。総量は 39.9 万点で相対差
0.00e+00 と合うので、0 次だけ見る検査はこの欠陥に構造的に盲目です。
双一次で積むと重心差は 0.000e+00 —— 1 次元で i(1−f) + (i+1)f = i + f に
戻るからで、2 次元は軸ごとに分かれるので両方とも厳密です。

トーンは asinh にしました。狭義単調なので画素の大小は 1 組も入れ替わりません
(4,998 組で検査)。ただし正規化を最大値でやると、バーンズリーのシダは根元の
1 点が飛び抜けるので他が全部潰れて真っ暗になりました。分位点に替えて
std 0.052 → 0.0996。反応拡散は種が中央に 1 個だけで、模様の被覆が
0.086 しかありませんでした —— 散らして 12 個置くと同じ計算量で 0.379。
★格子状に並べると格子そのものが絵に出るので、ずらして置きます。

★外したこと: アポロニウスの色が大きい円で潰れていたので「順位で
正規化すれば階調が配り直される」と読みました。逆効果でした ——
極小円が多数派なので、大きい円が全部下端に潰れて前より悪くなりました。
対数に戻してガンマ 0.45 で低域を持ち上げるのが正解でした。
「分布を均す」は、見せたい対象が少数派のときは逆に働きます。

正反対の主張の図が、同じ絵だった

超立方体を 2 枚の面で同時に回す章で、「比 1 : 2 は 60 コマで閉じる」と
「比 1 : φ は閉じない」の GIF が バイト単位で同一でした。

原因は投影です。図は落とした 3 次元の x, y の 2 成分だけを描いていました。
rot4(0, t1, 1, t2) の t2 は zw 面の回転なので、x と y に一切効きません
—— 60 コマ通して x,y の差は厳密に 0.000e+00、x,y,z なら 2.494。
章の主題が、その投影では原理的に見えなかったわけです。3 次元を一般の向きから
見る 2×3 の正射影(正規直交性 1.11e-16)に替えて差 1.322 になりました。

★この嘘が通ったのは、「2 枚の図が同じ中身か」を見る門がどこにも無かった
から。枚数も名前も説明も存在も検査していたのに、そこだけ穴でした。書いた図の
sha256 を記録して、同一バイト列が 2 枚出たら欠陥にする門を入れました。
全 151 本を走査して該当は 2 組だけ(もう 1 組は図の枚数が変わって位置番号が
ずれた置き去りファイル)でした。

錯視の章は、測っても何も言えなかった

第 6 回で落とした話です —— 詳しくはその節に書き直してあります。要点だけ: 検査 7 件が自分で置いた定数を読み返すだけで、間に変換が 1 つもありませんでした。残したのは測る側の偏り 1 件だけです。

作った図は 1,155 枚、記事に出ていたのは 317 枚だった

数え直して分かりました。展示館が参照していたのは 27 % で、848 枚が
記事から辿れない
状態でした。1 展示あたり 2.1 枚(150 件のうち 139 件が
ちょうど 2 枚
)で、PoC は中央値 6 枚・最大 25 枚作っているのに、
残りへの道が記事に無かった。

出す枚数を増やして 318 → 767 枚にし、それでも出ない分のために、
展示ごとに「この回が作った図は全部で N 枚 —— 全部見る」を置きました
(案内 1,155 / 1,155 枚)。追加分の説明は最初の 1 文に切ってあります ——
枚数を増やすのが目的で、詳しい説明は主図と 2 枚目が持っているからです。

健診結果

健診表

↑ この表に「絵を見て判断した」行は 1 つもありません。真値は生成器が保証し、答えは既存の測定 op が返したものです。

真値をそのまま回復したのは 3 行、外れたのは 5 行。外れた内訳は 3 通りでした ―― 決めれば戻るもの(探索半幅・線幅の下駄)、拒否に変えられるもの(想定幅の宣言)、そして 戻らなかったもの(ポッゲンドルフ)。

検査が見つけたもの

この回で自分の書いたものが 2 か所、データと食い違っていました。どちらも「生成された図説を読み返して」見つけたものです。

  1. カフェウォールの掃引の図説に「半幅を変えると採用点の数が変わる」と書きましたが、4 通りとも 41 点でした。証拠の数が実際に動くのはツェルナーのほうです。
  2. 健診表の図説に「外した行は 2 行・決めれば戻る」と書きましたが、実際は 5 行で、ポッゲンドルフは戻っていません。

図説は、書いた時点ではなく、生成されたものを読んで確かめる。 数字を % で埋め込んで自動生成していても、その周りの文章は手で書いているので、同じようにずれます。

向かないこと

この PoC は「測定器は錯視に強い/弱い」を言うものではありません。測り方を決めていない分だけ答えが割れることを、真値つきの図で数として見せるものです。工場で使う測定線の張り方(探索半幅・想定幅・エッジの極性)を決めるときの、机上の確認には使えます。実写のノイズ・照明むら・被写界深度は入っていません。


この回が作った図は全部で 16 枚あります —— 全部見る

この回の残りの図

以下は同じ実行が作った残り 1 枚です。説明は 1 文目だけに切ってあります —— 詳しい読み方は上の主図が持っています。

cafe wall sweep

↑ measure_length は「参照線の法線方向に何 px 探すか」。

出典

  • 画像: 葛飾北斎「神奈川沖浪裏」(富嶽三十六景、1830–32 年頃)。メトロポリタン美術館 Open Access が isPublicDomain: true(CC0)で公開している版(object 45434 / 画像 DP130155)を輝度化して縮小。
  • 手法: Craig S. Kaplan and Robert Bosch, "TSP Art", Computational Aesthetics (2005)。
  • 点密度の指数: A. Gersho, "Asymptotically optimal block quantization", IEEE Trans. Inform. Theory 25(4), 1979。
  • ニュートン吸引域: A. Cayley, "The Newton–Fourier imaginary problem", Amer. J. Math. 2, 1879。
  • 円の充填: R. Descartes (1643) と F. Soddy, "The kiss precise", Nature 137, 1936; J. Lagarias, C. Mallows, A. Wilks, "Beyond the Descartes circle theorem", Amer. Math. Monthly 109, 2002。
  • フォード円: L. R. Ford, "Fractions", Amer. Math. Monthly 45, 1938。
  • 葉序: H. Vogel, "A better way to construct the sunflower head", Math. Biosci. 44, 1979。
  • 相似次元: P. A. P. Moran, "Additive functions of intervals and Hausdorff measure", Proc. Camb. Phil. Soc. 42, 1946。
  • 膜の固有値: Lord Rayleigh, The Theory of Sound, 1877(クラドニの砂の図は板であって、ここで解くのは膜)。
  • 二重スリット: T. Young, 1804。
  • モアレのベクトル解析: B. Oztan, G. Sharma, R. P. Loce, "Misregistration sensitivity in clustered-dot color halftones", J. Electronic Imaging 17, 2008。
  • 点描と量子化: A. Secord, "Weighted Voronoi stippling", NPAR 2002; S. Lloyd, "Least squares quantization in PCM", IEEE Trans. Inf. Theory 28, 1982。
  • 力学系: E. N. Lorenz, "Deterministic nonperiodic flow", J. Atmos. Sci. 20, 1963; O. E. Rössler, "An equation for continuous chaos", Phys. Lett. A 57, 1976; M. J. Feigenbaum, J. Stat. Phys. 19, 1978; P. Grassberger and I. Procaccia, Phys. Rev. Lett. 50, 1983; G. Benettin et al., Meccanica 15, 1980。
  • 極小曲面: H. A. Schwarz, Gesammelte Mathematische Abhandlungen, 1890; A. H. Schoen, "Infinite periodic minimal surfaces without self-intersections", NASA TN D-5541, 1970。
  • 管の枠: R. L. Bishop, "There is more than one way to frame a curve", Amer. Math. Monthly 82, 1975。
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?