Compare commits
42 Commits
number-num
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 35831eaa72 | |||
| 40e3eb831a | |||
| e693e840ae | |||
| e30024cbd5 | |||
| b4ed75e95c | |||
| baf09ec891 | |||
| b4813a5862 | |||
| 77897b5488 | |||
| 9d63317254 | |||
| 318338e39b | |||
| 5a696f143e | |||
| 5cab55a9fd | |||
| bd8f52d1bb | |||
| 3291ddded3 | |||
| 64d2b07865 | |||
| 71b4f64594 | |||
| 12fe38bfd5 | |||
| dd3b9a4ee7 | |||
| 125c7a6c02 | |||
| c47d9cb6f1 | |||
| 3e7746714c | |||
| a699e24eaf | |||
| 5c6795163f | |||
| 46128385fc | |||
| 93889bc2a9 | |||
| 3581c804cf | |||
| 91cfd2397a | |||
| 8d14c5b414 | |||
| 52913ce62d | |||
| ee4b2cd0a5 | |||
| 9385797c63 | |||
| 86778da52a | |||
| bee03e467f | |||
| 8d590251f6 | |||
| b0ce5e8f83 | |||
| 72e2489e3c | |||
| a293cd57e9 | |||
| 6fc358b8a5 | |||
| dea3fa5e5b | |||
| 8c0839c086 | |||
| 398eedc07a | |||
| 497f057b9b |
@ -0,0 +1,334 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="460.8pt" height="345.6pt" viewBox="0 0 460.8 345.6" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
<metadata>
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-08-03T22:53:53.738594</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Matplotlib v3.10.3, https://matplotlib.org/</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs>
|
||||
<style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 345.6
|
||||
L 460.8 345.6
|
||||
L 460.8 0
|
||||
L 0 0
|
||||
z
|
||||
" style="fill: #ffffff"/>
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="patch_2">
|
||||
<path d="M 57.6 307.584
|
||||
L 414.72 307.584
|
||||
L 414.72 41.472
|
||||
L 57.6 41.472
|
||||
z
|
||||
" style="fill: #ffffff"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 390.514075 187.957836
|
||||
Q 236.158737 122.454172 82.832593 57.387266
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 85.733454 60.790941
|
||||
L 82.832593 57.387266
|
||||
L 87.29605 57.108782
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="patch_4">
|
||||
<path d="M 279.499516 173.899003
|
||||
Q 334.710372 181.992361 388.815017 189.92356
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 385.147394 187.364549
|
||||
L 388.815017 189.92356
|
||||
L 384.567234 191.322252
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="patch_5">
|
||||
<path d="M 263.518245 168.164289
|
||||
Q 172.381729 113.105228 82.202167 58.624299
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 84.591676 62.404543
|
||||
L 82.202167 58.624299
|
||||
L 86.660068 58.980838
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="patch_6">
|
||||
<path d="M 267.221177 180.467066
|
||||
Q 241.802173 234.06546 216.862251 286.653664
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 220.383344 283.89651
|
||||
L 216.862251 286.653664
|
||||
L 216.769184 282.182497
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="patch_7">
|
||||
<path d="M 220.228666 291.252913
|
||||
Q 305.581235 243.413972 389.958514 196.121668
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 385.491361 196.332723
|
||||
L 389.958514 196.121668
|
||||
L 387.447066 199.822023
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="patch_8">
|
||||
<path d="M 208.361368 287.975988
|
||||
Q 143.251403 174.525827 78.697949 62.045353
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 78.954355 66.510133
|
||||
L 78.697949 62.045353
|
||||
L 82.423616 64.519096
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="patch_9">
|
||||
<path d="M 216.383108 287.663982
|
||||
Q 241.802112 234.065589 266.742034 181.477385
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: none; stroke: #000000; stroke-linecap: round"/>
|
||||
<path d="M 263.22094 184.234539
|
||||
L 266.742034 181.477385
|
||||
L 266.835101 185.948552
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="stroke: #000000; stroke-linecap: round"/>
|
||||
</g>
|
||||
<g id="matplotlib.axis_1">
|
||||
<g id="xtick_1"/>
|
||||
<g id="xtick_2"/>
|
||||
<g id="xtick_3"/>
|
||||
<g id="xtick_4"/>
|
||||
<g id="xtick_5"/>
|
||||
<g id="xtick_6"/>
|
||||
<g id="xtick_7"/>
|
||||
<g id="xtick_8"/>
|
||||
<g id="xtick_9"/>
|
||||
</g>
|
||||
<g id="matplotlib.axis_2">
|
||||
<g id="ytick_1"/>
|
||||
<g id="ytick_2"/>
|
||||
<g id="ytick_3"/>
|
||||
<g id="ytick_4"/>
|
||||
<g id="ytick_5"/>
|
||||
<g id="ytick_6"/>
|
||||
<g id="ytick_7"/>
|
||||
</g>
|
||||
<g id="PathCollection_1">
|
||||
<path d="M 398.487273 200.00167
|
||||
C 400.783999 200.00167 402.986966 199.089171 404.610997 197.465141
|
||||
C 406.235028 195.84111 407.147527 193.638142 407.147527 191.341416
|
||||
C 407.147527 189.04469 406.235028 186.841722 404.610997 185.217692
|
||||
C 402.986966 183.593661 400.783999 182.681162 398.487273 182.681162
|
||||
C 396.190547 182.681162 393.987579 183.593661 392.363548 185.217692
|
||||
C 390.739518 186.841722 389.827019 189.04469 389.827019 191.341416
|
||||
C 389.827019 193.638142 390.739518 195.84111 392.363548 197.465141
|
||||
C 393.987579 199.089171 396.190547 200.00167 398.487273 200.00167
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: #1f78b4; stroke: #1f78b4"/>
|
||||
<path d="M 73.832727 62.228254
|
||||
C 76.129453 62.228254 78.332421 61.315755 79.956452 59.691724
|
||||
C 81.580482 58.067694 82.492981 55.864726 82.492981 53.568
|
||||
C 82.492981 51.271274 81.580482 49.068306 79.956452 47.444276
|
||||
C 78.332421 45.820245 76.129453 44.907746 73.832727 44.907746
|
||||
C 71.536001 44.907746 69.333034 45.820245 67.709003 47.444276
|
||||
C 66.084972 49.068306 65.172473 51.271274 65.172473 53.568
|
||||
C 65.172473 55.864726 66.084972 58.067694 67.709003 59.691724
|
||||
C 69.333034 61.315755 71.536001 62.228254 73.832727 62.228254
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: #1f78b4; stroke: #1f78b4"/>
|
||||
<path d="M 270.931712 181.303303
|
||||
C 273.228439 181.303303 275.431406 180.390804 277.055437 178.766773
|
||||
C 278.679467 177.142742 279.591966 174.939775 279.591966 172.643049
|
||||
C 279.591966 170.346322 278.679467 168.143355 277.055437 166.519324
|
||||
C 275.431406 164.895294 273.228439 163.982795 270.931712 163.982795
|
||||
C 268.634986 163.982795 266.432019 164.895294 264.807988 166.519324
|
||||
C 263.183957 168.143355 262.271458 170.346322 262.271458 172.643049
|
||||
C 262.271458 174.939775 263.183957 177.142742 264.807988 178.766773
|
||||
C 266.432019 180.390804 268.634986 181.303303 270.931712 181.303303
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: #1f78b4; stroke: #1f78b4"/>
|
||||
<path d="M 212.672572 304.148254
|
||||
C 214.969299 304.148254 217.172266 303.235755 218.796297 301.611724
|
||||
C 220.420327 299.987694 221.332826 297.784726 221.332826 295.488
|
||||
C 221.332826 293.191274 220.420327 290.988306 218.796297 289.364276
|
||||
C 217.172266 287.740245 214.969299 286.827746 212.672572 286.827746
|
||||
C 210.375846 286.827746 208.172879 287.740245 206.548848 289.364276
|
||||
C 204.924817 290.988306 204.012318 293.191274 204.012318 295.488
|
||||
C 204.012318 297.784726 204.924817 299.987694 206.548848 301.611724
|
||||
C 208.172879 303.235755 210.375846 304.148254 212.672572 304.148254
|
||||
z
|
||||
" clip-path="url(#p8fa51a7528)" style="fill: #1f78b4; stroke: #1f78b4"/>
|
||||
</g>
|
||||
<g id="patch_10">
|
||||
<path d="M 57.6 307.584
|
||||
L 57.6 41.472
|
||||
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_11">
|
||||
<path d="M 414.72 307.584
|
||||
L 414.72 41.472
|
||||
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_12">
|
||||
<path d="M 57.6 307.584
|
||||
L 414.72 307.584
|
||||
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_13">
|
||||
<path d="M 57.6 41.472
|
||||
L 414.72 41.472
|
||||
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
<!-- 10 -->
|
||||
<g transform="translate(390.852273 194.652666) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-31" d="M 794 531
|
||||
L 1825 531
|
||||
L 1825 4091
|
||||
L 703 3866
|
||||
L 703 4441
|
||||
L 1819 4666
|
||||
L 2450 4666
|
||||
L 2450 531
|
||||
L 3481 531
|
||||
L 3481 0
|
||||
L 794 0
|
||||
L 794 531
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
<path id="DejaVuSans-30" d="M 2034 4250
|
||||
Q 1547 4250 1301 3770
|
||||
Q 1056 3291 1056 2328
|
||||
Q 1056 1369 1301 889
|
||||
Q 1547 409 2034 409
|
||||
Q 2525 409 2770 889
|
||||
Q 3016 1369 3016 2328
|
||||
Q 3016 3291 2770 3770
|
||||
Q 2525 4250 2034 4250
|
||||
z
|
||||
M 2034 4750
|
||||
Q 2819 4750 3233 4129
|
||||
Q 3647 3509 3647 2328
|
||||
Q 3647 1150 3233 529
|
||||
Q 2819 -91 2034 -91
|
||||
Q 1250 -91 836 529
|
||||
Q 422 1150 422 2328
|
||||
Q 422 3509 836 4129
|
||||
Q 1250 4750 2034 4750
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use xlink:href="#DejaVuSans-30" transform="translate(63.623047 0)"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
<!-- 16 -->
|
||||
<g transform="translate(66.197727 56.87925) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-36" d="M 2113 2584
|
||||
Q 1688 2584 1439 2293
|
||||
Q 1191 2003 1191 1497
|
||||
Q 1191 994 1439 701
|
||||
Q 1688 409 2113 409
|
||||
Q 2538 409 2786 701
|
||||
Q 3034 994 3034 1497
|
||||
Q 3034 2003 2786 2293
|
||||
Q 2538 2584 2113 2584
|
||||
z
|
||||
M 3366 4563
|
||||
L 3366 3988
|
||||
Q 3128 4100 2886 4159
|
||||
Q 2644 4219 2406 4219
|
||||
Q 1781 4219 1451 3797
|
||||
Q 1122 3375 1075 2522
|
||||
Q 1259 2794 1537 2939
|
||||
Q 1816 3084 2150 3084
|
||||
Q 2853 3084 3261 2657
|
||||
Q 3669 2231 3669 1497
|
||||
Q 3669 778 3244 343
|
||||
Q 2819 -91 2113 -91
|
||||
Q 1303 -91 875 529
|
||||
Q 447 1150 447 2328
|
||||
Q 447 3434 972 4092
|
||||
Q 1497 4750 2381 4750
|
||||
Q 2619 4750 2861 4703
|
||||
Q 3103 4656 3366 4563
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use xlink:href="#DejaVuSans-36" transform="translate(63.623047 0)"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
<!-- 14 -->
|
||||
<g transform="translate(263.296712 175.954299) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-34" d="M 2419 4116
|
||||
L 825 1625
|
||||
L 2419 1625
|
||||
L 2419 4116
|
||||
z
|
||||
M 2253 4666
|
||||
L 3047 4666
|
||||
L 3047 1625
|
||||
L 3713 1625
|
||||
L 3713 1100
|
||||
L 3047 1100
|
||||
L 3047 0
|
||||
L 2419 0
|
||||
L 2419 1100
|
||||
L 313 1100
|
||||
L 313 1709
|
||||
L 2253 4666
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use xlink:href="#DejaVuSans-34" transform="translate(63.623047 0)"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
<!-- 17 -->
|
||||
<g transform="translate(205.037572 298.79925) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-37" d="M 525 4666
|
||||
L 3525 4666
|
||||
L 3525 4397
|
||||
L 1831 0
|
||||
L 1172 0
|
||||
L 2766 4134
|
||||
L 525 4134
|
||||
L 525 4666
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-31"/>
|
||||
<use xlink:href="#DejaVuSans-37" transform="translate(63.623047 0)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="p8fa51a7528">
|
||||
<rect x="57.6" y="41.472" width="357.12" height="266.112"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 10 KiB |
@ -6,7 +6,7 @@
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-07-30T03:24:44.195833</dc:date>
|
||||
<dc:date>2025-07-30T17:57:28.746680</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
@ -41,12 +41,12 @@ z
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path id="m892aac2c98" d="M 0 0
|
||||
<path id="m704ac02017" d="M 0 0
|
||||
L 0 3.5
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m892aac2c98" x="50.822178" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m704ac02017" x="50.822178" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
@ -91,7 +91,7 @@ z
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use xlink:href="#m892aac2c98" x="125.939735" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m704ac02017" x="125.939735" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
@ -132,7 +132,7 @@ z
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use xlink:href="#m892aac2c98" x="201.057292" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m704ac02017" x="201.057292" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
@ -168,7 +168,7 @@ z
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use xlink:href="#m892aac2c98" x="276.174848" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m704ac02017" x="276.174848" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
@ -215,7 +215,7 @@ z
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use xlink:href="#m892aac2c98" x="351.292405" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m704ac02017" x="351.292405" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
@ -271,7 +271,7 @@ z
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use xlink:href="#m892aac2c98" x="426.409961" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m704ac02017" x="426.409961" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
@ -304,12 +304,12 @@ z
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_7">
|
||||
<defs>
|
||||
<path id="m75d00b8c11" d="M 0 0
|
||||
<path id="m3fb0ce5ed4" d="M 0 0
|
||||
L -3.5 0
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="307.955174" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="307.955174" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
@ -324,7 +324,7 @@ L -3.5 0
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="271.90043" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="271.90043" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
@ -339,7 +339,7 @@ L -3.5 0
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_9">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="235.845686" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="235.845686" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
@ -388,7 +388,7 @@ z
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_10">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="199.790942" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="199.790942" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
@ -403,7 +403,7 @@ z
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="163.736198" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="163.736198" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
@ -445,7 +445,7 @@ z
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="127.681455" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="127.681455" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
@ -460,7 +460,7 @@ z
|
||||
<g id="ytick_7">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="91.626711" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="91.626711" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
@ -487,7 +487,7 @@ z
|
||||
<g id="ytick_8">
|
||||
<g id="line2d_14">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="55.571967" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="55.571967" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
@ -502,7 +502,7 @@ z
|
||||
<g id="ytick_9">
|
||||
<g id="line2d_15">
|
||||
<g>
|
||||
<use xlink:href="#m75d00b8c11" x="33.73" y="19.517223" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m3fb0ce5ed4" x="33.73" y="19.517223" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
@ -711,7 +711,7 @@ L 417.607123 38.931316
|
||||
L 420.541402 34.969256
|
||||
L 423.475682 28.530909
|
||||
L 423.475682 28.530909
|
||||
" clip-path="url(#pcdb8677c09)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
" clip-path="url(#p6dddd11e9f)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 33.73 317.72
|
||||
@ -736,7 +736,7 @@ L 442.035 14.76
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pcdb8677c09">
|
||||
<clipPath id="p6dddd11e9f">
|
||||
<rect x="33.73" y="14.76" width="408.305" height="302.96"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -6,7 +6,7 @@
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-07-30T03:24:45.626240</dc:date>
|
||||
<dc:date>2025-07-30T17:57:30.086732</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
@ -41,12 +41,12 @@ z
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path id="m5acaa62661" d="M 0 0
|
||||
<path id="m82b750d18c" d="M 0 0
|
||||
L 0 3.5
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="46.890244" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="46.890244" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
@ -105,7 +105,7 @@ z
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="95.481913" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="95.481913" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
@ -146,7 +146,7 @@ z
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="144.073583" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="144.073583" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
@ -195,7 +195,7 @@ z
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="192.665252" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="192.665252" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
@ -231,7 +231,7 @@ z
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="241.256921" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="241.256921" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
@ -273,7 +273,7 @@ z
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="289.848591" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="289.848591" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
@ -320,7 +320,7 @@ z
|
||||
<g id="xtick_7">
|
||||
<g id="line2d_7">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="338.44026" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="338.44026" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
@ -347,7 +347,7 @@ z
|
||||
<g id="xtick_8">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="387.03193" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="387.03193" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
@ -403,7 +403,7 @@ z
|
||||
<g id="xtick_9">
|
||||
<g id="line2d_9">
|
||||
<g>
|
||||
<use xlink:href="#m5acaa62661" x="435.623599" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b750d18c" x="435.623599" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
@ -452,12 +452,12 @@ z
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_10">
|
||||
<defs>
|
||||
<path id="m37c12ea835" d="M 0 0
|
||||
<path id="me91bacd9d9" d="M 0 0
|
||||
L -3.5 0
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m37c12ea835" x="33.73" y="305.0377" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#me91bacd9d9" x="33.73" y="305.0377" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
@ -472,7 +472,7 @@ L -3.5 0
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use xlink:href="#m37c12ea835" x="33.73" y="249.300898" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#me91bacd9d9" x="33.73" y="249.300898" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
@ -487,7 +487,7 @@ L -3.5 0
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use xlink:href="#m37c12ea835" x="33.73" y="193.564096" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#me91bacd9d9" x="33.73" y="193.564096" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
@ -502,7 +502,7 @@ L -3.5 0
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use xlink:href="#m37c12ea835" x="33.73" y="137.827294" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#me91bacd9d9" x="33.73" y="137.827294" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
@ -517,7 +517,7 @@ L -3.5 0
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_14">
|
||||
<g>
|
||||
<use xlink:href="#m37c12ea835" x="33.73" y="82.090492" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#me91bacd9d9" x="33.73" y="82.090492" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
@ -532,7 +532,7 @@ L -3.5 0
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_15">
|
||||
<g>
|
||||
<use xlink:href="#m37c12ea835" x="33.73" y="26.35369" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#me91bacd9d9" x="33.73" y="26.35369" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
@ -718,7 +718,7 @@ L 409.458854 32.885347
|
||||
L 414.798598 30.708128
|
||||
L 423.475682 28.530909
|
||||
L 423.475682 28.530909
|
||||
" clip-path="url(#p5e155994b3)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
" clip-path="url(#p34cae5d2d5)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 33.73 317.72
|
||||
@ -743,7 +743,7 @@ L 442.035 14.76
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="p5e155994b3">
|
||||
<clipPath id="p34cae5d2d5">
|
||||
<rect x="33.73" y="14.76" width="408.305" height="302.96"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -6,7 +6,7 @@
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-07-30T03:24:47.581890</dc:date>
|
||||
<dc:date>2025-07-30T17:57:32.094511</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
@ -41,12 +41,12 @@ z
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path id="m046502f3ac" d="M 0 0
|
||||
<path id="m89e3a504e2" d="M 0 0
|
||||
L 0 3.5
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="52.289318" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="52.289318" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
@ -91,7 +91,7 @@ z
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="101.129629" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="101.129629" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
@ -157,7 +157,7 @@ z
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="149.96994" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="149.96994" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
@ -172,7 +172,7 @@ z
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="198.810251" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="198.810251" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
@ -199,7 +199,7 @@ z
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="247.650562" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="247.650562" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
@ -231,7 +231,7 @@ z
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="296.490873" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="296.490873" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
@ -247,7 +247,7 @@ z
|
||||
<g id="xtick_7">
|
||||
<g id="line2d_7">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="345.331184" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="345.331184" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
@ -263,7 +263,7 @@ z
|
||||
<g id="xtick_8">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use xlink:href="#m046502f3ac" x="394.171495" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m89e3a504e2" x="394.171495" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
@ -281,12 +281,12 @@ z
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_9">
|
||||
<defs>
|
||||
<path id="m47bb0dc095" d="M 0 0
|
||||
<path id="ma9be8e3392" d="M 0 0
|
||||
L -3.5 0
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="285.587879" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="285.587879" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
@ -335,7 +335,7 @@ z
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_10">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="248.865455" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="248.865455" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
@ -371,7 +371,7 @@ z
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="212.14303" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="212.14303" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
@ -386,7 +386,7 @@ z
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="175.420606" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="175.420606" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
@ -433,7 +433,7 @@ z
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="138.698182" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="138.698182" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
@ -448,7 +448,7 @@ z
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_14">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="101.975758" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="101.975758" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
@ -504,7 +504,7 @@ z
|
||||
<g id="ytick_7">
|
||||
<g id="line2d_15">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="65.253333" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="65.253333" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
@ -551,7 +551,7 @@ z
|
||||
<g id="ytick_8">
|
||||
<g id="line2d_16">
|
||||
<g>
|
||||
<use xlink:href="#m47bb0dc095" x="33.73" y="28.530909" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma9be8e3392" x="33.73" y="28.530909" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_16">
|
||||
@ -585,7 +585,7 @@ L 364.867309 273.348939
|
||||
L 384.403433 273.346137
|
||||
L 403.939557 273.347538
|
||||
L 423.475682 273.346837
|
||||
" clip-path="url(#pa627a7ffc5)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
" clip-path="url(#pe6fd9f8a67)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 33.73 317.72
|
||||
@ -610,7 +610,7 @@ L 442.035 14.76
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pa627a7ffc5">
|
||||
<clipPath id="pe6fd9f8a67">
|
||||
<rect x="33.73" y="14.76" width="408.305" height="302.96"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@ -6,7 +6,7 @@
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-07-30T03:24:48.966696</dc:date>
|
||||
<dc:date>2025-07-30T17:57:33.434057</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
@ -41,12 +41,12 @@ z
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path id="m07f79dce64" d="M 0 0
|
||||
<path id="m833a6f6579" d="M 0 0
|
||||
L 0 3.5
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="52.289318" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="52.289318" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
@ -91,7 +91,7 @@ z
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="101.129629" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="101.129629" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
@ -157,7 +157,7 @@ z
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="149.96994" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="149.96994" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
@ -172,7 +172,7 @@ z
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="198.810251" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="198.810251" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
@ -199,7 +199,7 @@ z
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="247.650562" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="247.650562" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
@ -231,7 +231,7 @@ z
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="296.490873" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="296.490873" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
@ -247,7 +247,7 @@ z
|
||||
<g id="xtick_7">
|
||||
<g id="line2d_7">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="345.331184" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="345.331184" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
@ -263,7 +263,7 @@ z
|
||||
<g id="xtick_8">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use xlink:href="#m07f79dce64" x="394.171495" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m833a6f6579" x="394.171495" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
@ -281,12 +281,12 @@ z
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_9">
|
||||
<defs>
|
||||
<path id="mb3f1fe34b9" d="M 0 0
|
||||
<path id="ma722f2b5b1" d="M 0 0
|
||||
L -3.5 0
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
@ -335,7 +335,7 @@ z
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_10">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="276.407273" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="276.407273" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
@ -371,7 +371,7 @@ z
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="235.094545" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="235.094545" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
@ -386,7 +386,7 @@ z
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="193.781818" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="193.781818" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
@ -433,7 +433,7 @@ z
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="152.469091" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="152.469091" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
@ -448,7 +448,7 @@ z
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_14">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="111.156364" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="111.156364" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
@ -504,7 +504,7 @@ z
|
||||
<g id="ytick_7">
|
||||
<g id="line2d_15">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="69.843636" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="69.843636" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
@ -551,7 +551,7 @@ z
|
||||
<g id="ytick_8">
|
||||
<g id="line2d_16">
|
||||
<g>
|
||||
<use xlink:href="#mb3f1fe34b9" x="33.73" y="28.530909" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#ma722f2b5b1" x="33.73" y="28.530909" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_16">
|
||||
@ -585,7 +585,7 @@ L 364.867309 283.857633
|
||||
L 384.403433 283.857595
|
||||
L 403.939557 283.857609
|
||||
L 423.475682 283.857604
|
||||
" clip-path="url(#pd6aa75d2de)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
" clip-path="url(#p415d5a15a1)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 33.73 317.72
|
||||
@ -610,7 +610,7 @@ L 442.035 14.76
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pd6aa75d2de">
|
||||
<clipPath id="p415d5a15a1">
|
||||
<rect x="33.73" y="14.76" width="408.305" height="302.96"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@ -6,7 +6,7 @@
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-07-30T03:24:50.499195</dc:date>
|
||||
<dc:date>2025-07-30T17:57:34.918996</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
@ -41,12 +41,12 @@ z
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path id="mc3d70383f3" d="M 0 0
|
||||
<path id="m6d98df33d4" d="M 0 0
|
||||
L 0 3.5
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="45.540475" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="45.540475" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
@ -82,7 +82,7 @@ z
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="99.531219" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="99.531219" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
@ -111,7 +111,7 @@ z
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="153.521963" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="153.521963" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
@ -150,7 +150,7 @@ z
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="207.512707" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="207.512707" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
@ -197,7 +197,7 @@ z
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="261.50345" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="261.50345" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
@ -231,7 +231,7 @@ z
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="315.494194" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="315.494194" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
@ -271,7 +271,7 @@ z
|
||||
<g id="xtick_7">
|
||||
<g id="line2d_7">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="369.484938" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="369.484938" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
@ -316,7 +316,7 @@ z
|
||||
<g id="xtick_8">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use xlink:href="#mc3d70383f3" x="423.475682" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m6d98df33d4" x="423.475682" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
@ -343,12 +343,12 @@ z
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_9">
|
||||
<defs>
|
||||
<path id="m25ae3c1345" d="M 0 0
|
||||
<path id="mba0142a176" d="M 0 0
|
||||
L -3.5 0
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="305.0377" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="305.0377" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
@ -373,7 +373,7 @@ z
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_10">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="270.202199" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="270.202199" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
@ -389,7 +389,7 @@ z
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="235.366698" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="235.366698" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
@ -405,7 +405,7 @@ z
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="200.531197" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="200.531197" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
@ -421,7 +421,7 @@ z
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="165.695695" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="165.695695" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
@ -437,7 +437,7 @@ z
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_14">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="130.860194" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="130.860194" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
@ -453,7 +453,7 @@ z
|
||||
<g id="ytick_7">
|
||||
<g id="line2d_15">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="96.024693" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="96.024693" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
@ -469,7 +469,7 @@ z
|
||||
<g id="ytick_8">
|
||||
<g id="line2d_16">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="61.189192" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="61.189192" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_16">
|
||||
@ -485,7 +485,7 @@ z
|
||||
<g id="ytick_9">
|
||||
<g id="line2d_17">
|
||||
<g>
|
||||
<use xlink:href="#m25ae3c1345" x="33.73" y="26.35369" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#mba0142a176" x="33.73" y="26.35369" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_17">
|
||||
@ -635,7 +635,7 @@ L 315.494194 35.062566
|
||||
L 369.484938 30.708128
|
||||
L 423.475682 28.530909
|
||||
L 423.475682 28.530909
|
||||
" clip-path="url(#p73b8bb224c)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
" clip-path="url(#p3e78a3e766)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 33.73 317.72
|
||||
@ -660,7 +660,7 @@ L 442.035 14.76
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="p73b8bb224c">
|
||||
<clipPath id="p3e78a3e766">
|
||||
<rect x="33.73" y="14.76" width="408.305" height="302.96"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -6,7 +6,7 @@
|
||||
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<cc:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:date>2025-07-30T03:24:51.868847</dc:date>
|
||||
<dc:date>2025-07-30T17:57:36.284432</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
@ -41,12 +41,12 @@ z
|
||||
<g id="xtick_1">
|
||||
<g id="line2d_1">
|
||||
<defs>
|
||||
<path id="mc193fece69" d="M 0 0
|
||||
<path id="m0e596f21b4" d="M 0 0
|
||||
L 0 3.5
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="59.614794" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="59.614794" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_1">
|
||||
@ -123,7 +123,7 @@ z
|
||||
<g id="xtick_2">
|
||||
<g id="line2d_2">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="105.720873" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="105.720873" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_2">
|
||||
@ -180,7 +180,7 @@ z
|
||||
<g id="xtick_3">
|
||||
<g id="line2d_3">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="151.826952" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="151.826952" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_3">
|
||||
@ -196,7 +196,7 @@ z
|
||||
<g id="xtick_4">
|
||||
<g id="line2d_4">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="197.93303" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="197.93303" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_4">
|
||||
@ -212,7 +212,7 @@ z
|
||||
<g id="xtick_5">
|
||||
<g id="line2d_5">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="244.039109" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="244.039109" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_5">
|
||||
@ -227,7 +227,7 @@ z
|
||||
<g id="xtick_6">
|
||||
<g id="line2d_6">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="290.145188" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="290.145188" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_6">
|
||||
@ -242,7 +242,7 @@ z
|
||||
<g id="xtick_7">
|
||||
<g id="line2d_7">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="336.251266" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="336.251266" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_7">
|
||||
@ -257,7 +257,7 @@ z
|
||||
<g id="xtick_8">
|
||||
<g id="line2d_8">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="382.357345" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="382.357345" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_8">
|
||||
@ -272,7 +272,7 @@ z
|
||||
<g id="xtick_9">
|
||||
<g id="line2d_9">
|
||||
<g>
|
||||
<use xlink:href="#mc193fece69" x="428.463424" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m0e596f21b4" x="428.463424" y="317.72" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_9">
|
||||
@ -289,12 +289,12 @@ z
|
||||
<g id="ytick_1">
|
||||
<g id="line2d_10">
|
||||
<defs>
|
||||
<path id="m395566e9f4" d="M 0 0
|
||||
<path id="m82b563dc58" d="M 0 0
|
||||
L -3.5 0
|
||||
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</defs>
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="305.0377" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="305.0377" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_10">
|
||||
@ -310,7 +310,7 @@ L -3.5 0
|
||||
<g id="ytick_2">
|
||||
<g id="line2d_11">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="270.202199" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="270.202199" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_11">
|
||||
@ -326,7 +326,7 @@ L -3.5 0
|
||||
<g id="ytick_3">
|
||||
<g id="line2d_12">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="235.366698" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="235.366698" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_12">
|
||||
@ -342,7 +342,7 @@ L -3.5 0
|
||||
<g id="ytick_4">
|
||||
<g id="line2d_13">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="200.531197" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="200.531197" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_13">
|
||||
@ -370,7 +370,7 @@ z
|
||||
<g id="ytick_5">
|
||||
<g id="line2d_14">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="165.695695" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="165.695695" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_14">
|
||||
@ -386,7 +386,7 @@ z
|
||||
<g id="ytick_6">
|
||||
<g id="line2d_15">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="130.860194" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="130.860194" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_15">
|
||||
@ -402,7 +402,7 @@ z
|
||||
<g id="ytick_7">
|
||||
<g id="line2d_16">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="96.024693" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="96.024693" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_16">
|
||||
@ -418,7 +418,7 @@ z
|
||||
<g id="ytick_8">
|
||||
<g id="line2d_17">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="61.189192" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="61.189192" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_17">
|
||||
@ -434,7 +434,7 @@ z
|
||||
<g id="ytick_9">
|
||||
<g id="line2d_18">
|
||||
<g>
|
||||
<use xlink:href="#m395566e9f4" x="33.73" y="26.35369" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
<use xlink:href="#m82b563dc58" x="33.73" y="26.35369" style="stroke: #000000; stroke-width: 0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="text_18">
|
||||
@ -614,7 +614,7 @@ L 401.237608 32.885347
|
||||
L 409.261115 30.708128
|
||||
L 423.475682 28.530909
|
||||
L 423.475682 28.530909
|
||||
" clip-path="url(#p1ecf935b40)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
" clip-path="url(#pde11ae1218)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
</g>
|
||||
<g id="patch_3">
|
||||
<path d="M 33.73 317.72
|
||||
@ -639,7 +639,7 @@ L 442.035 14.76
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="p1ecf935b40">
|
||||
<clipPath id="pde11ae1218">
|
||||
<rect x="33.73" y="14.76" width="408.305" height="302.96"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
16
_freeze/posts/math/stereo/1/index/execute-results/html.json
Normal file
BIN
_freeze/posts/math/stereo/1/index/figure-html/cell-3-output-1.png
(Stored with Git LFS)
Normal file
BIN
_freeze/posts/math/stereo/1/index/figure-html/cell-3-output-2.png
(Stored with Git LFS)
Normal file
BIN
_freeze/posts/math/stereo/1/index/figure-html/cell-4-output-1.png
(Stored with Git LFS)
Normal file
BIN
_freeze/posts/math/stereo/1/index/figure-html/cell-4-output-2.png
(Stored with Git LFS)
Normal file
12
_freeze/posts/math/stereo/2/index/execute-results/html.json
Normal file
168
_quarto.yml
@ -2,18 +2,180 @@ project:
|
||||
type: website
|
||||
|
||||
website:
|
||||
favicon: ./logo-favicon.png
|
||||
favicon: "./logo-favicon.png"
|
||||
title: "zenzicubi.co"
|
||||
|
||||
repo-url: https://git.zenzicubi.co/cube/zenzicubi.co
|
||||
repo-branch: master
|
||||
repo-actions: [source]
|
||||
repo-link-target: "_blank"
|
||||
|
||||
navbar:
|
||||
logo: "/logo-vector.svg"
|
||||
logo: "./logo-vector.svg"
|
||||
left:
|
||||
- text: "Math"
|
||||
menu:
|
||||
- ./posts/math/polycount/index.qmd
|
||||
- ./posts/math/pentagons/index.qmd
|
||||
- ./posts/math/chebyshev/index.qmd
|
||||
- ./posts/math/stereo/index.qmd
|
||||
- ./posts/math/permutations/index.qmd
|
||||
- ./posts/math/type-algebra/index.qmd
|
||||
- ./posts/math/number-number/index.qmd
|
||||
- ./posts/math/finite-field/index.qmd
|
||||
- ./posts/math/misc/index.qmd
|
||||
right:
|
||||
# - about.qmd
|
||||
- ./about/index.qmd
|
||||
- icon: git
|
||||
href: https://git.zenzicubi.co/cube/zenzicubi.co
|
||||
- icon: github
|
||||
href: https://github.com/queue-miscreant
|
||||
background: primary
|
||||
search: true
|
||||
draft-mode: unlinked
|
||||
|
||||
sidebar:
|
||||
- id: topic-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Topics"
|
||||
contents:
|
||||
- ./posts/math/polycount/index.qmd
|
||||
- ./posts/math/pentagons/index.qmd
|
||||
- ./posts/math/chebyshev/index.qmd
|
||||
- ./posts/math/stereo/index.qmd
|
||||
- ./posts/math/permutations/index.qmd
|
||||
- ./posts/math/type-algebra/index.qmd
|
||||
- ./posts/math/number-number/index.qmd
|
||||
- ./posts/math/finite-field/index.qmd
|
||||
- ./posts/math/misc/index.qmd
|
||||
|
||||
- id: misc-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Miscellaneous"
|
||||
contents:
|
||||
- ./posts/math/misc/platonic-volume/index.qmd
|
||||
- ./posts/math/misc/infinitesimals/index.qmd
|
||||
|
||||
- id: polycount-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Polynomial Counting"
|
||||
href: ./posts/math/polycount/index.qmd
|
||||
contents:
|
||||
- text: "Part 1: A primer"
|
||||
href: ./posts/math/polycount/1/index.qmd
|
||||
- text: "Part 2: Binary and beyond"
|
||||
href: ./posts/math/polycount/2/index.qmd
|
||||
- text: "Part 3: The third degree"
|
||||
href: ./posts/math/polycount/3/index.qmd
|
||||
- text: "Part 4: Two twos"
|
||||
href: ./posts/math/polycount/4/index.qmd
|
||||
contents:
|
||||
- text: "Appendix"
|
||||
href: ./posts/math/polycount/4/appendix/index.qmd
|
||||
- text: "Part 5: Pentamerous multiplication"
|
||||
href: ./posts/math/polycount/5/index.qmd
|
||||
- section: 2D
|
||||
contents:
|
||||
- text: "Part 1: Lines, leaves, and sand"
|
||||
href: ./posts/math/polycount/sand-1/index.qmd
|
||||
- text: "Part 2: Reorienting Polynomials"
|
||||
href: ./posts/math/polycount/sand-2/index.qmd
|
||||
|
||||
- id: pentagons-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "12 Pentagons"
|
||||
href: ./posts/math/pentagons/index.qmd
|
||||
contents:
|
||||
- text: "Part 1"
|
||||
href: ./posts/math/pentagons/1/index.qmd
|
||||
- text: "Part 2"
|
||||
href: ./posts/math/pentagons/2/index.qmd
|
||||
- text: "Part 3"
|
||||
href: ./posts/math/pentagons/3/index.qmd
|
||||
|
||||
- id: chebyshev-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Generating Polynomials"
|
||||
href: ./posts/math/chebyshev/index.qmd
|
||||
contents:
|
||||
- text: "Part 1: Regular Constructability"
|
||||
href: ./posts/math/chebyshev/1/index.qmd
|
||||
- text: "Part 2: Ghostly Chains"
|
||||
href: ./posts/math/chebyshev/2/index.qmd
|
||||
- text: "Extra: Legendary"
|
||||
href: ./posts/math/chebyshev/extra/index.qmd
|
||||
|
||||
- id: stereography-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Algebraic Stereography"
|
||||
href: ./posts/math/stereo/index.qmd
|
||||
contents:
|
||||
- ./posts/math/stereo/1/index.qmd
|
||||
- ./posts/math/stereo/2/index.qmd
|
||||
|
||||
- id: permutations-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "A Game of Permutations"
|
||||
href: ./posts/math/permutations/index.qmd
|
||||
contents:
|
||||
- text: "Part 1"
|
||||
href: ./posts/math/permutations/1/index.qmd
|
||||
- text: "Part 2"
|
||||
href: ./posts/math/permutations/2/index.qmd
|
||||
- text: "Part 3"
|
||||
href: ./posts/math/permutations/3/index.qmd
|
||||
- text: "Appendix"
|
||||
href: ./posts/math/permutations/appendix/index.qmd
|
||||
|
||||
- id: type-algebra-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Type Algebra and You"
|
||||
href: ./posts/math/type-algebra/index.qmd
|
||||
contents:
|
||||
- text: "Part 1: Basics"
|
||||
href: ./posts/math/type-algebra/1/index.qmd
|
||||
- text: "Part 2: A Fixer-upper"
|
||||
href: ./posts/math/type-algebra/2/index.qmd
|
||||
- text: "Part 3: Combinatorial Types"
|
||||
href: ./posts/math/type-algebra/3/index.qmd
|
||||
|
||||
- id: number-number-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Numbering Numbers"
|
||||
href: ./posts/math/number-number/index.qmd
|
||||
contents:
|
||||
- text: "From 0 to ∞"
|
||||
href: ./posts/math/number-number/1/index.qmd
|
||||
- text: "Ordering Obliquely"
|
||||
href: ./posts/math/number-number/2/index.qmd
|
||||
|
||||
- id: finite-field-sidebar
|
||||
style: "floating"
|
||||
contents:
|
||||
- section: "Exploring Finite Fields"
|
||||
href: ./posts/math/finite-field/index.qmd
|
||||
contents:
|
||||
- text: "Part 1: Preliminaries"
|
||||
href: ./posts/math/finite-field/1/index.qmd
|
||||
- text: "Part 2: Matrix Boogaloo"
|
||||
href: ./posts/math/finite-field/2/index.qmd
|
||||
contents:
|
||||
- text: "Appendix"
|
||||
href: ./posts/math/finite-field/2/extra/index.qmd
|
||||
- text: "Part 3: Roll a d20"
|
||||
href: ./posts/math/finite-field/2/index.qmd
|
||||
- text: "Part 5: The Power of Forgetting"
|
||||
href: ./posts/math/finite-field/2/index.qmd
|
||||
|
||||
format:
|
||||
html:
|
||||
theme:
|
||||
|
||||
63
about/index.qmd
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "About"
|
||||
---
|
||||
|
||||
This is my personal website (and the third iteration thereof).
|
||||
|
||||
The first version used Wordpress since it was quite easy to get into,
|
||||
didn't require much research, and web hosting services made it easy to set up.
|
||||
It lasted around three months near the end of 2020, after which I lost my posts because of
|
||||
hosting troubles and because I wasn't using proper backups.
|
||||
|
||||
The second version also used Wordpress, and lasted until the start of 2025
|
||||
(though the last post I had written up to that point was from the start of 2024).
|
||||
|
||||
This version uses [Quarto](https://quarto.org/), an open-source publishing platform that has
|
||||
some nice features like text-based configuration and Jupyter integration.
|
||||
As a bonus, it also produces static web pages.
|
||||
|
||||
|
||||
Why Quarto?
|
||||
-----------
|
||||
|
||||
I had a couple of reasons for switching platforms:
|
||||
|
||||
- Wordpress is either overkill or not enough.
|
||||
I don't need a block editor or multiple users, and I don't want to make custom content
|
||||
just for it to be specific to Wordpress.
|
||||
- I write a lot of code and LaTeX, which Wordpress relies on plugins for.
|
||||
Quarto uses (primarily) Pandoc-style Markdown, which allows for inlining of both out of the box.
|
||||
- Also, because of Jupyter integration, code cells can generate output for the page they're in.
|
||||
- Since pages are written in Markdown, everything can be edited locally and version-controlled in Git.
|
||||
|
||||
The last two are particularly nice in ensuring that the site is reproducibile,
|
||||
technically even without Quarto.
|
||||
Instead of articles that live in a Wordpress database or as scattered random files,
|
||||
I have the complete documents in a structure 1:1 with how the website is organized.
|
||||
|
||||
|
||||
Mathematics
|
||||
-----------
|
||||
|
||||
As of writing, all posts on this site are about math.
|
||||
In particular, they are dedicated to certain non-obvious insights I choose to investigate.
|
||||
Typically, although information about these subjects may exist online, it does not exist in a single,
|
||||
easily-accessible source.
|
||||
|
||||
I find writing math posts to be an excellent motivator when it comes to researching things.
|
||||
It also gives me a chance to learn new tools that otherwise I would not have a reason to use,
|
||||
not to mention being a good exercise in writing and diagram creation.
|
||||
|
||||
An example of this (and one that relates to the creation of the site) is when I was writing code
|
||||
for what would become the contents of [this post](/posts/polycount/5/).
|
||||
It was easy enough to learn a library for rendering images (or GIFs),
|
||||
but I didn't have a gallery to host them, nor a means to share the rationale which produced them.
|
||||
In a frenzy, I tried gathering my notes in a single text file before eventually putting them on a website.
|
||||
Along the way, I learned LaTeX to typeset the relevant equations.
|
||||
|
||||
I do my best to attribute the programs I use and direct sources I consult along the way,
|
||||
but extra information is frequently available on Wikipedia,
|
||||
which I may link to in order to give my explanation some grounding.
|
||||
|
||||
Unless otherwise stated, the figures and articles in this category are available under
|
||||
[CC BY-SA](https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
20
index.qmd
@ -1,13 +1,15 @@
|
||||
---
|
||||
title: "Posts by topic"
|
||||
title: "Posts"
|
||||
listing:
|
||||
contents:
|
||||
- posts/number-number/index.*
|
||||
- posts/permutations/index.*
|
||||
- posts/stereo/index.*
|
||||
- posts/chebyshev/index.*
|
||||
- posts/pentagons/index.*
|
||||
- posts/polycount/index.*
|
||||
- posts/misc/*/index.*
|
||||
sort: false
|
||||
- posts/math/polycount/*/index.*
|
||||
- posts/math/pentagons/*/index.*
|
||||
- posts/math/chebyshev/*/index.*
|
||||
- posts/math/stereo/*/index.*
|
||||
- posts/math/permutations/*/index.*
|
||||
- posts/math/type-algebra/*/index.*
|
||||
- posts/math/number-number/*/index.*
|
||||
- posts/math/finite-field/*/index.*
|
||||
- posts/math/misc/*/index.*
|
||||
sort: "date desc"
|
||||
---
|
||||
|
||||
@ -27,9 +27,10 @@ Each term of the series is weighted by a geometrically decreasing coefficient *c
|
||||
|
||||
$$
|
||||
[...d_2 d_1 d_0]_p \mapsto e^{2\pi i [d_0] / p}
|
||||
+ c e^{2\pi i [d_1 d_0] / p^2}
|
||||
+ c^2 e^{2\pi i [d_2 d_1 d_0] / p^2}
|
||||
+ ... \\
|
||||
+ c e^{2\pi i [d_1 d_0] / p^2}
|
||||
+ c^2 e^{2\pi i [d_2 d_1 d_0] / p^2}
|
||||
+ ...
|
||||
\\
|
||||
f_N(d; p) = \sum_{n = 0}^N c^n e^{2\pi i \cdot [d_{n:0}]_p / p^{n + 1}}
|
||||
$$
|
||||
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
# freeze computational output
|
||||
freeze: auto
|
||||
|
||||
# Enable banner style title blocks
|
||||
title-block-banner: true
|
||||
3
posts/math/_metadata.yml
Normal file
@ -0,0 +1,3 @@
|
||||
toc: true
|
||||
toc-location: right
|
||||
toc-title: " "
|
||||
@ -38,7 +38,7 @@ from sympy.abc import z
|
||||
```
|
||||
|
||||
|
||||
[Recently](/posts/misc/platonic-volume), I used coordinate-free geometry to derive
|
||||
[Recently](/posts/math/misc/platonic-volume), I used coordinate-free geometry to derive
|
||||
the volumes of the Platonic solids, a problem which was very accessible to the ancient Greeks.
|
||||
On the other hand, they found certain problems regarding which figures can be constructed via
|
||||
compass and straightedge to be very difficult. For example, they struggled with problems
|
||||
@ -742,16 +742,13 @@ My initial jumping off point for writing this article was completely different.
|
||||
However, in the process of writing, its share of the article shrank and shrank until its
|
||||
introduction was only vaguely related to what preceded it.
|
||||
But alas, the introduction via geometric constructions flows better coming off my
|
||||
[post about the Platonic solids](/posts/misc/platonic-volume).
|
||||
[post about the Platonic solids](/posts/math/misc/platonic-volume).
|
||||
Also, it reads better if I rely less on "if you search for this sequence of numbers"
|
||||
and more on how to interpret the definition.
|
||||
|
||||
Consider reading [the follow-up](../2) to this post if you're interested in another way
|
||||
one can obtain the Chebyshev polynomials.
|
||||
I have since rederived the Chebyshev polynomials without the complex exponential,
|
||||
which you can read about in [this post](/posts/math/stereo/2).
|
||||
|
||||
Diagrams created with GeoGebra.
|
||||
|
||||
<!--
|
||||
Update: I have since rederived the Chebyshev polynomials without the complex exponential,
|
||||
which you can read about in [this post]().
|
||||
-->
|
||||
4
posts/math/chebyshev/_metadata.yml
Normal file
@ -0,0 +1,4 @@
|
||||
# freeze computational output
|
||||
freeze: auto
|
||||
|
||||
sidebar: chebyshev-sidebar
|
||||
@ -3,6 +3,9 @@ title: "Chebyshev Polynomials"
|
||||
listing:
|
||||
contents: .
|
||||
sort: "date"
|
||||
|
||||
bread-crumbs: false
|
||||
sidebar:
|
||||
---
|
||||
|
||||
Articles about the generating Chebyshev polynomials (and other related families).
|
||||
813
posts/math/finite-field/1/index.qmd
Normal file
@ -0,0 +1,813 @@
|
||||
---
|
||||
title: "Exploring Finite Fields: Preliminaries"
|
||||
description: |
|
||||
How to do arithmetic when there is a finite number of numbers
|
||||
format:
|
||||
html:
|
||||
html-math-method: katex
|
||||
date: "2024-01-09"
|
||||
date-modified: "2025-07-16"
|
||||
categories:
|
||||
- algebra
|
||||
- finite field
|
||||
- haskell
|
||||
---
|
||||
|
||||
<style>
|
||||
.cayley-table .header,
|
||||
.cayley-table td:nth-child(1),
|
||||
.cayley-table th:nth-child(1) {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
```{haskell}
|
||||
--|echo: false
|
||||
|
||||
import Data.List (intercalate)
|
||||
import Data.Profunctor (rmap)
|
||||
|
||||
import Colonnade
|
||||
import qualified Colonnade.Encode as CE
|
||||
import IHaskell.Display (markdown)
|
||||
|
||||
markdownTable col rows = unlines $ h:h':r where
|
||||
toColumns = ("| " ++) . (++ " |") . intercalate " | " . foldr (:) []
|
||||
h = toColumns $ CE.header id col
|
||||
h' = [if x == '|' then x else '-' | x <- h]
|
||||
r = map (toColumns . CE.row id col) rows
|
||||
```
|
||||
|
||||
|
||||
[Fields](https://en.wikipedia.org/wiki/Field_%28mathematics%29) are a basic structure in abstract algebra.
|
||||
Roughly, a field is a collection of elements paired with two operations,
|
||||
addition and multiplication, along with particular rules about their interactions.
|
||||
The most important elements of a field are 0 (the additive identity),
|
||||
1 (the multiplicative identity), and -1 (which forms additive inverses).
|
||||
Moreover, multiplicative inverses must also exist for everything but 0.
|
||||
|
||||
Certain fields are widely-known, such as the rational numbers $\mathbb{Q}$
|
||||
and complex numbers $\mathbb{C}$.
|
||||
Finite fields also exist, such as the field with two elements.
|
||||
This field only contains the elements 0 and 1[^1].
|
||||
The addition and multiplication tables are consequently the simplest possible according to familiar rules.
|
||||
|
||||
[^1]: The elements -1 and 1 are identical, unlike in most fields.
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {.cayley-table}
|
||||
| + | 0 | 1 |
|
||||
|---|---|---|
|
||||
| 0 | 0 | 1 |
|
||||
| 1 | 1 | 0 |
|
||||
:::
|
||||
|
||||
::: {.cayley-table}
|
||||
| × | 0 | 1 |
|
||||
|---|---|---|
|
||||
| 0 | 0 | 0 |
|
||||
| 1 | 0 | 1 |
|
||||
:::
|
||||
::::
|
||||
|
||||
This field expresses the parity of sums and products of two integers since,
|
||||
replacing "0" with "even" and "1" with odd:
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {}
|
||||
- even + even = even
|
||||
- even + odd = odd
|
||||
- odd + even = odd
|
||||
- odd + odd = even
|
||||
:::
|
||||
|
||||
::: {}
|
||||
- even × even = even
|
||||
- even × odd = even
|
||||
- odd × even = even
|
||||
- odd × odd = odd
|
||||
:::
|
||||
::::
|
||||
|
||||
|
||||
In One's Prime
|
||||
--------------
|
||||
|
||||
Two is not unique as the only possible size for a finite field -- all prime numbers are also candidates.
|
||||
Such fields are referred to as GF(*p*) or $\mathbb{F}_p$,
|
||||
where the prime *p* is the *order* of the field.
|
||||
|
||||
These fields inherit the properties of integer arithmetic.
|
||||
Addition is cyclic mod *p*: 0 and *p* are equivalent to one another.
|
||||
The role of -1 taken up by *p* - 1, and the additive inverse of an element *x* can be viewed in two ways:
|
||||
|
||||
- Multiplying -1 in the field with x, (i.e., $(p - 1)x \mod p$)
|
||||
- Counting backwards from zero (i.e., $p - x$)
|
||||
|
||||
For this to be a field, the product of any two elements in the field cannot be a multiple of *p*,
|
||||
since that would be congruent to 0.
|
||||
If there were such a product, then *p* would share factors (other than 1) with one of the two terms.
|
||||
But the order is prime, so this is impossible.
|
||||
More strongly, multiplicative inverses [can be found algorithmically](
|
||||
https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Extended_Euclidean_algorithm
|
||||
), although it is a somewhat tricky task.
|
||||
|
||||
|
||||
Polynomials
|
||||
-----------
|
||||
|
||||
For a given field *K*, we can also consider polynomials with coefficients
|
||||
from elements in the field, K\[*x*\].
|
||||
The mathematical structure that polynomials belong to is called a
|
||||
[*ring*](https://en.wikipedia.org/wiki/Ring_%28mathematics%29).
|
||||
Rings are slightly weaker than fields in the sense that there are not generally multiplicative inverses.
|
||||
Additive and multiplicative identities must still exist however, corresponding to
|
||||
the zero polynomial and constant polynomial 1, respectively.
|
||||
|
||||
Since GF(*p*) has a finite number of elements to consider, there are only so many choices
|
||||
for polynomial coefficients for a given degree.
|
||||
Contrast this with for polynomials over the integers, where there are infinitely many monomials $mx + n$.
|
||||
Looking at polynomials over GF(2), we have:
|
||||
|
||||
| Degree | Polynomial *q(x)* | List of coefficients of *q(x)* (ascending) | *q*(2) | *q*(2) (Binary) |
|
||||
|--------|-------------------|--------------------------------------------|--------|-----------------|
|
||||
| 1 | $x$ | [0, 1] | 2 | 10 |
|
||||
| 1 | $1 + x$ | [1, 1] | 3 | 11 |
|
||||
| 2 | $x^2$ | [0, 0, 1] | 4 | 100 |
|
||||
| 2 | $1 + x^2$ | [1, 0, 1] | 5 | 101 |
|
||||
| 2 | $x + x^2$ | [0, 1, 1] | 6 | 110 |
|
||||
| 2 | $1 + x + x^2$ | [1, 1, 1] | 7 | 111 |
|
||||
| 3 | $x^3$ | [0, 0, 0, 1] | 8 | 1000 |
|
||||
| 3 | $1 + x^3$ | [1, 0, 0, 1] | 9 | 1001 |
|
||||
| 3 | $x + x^3$ | [0, 1, 0, 1] | 10 | 1010 |
|
||||
| 3 | $1 + x + x^3$ | [1, 1, 0, 1] | 11 | 1011 |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
|
||||
### The Base-ics
|
||||
|
||||
There is a very close correspondence between binary expansions and polynomials over GF(2).
|
||||
This is evident by comparing the list of coefficients in the polynomial (column 3)
|
||||
with the binary expansions of the polynomial evaluated at 2 (column 5).
|
||||
This gives a handy way of referring to polynomials (mod *p*) without having to write out
|
||||
each individual "×" or "+".
|
||||
In fact, this is commonly used to compactly compute with (and refer to)
|
||||
[polynomials used in cyclic redundancy checks](
|
||||
https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks
|
||||
).
|
||||
|
||||
Again, 2 is not unique among primes.
|
||||
Polynomials over any prime field GF(*p*) can be expressed as integers in base *p*.
|
||||
To make the correspondence more explicit, I'll use the $_p m$ to denote a decimal integer *m*
|
||||
whose base *p* expansion should be interpreted as a polynomial (mod *p*).
|
||||
*m* is also the value of the polynomial at *p*.
|
||||
For example, $_2 11 = 1 + x + x^3$, and 1 + 2 + 2^3^ = 11.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of duality between polynomials mod *p* and base *p* expansions of integers
|
||||
</summary>
|
||||
<br>
|
||||
This implementation actually works for any base *b*, which is not necessarily prime.
|
||||
The only difference is that the coefficients lose "field-ness" for composite *b*.
|
||||
|
||||
```{haskell}
|
||||
import Data.List (unfoldr)
|
||||
import Data.Tuple (swap)
|
||||
|
||||
-- A polynomial is its ascending list of coefficients (of type a)
|
||||
newtype Polynomial a = Poly { coeffs :: [a] } deriving Functor
|
||||
|
||||
-- Interpret a number's base-b expansion as a polynomial
|
||||
asPoly :: Int -> Int -> Polynomial Int
|
||||
-- Build a list with f, which returns either Nothing
|
||||
-- or Just (next element of list, next argument to f)
|
||||
asPoly b = Poly . unfoldr f where
|
||||
-- Divide x by b. Emit the remainder and recurse with the quotient.
|
||||
f x | x /= 0 = Just $ swap $ divMod x b
|
||||
-- If there's nothing left to divide out, terminate
|
||||
| otherwise = Nothing
|
||||
|
||||
-- Horner evaluation of a polynomial at the integer b
|
||||
evalPoly :: Int -> Polynomial Int -> Int
|
||||
-- Start with the highest coefficient
|
||||
-- Multiply by b at each step and add the coefficient of the next term
|
||||
evalPoly b (Poly p) = foldr (\y acc -> acc*b + y) 0 p
|
||||
|
||||
-- evalPoly n . asPoly n = id :: Int -> Int
|
||||
```
|
||||
|
||||
An interesting detail here is that the duality is expressed through `foldr` using multiplication
|
||||
and addition and `unfoldr` using divMod.
|
||||
</details>
|
||||
|
||||
|
||||
### Mono, not Stereo
|
||||
|
||||
With respect to their roots (which will soon become of primary interest), polynomials are *projective*.
|
||||
That is, any scalar multiple of the polynomial has the same roots.
|
||||
For GF(2), this is insignificant since 1 is the only nonzero element, but over GF(5),
|
||||
the following polynomials have the same roots:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
x^2 + 2 \text{ over GF$(5)$} \quad
|
||||
&\longleftrightarrow \quad {_5 27}
|
||||
\\
|
||||
2x^2 + 4 \text{ over GF$(5)$} \quad
|
||||
&\longleftrightarrow \quad {_5 54}
|
||||
\\
|
||||
3x^2 + 1 \text{ over GF$(5)$} \quad
|
||||
&\longleftrightarrow \quad {_5 76}
|
||||
\\
|
||||
4x^2 + 3 \text{ over GF$(5)$} \quad
|
||||
&\longleftrightarrow \quad {_5 103}
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
Only the first polynomial is a *monic* polynomial, or has a leading coefficient of 1.
|
||||
It is preferable to work with these since the product of two monic polynomials is also monic.
|
||||
|
||||
An equivalent condition to this is that when evaluated with the order of the field,
|
||||
the polynomial has a value between 5^2^ and 2×5^2^ - 1 = 49.
|
||||
In general, monic polynomials over GF(*p*) as integers fall in the range *p*^2^ and 2*p*^2^ - 1.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of monic polynomials over GF(*p*)
|
||||
</summary>
|
||||
|
||||
Again, nothing about this definition depends on the base being prime.
|
||||
|
||||
```{haskell}
|
||||
-- All monic polynomials of degree d with coefficients mod n
|
||||
monics :: Int -> Int -> [Polynomial Int]
|
||||
monics n d = map (asPoly n) [n^d..2*(n^d) - 1]
|
||||
|
||||
-- All monic polynomials with coefficients mod n, ordered by degree
|
||||
allMonics :: Int -> [Polynomial Int]
|
||||
allMonics n = concat [monics n d | d <- [1..]]
|
||||
```
|
||||
</details>
|
||||
|
||||
As an aside, one can also read out monics by counting normally by using the digit alphabet
|
||||
{1, 0, -1, ..., -*p* + 2}.
|
||||
Unfortunately, these base-*p* expansions are more difficult to obtain algorithmically,
|
||||
and I'll leave this as an exercise to the reader.
|
||||
|
||||
|
||||
### Sieving out Irreducibles
|
||||
|
||||
Over the integers, we can factor a number into primes.
|
||||
To decide if a number is prime, we just divide it (using an algorithm like long division)
|
||||
by numbers less than it and see if we get a nonzero remainder.
|
||||
|
||||
Similarly, we can factor polynomials into *irreducible* polynomials,
|
||||
which have no "smaller" polynomial factors other than 1.
|
||||
More precisely, by "smaller", we mean those of lesser degree.
|
||||
For example, over the integers, the polynomial $x^2 - 1$ (degree 2) factors into
|
||||
$(x + 1)(x - 1)$ (both degree 1), but $x^2 + 1$ is irreducible.
|
||||
|
||||
In general, a factorization of a polynomial over the integers implies a factorization of one over GF(*p*),
|
||||
since the coefficients for each factor may be taken mod *p*.
|
||||
However, the converse does not hold.
|
||||
Over GF(2),
|
||||
|
||||
$$
|
||||
(x + 1)^2 = x^2 + 2x + 1 \equiv x^2 + 1 \text{ over GF$(2)$}
|
||||
$$
|
||||
|
||||
...but as just mentioned, the right-hand side is irreducible over the integers.
|
||||
|
||||
Just like integers, we can use [polynomial long division](
|
||||
https://en.wikipedia.org/wiki/Polynomial_long_division
|
||||
) with these objects to decide if a polynomial is irreducible.
|
||||
[Synthetic division](https://en.wikipedia.org/wiki/Synthetic_division) is an alternative which is
|
||||
slightly easier to implement (especially over GF(2), where it is, again, used in CRCs).
|
||||
It only works for monic polynomials, but this is all we need.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of synthetic division
|
||||
</summary>
|
||||
|
||||
The algorithm is similar to table-less algorithms for CRCs, but we don't have the luxury
|
||||
of working at the bit level with XOR for addition.
|
||||
We also have to watch out for negation and coefficients other than 1 for when not working over GF(2).
|
||||
|
||||
```{haskell}
|
||||
zipAdd :: Num a => [a] -> [a] -> [a]
|
||||
zipAdd [] ds = ds
|
||||
zipAdd cs [] = cs
|
||||
zipAdd (c:cs) (d:ds) = (c + d):zipAdd cs ds
|
||||
|
||||
-- Divide the polynomial ps by qs (coefficients in descending degree order)
|
||||
synthDiv' :: (Eq a, Num a) => [a] -> [a] -> ([a], [a])
|
||||
synthDiv' ps qs
|
||||
| head qs /= 1 = error "Cannot divide by non-monic polynomial"
|
||||
| otherwise = splitAt deg $ doDiv ps deg
|
||||
where
|
||||
-- Negate the denominator and ignore leading term
|
||||
qNeg = map negate $ tail qs
|
||||
-- The degree of the result, based on the degrees of the numerator and denominator
|
||||
deg = max 0 (length ps - length qs + 1)
|
||||
-- Pluck off the head of the list and add a shifted and scaled version of
|
||||
-- qs to the tail of the list. Repeat this d times
|
||||
doDiv xs 0 = xs
|
||||
doDiv (x:xs) d = x:doDiv (zipAdd xs $ map (*x) qNeg) (d - 1)
|
||||
|
||||
-- Use Polynomial (coefficients in ascending degree order) instead of lists
|
||||
synthDiv :: (Eq a, Num a) => Polynomial a -> Polynomial a
|
||||
-> (Polynomial a, Polynomial a)
|
||||
synthDiv (Poly p) (Poly q) = (Poly $ reverse quot, Poly $ reverse rem)
|
||||
where (quot, rem) = synthDiv' (reverse p) (reverse q)
|
||||
```
|
||||
</details>
|
||||
|
||||
Then, using our list of monic polynomials, we can use the same strategy
|
||||
for sieving out primes to find (monic) irreducibles.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of an irreducible polynomial sieve over GF(*p*)
|
||||
</summary>
|
||||
|
||||
```{haskell}
|
||||
-- All irreducible monic polynomials with coefficients mod n
|
||||
irreducibles :: Int -> [Polynomial Int]
|
||||
irreducibles n = go [] $ allMonics n where
|
||||
-- Divide the polynomial x by i, then take the remainder mod n
|
||||
remModN x i = fmap (`mod` n) $ snd $ synthDiv x i
|
||||
-- Find remainders of x divided by every irreducible in "is".
|
||||
-- If any give the zero polynomial, then x is a multiple of an irreducible
|
||||
notMultiple x is = and [not $ all (==0) $ coeffs $ remModN x i | i <- is]
|
||||
-- Sieve out by notMultiple
|
||||
go is (x:xs)
|
||||
| notMultiple x is = x:go (x:is) xs
|
||||
| otherwise = go is xs
|
||||
```
|
||||
</details>
|
||||
|
||||
Since we can denote polynomials by numbers, it may be tempting to freely switch
|
||||
between primes and irreducibles.
|
||||
However, irreducibles depend on the chosen field and do not generally correspond
|
||||
to the base-*p* expansion of a prime.
|
||||
|
||||
```{haskell}
|
||||
--|code-fold: true
|
||||
--|classes: plain
|
||||
|
||||
texifyPoly :: (Num a, Eq a, Show a) => Polynomial a -> String
|
||||
texifyPoly (Poly xs) = ("$" ++) $ (++ "$") $ texify' $ zip xs [0..] where
|
||||
texify' [] = "0"
|
||||
texify' ((c, n):xs)
|
||||
| all ((==0) . fst) xs = showPow c n
|
||||
| c == 0 = texify' xs
|
||||
| otherwise = showPow c n ++ " + " ++ texify' xs
|
||||
showPow c 0 = show c
|
||||
showPow 1 1 = "x"
|
||||
showPow c 1 = show c ++ showPow 1 1
|
||||
showPow 1 n = "x^{" ++ show n ++ "}"
|
||||
showPow c n = show c ++ showPow 1 n
|
||||
|
||||
-- Simple prime sieve
|
||||
primes = primes' [] 2 where
|
||||
primes' ps n
|
||||
| and [n `mod` p /= 0 | p <- ps] = n:primes' (n:ps) (n+1)
|
||||
| otherwise = primes' ps (n+1)
|
||||
|
||||
somePrimes = takeWhile (<50) primes
|
||||
someIrr2 = takeWhile (<50) $ map (evalPoly 2) $ irreducibles 2
|
||||
|
||||
irr2PrimeTable = columns (\(_, f) r -> f r) (\(c, _) -> Headed c) [
|
||||
("Irreducible over GF(2), *q*(x)", texifyPoly . (irreducibles 2 !!)),
|
||||
("*q*(2), [OEIS A014580](https://oeis.org/A014580)",
|
||||
(\x -> if x `notElem` somePrimes then redSpan $ show x else show x) .
|
||||
(someIrr2 !!)),
|
||||
("Prime",
|
||||
(\x -> if x `notElem` someIrr2 then greenSpan $ show x else show x) .
|
||||
(primes !!))
|
||||
] where
|
||||
greenSpan x = "<span style=\"color: green\">" ++ x ++ "</span>"
|
||||
redSpan x = "<span style=\"color: red\">" ++ x ++ "</span>"
|
||||
|
||||
markdown $ markdownTable irr2PrimeTable [0..10]
|
||||
```
|
||||
|
||||
The red entry in column 2 is not prime.
|
||||
Dually, the green entries in column 3 do not have binary expansions which correspond
|
||||
to irreducible polynomials over GF(2).
|
||||
|
||||
|
||||
Matrices
|
||||
--------
|
||||
|
||||
Along with polynomials over a finite field, we can also look at matrices.
|
||||
The most interesting matrices are square ones, since the product of two square matrices
|
||||
is another square matrix.
|
||||
With the zero matrix ($\bf 0_n$) as the additive identity and the identity matrix
|
||||
($\bf 1_n$) as the multiplicative, square matrices also form a ring over the field *K*,
|
||||
denoted $K^{n \times n}$.
|
||||
|
||||
Square matrices are associated to a [determinant](https://en.wikipedia.org/wiki/Determinant),
|
||||
which is an element from the underlying field.
|
||||
Determinants are nice, since the determinant of the product of two matrices is
|
||||
the product of the determinants.
|
||||
The determinant can be implemented using [Laplace expansion](
|
||||
https://en.wikipedia.org/wiki/Laplace_expansion
|
||||
), which is also useful for inductive proofs.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of Laplace expansion
|
||||
</summary>
|
||||
|
||||
Laplace expansion is ludicrously inefficient compared to other algorithms,
|
||||
and is only shown here due to its "straightforward" implementation and use in proof.
|
||||
Numeric computation will not be used to keep the arithmetic exact.
|
||||
|
||||
```{haskell}
|
||||
import Data.Array
|
||||
newtype Matrix a = Mat { unMat :: Array (Int, Int) a } deriving Functor
|
||||
|
||||
-- Simple function for building a Matrix from lists
|
||||
toMatrix :: [[a]] -> Matrix a
|
||||
toMatrix l = Mat $ listArray ((0,0),(n-1,m-1)) $ concat l where
|
||||
m = length $ head l
|
||||
n = length l
|
||||
|
||||
determinant :: (Num a, Eq a) => Matrix a -> a
|
||||
determinant (Mat xs) = determinant' xs where
|
||||
-- Evaluate (-1)^i without repeated multiplication
|
||||
parity i = if even i then 1 else -1
|
||||
-- Map old array addresses to new ones when eliminating row 0, column i
|
||||
rowMap i (x,y) = (x+1, if y >= i then y+1 else y)
|
||||
-- Recursive determinant Array
|
||||
determinant' xs
|
||||
-- Base case: 1x1 matrix
|
||||
| n == 0 = xs!(0,0)
|
||||
-- Sum of cofactor expansions
|
||||
| otherwise = sum $ map cofactor [0..n] where
|
||||
-- Produce the cofactor of row 0, column i
|
||||
cofactor i
|
||||
| xs!(0,i) == 0 = 0
|
||||
| otherwise = (parity i) * xs!(0,i) * determinant' (minor i)
|
||||
-- Furthest extent of the bounds, i.e., the size of the matrix
|
||||
(_,(n,_)) = bounds xs
|
||||
-- Build a new Array by eliminating row 0 and column i
|
||||
minor i = ixmap ((0,0),(n-1,n-1)) (rowMap i) xs
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### Back to Polynomials
|
||||
|
||||
The [characteristic polynomial](https://en.wikipedia.org/wiki/Characteristic_polynomial)
|
||||
is a stronger invariant which follows from the determinant.
|
||||
It is defined as, for *λ* a scalar variable:
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
\text{charpoly}(A) = p_A(\lambda)
|
||||
= \left| \lambda I - A \right|
|
||||
\\ \\
|
||||
= \left| \begin{matrix*}
|
||||
\lambda - a_{00} & -a_{01} & ... & -a_{0n} \\
|
||||
-a_{10} & \lambda - a_{11} & ... & -a_{1n} \\
|
||||
\vdots & \vdots & \ddots & \vdots \\
|
||||
-a_{n0} & -a_{n1} & ... & \lambda - a_{nn} \\
|
||||
\end{matrix*} \right|
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
Laplace expansion never gives *λ* a coefficient before recursing,
|
||||
so the characteristic polynomial is always monic.
|
||||
Also, the characteristic polynomial is over the same field that the entries of the matrix were from.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of the characteristic polynomial
|
||||
</summary>
|
||||
Since `determinant` was defined for all `Num` and `Eq`, it can immediately be applied
|
||||
if these instances are defined for polynomials.
|
||||
|
||||
```{haskell}
|
||||
instance (Eq a, Num a) => (Num (Polynomial a)) where
|
||||
(+) x@(Poly as) y@(Poly bs) = Poly $ zipAdd as bs
|
||||
|
||||
--convolution
|
||||
(*) (Poly []) (Poly bs) = Poly []
|
||||
(*) (Poly as) (Poly []) = Poly []
|
||||
(*) (Poly as) (Poly bs) = Poly $ zeroize $ finish $ foldl convolve' ([], []) as
|
||||
where convolve' (xs, ys) a = (a:xs, sum (zipWith (*) (a:xs) bs):ys)
|
||||
finish (xs, ys) = (reverse ys ++) $ finish' xs $ tail bs
|
||||
finish' xs [] = []
|
||||
finish' xs ys = sum (zipWith (*) xs ys):finish' xs (tail ys)
|
||||
zeroize xs = if all (==0) xs then [] else xs
|
||||
|
||||
-- this definition works
|
||||
negate = fmap negate
|
||||
-- but these two might run into some problems
|
||||
abs = fmap abs
|
||||
signum = fmap signum
|
||||
fromInteger 0 = Poly []
|
||||
fromInteger a = Poly [fromInteger a]
|
||||
|
||||
instance Eq a => Eq (Polynomial a) where
|
||||
(==) (Poly xs) (Poly ys) = xs == ys
|
||||
```
|
||||
|
||||
Then, along with some helper matrix functions, we can build a function for the characteristic polynomial.
|
||||
|
||||
```{haskell}
|
||||
-- Zero matrix
|
||||
zero :: Num a => Int -> Matrix a
|
||||
zero n = Mat $ listArray ((0,0),(n-1,n-1)) $ repeat 0
|
||||
|
||||
-- Identity matrix
|
||||
eye :: Num a => Int -> Matrix a
|
||||
eye n = Mat $ unMat (zero n) // take n [((i,i), 1) | i <- [0..]]
|
||||
|
||||
-- Maps
|
||||
mapRange :: Ix i => (i -> e) -> (i, i) -> [(i, e)]
|
||||
mapRange g r = map (\x -> (x, g x)) $ range r
|
||||
|
||||
-- Pointwise application
|
||||
zipWithArr :: Ix i => (a -> b -> c)
|
||||
-> Array i a -> Array i b -> Array i c
|
||||
zipWithArr f a b
|
||||
| ab == bb = array ab $ map (\x -> (x, f (a!x) (b!x))) $ indices a
|
||||
| otherwise = error "Array dimension mismatch" where
|
||||
ab = bounds a
|
||||
bb = bounds b
|
||||
|
||||
(|+|) (Mat x) (Mat y) = Mat $ zipWithArr (+) x y
|
||||
|
||||
-- Characteristic Polynomial
|
||||
charpoly :: Matrix Int -> Polynomial Int
|
||||
charpoly xs = determinant $ eyeLambda |+| negPolyXs where
|
||||
-- Furthest extent of the bounds, i.e., the size of the matrix
|
||||
(_,(n,_)) = bounds $ unMat xs
|
||||
-- Negative of input matrix, after being converted to polynomials
|
||||
negPolyXs :: Matrix (Polynomial Int)
|
||||
negPolyXs = fmap (\x -> Poly [-x]) xs
|
||||
-- Identity matrix times lambda (encoded as Poly [0, 1])
|
||||
eyeLambda :: Matrix (Polynomial Int)
|
||||
eyeLambda = (\x -> Poly [x] * Poly [0, 1]) <$> eye (n+1)
|
||||
|
||||
markdown $ texifyPoly $ charpoly $ toMatrix [[1,0],[0,1]]
|
||||
```
|
||||
</details>
|
||||
|
||||
Computation using this definition is only good for demonstrative purposes.
|
||||
The [Faddeev-LeVerrier algorithm](
|
||||
https://en.wikipedia.org/wiki/Faddeev%E2%80%93LeVerrier_algorithm
|
||||
) circumvents Laplace expansion entirely and happens to generate the determinant along the way.
|
||||
However, it has some problems:
|
||||
|
||||
- It inverts the order in which the determinant and characteristic polynomial are defined
|
||||
- It introduces division, which makes it unsuitable for direct use with matrices with entries mod *p*
|
||||
|
||||
Fortunately, we can just work with a matrix over the integers and mod out at the end instead,
|
||||
as the following diagram conveys:
|
||||
|
||||
$$
|
||||
\begin{array}{}
|
||||
\mathbb{F}_p ^{n \times n} & \textcolor{green}{\hookrightarrow} &
|
||||
\normalsize \mathbb{Z}^{n \times n} &
|
||||
\overset{\mod p ~~}{\longrightarrow} &
|
||||
\mathbb{F}_p^{n \times n} & \scriptsize \phantom{text{charpoly}}
|
||||
\\[10pt]
|
||||
& \scriptsize \textcolor{green}{\text{charpoly (FL)}} &
|
||||
\textcolor{green}{\downarrow} & &
|
||||
\textcolor{red}{\downarrow} & \scriptsize \textcolor{red}{\text{charpoly (LE)}}
|
||||
\\[12pt]
|
||||
& & \mathbb{Z}[\lambda] &
|
||||
\textcolor{green}{\underset{\mod p ~~}{\longrightarrow}} &
|
||||
\mathbb{F}_p[\lambda] & \scriptsize \phantom{text{charpoly}}
|
||||
\end{array}
|
||||
$$
|
||||
|
||||
The top row are matrices and the bottom row are polynomials.
|
||||
To get to the bottom-right, which contains the characteristic polynomial over GF(*p*),
|
||||
we can avoid the red arrow and follow the path in green instead.
|
||||
|
||||
|
||||
### Friends Among Matrices
|
||||
|
||||
In the reverse direction, a matrix with a specific characteristic polynomial
|
||||
can be constructed from a polynomal.
|
||||
The matrix is called the [companion matrix](https://en.wikipedia.org/wiki/Companion_matrix),
|
||||
and is defined as
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
p(\lambda) = \lambda^n + p_{n-1}\lambda^{n-1} + ... + p_1 \lambda + p_0
|
||||
\\[10pt]
|
||||
C_{p(\lambda)} = \left( \begin{matrix}
|
||||
0 & 1 & 0 & ... & 0 \\
|
||||
0 & 0 & 1 & ... & 0 \\
|
||||
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
||||
0 & 0 & 0 & ... & 1 \\
|
||||
-p_0 & -p_1 & -p_2 & ... & -p_{n-1}
|
||||
\end{matrix} \right)
|
||||
= \left( \begin{matrix}
|
||||
\overrightharpoon 0_{n-1} & \bold{1}_{n-1} \\
|
||||
-p_0 & -(\overrightharpoon{p}_{1:n-1})^T
|
||||
\end{matrix} \right)
|
||||
\\ \\[1pt]
|
||||
\text{charpoly}(C_{p}) = p_{C_{p}}(\lambda) = p(\lambda)
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
The definition of the companion matrix only depends on elements having an additive inverse,
|
||||
which is always true in a field.
|
||||
Therefore, there are always matrices over a field that have a monic polynomial
|
||||
as their characteristic polynomial.
|
||||
|
||||
Proving that the companion matrix has the characteristic polynomial it was constructed from
|
||||
can be done via Laplace expansion:
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
p_{0:n-1}(\lambda)
|
||||
= \left| \begin{matrix}
|
||||
\textcolor{red}{\lambda} & -1 & 0 & ... & 0 \\
|
||||
0 & \lambda & -1 & ... & 0 \\
|
||||
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
||||
0 & 0 & 0 & ... & -1 \\
|
||||
\textcolor{green}{p_0} & p_1 & p_2 & ... & \lambda + p_{n-1}
|
||||
\end{matrix} \right|
|
||||
\\ \\[1pt]
|
||||
= \textcolor{green}{p_0} \cdot (-1)^{n-1}
|
||||
\left| \begin{matrix}
|
||||
-1 & 0 & ... & 0 \\
|
||||
\lambda & -1 & ... & 0 \\
|
||||
\vdots & \vdots & \ddots & \vdots \\
|
||||
0 & 0 & ... & -1
|
||||
\end{matrix} \right|
|
||||
+ \textcolor{red}{\lambda}
|
||||
\left| \begin{matrix}
|
||||
\lambda & -1 & ... & 0 \\
|
||||
\vdots & \vdots & \ddots & \vdots \\
|
||||
0 & 0 & ... & -1 \\
|
||||
p_1 & p_2 & ... & \lambda + p_{n-1}
|
||||
\end{matrix} \right|
|
||||
\\ \\[1pt]
|
||||
= \textcolor{green}{p_0} \cdot (-1)^{n-1} \cdot (-1)^{n-1}
|
||||
+ \textcolor{red}{\lambda} \cdot p_{1:n-1}(\lambda)
|
||||
\\
|
||||
= p_0 + \lambda(p_1 + \lambda ( \cdots (p_{n-1} + \lambda ) \cdots )))
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
Pleasantly, this yields the Horner form, which was used above to evaluate polynomials.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of the companion matrix
|
||||
</summary>
|
||||
|
||||
```{haskell}
|
||||
companion :: Polynomial Int -> Matrix Int
|
||||
companion (Poly ps)
|
||||
| last ps' /= 1 = error "Cannot find companion matrix of non-monic polynomial"
|
||||
| otherwise = Mat $ array ((0,0), (n-1,n-1)) $ lastRow ++ shiftI where
|
||||
-- The degree of the polynomial, as well as the size of the matrix
|
||||
n = length ps' - 1
|
||||
-- Remove trailing 0s from ps
|
||||
ps' = reverse $ dropWhile (==0) $ reverse ps
|
||||
-- Address/value tuples for a shifted identity matrix:
|
||||
-- 1s on the diagonal just above the main diagonal, 0s elsewhere
|
||||
shiftI = map (\p@(x,y) -> (p, if y == x + 1 then 1 else 0)) $
|
||||
range ((0,0),(n-2,n-1))
|
||||
-- Address/value tuples for the last row of the companion matrix:
|
||||
-- ascending powers of the polynomial
|
||||
lastRow = zipWith (\x y -> ((n-1, x), y)) [0..n-1] $ map negate ps'
|
||||
|
||||
-- (charpoly . companion) = id :: Polynomial Int -> Polynomial Int
|
||||
```
|
||||
|
||||
Applying this to $1 - 2x + x^2$ gives us:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
reshape :: Int -> [a] -> [[a]]
|
||||
reshape n = unfoldr (reshape' n) where
|
||||
reshape' n x = if null x then Nothing else Just $ splitAt n x
|
||||
|
||||
fromMatrix :: Matrix a -> [[a]]
|
||||
fromMatrix (Mat m) = let (_,(_,n)) = bounds m in reshape (n+1) $ elems m
|
||||
|
||||
texifyMatrix mat = surround mat' where
|
||||
mat' = intercalate " \\\\ " $ map (intercalate " & " . map show) $
|
||||
fromMatrix mat
|
||||
surround = ("\\left( \\begin{matrix}" ++) . (++ "\\end{matrix} \\right)")
|
||||
|
||||
markdown $ ("$$" ++) $ (++ "$$") $ texifyMatrix $ companion $ Poly [1, -2, 1]
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
Field Extensions
|
||||
----------------
|
||||
|
||||
Aside from those of degree 1, the irreducible polynomials over a field cannot be factored
|
||||
into monomials over the field.
|
||||
In other words, irreducibles have roots which do not exist as elements of the field.
|
||||
A *field extension* formalizes the notion by which one can make a larger field from another
|
||||
by adding roots of a polynomial.
|
||||
|
||||
$x^2 + 1$ is irreducible, both over the integers and over an actual field like $\mathbb{R}$.
|
||||
On the other hand, it can be factored into $(x + i)(x - i)$ over $\mathbb{C}$.
|
||||
We can construct the latter field from the former if an extra number *i* exists alongside everything
|
||||
in $\mathbb{R}$ such that *i*^2^ = -1.
|
||||
Elements of this new field are linear combinations of multiples of powers of *i*
|
||||
less than the degree (in this case, 0 and 1; i.e., $a + bi$).
|
||||
|
||||
The equation that *i* obeys can be rewritten as $i^2 + 1 = 0$, which is the original polynomial,
|
||||
evaluated at *i*.
|
||||
In order to refer explicitly to the construction of the bigger field from the polynomial,
|
||||
we write[^2] $\mathbb{R}[x] / (x^2 + 1) \cong \mathbb{C}$.
|
||||
|
||||
[^2]: *Technically*, the left hand side refers to something else
|
||||
(cosets of polynomials, from which we extract the canonical member *i*), but this description is good enough.
|
||||
|
||||
|
||||
### The Power of Primes
|
||||
|
||||
We can extend a finite field in the same way.
|
||||
Over GF(2), the smallest irreducible of degree 2 is $x^2 + x + 1$.
|
||||
Using the same logic as before, we construct
|
||||
$\mathbb{F}_2[x] / (x^2 + x + 1) \cong \mathbb{F}_2[\alpha]$.
|
||||
The new element *α* is a root of the polynomial and obeys the relations:
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
\alpha^2 + \alpha + 1 = 0
|
||||
\\
|
||||
\alpha^2 = -\alpha - 1 \equiv \alpha + 1 \mod 2
|
||||
\\
|
||||
\alpha^3 = \alpha^2 + \alpha = (\alpha + 1) + \alpha \equiv 1 \mod 2
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
Just like *i*, only powers of *α* less than 2 (again, 0 and 1) are necessary
|
||||
to express elements of the field.
|
||||
Skipping a few steps, we can accumulate all possible sums and products over this new field
|
||||
into two new tables:
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {.cayley-table}
|
||||
| + | 0 | 1 | *α* | *α* + 1 |
|
||||
|---------|---------|---------|---------|---------|
|
||||
| 0 | 0 | 1 | *α* | *α* + 1 |
|
||||
| 1 | 1 | 0 | *α* + 1 | *α* |
|
||||
| *α* | *α* | *α* + 1 | 0 | 1 |
|
||||
| *α* + 1 | *α* + 1 | *α* | 1 | 0 |
|
||||
:::
|
||||
|
||||
::: {.cayley-table}
|
||||
| × | 0 | 1 | *α* | *α* + 1 |
|
||||
|---------|---------|---------|---------|---------|
|
||||
| 0 | 0 | 0 | 0 | 0 |
|
||||
| 1 | 0 | 1 | *α* | *α* + 1 |
|
||||
| *α* | 0 | *α* | *α* + 1 | 1 |
|
||||
| *α* + 1 | 0 | *α* + 1 | 1 | *α* |
|
||||
:::
|
||||
::::
|
||||
|
||||
As you might expect, the resulting field has 4 elements, so it's called GF(4) (or $\mathbb{F}_4$).
|
||||
In general, when adjoining an irreducible of degree *d* to GF(*p*), the resulting field has *p*^*d*^ elements,
|
||||
naturally denoted GF(p^d^) (or $\mathbb{F}_{p^d}$).
|
||||
*p* is still special, and is called the *characteristic* of the field.
|
||||
It denotes how many repeated additions are needed to get to 0.
|
||||
From the above table, it's clear that the characteristic is 2 since
|
||||
$1 + 1 = \alpha + \alpha = (\alpha + 1) + (\alpha + 1) = 0$.
|
||||
|
||||
|
||||
### ...and beyond?
|
||||
|
||||
All of this is manageable when you're adjoining a root of a degree 2 polynomial like *α* or *i*,
|
||||
but things get difficult when you start to work with higher degrees.
|
||||
The powers of the root form the basis for a *d*-dimensional vector space over GF(*p*)
|
||||
(hence the order of the field being *p*^*d*^).
|
||||
Proceeding as before, we'd have to be able to:
|
||||
|
||||
- recognize equality in the new field based on sums of powers of roots (times elements of the field)
|
||||
- have a canonical method of expressing other elements after adjoining a root
|
||||
- ideally, handle both with an algorithm that gives canonical forms from noncanonical ones
|
||||
- know when we've found every element of the new field
|
||||
|
||||
These problems make it difficult to study prime power fields on a computer without the use of
|
||||
a CAS like Maple or Mathematica.
|
||||
They're capable of taking care of these issues symbolically, working with the expressions
|
||||
in the same a mathematician would (or at least appearing to do so).
|
||||
As someone who likes to do things himself, implementing a CAS from scratch seemed a little too cumbersome.
|
||||
Furthermore, even a more direct approach using the previously-mentioned
|
||||
"canonical members of cosets of polynomials" was more annoying than I was willing to put up with.
|
||||
|
||||
Fortunately, there's a detour that makes it much easier to dodge all of these problems,
|
||||
and it has some interesting consequences.
|
||||
Join me in [the next post](../2) for a direct, non-symbolic way to work with prime power fields.
|
||||
1
posts/math/finite-field/2/MplIHaskell.hs
Symbolic link
@ -0,0 +1 @@
|
||||
../../number-number/1/MplIHaskell.hs
|
||||
234
posts/math/finite-field/2/Previous.hs
Normal file
@ -0,0 +1,234 @@
|
||||
module Previous where
|
||||
|
||||
import Control.Applicative (liftA2)
|
||||
import Data.Array
|
||||
import Data.List (unfoldr, transpose)
|
||||
import Data.Tuple (swap)
|
||||
|
||||
zipAdd :: Num a => [a] -> [a] -> [a]
|
||||
zipAdd [] ds = ds
|
||||
zipAdd cs [] = cs
|
||||
zipAdd (c:cs) (d:ds) = (c + d):zipAdd cs ds
|
||||
|
||||
{------------------------------------------------------------------------------
|
||||
- ___ _ _ _
|
||||
- | _ \___| |_ _ _ _ ___ _ __ (_)__ _| |___
|
||||
- | _/ _ \ | || | ' \/ _ \ ' \| / _` | (_-<
|
||||
- |_| \___/_|\_, |_||_\___/_|_|_|_\__,_|_/__/
|
||||
- |__/
|
||||
------------------------------------------------------------------------------}
|
||||
|
||||
-- A polynomial is its ascending list of coefficients (of type a)
|
||||
newtype Polynomial a = Poly { coeffs :: [a] } deriving (Functor)
|
||||
|
||||
instance (Eq a, Num a) => Num (Polynomial a) where
|
||||
(+) x@(Poly as) y@(Poly bs) = Poly $ zipAdd as bs
|
||||
|
||||
--convolution
|
||||
(*) (Poly []) (Poly bs) = Poly []
|
||||
(*) (Poly as) (Poly []) = Poly []
|
||||
(*) (Poly as) (Poly bs) = Poly $ zeroize $ finish $ foldl convolve' ([], []) as
|
||||
where convolve' (xs, ys) a = (a:xs, sum (zipWith (*) (a:xs) bs):ys)
|
||||
finish (xs, ys) = (reverse ys ++) $ finish' xs $ tail bs
|
||||
finish' xs [] = []
|
||||
finish' xs ys = sum (zipWith (*) xs ys):finish' xs (tail ys)
|
||||
zeroize xs = if all (==0) xs then [] else xs
|
||||
|
||||
-- this definition works
|
||||
negate = fmap negate
|
||||
-- but these two might run into some problems
|
||||
abs = fmap abs
|
||||
signum = fmap signum
|
||||
fromInteger 0 = Poly []
|
||||
fromInteger a = Poly [fromInteger a]
|
||||
|
||||
instance (Eq a, Num a) => Eq (Polynomial a) where
|
||||
(==) as bs = all (==0) $ coeffs $ as - bs
|
||||
|
||||
|
||||
-- Interpret a number's base-b expansion as a polynomial
|
||||
-- Build a list with f, which returns either Nothing
|
||||
-- or Just (next element of list, next argument to f)
|
||||
asPoly :: Int -> Int -> Polynomial Int
|
||||
asPoly b = Poly . unfoldr f where
|
||||
-- Divide x by b. Emit the remainder and recurse with the quotient.
|
||||
f x | x /= 0 = Just $ swap $ divMod x b
|
||||
-- If there's nothing left to divide out, terminate
|
||||
| otherwise = Nothing
|
||||
|
||||
-- Horner evaluation of a polynomial at the integer b
|
||||
-- Start with the highest coefficient
|
||||
-- Multiply by b at each step and add the coefficient of the next term
|
||||
evalPoly b (Poly p) = foldr (\y acc -> b*acc + y) 0 p
|
||||
|
||||
-- Divide the polynomial ps by qs (coefficients in descending degree order)
|
||||
synthDiv' :: (Eq a, Num a) => [a] -> [a] -> ([a], [a])
|
||||
synthDiv' ps qs
|
||||
| head qs /= 1 = error "Cannot divide by non-monic polynomial"
|
||||
| otherwise = splitAt deg $ doDiv ps deg
|
||||
where
|
||||
-- Negate the denominator and ignore leading term
|
||||
qNeg = map negate $ tail qs
|
||||
-- The degree of the result, based on the degrees of the numerator and denominator
|
||||
deg = max 0 (length ps - length qs + 1)
|
||||
-- Pluck off the head of the list and add a shifted and scaled version of
|
||||
-- qs to the tail of the list. Repeat this d times
|
||||
doDiv xs 0 = xs
|
||||
doDiv (x:xs) d = x:doDiv (zipAdd xs $ map (*x) qNeg) (d - 1)
|
||||
|
||||
-- Use Polynomial (coefficients in ascending degree order) instead of lists
|
||||
synthDiv :: (Eq a, Num a) => Polynomial a -> Polynomial a
|
||||
-> (Polynomial a, Polynomial a)
|
||||
synthDiv (Poly p) (Poly q) = (Poly $ reverse quot, Poly $ reverse rem)
|
||||
where (quot, rem) = synthDiv' (reverse p) (reverse q)
|
||||
|
||||
|
||||
{------------------------------------------------------------------------------
|
||||
- ___ _ _ _ ___
|
||||
- | _ \___| |_ _ _ _ ___ _ __ (_)__ _| | / __| ___ __ _ _ _ ___ _ _ __ ___ ___
|
||||
- | _/ _ \ | || | ' \/ _ \ ' \| / _` | | \__ \/ -_) _` | || / -_) ' \/ _/ -_|_-<
|
||||
- |_| \___/_|\_, |_||_\___/_|_|_|_\__,_|_| |___/\___\__, |\_,_\___|_||_\__\___/__/
|
||||
- |__/ |_|
|
||||
------------------------------------------------------------------------------}
|
||||
|
||||
-- All monic polynomials of degree d with coefficients mod n
|
||||
monics :: Int -> Int -> [Polynomial Int]
|
||||
monics n d = map (asPoly n) [n^d..2*(n^d) - 1]
|
||||
|
||||
-- All monic polynomials with coefficients mod n, ordered by degree
|
||||
allMonics :: Int -> [Polynomial Int]
|
||||
allMonics n = concat [monics n d | d <- [1..]]
|
||||
|
||||
-- All irreducible monic polynomials with coefficients mod n
|
||||
irreducibles :: Int -> [Polynomial Int]
|
||||
irreducibles n = go [] $ allMonics n where
|
||||
-- Divide the polynomial x by i, then take the remainder mod n
|
||||
remModN x i = fmap (`mod` n) $ snd $ synthDiv x i
|
||||
-- Find remainders of x divided by every irreducible in "is".
|
||||
-- If any give the zero polynomial, then x is a multiple of an irreducible
|
||||
notMultiple x is = and [not $ all (==0) $ coeffs $ remModN x i | i <- is]
|
||||
-- Sieve out by notMultiple
|
||||
go is (x:xs)
|
||||
| notMultiple x is = x:go (x:is) xs
|
||||
| otherwise = go is xs
|
||||
|
||||
|
||||
{------------------------------------------------------------------------------
|
||||
- __ __ _ _
|
||||
- | \/ |__ _| |_ _ _(_)__ ___ ___
|
||||
- | |\/| / _` | _| '_| / _/ -_|_-<
|
||||
- |_| |_\__,_|\__|_| |_\__\___/__/
|
||||
-
|
||||
------------------------------------------------------------------------------}
|
||||
|
||||
newtype Matrix a = Mat { unMat :: Array (Int, Int) a } deriving (Functor)
|
||||
|
||||
asScalar = Mat . listArray ((0,0),(0,0)) . pure
|
||||
|
||||
transposeM :: Matrix a -> Matrix a
|
||||
transposeM (Mat m) = Mat $ ixmap (bounds m) swap m
|
||||
|
||||
dotMul :: Num a => [a] -> [a] -> a
|
||||
dotMul = (sum .) . zipWith (*)
|
||||
|
||||
matMul' xs ys = reshape (length ys) $ liftA2 dotMul xs ys
|
||||
matMul xs ys = matMul' xs $ transpose ys
|
||||
|
||||
-- Pointwise application
|
||||
zipWithArr :: Ix i => (a -> b -> c)
|
||||
-> Array i a -> Array i b -> Array i c
|
||||
zipWithArr f a b
|
||||
| ab == bb = array ab $ map (\x -> (x, f (a!x) (b!x))) $ indices a
|
||||
| otherwise = error "Array dimension mismatch" where
|
||||
ab = bounds a
|
||||
bb = bounds b
|
||||
|
||||
-- Maps
|
||||
mapRange :: Ix i => (i -> e) -> (i, i) -> [(i, e)]
|
||||
mapRange g r = map (\x -> (x, g x)) $ range r
|
||||
|
||||
instance (Num a, Eq a) => Eq (Matrix a) where
|
||||
(==) (Mat x) (Mat y)
|
||||
| (0,0) == (snd $ bounds x) = and [x!(0,0) == if m == n then u else 0 | ((m,n), u) <- assocs y]
|
||||
| (0,0) == (snd $ bounds y) = and [x!(0,0) == if m == n then u else 0 | ((m,n), u) <- assocs x]
|
||||
| otherwise = x == y
|
||||
|
||||
|
||||
instance Num a => Num (Matrix a) where
|
||||
(+) (Mat x) (Mat y)
|
||||
| (0,0) == (snd $ bounds x) && (0,0) == (snd $ bounds y) = Mat $ zipWithArr (+) x y
|
||||
| (0,0) == (snd $ bounds x) = Mat y + (((x!(0,0)) *) <$> eye ((+1) $ snd $ snd $ bounds y)) --adding scalars
|
||||
| (0,0) == (snd $ bounds y) = Mat x + (((y!(0,0)) *) <$> eye ((+1) $ snd $ snd $ bounds x)) --adding scalars
|
||||
| otherwise = Mat $ zipWithArr (+) x y
|
||||
(*) x y
|
||||
| (0,0) == (snd $ bounds $ unMat x) = fmap (((unMat x)!(0,0))*) y --multiplying scalars
|
||||
| (0,0) == (snd $ bounds $ unMat y) = fmap (((unMat y)!(0,0))*) x --multiplying scalars
|
||||
| otherwise = toMatrix $ matMul' (fromMatrix x) (fromMatrix $ transposeM y)
|
||||
abs = fmap abs
|
||||
negate = fmap negate
|
||||
signum = undefined
|
||||
fromInteger = asScalar . fromInteger --"scalars"
|
||||
|
||||
reshape :: Int -> [a] -> [[a]]
|
||||
reshape n = unfoldr (reshape' n) where
|
||||
reshape' n x = if null x then Nothing else Just $ splitAt n x
|
||||
|
||||
-- Simple function for building a Matrix from lists
|
||||
toMatrix :: [[a]] -> Matrix a
|
||||
toMatrix l = Mat $ listArray ((0,0),(n-1,m-1)) $ concat l where
|
||||
m = length $ head l
|
||||
n = length l
|
||||
|
||||
-- ...and its inverse
|
||||
fromMatrix :: Matrix a -> [[a]]
|
||||
fromMatrix (Mat m) = let (_,(_,n)) = bounds m in reshape (n+1) $ elems m
|
||||
|
||||
-- Zero matrix
|
||||
zero :: Num a => Int -> Matrix a
|
||||
zero n = Mat $ listArray ((0,0),(n-1,n-1)) $ repeat 0
|
||||
|
||||
-- Identity matrix
|
||||
eye :: Num a => Int -> Matrix a
|
||||
eye n = Mat $ unMat (zero n) // take n [((i,i), 1) | i <- [0..]]
|
||||
|
||||
-- Companion matrix
|
||||
companion :: (Eq a, Num a) => Polynomial a -> Matrix a
|
||||
companion (Poly ps)
|
||||
| last ps' /= 1 = error "Cannot find companion matrix of non-monic polynomial"
|
||||
| otherwise = Mat $ array ((0,0), (n-1,n-1)) $ lastRow ++ shiftI where
|
||||
-- The degree of the polynomial, as well as the size of the matrix
|
||||
n = length ps' - 1
|
||||
-- Remove trailing 0s from ps
|
||||
ps' = reverse $ dropWhile (==0) $ reverse ps
|
||||
-- Address/value tuples for a shifted identity matrix:
|
||||
-- 1s on the diagonal just above the main diagonal, 0s elsewhere
|
||||
shiftI = map (\p@(x,y) -> (p, if y == x + 1 then 1 else 0)) $
|
||||
range ((0,0),(n-2,n-1))
|
||||
-- Address/value tuples for the last row of the companion matrix:
|
||||
-- ascending powers of the polynomial
|
||||
lastRow = zipWith (\x y -> ((n-1, x), y)) [0..n-1] $ map negate ps'
|
||||
|
||||
mDims = snd . bounds . unMat
|
||||
|
||||
-- trace of a matrix: sum of diagonal entries
|
||||
mTrace :: (Num a) => Matrix a -> a
|
||||
mTrace a = sum [(unMat a)!(i,i) | i <- [0..(uncurry min $ mDims a)]]
|
||||
|
||||
-- Faddeev-Leverrier algorithm for computing the determinant, characteristic polynomial, and inverse matrix
|
||||
-- this relies on dividing the trace by the current iteration count, so it is only suitable for Integral types
|
||||
fadeev :: (Num a, Integral a) => Matrix a -> ([a], a, Matrix a)
|
||||
fadeev a = fadeev' [1] (eye $ n+1) 1 where
|
||||
n = snd $ mDims a
|
||||
fadeev' rs m i
|
||||
| all (==0) (unMat nextm) = (replicate (n - (fromIntegral i) + 1) 0 ++ tram:rs , -tram, m)
|
||||
| otherwise = fadeev' (tram:rs) nextm (i+1) where
|
||||
am = a * m
|
||||
tram = -mTrace am `div` i
|
||||
nextm = am + (fmap (tram *) $ eye (n+1))
|
||||
|
||||
-- determinant from Faddeev-Leverrier algorithm
|
||||
determinant :: (Num a, Integral a) => Matrix a -> a
|
||||
determinant = (\(_,x,_) -> x) . fadeev
|
||||
-- characteristic polynomial from Faddeev-Leverrier algorithm
|
||||
charpoly :: (Num a, Integral a) => Matrix a -> Polynomial a
|
||||
charpoly = Poly . (\(x,_,_) -> x) . fadeev
|
||||
BIN
posts/math/finite-field/2/char_2_irreducibles_graphs.png
(Stored with Git LFS)
Normal file
287
posts/math/finite-field/2/extra/index.qmd
Normal file
@ -0,0 +1,287 @@
|
||||
---
|
||||
title: "Exploring Finite Fields, Part 2 Appendix"
|
||||
description: |
|
||||
Additional notes about polynomial evaluation.
|
||||
format:
|
||||
html:
|
||||
html-math-method: katex
|
||||
date: "2024-01-15"
|
||||
date-modified: "2025-07-16"
|
||||
categories:
|
||||
- algebra
|
||||
- finite field
|
||||
- haskell
|
||||
---
|
||||
|
||||
|
||||
In the [second post in this series](../), we briefly discussed alternate means
|
||||
of evaluating polynomials by "plugging in" different structures.
|
||||
|
||||
|
||||
Different Kinds of Polynomials
|
||||
------------------------------
|
||||
|
||||
Rather than redefining evaluation for each of these cases,
|
||||
we can map the polynomial into a structure compatible with it should be evaluated.
|
||||
Essentially, this means that from a polynomial in the base structure,
|
||||
we can derive polynomials in these other structures.
|
||||
|
||||
In particular, there is a distinction between a matrix of polynomials or a polynomial in matrices:
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {}
|
||||
$$
|
||||
\begin{align*}
|
||||
p &: K[x]
|
||||
\\
|
||||
p(x) &= x^n + p_{n-1}x^{n-1} + ...
|
||||
\\
|
||||
\phantom{= p} & + p_1 x + p_0
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
|
||||
::: {}
|
||||
$x$ is a scalar indeterminate
|
||||
|
||||
```haskell
|
||||
p :: Polynomial k
|
||||
```
|
||||
:::
|
||||
::::
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {}
|
||||
$$
|
||||
\begin{align*}
|
||||
P &: (K[x])^{m \times m}
|
||||
\\
|
||||
P(x I) &= (x I)^n + (p_{n-1})(x I)^{n-1} + ...
|
||||
\\
|
||||
& + p_1(x I)+ p_0 I
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
|
||||
::: {}
|
||||
$x$ is a scalar indeterminate, $P(x I)= p(x) I$ is a matrix of polynomials in $x$
|
||||
|
||||
```haskell
|
||||
asPolynomialMatrix
|
||||
:: Polynomial k -> Matrix (Polynomial k)
|
||||
|
||||
pMat :: Matrix (Polynomial k)
|
||||
pMat = asPolynomialMatrix p
|
||||
```
|
||||
:::
|
||||
::::
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {}
|
||||
$$
|
||||
\begin{align*}
|
||||
\hat P &: K^{m \times m}[X]
|
||||
\\
|
||||
\hat P(X) &= X^n + (p_{n-1}I)X^{n-1} + ...
|
||||
\\
|
||||
& + (p_1 I) X + (p_0 I)
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
|
||||
::: {}
|
||||
$X$ is a matrix indeterminate, $\hat P(X)$ is a polynomial over matrices
|
||||
|
||||
```haskell
|
||||
asMatrixPolynomial
|
||||
:: Polynomial k -> Polynomial (Matrix k)
|
||||
|
||||
pHat :: Polynomial (Matrix k)
|
||||
pHat = asMatrixPolynomial p
|
||||
```
|
||||
:::
|
||||
::::
|
||||
|
||||
It's easy to confuse the latter two, but the Haskell makes the difference in types clearer.
|
||||
There exists a natural isomorphism between the two, which is discussed further
|
||||
in the [fourth post in this series](../../4/).
|
||||
|
||||
|
||||
Cayley-Hamilton Theorem, Revisited
|
||||
----------------------------------
|
||||
|
||||
As a reminder, the
|
||||
[Cayley-Hamilton theorem](https://en.wikipedia.org/wiki/Cayley%E2%80%93Hamilton_theorem)
|
||||
says that a matrix satisfies its own characteristic polynomial.
|
||||
In a type-stricter sense, it says the following relationship holds:
|
||||
|
||||
```haskell
|
||||
evalPoly :: a -> Polynomial a -> a
|
||||
|
||||
mA :: Matrix a
|
||||
|
||||
charpolyA :: Polynomial a
|
||||
charpolyA = charpoly mA
|
||||
|
||||
charpolyA :: Polynomial (Matrix a)
|
||||
matCharpolyA = asMatrixPolynomial charPolyA
|
||||
|
||||
evalPoly mA matCharpolyA == (0 :: Matrix a)
|
||||
```
|
||||
|
||||
|
||||
Due to the aformentioned isomorphism, factoring a polynomial "inside" a matrix turns
|
||||
out to give the same answer as factoring a polynomial over matrices.
|
||||
|
||||
:::: {layout-ncol="2"}
|
||||
::: {}
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
P(xI) = \left( \begin{matrix}
|
||||
x^2 + x + 1 & 0 \\
|
||||
0 & x^2 + x + 1
|
||||
\end{matrix}\right)
|
||||
\\ \\
|
||||
= (xI - C)(xI - C')
|
||||
\\ \\
|
||||
= \left( \begin{matrix}
|
||||
x & -1 \\
|
||||
1 & x + 1
|
||||
\end{matrix} \right)
|
||||
\left( \begin{matrix}
|
||||
x - a & -b \\
|
||||
-c & x - d
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
\begin{align*}
|
||||
x(x - a) + c &= x^2 + x + 1
|
||||
\\
|
||||
\textcolor{green}{x(-b) - (x - d)} &\textcolor{green}{= 0}
|
||||
\\
|
||||
\textcolor{blue}{(x - a) + (x + 1)(-c)} &\textcolor{blue}{= 0}
|
||||
\\
|
||||
(-b) + (x + 1)(x - d) &= x^2 + x + 1
|
||||
\end{align*}
|
||||
\\ \\
|
||||
\textcolor{green}{(-b -1)x +d = 0} \implies b = -1, ~ d = 0 \\
|
||||
\textcolor{blue}{(1 - c)x - a - c = 0} \implies c = 1, ~ a = -1
|
||||
\\ \\
|
||||
C' =
|
||||
\left( \begin{matrix}
|
||||
-1 & -1 \\
|
||||
1 & 0
|
||||
\end{matrix} \right)
|
||||
\end{gather*}
|
||||
$$
|
||||
:::
|
||||
|
||||
::: {}
|
||||
$$
|
||||
\begin{gather*}
|
||||
\hat P(X) = X^2 + X + 1I
|
||||
\\[10pt]
|
||||
= (X - C)(X - C')
|
||||
\\[10pt]
|
||||
= X^2 - (C + C')X + CC'
|
||||
\\[10pt]
|
||||
\implies
|
||||
\\[10pt]
|
||||
C + C' = -I, ~ C' = -I - C
|
||||
\\[10pt]
|
||||
CC' = I, ~ C^{-1} = C'
|
||||
\\[10pt]
|
||||
C' = \left( \begin{matrix}
|
||||
-1 & -1 \\
|
||||
1 & 0
|
||||
\end{matrix} \right)
|
||||
\end{gather*}
|
||||
$$
|
||||
:::
|
||||
::::
|
||||
|
||||
It's important to not that a matrix factorization is not unique.
|
||||
*Any* matrix with a given characteristic polynomial can be used as a root of that polynomial.
|
||||
Of course, choosing one root affects the other matrix roots.
|
||||
|
||||
|
||||
### Moving Roots
|
||||
|
||||
All matrices commute with the identity and zero matrices.
|
||||
A less obvious fact is that for a matrix factorization, all roots *also* commute with one another.
|
||||
By the Fundamental Theorem of Algebra,
|
||||
[Vieta's formulas](https://en.wikipedia.org/wiki/Vieta%27s_formulas) state:
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
\hat P(X)
|
||||
= \prod_{[i]_n} (X - \Xi_i)
|
||||
= (X - \Xi_0) (X - \Xi_1)...(X - \Xi_{n-1})
|
||||
\\
|
||||
= \left\{ \begin{align*}
|
||||
& \phantom{+} X^n
|
||||
\\
|
||||
& - (\Xi_0 + \Xi_1 + ... + \Xi_{n-1}) X^{n-1}
|
||||
\\
|
||||
& + (\Xi_0 \Xi_1+ \Xi_0 \Xi_2 + ... + \Xi_0 \Xi_{n-1} + \Xi_1 \Xi_2 + ... + \Xi_{n-2} \Xi_{n-1})X^{n-2}
|
||||
\\
|
||||
& \qquad \vdots
|
||||
\\
|
||||
& + (-1)^n \Xi_0 \Xi_1 \Xi_2...\Xi_n
|
||||
\end{align*} \right.
|
||||
\\
|
||||
= X^n -\sigma_1([\Xi]_n)X^{n-1} + \sigma_2([\Xi]_n)X^{n-2} + ... + (-1)^n \sigma_n([\Xi]_n)
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
The product range \[*i*\]~*n*~ means that the terms are ordered from 0 to *n* - 1 over the index given.
|
||||
On the bottom line, *σ* are
|
||||
[elementary symmetric polynomials](https://en.wikipedia.org/wiki/Elementary_symmetric_polynomial)
|
||||
and \[*Ξ*\]~*n*~ is the list of root matrices from *Ξ*~*0*~ to Ξ~*n-1*~.
|
||||
|
||||
By factoring the matrix with the roots in a different order, we get another factorization.
|
||||
It suffices to only focus on *σ*~2~, which has all pairwise products.
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
\pi \in S_n
|
||||
\\
|
||||
\qquad
|
||||
\pi \circ \hat P(X) = \prod_{\pi ([i]_n)} (X - \Xi_i)
|
||||
\\ \\
|
||||
= X^n
|
||||
- \sigma_1 \left(\pi ([\Xi]_n) \vphantom{^{1}} \right)X^{n-1}
|
||||
+ \sigma_2 \left(\pi ([\Xi]_n) \vphantom{^{1}} \right)X^{n-2} + ...
|
||||
+ (-1)^n \sigma_n \left(\pi ([\Xi]_n) \vphantom{^{1}} \right)
|
||||
\\[10pt]
|
||||
\pi_{(0 ~ 1)} \circ \hat P(X) = (X - \Xi_{1}) (X - \Xi_0)(X - \Xi_2)...(X - \Xi_{n-1})
|
||||
\\
|
||||
= X^n + ... + \sigma_2(\Xi_1, \Xi_0, \Xi_2, ...,\Xi_{n-1})X^{n-2} + ...
|
||||
\\[10pt]
|
||||
\begin{array}{}
|
||||
e & (0 ~ 1) & (1 ~ 2) & ... & (n-2 ~~ n-1)
|
||||
\\ \hline
|
||||
\textcolor{red}{\Xi_0 \Xi_1} & \textcolor{red}{\Xi_1 \Xi_0} & \Xi_0 \Xi_1 & & \Xi_0 \Xi_1
|
||||
\\
|
||||
\Xi_0 \Xi_2 & \Xi_0 \Xi_2 & \Xi_0 \Xi_2 & & \Xi_0 \Xi_2
|
||||
\\
|
||||
\Xi_0 \Xi_3 & \Xi_0 \Xi_3 & \Xi_0 \Xi_3 & & \Xi_0 \Xi_3
|
||||
\\
|
||||
\vdots & \vdots & \vdots & & \vdots
|
||||
\\
|
||||
\Xi_0 \Xi_{n-1} & \Xi_0 \Xi_{n-1} & \Xi_{0} \Xi_{n-1} & & \Xi_{n-1} \Xi_0
|
||||
\\
|
||||
\textcolor{green}{\Xi_1 \Xi_2} & \Xi_1 \Xi_2 & \textcolor{green}{\Xi_2 \Xi_1} & & \Xi_1 \Xi_2
|
||||
\\
|
||||
\vdots & \vdots & \vdots & & \vdots
|
||||
\\
|
||||
\textcolor{blue}{\Xi_{n-2} \Xi_{n-1}} & \Xi_{n-2} \Xi_{n-1} & \Xi_{n-2} \Xi_{n-1} & & \textcolor{blue}{\Xi_{n-1} \Xi_{n-2}}
|
||||
\end{array}
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
The "[path swaps](/posts/math/permutations/1/)" shown commute only the adjacent elements.
|
||||
By contrast, the permutation $(0 ~ 2)$ commutes *Ξ*~0~ past both *Ξ*~1~ and *Ξ*~2~.
|
||||
But since we already know *Ξ*~0~ and *Ξ*~1~ commute by the above list,
|
||||
we learn at this step that *Ξ*~0~ and *Ξ*~2~ commute.
|
||||
This can be repeated until we reach the permutation $(0 ~ n-1)$ to prove commutativity between all pairs.
|
||||
926
posts/math/finite-field/2/index.qmd
Normal file
@ -0,0 +1,926 @@
|
||||
---
|
||||
title: "Exploring Finite Fields, Part 2: Matrix Boogaloo"
|
||||
description: |
|
||||
How do we extend a field non-symbolically?
|
||||
format:
|
||||
html:
|
||||
html-math-method: katex
|
||||
date: "2024-01-15"
|
||||
date-modified: "2025-08-03"
|
||||
categories:
|
||||
- algebra
|
||||
- finite field
|
||||
- haskell
|
||||
---
|
||||
|
||||
```{haskell}
|
||||
--| echo: false
|
||||
|
||||
:l Previous.hs
|
||||
:l MplIHaskell.hs
|
||||
|
||||
import Data.Array ((//), listArray, bounds)
|
||||
import Data.Bifunctor (bimap)
|
||||
import Data.Profunctor (rmap)
|
||||
import Data.List (unfoldr, intercalate, nub, (\\), subsequences, sortOn, groupBy)
|
||||
import Data.Maybe (fromMaybe, fromJust, listToMaybe)
|
||||
import Data.Either (partitionEithers)
|
||||
import Data.Function (on)
|
||||
|
||||
import Colonnade hiding (fromMaybe)
|
||||
import qualified Colonnade.Encode as CE
|
||||
import qualified Graphics.Matplotlib.Internal as MPLI
|
||||
import IHaskell.Display (markdown)
|
||||
|
||||
import Previous (
|
||||
Matrix(Mat, unMat), Polynomial(Poly, coeffs),
|
||||
asPoly, evalPoly, synthDiv, irreducibles,
|
||||
charpoly, companion, toMatrix, fromMatrix, zero, eye
|
||||
)
|
||||
import qualified MplIHaskell
|
||||
|
||||
markdownTable col rows = unlines $ h:h':r where
|
||||
toColumns = ("| " ++) . (++ " |") . intercalate " | " . foldr (:) []
|
||||
h = toColumns $ CE.header id col
|
||||
h' = [if x == '|' then x else '-' | x <- h]
|
||||
r = map (toColumns . CE.row id col) rows
|
||||
|
||||
-- Convert Polynomial to LaTeX string
|
||||
texifyPoly :: (Num a, Eq a, Show a) => Polynomial a -> String
|
||||
texifyPoly (Poly xs) = texify' $ zip xs [0..] where
|
||||
texify' [] = "0"
|
||||
texify' ((c, n):xs)
|
||||
| all ((==0) . fst) xs = showPow c n
|
||||
| c == 0 = texify' xs
|
||||
| otherwise = showPow c n ++ " + " ++ texify' xs
|
||||
showPow c 0 = show c
|
||||
showPow 1 1 = "x"
|
||||
showPow c 1 = show c ++ showPow 1 1
|
||||
showPow 1 n = "x^{" ++ show n ++ "}"
|
||||
showPow c n = show c ++ showPow 1 n
|
||||
|
||||
-- Convert matrix to LaTeX string
|
||||
texifyMatrix mat = surround mat' where
|
||||
mat' = intercalate " \\\\ " $ map (intercalate " & " . map show) $
|
||||
fromMatrix mat
|
||||
surround = ("\\left( \\begin{matrix}" ++) . (++ "\\end{matrix} \\right)")
|
||||
|
||||
-- networkx.display tries printing a string that ruins the SVG this generates
|
||||
plotDigraph x = MPLI.mplotString $
|
||||
"import networkx as nx \n\
|
||||
\import os \n\
|
||||
\original_stdout = sys.stdout \n\
|
||||
\sys.stdout = open(os.devnull, 'w') \n\
|
||||
\try: \n\
|
||||
\ nx.display(nx.DiGraph(" ++ MPLI.toPython x ++ ")) \n\
|
||||
\finally: \n\
|
||||
\ sys.stdout.close() \n\
|
||||
\ sys.stdout = original_stdout"
|
||||
```
|
||||
|
||||
In the [last post](../1/), we discussed finite fields, polynomials and matrices over them, and the typical,
|
||||
symbolic way of extending fields with polynomials.
|
||||
This post will will focus on circumventing symbolic means with numeric ones.
|
||||
|
||||
|
||||
More about Matrices (and Polynomials)
|
||||
-------------------------------------
|
||||
|
||||
Recall the definition of polynomial evaluation.
|
||||
Since a polynomial is defined with respect to a field or ring, we expect only to be able to evaluate the
|
||||
polynomial at values *in* that field or ring.
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
K[x] \times K \overset{\text{eval}}{\longrightarrow} K
|
||||
\\
|
||||
(p(x), n) \overset{\text{eval}}{\mapsto} p(n)
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
However, there's nothing wrong with evaluating polynomials with another polynomial,
|
||||
as long as they're defined over the same structure.
|
||||
After all, we can take powers of polynomials, scalar-multiply them with coefficients from *K*,
|
||||
and add them together.
|
||||
The same holds for matrices, or any "collection" structure *F* over *K* which has those properties.
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
K[x] \times K[x]
|
||||
&\overset{\text{eval}_{poly}}{\longrightarrow} K[x]
|
||||
\\
|
||||
(p(x), q(x)) \mapsto p(q(x))
|
||||
\\[10pt]
|
||||
K[x] \times K^{n \times n}
|
||||
&\overset{\text{eval}_{mat}}{\longrightarrow} K^{n \times n}
|
||||
(p(x), A) \overset{?}{\mapsto} p(A)
|
||||
\\[10pt]
|
||||
K[x] \times F(K)
|
||||
&\overset{\text{eval}_F}{\longrightarrow} F(K)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
|
||||
### Cayley-Hamilton Theorem
|
||||
|
||||
When evaluating the characteristic polynomial of a matrix *with* that matrix,
|
||||
something strange happens.
|
||||
Continuing from the previous article, using $x^2 + x + 1$ and its companion matrix, we have:
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
p(x) = x^2 + x + 1 \qquad C_{p} = C
|
||||
= \left( \begin{matrix}
|
||||
0 & 1 \\
|
||||
-1 & -1
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
\begin{align*}
|
||||
\text{eval}_{mat}(p, C) &= C^2 + C + (1 \cdot I)
|
||||
\\
|
||||
&= \left( \begin{matrix}
|
||||
-1 & -1 \\
|
||||
1 & 0
|
||||
\end{matrix} \right)
|
||||
+ \left( \begin{matrix}
|
||||
0 & 1 \\
|
||||
-1 & -1
|
||||
\end{matrix} \right)
|
||||
+ \left( \begin{matrix}
|
||||
1 & 0 \\
|
||||
0 & 1
|
||||
\end{matrix} \right)
|
||||
\\
|
||||
&= \left( \begin{matrix}
|
||||
0 & 0 \\
|
||||
0 & 0
|
||||
\end{matrix} \right)
|
||||
\end{align*}
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
The result is the zero matrix.
|
||||
This tells us that, at least in this case, the matrix *C* is a root of its own characteristic polynomial.
|
||||
By the [Cayley-Hamilton theorem](https://en.wikipedia.org/wiki/Cayley%E2%80%93Hamilton_theorem),
|
||||
this is true in general, no matter the degree of *p*, no matter its coefficients,
|
||||
and importantly, no matter the choice of field.
|
||||
|
||||
In addition to this, we can also note the following:
|
||||
|
||||
- Irreducible polynomials cannot have a constant term 0, otherwise *x* could be factored out.
|
||||
The constant term is equal to the determinant of the companion matrix (up to sign),
|
||||
so *C*~*p*~ must be invertible.
|
||||
- All powers of *C*~*p*~ are guaranteed to commute over multiplication,
|
||||
since this follows from associativity.
|
||||
|
||||
Both of these facts narrow the ring of matrices to a full-on field.
|
||||
This absolves us of needing to adjoin roots symbolically using α.
|
||||
Instead, we can take the companion matrix of an irreducible polynomial *p*
|
||||
and work with its powers in the same way we would a typical root[^1].
|
||||
|
||||
[^1]: For finite fields, it might make sense to do the following procedure
|
||||
to generate every possible element:
|
||||
|
||||
- Take all powers of a companion matrix *C*
|
||||
- Add all powers of *C* with prior elements of the field (times identity matrices)
|
||||
- Repeat until no new elements are generated
|
||||
|
||||
In fact, we can usually do a little better, as we'll see.
|
||||
|
||||
|
||||
GF(8)
|
||||
-----
|
||||
|
||||
This is all rather abstract, so let's look at an example before we proceed any further.
|
||||
The next smallest field of characteristic 2 is GF(8).
|
||||
We can construct this field from the two irreducible polynomials of degree 3 over GF(2):
|
||||
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
irrsOfDegree d n = map fst $ takeWhile ((==d) . snd) $ dropWhile ((/=d) . snd) $
|
||||
map ((,) <*> (+(-1)) . length . coeffs) $ irreducibles n
|
||||
|
||||
-- First (and only) two polynomials of degree 3
|
||||
qPoly:rPoly:_ = irrsOfDegree 3 2
|
||||
|
||||
|
||||
-- Display a polynomial in positional notation, base x
|
||||
texPolyAsPositional (Poly xs) = (++ "_{x}") $
|
||||
reverse xs >>= (\x -> if x < 0 then "\\bar{" ++ show (-x) ++ "}" else show x)
|
||||
-- Display a polynomial as its encoding in base b
|
||||
texPolyAsNumeric b p = (("{}_{" ++ show b ++ "} ") ++) $ show $ evalPoly b p
|
||||
|
||||
-- Display a polynomial and equivalent notations
|
||||
texPolyPosNum b p = texifyPoly p ++ " = " ++
|
||||
texPolyAsPositional p ++ "\\sim" ++
|
||||
texPolyAsNumeric b p
|
||||
|
||||
-- Display a polynomial and its companion matrix
|
||||
texPolyAndMatrix b p name = name ++ "(x) = " ++ texPolyPosNum b p ++
|
||||
"\\qquad C_{" ++ name ++ "} = " ++ texifyMatrix ((`mod` b) <$> companion p)
|
||||
|
||||
markdown $ "$$\\begin{gather*}" ++
|
||||
texPolyAndMatrix 2 qPoly "q" ++
|
||||
"\\\\" ++ texPolyAndMatrix 2 rPoly "r" ++
|
||||
"\\end{gather*}$$"
|
||||
```
|
||||
|
||||
Notice how the bit strings for either of these polynomials is the other, reversed.
|
||||
Arbitrarily, let's work with C~r~.
|
||||
The powers of this matrix (mod 2) are as follows:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- Compute all powers of a matrix, starting with the first
|
||||
matrixPowersMod b mat = iterate (((`mod` b) <$>) . (mat*)) mat
|
||||
|
||||
-- Show a matrix power
|
||||
texMatrixPower n b mat name = "(" ++ name ++ ")^{" ++ show n ++ "} = " ++
|
||||
texifyMatrix ((`mod` b) <$> mat)
|
||||
|
||||
-- Show all matrix powers
|
||||
texPows b mat name = [texMatrixPower n b matPow name |
|
||||
(n, matPow) <- zip [1..] (matrixPowersMod b mat)]
|
||||
|
||||
let pows = texPows 2 (companion rPoly) "C_r" in
|
||||
markdown $ "$$\\begin{gather*}" ++
|
||||
concat (take 3 pows) ++ "\\\\" ++
|
||||
concat (take 3 $ drop 3 pows) ++ "\\\\" ++
|
||||
(pows !! (7 - 1)) ++ " = I = (C_r)^0 \\quad" ++
|
||||
(pows !! (8 - 1)) ++ " = C_r" ++
|
||||
"\\end{gather*}$$"
|
||||
```
|
||||
|
||||
As a reminder, these matrices are taken mod 2, so the elements can only be 0 or 1.
|
||||
The seventh power of *C*~*r*~ is just the identity matrix,
|
||||
meaning that the eighth power is the original matrix.
|
||||
This means that *C*~*r*~ is cyclic of order 7 with respect to self-multiplication mod 2.
|
||||
Along with the zero matrix, this fully characterizes GF(8).
|
||||
|
||||
If we picked *C*~*q*~ instead, we would have gotten different matrices.
|
||||
I'll omit writing them here, but we get the same result: *C*~*q*~ is also cyclic of order 7.
|
||||
Since every nonzero element of the field can be written as a power of the root,
|
||||
the root (as well as the polynomial) is termed
|
||||
[primitive](https://en.wikipedia.org/wiki/Primitive_polynomial_%28field_theory%29).
|
||||
|
||||
|
||||
### Condensing
|
||||
|
||||
Working with matrices directly, as a human, is very cumbersome.
|
||||
While it makes computation explicit, it makes presentation difficult.
|
||||
One of the things in which we know we should be interested is the characteristic polynomial,
|
||||
since it is central to the definition and behavior of the matrices.
|
||||
Let's focus only on the characteristic polynomial for successive powers of *C*~*r*~
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- Create sequence of charpolys (mod b) from the powers of its companion matrix
|
||||
charPolyPows b = map (((`mod` b) <$>) . charpoly) . matrixPowersMod b . companion
|
||||
|
||||
-- Display row of charpoly array
|
||||
texCharPolyRow b poly name extra = "\\text{charpoly}(" ++ name ++ ")" ++ "&=&" ++
|
||||
fst (extra poly) ++ texifyPoly poly ++ "&=&" ++
|
||||
fst (extra poly) ++ texPolyAsPositional poly ++ "\\sim" ++
|
||||
texPolyAsNumeric b poly ++ " = " ++ snd (extra poly)
|
||||
|
||||
markdown $ "$$\\begin{array}{}" ++
|
||||
intercalate " \\\\ " [
|
||||
texCharPolyRow 2 mat ("(C_r)^{" ++ show n ++ "}") (\x ->
|
||||
if x == rPoly then ("\\color{blue}", "r")
|
||||
else if x == qPoly then ("\\color{red}", "q")
|
||||
else ("", "(x + 1)^3")
|
||||
)
|
||||
| (n, mat) <- zip [1..7] (charPolyPows 2 rPoly)
|
||||
] ++
|
||||
"\\end{array}$$"
|
||||
```
|
||||
|
||||
Somehow, even though we start with one characteristic polynomial, the other manages to work its way in here.
|
||||
Both polynomials are of degree 3 and have 3 matrix roots (distinguished in red and blue).
|
||||
|
||||
If we chose to use *C*~*q*~, we'd actually get the same sequence backwards (starting with ~2~11).
|
||||
It's beneficial to remember that 6, 5, and 3 can also be written as 7 - 1, 7 - 2, and 7 - 4.
|
||||
This makes it clear that the powers of 2 (the field characteristic) less than the 8 (the order of the field)
|
||||
play a role with respect to both the initial and terminal items.
|
||||
|
||||
|
||||
### Factoring
|
||||
|
||||
Intuitively, you may try using the roots to factor the matrix into powers of *C*~*r*~.
|
||||
This turns out to work:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- Convert a list of roots to a polynomial with those as its roots
|
||||
rootsToPoly :: (Num a, Eq a) => [a] -> Polynomial a
|
||||
rootsToPoly xs = Poly $ reverse $ zipWith (*) (cycle [1,-1]) vieta where
|
||||
-- Group by degree of subsequence
|
||||
elemSyms = groupBy ((==) `on` length) . sortOn length . subsequences
|
||||
-- Vieta's formulas over xs
|
||||
vieta = map (sum . map product) $ elemSyms xs
|
||||
|
||||
-- Make a polynomial from the powers of the companion matrix of p (mod b)
|
||||
companionPowerPoly b p = fmap (fmap (`mod` b)) . rootsToPoly .
|
||||
map ((matrixPowersMod b (companion p) !!) . (+(-1)))
|
||||
|
||||
|
||||
-- Show a polynomial over matrices
|
||||
showPolyMat :: (Show a, Num a, Eq a) => Polynomial (Matrix a) -> String
|
||||
showPolyMat = intercalate " + " . showCoeffs where
|
||||
showCoeffs = zipWith showCoeff [0..] . map showMatrix . coeffs
|
||||
-- Show the indeterminate as "X"
|
||||
showCoeff 0 x = x
|
||||
showCoeff 1 x = x ++ "X"
|
||||
showCoeff n x = x ++ "X^{" ++ show n ++ "}"
|
||||
-- Show identity matrices (but not their multiples) as "I"
|
||||
showMatrix x
|
||||
| x `elem` [eye 1, eye $ mSize x] = "I"
|
||||
| x `elem` [zero 1, zero $ mSize x] = "0"
|
||||
| otherwise = texifyMatrix x
|
||||
mSize = (+1) . snd . snd . bounds . unMat
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++
|
||||
"\\hat{R}(X) &= (X - (C_r)^1)(X - (C_r)^2)(X - (C_r)^4)" ++
|
||||
" \\\\ " ++
|
||||
" &= " ++ showPolyMat (companionPowerPoly 2 rPoly [1,2,4]) ++
|
||||
" \\\\[10pt] " ++
|
||||
"\\hat{Q}(X) &= (X - (C_r)^3)(X - (C_r)^5)(X - (C_r)^6)" ++
|
||||
" \\\\ " ++
|
||||
" &= " ++ showPolyMat (companionPowerPoly 2 rPoly [3,5,6]) ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
We could have factored our polynomials differently if we used *C*~*q*~ instead.
|
||||
However, the effect of splitting both polynomials into monomial factors is the same.
|
||||
|
||||
|
||||
GF(16)
|
||||
------
|
||||
|
||||
GF(8) is simple to study, but too simple to study the sequence of characteristic polynomials alone.
|
||||
Let's widen our scope to GF(16).
|
||||
There are three irreducible polynomials of degree 4 over GF(2).
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- First (and only) three polynomials of degree 4
|
||||
sPoly:tPoly:uPoly:_ = irrsOfDegree 4 2
|
||||
|
||||
markdown $ "$$\\begin{gather*}" ++
|
||||
texPolyAndMatrix 2 sPoly "s" ++ "\\\\" ++
|
||||
texPolyAndMatrix 2 tPoly "t" ++ "\\\\" ++
|
||||
texPolyAndMatrix 2 uPoly "u" ++
|
||||
"\\end{gather*}$$"
|
||||
```
|
||||
|
||||
Again, *s* and *t* form a pair under the reversal of their bit strings, while *u* is palindromic.
|
||||
Both *C*~*s*~ and *C*~*t*~ are cyclic of order 15, so *s* and *t* are primitive polynomials.
|
||||
Using *s* = ~2~19 to generate the field, the powers of its companion matrix *C*~*s*~
|
||||
have the following characteristic polynomials:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| classes: plain
|
||||
|
||||
sPolyCharPowers = charPolyPows 2 sPoly
|
||||
|
||||
-- Horizontal table of entries
|
||||
fromIndices ns = columns (\(_, f) r -> f r) (\(c, _) -> Headed c) $
|
||||
map (\i -> (show i, (!! i))) ns
|
||||
fromIndices' = (singleton (Headed "m") head <>) . fromIndices
|
||||
|
||||
-- Symbolic representation of a power of a companion matrix (in Markdown)
|
||||
compPowSymbolic "" m = "*f*(*C*^*" ++ m ++ "*^)"
|
||||
compPowSymbolic x m = "*f*((*C*~*" ++ x ++ "*~)^*" ++ m ++ "*^)"
|
||||
|
||||
-- Spans of a given color
|
||||
spanColor color = (("<span style=\"color: " ++ color ++ "\">") ++) . (++ "</span>")
|
||||
|
||||
markdown $ markdownTable (fromIndices' [1..15]) [
|
||||
compPowSymbolic "s" "m":
|
||||
map ((
|
||||
\x -> if x == 19 then spanColor "blue" (show x)
|
||||
else if x == 25 then spanColor "red" (show x)
|
||||
else show x
|
||||
) . evalPoly 2) sPolyCharPowers
|
||||
]
|
||||
```
|
||||
|
||||
The polynomial ~2~19 occurs at positions 1, 2, 4, and 8.
|
||||
These are obviously powers of 2, the characteristic of the field.
|
||||
Similarly, the polynomial *t* = ~2~25 occurs at positions 14 (= 15 - 1), 13 (= 15 - 2),
|
||||
11 (= 15 - 4), and 7 (= 15 - 8).
|
||||
We'd get the same sequence backwards if we used *C*~*t*~ instead, just like in GF(8).
|
||||
|
||||
|
||||
### Non-primitive
|
||||
|
||||
The polynomial *u* = ~2~31 occurs at positions 3, 6, 9, and 12
|
||||
-- multiples of 3, which is a factor of *15*.
|
||||
It follows that the roots of *u* are cyclic of order 5, so this polynomial is irreducible,
|
||||
but *not* primitive.
|
||||
|
||||
Naturally, *u* (or a polynomial isomorphic to it) can be factored as powers of (*C*~*s*~)^3^.
|
||||
We can also factor it more naively as powers of *C*~*u*~.
|
||||
Either way, we get the same sequence.
|
||||
|
||||
:::: {layout-ncol="2" layout-valign="center"}
|
||||
::: {}
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| classes: plain
|
||||
|
||||
-- Get every entry of an (infinite) list which is a multiple of n
|
||||
entriesEvery n = map head . unfoldr (Just . splitAt n)
|
||||
|
||||
markdown $ markdownTable (fromIndices' [1..5]) [
|
||||
compPowSymbolic "s" "3m":
|
||||
map (show . evalPoly 2)
|
||||
(entriesEvery 3 $ drop 2 sPolyCharPowers),
|
||||
compPowSymbolic "u" "m":
|
||||
map (show . evalPoly 2) (charPolyPows 2 uPoly)
|
||||
]
|
||||
```
|
||||
|
||||
Both of the matrices in column 5 happen to be the identity matrix.
|
||||
It follows that this root is only cyclic of order 5.
|
||||
|
||||
The polynomials ~2~19 and ~2~25 are reversals of one another, and the sequences that their companion matrices
|
||||
generate end one with another -- in this regard, they are dual.
|
||||
However, ${}_2 31 = 11111_x$ is a palindrome and its sequence ends where it begins, so it is self-dual.
|
||||
:::
|
||||
|
||||
::: {width = "33%"}
|
||||
$$
|
||||
\begin{gather*}
|
||||
(C_u)^1 =\left( \begin{matrix}
|
||||
0 & 1 & 0 & 0 \\
|
||||
0 & 0 & 1 & 0 \\
|
||||
0 & 0 & 0 & 1 \\
|
||||
1 & 1 & 1 & 1
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
(C_u)^2 =\left( \begin{matrix}
|
||||
0 & 0 & 1 & 0 \\
|
||||
0 & 0 & 0 & 1 \\
|
||||
1 & 1 & 1 & 1 \\
|
||||
1 & 0 & 0 & 0
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
(C_u)^3 =\left( \begin{matrix}
|
||||
0 & 0 & 0 & 1 \\
|
||||
1 & 1 & 1 & 1 \\
|
||||
1 & 0 & 0 & 0 \\
|
||||
0 & 1 & 0 & 0
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
(C_u)^4 =\left( \begin{matrix}
|
||||
1 & 1 & 1 & 1 \\
|
||||
1 & 0 & 0 & 0 \\
|
||||
0 & 1 & 0 & 0 \\
|
||||
0 & 0 & 1 & 0 \\
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
(C_u)^5 =\left( \begin{matrix}
|
||||
1 & 0 & 0 & 0 \\
|
||||
0 & 1 & 0 & 0 \\
|
||||
0 & 0 & 1 & 0 \\
|
||||
0 & 0 & 0 & 1 \\
|
||||
\end{matrix} \right)
|
||||
\\
|
||||
= I = (C_u)^0
|
||||
\end{gather*}
|
||||
$$
|
||||
:::
|
||||
::::
|
||||
|
||||
|
||||
### Non-irreducible
|
||||
|
||||
In addition to the three irreducibles, a fourth polynomial, ${}_2 21 \sim 10101_x$,
|
||||
also appears in the sequence on entries 5 and 10 -- multiples of 5, which is also a factor of 15.
|
||||
Like ~2~31, this polynomial is palindromic.
|
||||
This polynomial is *not* irreducible mod 2, and factors as:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
poly21 = asPoly 2 21
|
||||
|
||||
markdown $ "$$\\begin{gather*}" ++
|
||||
texPolyAsPositional poly21 ++ " = " ++ texifyPoly poly21 ++
|
||||
" = \\left( 1 + x + x^2 \\right)^2 \\mod 2" ++
|
||||
" \\\\[10pt] " ++
|
||||
"(X - (C_s)^5)(X - (C_s)^{10}) = " ++
|
||||
showPolyMat (companionPowerPoly 2 sPoly [5,10]) ++
|
||||
"\\end{gather*}$$"
|
||||
```
|
||||
|
||||
Just like how the fields we construct are powers of a prime, this extra element is a power
|
||||
of a smaller irreducible.
|
||||
This is unexpected, but perhaps not surprising.
|
||||
|
||||
Something a little more surprising is that the companion matrix is cyclic of degree *6*,
|
||||
rather than of degree 3 like the matrices encountered in GF(8).
|
||||
The powers of its companion matrix are:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| classes: plain
|
||||
|
||||
companion21Pows = matrixPowersMod 2 (companion poly21)
|
||||
|
||||
markdown $ markdownTable (fromIndices' [1..6]) [
|
||||
compPowSymbolic "s" "5m":
|
||||
map (show . evalPoly 2) (entriesEvery 5 $ drop 4 sPolyCharPowers),
|
||||
compPowSymbolic "21" "m":
|
||||
map (\x -> let
|
||||
p = (`mod` 2) <$> charpoly x
|
||||
p' = evalPoly 2 p
|
||||
comp21 = head companion21Pows
|
||||
in
|
||||
-- x shares its characteristic polynomial with the identity
|
||||
if p' == 17 then
|
||||
show p' ++
|
||||
(if x == eye 4 then " (identity)" else " (*Not* the identity)")
|
||||
-- x is either the companion matrix of the polynomial 21 or its inverse
|
||||
else if x == comp21 ||
|
||||
eye 4 == ((`mod` 2) <$> x * comp21) then
|
||||
spanColor "red" $ show p'
|
||||
else spanColor "blue" $ show p')
|
||||
companion21Pows
|
||||
]
|
||||
```
|
||||
|
||||
We can think of the repeated sequence as ensuring that there are enough roots of ~2~21.
|
||||
The Fundamental Theorem of Algebra states that there must be 4 roots.
|
||||
For *numbers*, we'd allow duplicate roots with multiplicities greater than 1,
|
||||
but the matrix roots are all distinct.
|
||||
|
||||
Basic group theory tells us that as a cyclic group, the matrix's first and fifth powers
|
||||
(in red) are pairs of inverses.
|
||||
The constant term of the characteristic polynomial is the product of all four roots and,
|
||||
as a polynomial over matrices, must be some nonzero multiple of the identity matrix.
|
||||
Since the red roots are a pair of inverses, the blue roots are, too.
|
||||
|
||||
|
||||
GF(32)
|
||||
------
|
||||
|
||||
GF(32) turns out to be special.
|
||||
There are six irreducible polynomials of degree 5 over GF(2).
|
||||
Picking the "smallest" at random, ~2~37, and looking at the polynomial sequence it generates, we see:
|
||||
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| classes: plain
|
||||
|
||||
-- Get all degree 5 polynomials over GF(2)
|
||||
deg5Char2Polys = irrsOfDegree 5 2
|
||||
leastDeg5Char2Poly = head deg5Char2Polys
|
||||
|
||||
colorByEval b ps x = (maybe show (. show) . getColor <*> id) $ evalPoly b x where
|
||||
getColor = flip lookup $ map (fmap spanColor) ps
|
||||
|
||||
colorDeg5Char2 = colorByEval 2 [
|
||||
(37, "red"),
|
||||
(47, "orange"),
|
||||
(55, "yellow"),
|
||||
(41, "green"),
|
||||
(61, "blue"),
|
||||
(59, "purple")
|
||||
]
|
||||
|
||||
markdown $ markdownTable (fromIndices' [1..16]) [
|
||||
compPowSymbolic "" "m":
|
||||
map colorDeg5Char2 (charPolyPows 2 leastDeg5Char2Poly)
|
||||
]
|
||||
markdown $ markdownTable (fromIndices' [17..31]) [
|
||||
compPowSymbolic "" "m":
|
||||
map colorDeg5Char2 (charPolyPows 2 leastDeg5Char2Poly)
|
||||
]
|
||||
```
|
||||
|
||||
31 is prime, so we don't have any sub-patterns that appear on multiples of factors.
|
||||
In fact, all six irreducible polynomials are present in this table.
|
||||
The pairs in complementary colors form pairs under reversing the polynomials:
|
||||
<span style="color: red">~2~37</span> and <span style="color: green">~2~41</span>,
|
||||
<span style="color: blue">~2~61</span> and <span style="color: orange">~2~47</span>,
|
||||
and <span style="color: yellow">~2~55</span> and <span style="color: purple">~2~59</span>.
|
||||
|
||||
Since their roots have order 31, these polynomials are actually
|
||||
the distinct factors of *x*^31^ - 1 mod 2:
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
x^{31} -1 = (x-1)(x^{30} +x^{29} + ... + x + 1)
|
||||
\\
|
||||
(x^{30} +x^{29} + ... + x + 1) =
|
||||
\left\{ \begin{align*}
|
||||
&\phantom\cdot (x^5 + x^2 + 1) &\sim \quad {}_2 37
|
||||
\\
|
||||
&\cdot (x^5 + x^3 + 1) &\sim \quad {}_2 41 \\
|
||||
&\cdot (x^5 + x^4 + x^3 + x^2 + 1) &\sim \quad {}_2 61
|
||||
\\
|
||||
&\cdot (x^5 + x^3 + x^2 + x + 1) &\sim \quad {}_2 47
|
||||
\\
|
||||
&\cdot (x^5 + x^4 + x^2 + x + 1) &\sim \quad {}_2 55
|
||||
\\
|
||||
&\cdot (x^5 + x^4 + x^3 + x + 1) &\sim \quad {}_2 59
|
||||
\end{align*} \right.
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
This is a feature special to fields of characteristic 2.
|
||||
2 is the only prime number whose powers can be one more than another prime,
|
||||
since all other prime powers are one more than even numbers.
|
||||
31 is a [Mersenne prime](https://en.wikipedia.org/wiki/Mersenne_prime),
|
||||
so all integers less than 31 are coprime to it.
|
||||
Thus, there is no room for the "extra" entries we observed in GF(16) which occurred
|
||||
on factors of 15 = 16 - 1.
|
||||
No entry can be irreducible (but not primitive) or the power of an irreducible of lower degree.
|
||||
In other words, *only primitive polynomials exist of degree* p *if 2^p^ - 1 is a Mersenne prime*.
|
||||
|
||||
|
||||
### Counting Irreducibles
|
||||
|
||||
The remark about coprimes to 31 may inspire you to think of the
|
||||
[totient function](https://en.wikipedia.org/wiki/Euler%27s_totient_function).
|
||||
We have *φ*(2^5^ - 1) = 30 = 5⋅6, where 5 is the degree and 6 is the number of primitive polynomials.
|
||||
We also have *φ*(24 - 1) = 8 = 4⋅2 and *φ*(23 - 1) = 6 = 3⋅2.
|
||||
In general, it is true that there are *φ*(*pm* - 1) / *m* primitive polynomials of degree m over GF(p).
|
||||
|
||||
|
||||
Polynomial Reversal
|
||||
-------------------
|
||||
|
||||
We've only been looking at fields of characteristic 2, where the meaning of
|
||||
"palindrome" and "reversed polynomial" is intuitive.
|
||||
Let's look at an example over characteristic 3.
|
||||
One primitive of degree 2 is ~3~14, which gives rise to the following sequence over GF(9):
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| classes: plain
|
||||
|
||||
-- Get all degree 2 polynomials over GF(3)
|
||||
deg2Char3Polys = map fst $ takeWhile ((==3) . snd) $ dropWhile ((/=3) . snd) $
|
||||
map ((,) <*> length . coeffs) $ irreducibles 3
|
||||
leastDeg2Char3Poly = deg2Char3Polys !! 1
|
||||
|
||||
colorDeg3Char3 = colorByEval 3 [(14, "red"), (17, "blue")]
|
||||
|
||||
markdown $ markdownTable (fromIndices' [1..8]) [
|
||||
compPowSymbolic "14" "m":
|
||||
map colorDeg3Char3 (charPolyPows 3 leastDeg2Char3Poly)
|
||||
]
|
||||
```
|
||||
|
||||
The table suggests that ${}_3 14 = 112_x = x^2 + x + 2$ and ${}_3 17 = 122_x = x^2 + 2x + 2$
|
||||
are reversals of one another.
|
||||
More naturally, you'd think that 112~*x*~ reversed is 211~*x*~.
|
||||
But remember that we prefer to work with monic polynomials.
|
||||
By multiplying the polynomial by the multiplicative inverse of the leading coefficient (in this case, 2),
|
||||
we get $422_x \equiv 122_x \mod 3$.
|
||||
This is a rule that applies over larger characteristics in general.
|
||||
|
||||
Note that ${}_3 16 \sim 121_x = x^2 + 2x + 1$ and ${}_3 13 \sim 111_x = x^2 + x + 1 = x^2 - 2x + 1$,
|
||||
both of which have factors over GF(3).
|
||||
|
||||
|
||||
Irreducible Graphs
|
||||
------------------
|
||||
|
||||
We can study the interplay of primitives, irreducibles, and their powers by converting
|
||||
our sequences into (directed) graphs.
|
||||
Each node in the graph will represent an irreducible polynomial over the field.
|
||||
Call the one under consideration *a*.
|
||||
If the sequence of characteristic polynomials generated by powers of *C*~*a*~ contains
|
||||
contains another polynomial *b*, then there is an edge from *a* to *b*.
|
||||
|
||||
```{haskell}
|
||||
-- Convert a polynomial to the integer representing it in characteristic n
|
||||
asPolyNum n = evalPoly n . fmap (`mod` n)
|
||||
|
||||
irreducibleGraph d n = concatMap (\(x:xs) -> map (x,) xs) polyKinClasses where
|
||||
-- All irreducible polynomials of degree d in characteristic n
|
||||
irrsOfDegree' = irrsOfDegree d n
|
||||
-- Get "kin" polynomials as integers -- all those who appear as characteristic
|
||||
-- polynomials in the powers of its companion matrix
|
||||
getKinPolys = map (asPolyNum n . charpoly) . matrixPowersMod n . companion
|
||||
-- Kin classes corresponding to each irreducible polynomial,
|
||||
-- which is the first entry
|
||||
polyKinClasses = map (nub . take (n^d) . getKinPolys) irrsOfDegree'
|
||||
```
|
||||
|
||||
We can do this for every GF(*p*^*m*^).
|
||||
Let's start with the first few fields of characteristic 2.
|
||||
We get the following graphs:
|
||||
|
||||

|
||||
|
||||
All nodes connect to the node corresponding to the identity matrix, since all roots are cyclic.
|
||||
Also, since all primitive polynomials are interchangeable with one another,
|
||||
they are all interconnected and form a [complete](https://en.wikipedia.org/wiki/Complete_graph) clique.
|
||||
This means that, excluding the identity node, the graphs for fields of order one more
|
||||
than a Mersenne prime are just the complete graphs.
|
||||
|
||||
Since all of the graphs share the identity node as a feature
|
||||
-- a node with incoming edges from every other node -- its convenient to omit it.
|
||||
Here are a few more of these graphs after doing so, over fields of other characteristics:
|
||||
|
||||
::: {layout="[[1,1], [1,1], [1,1,1]]"}
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| fig-cap: "GF(9)"
|
||||
|
||||
-- Characteristic polynomial of the identity matrix
|
||||
eyePoly d n = asPolyNum n $ charpoly $ eye d
|
||||
-- Remove edges directed toward the characteristic polynomial of the identity
|
||||
irreducibleGraphNoEye d n = filter ((/=eyePoly d n) . snd) $ irreducibleGraph d n
|
||||
|
||||
-- Only plot the graph for GF(9), since the others take too long to render
|
||||
plotDigraph $ irreducibleGraphNoEye 2 3
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
:::
|
||||
|
||||
<!-- This code cell is not evaluated because it takes too long -->
|
||||
```{haskell}
|
||||
--| eval: false
|
||||
--| echo: false
|
||||
--| layout: [[1,1], [1,1], [1,1,1]]
|
||||
--| fig-cap:
|
||||
--| - GF(9)
|
||||
--| - GF(25)
|
||||
--| - GF(49)
|
||||
--| - GF(121)
|
||||
--| - GF(27)
|
||||
--| - GF(125)
|
||||
--| - GF(343)
|
||||
|
||||
plotDigraph $ irreducibleGraphNoEye 2 3
|
||||
plotDigraph $ irreducibleGraphNoEye 2 5
|
||||
plotDigraph $ irreducibleGraphNoEye 2 7
|
||||
plotDigraph $ irreducibleGraphNoEye 2 11
|
||||
plotDigraph $ irreducibleGraphNoEye 3 3
|
||||
plotDigraph $ irreducibleGraphNoEye 3 5
|
||||
plotDigraph $ irreducibleGraphNoEye 3 7
|
||||
```
|
||||
|
||||
|
||||
### Spectra
|
||||
|
||||
Again, since visually interpreting graphs is difficult, we can study an invariant.
|
||||
From these graphs of polynomials, we can compute *their* characteristic polynomials
|
||||
(to add another layer to this algebraic cake) and look at their spectra.
|
||||
|
||||
It turns out that a removing a fully-connected node (like the one for the identity matrix)
|
||||
has a simple effect on characteristic polynomial of a graph: it just removes a factor of *x*.
|
||||
Here are a few of the (identity-reduced) spectra, arranged into a table.
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| classes: plain
|
||||
|
||||
-- Not technically correct, but enough for this example
|
||||
edgesToAdjacency [] = toMatrix [[0]]
|
||||
edgesToAdjacency es = Mat asArray where
|
||||
-- Vertices from the edge list
|
||||
vs = nub $ es >>= (\(x,y) -> [x,y])
|
||||
-- Lookup table for new vertices
|
||||
vs' = zip vs [0..]
|
||||
-- Largest vertex index for array bounds
|
||||
b = maximum $ map snd vs'
|
||||
-- Lookup function for new edge reindexing
|
||||
lookupVs = fromJust . flip lookup vs'
|
||||
-- List of reindexed edges
|
||||
reindexed = map (bimap lookupVs lookupVs) es
|
||||
-- Use a list of 2-tuples to set addresses in a matrix to 1
|
||||
asArray = listArray ((0,0),(b,b)) (repeat 0) // map (, 1) reindexed
|
||||
|
||||
-- Find roots of `p` by trial dividing the entries of `xs`
|
||||
findRootsFrom xs p = fmap head $ partitionEithers $ recurse p xs where
|
||||
-- We only need to test roots we haven't failed to divide
|
||||
tails [] = []
|
||||
tails x@(_:xs) = x:tails xs
|
||||
-- Try dividing `p` by every remaining 'integer' root from xs
|
||||
trialDivisions p xs = map (\x -> (x, p `synthDiv` Poly [-head x, 1])) $ tails xs
|
||||
-- Find the first root which has a zero remainder, or Nothing if none exists
|
||||
firstRoot p xs = listToMaybe $ dropWhile ((/= 0) . snd . snd) $
|
||||
trialDivisions p xs
|
||||
-- We either found a root (r) and need to recurse with the quotient (q)
|
||||
-- Or we couldn't find a root, and terminate with the number of unfound roots
|
||||
recurse p xs = case firstRoot p xs of
|
||||
(Just (next@(r:_), (q,_))) -> Left r:recurse q next
|
||||
_ -> [Right $ length (coeffs p) - 1]
|
||||
|
||||
-- Show the spectrum
|
||||
showSpectrum (xs, y) = intercalate ", " showMults ++ showMissing where
|
||||
showMults = map showMult (rle Nothing xs)
|
||||
-- Markdown notation for a root x repeated y times
|
||||
showMult (x,y) = show x ++ "^" ++ show y ++ "^"
|
||||
showMissing = if y == 0 then "" else " " ++ show y ++ " other roots"
|
||||
-- Run-length encode a list to a list containing (original entry, count)
|
||||
rle Nothing [] = []
|
||||
rle (Just x) [] = [x]
|
||||
rle Nothing (x:xs) = rle (Just (x, 1)) xs
|
||||
rle (Just (y, c)) (x:xs)
|
||||
| x == y = rle (Just (y, c+1)) xs
|
||||
| otherwise = (y, c):rle (Just (x, 1)) xs
|
||||
|
||||
-- Characteristic, degree, remark
|
||||
data CharGraphRow = CGR {
|
||||
cgrCharacteristic :: Int,
|
||||
cgrDegree :: Int,
|
||||
cgrRemark :: String
|
||||
}
|
||||
|
||||
charGraphTable = columns (\(_, f) r -> f r) (\(c, _) -> Headed c) [
|
||||
("Characteristic", \(CGR n d _) -> if d == 2 then show n else ""),
|
||||
("Order", show . \(CGR n d _) -> n^d),
|
||||
("Spectrum", \(CGR n d _) -> showSpectrum $ findRootsFrom [-1..35] $
|
||||
charpoly $ fmap fromIntegral $ edgesToAdjacency $
|
||||
irreducibleGraphNoEye d n),
|
||||
("Remark", cgrRemark)
|
||||
]
|
||||
|
||||
markdown $ markdownTable charGraphTable [
|
||||
CGR 2 2 "",
|
||||
CGR 2 3 "Mersenne",
|
||||
CGR 2 4 "",
|
||||
CGR 2 5 "Mersenne",
|
||||
CGR 3 2 "",
|
||||
CGR 3 3 "Pseudo-Mersenne?",
|
||||
CGR 5 2 "",
|
||||
CGR 5 3 "Prime power in spectrum",
|
||||
CGR 7 2 "",
|
||||
CGR 7 3 "Composite in spectrum",
|
||||
CGR 11 2 "Composite in spectrum"
|
||||
]
|
||||
```
|
||||
|
||||
Incredibly, all spectra shown are composed exclusively of integers, and thus,
|
||||
each of these graphs are integral graphs.
|
||||
Moreover, it does not appear that any integer sequences that one may try extracting from this table
|
||||
(for example, the multiplicity of -1) can be found in the
|
||||
[Online Encyclopedia of Integer Sequences](https://oeis.org/).
|
||||
|
||||
From what I was able to tell, the following subgraphs were *also* integral over the range I tested:
|
||||
|
||||
- the induced subgraph of vertices corresponding to non-primitives
|
||||
- the complement of the previous graph with respect to the whole graph
|
||||
- the induced subgraph of vertices corresponding only to irreducibles
|
||||
|
||||
Unfortunately, proving any such relationship is out of the scope of this post (and my abilities).
|
||||
|
||||
|
||||
Closing
|
||||
-------
|
||||
|
||||
This concludes the first foray into using matrices as elements of prime power fields.
|
||||
It is a subject which, using the tools of linear algebra, makes certain aspects of field theory
|
||||
more palatable and constructs some objects with fairly interesting properties.
|
||||
|
||||
One of the most intriguing parts to me is the sequence of polynomials generated by a companion matrix.
|
||||
Though I haven't proven it, I suspect that it suffices to study only the sequence generated
|
||||
by a primitive polynomial.
|
||||
It seems to be possible to get the non-primitive sequences by looking at the subsequences
|
||||
where the indices are multiples of a factor of the length of the sequence.
|
||||
But this means that the entire story about polynomials and finite fields can be foregone entirely,
|
||||
and the problem instead becomes one of number theory.
|
||||
|
||||
This post has [an addendum](./extra/) to it which discusses some additional notes about matrix roots and the
|
||||
Cayley-Hamilton theorem.
|
||||
The [next post](../3/) will focus on an "application" of matrix roots to other areas of abstract algebra.
|
||||
Diagrams made with Geogebra and NetworkX (GraphViz).
|
||||
BIN
posts/math/finite-field/2/irreducibles-graphs/irred_121.png
(Stored with Git LFS)
Normal file
BIN
posts/math/finite-field/2/irreducibles-graphs/irred_125.png
(Stored with Git LFS)
Normal file
BIN
posts/math/finite-field/2/irreducibles-graphs/irred_25.png
(Stored with Git LFS)
Normal file
BIN
posts/math/finite-field/2/irreducibles-graphs/irred_27.png
(Stored with Git LFS)
Normal file
BIN
posts/math/finite-field/2/irreducibles-graphs/irred_343.png
(Stored with Git LFS)
Normal file
BIN
posts/math/finite-field/2/irreducibles-graphs/irred_49.png
(Stored with Git LFS)
Normal file
1
posts/math/finite-field/3/Previous.hs
Symbolic link
@ -0,0 +1 @@
|
||||
../2/Previous.hs
|
||||