├── .gitignore
├── .jupyter
└── desktop-workspaces
│ └── default-37a8.jupyterlab-workspace
├── Chebdiff.ipynb
├── Chebint.ipynb
├── Chebminmax.ipynb
├── Chebroot.ipynb
├── Chebyshev_series.ipynb
├── Clenshaw.ipynb
├── DirectRounding.ipynb
├── Discrete_convolution.ipynb
├── FourierChebyshev.jl
├── Fourier_series.ipynb
├── Fourier_spectral_PO.ipynb
├── Interval-arithmetic.ipynb
├── IntervalFunctions.jl
├── Jacobi_polynomial.ipynb
├── Newton-Kantorovich.ipynb
├── Parallel_test.ipynb
├── README.md
├── floating-point.ipynb
├── interval_dot-mul.ipynb
├── nonrigorous_numerics.ipynb
├── plot_using_bc.ipynb
├── rounding-error.ipynb
├── verifyPO.ipynb
├── verifyalleig.ipynb
├── verifyeig.ipynb
├── verifyfft.ipynb
├── verifylss.ipynb
└── verifynlss.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 | /.ipy*
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/.jupyter/desktop-workspaces/default-37a8.jupyterlab-workspace:
--------------------------------------------------------------------------------
1 | {"data":{"layout-restorer:data":{"main":{"dock":{"type":"tab-area","currentIndex":0,"widgets":["notebook:nonrigorous_numerics.ipynb"]},"current":"notebook:nonrigorous_numerics.ipynb"},"down":{"size":0,"widgets":[]},"left":{"collapsed":true,"visible":true,"widgets":["filebrowser","running-sessions","@jupyterlab/toc:plugin","extensionmanager.main-view"]},"right":{"collapsed":true,"visible":true,"widgets":["jp-property-inspector","debugger-sidebar"]},"relativeSizes":[0,1,0],"top":{"simpleVisibility":true}},"notebook:nonrigorous_numerics.ipynb":{"data":{"path":"nonrigorous_numerics.ipynb","factory":"Notebook"}}},"metadata":{"id":"default"}}
--------------------------------------------------------------------------------
/DirectRounding.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "e4dbde92-15db-4a49-97f6-c1f12ad8dc34",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Juliaで方向付き丸め\n",
11 | "\n",
12 | "## 機械区間演算\n",
13 | "\n",
14 | "区間演算をコンピュータで実現するには$\\mathbb{R}$の代わりに$\\mathbb{F}$を使った区間が必要.そのような区間全体を\n",
15 | "\n",
16 | "$$\n",
17 | "\t\\mathbb{IF}:=\\{\\boldsymbol{x}\\in\\mathbb{IR}: \\underline{x},~\\overline{x}\\in\\mathbb{F}\\}\n",
18 | "$$\n",
19 | "\n",
20 | "と定義する.IEEE754規格に準拠したシステム上では演算後の丸めの向きを制御することができる.\n",
21 | "演算結果が浮動小数点数でない場合,丸めの向きを制御して計算する.\n",
22 | "いま$a,b\\in\\mathbb{F}$に対して,$\\circ\\in\\{+,-,\\times,\\div\\}$として\n",
23 | "\n",
24 | "\\begin{align*}\n",
25 | "\t\\mathtt{fl}_{\\bigtriangledown}\\!\\left(a\\circ b\\right)&:=\\max\\{x\\in\\mathbb{F}:x\\le a\\circ b\\}\\mbox{(下向き丸め)}\\\\\n",
26 | "\t\\mathtt{fl}_{\\bigtriangleup}\\!\\left(a\\circ b\\right)&:=\\min\\{x\\in\\mathbb{F}:x\\ge a\\circ b\\}\\mbox{(上向き丸め)}\n",
27 | "\\end{align*}\n",
28 | "\n",
29 | "とすると\n",
30 | "\n",
31 | "$$\n",
32 | "\t\\mathtt{fl}_{\\bigtriangledown}\\!\\left(a\\circ b\\right)\\le a\\circ b\\le\\mathtt{fl}_{\\bigtriangleup}\\!\\left(a\\circ b\\right)\n",
33 | "$$\n",
34 | "\n",
35 | "が成立する.\n",
36 | "\n",
37 | "$\\boldsymbol{X}=[a,b]$, $\\boldsymbol{Y}=[c,d]$ ($a,b,c,d\\in\\mathbb{F}$)に対して,機械区間演算は次のように実現できる.\n",
38 | "\n",
39 | "\\begin{align*}\n",
40 | "\t\\boldsymbol{X}+\\boldsymbol{Y}&=[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(a+c\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(b+d\\right)]\\\\\n",
41 | "\t\\boldsymbol{X}-\\boldsymbol{Y}&=[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(a-d\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(b-c\\right)]\\\\\n",
42 | "\t\\boldsymbol{X}\\times\\boldsymbol{Y}&=[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(\\min\\{ac,ad,bc,bd\\}\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(\\max\\{ac,ad,bc,bd\\}\\right)]\\\\\n",
43 | "\t\\boldsymbol{X}\\div\\boldsymbol{Y}&=[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(\\min\\{a/c,a/d,b/c,b/d\\}\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(\\max\\{a/c,a/d,b/c,b/d\\}\\right)]\n",
44 | "\\end{align*}\n",
45 | "\n",
46 | "\n",
47 | "|$\\boldsymbol{X}\\times\\boldsymbol{Y}$|$c>0$|$0\\in\\boldsymbol{Y}$|$d<0$|\n",
48 | "|:-------------:|:-------------:|:-------------:|:-------------:|\n",
49 | "|$a>0$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(ac\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(bd\\right)]$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(bc\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(bd\\right)]$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(bc\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(ad\\right)]$|\n",
50 | "|$0\\in\\boldsymbol{X}$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(ad\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(bd\\right)]$|$B$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(bc\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(ac\\right)]$|\n",
51 | "|$b<0$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(ad\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(bc\\right)]$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(ad\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(ad\\right)]$|$[\\mathtt{fl}_{\\bigtriangledown}\\!\\left(bd\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(ac\\right)]$|\n",
52 | "\n",
53 | "ただし $B=[\\min\\{\\mathtt{fl}_{\\bigtriangledown}\\!\\left(ad\\right),\\mathtt{fl}_{\\bigtriangledown}\\!\\left(bc\\right)\\},\\max\\{\\mathtt{fl}_{\\bigtriangleup}\\!\\left(ad\\right),\\mathtt{fl}_{\\bigtriangleup}\\!\\left(bc\\right)\\}].$\n",
54 | "\n",
55 | "## ベクトル・行列の区間演算\n",
56 | "\n",
57 | "上で述べた丸めの向きを制御することにより,ベクトル $x,y\\in\\mathbb{F}^n$ の内積 $x^Ty$,行列 $A, B\\in\\mathbb{F}^n$ の積,あるいは,ベクトル行列積 $Ax$ の結果を区間で厳密に包含することができる.\n",
58 | "\n",
59 | "$$\n",
60 | "\t\\mathtt{fl}_{\\bigtriangledown}\\!\\left(x^Ty\\right)\\le x^Ty\\le\\mathtt{fl}_{\\bigtriangleup}\\!\\left(x^Ty\\right)\n",
61 | "$$\n",
62 | "\n",
63 | "$$\n",
64 | "\t\\mathtt{fl}_{\\bigtriangledown}\\!\\left(Ax\\right)\\le Ax\\le\\mathtt{fl}_{\\bigtriangleup}\\!\\left(Ax\\right)\n",
65 | "$$\n",
66 | "\n",
67 | "$$\n",
68 | "\t\\mathtt{fl}_{\\bigtriangledown}\\!\\left(AB\\right)\\le AB\\le\\mathtt{fl}_{\\bigtriangleup}\\!\\left(AB\\right)\n",
69 | "$$\n",
70 | "\n",
71 | "このようにすると丸め方向の制御で区間演算が容易にできる.しかし,行列ベクトル積,行列積を高速に実装することは職人芸のレベルの難しさである(例えば,キャシュサイズをみて最適なブロック分割などを行う).そのため通常は数値計算ライブラリを利用するのが主流である.\n"
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "id": "2575497c-0bb7-48a1-8805-fca7c911dce8",
77 | "metadata": {},
78 | "source": [
79 | "Juliaの`setrounding_raw`という関数が方向付き丸めを制御できる。\n",
80 | "\n",
81 | "```\n",
82 | "setrounding_raw(::Type{<:Union{Float32,Float64}}, i::Integer) = ccall(:jl_set_fenv_rounding, Int32, (Int32,), i)\n",
83 | "```\n",
84 | "\n",
85 | "中身は単に`ccall`で`fesetround`を呼び出している。"
86 | ]
87 | },
88 | {
89 | "cell_type": "code",
90 | "execution_count": 1,
91 | "id": "8324ae22-e9ad-409f-8f01-0c0660c0b1aa",
92 | "metadata": {},
93 | "outputs": [
94 | {
95 | "name": "stdout",
96 | "output_type": "stream",
97 | "text": [
98 | "Julia Version 1.9.0\n",
99 | "Commit 8e630552924 (2023-05-07 11:25 UTC)\n",
100 | "Platform Info:\n",
101 | " OS: macOS (arm64-apple-darwin22.4.0)\n",
102 | " CPU: 8 × Apple M2\n",
103 | " WORD_SIZE: 64\n",
104 | " LIBM: libopenlibm\n",
105 | " LLVM: libLLVM-14.0.6 (ORCJIT, apple-m1)\n",
106 | " Threads: 2 on 4 virtual cores\n"
107 | ]
108 | }
109 | ],
110 | "source": [
111 | "versioninfo()"
112 | ]
113 | },
114 | {
115 | "cell_type": "code",
116 | "execution_count": 2,
117 | "id": "1bbe5573-ab8e-443e-a916-9364cb7c2ade",
118 | "metadata": {},
119 | "outputs": [
120 | {
121 | "name": "stdout",
122 | "output_type": "stream",
123 | "text": [
124 | "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `~/.julia/environments/v1.9/Project.toml`\n",
125 | " \u001b[90m[d1acc4aa] \u001b[39mIntervalArithmetic v0.20.8\n"
126 | ]
127 | }
128 | ],
129 | "source": [
130 | "using Pkg\n",
131 | "Pkg.status(\"IntervalArithmetic\")"
132 | ]
133 | },
134 | {
135 | "cell_type": "markdown",
136 | "id": "70268a0c-6da4-43a0-b824-55bcf7bc502f",
137 | "metadata": {
138 | "tags": []
139 | },
140 | "source": [
141 | "## 上向き/下向き丸めをJuliaでどう制御する?\n",
142 | "\n",
143 | "Julia v1.0.5からFloat32/Float64で[**丸めの向きを制御できない**](https://github.com/JuliaLang/julia/pull/27166)という致命的な状況になった。\n",
144 | "\n",
145 | "現在は`setrounding(Interval, rounding_type::Symbol)`という関数で、区間演算の方向付き丸めの制御の方法を選ぶことになっている。`rounding_type::Symbol`は\n",
146 | "\n",
147 | "- `:fast`: [FastRounding.jl](https://github.com/JeffreySarnoff/FastRounding.jl)を使った方向付き丸めの実装(無誤差変換を使うエミュレーション)、速いが極端な入力に対して**バグがある**!\n",
148 | "- `:tight`: (デフォルト)柏木先生の「[最近点丸めのみによる方向付き丸めのエミュレート](http://verifiedby.me/kv/rounding/emu.pdf)」の実装、kvの`-DKV_NOHWROUND`オプションの実装に相当\n",
149 | "- `:accurate`: `prevfloat`, `nextfloat` を使った丸めの実装、速いけど区間幅が大きくなる\n",
150 | "- `:slow`: 丸めの向きを変更する実装(BigFloatで53bitに指定して、`setrounding`を使ってる?)、遅い\n",
151 | "- `:none`: 丸めの向き変更なし(精度保証なし)、スピード比較のためのテスト用\n",
152 | "\n",
153 | "`:fast`モードは非正規化数を扱う際に[バグ](https://github.com/JuliaIntervals/IntervalArithmetic.jl/issues/215)があるので注意が必要。"
154 | ]
155 | },
156 | {
157 | "cell_type": "code",
158 | "execution_count": 2,
159 | "id": "703c2a7c-d075-4d1d-9eb9-d187ddd997c0",
160 | "metadata": {},
161 | "outputs": [
162 | {
163 | "name": "stdout",
164 | "output_type": "stream",
165 | "text": [
166 | "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `~/.julia/environments/v1.8/Project.toml`\n",
167 | " \u001b[90m [d1acc4aa] \u001b[39mIntervalArithmetic v0.20.8\n"
168 | ]
169 | }
170 | ],
171 | "source": [
172 | "using IntervalArithmetic\n",
173 | "using Pkg; Pkg.status(\"IntervalArithmetic\")"
174 | ]
175 | },
176 | {
177 | "cell_type": "code",
178 | "execution_count": 3,
179 | "id": "6d71830b-327a-428c-9e9d-4e4009f04220",
180 | "metadata": {},
181 | "outputs": [
182 | {
183 | "name": "stdout",
184 | "output_type": "stream",
185 | "text": [
186 | "tiny * tiny = [0, 0]\n"
187 | ]
188 | },
189 | {
190 | "data": {
191 | "text/plain": [
192 | ":tight"
193 | ]
194 | },
195 | "execution_count": 3,
196 | "metadata": {},
197 | "output_type": "execute_result"
198 | }
199 | ],
200 | "source": [
201 | "setrounding(Interval,:fast)\n",
202 | "tiny = interval(0, floatmin())\n",
203 | "@show tiny * tiny\n",
204 | "# huge = interval(floatmax(), Inf)\n",
205 | "# @show huge * huge\n",
206 | "# @show huge / tiny\n",
207 | "# @show tiny / huge\n",
208 | "setrounding(Interval,:tight)"
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "id": "a4724a1d-2103-4fc0-b8d9-0e186ca5c027",
214 | "metadata": {},
215 | "source": [
216 | "現在はデフォルトで`:tight`モードになっており、このバグは`:fast`モードで計算しない限り発生しない。"
217 | ]
218 | },
219 | {
220 | "cell_type": "code",
221 | "execution_count": 4,
222 | "id": "4f71f29c-395b-4dc9-b382-556d893eb6f3",
223 | "metadata": {},
224 | "outputs": [
225 | {
226 | "data": {
227 | "text/plain": [
228 | "[0, 4.94066e-324]"
229 | ]
230 | },
231 | "execution_count": 4,
232 | "metadata": {},
233 | "output_type": "execute_result"
234 | }
235 | ],
236 | "source": [
237 | "setrounding(Interval,:tight)\n",
238 | "tiny = interval(0, floatmin())\n",
239 | "tiny * tiny"
240 | ]
241 | },
242 | {
243 | "cell_type": "markdown",
244 | "id": "874dd611-c1f1-4fb7-b11d-f4e77425618a",
245 | "metadata": {},
246 | "source": [
247 | "そしてJuliaの`setrounding_raw`という関数が方向付き丸めを制御できる?\n",
248 | "\n",
249 | "```\n",
250 | "setrounding_raw(::Type{<:Union{Float32,Float64}}, i::Integer) = ccall(:jl_set_fenv_rounding, Int32, (Int32,), i)\n",
251 | "```\n",
252 | "\n",
253 | "中身は単に`ccall`で`fesetround`を呼び出している。"
254 | ]
255 | },
256 | {
257 | "cell_type": "code",
258 | "execution_count": 5,
259 | "id": "eddbcc40-a6dc-49fe-a3e0-f17865d33b89",
260 | "metadata": {},
261 | "outputs": [
262 | {
263 | "name": "stdout",
264 | "output_type": "stream",
265 | "text": [
266 | "xup = 0.1\n",
267 | "xdown = 0.09999999999999999\n"
268 | ]
269 | },
270 | {
271 | "data": {
272 | "text/plain": [
273 | "true"
274 | ]
275 | },
276 | "execution_count": 5,
277 | "metadata": {},
278 | "output_type": "execute_result"
279 | }
280 | ],
281 | "source": [
282 | "xup = Base.Rounding.setrounding_raw(Float64,4194304) do # RoundUp\n",
283 | " parse(Float64, \"0.1\")# * parse(Float64, \"0.2\")\n",
284 | "end\n",
285 | "xdown = Base.Rounding.setrounding_raw(Float64,8388608) do # RoundDown\n",
286 | " parse(Float64, \"0.1\")# * parse(Float64, \"0.2\")\n",
287 | "end\n",
288 | "@show xup\n",
289 | "@show xdown\n",
290 | "xup > xdown"
291 | ]
292 | },
293 | {
294 | "cell_type": "markdown",
295 | "id": "3112879c-1222-4995-b47b-e1d8f48f668d",
296 | "metadata": {},
297 | "source": [
298 | "謎の整数は環境によって異なるfenvの変数で、以下のコードで分かる。"
299 | ]
300 | },
301 | {
302 | "cell_type": "code",
303 | "execution_count": 7,
304 | "id": "2fd23c5d-2721-496f-88d2-d4175d927d00",
305 | "metadata": {},
306 | "outputs": [
307 | {
308 | "data": {
309 | "text/plain": [
310 | "4194304"
311 | ]
312 | },
313 | "execution_count": 7,
314 | "metadata": {},
315 | "output_type": "execute_result"
316 | }
317 | ],
318 | "source": [
319 | "Base.Rounding.to_fenv(RoundUp)"
320 | ]
321 | },
322 | {
323 | "cell_type": "code",
324 | "execution_count": 6,
325 | "id": "9c3ac81e-6d5e-4fb6-8db0-b6ae468a32e2",
326 | "metadata": {},
327 | "outputs": [
328 | {
329 | "name": "stdout",
330 | "output_type": "stream",
331 | "text": [
332 | "bitstring(1.0 + x) = \"0011111111110001100110011001100110011001100110011001100110011010\"\n",
333 | "bitstring(1.0 + x) = \"0011111111110001100110011001100110011001100110011001100110011001\"\n"
334 | ]
335 | },
336 | {
337 | "data": {
338 | "text/plain": [
339 | "RoundingMode{:Nearest}()"
340 | ]
341 | },
342 | "execution_count": 6,
343 | "metadata": {},
344 | "output_type": "execute_result"
345 | }
346 | ],
347 | "source": [
348 | "Base.Rounding.setrounding_raw(Float64,0) # RoundNearest\n",
349 | "x = 0.1\n",
350 | "Base.Rounding.setrounding_raw(Float64,Base.Rounding.to_fenv(RoundUp))\n",
351 | "@show bitstring(1.0+x)\n",
352 | "\n",
353 | "Base.Rounding.setrounding_raw(Float64,Base.Rounding.to_fenv(RoundDown))\n",
354 | "@show bitstring(1.0+x)\n",
355 | "\n",
356 | "Base.Rounding.setrounding_raw(Float64,0) # RoundNearest\n",
357 | "rounding(Float64)"
358 | ]
359 | },
360 | {
361 | "cell_type": "markdown",
362 | "id": "4f446257-1078-4c55-9162-3f8a02204a6d",
363 | "metadata": {},
364 | "source": [
365 | "丸めの向きを変えて元に戻すのを忘れることがよくあるので、ある関数`f`でやりたいことを定義しておいて、\n",
366 | "```\n",
367 | "setrounding(f::Function, T, mode)\n",
368 | "```\n",
369 | "と実行できる。"
370 | ]
371 | },
372 | {
373 | "cell_type": "code",
374 | "execution_count": 8,
375 | "id": "a2deef07-8067-4a60-b7c1-632dc98f4966",
376 | "metadata": {},
377 | "outputs": [
378 | {
379 | "data": {
380 | "text/plain": [
381 | "oneplusx (generic function with 1 method)"
382 | ]
383 | },
384 | "execution_count": 8,
385 | "metadata": {},
386 | "output_type": "execute_result"
387 | }
388 | ],
389 | "source": [
390 | "function oneplusx()\n",
391 | " return bitstring(cos(x))\n",
392 | "end"
393 | ]
394 | },
395 | {
396 | "cell_type": "code",
397 | "execution_count": 12,
398 | "id": "2de4b5c1-8c76-467b-9874-8df9b5d355fb",
399 | "metadata": {},
400 | "outputs": [
401 | {
402 | "data": {
403 | "text/plain": [
404 | "\"0011111111101111110101110001001011111001101010000001011111000001\""
405 | ]
406 | },
407 | "execution_count": 12,
408 | "metadata": {},
409 | "output_type": "execute_result"
410 | }
411 | ],
412 | "source": [
413 | "setrounding(oneplusx,Float64,RoundUp)"
414 | ]
415 | },
416 | {
417 | "cell_type": "code",
418 | "execution_count": 9,
419 | "id": "2e35a426-84ab-4998-a0e2-f2a2e7a5bfc7",
420 | "metadata": {},
421 | "outputs": [
422 | {
423 | "data": {
424 | "text/plain": [
425 | "\"0011111111101111110101110001001011111001101010000001011111000000\""
426 | ]
427 | },
428 | "execution_count": 13,
429 | "metadata": {},
430 | "output_type": "execute_result"
431 | }
432 | ],
433 | "source": [
434 | "setrounding(oneplusx,Float64,RoundDown)"
435 | ]
436 | },
437 | {
438 | "cell_type": "code",
439 | "execution_count": 10,
440 | "id": "c42d3cd8-ab41-4c4a-8686-e11eaa209be9",
441 | "metadata": {},
442 | "outputs": [
443 | {
444 | "data": {
445 | "text/plain": [
446 | "RoundingMode{:Nearest}()"
447 | ]
448 | },
449 | "execution_count": 10,
450 | "metadata": {},
451 | "output_type": "execute_result"
452 | }
453 | ],
454 | "source": [
455 | "rounding(Float64)"
456 | ]
457 | },
458 | {
459 | "cell_type": "markdown",
460 | "id": "ed878d12-1005-42a4-ad23-7a41a3851814",
461 | "metadata": {},
462 | "source": [
463 | "しかし、この方法は`Float64`に対しては、正式にサポートしていない。。\n",
464 | "\n",
465 | "> Note that this is currently only supported for `T == BigFloat`.\n",
466 | "\n",
467 | "> !!! warning\n",
468 | "> This function is not thread-safe. It will affect code running on all threads, but\n",
469 | "> its behavior is undefined if called concurrently with computations that use the\n",
470 | "> setting."
471 | ]
472 | },
473 | {
474 | "cell_type": "markdown",
475 | "id": "b9bb62d3-cc81-4808-8436-acd21e3f2874",
476 | "metadata": {},
477 | "source": [
478 | "## 最近、Juliaで方向付き丸めが実装できる?\n",
479 | "\n",
480 | "最近、LLVMのアップデートで`Float64`の[丸めの向きを制御できるようになった](https://github.com/JuliaLang/julia/pull/27166#issuecomment-1306908993)らしい。そこで[SetRoundingLLVM.jl](https://github.com/orkolorko/SetRoundingLLVM.jl)という怪しい(?)パッケージを使ってみる。\n",
481 | "\n",
482 | "```julia:\n",
483 | "(@v1.8) pkg> add https://github.com/orkolorko/SetRoundingLLVM.jl\n",
484 | "```\n",
485 | "\n",
486 | "と入力して、インストールする。インストールが完了したら、\n",
487 | "\n",
488 | "```julia:\n",
489 | "using SetRoundingLLVM\n",
490 | "```\n",
491 | "\n",
492 | "として呼び出す。"
493 | ]
494 | },
495 | {
496 | "cell_type": "code",
497 | "execution_count": 6,
498 | "id": "71308220-7c1f-4dd2-ab4f-e83da903b6b4",
499 | "metadata": {
500 | "tags": []
501 | },
502 | "outputs": [
503 | {
504 | "name": "stdout",
505 | "output_type": "stream",
506 | "text": [
507 | "bitstring(1.0 + x) = \"0011111111110001100110011001100110011001100110011001100110011010\"\n",
508 | "bitstring(1.0 + x) = \"0011111111110001100110011001100110011001100110011001100110011001\"\n"
509 | ]
510 | },
511 | {
512 | "data": {
513 | "text/plain": [
514 | "\"0011111111110001100110011001100110011001100110011001100110011001\""
515 | ]
516 | },
517 | "execution_count": 6,
518 | "metadata": {},
519 | "output_type": "execute_result"
520 | }
521 | ],
522 | "source": [
523 | "using SetRoundingLLVM\n",
524 | "x = 0.1\n",
525 | "\n",
526 | "llvm_setrounding(RoundUp) do\n",
527 | " @show bitstring(1.0+x)\n",
528 | "end\n",
529 | "\n",
530 | "llvm_setrounding(RoundDown) do\n",
531 | " @show bitstring(1.0+x)\n",
532 | "end"
533 | ]
534 | },
535 | {
536 | "cell_type": "code",
537 | "execution_count": 9,
538 | "id": "530bfc55-e039-4a9d-9406-f7f8dd76b22d",
539 | "metadata": {},
540 | "outputs": [
541 | {
542 | "data": {
543 | "text/plain": [
544 | "\"0011111111101111110101110001001011111001101010000001011111000001\""
545 | ]
546 | },
547 | "execution_count": 9,
548 | "metadata": {},
549 | "output_type": "execute_result"
550 | }
551 | ],
552 | "source": [
553 | "llvm_setrounding(oneplusx,RoundUp)"
554 | ]
555 | },
556 | {
557 | "cell_type": "code",
558 | "execution_count": 10,
559 | "id": "a224d4d1-128a-49a4-9ca5-31301991c15c",
560 | "metadata": {},
561 | "outputs": [
562 | {
563 | "data": {
564 | "text/plain": [
565 | "\"0011111111101111110101110001001011111001101010000001011111000000\""
566 | ]
567 | },
568 | "execution_count": 10,
569 | "metadata": {},
570 | "output_type": "execute_result"
571 | }
572 | ],
573 | "source": [
574 | "llvm_setrounding(oneplusx,RoundDown)"
575 | ]
576 | },
577 | {
578 | "cell_type": "code",
579 | "execution_count": 11,
580 | "id": "8f5cdc6e-e0ee-4881-8f4f-787577bcc9a4",
581 | "metadata": {},
582 | "outputs": [
583 | {
584 | "data": {
585 | "text/plain": [
586 | "RoundingMode{:Nearest}()"
587 | ]
588 | },
589 | "execution_count": 11,
590 | "metadata": {},
591 | "output_type": "execute_result"
592 | }
593 | ],
594 | "source": [
595 | "llvm_rounding()"
596 | ]
597 | },
598 | {
599 | "cell_type": "markdown",
600 | "id": "33f4298e-3336-4e41-b227-3dd247059f36",
601 | "metadata": {},
602 | "source": [
603 | "## BLASの方向付き丸め制御\n",
604 | "\n",
605 | "ここまできたらMATLABと同様、方向付き丸めの制御をしながら、BLASを実行して高速区間演算をしたい。MATLABコードは\n",
606 | "\n",
607 | "```MATLAB:\n",
608 | "for n = 60:70\n",
609 | " disp(['n = ',num2str(n)])\n",
610 | "\n",
611 | " A = randn(n); B = randn(n);\n",
612 | "\n",
613 | " feature('setround',inf)\n",
614 | " AB_sup = A * B;\n",
615 | "\n",
616 | " feature('setround',-inf)\n",
617 | " AB_inf = A * B;\n",
618 | "\n",
619 | " feature('setround',.5)\n",
620 | " if ~all(AB_sup - AB_inf > 0,\"all\")\n",
621 | " break\n",
622 | " end\n",
623 | "end\n",
624 | "```\n",
625 | "\n",
626 | "行列サイズが60〜70なのは $n>64$ でOpenBLASがマルチスレッドの計算に切り替わるサイズのようで、ここを境に[方向付き丸めが効かなくなる](https://siko1056.github.io/blog/2021/12/23/octave-matlab-directed-rounding.html)ことがよく観測されているから。"
627 | ]
628 | },
629 | {
630 | "cell_type": "code",
631 | "execution_count": 12,
632 | "id": "db8c4887-6324-4850-9141-b086c2b3af47",
633 | "metadata": {},
634 | "outputs": [
635 | {
636 | "data": {
637 | "text/plain": [
638 | "LinearAlgebra.BLAS.LBTConfig\n",
639 | "Libraries: \n",
640 | "└ [ILP64] libopenblas64_.dylib"
641 | ]
642 | },
643 | "execution_count": 12,
644 | "metadata": {},
645 | "output_type": "execute_result"
646 | }
647 | ],
648 | "source": [
649 | "using LinearAlgebra\n",
650 | "BLAS.get_config()"
651 | ]
652 | },
653 | {
654 | "cell_type": "code",
655 | "execution_count": 13,
656 | "id": "e95a7658-cbb0-4081-a0b3-f4981eed1258",
657 | "metadata": {},
658 | "outputs": [
659 | {
660 | "name": "stdout",
661 | "output_type": "stream",
662 | "text": [
663 | "n = 60\n",
664 | "all(Cup .> Cdown) = true\n",
665 | "n = 61\n",
666 | "all(Cup .> Cdown) = true\n",
667 | "n = 62\n",
668 | "all(Cup .> Cdown) = true\n",
669 | "n = 63\n",
670 | "all(Cup .> Cdown) = true\n",
671 | "n = 64\n",
672 | "all(Cup .> Cdown) = true\n",
673 | "n = 65\n",
674 | "all(Cup .> Cdown) = false\n",
675 | "n = 66\n",
676 | "all(Cup .> Cdown) = false\n",
677 | "n = 67\n",
678 | "all(Cup .> Cdown) = false\n",
679 | "n = 68\n",
680 | "all(Cup .> Cdown) = false\n",
681 | "n = 69\n",
682 | "all(Cup .> Cdown) = false\n",
683 | "n = 70\n",
684 | "all(Cup .> Cdown) = false\n"
685 | ]
686 | },
687 | {
688 | "data": {
689 | "text/plain": [
690 | "RoundingMode{:Nearest}()"
691 | ]
692 | },
693 | "execution_count": 13,
694 | "metadata": {},
695 | "output_type": "execute_result"
696 | }
697 | ],
698 | "source": [
699 | "for n = 60:70\n",
700 | " @show n\n",
701 | " A, B = randn(n,n), randn(n,n)\n",
702 | " llvm_setrounding(RoundUp)\n",
703 | " Cup = A*B;\n",
704 | " Base.Rounding.setrounding_raw(Float64,Base.Rounding.to_fenv(RoundDown))\n",
705 | " Cdown = A*B;\n",
706 | " Base.Rounding.setrounding_raw(Float64,Base.Rounding.to_fenv(RoundNearest))\n",
707 | " @show all(Cup .> Cdown)\n",
708 | "end\n",
709 | "rounding(Float64)"
710 | ]
711 | },
712 | {
713 | "cell_type": "markdown",
714 | "id": "702d671a-44d0-4fff-8168-f82c5018a314",
715 | "metadata": {},
716 | "source": [
717 | "$n=65$で丸め向き指定する演算が失敗している。最新のLLVMの丸め向き指定をしても結果は変わらず。"
718 | ]
719 | },
720 | {
721 | "cell_type": "code",
722 | "execution_count": 14,
723 | "id": "233a1890-9644-445a-9de2-2bf284753f16",
724 | "metadata": {},
725 | "outputs": [
726 | {
727 | "name": "stdout",
728 | "output_type": "stream",
729 | "text": [
730 | "n = 60\n",
731 | "all(Cup .> Cdown) = true\n",
732 | "n = 61\n",
733 | "all(Cup .> Cdown) = true\n",
734 | "n = 62\n",
735 | "all(Cup .> Cdown) = true\n",
736 | "n = 63\n",
737 | "all(Cup .> Cdown) = true\n",
738 | "n = 64\n",
739 | "all(Cup .> Cdown) = true\n",
740 | "n = 65\n",
741 | "all(Cup .> Cdown) = false\n",
742 | "n = 66\n",
743 | "all(Cup .> Cdown) = false\n",
744 | "n = 67\n",
745 | "all(Cup .> Cdown) = false\n",
746 | "n = 68\n",
747 | "all(Cup .> Cdown) = false\n",
748 | "n = 69\n",
749 | "all(Cup .> Cdown) = false\n",
750 | "n = 70\n",
751 | "all(Cup .> Cdown) = false\n"
752 | ]
753 | },
754 | {
755 | "data": {
756 | "text/plain": [
757 | "RoundingMode{:Nearest}()"
758 | ]
759 | },
760 | "execution_count": 14,
761 | "metadata": {},
762 | "output_type": "execute_result"
763 | }
764 | ],
765 | "source": [
766 | "for n = 60:70\n",
767 | " @show n\n",
768 | " A, B = randn(n,n), randn(n,n)\n",
769 | " Cup = llvm_setrounding(RoundUp) do\n",
770 | " A*B\n",
771 | " end\n",
772 | " Cdown = llvm_setrounding(RoundDown) do\n",
773 | " A*B\n",
774 | " end\n",
775 | " @show all(Cup .> Cdown)\n",
776 | "end\n",
777 | "llvm_rounding()"
778 | ]
779 | },
780 | {
781 | "cell_type": "markdown",
782 | "id": "7c45896b-4b1f-4cfe-aca5-84ed9993da4c",
783 | "metadata": {},
784 | "source": [
785 | "これは予想通り。JuliaのデフォルトのOpenBLASで、方向付き丸めの制御が効かない。IntelのCPUでMKLを使っている人は方向付き丸めが変わるはず。Apple siliconの環境だと変わらない。"
786 | ]
787 | },
788 | {
789 | "cell_type": "code",
790 | "execution_count": null,
791 | "id": "f57c753f-3668-4684-b942-dc77f52b2a62",
792 | "metadata": {},
793 | "outputs": [],
794 | "source": [
795 | "using MKL\n",
796 | "BLAS.get_config()\n",
797 | "\n",
798 | "for n = 60:70\n",
799 | " @show n\n",
800 | " A, B = randn(n,n), randn(n,n)\n",
801 | " Cup = llvm_setrounding(RoundUp) do\n",
802 | " A*B\n",
803 | " end\n",
804 | " Cdown = llvm_setrounding(RoundDown) do\n",
805 | " A*B\n",
806 | " end\n",
807 | " @show all(Cup .> Cdown)\n",
808 | "end\n",
809 | "llvm_rounding()"
810 | ]
811 | },
812 | {
813 | "cell_type": "markdown",
814 | "id": "68615743-9039-4f5a-9c4b-5518e6223075",
815 | "metadata": {},
816 | "source": [
817 | "他のBLAS?\n",
818 | "\n",
819 | "### BLISBLAS\n",
820 | "\n",
821 | "ごく最近、[BLIS](https://github.com/flame/blis)というBLAS-likeライブラリ(2023年のWilkinson Prize Winnerだそう)のラッパーが実装されていて、[BLISBLAS.jl](https://github.com/JuliaLinearAlgebra/BLISBLAS.jl)という名前で利用可能になっている。BLISについてはあまり良く知らないが、C99を使ったBLASの再実装のようなものだと思っている。"
822 | ]
823 | },
824 | {
825 | "cell_type": "code",
826 | "execution_count": 15,
827 | "id": "4b5e6bdc-c600-426e-9273-b28bdfcab9a8",
828 | "metadata": {},
829 | "outputs": [
830 | {
831 | "data": {
832 | "text/plain": [
833 | "LinearAlgebra.BLAS.LBTConfig\n",
834 | "Libraries: \n",
835 | "├ [ILP64] libopenblas64_.dylib└ [ILP64] libblis.4.0.0.dylib"
836 | ]
837 | },
838 | "execution_count": 15,
839 | "metadata": {},
840 | "output_type": "execute_result"
841 | },
842 | {
843 | "name": "stdout",
844 | "output_type": "stream",
845 | "text": [
846 | "\n"
847 | ]
848 | }
849 | ],
850 | "source": [
851 | "using BLISBLAS\n",
852 | "BLAS.get_config()\n",
853 | "# BLISBLAS.set_num_threads(8)\n",
854 | "# BLISBLAS.get_num_threads()"
855 | ]
856 | },
857 | {
858 | "cell_type": "code",
859 | "execution_count": 16,
860 | "id": "71cd3128-3532-4b72-a09e-42438e17da97",
861 | "metadata": {},
862 | "outputs": [],
863 | "source": [
864 | "BLISBLAS.set_num_threads(4)"
865 | ]
866 | },
867 | {
868 | "cell_type": "code",
869 | "execution_count": 31,
870 | "id": "42b030f5-199f-4ea5-b9c9-6b1e60488cd5",
871 | "metadata": {},
872 | "outputs": [],
873 | "source": [
874 | "using AppleAccelerateLinAlgWrapper\n",
875 | "# AppleAccelerateLinAlgWrapper.get_num_threads()"
876 | ]
877 | },
878 | {
879 | "cell_type": "code",
880 | "execution_count": 17,
881 | "id": "d9ca666b-2342-4038-bd8a-207a2ae3e808",
882 | "metadata": {},
883 | "outputs": [
884 | {
885 | "name": "stdout",
886 | "output_type": "stream",
887 | "text": [
888 | "n = 60\n",
889 | "all(Cup .> Cdown) = false\n",
890 | "n = 61\n",
891 | "all(Cup .> Cdown) = false\n",
892 | "n = 62\n",
893 | "all(Cup .> Cdown) = false\n",
894 | "n = 63\n",
895 | "all(Cup .> Cdown) = false\n",
896 | "n = 64\n",
897 | "all(Cup .> Cdown) = false\n",
898 | "n = 65\n",
899 | "all(Cup .> Cdown) = false\n",
900 | "n = 66\n",
901 | "all(Cup .> Cdown) = false\n",
902 | "n = 67\n",
903 | "all(Cup .> Cdown) = false\n",
904 | "n = 68\n",
905 | "all(Cup .> Cdown) = false\n",
906 | "n = 69\n",
907 | "all(Cup .> Cdown) = false\n",
908 | "n = 70\n",
909 | "all(Cup .> Cdown) = false\n"
910 | ]
911 | },
912 | {
913 | "data": {
914 | "text/plain": [
915 | "RoundingMode{:Nearest}()"
916 | ]
917 | },
918 | "execution_count": 17,
919 | "metadata": {},
920 | "output_type": "execute_result"
921 | }
922 | ],
923 | "source": [
924 | "using AppleAccelerateLinAlgWrapper\n",
925 | "for n = 60:70\n",
926 | " @show n\n",
927 | " A, B = randn(n,n), randn(n,n)\n",
928 | " Cup = llvm_setrounding(RoundUp) do\n",
929 | " # Cup = Base.Rounding.setrounding_raw(Float64,Base.Rounding.to_fenv(RoundUp)) do\n",
930 | " A*B\n",
931 | " end\n",
932 | " Cdown = llvm_setrounding(RoundDown) do\n",
933 | " # Cdown = Base.Rounding.setrounding_raw(Float64,Base.Rounding.to_fenv(RoundDown)) do\n",
934 | " A*B\n",
935 | " end\n",
936 | " @show all(Cup .> Cdown)\n",
937 | "end\n",
938 | "llvm_rounding()\n",
939 | "# rounding(Float64)"
940 | ]
941 | },
942 | {
943 | "cell_type": "markdown",
944 | "id": "6f92b7e3-8a3f-4cef-ad6d-873b2fd90edb",
945 | "metadata": {},
946 | "source": [
947 | "やっぱり変わらない。そんなに甘くはない。"
948 | ]
949 | },
950 | {
951 | "cell_type": "code",
952 | "execution_count": null,
953 | "id": "61d09917-7e7b-4fd6-b943-342ec52ad93b",
954 | "metadata": {},
955 | "outputs": [],
956 | "source": []
957 | },
958 | {
959 | "cell_type": "markdown",
960 | "id": "8e27b4b4-06ef-4391-8d80-2ae761725672",
961 | "metadata": {},
962 | "source": [
963 | "### BLASの換装\n",
964 | "\n",
965 | "最終手段、OpenBLASをmakeし直して、BLASを換装する。幸い、[libblastrampoline](https://github.com/JuliaLinearAlgebra/libblastrampoline)という謎システムのおかげでBLASの換装が簡単らしいのだが…"
966 | ]
967 | },
968 | {
969 | "cell_type": "markdown",
970 | "id": "f1d2a1a3-bded-4645-9f7a-484bd26c31a1",
971 | "metadata": {
972 | "tags": []
973 | },
974 | "source": [
975 | "## 動作チェックのコード\n",
976 | "\n",
977 | "Juliaはバージョンがコロコロ変わるので、動作をチェックするコードが必要。\n",
978 | "以下の動作チェックのコードはいつしかの柏木先生のMATLABコード。\n",
979 | "\n",
980 | "```\n",
981 | "function testmm(n)\n",
982 | "\tdisp('testing multiplication...')\n",
983 | "\tflag = 1;\n",
984 | "\tfor k=1:n\n",
985 | "\t\ta=zeros(n);\n",
986 | "\t\ta(:,k)=ones(n,1)*1/3;\n",
987 | "\t\tb=zeros(n);\n",
988 | "\t\tb(k,:)=ones(1,n)*1/3;\n",
989 | "\t\tc = (rad(intval(a) * b) ~= 0) + 0.0;\n",
990 | "\t\tif prod(prod(c)) == 0\n",
991 | "\t\t\tdisp('multiplication error!')\n",
992 | "\t\t\tflag = 0;\n",
993 | "\t\t\tbreak\n",
994 | "\t\tend\n",
995 | "\tend\n",
996 | "\tif flag == 1\n",
997 | "\t\tdisp('multiplication OK!')\n",
998 | "\tend\n",
999 | "\n",
1000 | "\tdisp('testing addition...')\n",
1001 | "\tflag = 1;\n",
1002 | "\tfor k=2:n\n",
1003 | "\t\ta=zeros(n);\n",
1004 | "\t\ta(:,1)=ones(n,1);\n",
1005 | "\t\ta(:,k)=ones(n,1)*2^(-27);\n",
1006 | "\t\tb=zeros(n);\n",
1007 | "\t\tb(1,:)=ones(1,n);\n",
1008 | "\t\tb(k,:)=ones(1,n)*2^(-27);\n",
1009 | "\t\tc = (rad(intval(a) * b) ~= 0) + 0.0;\n",
1010 | "\t\tif prod(prod(c)) == 0\n",
1011 | "\t\t\tdisp('addition error!')\n",
1012 | "\t\t\tflag = 0;\n",
1013 | "\t\t\tbreak\n",
1014 | "\t\tend\n",
1015 | "\tend\n",
1016 | "\tif flag == 1\n",
1017 | "\t\tdisp('addition OK!')\n",
1018 | "\tend\n",
1019 | "end\n",
1020 | "```\n"
1021 | ]
1022 | },
1023 | {
1024 | "cell_type": "code",
1025 | "execution_count": 18,
1026 | "id": "79db71c5-466a-47e6-bffa-a3b9992534a8",
1027 | "metadata": {
1028 | "tags": []
1029 | },
1030 | "outputs": [
1031 | {
1032 | "data": {
1033 | "text/plain": [
1034 | "testmm (generic function with 1 method)"
1035 | ]
1036 | },
1037 | "execution_count": 18,
1038 | "metadata": {},
1039 | "output_type": "execute_result"
1040 | }
1041 | ],
1042 | "source": [
1043 | "function testmm(n)\n",
1044 | " println(\"testing multiplication...\")\n",
1045 | " flag = 1\n",
1046 | " for k=1:n\n",
1047 | " a=zeros(n,n)\n",
1048 | " a[:,k] = ones(n)*1/3.\n",
1049 | " b=zeros(n,n)\n",
1050 | " b[k,:] = ones(n)*1/3.\n",
1051 | " c = (radius.(map(Interval,a) * b) .!= 0) .+ 0.0\n",
1052 | " if prod(c) == 0\n",
1053 | " println(\"multiplication error!\")\n",
1054 | " flag = 0\n",
1055 | " break\n",
1056 | " end\n",
1057 | " end\n",
1058 | " if flag == 1\n",
1059 | " println(\"multiplication OK!\")\n",
1060 | " end\n",
1061 | " println(\"testing addition...\")\n",
1062 | " flag = 1\n",
1063 | " for k=2:n\n",
1064 | " a=zeros(n,n)\n",
1065 | " a[:,1]=ones(n)\n",
1066 | " a[:,k]=ones(n)*2^(-27)\n",
1067 | " b=zeros(n,n)\n",
1068 | " b[1,:]=ones(n)\n",
1069 | " b[k,:]=ones(n)*2^(-27)\n",
1070 | " c = (radius.(map(Interval,a) * b) .!= 0) .+ 0.0\n",
1071 | " if prod(c) == 0\n",
1072 | " println(\"addition error!\")\n",
1073 | " flag = 0\n",
1074 | " break\n",
1075 | " end\n",
1076 | " end\n",
1077 | " if flag == 1\n",
1078 | " println(\"addition OK!\")\n",
1079 | " end\n",
1080 | "end"
1081 | ]
1082 | },
1083 | {
1084 | "cell_type": "code",
1085 | "execution_count": 19,
1086 | "id": "52605646-1370-43b2-a2aa-29ec4e774903",
1087 | "metadata": {},
1088 | "outputs": [
1089 | {
1090 | "name": "stdout",
1091 | "output_type": "stream",
1092 | "text": [
1093 | "testing multiplication...\n",
1094 | "multiplication OK!\n",
1095 | "testing addition...\n",
1096 | "addition OK!\n"
1097 | ]
1098 | }
1099 | ],
1100 | "source": [
1101 | "testmm(70)"
1102 | ]
1103 | },
1104 | {
1105 | "cell_type": "code",
1106 | "execution_count": 20,
1107 | "id": "a2e20ccb-3f20-4c0c-86a3-1dd3160b64d2",
1108 | "metadata": {},
1109 | "outputs": [
1110 | {
1111 | "name": "stdout",
1112 | "output_type": "stream",
1113 | "text": [
1114 | "n = 64\n",
1115 | "all(Cup .> Cdown) = false\n",
1116 | "n = 65\n",
1117 | "all(Cup .> Cdown) = false\n"
1118 | ]
1119 | }
1120 | ],
1121 | "source": [
1122 | "# import Pkg; Pkg.add(\"IntervalMatrices\")\n",
1123 | "using IntervalMatrices\n",
1124 | "# @show BLAS.vendor()\n",
1125 | "# @show BLAS.get_config()\n",
1126 | "for n = 64:65\n",
1127 | " # n=2^i\n",
1128 | " @show n\n",
1129 | " A, B = IntervalMatrix(randn(n,n)), IntervalMatrix(randn(n,n))\n",
1130 | " C = A * B\n",
1131 | " Cup = sup(C)\n",
1132 | " Cdown = inf(C)\n",
1133 | " @show all(Cup .> Cdown)\n",
1134 | "end"
1135 | ]
1136 | },
1137 | {
1138 | "cell_type": "code",
1139 | "execution_count": 21,
1140 | "id": "97ee1295-2ff1-4b63-8e8a-6b45681a9372",
1141 | "metadata": {},
1142 | "outputs": [
1143 | {
1144 | "data": {
1145 | "text/plain": [
1146 | "testmm_intmat (generic function with 1 method)"
1147 | ]
1148 | },
1149 | "execution_count": 21,
1150 | "metadata": {},
1151 | "output_type": "execute_result"
1152 | }
1153 | ],
1154 | "source": [
1155 | "function testmm_intmat(n)\n",
1156 | " println(\"testing multiplication...\")\n",
1157 | " flag = 1\n",
1158 | " for k=1:n\n",
1159 | " a=zeros(n,n)\n",
1160 | " a[:,k] = ones(n)*1/3.\n",
1161 | " b=zeros(n,n)\n",
1162 | " b[k,:] = ones(n)*1/3.\n",
1163 | " c = (radius.(IntervalMatrix(a) * b) .!= 0) .+ 0.0\n",
1164 | " if prod(c) == 0\n",
1165 | " println(\"multiplication error!\")\n",
1166 | " flag = 0\n",
1167 | " break\n",
1168 | " end\n",
1169 | " end\n",
1170 | " if flag == 1\n",
1171 | " println(\"multiplication OK!\")\n",
1172 | " end\n",
1173 | " println(\"testing addition...\")\n",
1174 | " flag = 1\n",
1175 | " for k=2:n\n",
1176 | " a=zeros(n,n)\n",
1177 | " a[:,1]=ones(n)\n",
1178 | " a[:,k]=ones(n)*2^(-27)\n",
1179 | " b=zeros(n,n)\n",
1180 | " b[1,:]=ones(n)\n",
1181 | " b[k,:]=ones(n)*2^(-27)\n",
1182 | " c = (radius.(IntervalMatrix(a) * b) .!= 0) .+ 0.0\n",
1183 | " if prod(c) == 0\n",
1184 | " println(\"addition error!\")\n",
1185 | " flag = 0\n",
1186 | " break\n",
1187 | " end\n",
1188 | " end\n",
1189 | " if flag == 1\n",
1190 | " println(\"addition OK!\")\n",
1191 | " end\n",
1192 | "end"
1193 | ]
1194 | },
1195 | {
1196 | "cell_type": "code",
1197 | "execution_count": 22,
1198 | "id": "e2143653-27f3-47a3-b067-19e7b47ee9ad",
1199 | "metadata": {},
1200 | "outputs": [
1201 | {
1202 | "name": "stdout",
1203 | "output_type": "stream",
1204 | "text": [
1205 | "testing multiplication...\n",
1206 | "multiplication error!\n",
1207 | "testing addition...\n",
1208 | "addition error!\n"
1209 | ]
1210 | }
1211 | ],
1212 | "source": [
1213 | "testmm_intmat(70)"
1214 | ]
1215 | },
1216 | {
1217 | "cell_type": "markdown",
1218 | "id": "4e5d41cb-26f3-47f3-ba37-ed55e197339f",
1219 | "metadata": {},
1220 | "source": [
1221 | "## 結論\n",
1222 | "\n",
1223 | "Apple siliconのJuliaではBLASの方向付き丸めの制御はまだ早い。。でもMATLABでできているので、技術的には可能なはず。"
1224 | ]
1225 | },
1226 | {
1227 | "cell_type": "markdown",
1228 | "id": "18914f80-3512-40af-8b17-a98c33d61e75",
1229 | "metadata": {},
1230 | "source": [
1231 | "### 謝辞\n",
1232 | "\n",
1233 | "本資料も筆者が学生の頃に精度保証付き数値計算を教えて下さった[柏木雅英](http://www.kashi.info.waseda.ac.jp/~kashi/)先生の「数値解析特論」の講義資料が基になっています.\n",
1234 | "また, 以下のような文献・Web ページ等を参考にこの文章は書いています.\n",
1235 | "\n",
1236 | "### 参考文献\n",
1237 | "\n",
1238 | "1. 大石進一編著, 精度保証付き数値計算の基礎, コロナ社, 2018.
\n",
1239 | "(精度保証付き数値計算の教科書. 浮動小数点数および区間演算に詳しい. この1章が読めたら大したもの)\n",
1240 | "1. [Calculating with sets: Interval methods in Julia](https://github.com/dpsanders/IntervalsJuliaCon2020).
\n",
1241 | "(Juliaで区間演算をするJuliaCon2020のチュートリアル資料、[動画](https://youtu.be/LAuRCy9jUU8)も公開されている)\n",
1242 | "1. [IntervalArithmetic.jl: Basic usage](https://github.com/JuliaIntervals/IntervalArithmetic.jl/blob/master/docs/src/usage.md).
\n",
1243 | "(IntervalArithmetic.jlの区間演算の説明ページ)\n",
1244 | "1. matsueushi, [デフォルトの丸めモードで上付き丸め、下付き丸めをエミュレートする(Julia)](https://matsueushi.github.io/posts/rounding-emulator/).
\n",
1245 | "(IntervalArithmetic.jlの丸め変更はこれを使用しているようです)\n",
1246 | "1. matsueushi, [Juliaで丸めモードを指定して浮動小数点数の計算をする(したい)](https://matsueushi.github.io/posts/julia-rounding/).
\n",
1247 | "(丸めモードの指定ができない!?最近点丸めだけで区間演算しないといけないのか...)\n",
1248 | "\n",
1249 | "\n",
1250 | "
高安亮紀,2020年8月6日(最終更新:2023年5月21日)
"
1251 | ]
1252 | }
1253 | ],
1254 | "metadata": {
1255 | "kernelspec": {
1256 | "display_name": "Julia 1.9.4",
1257 | "language": "julia",
1258 | "name": "julia-1.9"
1259 | },
1260 | "language_info": {
1261 | "file_extension": ".jl",
1262 | "mimetype": "application/julia",
1263 | "name": "julia",
1264 | "version": "1.9.4"
1265 | }
1266 | },
1267 | "nbformat": 4,
1268 | "nbformat_minor": 5
1269 | }
1270 |
--------------------------------------------------------------------------------
/FourierChebyshev.jl:
--------------------------------------------------------------------------------
1 | ### Fourier functions
2 | using FFTW, Plots
3 | function fouriercoeffs(f, N, I=[0,2π])
4 | # f: any periodic function on I
5 | # N: size of Fourier series
6 | a = I[1]; b = I[2]
7 | h = (b-a)/(2N-1)
8 | j = 0:2N-2
9 | xⱼ = a .+ j*h
10 | fⱼ = f.(xⱼ);
11 | return fftshift(fft(fⱼ))/(2N-1)
12 | end
13 |
14 | function odefouriercoeffs(f, N, I, n=1)
15 | a = I[1]; b = I[2];
16 | h = (b-a)/(2N-1)
17 | j = 0:2N-2
18 | xⱼ = a .+ j*h
19 | fⱼ = f(xⱼ)[n,:]
20 | return fftshift(fft(fⱼ))/(2N-1)
21 | end
22 |
23 | function plot_fourier(bc, I=[0,2π])
24 | # bc: Fourier coefficients
25 | a = I[1]; b = I[2]
26 | N = (length(bc)+1)/2 # 2N-1
27 | n_pad = 500
28 | bc_pad = [zeros(n_pad);bc;zeros(n_pad)]
29 | N_pad = N + n_pad
30 | h_pad = (b-a)/(2N_pad-1)
31 | xj_pad = a .+ h_pad*(0:2N_pad-2)
32 | fNj_pad = real((2N_pad-1)*ifft(ifftshift(bc_pad)))
33 | plot(xj_pad, fNj_pad, legend=false, xlabel = "\$x\$", ylabel = "\$f(x)\$")
34 | end
35 |
36 | function plot_fourier!(bc, I=[0,2π];label="")
37 | # bc: Fourier coefficients
38 | a = I[1]; b = I[2]
39 | N = (length(bc)+1)/2 # 2N-1
40 | n_pad = 500
41 | bc_pad = [zeros(n_pad);bc;zeros(n_pad)]
42 | N_pad = N + n_pad
43 | h_pad = (b-a)/(2N_pad-1)
44 | xj_pad = a .+ h_pad*(0:2N_pad-2)
45 | fNj_pad = real((2N_pad-1)*ifft(ifftshift(bc_pad)))
46 | plot!(xj_pad, fNj_pad, label=label)
47 | end
48 |
49 | function plot_fouriercoeffs(bc)
50 | N = (length(bc)+1)/2 # 2N-1
51 | plot(-N+1:N-1,abs.(bc),yscale=:log10,
52 | legend=false,
53 | xlabel = "\$k\$",
54 | ylabel = "\$|\\bar{c}_k\\,|\$",
55 | title = "Absolute values of Fourier coefficients"
56 | )
57 | end
58 |
59 | function plot_solution(u, index) # u = [ω, a_{-N+1}, ..., a_0, ..., a_{N-1}], length(u) = 2N
60 | # index = 1: profile of solution
61 | # 2: Fourier mode
62 | # 3: phase profile
63 | ω = real(u[1])
64 | L = 2π / ω
65 | a = u[2:end]
66 | N = length(u)/2 # N: size of Fourier
67 | n_pad = 500
68 | a_pad = [zeros(n_pad);a;zeros(n_pad)]
69 | N_pad = N + n_pad
70 | dx = L/(2*N_pad-1)
71 | x = dx*(0:2*N_pad-2)
72 | if index == 1
73 | # Plot profile:
74 | plot(plot_fourier(a, [0,L]),
75 | # plot(x,real((2N_pad-1)*ifft(ifftshift(a_pad))),
76 | xlabel = "\$t\$",
77 | ylabel = "\$x\\,(t)\$",
78 | line = 1.6,
79 | title = "Profile of solution",
80 | size = (720,400),
81 | legend = false,
82 | )
83 | elseif index == 2
84 | # Plot Fourier coefficients:
85 | plot(plot_fouriercoeffs(a),
86 | # plot((-N+1):(N-1),abs.(a),yscale=:log10,
87 | xlabel = "\$k\$",
88 | ylabel = "\$|a_k\\,|\$",
89 | line = 1.6,
90 | title = "Absolute values of Fourier coefficients",
91 | size = (720,400),
92 | legend = false,
93 | )
94 | elseif index == 3
95 | # Plot phase:
96 | k = (-N_pad+1):(N_pad-1)
97 | plot(real((2N_pad-1)*ifft(ifftshift(a_pad))),real((2N_pad-1)*ifft(ifftshift(a_pad.*(im*k*ω)))),
98 | xlabel = "\$x(t)\$",
99 | ylabel = "\$\\dot{x}\\,(t)\$",
100 | line = 1.6,
101 | title = "Phase plot of a numerical solution",
102 | size = (720,400),
103 | legend = false,
104 | )
105 | end
106 | end
107 |
108 | function plot_solution!(u)
109 | L = 2π/real(u[1])
110 | a = u[2:end]
111 | N = length(u)/2
112 | n_pad = 1000
113 | a_pad = [zeros(n_pad);a;zeros(n_pad)]
114 | N_pad = N+n_pad
115 | k = (-N_pad+1):(N_pad-1)
116 | dx = L/(2*N_pad-1)
117 | x = dx*(0:2*N_pad-2)
118 | plot!(real((2N_pad-1)*ifft(ifftshift(a_pad))),real((2N_pad-1)*ifft(ifftshift(a_pad.*(im*k)))),line=1.6,)
119 | end
120 |
121 | function powerconvfourier(a::Vector{Complex{T}},p) where T
122 | M = Int((length(a)+1)/2)
123 | N = (p-1)*M
124 | ta = [zeros(N,1);a;zeros(N,1)] # 1. Padding zeros: size(ta) = 2pM-1
125 | tb = ifft(ifftshift(ta)) # 2. IFFT of ta
126 | tbᵖ = tb.^p # 3. tb*^tb
127 | cᵖ = fftshift(fft(tbᵖ))*(2.0*p*M-1)^(p-1)
128 | return cᵖ[N+1:end-N], cᵖ[p:end-(p-1)]# return (truncated, full) version
129 | end
130 |
131 | function convfourier(ia...)
132 | p = length(ia)
133 | M = Int((length(ia[1])+1)/2) # length(a) = 2M-1
134 | N = (p-1)*M
135 |
136 | length_ia = 2*p*M-1
137 | # length_ia_ext = nextpow(2,length_ia)# 2pM-2+2L
138 |
139 | ibp_ext = (ones(ComplexF64, length_ia))
140 |
141 | L = 0
142 |
143 | for i = 1:p
144 | # step.1 : padding (p-1)M + L zeros for each sides
145 | ia_ext = (zeros(ComplexF64,length_ia))
146 | ia_ext[L+N+1:end-L-N] = ia[i] #\tilda{a}
147 | # step.2 : inverse fft
148 | ib_ext = ifft(ifftshift(ia_ext))
149 | # step.3 : power p elementwisely
150 | ibp_ext = ibp_ext .* ib_ext
151 | end
152 | # step.4 : fft with rescaling
153 | ic_extᵖ = fftshift(fft(ibp_ext)) * (length_ia)^(p-1)
154 | return ic_extᵖ[L+N+1:end-N-L], ic_extᵖ[L+p:end-(L+p-1)] # return (truncated, full) version
155 | end
156 |
157 | function convcos(ia...) # Input: Two-sided (real)
158 | M = length(ia[1])
159 | FourierCoeffs = []
160 | for i = 1:length(ia)
161 | ia[i][1] = ia[i][1]; ia[i][2:end] = (0.5) * ia[i][2:end] # Two-sided -> One-sided (real)
162 | FC_local = map(Complex,[reverse(ia[i][2:end]); ia[i]])
163 | if i==1
164 | FourierCoeffs = tuple(FC_local)
165 | else
166 | FourierCoeffs = tuple(FourierCoeffs...,tuple(FC_local)...)
167 | end
168 | end
169 | icp, icp_full = convfourier(FourierCoeffs...) # real -> complex (input)
170 | iap = zeros(M)
171 | iap[1] = real(icp[M]); iap[2:end] = 2 * real(icp[M+1:end]) # One-sided (complex) -> Two-sided (real)
172 | N = Int((length(icp_full)+1)/2) #2N-1
173 | iap_full = (zeros(N))
174 | iap_full[1] = real(icp_full[N]); iap_full[2:end] = (2) * real(icp_full[N+1:end]) # One-sided (complex) -> Two-sided (real)
175 | return iap, iap_full
176 | end
177 |
178 |
179 |
180 | ### Chebyshev functions
181 | function chebpts(n, a=-1, b=1) # n: order of Chebyshev polynomials
182 | m = -n:2:n
183 | x = sinpi.(m/(2*n))
184 | return (1.0 .- x).*a/2 + (1.0 .+ x).*b/2
185 | end
186 |
187 | function chebcoeffs(f,M,I=[-1,1])
188 | a = I[1]; b = I[2]
189 | n = M-1
190 | cpts = chebpts(n, a, b)
191 | fvals = f.(cpts)
192 | FourierCoeffs = real(fft([reverse(fvals);fvals[2:end-1]]))
193 | ChebCoeffs = FourierCoeffs[1:n+1]/n
194 | ChebCoeffs[1] = ChebCoeffs[1]/2
195 | ChebCoeffs[end] = ChebCoeffs[end]/2
196 | return ChebCoeffs # return Two-sided Chebyshev
197 | end
198 |
199 | function chebcoeffs_complex(f,M,I=[-1,1])
200 | a = I[1]; b = I[2]
201 | n = M-1
202 | cpts = chebpts(n, a, b)
203 | fvals = f.(cpts)
204 | FourierCoeffs = fft([reverse(fvals);fvals[2:end-1]])
205 | ChebCoeffs = FourierCoeffs[1:n+1]/n
206 | ChebCoeffs[1] = ChebCoeffs[1]/2
207 | ChebCoeffs[end] = ChebCoeffs[end]/2
208 | return ChebCoeffs # return Two-sided Chebyshev
209 | end
210 |
211 | function cheb(f,I=[-1,1]; tol = 5e-15,Nmax = 10000)
212 | a = I[1]; b = I[2]; m = 0.5*(a+b); r = 0.5*(b-a); x = rand(5)
213 | x1 = m .+ x*r; x2 = m .- x*r
214 | if f.(x1) ≈ f.(x2)
215 | odd_even = 1 # even function: 1
216 | elseif f.(x1) ≈ -f.(x2)
217 | odd_even = -1 # odd function: -1
218 | else
219 | odd_even = 0 # otherwise: 0
220 | end
221 | i = 3
222 | schbc = 0 # sampling chebyshev coefficients
223 | while true
224 | schbc = chebcoeffs(f,2^i+1,I)
225 | if all(abs.(schbc[end-2:end]) .< tol) || (2^i+1 > Nmax)
226 | break
227 | end
228 | i += 1
229 | end
230 | M = findlast(abs.(schbc) .> tol)
231 | cc = schbc[1:M]
232 | # cc = chebcoeffs(f,M,I)
233 | if odd_even == 1 # even function
234 | cc[2:2:end] .= 0
235 | elseif odd_even == -1 # odd function
236 | cc[1:2:end] .= 0
237 | end
238 | return cc # return Two-sided Chebyshev
239 | end
240 |
241 | using GenericFFT
242 | function bigcheb(f,I=[-1,1]; tol = 5e-15,Nmax = 10000)
243 | a = I[1]; b = I[2]; m = 0.5*(a+b); r = 0.5*(b-a); x = rand(5)
244 | x1 = m .+ x*r; x2 = m .- x*r
245 | if f.(x1) ≈ f.(x2)
246 | odd_even = 1 # even function: 1
247 | elseif f.(x1) ≈ -f.(x2)
248 | odd_even = -1 # odd function: -1
249 | else
250 | odd_even = 0 # otherwise: 0
251 | end
252 | i = 3
253 | schbc = 0 # sampling chebyshev coefficients
254 | while true
255 | schbc = chebcoeffs(f,big(2^i+1),I)
256 | if all(abs.(schbc[end-2:end]) .< tol) || (2^i+1 > Nmax)
257 | break
258 | end
259 | i += 1
260 | end
261 | M = findlast(abs.(schbc) .> tol)
262 | cc = schbc[1:M]
263 | # cc = chebcoeffs(f,M,I)
264 | if odd_even == 1 # even function
265 | cc[2:2:end] .= 0
266 | elseif odd_even == -1 # odd function
267 | cc[1:2:end] .= 0
268 | end
269 | return cc # return Two-sided Chebyshev
270 | end
271 |
272 | function cheb_complex(f,I=[-1;1]; tol = 5e-15,Nmax = 10000)
273 | i = 3
274 | schbc = 0 # sampling chebyshev coefficients
275 | while true
276 | schbc = chebcoeffs_complex(f,2^i+1,I)
277 | if all(abs.(schbc[end-2:end]) .< tol) || (2^i+1 > Nmax)
278 | break
279 | end
280 | i += 1
281 | end
282 | M = findlast(abs.(schbc) .> tol)
283 | return schbc[1:M]
284 | # return cc # return Two-sided Chebyshev
285 | end
286 |
287 | function plot_chebcoeffs(f)
288 | zero_ind = findall(x->x==0, f)
289 | f[zero_ind] .= f[zero_ind .+ 1]
290 | plot(0:length(f)-1, abs.(f),
291 | yscale=:log10,
292 | title="Chebyshev coefficients",
293 | xlabel="Degree of Chebyshev polynomial",
294 | ylabel="Magnitude of coefficient",
295 | size = (800,400),
296 | legend = false,
297 | )
298 | end
299 |
300 | function clenshaw(a,x) # Clenshaw's algorithm
301 | # a: (Two-sided) Chbyshev coefficients
302 | # x: evaluating points in [-1,1]
303 | n = length(a)-1
304 | bk1 = 0.0
305 | bk2 = 0.0
306 | x = 2x
307 | for r = (n+1):-2:3
308 | bk2 = x.*bk1 .- bk2 .+ a[r]
309 | bk1 = x.*bk2 .- bk1 .+ a[r-1]
310 | end
311 | if isodd(n)
312 | b2 = x.*bk1 .- bk2 .+ a[2]
313 | bk2 = bk1 # b3
314 | bk1 = b2
315 | end
316 | return -bk2 .+ 0.5x .* bk1 .+ a[1] # y = c(1) + .5*x.*bk1 - bk2;
317 | end
318 |
319 | function clenshaw_secondkind(a,x) # Clenshaw's algorithm
320 | # a: (Two-sided) Chbyshev coefficients
321 | # x: evaluating points in [-1,1]
322 | n = length(a)-1
323 | bk0 = 0
324 | bk1 = 0
325 | for r = (n+1):-1:1
326 | tmp = 2x.*bk0 .- bk1 .+ a[r]
327 | bk1 = bk0
328 | bk0 = tmp
329 | end
330 | return bk0 #.+ bk1*(-x)
331 | end
332 |
333 | function eval_cheb(a,x; I=[-1,1])
334 | # a: (Two-sided) Chbyshev coefficients
335 | # x: evaluating points in domain
336 | ξ = 2*(x.-I[1])/(I[2]-I[1]) .- 1
337 | return clenshaw(a,ξ)
338 | end
339 |
340 | function eval_cheb_naive(ChebCoeffs_twosided,x; I=[-1,1])
341 | M = length(ChebCoeffs_twosided) # M: size of chebyshev
342 | a = I[1]; b = I[2]
343 | k = 0:M-1
344 | ξ = 2*(x.-a)/(b-a) .- 1
345 | return cos.(Vector(k)' .* acos.(ξ)) * ChebCoeffs_twosided
346 | end
347 |
348 | function eval_cheb_bc(ChebCoeffs_twosided,x,n=200; I=[-1,1]) # Barycentric interportion formula
349 | M = length(ChebCoeffs_twosided) # M: size of chebyshev
350 | a = I[1]; b = I[2]
351 | k = 0:M-1
352 | ξⱼ = chebpts(n)
353 | xc = (1.0 .- ξⱼ)*a/2 + (1.0 .+ ξⱼ)*b/2 # Chebyshev points in [a,b]
354 | fxc = cos.(Vector(k)' .* acos.(ξⱼ)) * ChebCoeffs_twosided
355 | valnum = length(x)
356 | ξ = 2*(x.-a)/(b-a) .- 1
357 | # ξ = range(-1,stop=1,length=valnum)
358 | # x = (1.0 .- ξ)*a/2 + (1.0 .+ ξ)*b/2
359 | λ = [1/2; ones(n-1); 1/2] .* (-1).^(0:n)
360 |
361 | numer = zeros(valnum)
362 | denom = zeros(valnum)
363 | exact = zeros(Bool,valnum)
364 |
365 | for j = 1:n+1
366 | xdiff = x .- xc[j]
367 | temp = λ[j] ./ xdiff
368 | numer += temp * fxc[j]
369 | denom += temp
370 | exact[xdiff.==0] .= true
371 | end
372 |
373 | fx = numer ./ denom
374 | jj = findall(exact)
375 | fx[jj] = f.(x[jj])
376 | fx[jj] = cos.(Vector(k)' .* acos.(ξ[jj])) * ChebCoeffs_twosided
377 | return fx
378 | end
379 |
380 | function plot_cheb(ChebCoeffs_twosided; I=[-1,1],title="",label="",legend=true) # Input: Two-sided Chebyshev
381 | # M = length(ChebCoeffs_twosided) # M: size of chebyshev
382 | # a = I[1]; b = I[2];
383 | x = range(I[1],stop=I[2],length=5000)
384 | # ξ = 2*(x.-a)/(b-a) .- 1
385 | fx = eval_cheb(ChebCoeffs_twosided,x,I=I)
386 | plot(x, fx, legend=legend, label=label, title=title, xlabel="\$x\$",ylabel="\$f(x)\$")
387 | end
388 |
389 | function plot_cheb!(ChebCoeffs_twosided; I=[-1,1],title="",label="",legend=true) # Input: Two-sided Chebyshev
390 | # M = length(ChebCoeffs_twosided) # M: size of chebyshev
391 | # a = I[1]; b = I[2];
392 | x = range(I[1],stop=I[2],length=5000)
393 | # ξ = 2*(x.-a)/(b-a) .- 1
394 | fx = eval_cheb(ChebCoeffs_twosided,x,I=I)
395 | plot!(x, fx, legend=legend, label=label, title=title, xlabel="\$x\$",ylabel="\$f(x)\$")
396 | end
397 |
398 | function chebdiff(a; I=[-1,1])# Input is Two-sided
399 | M = length(a)
400 | b = zeros(M+1)
401 | for r = M-1:-1:1
402 | b[r] = b[r+2] + 2*r*a[r+1]
403 | end
404 | b[1] /= 2.0
405 | return b[setdiff(1:end,end)]*(2/(I[2]-I[1])) # Output is Two-sided
406 | end
407 |
408 | function chebdiff_oneside(a; I=[-1,1])# Input is One-sided
409 | M = length(a)
410 | b = zeros(M+1)
411 | for r = M-1:-1:1
412 | b[r] = b[r+2] + 2*r*a[r+1]
413 | end
414 | return b[setdiff(1:end,end)]*(2/(I[2]-I[1])) # Output is One-sided
415 | end
416 |
417 | function chebdiff_secondkind(a; I=[-1,1]) # Input is Two-sided
418 | M = length(a)
419 | b = zeros(M-1)
420 | for n = 0:M-2
421 | b[n+1] = (n+1)*a[n+2]
422 | end
423 | return b*(2/(I[2]-I[1])) # Output is second kind (Two-sided)
424 | end
425 |
426 | function chebindefint(a; I=[-1,1])# Input is Two-sided
427 | M = length(a)
428 | a_ext = zeros(M+2)
429 | a_ext[1] = 2*a[1]
430 | a_ext[2:M] = a[2:M]
431 | A = zeros(M+1)
432 | for n = 1:M
433 | A[n+1] = (a_ext[n] - a_ext[n+2])/(2n)
434 | end
435 | # A[1] = sum(A[2:2:end]) - sum(A[3:2:end]) # takes the value 0 at the left endpoint
436 | return A * (I[2]-I[1])/2
437 | end
438 |
439 | function chebint(a; I=[-1,1])# Input is Two-sided
440 | M = length(a)
441 | n = 0:2:M-1
442 | return sum(2a[1:2:end]./(1.0 .- n.^2))*((I[2]-I[1])/2)
443 | end
444 |
445 | function chebroots(a, I=[-1,1]) # Input is two-sided Chebyshev
446 | I_lo = I[1]; I_up = I[2]
447 | n = length(a)
448 | # create colleague matrix
449 | du = [1; ones(n-3)*0.5]
450 | dl = ones(n-2)*0.5
451 | d = zeros(n-1)
452 | A = Tridiagonal(dl, d, du)
453 | B = zeros(n-1,n-1)
454 | B[end,:] = a[1:end-1]
455 | C = A - (1/(2*a[n]))*B
456 | x = eigvals(C)
457 | ε = 100*eps()*(I_up-I_lo)*0.5
458 | if I_lo==-1.0 && I_up==1.0
459 | return real(x[(-1-ε .≤ real(x) .≤ 1+ε) .& (imag(x) .≈ 0)])
460 | else
461 | x = real(x[(-1-ε .≤ real(x) .≤ 1+ε) .& (imag(x) .≈ 0)])
462 | return (1.0 .- x).*I_lo/2 + (1.0 .+ x).*I_up/2
463 | end
464 | end
465 |
466 | function endpoints_of_cheb(a) # Input is two-sided Chebyshev
467 | n = length(a)
468 | atm1 = dot((-1).^(0:n-1),a) # endpoint at -1
469 | at1 = sum(a) # endpoint at 1
470 | return atm1, at1
471 | end
472 |
473 | function chebmax(a, I=[-1,1]) # Input is two-sided Chebyshev
474 | M = length(a)
475 | b = chebdiff(a)
476 | x = chebroots(b)
477 | fxc = eval_cheb(a, x)
478 | # k = 0:M-1
479 | # fxc = cos.((Vector(k))' .* acos.(x)) * a
480 | ep = endpoints_of_cheb(a)
481 | fvals = [ep[1];fxc[1:end];ep[2]]
482 | x = [interval(-1);x;interval(1)]
483 | ind = argmax(fvals)
484 | return x[ind], fvals[ind]
485 | end
486 |
487 | function chebmin(a, I=[-1,1]) # Input is two-sided Chebyshev
488 | M = length(a)
489 | b = chebdiff(a)
490 | x = chebroots(b)
491 | fxc = eval_cheb(a, x)
492 | # k = 0:M-1
493 | # fxc = cos.((Vector(k))' .* acos.(x)) * a
494 | ep = endpoints_of_cheb(a)
495 | fvals = [ep[1];fxc[1:end];ep[2]]
496 | x = [interval(-1);x;interval(1)]
497 | ind = argmin(fvals)
498 | return x[ind], fvals[ind]
499 | end
--------------------------------------------------------------------------------
/IntervalFunctions.jl:
--------------------------------------------------------------------------------
1 | ### Interval Matrix Multiplication
2 | function ufp(P)
3 | u = 2.0^(-53);
4 | ϕ = 2.0^52 + 1;
5 | q = ϕ * P;
6 | T = (1 - u)*q;
7 | return abs(q - T);
8 | end
9 |
10 | function succ(c)
11 | s_min = 2.0^-1074;
12 | u = 2.0^-53;
13 | ϕ = u * (1.0 + 2.0 * u);
14 | if abs(c) >= (1. / 2.) * u^(-2) * s_min # 2^(-969)(Float64)
15 | e = ϕ * abs(c);
16 | succ = c + e;
17 | elseif abs(c) < (1. / 2.) * u^(-1) * s_min # 2^(-1022)(Float64)
18 | succ = c + s_min;
19 | else
20 | C = u^(-1) * c;
21 | e = ϕ * abs(C);
22 | succ = (C + e) * u;
23 | end
24 | return succ
25 | end
26 |
27 | function pred(c)
28 | s_min = 2.0^-1074;
29 | u = 2.0^-53;
30 | ϕ = u * (1.0 + 2.0 * u);
31 | if abs(c) >= (1. / 2.) * u^(-2) * s_min # 2^(-969)(Float64)
32 | e = ϕ * abs(c);
33 | pred = c - e;
34 | elseif abs(c) < (1. / 2.) * u^(-1) * s_min # 2^(-1022)(Float64)
35 | pred = c - s_min;
36 | else
37 | C = u^(-1) * c;
38 | e = ϕ * abs(C);
39 | pred = (C - e) * u;
40 | end
41 | return pred
42 | end
43 |
44 | function mm_ufp(A_mid, B_mid) # A_mid, B_mid: Point matrix
45 | u = 2.0^(-53);
46 | realmin = 2.0^(-1022);
47 | n = size(A_mid,2);
48 |
49 | if(2*(n+2)*u>=1)
50 | error("mm_ufp is failed!(2(n+2)u>=1)")
51 | end
52 | # C_mid = A_mid * B_mid;
53 | # C_rad = (n+2) * u * ufp.(abs.(A_mid)*abs.(B_mid)) .+ realmin;
54 | # return C_mid, C_rad;
55 | return A_mid * B_mid, (n+2) * u * ufp.(abs.(A_mid)*abs.(B_mid)) .+ realmin
56 | end
57 |
58 | function imm_ufp(A_mid, A_rad, B_mid, B_rad) # A = , B = : Interval matrix
59 | u = 2.0^(-53);
60 | realmin = 2.0^(-1022);
61 | n = size(A_mid,2);
62 |
63 | if(2*(n+2)*u>=1)
64 | error("mm_ufp is failed!(2(n+2)u>=1)")
65 | end
66 | # C, R = mm_ufp(A_mid,B_mid);
67 | # C_mid = A_mid * B_mid;
68 | R = (n+2) * u * ufp.(abs.(A_mid)*abs.(B_mid)) .+ realmin;
69 |
70 | # T_1, T_2 = mm_ufp(abs.(A_mid), B_rad);
71 | T1 = abs.(A_mid) * B_rad;
72 | T2 = (n+2)*u*ufp.(T1) .+ realmin;
73 |
74 | # T_3 = succ.(abs.(B_mid)+B_rad);
75 | T3 = succ.(abs.(B_mid)+B_rad)
76 |
77 | # T_4, T_5 = mm_ufp(A_r, T_3);
78 | T4 = A_rad * T3;
79 | T5 = (n+2)*u*ufp.(T4) .+ realmin;
80 |
81 | rad_sum = R + T1 + T2 + T4 + T5;
82 |
83 | # C_rad = succ.(rad_sum + 4*u*ufp.(rad_sum));
84 |
85 | # return C_mid, C_rad;
86 | return A_mid * B_mid, succ.(rad_sum + 4*u*ufp.(rad_sum))
87 | end
88 |
89 | # USE IntervalArithmetic.jl
90 | using IntervalArithmetic
91 | function int_mul(A::Matrix{T}, B::Matrix{T}) where T
92 | Cmid, Crad = mm_ufp(A, B);
93 | return interval(Cmid, Crad; format=:midpoint)
94 | # return Cmid .± Crad
95 | end
96 |
97 | function int_mul(A::Matrix{Interval{T}}, B::Matrix{T}) where T
98 | Cmid, Crad = imm_ufp(mid.(A), radius.(A), B, zeros(size(B)));
99 | return interval(Cmid, Crad; format=:midpoint)
100 | # return Cmid .± Crad
101 | end
102 |
103 | function int_mul(A::Matrix{T}, B::Matrix{Interval{T}}) where T
104 | Cmid, Crad = imm_ufp(A, zeros(size(A)), mid.(B), radius.(B));
105 | return interval(Cmid, Crad; format=:midpoint)
106 | # return Cmid .± Crad
107 | end
108 |
109 | function int_mul(A::Matrix{Interval{T}}, B::Matrix{Interval{T}}) where T
110 | Cmid, Crad = imm_ufp(mid.(A), radius.(A), mid.(B), radius.(B));
111 | return interval(Cmid, Crad; format=:midpoint)
112 | # return Cmid .± Crad
113 | end
114 |
115 | function int_mul(A::Matrix{Complex{T}}, B::Matrix{T}) where T
116 | Ar = real.(A); Ai = imag.(A); # (Ar + im*Ai)*B = Ar*B + im*(Ai*B)
117 | return complex.(int_mul(Ar, B), int_mul(Ar, B))
118 | end
119 |
120 | function int_mul(A::Matrix{T}, B::Matrix{Complex{T}}) where T
121 | Br = real.(B); Bi = imag.(B); # A*(Br + im*Bi) = A*Br + im*(A*Bi)
122 | return complex.(int_mul(A, Br), int_mul(A, Bi))
123 | end
124 |
125 | function int_mul(A::Matrix{Complex{T}}, B::Matrix{Complex{T}}) where T
126 | Ar = real.(A); Ai = imag.(A); Br = real.(B); Bi = imag.(B);
127 | # (Ar + im*Ai)*(Br + im*Bi) = (Ar*Br - Ai*Bi) + im*(Ar*Bi + Ai*Br)
128 | return complex.((int_mul(Ar,Br) - int_mul(Ai, Bi)), (int_mul(Ar, Bi) + int_mul(Ai, Br)))
129 | end
130 |
131 |
132 | ### Interval Linear system solver
133 | import LinearAlgebra: opnorm
134 | function LinearAlgebra.opnorm(a::Matrix{Complex{Interval{Float64}}},p::String)
135 | if p == "1"
136 | suma = sum(abs.(a), dims = 1)
137 | return interval(maximum(inf,suma), maximum(sup,suma))
138 | elseif p == "Inf"
139 | suma = sum(abs.(a), dims = 2)
140 | return interval(maximum(inf,suma),maximum(sup,suma))
141 | end
142 | return NaN
143 | end
144 |
145 | function LinearAlgebra.opnorm(a::Matrix{Interval{Float64}},p::String)
146 | if p == "1"
147 | suma = sum(abs.(a), dims = 1)
148 | return interval(maximum(inf,suma), maximum(sup,suma))
149 | elseif p == "Inf"
150 | suma = sum(abs.(a), dims = 2)
151 | return interval(maximum(inf,suma),maximum(sup,suma))
152 | end
153 | return NaN
154 | end
155 |
156 | function verifylss_iB(iA,iB) # verify the solution element-wisely
157 | A = mid.(iA); B = mid.(iB)
158 | X̄ = A\B
159 | n = size(X̄,2)
160 | R = inv(A)
161 | #########
162 | iG = interval(Matrix{Float64}(I, n, n)) - interval(R)*iA
163 | α = opnorm(iG,"Inf")# Interval arithmetic
164 | #########
165 | if sup(α) < 1
166 | η = (abs.(iG)*interval(ones(n)))/(interval(1)-α)
167 | Err = interval(zeros(n,n))
168 | X̄ = interval(X̄)
169 | ir = iA*X̄ - iB # Interval arithmetic
170 | iRr = interval(R)*ir
171 | for i = 1:n
172 | Err[:,i] = abs.(iRr[:,i]) + interval(sup(norm(iRr[:,i],Inf)))*η # Interval arithmetic
173 | end
174 | return interval(X̄, Err, format=:midpoint)
175 | else
176 | println("Oh my way, verification is failed...")
177 | return nan
178 | end
179 | end
180 |
181 |
182 | ### Interval all eigenvalues solver
183 |
184 | function verifyalleig(iA, X)
185 | n = size(iA, 2)
186 | # iD = diagm(interval(λ))
187 | iX = interval(X)
188 | iB = int_mul(iA, iX)
189 | iG = verifylss_iB(iX, iB)
190 | ir = interval(zeros(n))
191 | ic = diag(iG)
192 | for i = 1:n
193 | for j = 1:n
194 | if i != j
195 | ir[i] += interval(mag(iG[i, j]))
196 | end
197 | end
198 | ir[i] += interval(radius(ic[i]))
199 | end
200 | return interval(ic, ir, format=:midpoint)
201 | end
202 |
203 | ### Interval eigen solver (eigpair)
204 |
205 | function verifyeig(iA::Matrix{Interval{T}}, lam, x, B=Matrix{T}(I, size(iA))) where {T<:Real}
206 | if typeof(lam) <: Complex || eltype(x) <: Complex
207 | lam = convert(Complex{T}, lam)
208 | x = convert.(Complex{T}, Vector(x))
209 | else
210 | lam = convert(T, lam)
211 | x = convert.(T, Vector(x))
212 | end
213 | x = x ./ norm(x)
214 | ysize = length(x)
215 | ilam = interval(lam)
216 | ix = interval(x)
217 | iB = interval(B)
218 |
219 | function iDF(w)
220 | mat = (zeros(typeof(ilam), length(w), length(w)))
221 | mat[1, 2:end] = transpose(interval(2) * (ix + w[2:end]))
222 | mat[2:end, 1] = -iB * (ix + w[2:end])
223 | mat[2:end, 2:end] = iA - (ilam + w[1]) * iB
224 | return mat
225 | end
226 | zero = zeros(T, ysize + 1)
227 | R = inv(mid.(iDF(zero)))
228 | iR = interval(R)
229 | iz = -iR * [dot(ix, ix) - interval(1.0); iA * ix - ilam * iB * ix]
230 | ϵ = 2 * sup(norm(iz, 1))
231 | if isreal(lam) && isreal(x)
232 | lam = real(lam)
233 | x = real(x)
234 | id = interval(0, ϵ; format=:midpoint)
235 | iy = interval.(zeros(ysize), ϵ; format=:midpoint)
236 | iI = interval(Matrix{T}(I, ysize + 1, ysize + 1))
237 | else
238 | id = Complex(interval(0, ϵ; format=:midpoint), interval(0, ϵ; format=:midpoint))
239 | iy = Complex.(interval.(zeros(ysize), ϵ; format=:midpoint), interval.(zeros(ysize), ϵ; format=:midpoint))
240 | iI = interval(Matrix{Complex{T}}(I, ysize + 1, ysize + 1))
241 | end
242 | iw = [id; iy]
243 | g(w) = iz + (iI - iR * iDF(w)) * w
244 | gw = g(iw)
245 | if all(issubset_interval.(gw, iw)) #gw .⊂ iw
246 | # while maximum(radius, gw) / norm([lam; x], 1) >= 1e3 * eps(T)
247 | # gw = g(gw)
248 | # end
249 | return ilam + gw[1] #, ix .+ gw[2:end] #固有ベクトルも返すようにする
250 | else
251 | return NaN
252 | end
253 | end
254 |
255 | ### Verify FFT using Interval Arithmetic
256 | function verifyfft(z::Vector{Complex{Interval{T}}}, sign=1) where T
257 | n = length(z); col = 1; array1 = true
258 | if n==1
259 | return z
260 | else
261 | isrow_ = false
262 | end
263 | log2n = Int(round(log2(n))) #check dimension
264 | if 2^log2n ≠ n # return error if n is not the powers of 2
265 | error("length must be power of 2")
266 | end
267 | #bit-reversal
268 | f = 2^(log2n-1)
269 | v = [0;f]
270 | for k = 1:log2n-1
271 | f = f >> 1
272 | v = append!(v,f.+v)
273 | end
274 | z2 = zeros(Complex{Interval{T}},n,col)
275 | # if isa(real(z[1]),Interval)
276 | # z2 = map(Interval{T},z2)
277 | # end
278 | # replace z
279 | for j = 1: n
280 | z2[j,:] = z[v[j]+1,:]
281 | end
282 | #Danielson-Lanczos algorithm
283 | # Z = complex.(interval(z2))
284 | Z = z2
285 | Index = reshape([1:n*col;],n,col)
286 |
287 | theta = sign * (0:n-1)/n; # division exact because n is power of 2
288 | itheta = interval(theta)
289 | Phi = complex.(cospi.(itheta),sinpi.(itheta)) # SLOW?
290 | # Phi = cospi.(theta) + im*sinpi.(theta)
291 |
292 | v = [1:2:n;]
293 | w = [2:2:n;]
294 | t = Z[w,:]
295 | Z[w,:] = Z[v,:] - t
296 | Z[v,:] = Z[v,:] + t
297 | for index in 1: (log2n-1)
298 | m = 2^index
299 | m2 = 2*m
300 | vw = reshape([1:n;],m2,Int(n/m2))
301 | v = vw[1: m, :]
302 | w = vw[m+1: m2, : ]
303 | indexv = reshape(Index[v[:],:],m,Int(col*n/m2))
304 | indexw = reshape(Index[w[:],:],m,Int(col*n/m2))
305 | Phi1 = repeat(Phi[1:Int(n/m):end],outer=[1,Int(col*n/m2)])
306 | t = Phi1 .* Z[indexw]
307 | Z[indexw] = Z[indexv] - t
308 | Z[indexv] = Z[indexv] + t
309 | end
310 | reverse(Z[2:end,:],dims=2)
311 | if sign==-1
312 | Z = Z/interval(n)
313 | end
314 | if isrow_
315 | Z = transpose(Z) #transpose of Z
316 | end
317 | if array1
318 | Z = Z[:,1]
319 | end
320 | return Z
321 | end
322 |
323 | verifyfft(z::Vector{Interval{T}}, sign=1) where {T} = verifyfft(complex.(z), sign)
324 |
325 | ### Rigorous convolution algorithm via FFT
326 | function powerconvfourier(ia::Vector{Complex{Interval{T}}},p) where T
327 | M = Int((length(a)+1)/2) # length(a) = 2M-1
328 | N = (p-1)*M
329 |
330 | length_ia = 2*p*M-1
331 | length_ia_ext = nextpow(2,length_ia)# 2pM-2+2L
332 |
333 | L = Int((length_ia_ext - length_ia + 1)/2)
334 |
335 | # step.1 : padding (p-1)M + L zeros for each sides
336 | ia_ext = interval(zeros(Complexlength_ia_ext))
337 | ia_ext[L+N+1:end-L-N+1] = ia #\tilda{a}
338 |
339 | # step.2 : inverse fft
340 | ib_ext = verifyfft(ifftshift(ia_ext), -1) #sign = -1 : ifft
341 |
342 | # step.3 : power p elementwisely
343 | ib_extᵖ = ib_ext.^p
344 |
345 | # step.4 : fft with rescaling
346 | ic_extᵖ = fftshift(verifyfft(ib_extᵖ, 1)) * length_ia_ext^(p-1) #sign = 1 : fft
347 |
348 | # return ic_extᵖ,ic_extᵖ
349 | return ic_extᵖ[L+N+1:end-N-L+1], ic_extᵖ[L+p:end-(L+p-2)] # return (truncated, full) version
350 | end
351 |
352 |
353 | function convfourier(ia::Vector{Complex{Interval{T}}}...) where {T<:Real}
354 | p = length(ia)
355 | M = Int((length(ia[1])+1)/2) # length(a) = 2M-1
356 | N = (p-1)*M
357 |
358 | length_ia = 2*p*M-1
359 | length_ia_ext = nextpow(2,length_ia)# 2pM-2+2L
360 |
361 | # itbᵖ = ones(Interval,N+length(ia[1])+N)
362 | # ibp_ext = map(Complex{Interval},ones(length_ia_ext))
363 | ibp_ext = interval(ones(ComplexF64, length_ia_ext))
364 |
365 | L = Int((length_ia_ext - length_ia + 1)/2)
366 |
367 | for i = 1:p
368 | # step.1 : padding (p-1)M + L zeros for each sides
369 | # ia_ext = map(Complex{Interval},zeros(length_ia_ext))
370 | ia_ext = interval(zeros(ComplexF64,length_ia_ext))
371 | ia_ext[L+N+1:end-L-N+1] = ia[i] #\tilda{a}
372 | # step.2 : inverse fft
373 | ib_ext = verifyfft(ifftshift(ia_ext), -1) #sign = -1 : ifft
374 | # step.3 : power p elementwisely
375 | ibp_ext = ibp_ext .* ib_ext
376 | # ib_extᵖ = ibp_ext.^p
377 | end
378 | # step.4 : fft with rescaling
379 | ic_extᵖ = fftshift(verifyfft(ibp_ext, 1)) * interval(length_ia_ext)^interval(p-1) #sign = 1 : fft
380 | return ic_extᵖ[L+N+1:end-N-L+1], ic_extᵖ[L+p:end-(L+p-2)] # return (truncated, full) version
381 | end
382 |
383 | function convcos(ia::Vector{Interval{T}}...) where {T<:Real}# Input: Two-sided (real)
384 | M = length(ia[1])
385 | FourierCoeffs = []
386 | for i = 1:length(ia)
387 | ia[i][1] = ia[i][1]; ia[i][2:end] = interval(0.5) * ia[i][2:end] # Two-sided -> One-sided (real)
388 | FC_local = map(Complex,[reverse(ia[i][2:end]); ia[i]])
389 | if i==1
390 | FourierCoeffs = tuple(FC_local)
391 | else
392 | FourierCoeffs = tuple(FourierCoeffs...,tuple(FC_local)...)
393 | end
394 | end
395 | icp, icp_full = convfourier(FourierCoeffs...) # real -> complex (input)
396 | iap = interval(zeros(M))
397 | iap[1] = real(icp[M]); iap[2:end] = interval(2) * real(icp[M+1:end]) # One-sided (complex) -> Two-sided (real)
398 | N = Int((length(icp_full)+1)/2) #2N-1
399 | iap_full = interval(zeros(N))
400 | iap_full[1] = real(icp_full[N]); iap_full[2:end] = interval(2) * real(icp_full[N+1:end]) # One-sided (complex) -> Two-sided (real)
401 | return iap, iap_full
402 | end
403 |
--------------------------------------------------------------------------------
/Parallel_test.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "42e30b81",
7 | "metadata": {},
8 | "outputs": [
9 | {
10 | "data": {
11 | "text/plain": [
12 | "1"
13 | ]
14 | },
15 | "execution_count": 1,
16 | "metadata": {},
17 | "output_type": "execute_result"
18 | }
19 | ],
20 | "source": [
21 | "Threads.nthreads()"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": 3,
27 | "id": "55d9bd17-807b-4ae1-b5e4-a2e8df7e3a47",
28 | "metadata": {},
29 | "outputs": [
30 | {
31 | "data": {
32 | "text/plain": [
33 | "f (generic function with 1 method)"
34 | ]
35 | },
36 | "metadata": {},
37 | "output_type": "display_data"
38 | }
39 | ],
40 | "source": [
41 | "f(x) = sin(x)"
42 | ]
43 | },
44 | {
45 | "cell_type": "code",
46 | "execution_count": 4,
47 | "id": "98b36d30-b652-4b73-bc68-3efacdc653f3",
48 | "metadata": {},
49 | "outputs": [
50 | {
51 | "data": {
52 | "text/plain": [
53 | "100000000-element Vector{Float64}:\n",
54 | " 0.6488815218670269\n",
55 | " -0.38905042331697814\n",
56 | " 1.4113533889757908\n",
57 | " 0.6293867097580864\n",
58 | " -0.4965669693794891\n",
59 | " -0.18951588049230653\n",
60 | " 0.02251079629401766\n",
61 | " -1.688288353986386\n",
62 | " 0.9979018708880366\n",
63 | " 0.6860993643250246\n",
64 | " ⋮\n",
65 | " 1.7176179792770403\n",
66 | " 0.5861549943154972\n",
67 | " 1.5844221997479975\n",
68 | " -1.3421998667134407\n",
69 | " -0.24091986519785483\n",
70 | " -1.2250387660791364\n",
71 | " -0.17431680682853287\n",
72 | " -0.6718145936834968\n",
73 | " 2.865945481034396"
74 | ]
75 | },
76 | "metadata": {},
77 | "output_type": "display_data"
78 | }
79 | ],
80 | "source": [
81 | "x = randn(100000000)"
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": 6,
87 | "id": "b68418a1-e2d9-4fc4-aedb-22cf9fe5dd11",
88 | "metadata": {},
89 | "outputs": [
90 | {
91 | "name": "stdout",
92 | "output_type": "stream",
93 | "text": [
94 | " 1.016487 seconds (3 allocations: 762.940 MiB, 2.04% gc time)\n"
95 | ]
96 | }
97 | ],
98 | "source": [
99 | "@time f1 = f.(x);"
100 | ]
101 | },
102 | {
103 | "cell_type": "code",
104 | "execution_count": 7,
105 | "id": "c69c4da8-7f36-4999-abac-b0a404e2581d",
106 | "metadata": {},
107 | "outputs": [
108 | {
109 | "data": {
110 | "text/plain": [
111 | "test_threads (generic function with 1 method)"
112 | ]
113 | },
114 | "metadata": {},
115 | "output_type": "display_data"
116 | }
117 | ],
118 | "source": [
119 | "function test_threads(f,x)\n",
120 | " fval = similar(x)\n",
121 | " Threads.@threads for i = 1:length(x)\n",
122 | " fval[i] = f(x[i])\n",
123 | " end\n",
124 | " return fval\n",
125 | "end"
126 | ]
127 | },
128 | {
129 | "cell_type": "code",
130 | "execution_count": 9,
131 | "id": "8b7528e3-37a8-47f6-8a90-ce56287378a3",
132 | "metadata": {},
133 | "outputs": [
134 | {
135 | "name": "stdout",
136 | "output_type": "stream",
137 | "text": [
138 | " 0.408296 seconds (25 allocations: 762.942 MiB)\n"
139 | ]
140 | }
141 | ],
142 | "source": [
143 | "@time f2 = test_threads(f,x);"
144 | ]
145 | },
146 | {
147 | "cell_type": "code",
148 | "execution_count": 10,
149 | "id": "2b1ced4a-f637-431d-ac34-7e6aab13d949",
150 | "metadata": {},
151 | "outputs": [],
152 | "source": [
153 | "using Distributed\n",
154 | "addprocs()\n",
155 | "@everywhere f(x) = sin(x)\n",
156 | "using SharedArrays"
157 | ]
158 | },
159 | {
160 | "cell_type": "code",
161 | "execution_count": 11,
162 | "id": "0d4b3e4f-a981-4cd6-8edc-a3a3511092c2",
163 | "metadata": {},
164 | "outputs": [
165 | {
166 | "data": {
167 | "text/plain": [
168 | "test_process (generic function with 1 method)"
169 | ]
170 | },
171 | "metadata": {},
172 | "output_type": "display_data"
173 | }
174 | ],
175 | "source": [
176 | "function test_process(f,x)\n",
177 | " n = length(x)\n",
178 | " fval = SharedArray{Float64}(n)\n",
179 | " @distributed for i = 1:n\n",
180 | " fval[i] = f(x[i])\n",
181 | " end\n",
182 | " return fval\n",
183 | "end\n",
184 | "# fval"
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": 17,
190 | "id": "863b8abb-8708-4507-8205-9771f07288cf",
191 | "metadata": {},
192 | "outputs": [
193 | {
194 | "name": "stdout",
195 | "output_type": "stream",
196 | "text": [
197 | " 0.001549 seconds (349 allocations: 20.352 KiB)\n"
198 | ]
199 | }
200 | ],
201 | "source": [
202 | "@time f4 = test_process(f,x);"
203 | ]
204 | },
205 | {
206 | "cell_type": "code",
207 | "execution_count": 18,
208 | "id": "f9b85025-9ea1-4c22-b28a-d76d72b68e81",
209 | "metadata": {},
210 | "outputs": [
211 | {
212 | "data": {
213 | "text/plain": [
214 | "0.0"
215 | ]
216 | },
217 | "metadata": {},
218 | "output_type": "display_data"
219 | }
220 | ],
221 | "source": [
222 | "using LinearAlgebra\n",
223 | "norm(f1 - f4)"
224 | ]
225 | }
226 | ],
227 | "metadata": {
228 | "kernelspec": {
229 | "display_name": "Julia 1.10.0",
230 | "language": "julia",
231 | "name": "julia-1.10"
232 | },
233 | "language_info": {
234 | "file_extension": ".jl",
235 | "mimetype": "application/julia",
236 | "name": "julia",
237 | "version": "1.10.0"
238 | }
239 | },
240 | "nbformat": 4,
241 | "nbformat_minor": 5
242 | }
243 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Julia言語を使った精度保証付き数値計算のチュートリアル
2 |
3 | 精度保証付き数値計算の標準的な方法をJulia言語を用いて紹介する。
4 |
5 | # 目次
6 |
7 | - はじめに -間違える数値計算-([nonrigorous_numerics.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/nonrigorous_numerics.html))
8 | - 整数・浮動小数点数([floating-point.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/floating-point.html))
9 | - 丸め誤差・その他の誤差([rounding-error.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/rounding-error.html))
10 | - 区間演算 -精度保証付き数値計算の入り口-([interval-arithmetic.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/interval-arithmetic.html))
11 | - ベクトルの内積・行列ベクトル積・行列積の区間演算([interval_dot-mul.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/interval_dot-mul.html))
12 | - 線型方程式の解の精度保証付き数値計算([verifylss.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifylss.html))
13 | - 標準固有値問題の精度保証付き数値解法([verifyalleig.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifyalleig.html))
14 | - 部分固有対の精度保証付き数値計算([verifyeig.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifyeig.html))
15 | - 高速フーリエ変換の精度保証([verifyfft.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifyfft.html))
16 | - 非線形方程式([verifynlss.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifynlss.html))
17 | - Newton-Kantorovich型定理 (radii-polynomial approach)([Newton-Kantorovich.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Newton-Kantorovich.html))
18 | - フーリエ級数(計算機で表現する方法)([Fourier_series.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Fourier_series.html))
19 | - 離散畳み込みの精度保証付き数値計算([discrete_convolution.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/discrete_convolution.html))
20 | - フーリエ・スペクトル法による常微分方程式の周期解の数値計算([Fourier_spectral_PO.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Fourier_spectral_PO.html))
21 | - 常微分方程式の周期解の数値検証([verifyPO.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifyPO.html))
22 | - チェビシェフ級数([Chebyshev_series.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Chebyshev_series.html))
23 | - Clenshawのアルゴリズム([Clenshaw.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Clenshaw.html))
24 | - Chebyshev補間の微分([Chebdiff.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Chebdiff.html))
25 | - Chebyshev補間の積分([Chebint.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Chebint.html))
26 | - Chebyshev補間の求根([Chebroot.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Chebroot.html))
27 | - Chebyshev補間の最大・最小値([Chebminmax.ipynb](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/Chebminmax.html))
28 | -
29 | etc.
30 |
31 | # 構成ファイルの説明(.jlファイルだけ。途中でincludeして使用している)
32 |
33 | - `FourierChebyshev.jl`: フーリエ級数・チェビシェフ級数を扱う関数が記述されている(区間演算なし)。
34 | - `IntervalFunctions.jl`: 丸め向きを変更しない区間行列積(`int_mul`)や区間演算を使ったFFTの`verifyfft`、FFTを使った離散畳み込みの計算(`powerconvfourier`)が実装されている。入力のタイプによって区間行列積の場合分けをしている。
35 | - 今後、区間連立一次方程式、非線形方程式などを適宜追加予定。
36 |
37 | ## 筆者
38 |
39 | 本資料は、[高安研究室](http://www.taklab.org/)のゼミ資料として作成しています。これまで貢献した人々は以下の通りです。
40 |
41 | - [舩越康太](https://github.com/2754github)
42 | - 井藤佳奈子
43 | - 大谷俊輔
44 | - 近藤慎佑
45 | - 高橋和暉
46 | - 瀬戸翔太
47 | - 二平泰知
48 | - [高安亮紀](https://www.risk.tsukuba.ac.jp/~takitoshi/)
49 |
50 | ### gitの基本操作(内部向け)
51 |
52 | 1. `git pull`
53 | 2. (各自ファイルを変更)
54 | 3. `git add (編集したファイル名)`
55 | 4. `git commit -m "コミットメッセージ"`
56 | 5. `git push`
57 |
--------------------------------------------------------------------------------
/floating-point.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 浮動小数点数\n",
8 | "計算機で扱える数「整数」と「浮動小数点数」について解説する。\n",
9 | "\n",
10 | "## そもそも数値計算とは?\n",
11 | "* 数値計算(数値解析)とは…数学の問題を有限桁の浮動小数点数を使って「数値的」に解く.数式処理とは違う\n",
12 | "* 数値計算でできること,できないこと\n",
13 | "* 数値計算は誤差との戦い\n",
14 | "* 整数(integer)と浮動小数点数(binary32/binary64)\n",
15 | "\n",
16 | "## 整数型\n",
17 | "Juliaで整数は\n",
18 | "\n",
19 | "|型| 機械内表現 |\n",
20 | "|:-----------|:----------|\n",
21 | "| Int8 | 8bit整数 |\n",
22 | "| Int16 | 16bit整数 |\n",
23 | "| Int32 | 32bit整数 |\n",
24 | "| Int64 | 64bit整数 |\n",
25 | "| Int128 | 128bit整数 |\n",
26 | "| BigInt | 任意精度 |\n",
27 | "\n",
28 | "\n",
29 | "のような数値がある."
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": 1,
35 | "metadata": {
36 | "tags": []
37 | },
38 | "outputs": [
39 | {
40 | "name": "stdout",
41 | "output_type": "stream",
42 | "text": [
43 | "Julia Version 1.11.5\n",
44 | "Commit 760b2e5b739 (2025-04-14 06:53 UTC)\n",
45 | "Build Info:\n",
46 | " Official https://julialang.org/ release\n",
47 | "Platform Info:\n",
48 | " OS: macOS (arm64-apple-darwin24.0.0)\n",
49 | " CPU: 8 × Apple M2\n",
50 | " WORD_SIZE: 64\n",
51 | " LLVM: libLLVM-16.0.6 (ORCJIT, apple-m2)\n",
52 | "Threads: 4 default, 0 interactive, 2 GC (on 4 virtual cores)\n",
53 | "Environment:\n",
54 | " JULIA_NUM_THREADS = 4\n"
55 | ]
56 | }
57 | ],
58 | "source": [
59 | "versioninfo()"
60 | ]
61 | },
62 | {
63 | "cell_type": "code",
64 | "execution_count": 2,
65 | "metadata": {},
66 | "outputs": [
67 | {
68 | "data": {
69 | "text/plain": [
70 | "10"
71 | ]
72 | },
73 | "metadata": {},
74 | "output_type": "display_data"
75 | }
76 | ],
77 | "source": [
78 | "a = Int32(10)"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": 3,
84 | "metadata": {},
85 | "outputs": [
86 | {
87 | "data": {
88 | "text/plain": [
89 | "-2147483648"
90 | ]
91 | },
92 | "metadata": {},
93 | "output_type": "display_data"
94 | }
95 | ],
96 | "source": [
97 | "b = Int32(2147483647) + Int32(1)"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": 4,
103 | "metadata": {},
104 | "outputs": [
105 | {
106 | "data": {
107 | "text/plain": [
108 | "2147483647"
109 | ]
110 | },
111 | "metadata": {},
112 | "output_type": "display_data"
113 | }
114 | ],
115 | "source": [
116 | "c = typemax(Int32)"
117 | ]
118 | },
119 | {
120 | "cell_type": "markdown",
121 | "metadata": {},
122 | "source": [
123 | "これからInt32の最大値が2147483647であることがわかり\n",
124 | "$2^{31}-1=2147483647$\n",
125 | "である.\n",
126 | "\n",
127 | "説明のために32bit整数(Int32)を考えると\n",
128 | "\n",
129 | "|ビットパターン | 数値 |\n",
130 | "|:-----------:|:----------:|\n",
131 | "|01111111111111111111111111111111 | 2147483647|\n",
132 | "|00000000000000000000000000000010 | 2|\n",
133 | "|00000000000000000000000000000001 | 1|\n",
134 | "|00000000000000000000000000000000 | 0|\n",
135 | "|11111111111111111111111111111111 | -1|\n",
136 | "|11111111111111111111111111111110 |-2|\n",
137 | "|10000000000000000000000000000000 |-2147483648|\n",
138 | "\n",
139 | "このような負の数の表現形式を「**2の補数形式**」と呼ぶ.\n",
140 | "32個の各bitが次のような重みをもっていると考えられる.\n",
141 | "\n",
142 | "$$\\fbox{$-2^{31}$}\\fbox{$2^{30}$}\\fbox{$2^{29}$}\\,\\cdots\\fbox{$2^{1}$}\\fbox{$2^{0}$}$$\n",
143 | "\n",
144 | "2の補数形式の場合,$n$ビットで $-2^{n-1}$〜$2^{n-1}-1$の範囲の数を表現できる.つまり\n",
145 | "\n",
146 | "|型|表現範囲|\n",
147 | "|:-|:-:|\n",
148 | "|Int8|-128〜127|\n",
149 | "|Int16|-32768〜32767|\n",
150 | "|Int32|-2147483648〜2147483647|\n",
151 | "|Int64|-9223372036854775808〜9223372036854775807|\n",
152 | "|Int128|-170141183460469231731687303715884105728〜170141183460469231731687303715884105727|\n",
153 | "\n",
154 | "の範囲の整数が表せる.\n"
155 | ]
156 | },
157 | {
158 | "cell_type": "code",
159 | "execution_count": 5,
160 | "metadata": {},
161 | "outputs": [
162 | {
163 | "data": {
164 | "text/plain": [
165 | "2147483648"
166 | ]
167 | },
168 | "metadata": {},
169 | "output_type": "display_data"
170 | }
171 | ],
172 | "source": [
173 | "2147483647+1"
174 | ]
175 | },
176 | {
177 | "cell_type": "code",
178 | "execution_count": 6,
179 | "metadata": {},
180 | "outputs": [
181 | {
182 | "data": {
183 | "text/plain": [
184 | "-9223372036854775808"
185 | ]
186 | },
187 | "metadata": {},
188 | "output_type": "display_data"
189 | }
190 | ],
191 | "source": [
192 | "9223372036854775807+1"
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "metadata": {},
198 | "source": [
199 | "## 浮動小数点数\n",
200 | "\n",
201 | "**浮動小数点数**(Float64/double, Float32/float)は,「浮動小数点形式」と呼ばれる形式で表現できる.\n",
202 | "\n",
203 | "例えば,「$1234.5$」を「$1.2345\\times 10^3$」のように, 小数点の位置を1番左の数値と左から2番目の数値の間に移動(「正規化」と呼ぶ)し,それに指数を掛けた形式で数を表現する.\n",
204 | "この「$1.2345$」の部分を「仮数部」,「$10^3$」の部分(厳密には$~^3$)を「指数部」という.\n",
205 | "\n",
206 | "浮動小数点数は仮数部の長さ,指数部の長さ,基数が2,10,16など,多様な規格が考えられる.\n",
207 | "そこで1985年にWilliam Kahanが中心となって\n",
208 | "\n",
209 | "[IEEE 754: Standard for Binary Floating-Point Arithmetic](https://ja.wikipedia.org/wiki/IEEE_754)\n",
210 | "\n",
211 | "という標準規格が制定された.最近では世に出るハードウェアのほぼ全てがこの規格に従っている.\n",
212 | "\n",
213 | "### 倍精度 (Float64/double, binary64)\n",
214 | "倍精度は,符号($\\pm$)に1ビット.指数部に11ビット,仮数部に 52ビットを使う.\n",
215 | "全部で64ビット=8バイトである.\n",
216 | "\n",
217 | "$\\fbox{1(符号)}\\fbox{11(指数部)}\\fbox{52(仮数部)}$\n",
218 | "\n",
219 | "* 符号は,0なら正,1なら負\n",
220 | "* 指数部は「$\\times 2^{{指数}}$」の指数の部分に$1023$を加えたものが11ビット符号無しの整数の形で格納されている\n",
221 | "* 仮数部は,実際の仮数部の先頭の「1」を取り除いた残りが格納されている.仮数部の先頭は必ず1にしてメモリに格納しないことで1ビット分精度を稼いでいる\n",
222 | "\n",
223 | "数値$x$は\n",
224 | "\n",
225 | "$$x=\\pm\\; 1.d_1d_2\\cdots d_{52}\\times 2^{m}=\\pm\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\frac{d_2}{2^2}+\\cdots+\\frac{d_{52}}{2^{52}}\\right)2^{e}_{(10)}$$\n",
226 | "\n",
227 | "と書ける($-1022\\le e\\le 1023$,$m$:$e+1023$の2進表現).\n",
228 | "\n",
229 | "例えば,5.25は2進数で書くと\n",
230 | "$$\n",
231 | "\t101.01_{(2)} = \\left(\\frac{1}{2^0}+\\frac{0}{2^1}+\\frac{1}{2^2}+\\frac{0}{2^{3}}+\\frac{1}{2^4}\\right)\\times 2^2_{(10)}\n",
232 | "$$\n",
233 | "であるから,計算機内では\n",
234 | "\n",
235 | "$\\fbox{0}\\fbox{10000000001}\\fbox{0101000000000000000000000000000000000000000000000000}$\n",
236 | "\n",
237 | "のように格納されている。指数部の「1000000001」は、「2+1023=1025」 を2進数にしたもの.\n",
238 | "\n",
239 | "これは**正規化数**と呼ばれる数の範囲.\n",
240 | "\n",
241 | "実際にJuliaのbitstring関数を使って浮動小数点数を見てみよう."
242 | ]
243 | },
244 | {
245 | "cell_type": "code",
246 | "execution_count": 7,
247 | "metadata": {
248 | "tags": []
249 | },
250 | "outputs": [
251 | {
252 | "name": "stdout",
253 | "output_type": "stream",
254 | "text": [
255 | "0100000000010101000000000000000000000000000000000000000000000000\n",
256 | "0100000000010100111101011100001010001111010111000010100011110110\n"
257 | ]
258 | }
259 | ],
260 | "source": [
261 | "x = 5.25\n",
262 | "println(bitstring(x))\n",
263 | "println(bitstring(5.24))"
264 | ]
265 | },
266 | {
267 | "cell_type": "code",
268 | "execution_count": 8,
269 | "metadata": {},
270 | "outputs": [
271 | {
272 | "name": "stdout",
273 | "output_type": "stream",
274 | "text": [
275 | "0\n",
276 | "10000000001\n",
277 | "0101000000000000000000000000000000000000000000000000\n"
278 | ]
279 | }
280 | ],
281 | "source": [
282 | "binx = bitstring(x) \n",
283 | "sign = binx[1]; println(sign)\n",
284 | "exp = binx[2:12]; println(exp)\n",
285 | "frac = binx[13:end]; println(frac)"
286 | ]
287 | },
288 | {
289 | "cell_type": "markdown",
290 | "metadata": {},
291 | "source": [
292 | "符号はsign = 0で正の数, 指数部はexp-1023=1025-1023=**2**, \n",
293 | "仮数部はfrac=(101000000000000000000000000000000000000000000000000)$_2$で2進数表示され,実際の値は**1.3125**, \n",
294 | "元の浮動小数点数に戻すと\n",
295 | "\n",
296 | "$$(-1)^{\\mathrm{sign}}*\\mathrm{frac2}*2^{\\mathrm{exp}-1023}=5.25$$\n",
297 | "\n",
298 | "で次のように確かに元の数に戻ることがわかる."
299 | ]
300 | },
301 | {
302 | "cell_type": "code",
303 | "execution_count": 9,
304 | "metadata": {},
305 | "outputs": [
306 | {
307 | "name": "stdout",
308 | "output_type": "stream",
309 | "text": [
310 | "1.3125\n",
311 | "2\n",
312 | "5.25\n"
313 | ]
314 | }
315 | ],
316 | "source": [
317 | "aa = [parse(Int, f) for f in frac];\n",
318 | "bb = 2. .^(1:52);\n",
319 | "dfrac = 1 + sum(aa ./ bb); println(dfrac)\n",
320 | "cc = [parse(Int, f) for f in exp]\n",
321 | "dexp = sum(cc .* (2 .^(10:-1:0))) - 1023; println(dexp)\n",
322 | "x = (-1)^(parse(Int,sign))*dfrac*2^(dexp); println(x)"
323 | ]
324 | },
325 | {
326 | "cell_type": "markdown",
327 | "metadata": {},
328 | "source": [
329 | "次に$e$の範囲$-1022\\le e\\le 1023$に注目する.\n",
330 | "\n",
331 | "$e+1023$が11ビット符号無しの整数なら範囲は$0$〜$2047$であるが,$e$範囲から\n",
332 | "$e+1023=0$のときと$e+1023=2047$のときが使われていない.\n",
333 | "\n",
334 | "これらは**特殊な数を表す**のに使われる.それらは\n",
335 | "\n",
336 | "* 零\n",
337 | "* 無限大 (Inf)\n",
338 | "* NaN (Not a Number)\n",
339 | "* 非正規化数\n",
340 | "\n",
341 | "と呼ばれる.\n",
342 | "\n",
343 | "#### 零\n",
344 | "指数部が$e+1023=0$かつ仮数部が0のとき.\n",
345 | "\n",
346 | "$$\\pm\\; 0.00\\cdots 0\\times 2^{0}={\\pm\\left(\\frac{0}{2^0}+\\frac{0}{2^1}+\\frac{0}{2^2}+\\cdots+\\frac{0}{2^{52}}\\right)2^{-1023}}_{(10)}.$$\n",
347 | "\n",
348 | "\n"
349 | ]
350 | },
351 | {
352 | "cell_type": "code",
353 | "execution_count": 10,
354 | "metadata": {},
355 | "outputs": [
356 | {
357 | "name": "stdout",
358 | "output_type": "stream",
359 | "text": [
360 | "0.0\n",
361 | "0000000000000000000000000000000000000000000000000000000000000000\n",
362 | "-0.0\n",
363 | "1000000000000000000000000000000000000000000000000000000000000000\n"
364 | ]
365 | },
366 | {
367 | "data": {
368 | "text/plain": [
369 | "(nothing, nothing)"
370 | ]
371 | },
372 | "metadata": {},
373 | "output_type": "display_data"
374 | }
375 | ],
376 | "source": [
377 | "x = 0.0; println(x), println(bitstring(x))\n",
378 | "x = -0.0; println(x), println(bitstring(x))"
379 | ]
380 | },
381 | {
382 | "cell_type": "markdown",
383 | "metadata": {},
384 | "source": [
385 | "#### 無限大\n",
386 | "$e+1023=2047$かつ仮数部が0のとき,$\\pm\\infty$を表す.\n",
387 | "\n",
388 | "$$\\pm\\; 1.00\\cdots 0\\times 2^{m}={\\pm\\left(\\frac{1}{2^0}+\\frac{0}{2^1}+\\frac{0}{2^2}+\\cdots+\\frac{0}{2^{52}}\\right)2^{1024}}_{(10)},$$\n",
389 | "\n",
390 | "$m$: 2047の2進表現. このとき**オーバーフロー**が起こるという."
391 | ]
392 | },
393 | {
394 | "cell_type": "code",
395 | "execution_count": 11,
396 | "metadata": {},
397 | "outputs": [
398 | {
399 | "name": "stdout",
400 | "output_type": "stream",
401 | "text": [
402 | "Inf\n",
403 | "0111111111110000000000000000000000000000000000000000000000000000\n",
404 | "-Inf\n",
405 | "1111111111110000000000000000000000000000000000000000000000000000\n"
406 | ]
407 | },
408 | {
409 | "data": {
410 | "text/plain": [
411 | "(nothing, nothing)"
412 | ]
413 | },
414 | "metadata": {},
415 | "output_type": "display_data"
416 | }
417 | ],
418 | "source": [
419 | "x = Inf; println(x), println(bitstring(x))\n",
420 | "println(-x), println(bitstring(-x))"
421 | ]
422 | },
423 | {
424 | "cell_type": "markdown",
425 | "metadata": {},
426 | "source": [
427 | "#### NaN (Not a Number)\n",
428 | "$e+1023=2047$かつ仮数部が0でないとき.\n",
429 | "\n",
430 | "負数の平方根など,不可能な演算の結果を表すのに使われる."
431 | ]
432 | },
433 | {
434 | "cell_type": "code",
435 | "execution_count": 12,
436 | "metadata": {},
437 | "outputs": [
438 | {
439 | "name": "stdout",
440 | "output_type": "stream",
441 | "text": [
442 | "0111111111110000000000000000000000000000000000000000000000000000\n",
443 | "NaN\n",
444 | "0111111111111000000000000000000000000000000000000000000000000000\n",
445 | "NaN\n",
446 | "0111111111111000000000000000000000000000000000000000000000000000\n",
447 | "NaN\n",
448 | "0111111111111000000000000000000000000000000000000000000000000000\n"
449 | ]
450 | },
451 | {
452 | "data": {
453 | "text/plain": [
454 | "(nothing, nothing)"
455 | ]
456 | },
457 | "metadata": {},
458 | "output_type": "display_data"
459 | }
460 | ],
461 | "source": [
462 | "x = Inf; println(bitstring(x))\n",
463 | "x = NaN; println(x), println(bitstring(x))\n",
464 | "x = Inf - Inf; println(x), println(bitstring(x))\n",
465 | "x = Inf / Inf; println(x), println(bitstring(x))"
466 | ]
467 | },
468 | {
469 | "cell_type": "markdown",
470 | "metadata": {},
471 | "source": [
472 | "\n",
473 | "#### 正規化数の最大最小\n",
474 | "正の正規化数の最大の数は,$e+1023=2046$, かつ仮数部のビットが全て1のとき.\n",
475 | "\n",
476 | "$${\\left(\\frac{{1}}{2^0}+\\frac{1}{2^1}+\\frac{1}{2^2}+\\cdots+\\frac{1}{2^{52}}\\right)2^{{1023}}}_{(10)}= 2^{1024}-2^{971}\\approx10^{308.25}$$\n",
477 | "\n",
478 | "であり,これを1ビットでも超えたら無限大になる.これを**オーバーフロー**という.\n",
479 | "\n"
480 | ]
481 | },
482 | {
483 | "cell_type": "code",
484 | "execution_count": 13,
485 | "metadata": {},
486 | "outputs": [
487 | {
488 | "name": "stdout",
489 | "output_type": "stream",
490 | "text": [
491 | "1.7976931348623157e308\n",
492 | "0111111111101111111111111111111111111111111111111111111111111111\n",
493 | "Inf\n",
494 | "0111111111110000000000000000000000000000000000000000000000000000\n"
495 | ]
496 | }
497 | ],
498 | "source": [
499 | "x = (2. ^ 53 - 1) * 2. ^ 971\n",
500 | "println(x)\n",
501 | "println(bitstring(x))\n",
502 | "x = (2. ^ 53) * 2. ^ 971\n",
503 | "println(x)\n",
504 | "println(bitstring(x))"
505 | ]
506 | },
507 | {
508 | "cell_type": "markdown",
509 | "metadata": {},
510 | "source": [
511 | "正の正規化数の最小の数は,$e+1023=1$, かつ仮数部のビットが全て0のとき.\n",
512 | "\n",
513 | "$${\\left(\\frac{{1}}{2^0}+\\frac{0}{2^1}+\\frac{0}{2^2}+\\cdots+\\frac{0}{2^{52}}\\right)2^{-1022}}_{(10)}= 2^{-1022}\\approx10^{-307.65}$$\n",
514 | "\n",
515 | "であり,これを下回ると**アンダーフロー**というが,IEEE 754 では,ここでアンダーフローさせないで仮数部を使った「悪あがき」をする.次の例を見てみよう."
516 | ]
517 | },
518 | {
519 | "cell_type": "code",
520 | "execution_count": 14,
521 | "metadata": {},
522 | "outputs": [
523 | {
524 | "name": "stdout",
525 | "output_type": "stream",
526 | "text": [
527 | "2.2250738585072014e-308\n",
528 | "0000000000010000000000000000000000000000000000000000000000000000\n",
529 | "2.225073858507201e-308\n",
530 | "0000000000001111111111111111111111111111111111111111111111111111\n"
531 | ]
532 | }
533 | ],
534 | "source": [
535 | "x = 2. ^ (-1022)\n",
536 | "println(x)\n",
537 | "println(bitstring(x))\n",
538 | "y = x-2^(-1074);\n",
539 | "println(y)\n",
540 | "println(bitstring(y))"
541 | ]
542 | },
543 | {
544 | "cell_type": "markdown",
545 | "metadata": {},
546 | "source": [
547 | "#### 非正規化数\n",
548 | "指数部が$e+1023=0$かつ仮数部が0でないとき,\n",
549 | "\n",
550 | "仮数部の最初の桁を$0$にして\n",
551 | "\n",
552 | "$$\\pm\\; 0.d_1d_2\\cdots d_{52}\\times 2^{0}={\\pm\\left(\\frac{\\color{red}0}{2^0}+\\frac{d_1}{2^1}+\\frac{d_2}{2^2}+\\cdots+\\frac{d_{52}}{2^{52}}\\right)2^{{\\color{red}{-1022}}}}_{(10)}.$$\n",
553 | "\n",
554 | "という数の表現をする.つまり指数部が$e+1023=1$よりも小さくなったら、正規化しないで指数部は$e+1023=1$のままにする. 代わりに仮数部の最初の桁を$0$にする.\n",
555 | "このような決まりによって, 非正規化数は文字通り「正規化していない」数となる.\n",
556 | "\n",
557 | "#### 漸近アンダーフロー\n",
558 | "上の例のように正規化数の最小数の最終bitを1だけ減らすと\n",
559 | "\n",
560 | "$${\\left(\\frac{{0}}{2^0}+\\frac{1}{2^1}+\\frac{1}{2^2}+\\cdots+\\frac{1}{2^{52}}\\right)2^{-1022}}_{(10)}$$\n",
561 | "\n",
562 | "となり,これを正規化すると\n",
563 | "\n",
564 | "$${\\left(\\frac{{1}}{2^0}+\\frac{1}{2^1}+\\frac{1}{2^2}+\\cdots+\\frac{0}{2^{52}}\\right)2^{-1023}}_{(10)}$$\n",
565 | "\n",
566 | "となって,指数部の下限$-1022\\le e$を超えてしまう.\n",
567 | "そこで,「$2^{-1022}$を下回ったら正規化をやめて指数部を$2^{-1022}$に固定して仮数部の最初の桁を0としてみて格納する」ルールが発動し, 非正規化数での数の表現が始まる.\n",
568 | "以下, 1ビットずつ減らしていくと\n",
569 | "\n",
570 | "$${\\left(\\frac{{0}}{2^0}+\\frac{1}{2^1}+\\frac{1}{2^2}+\\cdots+\\frac{1}{2^{52}}\\right)2^{-1022}}_{(10)}$$\n",
571 | "$${\\left(\\frac{{0}}{2^0}+\\frac{1}{2^1}+\\frac{1}{2^2}+\\cdots+\\frac{0}{2^{52}}\\right)2^{-1022}}_{(10)}$$\n",
572 | "$$\\vdots$$\n",
573 | "$$\\left(\\frac{{0}}{2^0}+\\frac{1}{2^1}+\\frac{0}{2^2}+\\cdots+\\frac{0}{2^{52}}\\right)2^{-1022}$$\n",
574 | "$${\\left(\\frac{{0}}{2^0}+\\frac{0}{2^1}+\\frac{1}{2^2}+\\cdots+\\frac{1}{2^{52}}\\right)2^{-1022}}_{(10)}$$\n",
575 | "$$\\vdots$$\n",
576 | "$${\\left(\\frac{{0}}{2^0}+\\frac{0}{2^1}+\\frac{0}{2^2}+\\cdots+\\frac{1}{2^{52}}\\right)2^{-1022}}_{(10)}=2^{-1074}\\approx10^{-323.31}$$\n",
577 | "\n",
578 | "のような数が表現できる.ただし,$2^{-1022}$と$2^{-1074}$の間の数は,本来53ビットあるべき仮数部の長さが52ビット〜1ビットまで減ってしまっており,精度が低下していることに注意が必要である."
579 | ]
580 | },
581 | {
582 | "cell_type": "code",
583 | "execution_count": 15,
584 | "metadata": {},
585 | "outputs": [
586 | {
587 | "name": "stdout",
588 | "output_type": "stream",
589 | "text": [
590 | "5.0e-324\n",
591 | "0000000000000000000000000000000000000000000000000000000000000001\n",
592 | "0.0\n",
593 | "0000000000000000000000000000000000000000000000000000000000000000\n"
594 | ]
595 | }
596 | ],
597 | "source": [
598 | "x = 2. ^ (-1074)\n",
599 | "println(x)\n",
600 | "println(bitstring(x))\n",
601 | "\n",
602 | "x = (2. ^ (-1074)) / 2\n",
603 | "println(x)\n",
604 | "println(bitstring(x))"
605 | ]
606 | },
607 | {
608 | "cell_type": "markdown",
609 | "metadata": {},
610 | "source": [
611 | "### 浮動小数点数まとめ\n",
612 | "\n",
613 | "倍精度(binary64, Float64/double)は\n",
614 | "\n",
615 | "||仮数部が0|仮数部が0でない|\n",
616 | "|:-:|:-:|:-:|\n",
617 | "|$e+1023=0$ | $\\pm0$ | 非正規化数|\n",
618 | "|$1\\le e+1023\\le 2046$ | 正規化数 | 正規化数 |\n",
619 | "|$e+1023=2047$ | $\\pm\\infty$ | NaN|\n",
620 | "\n",
621 | "\n",
622 | "単精度 (binary32, Float32/float) は\n",
623 | "\n",
624 | "\n",
625 | "||仮数部が0|仮数部が0でない|\n",
626 | "|:-:|:-:|:-:|\n",
627 | "|$e+127=0$ | $\\pm0$ | 非正規化数|\n",
628 | "|$1\\le e+127\\le 254$ | 正規化数 | 正規化数 |\n",
629 | "|$e+127=255$ | $\\pm\\infty$ | NaN|"
630 | ]
631 | },
632 | {
633 | "cell_type": "markdown",
634 | "metadata": {},
635 | "source": [
636 | "今後,浮動小数点数全体の集合を$\\mathbb{F}$と表すことにする.特に断りがなければ,浮動小数点数は倍精度浮動小数点数(64bit)とする.IEEE754では**binary64**とも呼ばれている.\n",
637 | "\n",
638 | "### 謝辞\n",
639 | "\n",
640 | "本資料は筆者が学生の頃に精度保証付き数値計算を教えて下さった[柏木雅英](http://www.kashi.info.waseda.ac.jp/~kashi/)先生の「数値解析特論」の講義資料が基になっています.\n",
641 | "また, 以下のような文献・Web ページ等を参考にこの文章は書いています.\n",
642 | "\n",
643 | "### 参考文献\n",
644 | "\n",
645 | "1. 伊理正夫, 藤野和建, 数値計算の常識, 共立出版, 1985.
\n",
646 | "(Twitterとかでも度々話題に上がる名著. IEEE754 の制定の年にすでに浮動小数点数に対する注意が詰まっている書籍が出版されている)\n",
647 | "1. 齊藤宣一, 数値解析入門, 東京大学出版会, 2012.
\n",
648 | "(数値解析学の現在最も詳しい教科書. 浮動小数点数についても1章に詳しく記述がある.)\n",
649 | "1. 大石進一編著, 精度保証付き数値計算の基礎, コロナ社, 2018.
\n",
650 | "(精度保証付き数値計算の教科書. 浮動小数点数および区間演算に詳しい. この1章が読めたら大したもの)\n",
651 | "1. [ushiostarfish, IEEE 754 浮動小数点入門.](https://ushiostarfish.hatenablog.com/entry/2019/08/12/210023)
\n",
652 | "(IEEE 754 浮動小数点数を細かく紹介し, 丸め誤差の詳細, および区間演算について触れている)\n",
653 | "1. Nick Higham, [What Is Floating-Point Arithmetic](https://nhigham.com/2020/05/04/what-is-floating-point-arithmetic/)/[IEEE Standard Arithmetic](https://nhigham.com/2020/05/07/what-is-ieee-standard-arithmetic/).
\n",
654 | "(数値解析の超有名人によるブログ記事, (IEEE754/854)浮動小数点数について端的にまとめられている)"
655 | ]
656 | },
657 | {
658 | "cell_type": "markdown",
659 | "metadata": {},
660 | "source": [
661 | "高安亮紀,2020年7月17日(最終更新:2025年5月19日)
"
662 | ]
663 | }
664 | ],
665 | "metadata": {
666 | "kernelspec": {
667 | "display_name": "Julia 1.11.5",
668 | "language": "julia",
669 | "name": "julia-1.11"
670 | },
671 | "language_info": {
672 | "file_extension": ".jl",
673 | "mimetype": "application/julia",
674 | "name": "julia",
675 | "version": "1.11.5"
676 | }
677 | },
678 | "nbformat": 4,
679 | "nbformat_minor": 4
680 | }
681 |
--------------------------------------------------------------------------------
/interval_dot-mul.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# ベクトルの内積・行列ベクトル積・行列積の区間演算\n",
8 | "\n",
9 | "ベクトルの内積、行列ベクトル積、行列積の区間演算はとても重要。すなわち\n",
10 | "$x, y\\in\\mathbb{IF}^n$, $A, B\\in\\mathbb{IF}^{n\\times n}$ に対して、それぞれ$x^Ty \\subset \\alpha$, $Ax\\subset z$, $AB\\subset C$となる\n",
11 | "$$\n",
12 | "\\alpha\\in\\mathbb{IF},\\quad z\\in\\mathbb{IF}^n,\\quad C\\in\\mathbb{IF}^{n\\times n}\n",
13 | "$$\n",
14 | "を求める方法を考える。\n",
15 | "\n",
16 | "## 素朴な方法\n",
17 | "\n",
18 | "最も素朴な方法は各演算を区間演算に変更した方式である。\n",
19 | "\n",
20 | "$$\n",
21 | " \\alpha = \\sum_{k = 1}^n x_ky_k\n",
22 | "$$\n",
23 | "\n",
24 | "$$\n",
25 | " z_i = \\sum_{k = 1}^n A_{i,k}x_k\\quad (1\\le i\\le n)\n",
26 | "$$\n",
27 | "\n",
28 | "$$\n",
29 | " C_{ij} = \\sum_{k+1}^n A_{i,k}B_{k,j}\\quad (1\\le i,j\\le n)\n",
30 | "$$\n",
31 | "\n",
32 | "特にJuliaの`IntervalArithmetic.jl`パッケージでは、この方法で区間演算が行なわれている。しかし、この方法だとプログラムの最適化が難しく、計算機の性能を十分に引き出した実装が難しく、計算時間がかかってしまう。"
33 | ]
34 | },
35 | {
36 | "cell_type": "code",
37 | "execution_count": 2,
38 | "metadata": {},
39 | "outputs": [
40 | {
41 | "name": "stdout",
42 | "output_type": "stream",
43 | "text": [
44 | "Julia Version 1.11.5\n",
45 | "Commit 760b2e5b739 (2025-04-14 06:53 UTC)\n",
46 | "Build Info:\n",
47 | " Official https://julialang.org/ release\n",
48 | "Platform Info:\n",
49 | " OS: macOS (arm64-apple-darwin24.0.0)\n",
50 | " CPU: 8 × Apple M2\n",
51 | " WORD_SIZE: 64\n",
52 | " LLVM: libLLVM-16.0.6 (ORCJIT, apple-m2)\n",
53 | "Threads: 4 default, 0 interactive, 2 GC (on 4 virtual cores)\n",
54 | "Environment:\n",
55 | " JULIA_NUM_THREADS = 4\n"
56 | ]
57 | }
58 | ],
59 | "source": [
60 | "versioninfo()"
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 3,
66 | "metadata": {},
67 | "outputs": [
68 | {
69 | "name": "stdout",
70 | "output_type": "stream",
71 | "text": [
72 | "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `~/.julia/environments/v1.11/Project.toml`\n",
73 | " \u001b[90m[d1acc4aa] \u001b[39mIntervalArithmetic v0.22.35\n"
74 | ]
75 | }
76 | ],
77 | "source": [
78 | "using Pkg\n",
79 | "Pkg.status(\"IntervalArithmetic\")"
80 | ]
81 | },
82 | {
83 | "cell_type": "code",
84 | "execution_count": 4,
85 | "metadata": {},
86 | "outputs": [
87 | {
88 | "name": "stdout",
89 | "output_type": "stream",
90 | "text": [
91 | " 44.000 μs (0 allocations: 0 bytes)\n"
92 | ]
93 | },
94 | {
95 | "data": {
96 | "text/plain": [
97 | "[12.0404, 12.0405]_com"
98 | ]
99 | },
100 | "metadata": {},
101 | "output_type": "display_data"
102 | }
103 | ],
104 | "source": [
105 | "using LinearAlgebra\n",
106 | "using IntervalArithmetic, BenchmarkTools\n",
107 | "\n",
108 | "n = 1000\n",
109 | "x = randn(n)\n",
110 | "y = randn(n)\n",
111 | "\n",
112 | "ix = interval(x)\n",
113 | "iy = interval(y)\n",
114 | "@btime alpha = dot($ix,$iy) # Level 1\n",
115 | "\n",
116 | "# setrounding(Interval, :fast)"
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": 5,
122 | "metadata": {},
123 | "outputs": [
124 | {
125 | "name": "stdout",
126 | "output_type": "stream",
127 | "text": [
128 | " 0.168998 seconds (630.81 k allocations: 31.796 MiB, 3.18% gc time, 64.04% compilation time)\n",
129 | " 0.062098 seconds (72 allocations: 3.297 KiB, 3.74% compilation time)\n",
130 | "true\n",
131 | "true\n"
132 | ]
133 | }
134 | ],
135 | "source": [
136 | "n = 1000; A = randn(n,n); x = randn(n)\n",
137 | "iA = interval(A); ix = interval(x); y = similar(ix)\n",
138 | "@time z = iA * ix # Level 2\n",
139 | "@time mul!(y, iA, ix) # Level 2\n",
140 | "\n",
141 | "# ix = IntervalBox(ix)\n",
142 | "# @time iz = iA * ix # Level 2\n",
143 | "\n",
144 | "println(issubset_interval(y,z))\n",
145 | "println(issubset_interval(z,y))\n",
146 | "\n",
147 | "# @btime z = $A_int*$x_int;"
148 | ]
149 | },
150 | {
151 | "cell_type": "code",
152 | "execution_count": 6,
153 | "metadata": {},
154 | "outputs": [
155 | {
156 | "name": "stdout",
157 | "output_type": "stream",
158 | "text": [
159 | " 0.151230 seconds (494.64 k allocations: 25.638 MiB, 59.93% compilation time)\n",
160 | " 0.060364 seconds (3 allocations: 256.078 KiB)\n",
161 | " 0.060519 seconds (3 allocations: 256.078 KiB)\n"
162 | ]
163 | },
164 | {
165 | "data": {
166 | "text/plain": [
167 | "100×100 Matrix{Interval{Float64}}:\n",
168 | " [-4.52848, -4.52847]_com … [-10.1517, -10.1515]_com\n",
169 | " [-5.90354, -5.90353]_com [19.0921, 19.0922]_com\n",
170 | " [-10.3552, -10.3551]_com [0.798259, 0.79826]_com\n",
171 | " [-10.111, -10.1109]_com [15.4903, 15.4904]_com\n",
172 | " [-1.19862, -1.19861]_com [23.243, 23.2431]_com\n",
173 | " [1.52761, 1.52762]_com … [-18.365, -18.3649]_com\n",
174 | " [-13.2879, -13.2878]_com [6.86769, 6.8677]_com\n",
175 | " [-19.5613, -19.5611]_com [-0.945912, -0.945911]_com\n",
176 | " [-0.922418, -0.922417]_com [-1.75119, -1.75118]_com\n",
177 | " [-0.695091, -0.69509]_com [-10.1601, -10.16]_com\n",
178 | " ⋮ ⋱ \n",
179 | " [-16.1018, -16.1017]_com [19.2508, 19.2509]_com\n",
180 | " [-0.366072, -0.366071]_com [10.488, 10.4881]_com\n",
181 | " [1.95692, 1.95693]_com [8.64718, 8.64719]_com\n",
182 | " [-11.1707, -11.1706]_com [-7.44171, -7.4417]_com\n",
183 | " [2.48841, 2.48842]_com … [-6.99556, -6.99555]_com\n",
184 | " [5.98563, 5.98564]_com [10.6551, 10.6552]_com\n",
185 | " [-5.68495, -5.68494]_com [-5.91162, -5.91161]_com\n",
186 | " [7.3865, 7.38651]_com [4.74594, 4.74595]_com\n",
187 | " [-1.33356, -1.33355]_com [5.33925, 5.33926]_com"
188 | ]
189 | },
190 | "metadata": {},
191 | "output_type": "display_data"
192 | }
193 | ],
194 | "source": [
195 | "n = 100\n",
196 | "A = randn(n,n)\n",
197 | "B = randn(n,n)\n",
198 | "iA = interval(A)\n",
199 | "iB = interval(B)\n",
200 | "@time C = iA * iB # Level 3\n",
201 | "@time C = iA * iB # Level 3\n",
202 | "@time C = iA * iB; # Level 3\n",
203 | "\n",
204 | "# A_int = SMatrix{n,n}(A_int);\n",
205 | "# B_int = SMatrix{n,n}(B_int);\n",
206 | "# @time C = A_int*B_int; # Level 3\n",
207 | "# @time C = A_int*B_int;\n",
208 | "# @time C = A_int*B_int;"
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "metadata": {},
214 | "source": [
215 | "このくらいの計算スピードならば、体感として、そこまで遅い印象はないが、そこそこ大きなサイズの行列積(Level 3 BLAS)は計算時間がかかってしまう。例えば、"
216 | ]
217 | },
218 | {
219 | "cell_type": "code",
220 | "execution_count": 7,
221 | "metadata": {},
222 | "outputs": [
223 | {
224 | "name": "stdout",
225 | "output_type": "stream",
226 | "text": [
227 | " 60.439004 seconds (3 allocations: 22.891 MiB)\n"
228 | ]
229 | }
230 | ],
231 | "source": [
232 | "n = 1000; A, B = randn(n,n), randn(n,n)\n",
233 | "iA = interval(A)\n",
234 | "iB = interval(B)\n",
235 | "@time C = iA * iB; # Level 3"
236 | ]
237 | },
238 | {
239 | "cell_type": "markdown",
240 | "metadata": {},
241 | "source": [
242 | "**注意** `BenchmarkTools`の`@btime`や`@benckmark`マクロを使うときは、\n",
243 | "``\n",
244 | "@btime A*B;\n",
245 | "``\n",
246 | "とせずに\n",
247 | "``\n",
248 | "@btime $A*$B;\n",
249 | "``\n",
250 | "と変数名に`$`をつける。これは計測時間に変数の呼び出しを含めるか指定するもので、前者が「変数の呼び出し時間を含めた計測」、後者は「行列積の計算時間の計測」を意味している。\n",
251 | "\n",
252 | "\n",
253 | "さて、区間行列積(Level 3)を高速化したい。考えられる方法は\n",
254 | "\n",
255 | "1. 並列化([Loopvectrization](https://github.com/chriselrod/LoopVectorization.jl), [Tullio](https://github.com/mcabbott/Tullio.jl), `@simd`命令)\n",
256 | "\n",
257 | "Tullioパッケージを使うと行列積を自動的に並列化して計算してくれる。小さいサイズの行列積はそこまで高速化されないが、ある程度行列サイズが大きくなると区間行列積の自動並列によって、計算時間が短くなることが観測される。ただし、`JULIA_NUM_THREADS`という環境変数で使用するスレッド数をあらかじめ定義しておく必要がある。例えばmacOSでzshというシェルを使っているなら、`~/.zshrc`というファイル内に\n",
258 | "\n",
259 | "``export JULIA_NUM_THREADS=8``\n",
260 | "\n",
261 | "と計算スレッド数を指定しておく必要がある。CPUのコア数分だけ指定したら良い?"
262 | ]
263 | },
264 | {
265 | "cell_type": "code",
266 | "execution_count": 8,
267 | "metadata": {},
268 | "outputs": [
269 | {
270 | "name": "stdout",
271 | "output_type": "stream",
272 | "text": [
273 | " 60.186 ms (3 allocations: 256.08 KiB)\n",
274 | " 15.703 ms (50 allocations: 258.55 KiB)\n"
275 | ]
276 | },
277 | {
278 | "data": {
279 | "text/plain": [
280 | "100×100 Matrix{Interval{Float64}}:\n",
281 | " [-11.6278, -11.6277]_com … [3.29631, 3.29632]_com\n",
282 | " [-1.162, -1.16199]_com [2.92584, 2.92585]_com\n",
283 | " [13.2834, 13.2835]_com [0.721624, 0.721625]_com\n",
284 | " [6.59471, 6.59472]_com [7.34718, 7.34719]_com\n",
285 | " [7.96798, 7.96799]_com [-18.1403, -18.1402]_com\n",
286 | " [10.8267, 10.8268]_com … [-8.28956, -8.28955]_com\n",
287 | " [-7.11751, -7.11749]_com [-4.73135, -4.73134]_com\n",
288 | " [-10.4796, -10.4795]_com [14.2798, 14.2799]_com\n",
289 | " [-11.2425, -11.2424]_com [11.0072, 11.0073]_com\n",
290 | " [14.6567, 14.6568]_com [4.09116, 4.09117]_com\n",
291 | " ⋮ ⋱ \n",
292 | " [-8.62427, -8.62426]_com [-15.6644, -15.6643]_com\n",
293 | " [8.26026, 8.26027]_com [12.3283, 12.3284]_com\n",
294 | " [10.6095, 10.6096]_com [-3.6928, -3.69279]_com\n",
295 | " [-2.0886, -2.08859]_com [3.25317, 3.25318]_com\n",
296 | " [-3.50003, -3.50002]_com … [-4.1966, -4.19659]_com\n",
297 | " [-3.45124, -3.45123]_com [0.76803, 0.768031]_com\n",
298 | " [-11.7032, -11.7031]_com [9.15455, 9.15456]_com\n",
299 | " [3.36447, 3.36448]_com [-15.1358, -15.1357]_com\n",
300 | " [-13.9981, -13.998]_com [-1.56078, -1.56077]_com"
301 | ]
302 | },
303 | "metadata": {},
304 | "output_type": "display_data"
305 | }
306 | ],
307 | "source": [
308 | "# using LoopVectorization, BenchmarkTools, IntervalArithmetic, Tullio\n",
309 | "using Tullio\n",
310 | "\n",
311 | "tullio_gemm(A, B) = @tullio C[i,j] := A[i,k] * B[k,j]\n",
312 | "\n",
313 | "n = 100\n",
314 | "A, B = randn(n,n), randn(n,n)\n",
315 | "iA = interval(A)\n",
316 | "iB = interval(B)\n",
317 | "\n",
318 | "@btime C = $iA * $iB;\n",
319 | "@btime C1 = tullio_gemm($iA, $iB)"
320 | ]
321 | },
322 | {
323 | "cell_type": "code",
324 | "execution_count": 9,
325 | "metadata": {},
326 | "outputs": [
327 | {
328 | "name": "stdout",
329 | "output_type": "stream",
330 | "text": [
331 | " 16.167 s (50 allocations: 22.89 MiB)\n"
332 | ]
333 | }
334 | ],
335 | "source": [
336 | "# Threads.nthreads()\n",
337 | "n = 1000;\n",
338 | "iA, iB = interval(randn(n,n)), interval(randn(n,n))\n",
339 | "# @btime C = A*B; # ≈ 40 sec\n",
340 | "# setrounding(Interval, :tight)\n",
341 | "@btime C1 = tullio_gemm(iA,iB); # ≈ 6 sec"
342 | ]
343 | },
344 | {
345 | "cell_type": "markdown",
346 | "metadata": {},
347 | "source": [
348 | "次、行列、ベクトルの配列に\n",
349 | "\n",
350 | "2. [StaticArrays](https://github.com/JuliaArrays/StaticArrays.jl_)を使うと[速いらしい](https://qiita.com/cometscome_phys/items/669273a49ab2417d3af8)\n",
351 | "\n",
352 | "`StaticArrays`は小さいサイズでは多少計算時間が変わるが、要素数が多くなるほどコンパイルに時間がかかり、全体の経過時間は長くなってしまった。要素数が100以下なら`StaticArrays`が使えると[Webサイト](https://github.com/JuliaArrays/StaticArrays.jl_)に書いてありました。\n",
353 | "\n",
354 | "残るは\n",
355 | "\n",
356 | "3. BLAS-Level 3を使う区間演算を実装する、だけど丸め方向が変えられないので、丸めの向きを変えない区間演算を実装する必要あり\n",
357 | " - 内積は文献3.の定理8 (今回は実装しない)\n",
358 | " - 行列ベクトル積は?? (今回は実装しない)\n",
359 | " - 行列積は文献3.の4節、アルゴリズム5\n",
360 | "\n",
361 | " \n",
362 | " ## BLASを使う\n",
363 | " \n",
364 | " まずJuliaの中で、[BLAS](https://ja.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms)を使うには``LinearAlgebra``パッケージが必要。"
365 | ]
366 | },
367 | {
368 | "cell_type": "code",
369 | "execution_count": 10,
370 | "metadata": {},
371 | "outputs": [
372 | {
373 | "name": "stdout",
374 | "output_type": "stream",
375 | "text": [
376 | " 1.628 s (0 allocations: 0 bytes)\n"
377 | ]
378 | }
379 | ],
380 | "source": [
381 | "using LinearAlgebra, BenchmarkTools\n",
382 | "# versioninfo()\n",
383 | "# BLAS.vendor()\n",
384 | "n = 5000\n",
385 | "A, B, C = randn(n,n), randn(n,n), zeros(n,n)\n",
386 | "\n",
387 | "# @btime C = $A * $B;\n",
388 | "@btime mul!(C, A, B);"
389 | ]
390 | },
391 | {
392 | "cell_type": "code",
393 | "execution_count": 11,
394 | "metadata": {},
395 | "outputs": [],
396 | "source": [
397 | "# using IntervalArithmetic, MKL\n",
398 | "# n = 65\n",
399 | "# A, B = randn(n,n), randn(n,n)\n",
400 | "# setrounding(Float64, RoundUp)\n",
401 | "# C_up = A * B\n",
402 | "# setrounding(Float64, RoundDown)\n",
403 | "# C_down = A * B\n",
404 | "# setrounding(Float64, RoundNearest)\n",
405 | "# C_down .< C_up"
406 | ]
407 | },
408 | {
409 | "cell_type": "markdown",
410 | "metadata": {},
411 | "source": [
412 | "このようにBLASを使うと、物凄いスピードで数値計算ができる。ただし、行列の内部の型がFloat64に限られるのと、丸め方向を変更した計算ができないため、区間演算の結果が粗くなる(区間幅が増加する)。以下、**丸め方向を変更しない区間行列積**を実装する。\n",
413 | "\n",
414 | "**定義** $\\mathbf{u}=2^{-53}$を倍精度の**単位相対丸め**とする。$\\mathbf{S}_{\\min}=2^{-1074}$を倍精度浮動小数点数の正の最小数とする。$\\mathbf{F}_{\\min}=2^{-1022}$を倍精度浮動小数点数の正規化数の正の最小数とする。 \n",
415 | "\n",
416 | "**注意** 単位相対丸めは$1$と$1$よりも小さい最大の浮動小数点数との差を表す。つまり\n",
417 | "\n",
418 | "$$\n",
419 | "1-\\mathbf{u}= (1. / 2.) * u^(-2) * s_min # 2^(-969)(Float64)\n",
559 | " e = ϕ * abs(c);\n",
560 | " succ = c + e;\n",
561 | " elseif abs(c) < (1. / 2.) * u^(-1) * s_min # 2^(-1022)(Float64)\n",
562 | " succ = c + s_min;\n",
563 | " else\n",
564 | " C = u^(-1) * c;\n",
565 | " e = ϕ * abs(C);\n",
566 | " succ = (C + e) * u;\n",
567 | " end\n",
568 | " return succ\n",
569 | "end\n",
570 | "\n",
571 | "function pred(c)\n",
572 | " s_min = 2.0^-1074;\n",
573 | " u = 2.0^-53;\n",
574 | " ϕ = u * (1.0 + 2.0 * u);\n",
575 | " if abs(c) >= (1. / 2.) * u^(-2) * s_min # 2^(-969)(Float64)\n",
576 | " e = ϕ * abs(c);\n",
577 | " pred = c - e;\n",
578 | " elseif abs(c) < (1. / 2.) * u^(-1) * s_min # 2^(-1022)(Float64)\n",
579 | " pred = c - s_min;\n",
580 | " else\n",
581 | " C = u^(-1) * c;\n",
582 | " e = ϕ * abs(C);\n",
583 | " pred = (C - e) * u;\n",
584 | " end\n",
585 | " return pred\n",
586 | "end"
587 | ]
588 | },
589 | {
590 | "cell_type": "code",
591 | "execution_count": 16,
592 | "metadata": {},
593 | "outputs": [
594 | {
595 | "name": "stdout",
596 | "output_type": "stream",
597 | "text": [
598 | "0011111110111001100110011001100110011001100110011001100110011011\n",
599 | "0011111110111001100110011001100110011001100110011001100110011010\n",
600 | "0011111110111001100110011001100110011001100110011001100110011001\n"
601 | ]
602 | }
603 | ],
604 | "source": [
605 | "a = 0.1;\n",
606 | "println(bitstring(succ(a)))\n",
607 | "println(bitstring(a))\n",
608 | "println(bitstring(pred(a)))"
609 | ]
610 | },
611 | {
612 | "cell_type": "markdown",
613 | "metadata": {},
614 | "source": [
615 | "ベクトルの総和と内積のufpを用いた事前誤差解析を応用して、行列 $A\\in\\mathbb{F}^{m\\times n}$, $B\\in\\mathbb{F}^{n\\times p}$ の積 $AB$ を包含する中心半径型区間行列 $\\langle C,R\\rangle\\in\\mathbb{IF}^{m\\times p}$ を計算する。\n",
616 | "\n",
617 | "$$\n",
618 | " C=\\text{fl}(AB),\\quad R=\\text{fl}((n+2)\\mathbf{u}\\cdot \\text{ufp}(|A||B|)) + \\mathbf{F}_{\\min}\\cdot e_{m}e_{p}^T).\n",
619 | "$$\n",
620 | "\n",
621 | "ただし $e_n=(1,\\dots,1)^T\\in\\mathbb{F}^{n}$ で $n$ は $2(n+\\mathbf{u})<1$を満たすとする。このとき必要な行列積の計算は、$\\text{fl}(AB)$, $\\text{fl}(|A||B|)$ の2回である。これらをBLASを用いて計算することにより、高速な数値計算が実行可能である。そして、浮動小数点演算を行った結果 $C$ とその誤差半径を $R$ とし、行列積の真値を包含する。"
622 | ]
623 | },
624 | {
625 | "cell_type": "code",
626 | "execution_count": 17,
627 | "metadata": {},
628 | "outputs": [
629 | {
630 | "data": {
631 | "text/plain": [
632 | "mm_ufp (generic function with 1 method)"
633 | ]
634 | },
635 | "metadata": {},
636 | "output_type": "display_data"
637 | }
638 | ],
639 | "source": [
640 | "function mm_ufp(A_mid, B_mid)\n",
641 | " u = 2.0^(-53);\n",
642 | " realmin = 2.0^(-1022);\n",
643 | " n = size(A_mid,2);\n",
644 | " \n",
645 | " if(2*(n+2)*u>=1)\n",
646 | " error(\"mm_ufp is failed!(2(n+2)u>=1)\")\n",
647 | " end\n",
648 | " C_mid = A_mid * B_mid;\n",
649 | " C_rad = (n+2) * u * ufp.(abs.(A_mid)*abs.(B_mid)) .+ realmin;\n",
650 | " \n",
651 | " return C_mid, C_rad;\n",
652 | "end"
653 | ]
654 | },
655 | {
656 | "cell_type": "code",
657 | "execution_count": null,
658 | "metadata": {},
659 | "outputs": [
660 | {
661 | "name": "stdout",
662 | "output_type": "stream",
663 | "text": [
664 | " 26.459 ms (22 allocations: 53.59 MiB)\n",
665 | " 0.034732 seconds (24 allocations: 53.594 MiB, 3.97% gc time)\n"
666 | ]
667 | }
668 | ],
669 | "source": [
670 | "using IntervalArithmetic, LinearAlgebra\n",
671 | "# using IntervalArithmetic.Symbols\n",
672 | "n = 1000\n",
673 | "A = randn(n,n)\n",
674 | "B = randn(n,n)\n",
675 | "iA = interval(A)\n",
676 | "iB = interval(B)\n",
677 | "\n",
678 | "A_mid = mid.(iA)\n",
679 | "B_mid = mid.(iB)\n",
680 | "\n",
681 | "@btime C_mid, C_rad = mm_ufp(A_mid, B_mid)\n",
682 | "@time C_mid, C_rad = mm_ufp(A_mid, B_mid);\n",
683 | "C_int = interval(C_mid, C_rad; format=:midpoint);"
684 | ]
685 | },
686 | {
687 | "cell_type": "markdown",
688 | "metadata": {},
689 | "source": [
690 | "**注意** `@tullio`マクロで並列化した区間演算(8コア!)で約6秒かかる区間行列積が、BLASを使うと、約0.02秒と300倍程度高速化される。これがBLASの力だー!しかし、BLASの計算はFloat64のみサポートされるため、64ビットの浮動小数点数を用いた区間演算のとき以外は、このままでは高速化されない点を注意したい。例えば端点に`BigFloat`のような多倍長数値を用いたいときなどは、残念ながらBLASを用いたの高速な計算が出来ないのが現状である。また、ufpを用いる区間演算は過大評価になるため、区間幅の増大が避けられない事(概ね10倍大きな区間幅になる)にも注意が必要である。"
691 | ]
692 | },
693 | {
694 | "cell_type": "code",
695 | "execution_count": 25,
696 | "metadata": {},
697 | "outputs": [
698 | {
699 | "name": "stdout",
700 | "output_type": "stream",
701 | "text": [
702 | "9.492850949754938e-12\n",
703 | "5.695710569852963e-11\n"
704 | ]
705 | }
706 | ],
707 | "source": [
708 | "C1 = tullio_gemm(iA, iB);\n",
709 | "println(maximum(radius.(C1[:])))\n",
710 | "println(maximum(C_rad[:]))"
711 | ]
712 | },
713 | {
714 | "cell_type": "markdown",
715 | "metadata": {},
716 | "source": [
717 | "次に区間行列 $A=\\langle A_m,A_r\\rangle\\in\\mathbb{IF}^{m\\times n}$, $B=\\langle B_m,B_r\\rangle\\in\\mathbb{IF}^{n\\times p}$ の積 $AB$ を包含する区間行列 $\\langle C,T\\rangle\\in\\mathbb{IF}^{m\\times p}$ を計算することを考える。はじめに区間行列積 $AB$ は\n",
718 | "\n",
719 | "$$\n",
720 | " AB\\subseteq \\langle A_mB_m,\\tilde{T}\\rangle,\\quad \\tilde{T}:=|A_m|B_r+A_r(|B_m|+B_r)\n",
721 | "$$\n",
722 | "\n",
723 | "と包含できる。丸め方向の変更ができる場合は、この中心と半径を丸めの向きを変えることで計算し厳密に包含することができる。しかし今回は丸め方向の変更ができないため、事前誤差評価によって中心と半径を厳密に包含する必要がある。いま $n$ は $2(n+\\mathbf{u})<1$ を満たすとし、区間行列の中心同士の積 $A_mB_m$ は\n",
724 | "\n",
725 | "$$\n",
726 | " C=\\text{fl}(A_mB_m),\\quad R=\\text{fl}((n+2)\\mathbf{u}\\cdot \\text{ufp}(|A_m||B_m|)) + \\mathbf{F}_{\\min}\\cdot e_{m}e_{p}^T).\n",
727 | "$$\n",
728 | "\n",
729 | "として $\\langle C,R\\rangle(=\\texttt{mm\\_ufp}(A_m,B_m))$ で包含できる。次に $|A_m|B_r$ の上限を考える。\n",
730 | "\n",
731 | "\\begin{align*}\n",
732 | "&T_1:=\\text{fl}(|A_m|B_r)\\\\\n",
733 | "&||A_m|B_r-\\text{fl}(|A_m|B_r)|\\le \\text{fl}((n+2)\\mathbf{u}\\cdot\\text{ufp}(T_1) + \\mathbf{F}_{\\min}\\cdot e_{m}e_{p}^T)=:T_2\\\\\n",
734 | "&|A_m|B_r\\le T_1+T_2\n",
735 | "\\end{align*}\n",
736 | "\n",
737 | "さらに $|B_m|+B_r$ は\n",
738 | "\n",
739 | "$$\n",
740 | " |B_m|+B_r\\le\\text{succ}(\\text{fl}(|B_m|+B_r))=:T_3\n",
741 | "$$\n",
742 | "\n",
743 | "で抑えられる。さらに $A_rT_3$ の上限は\n",
744 | "\n",
745 | "\\begin{align*}\n",
746 | "&T_4:=\\text{fl}(A_rT_3)\\\\\n",
747 | "&|A_rT_3-\\text{fl}(A_rT_3)|\\le \\text{fl}((n+2)\\mathbf{u}\\cdot\\text{ufp}(T_4) + \\mathbf{F}_{\\min}\\cdot e_{m}e_{p}^T)=:T_5\\\\\n",
748 | "&A_rT_3\\le T_4+T_5.\n",
749 | "\\end{align*}\n",
750 | "\n",
751 | "最後に誤差半径 $T$ は $T_1+T_2+T_3+T_4+T_5+R$の上限で得られ、\n",
752 | "\n",
753 | "\\begin{align*}\n",
754 | "&T_1+T_2+T_3+T_4+T_5+R\\\\\n",
755 | "&\\le \\text{fl}(T_1+T_2+T_3+T_4+T_5+R) + \\text{fl}(4\\mathbf{u}(T_1+T_2+T_3+T_4+T_5+R))\\\\\n",
756 | "&\\le \\text{succ}(\\text{fl}(T_1+T_2+T_3+T_4+T_5+R) + \\text{fl}(4\\mathbf{u}(T_1+T_2+T_3+T_4+T_5+R)))=:T.\n",
757 | "\\end{align*}\n",
758 | "\n",
759 | "したがって、$AB\\subseteq \\langle C,T\\rangle$ となる $C\\in\\mathbb{F}^{m\\times p}$ と $T\\in\\mathbb{F}^{m\\times p}$ が得られる。"
760 | ]
761 | },
762 | {
763 | "cell_type": "code",
764 | "execution_count": 20,
765 | "metadata": {},
766 | "outputs": [
767 | {
768 | "data": {
769 | "text/plain": [
770 | "imm_ufp (generic function with 1 method)"
771 | ]
772 | },
773 | "metadata": {},
774 | "output_type": "display_data"
775 | }
776 | ],
777 | "source": [
778 | "function imm_ufp(A_mid, A_rad, B_mid, B_rad)\n",
779 | " u = 2.0^(-53);\n",
780 | " realmin = 2.0^(-1022);\n",
781 | " n = size(A_mid,2);\n",
782 | " \n",
783 | " if(2*(n+2)*u>=1)\n",
784 | " error(\"mm_ufp is failed!(2(n+2)u>=1)\")\n",
785 | " end\n",
786 | "# C, R = mm_ufp(A_mid,B_mid);\n",
787 | " C_mid = A_mid * B_mid;\n",
788 | " R = (n+2) * u * ufp.(abs.(A_mid)*abs.(B_mid)) .+ realmin;\n",
789 | " \n",
790 | "# T_1, T_2 = mm_ufp(abs.(A_mid), B_rad);\n",
791 | " T1 = abs.(A_mid) * B_rad;\n",
792 | " T2 = (n+2)*u*ufp.(T1) .+ realmin;\n",
793 | " \n",
794 | "# T_3 = succ.(abs.(B_mid)+B_rad);\n",
795 | " T3 = succ.(abs.(B_mid)+B_rad)\n",
796 | " \n",
797 | "# T_4, T_5 = mm_ufp(A_r, T_3);\n",
798 | " T4 = A_rad * T3;\n",
799 | " T5 = (n+2)*u*ufp.(T4) .+ realmin;\n",
800 | " \n",
801 | " rad_sum = R + T1 + T2 + T4 + T5;\n",
802 | "\n",
803 | " C_rad = succ.(rad_sum + 4*u*ufp.(rad_sum));\n",
804 | " \n",
805 | " return C_mid, C_rad;\n",
806 | "end"
807 | ]
808 | },
809 | {
810 | "cell_type": "code",
811 | "execution_count": 26,
812 | "metadata": {},
813 | "outputs": [
814 | {
815 | "name": "stdout",
816 | "output_type": "stream",
817 | "text": [
818 | " 64.713 ms (73 allocations: 183.75 MiB)\n"
819 | ]
820 | }
821 | ],
822 | "source": [
823 | "using IntervalArithmetic, LinearAlgebra\n",
824 | "\n",
825 | "# n = 1000;\n",
826 | "# A = randn(n,n);\n",
827 | "# B = randn(n,n);\n",
828 | "# A_int = map(Interval, A);\n",
829 | "# B_int = map(Interval, B);\n",
830 | "\n",
831 | "A_mid = mid.(iA);\n",
832 | "A_rad = radius.(iA);\n",
833 | "B_mid = mid.(iB);\n",
834 | "B_rad = radius.(iB);\n",
835 | "\n",
836 | "@btime C1_mid, C1_rad = imm_ufp(A_mid, A_rad, B_mid, B_rad);\n",
837 | "C1_mid, C1_rad = imm_ufp(A_mid,A_rad,B_mid,B_rad);\n",
838 | "C1_int = interval(C1_mid, C1_rad; format=:midpoint);\n"
839 | ]
840 | },
841 | {
842 | "cell_type": "code",
843 | "execution_count": 22,
844 | "metadata": {},
845 | "outputs": [
846 | {
847 | "name": "stdout",
848 | "output_type": "stream",
849 | "text": [
850 | "5.695710569852963e-11\n",
851 | "5.695710569852965e-11\n"
852 | ]
853 | }
854 | ],
855 | "source": [
856 | "# CC = A_mid*B_mid .∈ C1;\n",
857 | "# sum(CC[:])\n",
858 | "# println(size(C_int))\n",
859 | "# println(size(C1))\n",
860 | "# println(maximum(radius.(C1[:])))\n",
861 | "println(maximum(C_rad[:]))\n",
862 | "println(maximum(C1_rad[:]))\n",
863 | "# println(size(C_int))"
864 | ]
865 | },
866 | {
867 | "cell_type": "code",
868 | "execution_count": 23,
869 | "metadata": {},
870 | "outputs": [
871 | {
872 | "name": "stdout",
873 | "output_type": "stream",
874 | "text": [
875 | " 0.443668 seconds (595.64 k allocations: 275.514 MiB, 3.45% gc time, 63.26% compilation time)\n"
876 | ]
877 | }
878 | ],
879 | "source": [
880 | "include(\"IntervalFunctions.jl\")\n",
881 | "n = 1000\n",
882 | "A, B = randn(n,n), randn(n,n)\n",
883 | "iA, iB = interval(A), interval(B)\n",
884 | "@time iC = int_mul(iA, iB);"
885 | ]
886 | },
887 | {
888 | "cell_type": "markdown",
889 | "metadata": {},
890 | "source": [
891 | "**注意** `mm_ufp`と`imm_ufp`は入力の行列 $A$, $B$ に応じて使い分けることを計算速度、誤差半径の両面からおすすめする。\n",
892 | "\n",
893 | "### 謝辞\n",
894 | "\n",
895 | "本資料も筆者が学生の頃に精度保証付き数値計算を教えて下さった[柏木雅英](http://www.kashi.info.waseda.ac.jp/~kashi/)先生の「数値解析特論」の講義資料を参考にしています。また、丸め方向を変更しないBLASを使った区間行列積について[森倉悠介](http://y-m.jp/)先生から助言をいただき、`imm_ufp`のMATLABコードをご提供していただきました。ここに感謝申し上げます。\n",
896 | "さらに、以下のような文献・Web ページ等を参考にこの文章は書いています。\n",
897 | "\n",
898 | "\n",
899 | "### 参考文献\n",
900 | "1. S. M. Rump, P. Zimmermann, S. Boldo and G. Melquiond: “Computing predecessor and successor in rounding to nearest”, BIT Vol. 49, No. 2, pp.419–431, 2009.\n",
901 | "(http://www.ti3.tu-harburg.de/paper/rump/RuZiBoMe08.pdf)\n",
902 | "(succ, predのRumpの方法が紹介されている)\n",
903 | "1. 柏木雅英, IEEE754 浮動小数点数の隣の数を計算する方法のまとめ, [http://www.kashi.info.waseda.ac.jp/~kashi/lec2020/nac/succ.pdf](http://www.kashi.info.waseda.ac.jp/~kashi/lec2020/nac/succ.pdf)\n",
904 | "(succ, predの様々な実装方法が紹介されている)\n",
905 | "1. 森倉悠介, 事前誤差評価を用いた線形計算の精度保証 –誤差解析から大規模計算まで–, [http://www.sr3.t.u-tokyo.ac.jp/jsiam/slides/2015/morikura_20151224_yokou_ver1.3.pdf](http://www.sr3.t.u-tokyo.ac.jp/jsiam/slides/2015/morikura_20151224_yokou_ver1.3.pdf)\n",
906 | "(丸め方向を変更しない数値線形代数の区間演算について端的にまとまっている)\n",
907 | "1. 大石進一編著, 精度保証付き数値計算の基礎, コロナ社, 2018.
\n",
908 | "(精度保証付き数値計算の教科書。2章に浮動小数点数の事前誤差解析の話が、3.2章に区間行列積の高速実装についての話がそれぞれ載っている。ここでは紹介しなかったが、この先にエラーフリー変換という沼が待っている。ぜひご体感あれ!)\n",
909 | "\n",
910 | "\n",
911 | "高安亮紀,2020年9月25日(最終更新:2025年5月26日)
"
912 | ]
913 | }
914 | ],
915 | "metadata": {
916 | "kernelspec": {
917 | "display_name": "Julia 1.11.5",
918 | "language": "julia",
919 | "name": "julia-1.11"
920 | },
921 | "language_info": {
922 | "file_extension": ".jl",
923 | "mimetype": "application/julia",
924 | "name": "julia",
925 | "version": "1.11.5"
926 | }
927 | },
928 | "nbformat": 4,
929 | "nbformat_minor": 4
930 | }
931 |
--------------------------------------------------------------------------------
/nonrigorous_numerics.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Juliaで間違える数値計算\n",
8 | "\n",
9 | "この文章は筑波大学大学院システム情報工学研究科リスク工学専攻の紀要[「リスク工学研究」Vol. 16, pp.27-30](https://www.risk.tsukuba.ac.jp/pdf/bulletin16.pdf)に掲載された内容をJuliaを用いた計算例を加えて加筆修正したものです。\n",
10 | "\n",
11 | "\n",
12 | "## はじめに\n",
13 | "\n",
14 | "「数値計算・数値解析」という言葉、日頃よく耳にすると思いますが、**「数値計算・数値解析」とは何でしょう**か。本稿は、まずこの二つの言葉を明確に区別する事から始めます。今日、筆者が知る限り、「数値解析」は同じ言葉で二つの使い方をされます。一つは、「数値解析」とは数値を用いて代数的操作によって解くことができない数学の問題を解決する手法とする使い方、数値計算・数値実験・数値シミュレーションなどと同義のように使用されます。もう一つは、「数値解析」とは応用数学の一分野で、上記の数学問題を、数値を用いて近似的に解く手法に関する数学的な概念を研究する分野とする使い方。筆者は後者の立場で、数値解析という言葉を使います。すなわち、**数値を用いた計算を数値計算、数値計算に関する数学分野を数値解析**とします。\n",
15 | "\n",
16 | "さて、区別をはっきりさせたところで質問があります。数値計算の結果はいつも正しいのでしょうか?普段、数値計算を頻繁に利用するけれども、その計算結果を闇雲に信じてはいませんか。あるいは「数値計算には誤差がある」という知識がある人は計算結果は正しいかもしれないし、間違っているときもあるだろうと認識されているかもしれません。本稿の目的は、数値計算の結果が間違える事がある例をいくつか紹介し、数値計算の不正確さ・不確実性を明確にし、数値計算による間違いのリスクを明らかにする事です。\n",
17 | "\n",
18 | "では、古典的な電卓(電子卓上計算機)の計算結果から紹介します。お手元の電卓 に以下の計算をさせてみて下さい。\n",
19 | "\n",
20 | "$$\n",
21 | "\\begin{array}{rrrl}\n",
22 | "&100 & 000 & 000\\\\\n",
23 | "-)& 9 & 999 & 999.99 \\\\\n",
24 | "\\hline\n",
25 | "\\end{array}\n",
26 | "$$\n",
27 | "\n",
28 | "答えは\n",
29 | "\n",
30 | "$$\n",
31 | "\\begin{array}{rrrl}\n",
32 | "&100 & 000 & 000\\\\\n",
33 | "-)& 9 & 999 & 999.99 \\\\\n",
34 | "\\hline\n",
35 | "&\\color{red}{90} &\\color{red} {000} &\\color{red} {000.0}~(!?)\n",
36 | "\\end{array}\n",
37 | "$$\n",
38 | "\n",
39 | "流石にこれはまずい。一目で間違えに気づきます。筆者の手元のiPhoneの電卓は9桁の表示領域があり、これに2桁加えた11桁の固定小数で計算が実行されます。すると\n",
40 | "\n",
41 | "$$\n",
42 | "\\begin{array}{rrrl}\n",
43 | "&100 & 000 & 000.\\color{red} {00}|\\\\\n",
44 | "-)& 9 & 999 & 999.99|\\color{red} {00}\\\\\n",
45 | "\\hline\n",
46 | "&90 & 000 & 000.01\n",
47 | "\\end{array}\n",
48 | "$$\n",
49 | "\n",
50 | "一見、正しい答えが計算されているように見えますが、表示できる数値が9桁のため\n",
51 | "\n",
52 | "$$\n",
53 | "\\begin{array}{rrrl}\n",
54 | "&100 & 000 & 000.00\\\\\n",
55 | "-)& 9 & 999 & 999.99{00}\\\\\n",
56 | "\\hline\n",
57 | "&90 & 000 & 000.0\\not{1}\n",
58 | "\\end{array}\n",
59 | "$$\n",
60 | "\n",
61 | "このように最後の「1」は消えてしまいます。数値計算が間違えた時、一番の問題は結果が間違っていても実行した計算機は知らせてくれない事です。この問題は結果を見れば間違いに気づきますが、少し複雑化した次の場合はどうでしょうか。\n",
62 | "\n",
63 | "## フェルマーの最終定理\n",
64 | "\n",
65 | "> $3$ 以上の自然数 $n$ について、\n",
66 | "> \n",
67 | "> $$\n",
68 | " \tx^n + y^n = z^n\n",
69 | "$$\n",
70 | "> \n",
71 | "> となる $0$ でない自然数 $(x, y, z)$ の組が存在しない。\n",
72 | "\n",
73 | "これは17世紀、数学者のフェルマーによって古代ギリシャの数学者ディオファントスの著作「算術」の余白に「この定理に関して、私は真に驚くべき証明を見つけたが、この余白はそれを書くには狭すぎる」というメモとともに書かれた定理で、長い間数学の未解決問題として残っていた問題です。そして1995年、この定理はイギリスの数学者ワイルズによって完全に証明され (文献1)、その証明の顛末を含めて、大変脚光を浴びました。\n",
74 | "\n",
75 | "筆者はこの定理の証明に関して、これ以上書ける事はないのですが、数値計算を使うと以下の主張ができます。\n",
76 | "\n",
77 | "\n",
78 | "> $(x,y,z)=(139,954,2115)$ において\n",
79 | "> \n",
80 | "> $$\n",
81 | " \t139^3 + 954^3 = 2115^3\n",
82 | " $$\n",
83 | "> \n",
84 | "> が成り立つ!?\n",
85 | "\n",
86 | "これはフェルマーの最終定理の反例です。根拠として以下を示します。"
87 | ]
88 | },
89 | {
90 | "cell_type": "code",
91 | "execution_count": 1,
92 | "metadata": {},
93 | "outputs": [
94 | {
95 | "name": "stdout",
96 | "output_type": "stream",
97 | "text": [
98 | "Julia Version 1.11.5\n",
99 | "Commit 760b2e5b739 (2025-04-14 06:53 UTC)\n",
100 | "Build Info:\n",
101 | " Official https://julialang.org/ release\n",
102 | "Platform Info:\n",
103 | " OS: macOS (arm64-apple-darwin24.0.0)\n",
104 | " CPU: 8 × Apple M2\n",
105 | " WORD_SIZE: 64\n",
106 | " LLVM: libLLVM-16.0.6 (ORCJIT, apple-m2)\n",
107 | "Threads: 4 default, 0 interactive, 2 GC (on 4 virtual cores)\n",
108 | "Environment:\n",
109 | " JULIA_NUM_THREADS = 4\n"
110 | ]
111 | }
112 | ],
113 | "source": [
114 | "versioninfo()"
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "execution_count": 2,
120 | "metadata": {},
121 | "outputs": [
122 | {
123 | "name": "stdout",
124 | "output_type": "stream",
125 | "text": [
126 | "x ^ 3 + y ^ 3 = 870936283\n",
127 | "z ^ 3 = 870936283\n",
128 | "Counter example of Fermat's theorem\n",
129 | "(x, y, z) = (139, 954, 2115)\n"
130 | ]
131 | }
132 | ],
133 | "source": [
134 | "x = Int32(139)\n",
135 | "y = Int32(954)\n",
136 | "z = Int32(2115)\n",
137 | "@show x ^ 3 + y ^ 3\n",
138 | "@show z ^ 3\n",
139 | "if x ^ 3 + y ^ 3 == z ^ 3\n",
140 | " println(\"Counter example of Fermat's theorem\")\n",
141 | " println(\"(x, y, z) = ($x, $y, $z)\")\n",
142 | "end\n",
143 | "\n",
144 | "# x = 139; y = 954; z = 2115\n",
145 | "# @show x ^ 3 + y ^ 3\n",
146 | "# @show z ^ 3"
147 | ]
148 | },
149 | {
150 | "cell_type": "code",
151 | "execution_count": 3,
152 | "metadata": {},
153 | "outputs": [
154 | {
155 | "name": "stdout",
156 | "output_type": "stream",
157 | "text": [
158 | "x ^ 3 + y ^ 3 = 870936283\n",
159 | "z ^ 3 = 9460870875\n"
160 | ]
161 | },
162 | {
163 | "data": {
164 | "text/plain": [
165 | "9460870875"
166 | ]
167 | },
168 | "metadata": {},
169 | "output_type": "display_data"
170 | }
171 | ],
172 | "source": [
173 | "x = 139; y = 954; z = 2115\n",
174 | "@show x ^ 3 + y ^ 3\n",
175 | "@show z ^ 3"
176 | ]
177 | },
178 | {
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": [
182 | "もちろん(!)筆者の数値計算結果は間違っており、これは符号付き整数の計算機内での表現が引き起こす間違いが原因です。正しくは\n",
183 | "\n",
184 | "$$\n",
185 | "(x^3,y^3,z^3)=(2685619,868250664,9460870875)\n",
186 | "$$\n",
187 | "\n",
188 | "となり反例にはなりません。**数の表現範囲(精度)が足りなかった**のです。\n",
189 | "\n",
190 | "\n",
191 | "## Rumpの例題\n",
192 | "\n",
193 | "では十分精度があれば、正しい結果が得られるのでしょうか?先の例では32ビット符号付き整数という数値を用いて失敗しました。もっと数の表現範囲が増える64ビット浮動小数点数(binary64と呼ばれる計算機上での小数の標準的表現方法)や128ビットのdouble-double (dd)型(四倍精度には仮数部が足りない擬似四倍精度)、あるいはそれ以上の桁数の精度を使って計算したら…\n",
194 | "\n",
195 | "次の例は Rumpの例題(文献2)として有名な例題です。\n",
196 | "\n",
197 | "> $2$ 変数 $a$, $b$ を引数に持つ非線形関数\n",
198 | "> \n",
199 | "> $$\n",
200 | "\tf(a,b)=(333.75-a^2)b^6+a^2(11a^2b^2-121b^4-2)+5.5b^8+\\frac{a}{2b}\n",
201 | "$$\n",
202 | "> \n",
203 | "> について $a=77617$, $b=33096$ のときの値を求めよ。\n",
204 | "\n",
205 | "この関数、最大8次の多項式があるので、手計算はまずやりたくありません。そこで数値計算の出番ですが、先ほど懲りましたので、今回はいくつかの精度(`Float32`、`Float64`、dd型、真の四倍精度に相当する仮数部113桁の`BigFloat`型)で計算をしてみます。\n",
206 | "\n",
207 | "注意:Juliaでdd型を実装するパッケージとしてここでは`DoubleFloats`を使いました。`setprecision(BigFloat, 106)`としても同じ計算ができます。"
208 | ]
209 | },
210 | {
211 | "cell_type": "code",
212 | "execution_count": 4,
213 | "metadata": {},
214 | "outputs": [
215 | {
216 | "name": "stdout",
217 | "output_type": "stream",
218 | "text": [
219 | "float: 1.172604\n",
220 | "double: 1.1726039400531787\n",
221 | "dd: 1.17260394005317863185883490452018014\n",
222 | "qd: 1.17260394005317863185883490452018380e+00\n",
223 | "BigFloat: 1.1726039400531786318588349045201838\n"
224 | ]
225 | }
226 | ],
227 | "source": [
228 | "using DoubleFloats\n",
229 | "# f(a, b) = (333.75 - a ^ 2) * b^6 + a ^ 2 * (11a ^ 2 * b ^ 2 - 121b ^ 4 - 2) + 5.5b ^ 8 + a / (2b)\n",
230 | "function f(a::T, b::T) where T\n",
231 | " return (convert(T,333.75) - a^2) * b^6 + a^2 * (convert(T,11) * a^2 * b^2 - convert(T,121) * b^4 - convert(T,2)) + convert(T,5.5) * b^8 + a / (convert(T,2)*b)\n",
232 | "end\n",
233 | "\n",
234 | "a = Float32(77617)\n",
235 | "b = Float32(33096)\n",
236 | "c = f(a,b)\n",
237 | "println(\"float: $c\")\n",
238 | "\n",
239 | "a = Float64(77617)\n",
240 | "b = Float64(33096)\n",
241 | "c = f(a,b)\n",
242 | "println(\"double: $c\")\n",
243 | "\n",
244 | "a = Double64(77617)\n",
245 | "b = Double64(33096)\n",
246 | "print(\"dd: \")\n",
247 | "showall(f(a,b))\n",
248 | "println(\"\")\n",
249 | "\n",
250 | "using Quadmath\n",
251 | "a = Float128(77617)\n",
252 | "b = Float128(33096)\n",
253 | "c = f(a,b)\n",
254 | "println(\"qd: $c\")\n",
255 | "\n",
256 | "setprecision(113)\n",
257 | "a = BigFloat(77617)\n",
258 | "b = BigFloat(33096)\n",
259 | "c = f(a,b)\n",
260 | "println(\"BigFloat: $c\")"
261 | ]
262 | },
263 | {
264 | "cell_type": "markdown",
265 | "metadata": {},
266 | "source": [
267 | "計算結果はどの数値も $1.17260\\dots$ であり、おそらくこの数値が正しい値であろうと数値計算の結果から予想できます。しかし、真の値は $f(a,b)=-0.827396059946821\\dots$ 符号さえも合っていません。一体何が起こっているのでしょうか。\n",
268 | "\n",
269 | "Rumpの例題は $a=77617$, $b=33096$ のとき $a^2=5.5b^2+1$ という等式が成立する事を巧みに利用して、「桁落ち」という数値計算の誤差が発生するように作られています。従って、単に精度を上げれば計算が正しいとは限らない。さらに恐ろしい事に、**数値計算をしている際に突如このような間違いが起らないと言い切れない**という事です。\n",
270 | "\n",
271 | "ちなみにRumpの例題は仮数部を122桁に設定した小数を使うと正しく計算できます。binary128の113桁ではちょっと足りないというRump先生の匠の技であることがうかがえます。そして普通、そこまで多数桁の計算をする気にはならない。"
272 | ]
273 | },
274 | {
275 | "cell_type": "code",
276 | "execution_count": 5,
277 | "metadata": {},
278 | "outputs": [
279 | {
280 | "name": "stdout",
281 | "output_type": "stream",
282 | "text": [
283 | "BigFloat: -0.82739605994682136814116509547981629201\n"
284 | ]
285 | }
286 | ],
287 | "source": [
288 | "setprecision(122)\n",
289 | "a = BigFloat(77617)\n",
290 | "b = BigFloat(33096)\n",
291 | "c = f(a,b)\n",
292 | "println(\"BigFloat: $c\")"
293 | ]
294 | },
295 | {
296 | "cell_type": "code",
297 | "execution_count": 6,
298 | "metadata": {},
299 | "outputs": [
300 | {
301 | "name": "stdout",
302 | "output_type": "stream",
303 | "text": [
304 | " 0.000003 seconds (1 allocation: 16 bytes)\n",
305 | "double: 1.1726039400531787\n"
306 | ]
307 | }
308 | ],
309 | "source": [
310 | "a = Float64(77617)\n",
311 | "b = Float64(33096)\n",
312 | "@time c = f(a,b)\n",
313 | "println(\"double: $c\")"
314 | ]
315 | },
316 | {
317 | "cell_type": "code",
318 | "execution_count": 7,
319 | "metadata": {},
320 | "outputs": [
321 | {
322 | "name": "stdout",
323 | "output_type": "stream",
324 | "text": [
325 | " 0.303327 seconds (670.76 k allocations: 32.367 MiB, 99.97% compilation time)\n"
326 | ]
327 | },
328 | {
329 | "data": {
330 | "text/plain": [
331 | "[-0.827397, -0.827396]₁₂₂_com_NG"
332 | ]
333 | },
334 | "metadata": {},
335 | "output_type": "display_data"
336 | }
337 | ],
338 | "source": [
339 | "using IntervalArithmetic\n",
340 | "setprecision(122)\n",
341 | "a = interval(BigFloat,77617)\n",
342 | "b = interval(BigFloat,33096)\n",
343 | "@time c = f(a,b)"
344 | ]
345 | },
346 | {
347 | "cell_type": "markdown",
348 | "metadata": {},
349 | "source": [
350 | "\n",
351 | "またRumpの例題の亜種として柏木の例題「$a=10^9$ に対して、$(a^2+1)(a+1)(a-1)-a^4+1/7$ のときの値を求めよ」というもの(文献3)もあります。"
352 | ]
353 | },
354 | {
355 | "cell_type": "code",
356 | "execution_count": 8,
357 | "metadata": {},
358 | "outputs": [
359 | {
360 | "name": "stdout",
361 | "output_type": "stream",
362 | "text": [
363 | "g(a) = 0.14285714285714285\n",
364 | "g(a) = 0.14285714285714285\n",
365 | "g(a) = 1.42857142857142857142857142857142417e-01\n",
366 | "g(a) = -0.85714285714285714285714285714285714296\n"
367 | ]
368 | }
369 | ],
370 | "source": [
371 | "function g(a::T) where T\n",
372 | " return (a^2 + convert(T,1))*(a + convert(T,1))*(a - convert(T,1))-a^4 + convert(T,1)/7.\n",
373 | "end\n",
374 | "a = Float32(10^9)\n",
375 | "@show g(a)\n",
376 | "a = Float64(10^9)\n",
377 | "@show g(a)\n",
378 | "a = Double64(10^9)\n",
379 | "@show g(a)\n",
380 | "setprecision(120)\n",
381 | "a = BigFloat(10^9)\n",
382 | "@show g(a);"
383 | ]
384 | },
385 | {
386 | "cell_type": "markdown",
387 | "metadata": {},
388 | "source": [
389 | "## 連立一次方程式\n",
390 | "\n",
391 | "ここまでは関数値の評価などの数値演算に起こる間違いの事例を紹介しました。もう少し実用的な数値計算についても考えます。基本的な数値計算として、連立一次方程式の求解を考えましょう。数値計算法としては掃き出し法やGaussの消去法という名前の計算方法を使います。いま、連立一次方程式\n",
392 | "\n",
393 | "$$\n",
394 | "\\left\\{\\begin{array}{c}\n",
395 | "64919121 x-159018721 y=1 \\\\\n",
396 | "41869520.5 x-102558961 y=0\n",
397 | "\\end{array}\\right.\n",
398 | "$$\n",
399 | "\n",
400 | "が与えられた(文献4)とします。連立一次方程式の求解も手計算は複雑になるため、できれば数値計算を実行したい。実行速度はとても速く、解は $x=1.45867\\times 10^8$, $y=5.95501\\times 10^7$ と瞬時に計算されます。しかし、実際の解は\n",
401 | "\n",
402 | "$$\n",
403 | "x=205117922,\\quad y=83739041.\n",
404 | "$$\n",
405 | "\n",
406 | "またしても数値計算は計算間違いを指摘することができずに、間違った結果を返してしまいました。連立一次方程式の求解は様々な数値解法に現れる最も基本的な数値計算です。さらに大規模な問題に対しては数値計算の結果を確認しづらい面もあります。そのような問題を数値計算する際、解が正しいのか疑い出すと怖くて数値計算なんてできなくなってしまいます。"
407 | ]
408 | },
409 | {
410 | "cell_type": "code",
411 | "execution_count": 9,
412 | "metadata": {},
413 | "outputs": [
414 | {
415 | "data": {
416 | "text/plain": [
417 | "2-element Vector{Float64}:\n",
418 | " 1.4586712354655445e8\n",
419 | " 5.955000382276195e7"
420 | ]
421 | },
422 | "metadata": {},
423 | "output_type": "display_data"
424 | }
425 | ],
426 | "source": [
427 | "A = [64919121 -159018721; 41869520.5 -102558961]\n",
428 | "b = [1; 0]\n",
429 | "x = A\\b\n",
430 | "# A*x - b"
431 | ]
432 | },
433 | {
434 | "cell_type": "markdown",
435 | "metadata": {},
436 | "source": [
437 | "## 数値誤差に対する処方箋\n",
438 | "\n",
439 | "一体どのようにしたら数値計算の間違いを正しく認識する事ができるのでしょうか。そのための一つの方法として、**数学の知識を使う**事が考えられます。電卓の例は自明にしても、フェルマーの最終定理に対してはこの定理が数学的に証明されている事から反例は原則ありえません。Rumpの例題に対しては、「桁落ち」という数値計算に起こる誤差を知っておくと怪しいと考える事ができます。さらに連立一次方程式では「行列の条件数」という数を考えると\n",
440 | "\n",
441 | "$$\n",
442 | "\\mathrm{cond}(A) = 1.52008\\times 10^{16}\n",
443 | "$$\n",
444 | "\n",
445 | "という数値が計算され、これは数値解の相対的な誤差が最大10進16桁ほど混入する可能性を示唆しています。先に述べた64ビット浮動小数点数が、概ね16桁の精度を持つと言われているため、この誤差の大きさは致命的です。すなわち解は1桁も合わない事になります。実際に前節の例では1桁も数値が合いませんでした。"
446 | ]
447 | },
448 | {
449 | "cell_type": "code",
450 | "execution_count": 10,
451 | "metadata": {},
452 | "outputs": [
453 | {
454 | "data": {
455 | "text/plain": [
456 | "1.5200769519527394e16"
457 | ]
458 | },
459 | "metadata": {},
460 | "output_type": "display_data"
461 | }
462 | ],
463 | "source": [
464 | "using LinearAlgebra\n",
465 | "cond(A)"
466 | ]
467 | },
468 | {
469 | "cell_type": "markdown",
470 | "metadata": {},
471 | "source": [
472 | "このように、数学の知識と数値計算を合わせる事で、数値計算に対するリスクを制御しようというのが一つのアプローチ方法です。そしてこの考え方が筆者の研究活動の根源です。\n",
473 | " 数値計算の名誉挽回のために、これらは人工的に作成された問題である事を強調しておきます。しかし、このような問題が実際の数値計算において起きうる事は数値計算のリスクとして認識して欲しいところです。今日の計算機で使用されている64ビット浮動小数点数の規格の制定に尽力したW. M. Kahanの言葉を借りると、\n",
474 | "\n",
475 | "> 浮動小数点演算によって得られた結果と真値に大きな差が生じることは非常に稀であり、つねに心配するにはあまりにも稀であるが、だからといって無視できるほど稀なわけではない。\n",
476 | "\n",
477 | "という事になります。非常にオブラートに包まれた感じがしますが、毎日の数値計算に対して杞憂するほど、数値計算は間違えない事も事実です。"
478 | ]
479 | },
480 | {
481 | "cell_type": "markdown",
482 | "metadata": {},
483 | "source": [
484 | "## おわりに\n",
485 | "\n",
486 | "最後に、筆者の研究テーマである「**精度保証付き数値計算**」について紹介して、本稿を擱筆したいと思います。「精度保証付き数値計算」とは数学的に正しい結果を数値計算によって導く手法全般、数値計算結果の品質保証だけでなく、計算機を援用する数学解析手法も「精度保証付き数値計算」といいます。精度保証付き数値計算を実行するためには数値計算に生じる全ての誤差(図1、離散化誤差、打ち切り誤差、丸め誤差等)をすべて把握する必要があります。手法の基本原則として、区間の端点に浮動小数点数を用いる(機械)区間演算という演算規則を使用して、数値の代わりに区間を利用して数値計算を実行します。精度保証付き数値計算は計算を間違えないので、先のような例に遭遇すると区間の幅が著しく増大したり、解の検証が失敗して、エラーとなり警告を発します。これにより数値計算の間違いを見落とす事がありません。\n",
487 | "\n",
488 | "一方で、課題として、本来の数値計算に比べて実行速度が遅い(検算を伴うため、原則、数値計算よりも実行時間を要する)、警告を発して計算が度々失敗するなど、本来の数値計算に比べて「有用性」の面で劣る部分があります。従って、精度保証付き数値計算の有用性を世の中に浸透させ、多くの人に使ってもらうための研究が、今後、本分野の研究者には必須です。また、「精度保証付き数値計算は難しい」、「数学も計算機の知識も必要で敷居が高い」、「細かな職人芸が必要になる」、「プログラミングによる実装力が必要」という理由で研究対象としても敬遠されがちです。\n",
489 | "\n",
490 | "筆者の研究に対する基本姿勢は、**精度保証付き数値計算を使って何ができるかを示す**事であり、様々な数理モデルに現れる微分方程式(偏微分方程式・遅延微分方程式・常微分方程式)を対象に、精度保証付き数値計算による計算機援用証明を研究主題としています。数理モデルの間違えない数値計算は、すなわち現象の正しい理解へとつながり、モデルの信頼性検証が可能になります。今後も精度保証付き数値計算を使ってできる事を発信し続けます。"
491 | ]
492 | },
493 | {
494 | "cell_type": "markdown",
495 | "metadata": {},
496 | "source": [
497 | "### 謝辞\n",
498 | "\n",
499 | "本資料はルンプの例題のS.M. Rump先生、[柏木雅英](http://www.kashi.info.waseda.ac.jp/~kashi/)先生との雑談が元になっています。他にも精度保証付き数値計算の研究に従事されている先生方には日頃から様々なことをご教示いただいています。ここに感謝の意を表します。また、以下のような文献・Web ページ等を参考にこの文章は書いています。\n",
500 | "\n",
501 | "### 参考文献\n",
502 | "\n",
503 | "1. A. Wiles, Modular elliptic curves and Fermat's Last Theorem, Annals of Mathematics, 141 (3), pp. 443-551, 1995.\n",
504 | "1. E. Loh, G. W. Walster, Rump's Example Revisited, Reliable Computing, 8 (3), pp. 245-248, 2002.\n",
505 | "1. https://twitter.com/mkashi/status/1176148600373633024?s=20\n",
506 | "1. U. W. Kulisch, W. L. Miranker, The Arithmetic of the Digital Computer: A New Approach, SIAM Review, 28, pp. 1-40, 1986.\n",
507 | "1. W. M. Kahan, The Regrettable Failure of Automated Error Analysis, A Mini-Course prepared for the conference at MIT on Computers and Mathematics, 1989.\n"
508 | ]
509 | },
510 | {
511 | "cell_type": "markdown",
512 | "metadata": {},
513 | "source": [
514 | "高安亮紀,2020年12月16日(最終更新:2025年5月19日)
"
515 | ]
516 | }
517 | ],
518 | "metadata": {
519 | "kernelspec": {
520 | "display_name": "Julia 1.11.5",
521 | "language": "julia",
522 | "name": "julia-1.11"
523 | },
524 | "language_info": {
525 | "file_extension": ".jl",
526 | "mimetype": "application/julia",
527 | "name": "julia",
528 | "version": "1.11.5"
529 | }
530 | },
531 | "nbformat": 4,
532 | "nbformat_minor": 4
533 | }
534 |
--------------------------------------------------------------------------------
/rounding-error.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 数値計算の誤差\n",
8 | "\n",
9 | "数値計算に混入する誤差を紹介する.\n",
10 | "\n",
11 | "- 丸め誤差\n",
12 | " - 桁落ち\n",
13 | " - 情報落ち\n",
14 | "- 打ち切り誤差\n",
15 | "- 離散化誤差\n",
16 | "\n",
17 | "注)このほかに現象を数値計算で理解・予測する際にはモデル化誤差という数値計算ではどうにもならない誤差もある.\n",
18 | "\n",
19 | "## 丸め誤差\n",
20 | "浮動小数点数を使用した演算に混入する誤差について述べる\n",
21 | "\n",
22 | "浮動小数点数同士の演算(加減乗除など)の結果は,浮動小数点数で表せるとは限らない.\n",
23 | "例えば,10進数で仮数部3桁の浮動小数点演算を考え,2/3を計算すると,\n",
24 | "\n",
25 | "$$\n",
26 | "\t2.00\\times 10^0/3.00\\times 10^0=0.66666666...\\times 10^0\n",
27 | "$$\n",
28 | "\n",
29 | "となり,仮数部3桁に収まらない.仮数部の4桁目で四捨五入を行うと\n",
30 | "\n",
31 | "$$\n",
32 | "\t6.67\\times10^{-1}\n",
33 | "$$\n",
34 | "\n",
35 | "となる.このときの計算値と真値との差\n",
36 | "\n",
37 | "$$\n",
38 | "\t6.67\\times10^{-1}-6.6666666...\\times10^{-1}=3.3333333...\\times10^{-4}\n",
39 | "$$\n",
40 | "\n",
41 | "が丸め誤差である.\n",
42 | "\n",
43 | "IEEE 754は2進数浮動小数点数なので,基本的には0捨1入で丸められる.\n",
44 | "例えば,10進数の「0.1」をIEEE 754のbinary64に変換してみると\n",
45 | "\n",
46 | "\\begin{align*}\n",
47 | "\t0.1_{(10)} &= 0.000110011001100110011..._{(2)}\\\\\n",
48 | "\t& = 1.10011001100110011...\\times 2^{m},\n",
49 | "\\end{align*}\n",
50 | "\n",
51 | "$m$は$-4+1023=1019$の2進表現,つまり$m=01111111011$.\n",
52 | "仮数部は無限小数になっているのでそのまま格納出来ない.\n",
53 | "小数点以下を 52bit以内とそれ以降で区切って表示すると\n",
54 | "\n",
55 | "$\\fbox{1001100110011001100110011001100110011001100110011001}\\fbox{10011001100....}$\n",
56 | "\n",
57 | "となり,はみ出た部分の先頭が「1」なので、0捨1入で繰り上げる.\n",
58 | "最終的には, 10進数の「0.1」は\n",
59 | "\n",
60 | "$\\fbox{0}\\fbox{01111111011}\\fbox{1001100110011001100110011001100110011001100110011010}$\n",
61 | "\n",
62 | "のように格納されている."
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": 1,
68 | "metadata": {},
69 | "outputs": [
70 | {
71 | "name": "stdout",
72 | "output_type": "stream",
73 | "text": [
74 | "Julia Version 1.11.5\n",
75 | "Commit 760b2e5b739 (2025-04-14 06:53 UTC)\n",
76 | "Build Info:\n",
77 | " Official https://julialang.org/ release\n",
78 | "Platform Info:\n",
79 | " OS: macOS (arm64-apple-darwin24.0.0)\n",
80 | " CPU: 8 × Apple M2\n",
81 | " WORD_SIZE: 64\n",
82 | " LLVM: libLLVM-16.0.6 (ORCJIT, apple-m2)\n",
83 | "Threads: 4 default, 0 interactive, 2 GC (on 4 virtual cores)\n",
84 | "Environment:\n",
85 | " JULIA_NUM_THREADS = 4\n"
86 | ]
87 | }
88 | ],
89 | "source": [
90 | "versioninfo()"
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": 2,
96 | "metadata": {},
97 | "outputs": [
98 | {
99 | "name": "stdout",
100 | "output_type": "stream",
101 | "text": [
102 | "0011111110111001100110011001100110011001100110011001100110011010\n"
103 | ]
104 | }
105 | ],
106 | "source": [
107 | "x = 0.1;\n",
108 | "println(bitstring(x))"
109 | ]
110 | },
111 | {
112 | "cell_type": "markdown",
113 | "metadata": {},
114 | "source": [
115 | "10進数の0.1は計算機には正確に格納できず,少しだけ0.1より大きい値で格納されており,\n",
116 | "**丸め誤差**が含まれている\n",
117 | "\n",
118 | "$$\n",
119 | " \\displaystyle\\frac{1}{10}\\approx{\\color{red}{0.1000000000000000}}055511151231257827021181583404541015625\n",
120 | "$$"
121 | ]
122 | },
123 | {
124 | "cell_type": "code",
125 | "execution_count": 3,
126 | "metadata": {},
127 | "outputs": [
128 | {
129 | "name": "stdout",
130 | "output_type": "stream",
131 | "text": [
132 | "0.1000000000000000055511151231257827021181583404541015625"
133 | ]
134 | }
135 | ],
136 | "source": [
137 | "using Printf\n",
138 | "@printf(\"%.55f\", x)"
139 | ]
140 | },
141 | {
142 | "cell_type": "markdown",
143 | "metadata": {},
144 | "source": [
145 | "さらに, 0.1を逐次的に加えていくと計算結果は丸め誤差の影響により, 不可解な計算結果を返す事がある."
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": 4,
151 | "metadata": {},
152 | "outputs": [
153 | {
154 | "name": "stdout",
155 | "output_type": "stream",
156 | "text": [
157 | "9.9999999999999804600747665972448885440826416015625000000\n"
158 | ]
159 | }
160 | ],
161 | "source": [
162 | "x = 0.0;\n",
163 | "for i = 1:100\n",
164 | " x += 0.1;\n",
165 | "end\n",
166 | "@printf(\"%.55f\\n\", x)"
167 | ]
168 | },
169 | {
170 | "cell_type": "markdown",
171 | "metadata": {},
172 | "source": [
173 | "## 丸めモード\n",
174 | "\n",
175 | "IEEE754では,4つの丸めモードを用意している.$\\tilde{x}$を実数($\\tilde{x}\\in\\mathbb{R}$)とする.いま実数 $\\tilde x$ が\n",
176 | "\n",
177 | "$$\\tilde x=\\pm\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\frac{d_2}{2^2}+\\dots\\right)2^{e}_{(10)}$$\n",
178 | "\n",
179 | "であるときに,その近似\n",
180 | "\n",
181 | "$$x=\\pm\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\frac{d_2}{2^2}+\\cdots+\\frac{d_{52}}{2^{52}}\\right)2^{e}_{(10)}\\in\\mathbb{F}$$\n",
182 | "\n",
183 | "を採用する丸めは**原点方向への丸め**(切り捨て)と呼ばれている.\n",
184 | "\n",
185 | "また $\\tilde x$ の近似として誤差$|x-\\tilde{x}|$の値が最小になる$x\\in\\mathbb{F}$,すなわち\n",
186 | "\n",
187 | "$$\n",
188 | " |x-\\tilde{x}|=\\min_{y\\in\\mathbb{F}}|y-\\tilde{x}|\n",
189 | "$$\n",
190 | "\n",
191 | "をみたす$x\\in\\mathbb{F}$を採用する方法を**最近点への丸め**という.しかし $\\tilde x$ が2つの浮動小数点数\n",
192 | "\n",
193 | "$$\n",
194 | " x_1=\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\frac{d_2}{2^2}+\\cdots+\\frac{d_{52}}{2^{52}}\\right)2^{e}_{(10)},~~~x_2=\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\frac{d_2}{2^2}+\\cdots+\\frac{d_{52}+1}{2^{52}}\\right)2^{e}_{(10)}\n",
195 | "$$\n",
196 | "\n",
197 | "の中点となると$|x_1-\\tilde{x}|=|x_2-\\tilde{x}|$が成り立ち,$x$は一つに決まらない.このときは**仮数部の最後のビットが0**になる方に丸めが実行される.これを**最近偶数への丸め**という.\n"
198 | ]
199 | },
200 | {
201 | "cell_type": "code",
202 | "execution_count": 5,
203 | "metadata": {},
204 | "outputs": [
205 | {
206 | "name": "stdout",
207 | "output_type": "stream",
208 | "text": [
209 | "1.00000000000000000000000000000000000000000000000000\n",
210 | "0011111111110000000000000000000000000000000000000000000000000000\n",
211 | "1.00000000000000022204460492503130808472633361816406\n",
212 | "0011111111110000000000000000000000000000000000000000000000000001\n",
213 | "1.00000000000000000000000000000000000000000000000000\n",
214 | "0011111111110000000000000000000000000000000000000000000000000000\n"
215 | ]
216 | }
217 | ],
218 | "source": [
219 | "eps = 2. ^ (-52) # unit round off\n",
220 | "x = 1. + eps/4.\n",
221 | "@printf(\"%.50f\\n\", x)\n",
222 | "println(bitstring(x))\n",
223 | "\n",
224 | "y = 1. + 3*(eps/4.)\n",
225 | "@printf(\"%.50f\\n\", y)\n",
226 | "println(bitstring(y))\n",
227 | "\n",
228 | "z = 1. + (eps/2.)\n",
229 | "@printf(\"%.50f\\n\", z)\n",
230 | "println(bitstring(z))"
231 | ]
232 | },
233 | {
234 | "cell_type": "code",
235 | "execution_count": 6,
236 | "metadata": {},
237 | "outputs": [
238 | {
239 | "name": "stdout",
240 | "output_type": "stream",
241 | "text": [
242 | "2.00000000000000000000000000000000000000000000000000\n",
243 | "0100000000000000000000000000000000000000000000000000000000000000\n",
244 | "1.99999999999999977795539507496869191527366638183594\n",
245 | "0011111111111111111111111111111111111111111111111111111111111111\n",
246 | "2.00000000000000000000000000000000000000000000000000\n",
247 | "0100000000000000000000000000000000000000000000000000000000000000\n"
248 | ]
249 | }
250 | ],
251 | "source": [
252 | "x = 2.\n",
253 | "@printf(\"%.50f\\n\", x)\n",
254 | "println(bitstring(x))\n",
255 | "\n",
256 | "y = 2. - eps\n",
257 | "@printf(\"%.50f\\n\", y)\n",
258 | "println(bitstring(y))\n",
259 | "\n",
260 | "z = 2. - eps/2.\n",
261 | "@printf(\"%.50f\\n\", z)\n",
262 | "println(bitstring(z))"
263 | ]
264 | },
265 | {
266 | "cell_type": "markdown",
267 | "metadata": {},
268 | "source": [
269 | "次は$\\tilde{x}$よりも必ず大きな$a\\in\\mathbb{F}$,すなわち\n",
270 | "\n",
271 | "$$\n",
272 | " a=\\min\\left\\{x\\in\\mathbb{F}:x\\ge\\tilde{x}\\right\\}\n",
273 | "$$\n",
274 | "\n",
275 | "をみたす$a\\in\\mathbb{F}$を採用する方法を**$+\\infty$方向への丸め**(上向き丸め)という.逆に$\\tilde{x}$よりも必ず小さな$b\\in\\mathbb{F}$,すなわち\n",
276 | "\n",
277 | "$$\n",
278 | " b=\\max\\left\\{x\\in\\mathbb{F}:x\\le\\tilde{x}\\right\\}\n",
279 | "$$\n",
280 | "\n",
281 | "をみたす$b\\in\\mathbb{F}$を採用する方法を**$-\\infty$方向への丸め**(下向き丸め)という.これらより\n",
282 | "\n",
283 | "$$ b\\le\\tilde{x}\\le a $$\n",
284 | "\n",
285 | "が常に成立する.特に精度保証付き数値計算ではこの2つの丸めモードを利用することで,*厳密な包含*を得ることができるようになる.まとめると\n",
286 | "\n",
287 | "* 最近点への丸め(デフォルト):$\\tilde{x}$ に最も近い浮動小数点数に丸める.もし2点あるならば仮数部の最後のビットが0である浮動小数点数に丸める.\n",
288 | "* $+\\infty$方向への丸め:$\\tilde{x}$ 以上の浮動小数点数の中で最も小さい浮動小数点数に丸める.\n",
289 | "* $-\\infty$方向への丸め:$\\tilde{x}$ 以下の浮動小数点数の中で最も大きい浮動小数点数に丸める.\n",
290 | "* 原点方向への丸め:絶対値が $\\tilde{x}$ 以下の浮動小数点数の中で,$\\tilde{x}$ に最も近いものに丸める.\n",
291 | "\n",
292 | "## その他の丸め誤差いろいろ\n",
293 | "\n",
294 | "丸め誤差は実数を浮動小数点数で近似する際の誤差であった.ここでは浮動小数点数を用いた演算のその他の問題点を紹介する.\n",
295 | "\n",
296 | "### 桁落ち\n",
297 | "極めて近い数どうしの減算によって,誤差が著しく大きくなってしまう現象.\n",
298 | "2つの浮動小数点数\n",
299 | "\n",
300 | "$$\n",
301 | " x=\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\dots+\\frac{d_p}{2^p}+\\frac{1}{2^{p+1}}+\\cdots+\\frac{b_{p+2}}{2^{p+2}}+\\dots+\\frac{b_{52}}{2^{52}}\\right)2^{e}_{(10)},\n",
302 | "$$\n",
303 | "$$\n",
304 | " y=\\left(\\frac{1}{2^0}+\\frac{d_1}{2^1}+\\dots+\\frac{d_p}{2^p}+\\frac{0}{2^{p+1}}+\\cdots+\\frac{c_{p+2}}{2^{p+2}}+\\dots+\\frac{c_{52}}{2^{52}}\\right)2^{e}_{(10)}\n",
305 | "$$\n",
306 | "\n",
307 | "が$x>y$とし,仮数部の最初から $p$ ビットが等しいとする.このとき\n",
308 | "\n",
309 | "$$\n",
310 | " x-y=\\left(\\frac{1}{2^0}+\\frac{b_{p+2}-c_{p+2}}{2^1}+\\dots+\\frac{b_{52}-c_{52}}{2^{52-p-1}}\\right)2^{e-p-1}_{(10)}\n",
311 | "$$\n",
312 | "\n",
313 | "これよりもともと $52$ 個あった仮数部の情報が,$52-p$ 個に減っている.例を挙げよう.\n",
314 | "\n",
315 | "$b>0$とし,2次方程式 $x^2+bx+c=0$の解の公式\n",
316 | "\n",
317 | "$$\n",
318 | " x_1=\\frac{-b+\\sqrt{b^2-4c}}{2},\\quad x_2=\\frac{-b-\\sqrt{b^2-4c}}{2}\n",
319 | "$$\n",
320 | "\n",
321 | "を考える.いまもしも$b^2\\gg c$となるならば,$b$ と $\\sqrt{b^2-4c}$が近い数になるので,$x_1$の分子の計算で,桁落ちが起こる."
322 | ]
323 | },
324 | {
325 | "cell_type": "code",
326 | "execution_count": 7,
327 | "metadata": {},
328 | "outputs": [
329 | {
330 | "name": "stdout",
331 | "output_type": "stream",
332 | "text": [
333 | "-0.125\n",
334 | "-2.4999999999999984e13\n",
335 | "-0.10000000000000002\n",
336 | "0.0\n"
337 | ]
338 | }
339 | ],
340 | "source": [
341 | "b = 1e+15;\n",
342 | "c = 1e+14;\n",
343 | "\n",
344 | "x1 = (-b + sqrt(b^2 - 4c)) / 2;\n",
345 | "println(x1)\n",
346 | "println(x1^2 + b*x1 + c)\n",
347 | "\n",
348 | "x2 = 2c / (-b - sqrt(b^2 - 4*c))\n",
349 | "println(x2)\n",
350 | "println(x2^2 + b*x2 + c)"
351 | ]
352 | },
353 | {
354 | "cell_type": "markdown",
355 | "metadata": {},
356 | "source": [
357 | "### 情報落ち\n",
358 | "絶対値の大きさが極端に違う2 数の加減算を行った時,小さいほうの数値の下位の桁が失われてしまう現象."
359 | ]
360 | },
361 | {
362 | "cell_type": "code",
363 | "execution_count": 8,
364 | "metadata": {},
365 | "outputs": [
366 | {
367 | "name": "stdout",
368 | "output_type": "stream",
369 | "text": [
370 | "3.141592025756836\n",
371 | "3.14159265358979\n"
372 | ]
373 | }
374 | ],
375 | "source": [
376 | "println((3.14159265358979+1e10)-1e10)\n",
377 | "println(3.14159265358979 + (1e10-1e10))"
378 | ]
379 | },
380 | {
381 | "cell_type": "code",
382 | "execution_count": 9,
383 | "metadata": {},
384 | "outputs": [
385 | {
386 | "name": "stdout",
387 | "output_type": "stream",
388 | "text": [
389 | "0.0\n",
390 | "666.6\n",
391 | "-3.9230984419161617e30\n"
392 | ]
393 | }
394 | ],
395 | "source": [
396 | "println(1e48+543.2-1e48-1e36+123.4+1e36)\n",
397 | "println(1e48-1e48-1e36+1e36+543.2+123.4)\n",
398 | "println(1e48-1e36-1e48+1e36+543.2+123.4)"
399 | ]
400 | },
401 | {
402 | "cell_type": "markdown",
403 | "metadata": {},
404 | "source": [
405 | "## 打ち切り誤差\n",
406 | "無限回行うべき計算を有限回の計算で置き換えることにより生じる誤差.計算機は有限回の四則演算しかできない.そのため,無限級数や収束列のような値を求めるためには,有限項で打ち切った近似値を用いる.その際に誤差が生じる.\n",
407 | "\n",
408 | "(例)Taylor展開の打ち切り誤差,Newton法の打ち切り誤差,数値積分の打ち切り誤差"
409 | ]
410 | },
411 | {
412 | "cell_type": "code",
413 | "execution_count": 10,
414 | "metadata": {},
415 | "outputs": [
416 | {
417 | "name": "stdout",
418 | "output_type": "stream",
419 | "text": [
420 | "4.851651954097903e8\n",
421 | "4.851651954097903e8\n",
422 | "6.147561828914626e-9\n",
423 | "2.061153622438558e-9\n"
424 | ]
425 | }
426 | ],
427 | "source": [
428 | "function exp_taylor(x) # naive implementation of \"exp\" function\n",
429 | " s = 0.0;\n",
430 | " t = 1.0;\n",
431 | " i = 1;\n",
432 | " while true\n",
433 | " s += t;\n",
434 | " if abs(t) < abs(s) * 1e-15\n",
435 | " break\n",
436 | " end\n",
437 | " t *= x / i;\n",
438 | " i += 1;\n",
439 | " end\n",
440 | " return s\n",
441 | "end\n",
442 | "println(exp_taylor(20.))\n",
443 | "println(exp(20.))\n",
444 | "println(exp_taylor(-20.))\n",
445 | "println(exp(-20.))"
446 | ]
447 | },
448 | {
449 | "cell_type": "markdown",
450 | "metadata": {},
451 | "source": [
452 | "## 離散化誤差\n",
453 | "\n",
454 | "離散化誤差の説明はそのうち加える。"
455 | ]
456 | },
457 | {
458 | "cell_type": "markdown",
459 | "metadata": {},
460 | "source": [
461 | "### 謝辞\n",
462 | "\n",
463 | "本資料も筆者が学生の頃に精度保証付き数値計算を教えて下さった[柏木雅英](http://www.kashi.info.waseda.ac.jp/~kashi/)先生の「数値解析特論」の講義資料が基になっています.\n",
464 | "また, 以下のような文献・Web ページ等を参考にこの文章は書いています.\n",
465 | "\n",
466 | "### 参考文献\n",
467 | "\n",
468 | "1. 伊理正夫, 藤野和建, 数値計算の常識, 共立出版, 1985.
\n",
469 | "(Twitterとかでも度々話題に上がる名著. IEEE754 の制定の年にすでに浮動小数点数に対する注意が詰まっている書籍が出版されている. 桁落ち, 情報落ちなどの誤差に詳しい)\n",
470 | "1. 齊藤宣一, 数値解析入門, 東京大学出版会, 2012.
\n",
471 | "(数値解析学の現在最も詳しい教科書)\n",
472 | "1. 大石進一編著, 精度保証付き数値計算の基礎, コロナ社, 2018.
\n",
473 | "(精度保証付き数値計算の教科書. 浮動小数点数および区間演算に詳しい. この1章が読めたら大したもの)\n",
474 | "1. [ushiostarfish, IEEE 754 浮動小数点入門.](https://ushiostarfish.hatenablog.com/entry/2019/08/12/210023)
\n",
475 | "(IEEE 754 浮動小数点数を細かく紹介し, 丸め誤差の詳細, および区間演算について触れている)\n",
476 | "1. Nick Higham, [What Is Rounding?](https://nhigham.com/2020/04/28/what-is-rounding/).
\n",
477 | "(数値解析の超有名人によるブログ記事, 丸めについて端的にまとめられている)"
478 | ]
479 | },
480 | {
481 | "cell_type": "markdown",
482 | "metadata": {},
483 | "source": [
484 | "高安亮紀,2020年7月22日(最終更新:2025年5月19日)
"
485 | ]
486 | }
487 | ],
488 | "metadata": {
489 | "kernelspec": {
490 | "display_name": "Julia 1.11.5",
491 | "language": "julia",
492 | "name": "julia-1.11"
493 | },
494 | "language_info": {
495 | "file_extension": ".jl",
496 | "mimetype": "application/julia",
497 | "name": "julia",
498 | "version": "1.11.5"
499 | }
500 | },
501 | "nbformat": 4,
502 | "nbformat_minor": 4
503 | }
504 |
--------------------------------------------------------------------------------
/verifyeig.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "395c47d9-5053-467f-ad79-40f1044674f4",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# 部分固有値と付随する固有ベクトル(部分固有対)の精度保証付き数値計算\n",
11 | "\n",
12 | "MATLABの区間演算パッケージである[INTLAB](https://www.tuhh.de/ti3/rump/intlab/)には一般化固有値問題の固有値・固有ベクトルを精度保証付きで計算する`verifyeig`という関数が実装されている。本稿では、文献1および2を参考に`verifyeig`関数のアルゴリズムを説明し、一般化固有値問題 \n",
13 | "\n",
14 | "$$\n",
15 | "Ax=\\lambda Bx\n",
16 | "$$\n",
17 | "\n",
18 | "を満たす固有値 $\\lambda\\in\\mathbb{C}$ と固有ベクトル $x\\in\\mathbb{C}^n$ ($n$ は行列の次元) を求める実装を紹介する。\n",
19 | "\n",
20 | "行列 $A,B\\in \\mathbb{C}^{n\\times n}$ を与え、$B$ は正則、$B^{-1}A$ が対角化可能、かつ全ての固有値 $\\lambda$ が重複固有値でないと仮定する。写像 $F:\\mathbb{C}^{n+1}\\to\\mathbb{C}^{n+1}$ を\n",
21 | "\n",
22 | "$$\n",
23 | " F(\\lambda, x) := \\begin{bmatrix}x^Hx -1\\\\ Ax-\\lambda Bx\\end{bmatrix}\n",
24 | "$$\n",
25 | "\n",
26 | "と定義すると $F(\\lambda, x)=0$ をみたす $(\\lambda, x)$ は一般化固有値問題の固有対となる(固有ベクトルは $\\|x\\|_2=1$ と正規化されている)。いま $\\bar x\\in \\mathbb{C}^n$, $\\bar \\lambda \\in \\mathbb{C}$ を $F(\\bar\\lambda,\\bar x)\\approx 0$ とする近似解とし、\n",
27 | "$R\\in \\mathbb{C}^{n+1\\times n+1}$ を写像 $F$ の近似解におけるヤコビ行列の近似逆行列($R\\approx DF(\\bar\\lambda,\\bar x)^{-1}$)とする。部分固有値・固有ベクトルの精度保証付き数値計算には次の定理を利用する。\n",
28 | "\n",
29 | "**定理** $\\boldsymbol{y}\\in \\mathbb{IC}^n, \\boldsymbol{d} \\in \\mathbb{IC}$ をある区間ベクトルと区間として、$\\boldsymbol{w}:=(\\boldsymbol{d},\\boldsymbol{y}^T)^T\\in \\mathbb{IC}^{n+1}$ とする(${}^T$はベクトルの単なる転置を意味する)。 さらに\n",
30 | "\n",
31 | "$$\n",
32 | " g(\\boldsymbol{w}):=z+\\left(I-R\\cdot DF\\left(\\bar\\lambda + \\boldsymbol{d}, \\bar x + \\boldsymbol{y}\\right)\\right)\\boldsymbol{w}\n",
33 | "$$\n",
34 | "\n",
35 | "と定義する。上の式のヤコビ行列 $DF(\\lambda,x)$ と $z$ は\n",
36 | "\n",
37 | "$$\n",
38 | "\\begin{align*}\n",
39 | "DF(\\lambda,x):=\n",
40 | "\\begin{bmatrix}\n",
41 | "0 & 2x^T\\\\\n",
42 | "-B\\boldsymbol{x} & A-\\boldsymbol{\\lambda}B \\\\\n",
43 | "\\end{bmatrix},\\quad\n",
44 | "z:=-R\n",
45 | "\\left[\\begin{array}\n",
46 | "a\\bar x_1^2+\\bar x_2^2+\\dots +\\bar x_n^2-1 \\\\\n",
47 | "A\\bar x-\\bar\\lambda B\\bar x \\\\\n",
48 | "\\end{array}\\right]\n",
49 | "\\end{align*}\n",
50 | "$$\n",
51 | "\n",
52 | "で与えられる。\n",
53 | "このとき $\\mathrm{int}(\\boldsymbol{w})$ を区間ベクトル $\\boldsymbol{w}$ の(要素ごとの)内部とすると、$g(\\boldsymbol{w})\\subset \\mathrm{int}(\\boldsymbol{w})$ ならば、一般化固有値問題 $Ax=\\lambda Bx$ の真の固有値 $\\lambda$ が\n",
54 | "$\\bar \\lambda +\\boldsymbol{d}$ 内に唯一存在し、対応する固有ベクトルが $\\bar x + \\boldsymbol{y}$ に包含される。"
55 | ]
56 | },
57 | {
58 | "cell_type": "markdown",
59 | "id": "9b976357-f6cc-4eef-bb04-5de1c54471eb",
60 | "metadata": {},
61 | "source": [
62 | "この定理をもとに精度保証付き数値計算を次のように実装する。\n",
63 | "\n",
64 | "1. 近似固有値 $\\bar{\\lambda}$、近似固有ベクトル $\\bar{x}$ を入力し、これらが実数であるときは実数の区間・区間ベクトルで、いずれかが複素数であるときは複素数の区間・区間ベクトルで $\\boldsymbol{y}$, $\\boldsymbol{d}$ を定義し、候補ベクトル $\\boldsymbol{w}=(\\boldsymbol{d},\\boldsymbol{y}^T)^T$ を作成する。\n",
65 | "1. ヤコビ行列$DF$ と $z$、$g(\\boldsymbol{w}):=z+\\left(I-R\\cdot DF\\left(\\bar\\lambda + \\boldsymbol{d}, \\bar x + \\boldsymbol{y}\\right)\\right)\\boldsymbol{w}$ を区間演算により計算する。\n",
66 | "1. $g(\\boldsymbol{w})\\subsetneq \\boldsymbol{w}$を満たすとき、$(\\bar\\lambda,\\bar x)^T+g(\\boldsymbol{w})$ の値を返す。\n",
67 | "\n",
68 | "上記の手順に従って次元 $n=30$ のある行列 $A,B\\in \\mathbb{C}^{n\\times n}$ に対し、$Ax=\\lambda Bx$ を考える。\n",
69 | "まず近似固有値$\\bar{\\lambda}$、近似固有ベクトル$\\bar{x}$を計算する。"
70 | ]
71 | },
72 | {
73 | "cell_type": "code",
74 | "execution_count": 1,
75 | "id": "0a2ad074-cca0-4a3e-b867-cfcff8def16f",
76 | "metadata": {},
77 | "outputs": [
78 | {
79 | "name": "stdout",
80 | "output_type": "stream",
81 | "text": [
82 | "Julia Version 1.10.0\n",
83 | "Commit 3120989f39b (2023-12-25 18:01 UTC)\n",
84 | "Build Info:\n",
85 | " Official https://julialang.org/ release\n",
86 | "Platform Info:\n",
87 | " OS: macOS (arm64-apple-darwin22.4.0)\n",
88 | " CPU: 14 × Apple M3 Max\n",
89 | " WORD_SIZE: 64\n",
90 | " LIBM: libopenlibm\n",
91 | " LLVM: libLLVM-15.0.7 (ORCJIT, apple-m1)\n",
92 | " Threads: 2 on 10 virtual cores\n"
93 | ]
94 | }
95 | ],
96 | "source": [
97 | "versioninfo()"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": 2,
103 | "id": "69106119",
104 | "metadata": {},
105 | "outputs": [
106 | {
107 | "name": "stdout",
108 | "output_type": "stream",
109 | "text": [
110 | "\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `~/.julia/environments/v1.10/Project.toml`\n",
111 | " \u001b[90m[d1acc4aa] \u001b[39mIntervalArithmetic v0.22.6\n"
112 | ]
113 | }
114 | ],
115 | "source": [
116 | "using Pkg\n",
117 | "Pkg.status(\"IntervalArithmetic\")"
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": 3,
123 | "id": "ad74b358-4346-475e-80d6-072b7da32862",
124 | "metadata": {},
125 | "outputs": [
126 | {
127 | "data": {
128 | "text/plain": [
129 | "GeneralizedEigen{ComplexF64, ComplexF64, Matrix{ComplexF64}, Vector{ComplexF64}}\n",
130 | "values:\n",
131 | "30-element Vector{ComplexF64}:\n",
132 | " -8.807235191171742 + 0.0im\n",
133 | " -1.8720739353881435 - 0.4211957322307501im\n",
134 | " -1.8720739353881435 + 0.42119573223075013im\n",
135 | " -1.2450164394864036 - 2.338661769764113im\n",
136 | " -1.2450164394864036 + 2.338661769764113im\n",
137 | " -1.1558402670064905 + 0.0im\n",
138 | " -0.789551856115187 - 1.002224359974505im\n",
139 | " -0.789551856115187 + 1.002224359974505im\n",
140 | " -0.5393502183046783 - 0.7116749651587355im\n",
141 | " -0.5393502183046783 + 0.7116749651587354im\n",
142 | " -0.3675440335623449 + 0.0im\n",
143 | " -0.22852573744671337 - 0.1592805432423365im\n",
144 | " -0.22852573744671337 + 0.15928054324233654im\n",
145 | " ⋮\n",
146 | " 0.281692718115899 - 0.545904155982315im\n",
147 | " 0.281692718115899 + 0.5459041559823149im\n",
148 | " 0.2965924537980811 - 0.8603906140438535im\n",
149 | " 0.2965924537980811 + 0.8603906140438535im\n",
150 | " 0.5212264660922051 + 0.0im\n",
151 | " 0.8420124984325313 - 0.4056142428796466im\n",
152 | " 0.8420124984325315 + 0.4056142428796465im\n",
153 | " 0.8668146997815379 + 0.0im\n",
154 | " 1.2621456553888593 + 0.0im\n",
155 | " 1.560528109473799 - 1.3546291706469042im\n",
156 | " 1.560528109473799 + 1.354629170646904im\n",
157 | " 186.07116084856375 + 0.0im\n",
158 | "vectors:\n",
159 | "30×30 Matrix{ComplexF64}:\n",
160 | " 0.20217+0.0im 0.0318037+0.0280408im … 0.234905+0.0im\n",
161 | " -0.217436+0.0im 0.364885+0.312187im -0.20174+0.0im\n",
162 | " -1.0+0.0im 0.211263+0.346095im -1.0+0.0im\n",
163 | " -0.26971+0.0im 0.0893835+0.155071im -0.300353+0.0im\n",
164 | " -0.0561925+0.0im 0.0777207+0.195086im 0.0945225+0.0im\n",
165 | " 0.253824+0.0im -0.176184-0.131949im … 0.250935+0.0im\n",
166 | " -0.16729+0.0im 0.252789+0.191767im 0.0614289+0.0im\n",
167 | " -0.321578+0.0im 0.0476998+0.0385842im -0.31054+0.0im\n",
168 | " -0.348441+0.0im 0.126015+0.237507im -0.238223+0.0im\n",
169 | " -0.18753+0.0im 0.491601+0.191111im 0.0205939+0.0im\n",
170 | " -0.0727752+0.0im -0.0939492-0.101164im … -0.00795106+0.0im\n",
171 | " -0.438972+0.0im 0.0789246+0.187319im -0.349904+0.0im\n",
172 | " 0.0625889+0.0im 0.0358692+0.00392634im 0.0791018+0.0im\n",
173 | " ⋮ ⋱ \n",
174 | " -0.0121096+0.0im 0.059365+0.0364191im -0.0724114+0.0im\n",
175 | " -0.146576+0.0im -0.130795-0.116416im -0.12067+0.0im\n",
176 | " -0.311531+0.0im 0.0906312+0.390606im … -0.176047+0.0im\n",
177 | " 0.00415899+0.0im 0.333781+0.246889im 0.0806722+0.0im\n",
178 | " 0.224384+0.0im -0.270805-0.0337405im 0.210771+0.0im\n",
179 | " -0.207086+0.0im 0.120852+0.105772im -0.331818+0.0im\n",
180 | " 0.310787+0.0im 0.236172+0.0814331im 0.527194+0.0im\n",
181 | " -0.296509+0.0im 0.311386+0.265348im … -0.14192+0.0im\n",
182 | " -0.540491+0.0im 0.507163+0.492837im -0.233728+0.0im\n",
183 | " -0.341711+0.0im -0.0338126-0.0721759im -0.508083+0.0im\n",
184 | " 0.243982+0.0im -0.0719414-0.250216im 0.0968205+0.0im\n",
185 | " -0.039231+0.0im 0.161358+0.112677im 0.0785697+0.0im"
186 | ]
187 | },
188 | "execution_count": 3,
189 | "metadata": {},
190 | "output_type": "execute_result"
191 | }
192 | ],
193 | "source": [
194 | "using LinearAlgebra\n",
195 | "n = 30\n",
196 | "A, B = randn(n, n), randn(n, n)\n",
197 | "λ, x = eigen(A, B)"
198 | ]
199 | },
200 | {
201 | "cell_type": "markdown",
202 | "id": "007b9fac-8dda-400f-8503-05a22bd6207c",
203 | "metadata": {},
204 | "source": [
205 | "1. 近似固有値 $\\bar{\\lambda}$、近似固有ベクトル $\\bar{x}$ を入力し、これらが実数であるときは実数の区間・区間ベクトルで、いずれかが複素数であるときは複素数の区間・区間ベクトルで $\\boldsymbol{y}$, $\\boldsymbol{d}$ を定義し、候補ベクトル $\\boldsymbol{w}=(\\boldsymbol{d},\\boldsymbol{y}^T)^T$ を作成する。\n",
206 | "\n",
207 | "候補ベクトルの区間幅は大きすぎたり、小さすぎたりすると定理の検証が失敗する。そのため**ある程度**小さい大きさを選ぶ必要がある。今回の実装では $\\epsilon=10^{-9}$ とした。"
208 | ]
209 | },
210 | {
211 | "cell_type": "code",
212 | "execution_count": 4,
213 | "id": "ca1396bd-1d6e-4845-98ac-331db7ced1d7",
214 | "metadata": {},
215 | "outputs": [
216 | {
217 | "data": {
218 | "text/plain": [
219 | "31-element Vector{Interval{Float64}}:\n",
220 | " [-1.00001e-09, 1.00001e-09]_com\n",
221 | " [-1.00001e-09, 1.00001e-09]_com\n",
222 | " [-1.00001e-09, 1.00001e-09]_com\n",
223 | " [-1.00001e-09, 1.00001e-09]_com\n",
224 | " [-1.00001e-09, 1.00001e-09]_com\n",
225 | " [-1.00001e-09, 1.00001e-09]_com\n",
226 | " [-1.00001e-09, 1.00001e-09]_com\n",
227 | " [-1.00001e-09, 1.00001e-09]_com\n",
228 | " [-1.00001e-09, 1.00001e-09]_com\n",
229 | " [-1.00001e-09, 1.00001e-09]_com\n",
230 | " [-1.00001e-09, 1.00001e-09]_com\n",
231 | " [-1.00001e-09, 1.00001e-09]_com\n",
232 | " [-1.00001e-09, 1.00001e-09]_com\n",
233 | " ⋮\n",
234 | " [-1.00001e-09, 1.00001e-09]_com\n",
235 | " [-1.00001e-09, 1.00001e-09]_com\n",
236 | " [-1.00001e-09, 1.00001e-09]_com\n",
237 | " [-1.00001e-09, 1.00001e-09]_com\n",
238 | " [-1.00001e-09, 1.00001e-09]_com\n",
239 | " [-1.00001e-09, 1.00001e-09]_com\n",
240 | " [-1.00001e-09, 1.00001e-09]_com\n",
241 | " [-1.00001e-09, 1.00001e-09]_com\n",
242 | " [-1.00001e-09, 1.00001e-09]_com\n",
243 | " [-1.00001e-09, 1.00001e-09]_com\n",
244 | " [-1.00001e-09, 1.00001e-09]_com\n",
245 | " [-1.00001e-09, 1.00001e-09]_com"
246 | ]
247 | },
248 | "execution_count": 4,
249 | "metadata": {},
250 | "output_type": "execute_result"
251 | }
252 | ],
253 | "source": [
254 | "using IntervalArithmetic\n",
255 | "lam = λ[1]\n",
256 | "x1 = x[:, 1] # 対応する固有ベクトル\n",
257 | "x = x1 ./ sqrt(x1' * x1)\n",
258 | "ysize = length(x)\n",
259 | "\n",
260 | "ϵ = 1e-9 # size of candidate vector\n",
261 | "\n",
262 | "if isreal(lam) && isreal(x)\n",
263 | " lam = real(lam)\n",
264 | " x = real(x)\n",
265 | " id = interval(0, ϵ; format=:midpoint)\n",
266 | " iy = interval.(zeros(ysize), ϵ; format=:midpoint)\n",
267 | " iI = interval(Matrix{Float64}(I, n + 1, n + 1))\n",
268 | " izero = interval(0)\n",
269 | "else\n",
270 | " id = Complex(interval.(0, ϵ; format=:midpoint), interval.(0, ϵ; format=:midpoint))\n",
271 | " iy = Complex.(interval.(zeros(ysize), ϵ; format=:midpoint), interval.(zeros(ysize), ϵ; format=:midpoint))\n",
272 | " iI = interval(Matrix{Complex{Float64}}(I, n + 1, n + 1))\n",
273 | " izero = interval(0 + 0im)\n",
274 | "end\n",
275 | "iw = [id; iy]"
276 | ]
277 | },
278 | {
279 | "cell_type": "markdown",
280 | "id": "b67d6910-ea6d-4cbc-acdb-2fbdb2151d1c",
281 | "metadata": {},
282 | "source": [
283 | "2. ヤコビ行列$DF$ と $z$、$g(\\boldsymbol{w}):=z+\\left(I-R\\cdot DF\\left(\\bar\\lambda + \\boldsymbol{d}, \\bar x + \\boldsymbol{y}\\right)\\right)\\boldsymbol{w}$ を区間演算により計算する。"
284 | ]
285 | },
286 | {
287 | "cell_type": "code",
288 | "execution_count": 5,
289 | "id": "bb0c62bc-5941-4f16-9c47-fa697e9aa06e",
290 | "metadata": {},
291 | "outputs": [
292 | {
293 | "data": {
294 | "text/plain": [
295 | "31-element Vector{Interval{Float64}}:\n",
296 | " [-1.53421e-12, 1.60364e-12]_com\n",
297 | " [-6.28847e-15, 5.87119e-15]_com\n",
298 | " [-6.2841e-15, 6.24573e-15]_com\n",
299 | " [-1.33737e-14, 1.35622e-14]_com\n",
300 | " [-5.34187e-15, 5.95928e-15]_com\n",
301 | " [-1.65682e-14, 1.55644e-14]_com\n",
302 | " [-4.44868e-15, 4.00426e-15]_com\n",
303 | " [-2.1826e-14, 2.10103e-14]_com\n",
304 | " [-5.66461e-15, 6.17318e-15]_com\n",
305 | " [-9.08141e-15, 8.89336e-15]_com\n",
306 | " [-1.68061e-14, 1.59398e-14]_com\n",
307 | " [-3.91177e-15, 3.74838e-15]_com\n",
308 | " [-3.26079e-15, 3.35143e-15]_com\n",
309 | " ⋮\n",
310 | " [-3.99718e-15, 4.32441e-15]_com\n",
311 | " [-9.42529e-15, 9.6057e-15]_com\n",
312 | " [-1.38155e-14, 1.34268e-14]_com\n",
313 | " [-1.58001e-14, 1.49484e-14]_com\n",
314 | " [-3.53401e-15, 3.64503e-15]_com\n",
315 | " [-1.14521e-14, 1.23865e-14]_com\n",
316 | " [-2.50687e-14, 2.35591e-14]_com\n",
317 | " [-1.37185e-14, 1.32005e-14]_com\n",
318 | " [-2.83772e-14, 2.69667e-14]_com\n",
319 | " [-2.06638e-14, 2.20965e-14]_com\n",
320 | " [-1.31275e-14, 1.3718e-14]_com\n",
321 | " [-1.19503e-14, 1.15808e-14]_com"
322 | ]
323 | },
324 | "execution_count": 5,
325 | "metadata": {},
326 | "output_type": "execute_result"
327 | }
328 | ],
329 | "source": [
330 | "ix = interval(x);\n",
331 | "ilam = interval(lam);\n",
332 | "iA = interval(A);\n",
333 | "iB = interval(B);\n",
334 | "iDF = [izero transpose(interval(2) * (ix + iy)); -iB*(ix+iy) iA-(ilam.+id)*iB]\n",
335 | "R = inv(mid.(iDF));\n",
336 | "iR = interval(R);\n",
337 | "z = -iR * [dot(ix, ix) - interval(1); iA * ix - ilam * iB * ix]\n",
338 | "gw = z + (iI - iR * iDF) * iw"
339 | ]
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "id": "816e3ef6-5584-4552-b1f0-12c15a62ad58",
344 | "metadata": {},
345 | "source": [
346 | "3. $g(\\boldsymbol{w})\\subsetneq \\boldsymbol{w}$を満たすとき、$(\\bar\\lambda,\\bar x)^T+g(\\boldsymbol{w})$ の値を返す。"
347 | ]
348 | },
349 | {
350 | "cell_type": "code",
351 | "execution_count": 6,
352 | "id": "be7008a9-fa3c-4718-b813-93d7b753f836",
353 | "metadata": {},
354 | "outputs": [
355 | {
356 | "data": {
357 | "text/plain": [
358 | "true"
359 | ]
360 | },
361 | "execution_count": 6,
362 | "metadata": {},
363 | "output_type": "execute_result"
364 | }
365 | ],
366 | "source": [
367 | "all(issubset_interval.(gw, iw))"
368 | ]
369 | },
370 | {
371 | "cell_type": "code",
372 | "execution_count": 7,
373 | "id": "b93a27ed-9bb2-4d73-a4e6-67641aab12b7",
374 | "metadata": {},
375 | "outputs": [
376 | {
377 | "data": {
378 | "text/plain": [
379 | "31-element Vector{Interval{Float64}}:\n",
380 | " [-8.80724, -8.80723]_com\n",
381 | " [0.121617, 0.121618]_com\n",
382 | " [-0.130802, -0.130801]_com\n",
383 | " [-0.601561, -0.60156]_com\n",
384 | " [-0.162248, -0.162246]_com\n",
385 | " [-0.0338032, -0.0338031]_com\n",
386 | " [0.15269, 0.152691]_com\n",
387 | " [-0.100636, -0.100635]_com\n",
388 | " [-0.193449, -0.193448]_com\n",
389 | " [-0.209609, -0.209608]_com\n",
390 | " [-0.112811, -0.11281]_com\n",
391 | " [-0.0437788, -0.0437787]_com\n",
392 | " [-0.264069, -0.264068]_com\n",
393 | " ⋮\n",
394 | " [-0.00728467, -0.00728466]_com\n",
395 | " [-0.0881746, -0.0881745]_com\n",
396 | " [-0.187405, -0.187404]_com\n",
397 | " [0.00250188, 0.00250189]_com\n",
398 | " [0.13498, 0.134981]_com\n",
399 | " [-0.124575, -0.124574]_com\n",
400 | " [0.186956, 0.186958]_com\n",
401 | " [-0.178369, -0.178368]_com\n",
402 | " [-0.325139, -0.325138]_com\n",
403 | " [-0.205561, -0.205559]_com\n",
404 | " [0.146769, 0.14677]_com\n",
405 | " [-0.0235999, -0.0235998]_com"
406 | ]
407 | },
408 | "execution_count": 7,
409 | "metadata": {},
410 | "output_type": "execute_result"
411 | }
412 | ],
413 | "source": [
414 | "[ilam; ix] + gw"
415 | ]
416 | },
417 | {
418 | "cell_type": "markdown",
419 | "id": "3af37888-4923-48d5-8e06-7c81597eb311",
420 | "metadata": {},
421 | "source": [
422 | "この第一成分は対象の固有値の厳密な包含である。"
423 | ]
424 | },
425 | {
426 | "cell_type": "code",
427 | "execution_count": 8,
428 | "id": "17e37283-6871-4d1c-a18e-7dd0d74fb995",
429 | "metadata": {},
430 | "outputs": [
431 | {
432 | "data": {
433 | "text/plain": [
434 | "[-8.80724, -8.80723]_com"
435 | ]
436 | },
437 | "execution_count": 8,
438 | "metadata": {},
439 | "output_type": "execute_result"
440 | }
441 | ],
442 | "source": [
443 | "ilam + gw[1]"
444 | ]
445 | },
446 | {
447 | "cell_type": "markdown",
448 | "id": "cb2f4e66-57ac-496a-ac04-abfd4a8cf71b",
449 | "metadata": {},
450 | "source": [
451 | "この手順を`verifyeig関数`として定義する。"
452 | ]
453 | },
454 | {
455 | "cell_type": "code",
456 | "execution_count": 9,
457 | "id": "e30f5503-7761-4f28-beeb-f60801c8dfe2",
458 | "metadata": {},
459 | "outputs": [
460 | {
461 | "data": {
462 | "text/plain": [
463 | "verifyeig (generic function with 2 methods)"
464 | ]
465 | },
466 | "execution_count": 9,
467 | "metadata": {},
468 | "output_type": "execute_result"
469 | }
470 | ],
471 | "source": [
472 | "using IntervalArithmetic\n",
473 | "function verifyeig(A, lam, x, B=B = Matrix(I,size(A)))\n",
474 | " x = x ./ sqrt(x' * x)\n",
475 | " ysize = length(x)\n",
476 | "\n",
477 | " ϵ = 1e-9 # size of candidate vector\n",
478 | "\n",
479 | " if isreal(lam) && isreal(x)\n",
480 | " lam = real(lam)\n",
481 | " x = real(x)\n",
482 | " id = interval(0, ϵ; format=:midpoint)\n",
483 | " iy = interval.(zeros(ysize), ϵ; format=:midpoint)\n",
484 | " iI = interval(Matrix{Float64}(I, ysize + 1, ysize + 1))\n",
485 | " izero = interval(0)\n",
486 | " else\n",
487 | " id = Complex(interval.(0, ϵ; format=:midpoint), interval.(0, ϵ; format=:midpoint))\n",
488 | " iy = Complex.(interval.(zeros(ysize), ϵ; format=:midpoint), interval.(zeros(ysize), ϵ; format=:midpoint))\n",
489 | " iI = interval(Matrix{Complex{Float64}}(I, ysize + 1, ysize + 1))\n",
490 | " izero = interval(0 + 0im)\n",
491 | " end\n",
492 | " iw = [id; iy]\n",
493 | "\n",
494 | " # DF(w) = [0 transpose(2*(x+w[2:end])) ; -B*(x+w[2:end]) A-(lam+w[1]).*B]\n",
495 | " ix = interval(x)\n",
496 | " ilam = interval(lam)\n",
497 | " iA = interval(A)\n",
498 | " iB = interval(B)\n",
499 | "\n",
500 | " iDF(w) = [izero transpose(interval(2) * (ix + w[2:end])); -iB*(ix+w[2:end]) iA-(ilam.+w[1])*iB]\n",
501 | " R = inv(mid.(iDF(zeros(ysize + 1))))\n",
502 | " iR = interval(R)\n",
503 | " z = -iR * [dot(ix, ix) - interval(1); iA * ix - ilam * iB * ix]\n",
504 | " g(w) = z + (iI - iR * iDF(w)) * w\n",
505 | " gw = g(iw)\n",
506 | " if all(issubset_interval.(gw, iw))\n",
507 | " while maximum(radius, gw) / norm([lam; x], 1) > 5e-13\n",
508 | " iw = gw\n",
509 | " gw = g(iw)\n",
510 | " end\n",
511 | " return ilam + gw[1]\n",
512 | " else\n",
513 | " return NaN\n",
514 | " end\n",
515 | "end\n"
516 | ]
517 | },
518 | {
519 | "cell_type": "markdown",
520 | "id": "6eeba5f2-e93f-4b27-9853-3c316069fb62",
521 | "metadata": {},
522 | "source": [
523 | "部分固有対の精度保証方法であるので、全固有値を精度保証するには向かない。以下のように全固有値を精度保証するには $O(n^4)$ のオーダーが必要になる。全固有値を精度保証するには、例えば、[標準固有値問題の精度保証付き数値解法](https://www.risk.tsukuba.ac.jp/~takitoshi/tutorial/verifyalleig.html)のような方法が有効である。"
524 | ]
525 | },
526 | {
527 | "cell_type": "code",
528 | "execution_count": 10,
529 | "id": "cb92add8-c971-4930-bf14-6ec4efc28f8b",
530 | "metadata": {},
531 | "outputs": [
532 | {
533 | "data": {
534 | "text/plain": [
535 | "GeneralizedEigen{ComplexF64, ComplexF64, Matrix{ComplexF64}, Vector{ComplexF64}}\n",
536 | "values:\n",
537 | "30-element Vector{ComplexF64}:\n",
538 | " -3.927248809102562 + 0.0im\n",
539 | " -2.342175403774986 + 0.0im\n",
540 | " -1.8204436211401438 + 0.0im\n",
541 | " -0.9825359650919164 + 0.9062641005675697im\n",
542 | " -0.9825359650919163 - 0.9062641005675698im\n",
543 | " -0.9369434243815118 + 0.0im\n",
544 | " -0.7678548617673059 + 0.0im\n",
545 | " -0.6336042505433659 - 4.4581246542775945im\n",
546 | " -0.6336042505433657 + 4.4581246542775945im\n",
547 | " -0.41284829356200076 + 0.0im\n",
548 | " -0.3207959087791363 + 0.0im\n",
549 | " -0.26666982104990433 - 0.36793157410745986im\n",
550 | " -0.26666982104990433 + 0.36793157410745986im\n",
551 | " ⋮\n",
552 | " -0.04156652673607428 + 0.8154422472066382im\n",
553 | " 0.2577455745317516 - 0.18455474115985465im\n",
554 | " 0.2577455745317516 + 0.18455474115985465im\n",
555 | " 0.6689537858084744 - 0.24860338555344522im\n",
556 | " 0.6689537858084744 + 0.24860338555344522im\n",
557 | " 0.7723122085046161 + 0.6492318019823247im\n",
558 | " 0.7723122085046162 - 0.6492318019823247im\n",
559 | " 1.1473309779693959 - 0.10819027220057749im\n",
560 | " 1.1473309779693959 + 0.10819027220057749im\n",
561 | " 1.7003772980863652 + 0.0im\n",
562 | " 1.7723005862476457 - 2.661353859339048im\n",
563 | " 1.7723005862476457 + 2.661353859339048im\n",
564 | "vectors:\n",
565 | "30×30 Matrix{ComplexF64}:\n",
566 | " -0.696811+0.0im -0.920033+0.0im … 0.867718+0.0608822im\n",
567 | " -0.637245+0.0im -0.236322+0.0im 0.24356+0.228255im\n",
568 | " -0.630751+0.0im -0.86956+0.0im 0.486714-0.0649732im\n",
569 | " 0.00190353+0.0im -0.102257+0.0im -0.334326-0.375381im\n",
570 | " -0.0135138+0.0im 0.452453+0.0im -0.128828+0.209189im\n",
571 | " 0.0991277+0.0im 0.461829+0.0im … -0.179949-0.13841im\n",
572 | " 0.0410025+0.0im -0.674928+0.0im 0.408206+0.591794im\n",
573 | " 0.0469751+0.0im 0.187833+0.0im -0.276895+0.386155im\n",
574 | " 0.00228834+0.0im -0.833872+0.0im 0.0625323-0.136356im\n",
575 | " 0.123516+0.0im -0.0498425+0.0im -0.090858+0.091819im\n",
576 | " -0.54912+0.0im 0.768362+0.0im … 0.138197-0.169806im\n",
577 | " 0.241703+0.0im -0.568661+0.0im 0.172141+0.26653im\n",
578 | " 0.317403+0.0im -0.551095+0.0im 0.236936-0.0286702im\n",
579 | " ⋮ ⋱ \n",
580 | " 1.0+0.0im 0.742088+0.0im -0.20981+0.116056im\n",
581 | " -0.189429+0.0im 0.174489+0.0im -0.130892-0.10924im\n",
582 | " -0.010899+0.0im 0.0251151+0.0im … 0.289061-0.191932im\n",
583 | " 0.150996+0.0im -0.278616+0.0im -0.0761204-0.171493im\n",
584 | " -0.110771+0.0im 1.0+0.0im 0.0302184+0.598693im\n",
585 | " -0.229355+0.0im 0.44826+0.0im 0.44877+0.289908im\n",
586 | " 0.852991+0.0im 0.715839+0.0im -0.261972+0.551214im\n",
587 | " 0.540259+0.0im 0.099366+0.0im … -0.718619-0.13813im\n",
588 | " -0.70162+0.0im -0.413567+0.0im 0.349212+0.152074im\n",
589 | " 0.133896+0.0im -0.0737264+0.0im 0.358246-0.0728801im\n",
590 | " 0.246352+0.0im 0.18205+0.0im 0.00756773+0.0964924im\n",
591 | " 0.317812+0.0im -0.35158+0.0im -0.473799-0.141664im"
592 | ]
593 | },
594 | "execution_count": 10,
595 | "metadata": {},
596 | "output_type": "execute_result"
597 | }
598 | ],
599 | "source": [
600 | "using LinearAlgebra\n",
601 | "n = 30\n",
602 | "A, B = randn(n, n), randn(n, n)\n",
603 | "λ, x = eigen(A, B)"
604 | ]
605 | },
606 | {
607 | "cell_type": "code",
608 | "execution_count": 11,
609 | "id": "5b5f4409-3fe2-43f8-bd32-65487532768a",
610 | "metadata": {},
611 | "outputs": [
612 | {
613 | "name": "stdout",
614 | "output_type": "stream",
615 | "text": [
616 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-3.9272488091026454, -3.9272488091024935, com)\n",
617 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-2.3421754037750544, -2.342175403774924, com)\n",
618 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-1.8204436211401773, -1.820443621140108, com)\n",
619 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.9825359650919452, -0.9825359650918856, com) + Interval{Float64}(0.9062641005675408, 0.9062641005675977, com)im\n",
620 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.9825359650919446, -0.9825359650918858, com) + Interval{Float64}(-0.9062641005675972, -0.9062641005675407, com)im\n",
621 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.9369434243815297, -0.9369434243814961, com)\n",
622 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.7678548617673198, -0.7678548617672895, com)\n",
623 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.6336042505435233, -0.6336042505432147, com) + Interval{Float64}(-4.458124654277739, -4.458124654277461, com)im\n",
624 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.6336042505435259, -0.6336042505432139, com) + Interval{Float64}(4.458124654277459, 4.45812465427774, com)im\n",
625 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.4128482935620064, -0.41284829356199426, com)\n",
626 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.32079590877914593, -0.320795908779129, com)\n",
627 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.26666982104991177, -0.2666698210498967, com) + Interval{Float64}(-0.3679315741074671, -0.3679315741074521, com)im\n",
628 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.26666982104991177, -0.2666698210498967, com) + Interval{Float64}(0.3679315741074521, 0.3679315741074671, com)im\n",
629 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.20595380010554623, -0.20595380010549347, com) + Interval{Float64}(1.0529535033506603, 1.0529535033507122, com)im\n",
630 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.20595380010554612, -0.20595380010549386, com) + Interval{Float64}(-1.0529535033507118, -1.0529535033506605, com)im\n",
631 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.09080177424957564, -0.0908017742495591, com) + Interval{Float64}(-0.18038183818252132, -0.1803818381825058, com)im\n",
632 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.09080177424957567, -0.09080177424955913, com) + Interval{Float64}(0.1803818381825058, 0.1803818381825213, com)im\n",
633 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.04156652673609379, -0.04156652673605269, com) + Interval{Float64}(-0.8154422472066599, -0.8154422472066175, com)im\n",
634 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(-0.04156652673609379, -0.04156652673605269, com) + Interval{Float64}(0.8154422472066175, 0.8154422472066599, com)im\n",
635 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(0.25774557453174884, 0.25774557453175456, com) + Interval{Float64}(-0.18455474115985773, -0.18455474115985138, com)im\n",
636 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(0.25774557453174884, 0.25774557453175456, com) + Interval{Float64}(0.18455474115985138, 0.18455474115985773, com)im\n",
637 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(0.668953785808457, 0.6689537858084947, com) + Interval{Float64}(-0.24860338555346076, -0.24860338555342834, com)im\n",
638 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(0.668953785808457, 0.6689537858084947, com) + Interval{Float64}(0.24860338555342834, 0.24860338555346076, com)im\n",
639 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(0.7723122085045907, 0.7723122085046473, com) + Interval{Float64}(0.6492318019822947, 0.6492318019823529, com)im\n",
640 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(0.7723122085045908, 0.7723122085046472, com) + Interval{Float64}(-0.6492318019823528, -0.6492318019822946, com)im\n",
641 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(1.1473309779693606, 1.1473309779694252, com) + Interval{Float64}(-0.10819027220060964, -0.10819027220054633, com)im\n",
642 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(1.1473309779693606, 1.1473309779694252, com) + Interval{Float64}(0.10819027220054633, 0.10819027220060964, com)im\n",
643 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(1.7003772980863459, 1.7003772980864003, com)\n",
644 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(1.7723005862475718, 1.77230058624774, com) + Interval{Float64}(-2.6613538593391373, -2.661353859338972, com)im\n",
645 | "ilam = verifyeig(A, λ[i], x[:, i], B) = Interval{Float64}(1.7723005862475718, 1.77230058624774, com) + Interval{Float64}(2.661353859338972, 2.6613538593391373, com)im\n"
646 | ]
647 | }
648 | ],
649 | "source": [
650 | "# ilam = zeros(Interval,length(λ))\n",
651 | "for i = 1:length(λ)\n",
652 | " @show ilam = verifyeig(A, λ[i], x[:, i], B)\n",
653 | "end"
654 | ]
655 | },
656 | {
657 | "cell_type": "markdown",
658 | "id": "06d1cccf-ad3d-407f-9a3c-37648947a5b5",
659 | "metadata": {},
660 | "source": [
661 | "**TODO** 固有値が縮退し多重固有値である場合や複数の固有値がクラスター形成している場合、この方法は失敗する。この場合の精度保証付き数値計算方法は文献3にある。今後この方法を実装していきたい。\n",
662 | "\n",
663 | "\n",
664 | "本資料は以下のような文献・Web ページ等を参考に書いている。\n",
665 | "\n",
666 | "### 参考文献\n",
667 | "\n",
668 | "1. 大石進一編著, 精度保証付き数値計算の基礎, コロナ社, 2018.
\n",
669 | "(精度保証付き数値計算の教科書、3.4.2章にある「非線形方程式を利用した精度保証法」を実装した)\n",
670 | "\n",
671 | "1. S.M. Rump. Guaranteed Inclusions for the Complex Generalized Eigenproblem. Computing, 42:225-238, 1989.
\n",
672 | "(今回の方法を初めて発表した原著論文)\n",
673 | "\n",
674 | "1. S.M. Rump. Computational Error Bounds for Multiple or Nearly Multiple Eigenvalues. Linear Algebra and its Applications (LAA), 324:209–226, 2001.
\n",
675 | "(多重固有値や多数の固有値のクラスターに対する精度保証付き数値計算方法。INTLABの`verifyeig.m`にはこの方法も実装されている)\n",
676 | "\n",
677 | "\n",
678 | "近藤慎佑,
高安亮紀,2022年9月19日(最終更新:2024年2月5日)
"
679 | ]
680 | }
681 | ],
682 | "metadata": {
683 | "kernelspec": {
684 | "display_name": "Julia (multi-threads) 1.10.0",
685 | "language": "julia",
686 | "name": "julia-n-threads-1.10"
687 | },
688 | "language_info": {
689 | "file_extension": ".jl",
690 | "mimetype": "application/julia",
691 | "name": "julia",
692 | "version": "1.10.0"
693 | }
694 | },
695 | "nbformat": 4,
696 | "nbformat_minor": 5
697 | }
698 |
--------------------------------------------------------------------------------
/verifynlss.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Juliaで非線型方程式の解の精度保証付き数値計算\n",
8 | "\n",
9 | "非線形方程式\n",
10 | "\n",
11 | "$$\n",
12 | "f(x) = 0,\\quad f:D\\subset\\mathbb{R}^n \\rightarrow \\mathbb{R}^n\n",
13 | "$$\n",
14 | "\n",
15 | "の解を精度保証することを考える。$D$ は $f$ の定義域とし、$f$ は一階連続微分可能な関数とする。\n",
16 | "\n",
17 | "\n",
18 | "今回は、区間解析の標準的な手法となっているR. Krawczykによる解の検証手法、ならびに区間ニュートン法による解の検証方法を紹介する。\n",
19 | "\n",
20 | "## Krawczyk(クラフチック)法\n",
21 | "\n",
22 | "Krawczykの主張は、以下の定理で表される。\n",
23 | "\n",
24 | "**定理** $X\\in \\mathbb{R}^n$を区間ベクトル(候補者集合ともいう)、$c = \\mathrm{mid}(X)$、$R\\simeq Df(c)^{-1}=J(c)^{-1}$、$E$ を単位行列とし、\n",
25 | "\n",
26 | "\\begin{equation}\n",
27 | "K(X) = c-Rf(c)+(E-RDf(X))(X-c)\n",
28 | "\\end{equation}\n",
29 | "\n",
30 | "としたとき、$K(X)\\subset \\mathrm{int}(X)$ ($\\mathrm{int}(X)$:$X$ の内部)ならば $X$ に $f(x)=0$ の解が唯一存在する。\n",
31 | "\n",
32 | "この定理の証明は、簡易ニュートン写像 $g(x)=x-Rf(x)$ に対して、縮小写像の原理が成立することを確認する。\n",
33 | "\n",
34 | "#### 縮小写像の原理\n",
35 | "\n",
36 | "**定義** $X\\subseteq \\mathbb{R}^n$ として、写像 $T:X\\rightarrow X$ を考える。このとき、全ての $x,y\\in X$ において、距離 $d(x,y)$ を $d(x,y):=\\|x-y\\|_E$ で定義し、\n",
37 | "\n",
38 | "$$\n",
39 | "d(T(x),T(y))\\le \\alpha d(x,y)\n",
40 | "$$\n",
41 | "\n",
42 | "が成り立つ $\\alpha\\in [0,1)$ が存在するとき、このような $T$ を縮小写像という。\n",
43 | "\n",
44 | "**定理**(縮小写像の原理) $X\\subseteq \\mathbb{R}^n$ が閉集合、写像 $T:X\\rightarrow X$ が縮小写像とする。このとき、$T(\\tilde{x}) = \\tilde{x}$ を満たす点 $\\tilde{x}$ が $X$ に唯一存在する。($\\tilde{x}$ を不動点という)\n"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "### Krawczyk写像と簡易ニュートン写像の関係\n",
52 | "\n",
53 | "$X\\in \\mathbb{IR}^n$ を区間ベクトルとし、写像 $f$ に対する簡易ニュートン写像 $g:\\mathbb{R}^n\\rightarrow \\mathbb{R}^n$ を次で定義する。\n",
54 | "\n",
55 | "$$\n",
56 | "g(x)=x-Rf(x).\n",
57 | "$$\n",
58 | "\n",
59 | "ここで $R\\in \\mathbb{R}^{n\\times n}$ は、$n$ 次元正方行列として、$c\\in X$ におけるヤコビ行列 $J(c)$ の逆近似行列 $(R\\simeq f'(c)^{-1})$ とする。\n",
60 | "\n",
61 | "このとき、$R$ が正則であるならば、\n",
62 | "\n",
63 | "$$\n",
64 | "f(x)=0\\Longleftrightarrow g(x)=x\n",
65 | "$$\n",
66 | "\n",
67 | "が成り立つ。\n",
68 | "\n",
69 | "そして、写像 $g$ が、区間ベクトル $X$ から $X$ への縮小写像となれば、縮小写像の原理から、$f(x)=0$ の真の解 $\\tilde{x}$ が $X$ 内に一意存在することが示せる。しかし、写像 $g$ の区間 $X$ における区間拡張 $g_{[~]}(X)$ を考えると、常に\n",
70 | "\n",
71 | "$$g_{[~]}(X)=X-Rf_{[~]}(X)\\not\\subset X$$\n",
72 | "\n",
73 | "となり、縮小写像の原理を示すことができない。ここで\n",
74 | "\n",
75 | "$$\n",
76 | "g(X):=\\{g(x)\\mid x\\in X\\} (\\mbox{写像 $g$ の値域})\n",
77 | "$$\n",
78 | "\n",
79 | "$$\n",
80 | "g_{[~]}(X)\\supset g(X)\n",
81 | "$$\n",
82 | "\n",
83 | "である。以下に、具体的な例を示す。\n",
84 | "\n",
85 | "$$\n",
86 | "F(x,y) = \\left(\\begin{array}{c}f(x,y)\\\\ g(x,y)\\end{array}\\right) = \\left(\\begin{array}{c}x^2 + y^2 - 1\\\\ x - y\\end{array}\\right)\n",
87 | "$$\n",
88 | "\n",
89 | "とし、候補区間 $X$ を $ (0.707107 ± 1.0\\times 10^{-8}, 0.707107 ± 1.0\\times 10^{-8})^T$ とする。\n",
90 | "\n",
91 | "区間拡張 $g_{[~]}(X)$ を計算すると、\n",
92 | "\n",
93 | "$$\n",
94 | " g_{[~]}(X)=X-Rf_{[~]}(X) = ([0.707106, 0.707107], [0.707106, 0.707107])^T \\not\\subset X\n",
95 | "$$\n",
96 | "\n",
97 | "となり、候補区間 $X$ に含まれないことがわかる。"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": 1,
103 | "metadata": {},
104 | "outputs": [
105 | {
106 | "name": "stdout",
107 | "output_type": "stream",
108 | "text": [
109 | "s = Interval{Float64}[[0.707106, 0.707107], [0.707106, 0.707107]]\n",
110 | "s .⊂ X = Bool[0, 0]\n"
111 | ]
112 | }
113 | ],
114 | "source": [
115 | "using LinearAlgebra,IntervalArithmetic, ForwardDiff\n",
116 | "X = [0.707107 ± 1e-8, 0.707107 ± 1e-8]\n",
117 | "c = mid.(X)\n",
118 | "f(x, y) = x^2 + y^2 - 1\n",
119 | "g(x, y) = x - y\n",
120 | "F( (x, y) ) = [f(x, y); g(x, y)]\n",
121 | "DF = ForwardDiff.jacobian(F,c)\n",
122 | "R = inv(DF)\n",
123 | "s = X - R*F(X)\n",
124 | "@show s\n",
125 | "@show s .⊂ X;"
126 | ]
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "metadata": {},
131 | "source": [
132 | "この困難を解決するために平均値形式を導入する。平均値形式は区間演算における区間の増大を抑制するための基本手法である。\n",
133 | "\n",
134 | "#### 平均値形式\n",
135 | "\n",
136 | "**定義** 写像 $f: D \\rightarrow \\mathbb{R}^{n}$ が区間 $X\\subset D$ ($D$ は $f$ の定義域)において、1階連続微分可能とする。\n",
137 | "このとき、$x,~\\tilde{x} \\in X$に対して、\n",
138 | "\n",
139 | "$$\n",
140 | "f(x) \\in f(\\tilde{x})+Df_{[~]}(X)(x-\\tilde{x})\n",
141 | "$$\n",
142 | "\n",
143 | "が成立する。($Df_{[~]}(x)$ は、写像 $f$ のヤコビ行列 $J(x)$ の区間 $X$ における区間拡張)上記の右辺を写像 $f$ の**平均値形式**と呼ぶ。\n",
144 | "\n",
145 | "今回は、簡易ニュートン写像 $g$ の点 $c\\in X$における平均値形式によって、Krawczyk写像 $K(X):\\mathbb{IR}^n \\rightarrow \\mathbb{IR}^n$が以下で定義することができる。\n",
146 | "\n",
147 | "$$\n",
148 | "K(X)=c-Rf_{[~]}(c)+(I-RDf_{[~]}(X))(X-c)\n",
149 | "$$\n",
150 | "\n",
151 | "ここでは、 $I\\in \\mathbb{R}^{n\\times n}$ 単位行列、$Df_{[~]}(X)$ は写像 $f$ のヤコビ行列 $J(x)$ の区間 $X$ における区間拡張とする。 "
152 | ]
153 | },
154 | {
155 | "cell_type": "markdown",
156 | "metadata": {},
157 | "source": [
158 | "### 自動微分を利用したヤコビ行列の計算\n",
159 | "\n",
160 | "Kwawczyk法を使う際には、区間拡張 $Df_{[~]}(X)$ を計算する必要がある。計算の方法として、最も標準的な実装方法は、自動微分を利用することである。\n",
161 | "\n",
162 | "Juliaで自動微分を適用する場合には、`ForwardDiff`パッケージを利用する。使用例は以下の通りである。以下では $f(x,y) = x^2+y^2-1$, $g(x,y) = x^3 + y^4$ として\n",
163 | "\n",
164 | "$$\n",
165 | " h(x,y) = \\left(\\begin{array}{c}f(x,y)\\\\ g(x,y)\\end{array}\\right)\n",
166 | "$$\n",
167 | "\n",
168 | "のヤコビ行列\n",
169 | "\n",
170 | "$$\n",
171 | "J(x) = \\left(\\begin{array}{cc}f_x(x,y) & f_y(x,y)\\\\ g_x(x,y) & g_y(x,y)\\end{array}\\right)= \\left(\\begin{array}{cc} 2x & 2y\\\\ 3x^2 & 4y^3\\end{array}\\right)\n",
172 | "$$\n",
173 | "\n",
174 | "の区間 $(x,y) = ([0.8,0.9], [-1,-0.5])$ における値を求める。"
175 | ]
176 | },
177 | {
178 | "cell_type": "code",
179 | "execution_count": 2,
180 | "metadata": {
181 | "scrolled": true
182 | },
183 | "outputs": [
184 | {
185 | "data": {
186 | "text/plain": [
187 | "2×2 Matrix{Interval{Float64}}:\n",
188 | " [1.59999, 1.80001] [-2, -1]\n",
189 | " [1.91999, 2.43001] [-4, -0.5]"
190 | ]
191 | },
192 | "execution_count": 2,
193 | "metadata": {},
194 | "output_type": "execute_result"
195 | }
196 | ],
197 | "source": [
198 | "using LinearAlgebra,IntervalArithmetic, ForwardDiff\n",
199 | "X = [(0.8..0.9),(-1..(-0.5))]\n",
200 | "f(x, y) = x^2 + y^2 - 1\n",
201 | "g(x, y) = x^3 + y^4\n",
202 | "h( (x, y) ) = [f(x, y); g(x, y)]\n",
203 | "# ForwardDiff.jacobian(g, X::IntervalBox) = ForwardDiff.jacobian(g, X.v)\n",
204 | "J = ForwardDiff.jacobian(h,X)"
205 | ]
206 | },
207 | {
208 | "cell_type": "markdown",
209 | "metadata": {},
210 | "source": [
211 | "関数 $h(x,y)$ の区間 $X = ([0.8,0.9], [-1,-0.5])$ における区間拡張 $h_{[~]}(X)$ は"
212 | ]
213 | },
214 | {
215 | "cell_type": "code",
216 | "execution_count": 3,
217 | "metadata": {},
218 | "outputs": [
219 | {
220 | "data": {
221 | "text/plain": [
222 | "2-element Vector{Interval{Float64}}:\n",
223 | " [-0.110001, 0.810001]\n",
224 | " [0.574499, 1.72901]"
225 | ]
226 | },
227 | "execution_count": 3,
228 | "metadata": {},
229 | "output_type": "execute_result"
230 | }
231 | ],
232 | "source": [
233 | "h(X)"
234 | ]
235 | },
236 | {
237 | "cell_type": "markdown",
238 | "metadata": {},
239 | "source": [
240 | "## Krawczyk法の実装\n",
241 | "\n",
242 | "ここから、Juliaを使ったKrawczyk法の実装を行う。以下では、 $f(x,y) = x^2+y^2-1$, $g(x,y) = x - y$ とする。"
243 | ]
244 | },
245 | {
246 | "cell_type": "code",
247 | "execution_count": 4,
248 | "metadata": {},
249 | "outputs": [
250 | {
251 | "data": {
252 | "text/plain": [
253 | "2×2 Matrix{Interval{Float64}}:\n",
254 | " [1.19999, 1.60001] [1.19999, 1.60001]\n",
255 | " [1, 1] [-1, -1]"
256 | ]
257 | },
258 | "execution_count": 4,
259 | "metadata": {},
260 | "output_type": "execute_result"
261 | }
262 | ],
263 | "source": [
264 | "using LinearAlgebra,IntervalArithmetic, ForwardDiff\n",
265 | "# 区間Xを定義\n",
266 | "X = [(0.6..0.8),(0.6..0.8)]\n",
267 | "# 計算対象となる方程式を定義\n",
268 | "f(x, y) = x^2 + y^2 - 1\n",
269 | "g(x, y) = x - y\n",
270 | "F( (x, y) ) = [f(x, y); g(x, y)]\n",
271 | "# ForwardDiff.jacobian(g, X::IntervalBox) = ForwardDiff.jacobian(g, X.v)\n",
272 | "# 区間Xにおけるヤコビ行列を計算\n",
273 | "iDF = ForwardDiff.jacobian(F,X)"
274 | ]
275 | },
276 | {
277 | "cell_type": "code",
278 | "execution_count": 5,
279 | "metadata": {
280 | "scrolled": true
281 | },
282 | "outputs": [
283 | {
284 | "data": {
285 | "text/plain": [
286 | "2×2 Matrix{Float64}:\n",
287 | " 1.4 1.4\n",
288 | " 1.0 -1.0"
289 | ]
290 | },
291 | "execution_count": 5,
292 | "metadata": {},
293 | "output_type": "execute_result"
294 | }
295 | ],
296 | "source": [
297 | "c = mid.(X)\n",
298 | "ic = map(Interval,c)\n",
299 | "# Rを計算するためのf'(c)を求める\n",
300 | "DF = ForwardDiff.jacobian(F,c) # 区間演算なし"
301 | ]
302 | },
303 | {
304 | "cell_type": "code",
305 | "execution_count": 6,
306 | "metadata": {},
307 | "outputs": [
308 | {
309 | "data": {
310 | "text/plain": [
311 | "2×2 Matrix{Float64}:\n",
312 | " 0.357143 0.5\n",
313 | " 0.357143 -0.5"
314 | ]
315 | },
316 | "execution_count": 6,
317 | "metadata": {},
318 | "output_type": "execute_result"
319 | }
320 | ],
321 | "source": [
322 | "# 区間Xにおけるヤコビ行列Jの逆行列Rを定義する\n",
323 | "R = inv(DF) # 区間演算なし"
324 | ]
325 | },
326 | {
327 | "cell_type": "code",
328 | "execution_count": 7,
329 | "metadata": {},
330 | "outputs": [
331 | {
332 | "data": {
333 | "text/plain": [
334 | "2×2 Matrix{Interval{Float64}}:\n",
335 | " [-0.0714286, 0.0714286] [-0.0714286, 0.0714286]\n",
336 | " [-0.0714286, 0.0714286] [-0.0714286, 0.0714286]"
337 | ]
338 | },
339 | "execution_count": 7,
340 | "metadata": {},
341 | "output_type": "execute_result"
342 | }
343 | ],
344 | "source": [
345 | "M = Matrix{Float64}(I,size(R)) - R*iDF"
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": 8,
351 | "metadata": {},
352 | "outputs": [
353 | {
354 | "data": {
355 | "text/plain": [
356 | "2-element Vector{Interval{Float64}}:\n",
357 | " [-0.00714286, -0.00714285]\n",
358 | " [-0.00714286, -0.00714285]"
359 | ]
360 | },
361 | "execution_count": 8,
362 | "metadata": {},
363 | "output_type": "execute_result"
364 | }
365 | ],
366 | "source": [
367 | "R*F(ic)"
368 | ]
369 | },
370 | {
371 | "cell_type": "code",
372 | "execution_count": 9,
373 | "metadata": {
374 | "scrolled": true
375 | },
376 | "outputs": [
377 | {
378 | "data": {
379 | "text/plain": [
380 | "2-element Vector{Interval{Float64}}:\n",
381 | " [0.692857, 0.721429]\n",
382 | " [0.692857, 0.721429]"
383 | ]
384 | },
385 | "execution_count": 9,
386 | "metadata": {},
387 | "output_type": "execute_result"
388 | }
389 | ],
390 | "source": [
391 | "# Krawczyk写像を計算\n",
392 | "K = c - R*F(ic) + M*(X - c) # 区間演算必要"
393 | ]
394 | },
395 | {
396 | "cell_type": "code",
397 | "execution_count": 10,
398 | "metadata": {},
399 | "outputs": [
400 | {
401 | "data": {
402 | "text/plain": [
403 | "2-element BitVector:\n",
404 | " 1\n",
405 | " 1"
406 | ]
407 | },
408 | "execution_count": 10,
409 | "metadata": {},
410 | "output_type": "execute_result"
411 | }
412 | ],
413 | "source": [
414 | "# 収束判定\n",
415 | "K .⊂ X"
416 | ]
417 | },
418 | {
419 | "cell_type": "markdown",
420 | "metadata": {},
421 | "source": [
422 | "Krawczyk写像 $K$ が候補区間 $X$ に含まれているため、この方程式の解は $X$の中に一意に存在することが示される。\n",
423 | "以下では、上記の計算をまとめ、1つの関数として定義する。"
424 | ]
425 | },
426 | {
427 | "cell_type": "code",
428 | "execution_count": 11,
429 | "metadata": {},
430 | "outputs": [
431 | {
432 | "data": {
433 | "text/plain": [
434 | "krawczyk (generic function with 1 method)"
435 | ]
436 | },
437 | "execution_count": 11,
438 | "metadata": {},
439 | "output_type": "execute_result"
440 | }
441 | ],
442 | "source": [
443 | "function krawczyk(F,X)\n",
444 | " iDF = ForwardDiff.jacobian(F,X)\n",
445 | " c = mid.(X); ic = map(Interval,c)\n",
446 | " DF = ForwardDiff.jacobian(F,c)\n",
447 | " R = inv(DF)\n",
448 | " M = Matrix{Float64}(I,size(R)) - R*iDF\n",
449 | " return c - R*F(ic) + M*(X - c)\n",
450 | "end"
451 | ]
452 | },
453 | {
454 | "cell_type": "markdown",
455 | "metadata": {},
456 | "source": [
457 | "また、候補区間の範囲をできる限り絞るために、Krawczyk法を条件の範囲内で繰り返し行う。\n",
458 | "以下は、その計算を行った結果である。"
459 | ]
460 | },
461 | {
462 | "cell_type": "code",
463 | "execution_count": 12,
464 | "metadata": {},
465 | "outputs": [
466 | {
467 | "name": "stdout",
468 | "output_type": "stream",
469 | "text": [
470 | "radius.(K) = [0.00028860028860044906, 0.00028860028860044906]\n",
471 | "radius.(K) = [1.1779002662137827e-7, 1.1779002662137827e-7]\n",
472 | "radius.(K) = [1.9761969838327786e-14, 1.9761969838327786e-14]\n"
473 | ]
474 | },
475 | {
476 | "data": {
477 | "text/plain": [
478 | "2-element Vector{Interval{Float64}}:\n",
479 | " [0.707106, 0.707107]\n",
480 | " [0.707106, 0.707107]"
481 | ]
482 | },
483 | "execution_count": 12,
484 | "metadata": {},
485 | "output_type": "execute_result"
486 | }
487 | ],
488 | "source": [
489 | "K = krawczyk(F,X)\n",
490 | "tol = 5e-10\n",
491 | "while maximum(radius,K) >= tol\n",
492 | " K = krawczyk(F,K)\n",
493 | " @show radius.(K)\n",
494 | "end\n",
495 | "K"
496 | ]
497 | },
498 | {
499 | "cell_type": "markdown",
500 | "metadata": {},
501 | "source": [
502 | "以下の例のように候補区間 $X$ の選び方次第では、$K(X)\\subset \\mathrm{int}(X)$ が成立しない場合もある。"
503 | ]
504 | },
505 | {
506 | "cell_type": "code",
507 | "execution_count": 13,
508 | "metadata": {
509 | "scrolled": true
510 | },
511 | "outputs": [
512 | {
513 | "data": {
514 | "text/plain": [
515 | "2-element BitVector:\n",
516 | " 0\n",
517 | " 0"
518 | ]
519 | },
520 | "execution_count": 13,
521 | "metadata": {},
522 | "output_type": "execute_result"
523 | }
524 | ],
525 | "source": [
526 | "err = 0.7;\n",
527 | "X = [0.7 ± err , 0.7 ± err ];\n",
528 | "c = mid.(X);\n",
529 | "K = krawczyk(F,X);\n",
530 | "K .⊂ X"
531 | ]
532 | },
533 | {
534 | "cell_type": "markdown",
535 | "metadata": {},
536 | "source": [
537 | "この時は、柏木の方法によって、候補区間 $X$ を変更すると有効であることがある。以下に、その手順を示す。いま $c\\in \\mathbb{F}^m$ を非線形方程式の数値計算で得られた近似解とする。\n",
538 | "ここで、$r=|Rf(c)|\\in \\mathbb{F}^m$ をベクトルとして考え、候補区間 $X$ を\n",
539 | "\n",
540 | "$$\n",
541 | "X=\\left(\\begin{array}{c}\n",
542 | "{\\left[c_{1}-u_{1}, c_{1}+u_{1}\\right]} \\\\\n",
543 | "{\\left[c_{2}-u_{2}, c_{2}+u_{2}\\right]} \\\\\n",
544 | "\\vdots \\\\\n",
545 | "{\\left[c_{m}-u_{m}, c_{m}+u_{m}\\right]}\n",
546 | "\\end{array}\\right),\\quad u_{i}=r_{i}+\\frac{1}{n} \\Sigma_{k} r_{k}\n",
547 | "$$\n",
548 | "\n",
549 | "とする。そうすることで、$K(X)\\subset \\mathrm{int}(X)$ がより成立するようになる。以下に実装方法を示す。"
550 | ]
551 | },
552 | {
553 | "cell_type": "code",
554 | "execution_count": 14,
555 | "metadata": {
556 | "scrolled": true
557 | },
558 | "outputs": [
559 | {
560 | "data": {
561 | "text/plain": [
562 | "2-element Vector{Float64}:\n",
563 | " 0.00714285714285707\n",
564 | " 0.00714285714285707"
565 | ]
566 | },
567 | "execution_count": 14,
568 | "metadata": {},
569 | "output_type": "execute_result"
570 | }
571 | ],
572 | "source": [
573 | "# rを計算\n",
574 | "r = abs.(R*F(c))"
575 | ]
576 | },
577 | {
578 | "cell_type": "code",
579 | "execution_count": 15,
580 | "metadata": {},
581 | "outputs": [
582 | {
583 | "data": {
584 | "text/plain": [
585 | "2-element Vector{Float64}:\n",
586 | " 0.01428571428571414\n",
587 | " 0.01428571428571414"
588 | ]
589 | },
590 | "execution_count": 15,
591 | "metadata": {},
592 | "output_type": "execute_result"
593 | }
594 | ],
595 | "source": [
596 | "# uを計算\n",
597 | "u = r .+ (sum(r)/length(r))"
598 | ]
599 | },
600 | {
601 | "cell_type": "code",
602 | "execution_count": 16,
603 | "metadata": {},
604 | "outputs": [
605 | {
606 | "data": {
607 | "text/plain": [
608 | "2-element BitVector:\n",
609 | " 1\n",
610 | " 1"
611 | ]
612 | },
613 | "execution_count": 16,
614 | "metadata": {},
615 | "output_type": "execute_result"
616 | }
617 | ],
618 | "source": [
619 | "# 候補区間Xを新たに定める\n",
620 | "X_new = c .± u;\n",
621 | "X = X_new;\n",
622 | "K = krawczyk(F,X);\n",
623 | "K .⊂ X"
624 | ]
625 | },
626 | {
627 | "cell_type": "markdown",
628 | "metadata": {},
629 | "source": [
630 | "今までの結果を集約して、最終的なKrawczyk法のアルゴリズムを実装する。以下では $f(x,y) = x^2+y^2-1$, $g(x,y) = x^2 - y^4$ として計算を行った。"
631 | ]
632 | },
633 | {
634 | "cell_type": "code",
635 | "execution_count": 17,
636 | "metadata": {},
637 | "outputs": [
638 | {
639 | "data": {
640 | "text/plain": [
641 | "F (generic function with 1 method)"
642 | ]
643 | },
644 | "execution_count": 17,
645 | "metadata": {},
646 | "output_type": "execute_result"
647 | }
648 | ],
649 | "source": [
650 | "using LinearAlgebra, IntervalArithmetic, ForwardDiff\n",
651 | "\n",
652 | "# 候補区間Xを定義\n",
653 | "X = [(0.6..0.7),(0.6..0.8)]\n",
654 | "\n",
655 | "# 計算対象となる方程式を定義\n",
656 | "f(x, y) = x^2 + y^2 - 1\n",
657 | "g(x, y) = x^2 - y^4\n",
658 | "F( (x, y) ) = [f(x, y); g(x, y)]\n"
659 | ]
660 | },
661 | {
662 | "cell_type": "code",
663 | "execution_count": 18,
664 | "metadata": {},
665 | "outputs": [
666 | {
667 | "data": {
668 | "text/plain": [
669 | "(1, Interval{Float64}[[0.618033, 0.618034], [0.786151, 0.786152]])"
670 | ]
671 | },
672 | "execution_count": 18,
673 | "metadata": {},
674 | "output_type": "execute_result"
675 | }
676 | ],
677 | "source": [
678 | "#ニュートン法で近似解を計算する\n",
679 | "function newton(F,x0)\n",
680 | " #初期値を設定\n",
681 | " tol = 5e-10; count = 0;\n",
682 | " x = x0;\n",
683 | " Fx = F(x);\n",
684 | " #条件の範囲内で計算を回す\n",
685 | " while maximum(abs,Fx) ≥ tol && count ≤ 20\n",
686 | " DF = ForwardDiff.jacobian(F,x);\n",
687 | " x -= DF\\Fx;\n",
688 | " Fx = F(x);\n",
689 | " count += 1;\n",
690 | " end\n",
691 | " return x\n",
692 | "end\n",
693 | "\n",
694 | "#クラフチック法を計算する\n",
695 | "function krawczyk(F,X)\n",
696 | " iDF = ForwardDiff.jacobian(F,X);\n",
697 | " c = mid.(X); ic = map(Interval,c);\n",
698 | " DF = ForwardDiff.jacobian(F,c);\n",
699 | " R = inv(DF);\n",
700 | " M = Matrix{Float64}(I,size(R)) - R*iDF;\n",
701 | " #クラフチック写像の値を返す\n",
702 | " return c - R*F(ic) + M*(X - c)\n",
703 | "end\n",
704 | "\n",
705 | "#最終的に完成した関数\n",
706 | "function verifynlss_krawczyk(F,c)\n",
707 | " DF = ForwardDiff.jacobian(F,c)\n",
708 | " R = inv(DF)\n",
709 | " r = abs.(R*F(c))\n",
710 | " u = r .+ (sum(r)/length(r))\n",
711 | " X = c .± u\n",
712 | " K = krawczyk(F,X)\n",
713 | " #範囲内に入っていたら、さらに範囲の精度をあげていく\n",
714 | " if all(K .⊂ X)\n",
715 | " tol = 5e-10\n",
716 | " count = 0\n",
717 | " while maximum(radius,K) >= tol && count ≤ 100\n",
718 | " K = krawczyk(F,K)\n",
719 | " count += 1\n",
720 | " end\n",
721 | " success = 1\n",
722 | " return success, K\n",
723 | " end\n",
724 | " println(\"Oh my way, verification is failed...return a improved approximate solution\") # cをNewton法で改善しても良い。\n",
725 | " success = 0\n",
726 | " return success, newton(F,c)\n",
727 | "end\n",
728 | "\n",
729 | "success, X = verifynlss_krawczyk(F,[0.61,0.78])\n",
730 | "if success == 0\n",
731 | " success, X = verifynlss_krawczyk(F,X)\n",
732 | "end\n",
733 | "success, X"
734 | ]
735 | },
736 | {
737 | "cell_type": "markdown",
738 | "metadata": {},
739 | "source": [
740 | "## 区間ニュートン法\n",
741 | "\n",
742 | "次に、もう1つの手法である区間ニュートン法について説明する。区間ニュートン法は、G.Alefeldによって提案された手法である。主張は以下の通りである。\n",
743 | "\n",
744 | "与えられた区間ベクトル $X\\in \\mathbb{IR}^n$ に対して、$f:X\\rightarrow \\mathbb{R}^n$ が1階連続微分可能な関数とする。$M\\in Df_{[~]}(X)$ を満たす任意の行列 $M$ が正則であると仮定し、ある $c\\in X$ に対して、集合 $N(c,X)$ を\n",
745 | "\n",
746 | "$$\n",
747 | "N(c,X) := \\{c - M^{-1}f(c)|M\\in Df_{[~]}(X)\\}\n",
748 | "$$\n",
749 | "\n",
750 | "と定義する。この時、$N(c,X)\\subset X$ が成立するならば、非線形方程式の真の解 $\\tilde{x}$ が区間ベクトル $X$ 内に一意存在する。また、$N(c,X)\\cap X=\\emptyset$ ならば、非線形方程式の解は $X$ 内に存在しない。さらに、$\\tilde{x}\\in N(c,X)$ である。"
751 | ]
752 | },
753 | {
754 | "cell_type": "markdown",
755 | "metadata": {},
756 | "source": [
757 | "### Krawczyk法との違い\n",
758 | "\n",
759 | "Krawczyk法と比較すると、解の存在検証部分は同一のプログラムで動作する。\n",
760 | "大きく違う部分は、区間連立1次方程式を解く必要がある点である。Krawczyk法は、近似解 $c$ におけるヤコビ行列 $J(c)$ の逆近似行列 $R$ だけを利用する手法である。一方で区間ニュートン法は要素数が大きくなると区間連立1次方程式の計算速度が遅くなり、さらに精度が悪くなるという問題点がある。\n",
761 | "したがって、区間ニュートン法とKrawczyk法は問題によって使い分けるのがよい。\n",
762 | "以下は、Juliaで区間連立1次方程式を計算するアルゴリズムである。"
763 | ]
764 | },
765 | {
766 | "cell_type": "code",
767 | "execution_count": 19,
768 | "metadata": {},
769 | "outputs": [
770 | {
771 | "data": {
772 | "text/plain": [
773 | "verifylss_iAib (generic function with 1 method)"
774 | ]
775 | },
776 | "execution_count": 19,
777 | "metadata": {},
778 | "output_type": "execute_result"
779 | }
780 | ],
781 | "source": [
782 | "# include(\"IntervalLinearAlgebra.jl\");\n",
783 | "\n",
784 | "#区間連立1次方程式を解く関数\n",
785 | "function verifylss_iAib(iA,ib) \n",
786 | " A = mid.(iA)\n",
787 | " b = mid.(ib)\n",
788 | " x̄ = A\\b\n",
789 | " n = length(x̄)\n",
790 | " R = inv(A)\n",
791 | " #########\n",
792 | " G = Matrix{Float64}(I, n, n) - R*iA\n",
793 | " α = opnorm(G,Inf)# Interval arithmetic\n",
794 | " #########\n",
795 | " if α < 1\n",
796 | " x̄ = map(Interval,x̄)\n",
797 | " r = iA*x̄ - ib # Interval arithmetic\n",
798 | " Rr = R*r\n",
799 | " err = abs.(Rr) + supremum(norm(Rr,Inf))/(1-α)*(abs.(G)*ones(n)) # Interval arithmetic\n",
800 | " else\n",
801 | " println(\"Oh my way, verification is failed...\")\n",
802 | " err = nan\n",
803 | " end\n",
804 | " return x̄ .± supremum.(err)\n",
805 | "end"
806 | ]
807 | },
808 | {
809 | "cell_type": "markdown",
810 | "metadata": {},
811 | "source": [
812 | "### 区間ニュートン法の実装\n"
813 | ]
814 | },
815 | {
816 | "cell_type": "markdown",
817 | "metadata": {},
818 | "source": [
819 | "以下では、上記の関数を用いて区間ニュートン法の手順を紹介する。ここでは $f(x,y) = x^2+y^2-1$, $g(x,y) = x^2 - y^4$ として問題を解く。\n"
820 | ]
821 | },
822 | {
823 | "cell_type": "code",
824 | "execution_count": 20,
825 | "metadata": {},
826 | "outputs": [
827 | {
828 | "data": {
829 | "text/plain": [
830 | "2-element Vector{Interval{Float64}}:\n",
831 | " [-0.0150001, -0.0149999]\n",
832 | " [0.106093, 0.106094]"
833 | ]
834 | },
835 | "execution_count": 20,
836 | "metadata": {},
837 | "output_type": "execute_result"
838 | }
839 | ],
840 | "source": [
841 | "using LinearAlgebra,IntervalArithmetic, ForwardDiff\n",
842 | "# 区間Xを定義\n",
843 | "X = [(0.6.. 0.7),(0.7.. 0.8)]\n",
844 | "c = mid.(X)\n",
845 | "ic = map(Interval,c)\n",
846 | "\n",
847 | "# 計算対象となる方程式を定義\n",
848 | "f(x, y) = x^2 + y^2 - 1\n",
849 | "g(x, y) = x^2 - y^4\n",
850 | "F( (x, y) ) = [f(x, y); g(x, y)]\n",
851 | "\n",
852 | "# 区間Xにおけるヤコビ行列を計算\n",
853 | "iM = ForwardDiff.jacobian(F,X) # 区間演算必要\n",
854 | "ifc = F(ic)"
855 | ]
856 | },
857 | {
858 | "cell_type": "code",
859 | "execution_count": 21,
860 | "metadata": {},
861 | "outputs": [
862 | {
863 | "data": {
864 | "text/plain": [
865 | "2-element Vector{Interval{Float64}}:\n",
866 | " [0.0206957, 0.0432826]\n",
867 | " [-0.047109, -0.0283388]"
868 | ]
869 | },
870 | "execution_count": 21,
871 | "metadata": {},
872 | "output_type": "execute_result"
873 | }
874 | ],
875 | "source": [
876 | "#区間連立1次方程式を計算\n",
877 | "verifylss_iAib(iM,ifc)"
878 | ]
879 | },
880 | {
881 | "cell_type": "code",
882 | "execution_count": 22,
883 | "metadata": {},
884 | "outputs": [
885 | {
886 | "data": {
887 | "text/plain": [
888 | "2-element Vector{Interval{Float64}}:\n",
889 | " [0.606717, 0.629305]\n",
890 | " [0.778338, 0.797109]"
891 | ]
892 | },
893 | "execution_count": 22,
894 | "metadata": {},
895 | "output_type": "execute_result"
896 | }
897 | ],
898 | "source": [
899 | "#N(c,X)を計算\n",
900 | "N = ic - verifylss_iAib(iM,ifc)"
901 | ]
902 | },
903 | {
904 | "cell_type": "code",
905 | "execution_count": 23,
906 | "metadata": {},
907 | "outputs": [
908 | {
909 | "data": {
910 | "text/plain": [
911 | "2-element BitVector:\n",
912 | " 1\n",
913 | " 1"
914 | ]
915 | },
916 | "execution_count": 23,
917 | "metadata": {},
918 | "output_type": "execute_result"
919 | }
920 | ],
921 | "source": [
922 | "#収束判定\n",
923 | "N .⊂ X"
924 | ]
925 | },
926 | {
927 | "cell_type": "markdown",
928 | "metadata": {},
929 | "source": [
930 | "Krawczyk方と同じく、区間ニュートン法の最終的なアルゴリズムを実装する。"
931 | ]
932 | },
933 | {
934 | "cell_type": "code",
935 | "execution_count": 24,
936 | "metadata": {},
937 | "outputs": [
938 | {
939 | "data": {
940 | "text/plain": [
941 | "verifynlss_IntervalNewton (generic function with 1 method)"
942 | ]
943 | },
944 | "execution_count": 24,
945 | "metadata": {},
946 | "output_type": "execute_result"
947 | }
948 | ],
949 | "source": [
950 | "#ニュートン法を計算\n",
951 | "function newton(F,x0)\n",
952 | " #初期値を設定\n",
953 | " tol = 5e-10; count = 0;\n",
954 | " x = x0;\n",
955 | " Fx = F(x);\n",
956 | " #条件によってニュートン法をまわす\n",
957 | " while maximum(abs,Fx) ≥ tol && count ≤ 20\n",
958 | " DF = ForwardDiff.jacobian(F,x);\n",
959 | " x -= DF\\Fx;\n",
960 | " Fx = F(x);\n",
961 | " count += 1;\n",
962 | " end\n",
963 | " return x\n",
964 | "end\n",
965 | "\n",
966 | "#N(c,X)を計算する関数\n",
967 | "function IntervalNewton(F,X)\n",
968 | " c = mid.(X);\n",
969 | " ic = map(Interval,c);\n",
970 | " iM = ForwardDiff.jacobian(F,X);\n",
971 | " ib = F(ic);\n",
972 | " #N(c,X)の値を返す\n",
973 | " return ic - verifylss_iAib(iM,ib)\n",
974 | "# return ic - iM\\ib\n",
975 | "end\n",
976 | "\n",
977 | "#最終的に構築した関数\n",
978 | "function verifynlss_IntervalNewton(F, c)\n",
979 | " DF = ForwardDiff.jacobian(F,c);\n",
980 | " R = inv(DF);\n",
981 | " r = abs.(R*F(c));\n",
982 | " u = r .+ (sum(r)/length(r));\n",
983 | " X = c .± u;\n",
984 | " K = IntervalNewton(F,X);\n",
985 | " #範囲内に入っていたら、さらに解の精度をあげる\n",
986 | " if all(K .⊂ X)\n",
987 | " tol = 5e-10;\n",
988 | " while maximum(radius,K) >= tol\n",
989 | " K = IntervalNewton(F,K)\n",
990 | " end\n",
991 | " success = 1\n",
992 | " return success, K\n",
993 | " end\n",
994 | " println(\"Oh my way, verification is failed...return a improved approximate solution\") \n",
995 | " success = 0\n",
996 | " return success, newton(F,c)\n",
997 | "end"
998 | ]
999 | },
1000 | {
1001 | "cell_type": "code",
1002 | "execution_count": 25,
1003 | "metadata": {},
1004 | "outputs": [
1005 | {
1006 | "data": {
1007 | "text/plain": [
1008 | "(1, Interval{Float64}[[0.618033, 0.618034], [0.786151, 0.786152]])"
1009 | ]
1010 | },
1011 | "execution_count": 25,
1012 | "metadata": {},
1013 | "output_type": "execute_result"
1014 | }
1015 | ],
1016 | "source": [
1017 | "verifynlss_IntervalNewton(F,[0.7,0.8])"
1018 | ]
1019 | },
1020 | {
1021 | "cell_type": "markdown",
1022 | "metadata": {},
1023 | "source": [
1024 | "最後に、区間ニュートン法で3次方程式の解を求めてみる。ここでは、ロジスティック写像の3周期解を導く方程式を用いて計算する。解く式は以下のとおりである。\n",
1025 | "\n",
1026 | "$$\n",
1027 | "\\left\\{\\begin{array}{l}\n",
1028 | "x_{1}-\\lambda x_{3}\\left(1-x_{3}\\right)=0 \\\\\n",
1029 | "x_{2}-\\lambda x_{1}\\left(1-x_{1}\\right)=0 \\\\\n",
1030 | "x_{3}-\\lambda x_{2}\\left(1-x_{2}\\right)=0\n",
1031 | "\\end{array}\\right.\n",
1032 | "$$\n",
1033 | "\n",
1034 | "今回は、 $\\lambda=3.82843$として問題を解く。"
1035 | ]
1036 | },
1037 | {
1038 | "cell_type": "code",
1039 | "execution_count": 26,
1040 | "metadata": {},
1041 | "outputs": [
1042 | {
1043 | "data": {
1044 | "text/plain": [
1045 | "3-element Vector{Interval{Float64}}:\n",
1046 | " [0.899999, 1]\n",
1047 | " [0.0999999, 0.200001]\n",
1048 | " [0.5, 0.600001]"
1049 | ]
1050 | },
1051 | "execution_count": 26,
1052 | "metadata": {},
1053 | "output_type": "execute_result"
1054 | }
1055 | ],
1056 | "source": [
1057 | "using LinearAlgebra,IntervalArithmetic, ForwardDiff\n",
1058 | "\n",
1059 | "#\\lambdaを設定\n",
1060 | "lam = 3.82843\n",
1061 | "\n",
1062 | "#解く方程式を設定\n",
1063 | "f(x, y, z) = x - lam*z*(1-z) \n",
1064 | "g(x, y, z) = y - lam*x*(1-x)\n",
1065 | "h(x, y, z) = z - lam*y*(1-y)\n",
1066 | "F( (x, y, z) ) = [f(x, y, z); g(x, y, z); h(x, y, z)]\n",
1067 | "\n",
1068 | "#候補区間を設定\n",
1069 | "X = [(0.9.. 1.0),(0.1.. 0.2),(0.5.. 0.6)]"
1070 | ]
1071 | },
1072 | {
1073 | "cell_type": "code",
1074 | "execution_count": 27,
1075 | "metadata": {},
1076 | "outputs": [
1077 | {
1078 | "data": {
1079 | "text/plain": [
1080 | "3-element Vector{Float64}:\n",
1081 | " 0.9562724713863567\n",
1082 | " 0.16008745377675246\n",
1083 | " 0.5147686339721098"
1084 | ]
1085 | },
1086 | "execution_count": 27,
1087 | "metadata": {},
1088 | "output_type": "execute_result"
1089 | }
1090 | ],
1091 | "source": [
1092 | "x0 = newton(F,mid.(X)) # 区間演算なし"
1093 | ]
1094 | },
1095 | {
1096 | "cell_type": "code",
1097 | "execution_count": 28,
1098 | "metadata": {},
1099 | "outputs": [
1100 | {
1101 | "name": "stdout",
1102 | "output_type": "stream",
1103 | "text": [
1104 | " 30.000 μs (142 allocations: 12.19 KiB)\n"
1105 | ]
1106 | },
1107 | {
1108 | "data": {
1109 | "text/plain": [
1110 | "(1, Interval{Float64}[[0.956272, 0.956273], [0.160087, 0.160088], [0.514768, 0.514769]])"
1111 | ]
1112 | },
1113 | "execution_count": 28,
1114 | "metadata": {},
1115 | "output_type": "execute_result"
1116 | }
1117 | ],
1118 | "source": [
1119 | "using BenchmarkTools\n",
1120 | "@btime verifynlss_IntervalNewton($F,$x0)"
1121 | ]
1122 | },
1123 | {
1124 | "cell_type": "code",
1125 | "execution_count": 29,
1126 | "metadata": {},
1127 | "outputs": [
1128 | {
1129 | "name": "stdout",
1130 | "output_type": "stream",
1131 | "text": [
1132 | " 23.541 μs (144 allocations: 12.14 KiB)\n"
1133 | ]
1134 | },
1135 | {
1136 | "data": {
1137 | "text/plain": [
1138 | "(1, Interval{Float64}[[0.956272, 0.956273], [0.160087, 0.160088], [0.514768, 0.514769]])"
1139 | ]
1140 | },
1141 | "execution_count": 29,
1142 | "metadata": {},
1143 | "output_type": "execute_result"
1144 | }
1145 | ],
1146 | "source": [
1147 | "@btime verifynlss_krawczyk($F,$x0)"
1148 | ]
1149 | },
1150 | {
1151 | "cell_type": "markdown",
1152 | "metadata": {},
1153 | "source": [
1154 | "### 参考文献\n",
1155 | "\n",
1156 | "1. 大石進一編著, 精度保証付き数値計算の基礎, コロナ社, 2018.
\n",
1157 | "(精度保証付き数値計算の教科書. 浮動小数点数および区間演算に詳しい. 今回は6章を参考にした)\n",
1158 | "1. 柏木雅英, 非線形方程式の解の精度保証 (+ 自動微分), 数値解析特論C (2020年度)講義資料
\n",
1159 | "http://www.kashi.info.waseda.ac.jp/~kashi/lec2020/nac/krawczyk.pdf (最終閲覧日:2020年12月24日)."
1160 | ]
1161 | },
1162 | {
1163 | "cell_type": "markdown",
1164 | "metadata": {},
1165 | "source": [
1166 | "大谷俊輔,
高安亮紀,2020年12月24日日(最終更新:2023年5月21日)
"
1167 | ]
1168 | }
1169 | ],
1170 | "metadata": {
1171 | "kernelspec": {
1172 | "display_name": "Julia 1.9.0",
1173 | "language": "julia",
1174 | "name": "julia-1.9"
1175 | },
1176 | "language_info": {
1177 | "file_extension": ".jl",
1178 | "mimetype": "application/julia",
1179 | "name": "julia",
1180 | "version": "1.9.0"
1181 | }
1182 | },
1183 | "nbformat": 4,
1184 | "nbformat_minor": 4
1185 | }
1186 |
--------------------------------------------------------------------------------