mirror of
https://github.com/paboyle/Grid.git
synced 2026-08-06 02:43:29 +01:00
Compare commits
2329
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01c3189d17 | ||
|
|
f1249685aa | ||
|
|
722d68b0d9 | ||
|
|
c1284e080a | ||
|
|
d2189d59e0 | ||
|
|
e862dd662b | ||
|
|
42ff77cec8 | ||
|
|
d11ad4d2f0 | ||
|
|
9ebf2fec79 | ||
|
|
ab0d6d3706 | ||
|
|
4aa96a16c5 | ||
|
|
bb99123e6d | ||
|
|
9e8cba3944 | ||
|
|
d5cab295b2 | ||
|
|
5a67f0602c | ||
|
|
d57c49d73d | ||
|
|
5754c965d5 | ||
|
|
64ee6e90c9 | ||
|
|
7752e7ead1 | ||
|
|
f14be5e331 | ||
|
|
5a77a4a052 | ||
|
|
83b752bdd3 | ||
|
|
34a6ba3475 | ||
|
|
819f706778 | ||
|
|
3b28e3c98f | ||
|
|
a696953485 | ||
|
|
aecc50869c | ||
|
|
99be36c891 | ||
|
|
af1fb41d55 | ||
|
|
b4c77ca762 | ||
|
|
82a8f1342e | ||
|
|
d4b685cf2d | ||
|
|
84707cc3a8 | ||
|
|
88ea24687f | ||
|
|
167f94e86c | ||
|
|
09aa843984 | ||
|
|
24752002fa | ||
|
|
f3223021fd | ||
|
|
4e1d95d3bb | ||
|
|
2ed38f63ca | ||
|
|
80d2a8d88d | ||
|
|
595ceaac37 | ||
|
|
daf5834e8e | ||
|
|
2ac5431401 | ||
|
|
0d8658a039 | ||
|
|
095e004d01 | ||
|
|
0acabee7f6 | ||
|
|
76fbcffb60 | ||
|
|
a0a62d7ead | ||
|
|
c5038ea6a5 | ||
|
|
a5120903eb | ||
|
|
00b286a08a | ||
|
|
24a9759353 | ||
|
|
1b56f6f46d | ||
|
|
2a8084d569 | ||
|
|
6ff29f9d4f | ||
|
|
c4d3e79193 | ||
|
|
3e71cac7ae | ||
|
|
e8e7ef08fc | ||
|
|
5c00fe6bef | ||
|
|
3175788f97 | ||
|
|
6f1788bb38 | ||
|
|
7cd3f21e6b | ||
|
|
dcda74f924 | ||
|
|
df4c2a082b | ||
|
|
88611659a3 | ||
|
|
504b85dfc0 | ||
|
|
43ea83e5e1 | ||
|
|
376150c3df | ||
|
|
842e0391e7 | ||
|
|
17e3799bcc | ||
|
|
985ab70f85 | ||
|
|
1e85081986 | ||
|
|
3876fe5a29 | ||
|
|
6692425aa2 | ||
|
|
d5ac4fc67f | ||
|
|
3538faf449 | ||
|
|
4a0aaf0786 | ||
|
|
9c3835524c | ||
|
|
549351bb8a | ||
|
|
b650b89682 | ||
|
|
74e6b19f83 | ||
|
|
2e684028de | ||
|
|
0b457b9d52 | ||
|
|
fe0ab5f1a9 | ||
|
|
caa66418bd | ||
|
|
c54d87a472 | ||
|
|
4304245c1b | ||
|
|
786496f22e | ||
|
|
68af1bba67 | ||
|
|
bf2a715ef7 | ||
|
|
4042ebf1bf | ||
|
|
6165931afa | ||
|
|
1d1fd3bcaf | ||
|
|
82f35001ff | ||
|
|
fa30c791aa | ||
|
|
612049f1dd | ||
|
|
0b92ef990c | ||
|
|
82d411ca7b | ||
|
|
597086a031 | ||
|
|
b210ddf9a7 | ||
|
|
c5d02e5799 | ||
|
|
9dcd7ca761 | ||
|
|
23581333e6 | ||
|
|
e5fa3d887f | ||
|
|
583fa7bb0a | ||
|
|
fe0db53842 | ||
|
|
76c0ada1e1 | ||
|
|
92f49e9194 | ||
|
|
44c8057b5f | ||
|
|
0ad837f595 | ||
|
|
bd2103c746 | ||
|
|
9c18d2ddb0 | ||
|
|
1245a8c151 | ||
|
|
07113dc8ba | ||
|
|
c1e5ef9476 | ||
|
|
6fd71aea9d | ||
|
|
a18b0d496c | ||
|
|
a3420e6fa9 | ||
|
|
732836d9f8 | ||
|
|
87658f7b53 | ||
|
|
19f0737b98 | ||
|
|
16d3c9cf75 | ||
|
|
3b9fc72451 | ||
|
|
99644f5d0a | ||
|
|
e7f51e5fb1 | ||
|
|
1ce5f70dd1 | ||
|
|
473635f401 | ||
|
|
5adf2657dd | ||
|
|
82cfff2990 | ||
|
|
4397b1c442 | ||
|
|
9e6a4a4737 | ||
|
|
7780d88d26 | ||
|
|
2bf9179d2c | ||
|
|
c606f5dca0 | ||
|
|
632f5916c7 | ||
|
|
9057694895 | ||
|
|
5e85aef19d | ||
|
|
2b6d40c7e1 | ||
|
|
33b80c4e8e | ||
|
|
41f344bbd3 | ||
|
|
8419cc5c64 | ||
|
|
a77cd50b2f | ||
|
|
73af020f98 | ||
|
|
bffb83c46e | ||
|
|
7031f37350 | ||
|
|
829dd74cb2 | ||
|
|
66e671985d | ||
|
|
5afcbcf0f3 | ||
|
|
9730579312 | ||
|
|
bfae14d035 | ||
|
|
b78fc73d19 | ||
|
|
709f8ae76c | ||
|
|
7aa06329d0 | ||
|
|
9d6a38c44c | ||
|
|
6ec5cee368 | ||
|
|
f2e9a68825 | ||
|
|
d88750e6b6 | ||
|
|
821358eda7 | ||
|
|
fce6e1f135 | ||
|
|
8f0bb3e676 | ||
|
|
262c70d967 | ||
|
|
da43ef7c2d | ||
|
|
7b60ab5df1 | ||
|
|
f6b961a64e | ||
|
|
f1ed988aa3 | ||
|
|
eea51bb604 | ||
|
|
9203126aa5 | ||
|
|
f90ba4712a | ||
|
|
3737a24096 | ||
|
|
d418f78352 | ||
|
|
25163998a0 | ||
|
|
dc546aaa4b | ||
|
|
5364d580c9 | ||
|
|
2a9a6347e3 | ||
|
|
cfdb56f314 | ||
|
|
b517e88db3 | ||
|
|
bb317aba8d | ||
|
|
644cc6647e | ||
|
|
72397ce23b | ||
|
|
c646d91527 | ||
|
|
a2b98d82e1 | ||
|
|
7b9415c088 | ||
|
|
cb7110f492 | ||
|
|
0c7af66490 | ||
|
|
496d1b914a | ||
|
|
d60a80c098 | ||
|
|
bb8b6d9d73 | ||
|
|
2cc6deb8e0 | ||
|
|
19d0590579 | ||
|
|
677b4cc5b0 | ||
|
|
be565ffab6 | ||
|
|
df6120e5f6 | ||
|
|
21de6f7da8 | ||
|
|
dbe39f9ce0 | ||
|
|
ab3de50d5e | ||
|
|
c545bd2139 | ||
|
|
6a1c64fbdd | ||
|
|
b75809ed61 | ||
|
|
ecaf228e5c | ||
|
|
6d015ae8fc | ||
|
|
233150d93f | ||
|
|
7af8c77a52 | ||
|
|
a957e7bfa1 | ||
|
|
cee4c8ce8c | ||
|
|
96bf814d8c | ||
|
|
7ddc422788 | ||
|
|
e652fc2825 | ||
|
|
a49fa3f8d0 | ||
|
|
cd452a2f91 | ||
|
|
4f89f603ae | ||
|
|
11dc2c5e1d | ||
|
|
6fec3c15ca | ||
|
|
938c47480f | ||
|
|
3811d19298 | ||
|
|
83a3ab6b6f | ||
|
|
d66a9af6a3 | ||
|
|
adc90d3a86 | ||
|
|
ebbd015c5c | ||
|
|
4ab73b36b2 | ||
|
|
130e07a422 | ||
|
|
8f47bb367e | ||
|
|
0c3cb60135 | ||
|
|
9eae8fca5d | ||
|
|
882a217074 | ||
|
|
e465fce201 | ||
|
|
d41542c64b | ||
|
|
199818bd6c | ||
|
|
fe66c7ca30 | ||
|
|
e9177e4af3 | ||
|
|
d15a6c5933 | ||
|
|
25ab9325e7 | ||
|
|
19f9378b98 | ||
|
|
785bc7a14f | ||
|
|
1a1fe85428 | ||
|
|
0000d2e558 | ||
|
|
9ffd1ed4ce | ||
|
|
3d014864e2 | ||
|
|
1d22841811 | ||
|
|
a1cdda833f | ||
|
|
ad6db92690 | ||
|
|
e8ff9d8e50 | ||
|
|
795769c636 | ||
|
|
267a39d943 | ||
|
|
3624bd3d22 | ||
|
|
bc12dbbb38 | ||
|
|
eb8a008a8f | ||
|
|
c4d9aa1a21 | ||
|
|
6ae809ed40 | ||
|
|
311e2aab3f | ||
|
|
438dfbdb83 | ||
|
|
b2ce760cf4 | ||
|
|
b1ba209696 | ||
|
|
cb3e529b1e | ||
|
|
717f647418 | ||
|
|
98e7418187 | ||
|
|
fe05bf48b1 | ||
|
|
d2dd8f54e2 | ||
|
|
7726ee4b16 | ||
|
|
ba9bbe0221 | ||
|
|
4c3dd82d84 | ||
|
|
44e911b5b7 | ||
|
|
a7a16df9d0 | ||
|
|
382e0abefd | ||
|
|
6fdefe5b90 | ||
|
|
4788dd8e2e | ||
|
|
1cc5f221f3 | ||
|
|
93251bfba0 | ||
|
|
18b79508b8 | ||
|
|
4de5ed1613 | ||
|
|
0baaddbe98 | ||
|
|
8729c46169 | ||
|
|
09f81fe7c3 | ||
|
|
1876e5b7c0 | ||
|
|
355ec76257 | ||
|
|
b50fb34e71 | ||
|
|
de84d730ff | ||
|
|
c74d11e3d7 | ||
|
|
84cab5e6e7 | ||
|
|
c4fc972fec | ||
|
|
8cf809e231 | ||
|
|
94019a922e | ||
|
|
4f17c8d081 | ||
|
|
aaab753982 | ||
|
|
d6b2727f86 | ||
|
|
74a4f43946 | ||
|
|
1caf8b0f86 | ||
|
|
570b72a47b | ||
|
|
a5798a89ed | ||
|
|
3f3661a86f | ||
|
|
f7e2f9a401 | ||
|
|
2848a9b558 | ||
|
|
d4868991af | ||
|
|
e99d42404e | ||
|
|
3ba019c747 | ||
|
|
47429218bb | ||
|
|
8fe429346f | ||
|
|
5a4f9bf2e3 | ||
|
|
b91fc1b6b4 | ||
|
|
eafc150034 | ||
|
|
2877f1a268 | ||
|
|
1e893af775 | ||
|
|
d9f430a575 | ||
|
|
63abe87f36 | ||
|
|
368d649c8a | ||
|
|
5603464f39 | ||
|
|
655c79f39e | ||
|
|
565b231c03 | ||
|
|
62a9f180fa | ||
|
|
5ae77876a8 | ||
|
|
4ed2c2c74f | ||
|
|
955da582b6 | ||
|
|
11b07b950d | ||
|
|
8f70cfeda9 | ||
|
|
ce64271048 | ||
|
|
5cc4f3241d | ||
|
|
6815e138b4 | ||
|
|
a78a61d76f | ||
|
|
2eff3f34ed | ||
|
|
03687c1d62 | ||
|
|
febfe4e77f | ||
|
|
4d1aa134b5 | ||
|
|
5ec879860a | ||
|
|
f617468e04 | ||
|
|
b728af903c | ||
|
|
54f1999030 | ||
|
|
fd58f0b669 | ||
|
|
c5c67b706e | ||
|
|
be7a543e2c | ||
|
|
68f112d576 | ||
|
|
ec1395a304 | ||
|
|
beb0e474ee | ||
|
|
2b5fdcbbc5 | ||
|
|
295127d456 | ||
|
|
7dcfb13694 | ||
|
|
ee4046fe92 | ||
|
|
2a9cfeb9ea | ||
|
|
1147b8ea40 | ||
|
|
3f9119b39d | ||
|
|
35e8225abd | ||
|
|
bdbfbb7a14 | ||
|
|
f7d4be8d96 | ||
|
|
9fa8bd6438 | ||
|
|
02c8178f16 | ||
|
|
e637fbacae | ||
|
|
066544281f | ||
|
|
11be10d2c0 | ||
|
|
160969a758 | ||
|
|
622f78ebea | ||
|
|
aa67a5b095 | ||
|
|
af9ea0864c | ||
|
|
4e2a6d87c4 | ||
|
|
a465ecece9 | ||
|
|
575eb72182 | ||
|
|
3a973914d6 | ||
|
|
f568c07bbd | ||
|
|
2c9878fc3a | ||
|
|
27b1b1b005 | ||
|
|
130d7ab077 | ||
|
|
29f6b8a74a | ||
|
|
9779aaea33 | ||
|
|
ec25604a67 | ||
|
|
3668e81c5e | ||
|
|
d66b2423cb | ||
|
|
15cc78f0b6 | ||
|
|
06db4ddea2 | ||
|
|
6cfb90e99f | ||
|
|
d8be95a2a3 | ||
|
|
f82702872d | ||
|
|
3752c49ef0 | ||
|
|
fe65fa4988 | ||
|
|
1fe4c205a3 | ||
|
|
d4dc5e0f43 | ||
|
|
77944437ce | ||
|
|
c164bff758 | ||
|
|
aa2e3d954a | ||
|
|
de62b04728 | ||
|
|
d0bdb50f24 | ||
|
|
a8fecbc609 | ||
|
|
557fa483ff | ||
|
|
fc15d55df6 | ||
|
|
53573d7d94 | ||
|
|
bb3c177000 | ||
|
|
a3322b470f | ||
|
|
f8f408e7a9 | ||
|
|
baac1127d0 | ||
|
|
6f1328160c | ||
|
|
04cf902791 | ||
|
|
7a5b1c1a19 | ||
|
|
18d2d7da4a | ||
|
|
b461184797 | ||
|
|
4563b39305 | ||
|
|
c9d5674d5b | ||
|
|
486412635a | ||
|
|
8b23a1546a | ||
|
|
a901e4e369 | ||
|
|
804d9367d4 | ||
|
|
41d8adca95 | ||
|
|
059e8e5bb0 | ||
|
|
b3ee8ded96 | ||
|
|
cf3584ad15 | ||
|
|
a66973163f | ||
|
|
4502a8c8a1 | ||
|
|
9c902e4c2d | ||
|
|
f3eb36adcf | ||
|
|
7c246606c1 | ||
|
|
172c75029e | ||
|
|
6ae52da571 | ||
|
|
4ee9c68053 | ||
|
|
a15b4378a3 | ||
|
|
89fdd7f8dd | ||
|
|
c328be24b7 | ||
|
|
a73dc6dbf4 | ||
|
|
eee2a2657f | ||
|
|
12b8be7cb9 | ||
|
|
63c223ea5d | ||
|
|
2877fb4a2c | ||
|
|
d299c86633 | ||
|
|
6ce52092e8 | ||
|
|
b5926c1d21 | ||
|
|
9563238e9b | ||
|
|
fb9b1d76ca | ||
|
|
1739146599 | ||
|
|
ed20b39ab3 | ||
|
|
284fc05f15 | ||
|
|
07a07b6fa3 | ||
|
|
dc80b08969 | ||
|
|
a49a161f8d | ||
|
|
a6479ca50f | ||
|
|
0e607a55e7 | ||
|
|
8d305df0db | ||
|
|
c4b9f71357 | ||
|
|
394e506aea | ||
|
|
e19b26341b | ||
|
|
cfe1b13225 | ||
|
|
890c5ea1cd | ||
|
|
a87378d3b6 | ||
|
|
832fc08809 | ||
|
|
9a1ad6a5eb | ||
|
|
a90eafad24 | ||
|
|
ad14a82742 | ||
|
|
14e9d8ed9f | ||
|
|
0ac85fa70b | ||
|
|
c371de42b9 | ||
|
|
ccf147d6c1 | ||
|
|
7aa12b446f | ||
|
|
c293228102 | ||
|
|
5c4c9f721a | ||
|
|
057f86c1de | ||
|
|
cd52e3cbc2 | ||
|
|
24602e1259 | ||
|
|
8a098889fc | ||
|
|
5c3ace7c3e | ||
|
|
aa148455b7 | ||
|
|
98cf247f33 | ||
|
|
0cf16522d1 | ||
|
|
7b7c75f9e5 | ||
|
|
aefd255a3c | ||
|
|
1c5aa939fd | ||
|
|
3a0ff17be0 | ||
|
|
47829ae5cc | ||
|
|
bfa7b69aff | ||
|
|
2aaa959b5f | ||
|
|
ce2970b93a | ||
|
|
7b76970d10 | ||
|
|
9fd41882d2 | ||
|
|
ff2ea5de18 | ||
|
|
5147a42818 | ||
|
|
57552d8ca3 | ||
|
|
13713b2a76 | ||
|
|
36a14e4ee3 | ||
|
|
b4cc788b8c | ||
|
|
0f0e7512f3 | ||
|
|
1196b1a161 | ||
|
|
2c8c3be9ee | ||
|
|
5b79d51c22 | ||
|
|
da890dc293 | ||
|
|
93d0a1e73a | ||
|
|
f0a8c7d045 | ||
|
|
db8793777c | ||
|
|
c745484e65 | ||
|
|
da59379612 | ||
|
|
3ef2a41518 | ||
|
|
aa96f420c6 | ||
|
|
49e9e4ed0e | ||
|
|
f7b8163016 | ||
|
|
93769eacd3 | ||
|
|
59b0cc11df | ||
|
|
f32c275376 | ||
|
|
5404fc66ab | ||
|
|
1f53458af8 | ||
|
|
434c3e7f1d | ||
|
|
500b119f3d | ||
|
|
4b87259c1b | ||
|
|
503dec34ef | ||
|
|
d1e9fe50d2 | ||
|
|
d01e5fa838 | ||
|
|
a477c25e8c | ||
|
|
1bd20cd9e8 | ||
|
|
e49e95b037 | ||
|
|
6f59fed563 | ||
|
|
60b7f6c99d | ||
|
|
b92dfcc8d3 | ||
|
|
f6fd6dd053 | ||
|
|
79ad567dd5 | ||
|
|
fab1efb48c | ||
|
|
660eb76d93 | ||
|
|
461cd045c6 | ||
|
|
fee65d7a75 | ||
|
|
31f9971dbf | ||
|
|
62e7bf024a | ||
|
|
95f3d69cf9 | ||
|
|
89c0519f83 | ||
|
|
2704b82084 | ||
|
|
cf8632bbac | ||
|
|
d224297972 | ||
|
|
a4d11a630f | ||
|
|
2b4399f8b1 | ||
|
|
f17b8de907 | ||
|
|
d87296f3e8 | ||
|
|
be94cf1c6f | ||
|
|
cc04dc42dc | ||
|
|
070b61f08f | ||
|
|
7e5bd46dd3 | ||
|
|
228bbb9d81 | ||
|
|
b812a7b4c6 | ||
|
|
891a366f73 | ||
|
|
10116b3be8 | ||
|
|
a46a0f0882 | ||
|
|
a26a8a38f4 | ||
|
|
7435315d50 | ||
|
|
9b5f741e85 | ||
|
|
517822fdd2 | ||
|
|
1b93a9be88 | ||
|
|
783a66b348 | ||
|
|
976c3e9b59 | ||
|
|
f8ca971dae | ||
|
|
21bc8c24df | ||
|
|
30228214f7 | ||
|
|
2ae980ae43 | ||
|
|
6153dec2e4 | ||
|
|
c805f86343 | ||
|
|
04ca065281 | ||
|
|
88d8fa43d7 | ||
|
|
3c49762875 | ||
|
|
436bf1d9d3 | ||
|
|
f70df6e195 | ||
|
|
fce3852dff | ||
|
|
ee1b8bbdbd | ||
|
|
3f1636637d | ||
|
|
2e570f5300 | ||
|
|
9f89486df5 | ||
|
|
22b43b86cb | ||
|
|
3c9012676a | ||
|
|
ee3b3c4c56 | ||
|
|
462d706a63 | ||
|
|
ee0d460c8e | ||
|
|
cd15abe9d1 | ||
|
|
9f40467e24 | ||
|
|
d0b6593823 | ||
|
|
79fc821d8d | ||
|
|
d7fdb9a7e6 | ||
|
|
b74de51c18 | ||
|
|
b507fe209c | ||
|
|
6cd2d8fcd5 | ||
|
|
b02d022993 | ||
|
|
94581e3c7a | ||
|
|
88b52cc045 | ||
|
|
0a816b5509 | ||
|
|
1c8b807c2e | ||
|
|
44b466e072 | ||
|
|
5e5b471bb2 | ||
|
|
9c2565f64e | ||
|
|
e1d0a7cec3 | ||
|
|
b19ae8f465 | ||
|
|
cdff2c8e18 | ||
|
|
66391f84f2 | ||
|
|
97f7a9ecb3 | ||
|
|
15878f7613 | ||
|
|
e0d5e3c6c7 | ||
|
|
6f3455900e | ||
|
|
56827d6ad6 | ||
|
|
73c0b29535 | ||
|
|
303b83cdb8 | ||
|
|
5ef4da3f29 | ||
|
|
1502860004 | ||
|
|
585efc6f3f | ||
|
|
62055e04dd | ||
|
|
e4a641b64e | ||
|
|
8849f187f1 | ||
|
|
db420525b3 | ||
|
|
b5659d106e | ||
|
|
4b43307402 | ||
|
|
09af8c25a2 | ||
|
|
9514035b87 | ||
|
|
2da09ae99b | ||
|
|
a38fb0e04a | ||
|
|
7019916294 | ||
|
|
1514b4f137 | ||
|
|
91cf5ee312 | ||
|
|
0a6e2f42c5 | ||
|
|
ab2de131bd | ||
|
|
5bfa88be85 | ||
|
|
5af8da76d7 | ||
|
|
b8b9dc952d | ||
|
|
79a6ed32d8 | ||
|
|
caa5f97723 | ||
|
|
4924b3209e | ||
|
|
eb702f581b | ||
|
|
3d13fd56c5 | ||
|
|
6f51b49ef8 | ||
|
|
addc638856 | ||
|
|
00f24f8765 | ||
|
|
f5b3d582b0 | ||
|
|
981c93d67a | ||
|
|
c020b78e02 | ||
|
|
42ae36bc28 | ||
|
|
c69f73ff9f | ||
|
|
ca5ae8a2e6 | ||
|
|
d967eb53de | ||
|
|
839f9f1bbe | ||
|
|
b754a152c6 | ||
|
|
e07cb2b9de | ||
|
|
a1f8bbb078 | ||
|
|
7909683f3b | ||
|
|
25f71913b7 | ||
|
|
34ddd2b7b1 | ||
|
|
d5fd90b2f3 | ||
|
|
b7c7000d0d | ||
|
|
551f6c4edd | ||
|
|
defd814750 | ||
|
|
3d517bbd2a | ||
|
|
78ab955fec | ||
|
|
dd13937bb6 | ||
|
|
66a1b63aa9 | ||
|
|
22c611bd1a | ||
|
|
c9bb1bf8ea | ||
|
|
2a0d75bac2 | ||
|
|
9e489887cf | ||
|
|
9feb801bb9 | ||
|
|
c00b495933 | ||
|
|
d22eebe553 | ||
|
|
8bcbd82680 | ||
|
|
dfa617c439 | ||
|
|
48d1f0df89 | ||
|
|
b75cb7a12c | ||
|
|
332563e037 | ||
|
|
0cce97a4fe | ||
|
|
95a8e4be64 | ||
|
|
abcd6b8cb6 | ||
|
|
e8f21c9b6d | ||
|
|
f48298ad4e | ||
|
|
645e47c1ba | ||
|
|
d1d9827263 | ||
|
|
e054078b11 | ||
|
|
14643c0aab | ||
|
|
b77a9b8947 | ||
|
|
6835a7f208 | ||
|
|
f59993b979 | ||
|
|
2290b8f680 | ||
|
|
2c54be651c | ||
|
|
e859a199df | ||
|
|
0a3682ad0b | ||
|
|
59abaeb5cd | ||
|
|
3e448435d3 | ||
|
|
a294bc3c5b | ||
|
|
b302ad3d49 | ||
|
|
82fc4b1e94 | ||
|
|
b4f1740380 | ||
|
|
031f85247c | ||
|
|
639cc6f73a | ||
|
|
09946cf1ba | ||
|
|
f4fa95e7cb | ||
|
|
100e29e35e | ||
|
|
4cbe471a83 | ||
|
|
8bece1f861 | ||
|
|
a3ca71ec01 | ||
|
|
e0543e8af5 | ||
|
|
c1eb80d01a | ||
|
|
a26121d97b | ||
|
|
043031a757 | ||
|
|
807aeebe4c | ||
|
|
8aa1a37aad | ||
|
|
7d077fe493 | ||
|
|
9cd4128833 | ||
|
|
c8b17c9526 | ||
|
|
2ae2a81e85 | ||
|
|
69c869d345 | ||
|
|
df9b958c40 | ||
|
|
3d3376d1a3 | ||
|
|
4efa042f50 | ||
|
|
c7cb37e970 | ||
|
|
d34b207eab | ||
|
|
0e6fa6f6b8 | ||
|
|
38b87de53f | ||
|
|
aa5047a9e4 | ||
|
|
24b6ee0df9 | ||
|
|
1e79cc9cbe | ||
|
|
b3925df9c3 | ||
|
|
f2648e94b9 | ||
|
|
351795ac3a | ||
|
|
9c9c42d0df | ||
|
|
b6ad1bafc7 | ||
|
|
a5ca40f446 | ||
|
|
9ab54c5565 | ||
|
|
4341d96bde | ||
|
|
5fac47a26d | ||
|
|
e064f17346 | ||
|
|
afe10ba2a2 | ||
|
|
7cc3435ba8 | ||
|
|
541772313c | ||
|
|
3747494a09 | ||
|
|
f2b98d0dcc | ||
|
|
80471bf762 | ||
|
|
a06f63c110 | ||
|
|
0ae4478cd9 | ||
|
|
ae4e705e09 | ||
|
|
f5dcea9dbf | ||
|
|
21ed6ac0f4 | ||
|
|
7bb8ab7000 | ||
|
|
2c824c2641 | ||
|
|
391fd9cc6a | ||
|
|
2207309f8a | ||
|
|
51051df62c | ||
|
|
33097681b9 | ||
|
|
07e4900218 | ||
|
|
36ab567d67 | ||
|
|
e19171523b | ||
|
|
9626a2c7c0 | ||
|
|
e936f5b80b | ||
|
|
ffc0639cb9 | ||
|
|
c5b43b322c | ||
|
|
c9c4576237 | ||
|
|
bf4369f72d | ||
|
|
36600899e2 | ||
|
|
b9c70d156b | ||
|
|
eb89579fe7 | ||
|
|
0cfd13d18b | ||
|
|
e6ed516052 | ||
|
|
e2a3dae1f2 | ||
|
|
2111e7ab5f | ||
|
|
d29abfdcaf | ||
|
|
a751c42cc5 | ||
|
|
6a3bc9865e | ||
|
|
4d5f7e4377 | ||
|
|
78b117fb78 | ||
|
|
ded63a1319 | ||
|
|
df3e4d1e9c | ||
|
|
b58fd80379 | ||
|
|
7f6e0f57d0 | ||
|
|
cae27678d8 | ||
|
|
48ff655bad | ||
|
|
2525ad4623 | ||
|
|
e7020017c5 | ||
|
|
eacebfad74 | ||
|
|
3bc2da5321 | ||
|
|
2d710d6bfd | ||
|
|
6532b7f32b | ||
|
|
7b41b92d99 | ||
|
|
dd557af84b | ||
|
|
59b9d0e030 | ||
|
|
b82eee4733 | ||
|
|
6a87487544 | ||
|
|
fcf5023845 | ||
|
|
c8adad6d8b | ||
|
|
737d3ffb98 | ||
|
|
6d0c2de399 | ||
|
|
7786ea9921 | ||
|
|
d93eac7b1c | ||
|
|
b01e67bab1 | ||
|
|
8a70314f54 | ||
|
|
afc316f501 | ||
|
|
f14bfd5c1b | ||
|
|
c5f1420dea | ||
|
|
018e6da872 | ||
|
|
b77bccfac2 | ||
|
|
36ae6e5aba | ||
|
|
9db585cfeb | ||
|
|
c564611ba7 | ||
|
|
e187bcb85c | ||
|
|
be18ffe3b4 | ||
|
|
0d63dce4e2 | ||
|
|
26b30e1551 | ||
|
|
7fc58ac293 | ||
|
|
3a86cce8c1 | ||
|
|
80359e0d49 | ||
|
|
3d437c5cc4 | ||
|
|
37884d369f | ||
|
|
9246e653cd | ||
|
|
64283c8673 | ||
|
|
755002da9c | ||
|
|
31b8e8b437 | ||
|
|
0ec0de97e6 | ||
|
|
6c3ade5d89 | ||
|
|
980c5f9a34 | ||
|
|
63d9b8e8a3 | ||
|
|
d247031c98 | ||
|
|
e29b97b3ea | ||
|
|
ad2b699d2b | ||
|
|
471ca5f281 | ||
|
|
e82ddcff5d | ||
|
|
b9dcad89e8 | ||
|
|
993f43ef4a | ||
|
|
2b43308208 | ||
|
|
04a1ac3a76 | ||
|
|
990b8798bd | ||
|
|
b334a73a44 | ||
|
|
5d113d1c70 | ||
|
|
c14977aeab | ||
|
|
3e94838204 | ||
|
|
c0a0b8ca62 | ||
|
|
b8a7004365 | ||
|
|
affff3865f | ||
|
|
9c22655b5a | ||
|
|
99d879ea7f | ||
|
|
bd56c95a6f | ||
|
|
994512048e | ||
|
|
dbd8bb49dc | ||
|
|
3a29af0ce4 | ||
|
|
f7b79cdd45 | ||
|
|
075b9d22d0 | ||
|
|
b92428f05f | ||
|
|
34b11864b6 | ||
|
|
1dfaa08afb | ||
|
|
9d263d9a7d | ||
|
|
9015c229dc | ||
|
|
f44dce390f | ||
|
|
bb71e9a96a | ||
|
|
78bae9417c | ||
|
|
dd170ead01 | ||
|
|
014704856f | ||
|
|
a7eabaad56 | ||
|
|
eeb4703b84 | ||
|
|
a07421b3d3 | ||
|
|
cda53b4068 | ||
|
|
6f6844ccf1 | ||
|
|
4c6613d72c | ||
|
|
ee92e08edb | ||
|
|
c1dcee9328 | ||
|
|
559257bbe9 | ||
|
|
6b150961fe | ||
|
|
cff1f8d3b8 | ||
|
|
f27d2083cd | ||
|
|
36cc9c524f | ||
|
|
2822487450 | ||
|
|
e07fafe46a | ||
|
|
063d290bd8 | ||
|
|
4e6194d92a | ||
|
|
de30c4e22a | ||
|
|
df99f227c1 | ||
|
|
5bafcaedfa | ||
|
|
bfeceae708 | ||
|
|
eacb66591f | ||
|
|
fadaa85626 | ||
|
|
02a5b0d786 | ||
|
|
0e2141442a | ||
|
|
769eb0eecb | ||
|
|
4241c7d4a3 | ||
|
|
d536c67b9d | ||
|
|
f44f005dad | ||
|
|
26b2caf570 | ||
|
|
7b11075102 | ||
|
|
abc658dca5 | ||
|
|
8bb078db25 | ||
|
|
b61ba40023 | ||
|
|
452bf2e907 | ||
|
|
2372275b2c | ||
|
|
ef736e8aa4 | ||
|
|
5e539e2d54 | ||
|
|
96773f5254 | ||
|
|
d80df09f3b | ||
|
|
621e612c30 | ||
|
|
8c3792721b | ||
|
|
c95bbd3948 | ||
|
|
e28ab7a732 | ||
|
|
c797cbe737 | ||
|
|
e09dfbf1c2 | ||
|
|
85e35c4da1 | ||
|
|
d72e914cf0 | ||
|
|
3b5254e2d5 | ||
|
|
f1c358b596 | ||
|
|
c0ef210265 | ||
|
|
e3e1cc1962 | ||
|
|
723eadbb5c | ||
|
|
e24637ec1e | ||
|
|
8b01ff4ce7 | ||
|
|
588197c487 | ||
|
|
116d90b0ee | ||
|
|
b0646ca187 | ||
|
|
1352bad2e4 | ||
|
|
14d352ea4f | ||
|
|
1cf9ec1cce | ||
|
|
4895ff260e | ||
|
|
4b994a1bc7 | ||
|
|
e506d6d369 | ||
|
|
ab56ad8d7a | ||
|
|
470d93006a | ||
|
|
2f3d03f188 | ||
|
|
8db7c23bee | ||
|
|
69dc5172dc | ||
|
|
fd72eb6546 | ||
|
|
ffd7301649 | ||
|
|
d2a8494044 | ||
|
|
0982e0d19b | ||
|
|
3badbfc3c1 | ||
|
|
5465961e30 | ||
|
|
477b794bc5 | ||
|
|
e8c29e2fe5 | ||
|
|
4835fd1a87 | ||
|
|
6533c25814 | ||
|
|
b405767569 | ||
|
|
fe88a0c12f | ||
|
|
e61a9ed2b4 | ||
|
|
de8daa3824 | ||
|
|
3a50fb29cb | ||
|
|
6647d2656f | ||
|
|
a6f4dbeb6d | ||
|
|
92a282f2d8 | ||
|
|
ca2fd9fc7b | ||
|
|
3825329f8e | ||
|
|
be1a4f5860 | ||
|
|
1b2914ec09 | ||
|
|
519f795066 | ||
|
|
5897b93dd4 | ||
|
|
af091e0881 | ||
|
|
3c1e5e9517 | ||
|
|
85b2cb7a8a | ||
|
|
c7bdf2c0e4 | ||
|
|
4240ad5ca8 | ||
|
|
d418347d86 | ||
|
|
29a4bfe5e5 | ||
|
|
9955bf9daf | ||
|
|
da9cbfc7cc | ||
|
|
6b9f07c1ed | ||
|
|
b8bdc2eefb | ||
|
|
0078826ff1 | ||
|
|
e855c41772 | ||
|
|
d169c275b6 | ||
|
|
a5125e23f4 | ||
|
|
7b83c80757 | ||
|
|
e41821e206 | ||
|
|
bf91778550 | ||
|
|
5a75ab15a2 | ||
|
|
932c783fbf | ||
|
|
55f9cce577 | ||
|
|
b3533ca847 | ||
|
|
fd2a637010 | ||
|
|
eee27b8b30 | ||
|
|
8522352aa3 | ||
|
|
3beb8f4091 | ||
|
|
12a706e9b1 | ||
|
|
170aa7df01 | ||
|
|
e8ad1fef53 | ||
|
|
876c8f4478 | ||
|
|
9c8750f261 | ||
|
|
91efd08179 | ||
|
|
9953511b65 | ||
|
|
025fa9991a | ||
|
|
e8c60c355b | ||
|
|
6c9c7f9d85 | ||
|
|
f534523ede | ||
|
|
1b8a834beb | ||
|
|
aa9df63a05 | ||
|
|
3953312a93 | ||
|
|
6e62f4f616 | ||
|
|
6a7bdca53b | ||
|
|
c7fba9aace | ||
|
|
ac6c7cb8d6 | ||
|
|
c5924833a1 | ||
|
|
ac0a74be0d | ||
|
|
42b0e1125d | ||
|
|
339c4fda79 | ||
|
|
9b85bf9402 | ||
|
|
86b02c3cd8 | ||
|
|
7b3b7093fa | ||
|
|
881b08a465 | ||
|
|
3ee5444c69 | ||
|
|
5e28fe56d2 | ||
|
|
3aa43e6065 | ||
|
|
78ac4044ff | ||
|
|
119c3db47f | ||
|
|
21bbdb8fc2 | ||
|
|
5aabe074fe | ||
|
|
739bd7572c | ||
|
|
074627a5bd | ||
|
|
6a23b2c599 | ||
|
|
dace904c10 | ||
|
|
be98d26610 | ||
|
|
bd891fb3f5 | ||
|
|
3984265851 | ||
|
|
45361d188f | ||
|
|
80c9d77e02 | ||
|
|
3aff64dddb | ||
|
|
b4f2ca81ff | ||
|
|
d1dea5f840 | ||
|
|
54f8b84d16 | ||
|
|
da503fef0e | ||
|
|
4a6802098a | ||
|
|
f9b41a84d2 | ||
|
|
5d7e0d18b9 | ||
|
|
9e64387933 | ||
|
|
983b681d46 | ||
|
|
4072408b6f | ||
|
|
bd76b47fbf | ||
|
|
5f75735dab | ||
|
|
178376f24b | ||
|
|
18ce23aa75 | ||
|
|
6a0eb466ee | ||
|
|
ffa7fe0cc2 | ||
|
|
6b979f0a69 | ||
|
|
4ea29b8f0f | ||
|
|
778291230a | ||
|
|
86dac5ff4f | ||
|
|
4a382fad3f | ||
|
|
cc753670d9 | ||
|
|
cc9d88ea1c | ||
|
|
b281b0166e | ||
|
|
6a21f694ff | ||
|
|
fc4db5e963 | ||
|
|
6252ffaf76 | ||
|
|
026e736dfa | ||
|
|
4275b3f431 | ||
|
|
af64c1c6b6 | ||
|
|
866f48391a | ||
|
|
a4df527d74 | ||
|
|
5764d21161 | ||
|
|
496d04cd85 | ||
|
|
10e6d7c6ce | ||
|
|
c42e25e5b8 | ||
|
|
a00ae981e0 | ||
|
|
58e020b62a | ||
|
|
a7e1aceeca | ||
|
|
7212432f43 | ||
|
|
4a261fab30 | ||
|
|
6af97069b9 | ||
|
|
5068413cdb | ||
|
|
71c6960eea | ||
|
|
ddf6d5c9e3 | ||
|
|
39214702f6 | ||
|
|
3e4614c63a | ||
|
|
900e01f49b | ||
|
|
2376156fbc | ||
|
|
3f2fd49db4 | ||
|
|
0efa107cb6 | ||
|
|
8feedb4f6f | ||
|
|
05e562e3d7 | ||
|
|
dd3bbb8fa2 | ||
|
|
2fbcf13c46 | ||
|
|
4ea48ef0c4 | ||
|
|
5c85774ee3 | ||
|
|
d8a9a745d8 | ||
|
|
dcf172da3b | ||
|
|
d57ed25071 | ||
|
|
546be724e7 | ||
|
|
8a1b9073f9 | ||
|
|
1a7114d4b9 | ||
|
|
3f385f717c | ||
|
|
481bbaf1fc | ||
|
|
281488611a | ||
|
|
c180a52518 | ||
|
|
90130e25e9 | ||
|
|
23298acb81 | ||
|
|
52384e34cf | ||
|
|
d0bb033ea2 | ||
|
|
c6621806ca | ||
|
|
0b6f0f6d2f | ||
|
|
b5b759df73 | ||
|
|
7db8dd7a95 | ||
|
|
8b43be39c0 | ||
|
|
f17f879206 | ||
|
|
68428fceab | ||
|
|
4135f2dcd1 | ||
|
|
c5bdf61215 | ||
|
|
88e218e8ee | ||
|
|
0f2b786436 | ||
|
|
e1c326558a | ||
|
|
bae0f8ea99 | ||
|
|
bbbcd36ae5 | ||
|
|
39c0815d9e | ||
|
|
1b8176e2c0 | ||
|
|
cbc053c3db | ||
|
|
cdf3f6ef6e | ||
|
|
ba7f9d7b70 | ||
|
|
a997d24743 | ||
|
|
861e5d7f4c | ||
|
|
14cc142a14 | ||
|
|
f36b87deb5 | ||
|
|
eeb6e0a6e3 | ||
|
|
cad5b187dd | ||
|
|
87697eb07e | ||
|
|
371fd123fb | ||
|
|
d6ff644aab | ||
|
|
29586f6b5e | ||
|
|
fd057c838f | ||
|
|
f51222086c | ||
|
|
a3e935c902 | ||
|
|
7731c7db8e | ||
|
|
ff97340324 | ||
|
|
83d86943db | ||
|
|
e82cf1d311 | ||
|
|
1db58a8acc | ||
|
|
920a51438d | ||
|
|
be528b6d27 | ||
|
|
f73691ec47 | ||
|
|
ccd21f96ff | ||
|
|
4b90cb8888 | ||
|
|
7ebda3e9ec | ||
|
|
b10e1b7bc8 | ||
|
|
796abfad80 | ||
|
|
ad0270ac8c | ||
|
|
7d62f1d6d2 | ||
|
|
458c943987 | ||
|
|
88015b0858 | ||
|
|
4ca1bf7cca | ||
|
|
2ff868f7a5 | ||
|
|
ede02b6883 | ||
|
|
1822ced302 | ||
|
|
37ba32776f | ||
|
|
99b3697b03 | ||
|
|
43a45ec97b | ||
|
|
b00a4142e5 | ||
|
|
3791bc527b | ||
|
|
d7dea44ce7 | ||
|
|
d8c29f5fcf | ||
|
|
37b6b82869 | ||
|
|
92ad5b8f74 | ||
|
|
281f8101fe | ||
|
|
472ed2dd5c | ||
|
|
4f85672674 | ||
|
|
dc747c54be | ||
|
|
140684d706 | ||
|
|
5bb7ba92fa | ||
|
|
b54d0f3c73 | ||
|
|
ff6777a98d | ||
|
|
07acfe89f2 | ||
|
|
40234f531f | ||
|
|
d49694f38f | ||
|
|
8c80f1c168 | ||
|
|
dc6a38f177 | ||
|
|
82c1ecf60f | ||
|
|
67f569354e | ||
|
|
97a098636d | ||
|
|
e13930c8b2 | ||
|
|
0af7d5a793 | ||
|
|
505fa49983 | ||
|
|
7bcf33def9 | ||
|
|
a13820656a | ||
|
|
fa71b46a41 | ||
|
|
b8b3ae6ac1 | ||
|
|
55c008da21 | ||
|
|
2507606bd0 | ||
|
|
7c2ad4f8c8 | ||
|
|
54c8025aad | ||
|
|
921e23e83c | ||
|
|
6e750ecb0e | ||
|
|
b8f1f5d2a3 | ||
|
|
9273f2937c | ||
|
|
1aa28b47ae | ||
|
|
629cb2987a | ||
|
|
03235d6368 | ||
|
|
22064c7e4c | ||
|
|
5fa573dfd3 | ||
|
|
f6402cb6c4 | ||
|
|
bae6c263dc | ||
|
|
d71672dca9 | ||
|
|
121c9e2ceb | ||
|
|
63a30ae34f | ||
|
|
7d8231ba32 | ||
|
|
b690b1cbe9 | ||
|
|
c0fb20fc03 | ||
|
|
bc9579dac6 | ||
|
|
a5c77f8b95 | ||
|
|
2de03e5172 | ||
|
|
3af4929dda | ||
|
|
1ba429345b | ||
|
|
3dbfce5223 | ||
|
|
e51eaedc56 | ||
|
|
e2a938e7f7 | ||
|
|
ddad25211b | ||
|
|
6209120de9 | ||
|
|
fe6e8f5ac6 | ||
|
|
ee84dcb400 | ||
|
|
0ae0e5f436 | ||
|
|
e047616571 | ||
|
|
1af7572c61 | ||
|
|
653039695b | ||
|
|
ca62abd203 | ||
|
|
e74666a09c | ||
|
|
45a001e078 | ||
|
|
0352da34f0 | ||
|
|
7d302a525d | ||
|
|
e2e269e03b | ||
|
|
0db4f1803f | ||
|
|
5fe480d81c | ||
|
|
0566fc6267 | ||
|
|
a11c12e2e7 | ||
|
|
0655dab466 | ||
|
|
7f097bcc28 | ||
|
|
5c75aa5008 | ||
|
|
1873101362 | ||
|
|
63fd1dfa62 | ||
|
|
bd68861b28 | ||
|
|
82e959f66c | ||
|
|
006268f556 | ||
|
|
78acae9b50 | ||
|
|
a3927a8a27 | ||
|
|
d9dd9a5b5f | ||
|
|
eae1c02111 | ||
|
|
132d841b05 | ||
|
|
62e52de06d | ||
|
|
184adeedb8 | ||
|
|
5fa6a8b96d | ||
|
|
a2a879b668 | ||
|
|
9317d893b2 | ||
|
|
86075fdd45 | ||
|
|
b36442e263 | ||
|
|
513d797ea6 | ||
|
|
9e4835a3e3 | ||
|
|
2e8c3b0ddb | ||
|
|
991667ba5e | ||
|
|
8a07b52009 | ||
|
|
2bcff94b52 | ||
|
|
d089739e2f | ||
|
|
204c283e16 | ||
|
|
551a5f8dc8 | ||
|
|
c82b164f6b | ||
|
|
584a3ee45c | ||
|
|
eec0c9eb7d | ||
|
|
477ebf24f4 | ||
|
|
0d5639f707 | ||
|
|
413312f9a9 | ||
|
|
03508448f8 | ||
|
|
e1e5c75023 | ||
|
|
9296299b61 | ||
|
|
66d001ec9e | ||
|
|
fad2f969d9 | ||
|
|
48165c1dc1 | ||
|
|
25df2d2c3b | ||
|
|
af9ecb8b41 | ||
|
|
234324599e | ||
|
|
97448a93dc | ||
|
|
70c83ec3be | ||
|
|
8f4e2ee545 | ||
|
|
e8bfbf2f7c | ||
|
|
9e81b42981 | ||
|
|
6c9eef9726 | ||
|
|
7ffbc3e98e | ||
|
|
68e4d833dd | ||
|
|
a2cefaa53a | ||
|
|
a0d682687e | ||
|
|
eb552c3ecd | ||
|
|
97cce103d7 | ||
|
|
87ac7104f8 | ||
|
|
e4c117aabf | ||
|
|
5b128a6f9f | ||
|
|
19da647e3c | ||
|
|
1713de35c0 | ||
|
|
1177b8f661 | ||
|
|
442bfb3d42 | ||
|
|
e7d9b75fdd | ||
|
|
3d0e3ec363 | ||
|
|
3c1c51f9aa | ||
|
|
8cc3c522c3 | ||
|
|
913fbca74a | ||
|
|
5c87342108 | ||
|
|
66177bfbe2 | ||
|
|
5205e68963 | ||
|
|
cd5cf6d614 | ||
|
|
5abb19eab0 | ||
|
|
06d7b88c78 | ||
|
|
cf72799735 | ||
|
|
cdb8fcc269 | ||
|
|
b4f4130901 | ||
|
|
bb049847d5 | ||
|
|
fd33c835dd | ||
|
|
21371a7e5b | ||
|
|
abfaa00d3e | ||
|
|
efee33c55d | ||
|
|
db0fe6ddbb | ||
|
|
8a9e647120 | ||
|
|
e6dcb821ad | ||
|
|
9bff188f02 | ||
|
|
111b30ca1d | ||
|
|
24182ca8bf | ||
|
|
ee2d7369b3 | ||
|
|
7c686d29c9 | ||
|
|
e8a0a1e75d | ||
|
|
730be89abf | ||
|
|
f991ad7d5c | ||
|
|
b3f33f82f7 | ||
|
|
a34a6e059f | ||
|
|
1333319941 | ||
|
|
9295ed8d20 | ||
|
|
19cc7653fb | ||
|
|
5752538661 | ||
|
|
ca40a1b00b | ||
|
|
659fac9dfb | ||
|
|
4dc3d6fce0 | ||
|
|
60dfb49afa | ||
|
|
554c238359 | ||
|
|
f922adf05e | ||
|
|
95b640cb6b | ||
|
|
2cb5bedc15 | ||
|
|
806b02bddf | ||
|
|
de40395773 | ||
|
|
7ba4788715 | ||
|
|
06d9ce1a02 | ||
|
|
75bb6b2b40 | ||
|
|
74f10c2dc0 | ||
|
|
188d2c7a4d | ||
|
|
17d7177105 | ||
|
|
bb0a0da47a | ||
|
|
84110166e4 | ||
|
|
d32b923b6c | ||
|
|
a93d5459d4 | ||
|
|
9c21add0c6 | ||
|
|
639aab6563 | ||
|
|
8137cc7049 | ||
|
|
60e63dca1d | ||
|
|
486409574e | ||
|
|
a913b8be12 | ||
|
|
2239751850 | ||
|
|
9b20f1449c | ||
|
|
b99453083d | ||
|
|
2ab1af5754 | ||
|
|
5f8892bf03 | ||
|
|
f14e7e51e7 | ||
|
|
943fbb914d | ||
|
|
ca4603580d | ||
|
|
f73db8f1f3 | ||
|
|
f7217d12d2 | ||
|
|
fab50c57d9 | ||
|
|
3440534fbf | ||
|
|
177b1a7ec6 | ||
|
|
58182fe345 | ||
|
|
1f907d330d | ||
|
|
b0fe664e9d | ||
|
|
c0f8482402 | ||
|
|
3544965f54 | ||
|
|
33e4a0caee | ||
|
|
1f903d9296 | ||
|
|
4df1e0987f | ||
|
|
588c2f3cb1 | ||
|
|
bd99fd608c | ||
|
|
57b442d0de | ||
|
|
751a4562d7 | ||
|
|
ca66301dee | ||
|
|
808bb59206 | ||
|
|
4b7f51d19d | ||
|
|
d03152fac4 | ||
|
|
137f190258 | ||
|
|
53d01312b3 | ||
|
|
220050822a | ||
|
|
87ad76d81b | ||
|
|
042ab1a052 | ||
|
|
4ac1094856 | ||
|
|
d44a57b0af | ||
|
|
dc000d10ee | ||
|
|
3685f391cf | ||
|
|
efd7338a00 | ||
|
|
e1e7b1e224 | ||
|
|
7319d4e1ad | ||
|
|
fd933420c6 | ||
|
|
8208a6214f | ||
|
|
3d8146b596 | ||
|
|
31efa5c4da | ||
|
|
d10d30dda8 | ||
|
|
0e9666bc92 | ||
|
|
6efd80f104 | ||
|
|
fdef7a1a8c | ||
|
|
501bb117bf | ||
|
|
05ca7dc252 | ||
|
|
e9648a1635 | ||
|
|
2df98a99bc | ||
|
|
315ea18be2 | ||
|
|
9a9f4a111f | ||
|
|
1ad54d049d | ||
|
|
57bd0a0a22 | ||
|
|
b49db84b08 | ||
|
|
583f7c52f3 | ||
|
|
58a86c9164 | ||
|
|
a25b32847f | ||
|
|
6f1a2e132b | ||
|
|
b1ede7b46d | ||
|
|
e762c940c2 | ||
|
|
6a1a198144 | ||
|
|
34faa39f4f | ||
|
|
5ddea3829d | ||
|
|
7eb29cf529 | ||
|
|
f729b9b889 | ||
|
|
4f997c5f04 | ||
|
|
a9c2e1df03 | ||
|
|
d3496d2fe0 | ||
|
|
60f4cb0ffd | ||
|
|
136d843ce7 | ||
|
|
18028f4309 | ||
|
|
5164016740 | ||
|
|
d83beaa890 | ||
|
|
f9f05e995b | ||
|
|
e651b9e7ab | ||
|
|
47b4e91473 | ||
|
|
3f31afa4fc | ||
|
|
da4daea57a | ||
|
|
af3b065add | ||
|
|
e346154c5d | ||
|
|
7937ac2bab | ||
|
|
e909aeedf0 | ||
|
|
bab8aa8eb0 | ||
|
|
38b22f05be | ||
|
|
3ca0de1c40 | ||
|
|
c7205d2a73 | ||
|
|
617c5362c1 | ||
|
|
083b58e66d | ||
|
|
633427a2df | ||
|
|
2031d6910a | ||
|
|
f82ce67624 | ||
|
|
b52e8ef65a | ||
|
|
2594e3c230 | ||
|
|
8cedb45af2 | ||
|
|
aa008cbe99 | ||
|
|
79e34b3eb4 | ||
|
|
4f3d581ab4 | ||
|
|
6fb6ca5b6b | ||
|
|
b8ee19691c | ||
|
|
d16427b837 | ||
|
|
4b1997e2f3 | ||
|
|
8939d5dc73 | ||
|
|
b051e00de0 | ||
|
|
8aa75b492f | ||
|
|
0274f40686 | ||
|
|
77aa147ce5 | ||
|
|
32facbd02a | ||
|
|
6121397587 | ||
|
|
4de50ab146 | ||
|
|
8b12a61097 | ||
|
|
79ea027c0b | ||
|
|
62339d437f | ||
|
|
698e745276 | ||
|
|
0417b96896 | ||
|
|
9a6e2c315d | ||
|
|
e61fed87db | ||
|
|
81fe4c937e | ||
|
|
f77f3a6598 | ||
|
|
239afb18fb | ||
|
|
ef820a26cd | ||
|
|
65abe4d0d3 | ||
|
|
5012adfebf | ||
|
|
b808d48fa1 | ||
|
|
83f818a99d | ||
|
|
b8bc560b51 | ||
|
|
6bc2483d57 | ||
|
|
82aecbf4cf | ||
|
|
ee23a76aa0 | ||
|
|
d7191e5a02 | ||
|
|
c8a824425b | ||
|
|
f23626a6b8 | ||
|
|
6577a03d16 | ||
|
|
427c8695fe | ||
|
|
9e82c468ab | ||
|
|
603fd96747 | ||
|
|
fe993c0836 | ||
|
|
cdf31d52c1 | ||
|
|
0542eaf1da | ||
|
|
317bdcf158 | ||
|
|
387397374a | ||
|
|
9ca2c98882 | ||
|
|
605cf401e1 | ||
|
|
f99c3660d2 | ||
|
|
92a83a9eb3 | ||
|
|
53ae01a34a | ||
|
|
b615fa0f35 | ||
|
|
76c294a7ba | ||
|
|
0c0c2b1e20 | ||
|
|
e2fc3a0f04 | ||
|
|
451e7972fd | ||
|
|
56c089d347 | ||
|
|
acf740e44d | ||
|
|
182f513404 | ||
|
|
d5b2323a57 | ||
|
|
bad18d4417 | ||
|
|
bb5c16b97f | ||
|
|
0d80eeb545 | ||
|
|
d1decee4cc | ||
|
|
d4ae71b880 | ||
|
|
b0f4eee78b | ||
|
|
5340e50427 | ||
|
|
e16fc5b2e4 | ||
|
|
694306f202 | ||
|
|
9aac1e6d64 | ||
|
|
3e882f555d | ||
|
|
438caab25f | ||
|
|
239e2c1ee6 | ||
|
|
013dc2ef33 | ||
|
|
9616811c3d | ||
|
|
8a3002c03b | ||
|
|
0f1c5b08a1 | ||
|
|
70988e43d2 | ||
|
|
71034f828e | ||
|
|
aab3bcb46f | ||
|
|
11437930c5 | ||
|
|
3d44aa9cb9 | ||
|
|
2851870d70 | ||
|
|
da06d15f73 | ||
|
|
e8b1251b8c | ||
|
|
63dbaeefaa | ||
|
|
e8c187b323 | ||
|
|
fad5a74a4b | ||
|
|
e83f6a6ae9 | ||
|
|
0c1618197f | ||
|
|
f49d5c2d22 | ||
|
|
a3b022d469 | ||
|
|
48772f0976 | ||
|
|
c322420580 | ||
|
|
6283d11d50 | ||
|
|
86f4e17928 | ||
|
|
6616d5d090 | ||
|
|
215df671be | ||
|
|
1b6b12589f | ||
|
|
3082ab8252 | ||
|
|
add86cd7f4 | ||
|
|
0b6fd20c54 | ||
|
|
e83423fee6 | ||
|
|
b4f8e87982 | ||
|
|
135808dcfa | ||
|
|
7f7d06d963 | ||
|
|
2bf3b4d576 | ||
|
|
0bd83cdbda | ||
|
|
f34d34bd17 | ||
|
|
e32d5141b4 | ||
|
|
6d5277f2d7 | ||
|
|
14d82777e0 | ||
|
|
2a4e739513 | ||
|
|
8079dc2a14 | ||
|
|
6ceb556684 | ||
|
|
76cde73705 | ||
|
|
cc094366a9 | ||
|
|
41a575ff9b | ||
|
|
12ef413065 | ||
|
|
829a328451 | ||
|
|
402523c62e | ||
|
|
d7bef70b5c | ||
|
|
2ad1811642 | ||
|
|
88bdd4344b | ||
|
|
a65a497bae | ||
|
|
b27b12828e | ||
|
|
42d56ea6b6 | ||
|
|
0b905a72dd | ||
|
|
fe9edf8526 | ||
|
|
44204c7e06 | ||
|
|
33b3789598 | ||
|
|
195ab2888d | ||
|
|
85f750d753 | ||
|
|
a4ce6e42c7 | ||
|
|
5398b7e7e3 | ||
|
|
fd13a3f2be | ||
|
|
c144b32368 | ||
|
|
4044536eea | ||
|
|
4d8ae6221c | ||
|
|
ba7e371b90 | ||
|
|
99e7a5d18a | ||
|
|
f824d99059 | ||
|
|
749b8022a4 | ||
|
|
7e0057d2c4 | ||
|
|
cfe9e870d3 | ||
|
|
e9c4f06cbf | ||
|
|
1f9688417a | ||
|
|
4e31e4e094 | ||
|
|
0d6674e489 | ||
|
|
b145fd4f5b | ||
|
|
8a5b794f25 | ||
|
|
291e80f88a | ||
|
|
1ace5850ae | ||
|
|
283f14b7c1 | ||
|
|
1d6e708083 | ||
|
|
89457e25e3 | ||
|
|
7e3b298d3d | ||
|
|
7ff3e5eed4 | ||
|
|
19eb51cf41 | ||
|
|
470d4dcc6d | ||
|
|
ed03bfd555 | ||
|
|
8c0fbcccae | ||
|
|
d4866157fe | ||
|
|
16c2a99965 | ||
|
|
b6496b6cb5 | ||
|
|
4f5fe57920 | ||
|
|
11fb943b1e | ||
|
|
cda915a345 | ||
|
|
7c16189e16 | ||
|
|
ecbfccea43 | ||
|
|
a8eda8f6da | ||
|
|
9b1a0653cf | ||
|
|
7cb1ff7395 | ||
|
|
ab6ea29913 | ||
|
|
b5c81a02b6 | ||
|
|
d899ee80fc | ||
|
|
4016e705fc | ||
|
|
2f4e85e5d6 | ||
|
|
8ed0b57b09 | ||
|
|
046a23121e | ||
|
|
a976fa6746 | ||
|
|
6c66b8d997 | ||
|
|
9523ad3d73 | ||
|
|
73a95fa96f | ||
|
|
7e130076d6 | ||
|
|
6efdad6f21 | ||
|
|
a822c48565 | ||
|
|
014fb76e88 | ||
|
|
30e5311b43 | ||
|
|
67e08aa952 | ||
|
|
ed1f20f3a1 | ||
|
|
cffc736bb3 | ||
|
|
c0d56a1c04 | ||
|
|
3206f69478 | ||
|
|
b2ccaad761 | ||
|
|
8eb1232683 | ||
|
|
c6ce3ad03b | ||
|
|
b3b033d343 | ||
|
|
ca9816bfbb | ||
|
|
814d5abc7e | ||
|
|
a29122e2bf | ||
|
|
e188c0512e | ||
|
|
1fb6aaf150 | ||
|
|
894654f7ef | ||
|
|
109507888b | ||
|
|
68650b61fe | ||
|
|
7ee66bf453 | ||
|
|
8bd70ad8b5 | ||
|
|
af98525766 | ||
|
|
1c2f218519 | ||
|
|
c9aa1f507c | ||
|
|
ea7126496d | ||
|
|
f660dc67e4 | ||
|
|
ede8faea74 | ||
|
|
1b750761c2 | ||
|
|
145acf2919 | ||
|
|
cc4a27b9e6 | ||
|
|
b4690e6091 | ||
|
|
4b24800132 | ||
|
|
9d2238148c | ||
|
|
c15493218d | ||
|
|
001a556a34 | ||
|
|
3d0f88e702 | ||
|
|
dd091d0960 | ||
|
|
e2abbf9520 | ||
|
|
c7baeb5bae | ||
|
|
402d80e197 | ||
|
|
86e33c8ab2 | ||
|
|
5dae6a6dac | ||
|
|
361bb8a101 | ||
|
|
7efdb3cd2b | ||
|
|
65ef4ec29f | ||
|
|
d5835c0222 | ||
|
|
a7b943b33e | ||
|
|
7440cde92f | ||
|
|
0fc662bb24 | ||
|
|
8195890640 | ||
|
|
4c88104a73 | ||
|
|
73b944c152 | ||
|
|
d1b0b7f5c6 | ||
|
|
381d8797d0 | ||
|
|
11ee8a1061 | ||
|
|
b06526bc1e | ||
|
|
3044419111 | ||
|
|
bcfa9cf068 | ||
|
|
114920b8de | ||
|
|
0d588b95f4 | ||
|
|
5b3c530aa7 | ||
|
|
c6a5499c8b | ||
|
|
ec9c3fe77a | ||
|
|
6135ad530e | ||
|
|
40098424c7 | ||
|
|
7163b31a26 | ||
|
|
ffbdd91e0e | ||
|
|
5d29e175d8 | ||
|
|
417dbfa257 | ||
|
|
1eda4d8e0b | ||
|
|
50181f16e5 | ||
|
|
75030637cc | ||
|
|
fe5aaf7677 | ||
|
|
80ac2a73ca | ||
|
|
770680669d | ||
|
|
0cdfc5cf22 | ||
|
|
d75a66a3e6 | ||
|
|
fcc4374d7b | ||
|
|
67c3c16fe5 | ||
|
|
25e9be50b5 | ||
|
|
428b8ba907 | ||
|
|
323cf6c038 | ||
|
|
29a22ae603 | ||
|
|
403bff1a47 | ||
|
|
c50f27e68b | ||
|
|
80afacec5b | ||
|
|
6cd9224dd7 | ||
|
|
4bf8196ff1 | ||
|
|
4c5440fb06 | ||
|
|
a269a3d919 | ||
|
|
0c4f585496 | ||
|
|
33d2df46a0 | ||
|
|
2df308f649 | ||
|
|
92def28bd3 | ||
|
|
ca10bfa1c7 | ||
|
|
298a6ec51e | ||
|
|
e5dbe488a6 | ||
|
|
0e27e3847d | ||
|
|
393727b93b | ||
|
|
2b1fcd78c3 | ||
|
|
0a4e0b49a0 | ||
|
|
76af169f05 | ||
|
|
7b89232251 | ||
|
|
b5aeae526f | ||
|
|
ef0ddd5d04 | ||
|
|
9b73dacf50 | ||
|
|
244b4aa07f | ||
|
|
8cfc7342cd | ||
|
|
15ae317858 | ||
|
|
834f536b5f | ||
|
|
c332d9f08b | ||
|
|
cf2923d5dd | ||
|
|
0e4413ddde | ||
|
|
009ccd581e | ||
|
|
8cd4263974 | ||
|
|
d45c868656 | ||
|
|
955a8113de | ||
|
|
dbe210dd53 | ||
|
|
54c6b1376d | ||
|
|
86e11743ca | ||
|
|
f3f11b586f | ||
|
|
8083e3f7e8 | ||
|
|
980e721f6e | ||
|
|
364793154b | ||
|
|
3e2ae1e9af | ||
|
|
d38ae2fd18 | ||
|
|
030e7754e4 | ||
|
|
e2a0142d87 | ||
|
|
895244ecc3 | ||
|
|
addeb621a7 | ||
|
|
3b7fce1e76 | ||
|
|
4d15417f93 | ||
|
|
ab3c855f65 | ||
|
|
92e2c517d8 | ||
|
|
a7fb25adf6 | ||
|
|
e947992957 | ||
|
|
bb89a82a07 | ||
|
|
2bb374daea | ||
|
|
8bdadbadac | ||
|
|
15c50a7442 | ||
|
|
49b0af2c95 | ||
|
|
9c2b37218a | ||
|
|
3c67d626ba | ||
|
|
51f506553c | ||
|
|
226be84937 | ||
|
|
001814b442 | ||
|
|
db3ac67506 | ||
|
|
da91a884ef | ||
|
|
a71e6755e3 | ||
|
|
cd5891eecd | ||
|
|
5bb7336f27 | ||
|
|
ce1fc1f48a | ||
|
|
82402c6a7c | ||
|
|
d9c4afe5b7 | ||
|
|
f786ff8d69 | ||
|
|
a651caed5f | ||
|
|
0e21adb3f6 | ||
|
|
58bf9b9e6d | ||
|
|
2146eebb65 | ||
|
|
6a429ee6d3 | ||
|
|
4d1ea15c79 | ||
|
|
a76cb005e0 | ||
|
|
49ecbc81d4 | ||
|
|
9e5fb52eb9 | ||
|
|
a9604367c1 | ||
|
|
d7065023cc | ||
|
|
89d299ceec | ||
|
|
e34eda66df | ||
|
|
b24181aa4f | ||
|
|
aa173e2998 | ||
|
|
7a19432e0b | ||
|
|
9b15704290 | ||
|
|
017f955b2d | ||
|
|
f252d69eef | ||
|
|
3b06e4655e | ||
|
|
d4b4de8f42 | ||
|
|
c90beee774 | ||
|
|
1eea9d73b9 | ||
|
|
679d1d22f7 | ||
|
|
b2b5e0b98c | ||
|
|
03e54722c1 | ||
|
|
442336bd96 | ||
|
|
9c9566b9c9 | ||
|
|
1059a81a3c | ||
|
|
2e61556389 | ||
|
|
f9b1f240f6 | ||
|
|
69f41469dd | ||
|
|
d620b303ff | ||
|
|
157fd1428d | ||
|
|
c791cb2214 | ||
|
|
d5ab571a89 | ||
|
|
0ed800f6e4 | ||
|
|
0a32183825 | ||
|
|
2cacfbde2a | ||
|
|
c073e62e0b | ||
|
|
e3d019bc2f | ||
|
|
7ae030f585 | ||
|
|
86b58d5aff | ||
|
|
26e8b9f4a5 | ||
|
|
35114c9e62 | ||
|
|
dfd28a85c9 | ||
|
|
a503332924 | ||
|
|
1ac13ec3a7 | ||
|
|
55de69a569 | ||
|
|
eda9ab487b | ||
|
|
cd99edcc5f | ||
|
|
4705aa541d | ||
|
|
3215d88a91 | ||
|
|
9b9a53f870 | ||
|
|
019ffe17d4 | ||
|
|
bc496dd844 | ||
|
|
a673b6a54d | ||
|
|
1bf2e4d187 | ||
|
|
96dd7a8fbd | ||
|
|
7905afa9f5 | ||
|
|
712bb40650 | ||
|
|
81d88d9f4d | ||
|
|
77063418da | ||
|
|
2983b6fdf6 | ||
|
|
69f1f04f74 | ||
|
|
11a5fd09d6 | ||
|
|
ff1fa98808 | ||
|
|
df16202865 | ||
|
|
3ff7c2c02a | ||
|
|
fc6d07897f | ||
|
|
f9c8e5c8ef | ||
|
|
8bfa0e74f8 | ||
|
|
9b73a937e7 | ||
|
|
b0339bc5a4 | ||
|
|
3c23a947cc | ||
|
|
56111bb823 | ||
|
|
99445673f6 | ||
|
|
97a59643f7 | ||
|
|
579595f547 | ||
|
|
281ac5fc12 | ||
|
|
d8fa903b02 | ||
|
|
eaff0f3aeb | ||
|
|
e8e20c01b2 | ||
|
|
a4afc3ea2a | ||
|
|
fa12b9a329 | ||
|
|
45fc7ded3a | ||
|
|
74de2d9742 | ||
|
|
e759367d42 | ||
|
|
299d0de066 | ||
|
|
3fe75bc7cb | ||
|
|
45d49d8648 | ||
|
|
6013183361 | ||
|
|
4b882e8056 | ||
|
|
3f9ae6e7e7 | ||
|
|
909acd55cd | ||
|
|
4dd9e39e0d | ||
|
|
b4c1317ab4 | ||
|
|
f36d6f3923 | ||
|
|
7adb253e25 | ||
|
|
808f1e0e8c | ||
|
|
873519e960 | ||
|
|
9aec4a3c26 | ||
|
|
c438118fd7 | ||
|
|
70510d151b | ||
|
|
9e7bacb5a4 | ||
|
|
2ef1fa66a8 | ||
|
|
cf76741ec6 | ||
|
|
497e7c1c40 | ||
|
|
888eacd3b8 | ||
|
|
321f0f51b5 | ||
|
|
17ec9c5545 | ||
|
|
30ad9578a2 | ||
|
|
9dce101586 | ||
|
|
97e264d0ff | ||
|
|
683a5e5bf5 | ||
|
|
d4861a362c | ||
|
|
5ff3eae027 | ||
|
|
147dc15d26 | ||
|
|
c61ea72949 | ||
|
|
86e8b9fe38 | ||
|
|
612e468889 | ||
|
|
4ea8d128c2 | ||
|
|
e49b7f2f88 | ||
|
|
aace3d47b9 | ||
|
|
d5049949a4 | ||
|
|
f1c7480e3c | ||
|
|
5adae5d6ff | ||
|
|
a8412ace05 | ||
|
|
9fd1c2ad4b | ||
|
|
4cf3575353 | ||
|
|
804a810d68 | ||
|
|
8fcb392e24 | ||
|
|
dd8d70eeff | ||
|
|
aa8aba6543 | ||
|
|
13df14f96e | ||
|
|
3aab983760 | ||
|
|
9c4dcc5ea3 | ||
|
|
a1063ddbb9 | ||
|
|
18ef8056ec | ||
|
|
1c673977fa | ||
|
|
e9bc748828 | ||
|
|
f48156529b | ||
|
|
d05ce01809 | ||
|
|
cf23eff60e | ||
|
|
6e313575be | ||
|
|
b13d1f7238 | ||
|
|
b5e7945dd9 | ||
|
|
7535566f54 | ||
|
|
50b808ab33 | ||
|
|
f16c2665f5 | ||
|
|
41e28015ae | ||
|
|
3594ce877b | ||
|
|
9bae6b889a | ||
|
|
4014dfd5b9 | ||
|
|
67023c334b | ||
|
|
a3de7026c8 | ||
|
|
ee11678b1f | ||
|
|
a0ccbb3bd6 | ||
|
|
5eeabaa2bb | ||
|
|
00d0d6d008 | ||
|
|
537a9f7030 | ||
|
|
cc9c993f74 | ||
|
|
d10422ded8 | ||
|
|
f313565a3c | ||
|
|
b3881d2636 | ||
|
|
61d5860b46 | ||
|
|
52d17987dc | ||
|
|
19d8bba97d | ||
|
|
463d72d322 | ||
|
|
d060341168 | ||
|
|
c772bcd514 | ||
|
|
3362f8dfa0 | ||
|
|
bf3c9857e0 | ||
|
|
a88b3ceca5 | ||
|
|
aa135412f5 | ||
|
|
9945399e60 | ||
|
|
5eeffa49e8 | ||
|
|
3f06209720 | ||
|
|
12e239dd9f | ||
|
|
af2301afbb | ||
|
|
f98856a26f | ||
|
|
d55cc5b380 | ||
|
|
c2b688abc9 | ||
|
|
b0d61b9687 | ||
|
|
5f893bf9af | ||
|
|
0e17bd6597 | ||
|
|
22caa158cc | ||
|
|
b24a504d7c | ||
|
|
992ef6e9fc | ||
|
|
f32a320bc3 | ||
|
|
5f0fe029d2 | ||
|
|
6b1486e89b | ||
|
|
3f9c427a3a | ||
|
|
d201277652 | ||
|
|
fdda7cf9cf | ||
|
|
e22d30f715 | ||
|
|
1ba25a0d8c | ||
|
|
9ba3647bdf | ||
|
|
5ee832f738 | ||
|
|
467deee46f | ||
|
|
35a69a5133 | ||
|
|
e9c5a271a8 | ||
|
|
acac2d6938 | ||
|
|
97db2b8d20 | ||
|
|
80fd6ab407 | ||
|
|
5534921bee | ||
|
|
ace9cd64bb | ||
|
|
a3e2aeb603 | ||
|
|
049dd25785 | ||
|
|
d43d372294 | ||
|
|
b71a081cba | ||
|
|
c48909590b | ||
|
|
446ef40570 | ||
|
|
81441e98f4 | ||
|
|
ecd3f890f5 | ||
|
|
1c881ce23c | ||
|
|
dacbbdd051 | ||
|
|
2859955a03 | ||
|
|
cc220abd1d | ||
|
|
d1c0c0197e | ||
|
|
fd9424ef27 | ||
|
|
a5c35c4024 | ||
|
|
e03b64dc06 | ||
|
|
4677c40195 | ||
|
|
288c615782 | ||
|
|
48e81cf6f8 | ||
|
|
5cffa05c7e | ||
|
|
d50a2164d7 | ||
|
|
32ff766dbd | ||
|
|
01652d8cfe | ||
|
|
4d2dc7ba03 | ||
|
|
51d1beb1f3 | ||
|
|
65b724bb5f | ||
|
|
6dbd117aa5 | ||
|
|
198b29f618 | ||
|
|
a8309638d4 | ||
|
|
f98a4e880e | ||
|
|
8244caff25 | ||
|
|
bcd7895362 | ||
|
|
85b1c5df39 | ||
|
|
b4255140d6 | ||
|
|
0c3095e173 | ||
|
|
d3ce60713d | ||
|
|
eac1f08b7b | ||
|
|
1654c4f3c0 | ||
|
|
8807d998bc | ||
|
|
5791021dcd | ||
|
|
c273fb051c | ||
|
|
c545530170 | ||
|
|
d982a5b6d5 | ||
|
|
15ca8637f3 | ||
|
|
cbc995b74c | ||
|
|
8b74174d74 | ||
|
|
e21fef17df | ||
|
|
3d27708f07 | ||
|
|
b918744184 | ||
|
|
7d14a3c086 | ||
|
|
e14a84317d | ||
|
|
6c31b99f1f | ||
|
|
9522dcd611 | ||
|
|
ed469898dc | ||
|
|
1eee94a809 | ||
|
|
54523369a3 | ||
|
|
a98c91c2a5 | ||
|
|
a9b92867a8 | ||
|
|
65920faeba | ||
|
|
249e2db87d | ||
|
|
cf3535d16e | ||
|
|
d61ee817f4 | ||
|
|
3448b7387c | ||
|
|
47b89d2739 | ||
|
|
2a75516330 | ||
|
|
b2087f14c4 | ||
|
|
dd1ba266b2 | ||
|
|
1292d59563 | ||
|
|
9877ed9bf8 | ||
|
|
f0dc0f3621 | ||
|
|
1efe30d6cc | ||
|
|
0b787e9fe0 | ||
|
|
37ec4b241c | ||
|
|
63b0a19f37 | ||
|
|
90ea7dfa99 | ||
|
|
f866d7c33e | ||
|
|
542bdef198 | ||
|
|
06007db3d9 | ||
|
|
12e6059a70 | ||
|
|
dbaa24ebf6 | ||
|
|
3276aa67dc | ||
|
|
3b30b9f0c0 | ||
|
|
69db4816f7 | ||
|
|
3abe09025a | ||
|
|
e33878e0de | ||
|
|
27b4fbf3f0 | ||
|
|
968a90633a | ||
|
|
6365a89ba3 | ||
|
|
ddbb008694 | ||
|
|
7997e0a449 | ||
|
|
197612bc7a | ||
|
|
0e88bf4bff | ||
|
|
3e64d78469 | ||
|
|
2004611def | ||
|
|
a2868c96a4 | ||
|
|
7cf7f11e1a | ||
|
|
ea7f8fda5e | ||
|
|
906b78811b | ||
|
|
97703b181b | ||
|
|
d9474c6cb6 | ||
|
|
bbd145382b | ||
|
|
1b08cb7300 | ||
|
|
337d9dc043 | ||
|
|
8726e94ea7 | ||
|
|
67db4993c2 | ||
|
|
f1f655d92b | ||
|
|
43334e88c3 | ||
|
|
4f1e66b044 | ||
|
|
fd3c8b0e85 | ||
|
|
1635c263ee | ||
|
|
64fe5b21b4 | ||
|
|
ee9889821d | ||
|
|
eb470aa6dc | ||
|
|
77af9a3ddc | ||
|
|
102089798c | ||
|
|
39cea8b5a7 | ||
|
|
a65f66d2db | ||
|
|
936c5ecf69 | ||
|
|
22cfbdbbb3 | ||
|
|
093d1ee21b | ||
|
|
d6ba2581ce | ||
|
|
577c064184 | ||
|
|
2ff1fa6fad | ||
|
|
70be1bd8be | ||
|
|
4ef50ba31f | ||
|
|
3e97a26f90 | ||
|
|
599f28f6ef | ||
|
|
c48da35921 | ||
|
|
6c5fa8dcd8 | ||
|
|
0d2f913a1a | ||
|
|
5b117865b2 | ||
|
|
1a74816c25 | ||
|
|
73de335256 | ||
|
|
228fd450ce | ||
|
|
b949cf6b12 | ||
|
|
11bc1aeadc | ||
|
|
66005929af | ||
|
|
05bbc49a99 | ||
|
|
ff7c847735 | ||
|
|
1aa988b2af | ||
|
|
edf17708a8 | ||
|
|
81a8209749 | ||
|
|
a87e45ba25 | ||
|
|
465856331a | ||
|
|
cc958aa9ed | ||
|
|
f46f029dbb | ||
|
|
3dccd7aa2c | ||
|
|
a25e4b3d0c | ||
|
|
d1210ca12a | ||
|
|
36ea0e222a | ||
|
|
65e6e7da6f | ||
|
|
b5e87e8d97 | ||
|
|
5f5807d60a | ||
|
|
92281ec22d | ||
|
|
87266ce099 | ||
|
|
2a23f133e8 | ||
|
|
8dbf790f62 | ||
|
|
2402b4940e | ||
|
|
2111052fbe | ||
|
|
7974acff54 | ||
|
|
f0d17d2b49 | ||
|
|
244c003a1b | ||
|
|
0174f5f742 | ||
|
|
32b2b59be4 | ||
|
|
86bb0cc24b | ||
|
|
84c19587e7 | ||
|
|
237ce92540 | ||
|
|
a7ffc61e82 | ||
|
|
fd97f64612 | ||
|
|
8720aecb80 | ||
|
|
cdf0a04fc5 | ||
|
|
616d3dd737 | ||
|
|
8b066baca8 | ||
|
|
e97f3688db | ||
|
|
433766ac62 | ||
|
|
93a37c8f68 | ||
|
|
89a1e78390 | ||
|
|
ffbb3fc02c | ||
|
|
5a73ef3647 | ||
|
|
87e5d2f4b7 | ||
|
|
d720f10758 | ||
|
|
14fcd0912a | ||
|
|
3111c0bd4f | ||
|
|
e03064490e | ||
|
|
1a4c8c3387 | ||
|
|
2b1e259441 | ||
|
|
f39c2a240b | ||
|
|
0d95805cde | ||
|
|
f67830587f | ||
|
|
6bf7f839ff | ||
|
|
e3147881a9 | ||
|
|
9872c76825 | ||
|
|
fb559614ad | ||
|
|
e93e12b6a4 | ||
|
|
0c3112cd94 | ||
|
|
8cfd5d2639 | ||
|
|
1c9f20b15e | ||
|
|
32237895bd | ||
|
|
5ee3ea2144 | ||
|
|
9fcb47ee63 | ||
|
|
5050833b42 | ||
|
|
7bee4ebb54 | ||
|
|
71cf9851e7 | ||
|
|
b4735c9904 | ||
|
|
9b2699226c | ||
|
|
5f52804907 | ||
|
|
936071773e | ||
|
|
1732f9319e | ||
|
|
91c81cab30 | ||
|
|
38164f8480 | ||
|
|
f013979791 | ||
|
|
e947b563ea | ||
|
|
5cb3530c34 | ||
|
|
250008372f | ||
|
|
1d252d0922 | ||
|
|
006cc8a8f1 | ||
|
|
4fedd8d29f | ||
|
|
cf2938688a | ||
|
|
ee63721bad | ||
|
|
22c5168d70 | ||
|
|
949ac3cd24 | ||
|
|
7bc0166c1c | ||
|
|
cb0d1b3399 | ||
|
|
d1f1ccc705 | ||
|
|
c7519a237a | ||
|
|
32be2b13d3 | ||
|
|
92b342a477 | ||
|
|
556da86ac3 | ||
|
|
8285e41574 | ||
|
|
f999408e92 | ||
|
|
a7abda89e2 | ||
|
|
7860a50f70 | ||
|
|
6ddcef1bca | ||
|
|
8c5a5fdfce | ||
|
|
046b1cbbc0 | ||
|
|
a65ce237c1 | ||
|
|
cd27f1005d | ||
|
|
f8c0a59221 | ||
|
|
832485699f | ||
|
|
81484a4760 | ||
|
|
9a86059761 | ||
|
|
b780b7b7a0 | ||
|
|
9e085bd04e | ||
|
|
6c6812a5ca | ||
|
|
8358ee38c4 | ||
|
|
1f154fe652 | ||
|
|
d708c0258d | ||
|
|
a7635fd5ba | ||
|
|
6b6bf537d3 | ||
|
|
323a651c71 | ||
|
|
9f212679f1 | ||
|
|
032f7dde1a | ||
|
|
ebb60330c9 | ||
|
|
50b1db1e8b | ||
|
|
015d8bb38a | ||
|
|
10a34312dc | ||
|
|
db8c0e7584 | ||
|
|
32fbdf4fb1 | ||
|
|
a9847aa866 | ||
|
|
d15ccad8a7 | ||
|
|
0009b5cee8 | ||
|
|
20d1941a45 | ||
|
|
d24d8e8398 | ||
|
|
162e4bb567 | ||
|
|
07c0c02f8c | ||
|
|
8c31c065b5 | ||
|
|
b7c76ede29 | ||
|
|
05edf803bd | ||
|
|
b1c86900b2 | ||
|
|
78b8e40f83 | ||
|
|
fc2e9850d3 | ||
|
|
ffaaed679e | ||
|
|
bbbee5660d | ||
|
|
b2fd8b993a | ||
|
|
291ee8c3d0 | ||
|
|
e1a5b3ea49 | ||
|
|
55a55660cb | ||
|
|
52081acfa5 | ||
|
|
f8b8e00090 | ||
|
|
ceb8b374da | ||
|
|
4bc2ad2894 | ||
|
|
798af3e68f | ||
|
|
b0ef2367f3 | ||
|
|
71a7350a85 | ||
|
|
6f79369955 | ||
|
|
f9cb6b979f | ||
|
|
ed4d9d17f8 | ||
|
|
fbed02690d | ||
|
|
39f3ae5b1d | ||
|
|
e64bec8c8e | ||
|
|
0893b4e552 | ||
|
|
92f0f29670 | ||
|
|
48a340a9d1 | ||
|
|
f45621109b | ||
|
|
32d1a0bbea | ||
|
|
267cce66a1 | ||
|
|
3417147b11 | ||
|
|
b338719bc8 | ||
|
|
2b81cbe2c2 | ||
|
|
acff9d6ed2 | ||
|
|
a306a49788 | ||
|
|
7ef03c5368 | ||
|
|
28a1fcaaff | ||
|
|
5abec5b8a9 | ||
|
|
499edc0636 | ||
|
|
d990e61be3 | ||
|
|
3edb2dc2da | ||
|
|
345721220e | ||
|
|
6db68d6ecb | ||
|
|
09f0963d1f | ||
|
|
6f44e3c192 | ||
|
|
5893888f87 | ||
|
|
39b448affb | ||
|
|
e54a8f05a9 | ||
|
|
64b72fc17f | ||
|
|
6fdce60492 | ||
|
|
852db4626a | ||
|
|
6504a098cc | ||
|
|
79a385faca | ||
|
|
c12a67030a | ||
|
|
581392f2f2 | ||
|
|
113f277b6a | ||
|
|
974586bedc | ||
|
|
160f78c1e4 | ||
|
|
7e4e1bbbc2 | ||
|
|
e699b7e9f9 | ||
|
|
a28bc0de90 | ||
|
|
14d0fe4d6c | ||
|
|
0ad2e0815c | ||
|
|
1c8ca05e16 | ||
|
|
dc9c8340bb | ||
|
|
19eef97503 | ||
|
|
635246ce50 | ||
|
|
5cdbb7e71e | ||
|
|
8123590a1b | ||
|
|
86c9c4da8b | ||
|
|
cd1efee866 | ||
|
|
bd310932f7 | ||
|
|
304762e7ac | ||
|
|
d79ab03a6c | ||
|
|
d5708e0eb2 | ||
|
|
123f6b7a61 | ||
|
|
2b6457dd9a | ||
|
|
b367cbd422 | ||
|
|
e252c1aca3 | ||
|
|
b140c6a4f9 | ||
|
|
326de36467 | ||
|
|
9f224a1647 | ||
|
|
bb46ba9b5f | ||
|
|
dd5a22b36b | ||
|
|
1ea85b9972 | ||
|
|
8fb63f1c25 | ||
|
|
77fa586f6c | ||
|
|
15238e8d5e | ||
|
|
b27e31957a | ||
|
|
46927771e3 | ||
|
|
d8cea77707 | ||
|
|
5f8a76d490 | ||
|
|
28d49a3b60 | ||
|
|
b4c624ece6 | ||
|
|
2c22db841a |
@@ -0,0 +1,54 @@
|
||||
name: Bug report
|
||||
description: Report a bug.
|
||||
title: "<insert title>"
|
||||
labels: [bug]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Thank you for taking the time to file a bug report.
|
||||
Please check that the code is pointing to the HEAD of develop
|
||||
or any commit in master which is tagged with a version number.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Describe the issue:"
|
||||
description: >
|
||||
Describe the issue and any previous attempt to solve it.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Code example:"
|
||||
description: >
|
||||
If relevant, show how to reproduce the issue using a minimal working
|
||||
example.
|
||||
placeholder: |
|
||||
<< your code here >>
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Target platform:"
|
||||
description: >
|
||||
Give a description of the target platform (CPU, network, compiler).
|
||||
Please give the full CPU part description, using for example
|
||||
`cat /proc/cpuinfo | grep 'model name' | uniq` (Linux)
|
||||
or `sysctl machdep.cpu.brand_string` (macOS) and the full output
|
||||
the `--version` option of your compiler.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Configure options:"
|
||||
description: >
|
||||
Please give the exact configure command used and attach
|
||||
`config.log`, `grid.config.summary` and the output of `make V=1`.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,3 +1,7 @@
|
||||
# Doxygen stuff
|
||||
html/*
|
||||
latex/*
|
||||
|
||||
# Compiled Object files #
|
||||
#########################
|
||||
*.slo
|
||||
@@ -88,6 +92,7 @@ Thumbs.db
|
||||
# build directory #
|
||||
###################
|
||||
build*/*
|
||||
Documentation/_build
|
||||
|
||||
# IDE related files #
|
||||
#####################
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- clang
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
compiler: clang
|
||||
env: PREC=single
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
compiler: clang
|
||||
env: PREC=double
|
||||
|
||||
before_install:
|
||||
- export GRIDDIR=`pwd`
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]] && [ ! -e clang/bin ]; then wget $CLANG_LINK; tar -xf `basename $CLANG_LINK`; mkdir clang; mv clang+*/* clang/; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]]; then export PATH="${GRIDDIR}/clang/bin:${PATH}"; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]]; then export LD_LIBRARY_PATH="${GRIDDIR}/clang/lib:${LD_LIBRARY_PATH}"; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libmpc openssl; fi
|
||||
|
||||
install:
|
||||
- export CWD=`pwd`
|
||||
- echo $CWD
|
||||
- export CC=$CC$VERSION
|
||||
- export CXX=$CXX$VERSION
|
||||
- echo $PATH
|
||||
- which autoconf
|
||||
- autoconf --version
|
||||
- which automake
|
||||
- automake --version
|
||||
- which $CC
|
||||
- $CC --version
|
||||
- which $CXX
|
||||
- $CXX --version
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export LDFLAGS='-L/usr/local/lib'; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export EXTRACONF='--with-openssl=/usr/local/opt/openssl'; fi
|
||||
|
||||
script:
|
||||
- ./bootstrap.sh
|
||||
- mkdir build
|
||||
- cd build
|
||||
- mkdir lime
|
||||
- cd lime
|
||||
- mkdir build
|
||||
- cd build
|
||||
- wget http://usqcd-software.github.io/downloads/c-lime/lime-1.3.2.tar.gz
|
||||
- tar xf lime-1.3.2.tar.gz
|
||||
- cd lime-1.3.2
|
||||
- ./configure --prefix=$CWD/build/lime/install
|
||||
- make -j4
|
||||
- make install
|
||||
- cd $CWD/build
|
||||
- ../configure --enable-precision=$PREC --enable-simd=SSE4 --enable-comms=none --with-lime=$CWD/build/lime/install ${EXTRACONF}
|
||||
- make -j4
|
||||
- ./benchmarks/Benchmark_dwf --threads 1 --debug-signals
|
||||
- make check
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
|
||||
mpicxx -qmkl=parallel -fsycl BatchBlasBench.cc -o BatchBlasBench -DGRID_SYCL
|
||||
@@ -0,0 +1,5 @@
|
||||
CXX=hipcc
|
||||
MPICXX=mpicxx
|
||||
CXXFLAGS="-fPIC -I{$ROCM_PATH}/include/ -I${MPICH_DIR}/include -L/lib64 -I/opt/cray/pe/mpich/8.1.28/ofi/gnu/12.3/include -DGRID_HIP"
|
||||
LDFLAGS="-L/lib64 -L${MPICH_DIR}/lib -lmpi -L${CRAY_MPICH_ROOTDIR}/gtl/lib -lmpi_gtl_hsa -lamdhip64 -lhipblas -lrocblas -lmpi_gnu_123"
|
||||
hipcc $CXXFLAGS $LDFLAGS BatchBlasBench.cc -o BatchBlasBench
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
mpicxx -qmkl=parallel -fsycl BatchBlasBench.cc -o BatchBlasBench -DGRID_SYCL
|
||||
+15
-3
@@ -37,19 +37,31 @@ directory
|
||||
#endif
|
||||
|
||||
//disables and intel compiler specific warning (in json.hpp)
|
||||
#ifdef __ICC
|
||||
#pragma warning disable 488
|
||||
#endif
|
||||
|
||||
#ifdef __NVCC__
|
||||
//disables nvcc specific warning in json.hpp
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-register"
|
||||
|
||||
#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
|
||||
//disables nvcc specific warning in json.hpp
|
||||
#pragma nv_diag_suppress unsigned_compare_with_zero
|
||||
#pragma nv_diag_suppress cast_to_qualified_type
|
||||
//disables nvcc specific warning in many files
|
||||
#pragma nv_diag_suppress esa_on_defaulted_function_ignored
|
||||
#pragma nv_diag_suppress declared_but_not_referenced
|
||||
#pragma nv_diag_suppress extra_semicolon
|
||||
#else
|
||||
//disables nvcc specific warning in json.hpp
|
||||
#pragma diag_suppress unsigned_compare_with_zero
|
||||
#pragma diag_suppress cast_to_qualified_type
|
||||
|
||||
#pragma diag_suppress declared_but_not_referenced
|
||||
//disables nvcc specific warning in many files
|
||||
#pragma diag_suppress esa_on_defaulted_function_ignored
|
||||
#pragma diag_suppress extra_semicolon
|
||||
|
||||
//Eigen only
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Disable vectorisation in Eigen on the Power8/9 and PowerPC
|
||||
|
||||
+5
-3
@@ -44,12 +44,13 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
#include <Grid/GridStd.h>
|
||||
#include <Grid/threads/Pragmas.h>
|
||||
#include <Grid/perfmon/Timer.h>
|
||||
#include <Grid/perfmon/PerfCount.h>
|
||||
//#include <Grid/perfmon/PerfCount.h>
|
||||
#include <Grid/util/Util.h>
|
||||
#include <Grid/log/Log.h>
|
||||
#include <Grid/allocator/AlignedAllocator.h>
|
||||
#include <Grid/perfmon/Tracing.h>
|
||||
#include <Grid/allocator/Allocator.h>
|
||||
#include <Grid/simd/Simd.h>
|
||||
#include <Grid/threads/Threads.h>
|
||||
#include <Grid/threads/ThreadReduction.h>
|
||||
#include <Grid/serialisation/Serialisation.h>
|
||||
#include <Grid/util/Sha.h>
|
||||
#include <Grid/communicator/Communicator.h>
|
||||
@@ -58,6 +59,7 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
#include <Grid/lattice/Lattice.h>
|
||||
#include <Grid/cshift/Cshift.h>
|
||||
#include <Grid/stencil/Stencil.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
#include <Grid/parallelIO/BinaryIO.h>
|
||||
#include <Grid/algorithms/Algorithms.h>
|
||||
NAMESPACE_CHECK(GridCore)
|
||||
|
||||
@@ -36,6 +36,7 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
#include <Grid/GridCore.h>
|
||||
#include <Grid/qcd/QCD.h>
|
||||
#include <Grid/qcd/spin/Spin.h>
|
||||
#include <Grid/qcd/gparity/Gparity.h>
|
||||
#include <Grid/qcd/utils/Utils.h>
|
||||
#include <Grid/qcd/representations/Representations.h>
|
||||
NAMESPACE_CHECK(GridQCDCore);
|
||||
|
||||
+44
-4
@@ -1,11 +1,20 @@
|
||||
#ifndef GRID_STD_H
|
||||
#define GRID_STD_H
|
||||
|
||||
///////////////////
|
||||
// Grid config
|
||||
///////////////////
|
||||
#include "Config.h"
|
||||
|
||||
///////////////////
|
||||
// Std C++ dependencies
|
||||
///////////////////
|
||||
#define _NBACKTRACE (256)
|
||||
extern void * Grid_backtrace_buffer[_NBACKTRACE];
|
||||
|
||||
#include <cassert>
|
||||
#include <complex>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
@@ -14,17 +23,48 @@
|
||||
#include <random>
|
||||
#include <functional>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <ctime>
|
||||
#include <sys/time.h>
|
||||
#include <chrono>
|
||||
#include <zlib.h>
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
///////////////////
|
||||
// Grid config
|
||||
///////////////////
|
||||
#include "Config.h"
|
||||
void GridAbort(void);
|
||||
|
||||
#define ASSLOG(A) ::write(STDERR_FILENO,A,::strlen(A));
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#define GRID_ASSERT(b) if(!(b)) { \
|
||||
fflush(stdout); \
|
||||
ASSLOG(" GRID_ASSERT failure: "); \
|
||||
ASSLOG(__FILE__); \
|
||||
ASSLOG(" : "); \
|
||||
ASSLOG(#b); \
|
||||
ASSLOG(" : "); \
|
||||
int symbols = backtrace(Grid_backtrace_buffer,_NBACKTRACE); \
|
||||
backtrace_symbols_fd(Grid_backtrace_buffer,symbols,STDERR_FILENO); \
|
||||
GridAbort(); \
|
||||
};
|
||||
#else
|
||||
#define GRID_ASSERT(b) if(!(b)) { \
|
||||
ASSLOG(" GRID_ASSERT failure: "); \
|
||||
ASSLOG(__FILE__); \
|
||||
ASSLOG(" : "); \
|
||||
ASSLOG(#b); \
|
||||
ASSLOG(" : "); \
|
||||
GridAbort(); \
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TOFU
|
||||
#undef GRID_COMMS_THREADS
|
||||
#endif
|
||||
#endif /* GRID_STD_H */
|
||||
|
||||
+20
-3
@@ -14,25 +14,36 @@
|
||||
/* NVCC save and restore compile environment*/
|
||||
#ifdef __NVCC__
|
||||
#pragma push
|
||||
#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
|
||||
#pragma nv_diag_suppress code_is_unreachable
|
||||
#else
|
||||
#pragma diag_suppress code_is_unreachable
|
||||
#endif
|
||||
#pragma push_macro("__CUDA_ARCH__")
|
||||
#pragma push_macro("__NVCC__")
|
||||
#pragma push_macro("__CUDACC__")
|
||||
#undef __CUDA_ARCH__
|
||||
#undef __NVCC__
|
||||
#undef __CUDACC__
|
||||
#undef __CUDA_ARCH__
|
||||
#define __NVCC__REDEFINE__
|
||||
#endif
|
||||
|
||||
/* SYCL save and restore compile environment*/
|
||||
#ifdef __SYCL_DEVICE_ONLY__
|
||||
#ifdef GRID_SYCL
|
||||
#pragma push
|
||||
#pragma push_macro("__SYCL_DEVICE_ONLY__")
|
||||
#undef __SYCL_DEVICE_ONLY__
|
||||
#undef EIGEN_USE_SYCL
|
||||
#define EIGEN_DONT_VECTORIZE
|
||||
#undef EIGEN_USE_SYCL
|
||||
#define __SYCL__REDEFINE__
|
||||
#endif
|
||||
|
||||
/* HIP save and restore compile environment*/
|
||||
#ifdef GRID_HIP
|
||||
#pragma push
|
||||
#pragma push_macro("__HIP_DEVICE_COMPILE__")
|
||||
#endif
|
||||
#define EIGEN_NO_HIP
|
||||
|
||||
#include <Grid/Eigen/Dense>
|
||||
#include <Grid/Eigen/unsupported/CXX11/Tensor>
|
||||
@@ -51,6 +62,12 @@
|
||||
#pragma pop
|
||||
#endif
|
||||
|
||||
/*HIP restore*/
|
||||
#ifdef __HIP__REDEFINE__
|
||||
#pragma pop_macro("__HIP_DEVICE_COMPILE__")
|
||||
#pragma pop
|
||||
#endif
|
||||
|
||||
#if defined __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
+24
-2
@@ -21,7 +21,8 @@ if BUILD_HDF5
|
||||
extra_headers+=serialisation/Hdf5Type.h
|
||||
endif
|
||||
|
||||
all: version-cache
|
||||
|
||||
all: version-cache Version.h
|
||||
|
||||
version-cache:
|
||||
@if [ `git status --porcelain | grep -v '??' | wc -l` -gt 0 ]; then\
|
||||
@@ -42,7 +43,7 @@ version-cache:
|
||||
fi;\
|
||||
rm -f vertmp
|
||||
|
||||
Version.h:
|
||||
Version.h: version-cache
|
||||
cp version-cache Version.h
|
||||
|
||||
.PHONY: version-cache
|
||||
@@ -53,6 +54,27 @@ Version.h:
|
||||
include Make.inc
|
||||
include Eigen.inc
|
||||
|
||||
if BUILD_FERMION_INSTANTIATIONS
|
||||
extra_sources+=$(WILS_FERMION_FILES)
|
||||
extra_sources+=$(STAG_FERMION_FILES)
|
||||
if BUILD_ZMOBIUS
|
||||
extra_sources+=$(ZWILS_FERMION_FILES)
|
||||
endif
|
||||
if BUILD_GPARITY
|
||||
extra_sources+=$(GP_FERMION_FILES)
|
||||
endif
|
||||
if BUILD_FERMION_REPS
|
||||
extra_sources+=$(ADJ_FERMION_FILES)
|
||||
extra_sources+=$(TWOIND_FERMION_FILES)
|
||||
endif
|
||||
if BUILD_SP
|
||||
extra_sources+=$(SP_FERMION_FILES)
|
||||
if BUILD_FERMION_REPS
|
||||
extra_sources+=$(SP_TWOIND_FERMION_FILES)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
lib_LIBRARIES = libGrid.a
|
||||
|
||||
CCFILES += $(extra_sources)
|
||||
|
||||
@@ -29,6 +29,7 @@ directory
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <exception>
|
||||
#include <cassert>
|
||||
|
||||
#define NAMESPACE_BEGIN(A) namespace A {
|
||||
@@ -36,3 +37,7 @@ directory
|
||||
#define GRID_NAMESPACE_BEGIN NAMESPACE_BEGIN(Grid)
|
||||
#define GRID_NAMESPACE_END NAMESPACE_END(Grid)
|
||||
#define NAMESPACE_CHECK(x) struct namespaceTEST##x {}; static_assert(std::is_same<namespaceTEST##x, ::namespaceTEST##x>::value,"Not in :: at" );
|
||||
|
||||
#define EXCEPTION_CHECK_BEGIN(A) try {
|
||||
#define EXCEPTION_CHECK_END(A) } catch ( std::exception e ) { BACKTRACEFP(stderr); std::cerr << __PRETTY_FUNCTION__ << " : " <<__LINE__<< " Caught exception "<<e.what()<<std::endl; throw; }
|
||||
|
||||
|
||||
@@ -29,9 +29,14 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
#ifndef GRID_ALGORITHMS_H
|
||||
#define GRID_ALGORITHMS_H
|
||||
|
||||
NAMESPACE_CHECK(blas);
|
||||
#include <Grid/algorithms/blas/BatchedBlas.h>
|
||||
|
||||
NAMESPACE_CHECK(algorithms);
|
||||
#include <Grid/algorithms/SparseMatrix.h>
|
||||
#include <Grid/algorithms/LinearOperator.h>
|
||||
#include <Grid/algorithms/Preconditioner.h>
|
||||
NAMESPACE_CHECK(SparseMatrix);
|
||||
|
||||
#include <Grid/algorithms/approx/Zolotarev.h>
|
||||
#include <Grid/algorithms/approx/Chebyshev.h>
|
||||
@@ -41,15 +46,26 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
#include <Grid/algorithms/approx/Forecast.h>
|
||||
#include <Grid/algorithms/approx/RemezGeneral.h>
|
||||
#include <Grid/algorithms/approx/ZMobius.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/Deflation.h>
|
||||
NAMESPACE_CHECK(approx);
|
||||
#include <Grid/algorithms/deflation/Deflation.h>
|
||||
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
||||
#include <Grid/algorithms/deflation/MultiRHSDeflation.h>
|
||||
#include <Grid/algorithms/deflation/MultiRHSBlockCGLinalg.h>
|
||||
// Not really deflation, but useful
|
||||
#include <Grid/algorithms/blas/MomentumProject.h>
|
||||
NAMESPACE_CHECK(deflation);
|
||||
#include <Grid/algorithms/iterative/ConjugateGradient.h>
|
||||
//#include <Grid/algorithms/iterative/EigCG.h>
|
||||
NAMESPACE_CHECK(ConjGrad);
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
NAMESPACE_CHECK(BiCGSTAB);
|
||||
#include <Grid/algorithms/iterative/ConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/NormalEquations.h>
|
||||
#include <Grid/algorithms/iterative/SchurRedBlack.h>
|
||||
#include <Grid/algorithms/iterative/ConjugateGradientMultiShift.h>
|
||||
#include <Grid/algorithms/iterative/ConjugateGradientMixedPrec.h>
|
||||
#include <Grid/algorithms/iterative/ConjugateGradientMultiShiftMixedPrec.h>
|
||||
#include <Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTABMixedPrec.h>
|
||||
#include <Grid/algorithms/iterative/BlockConjugateGradient.h>
|
||||
#include <Grid/algorithms/iterative/ConjugateGradientReliableUpdate.h>
|
||||
@@ -60,9 +76,26 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
#include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h>
|
||||
#include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h>
|
||||
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
|
||||
#include <Grid/algorithms/iterative/SimpleLanczos.h>
|
||||
#include <Grid/algorithms/iterative/PowerMethod.h>
|
||||
|
||||
#include <Grid/algorithms/CoarsenedMatrix.h>
|
||||
#include <Grid/algorithms/iterative/AdefGeneric.h>
|
||||
#include <Grid/algorithms/iterative/AdefMrhs.h>
|
||||
NAMESPACE_CHECK(PowerMethod);
|
||||
#include <Grid/algorithms/multigrid/MultiGrid.h>
|
||||
NAMESPACE_CHECK(multigrid);
|
||||
#include <Grid/algorithms/FFT.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/KrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/BlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/SplitGridBlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/HarmonicBlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/TrueHarmonicBlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/Gamma5BlockLanczos.h>
|
||||
//#include <Grid/algorithms/iterative/Gamma5ScalarLanczos.h>
|
||||
#include <Grid/algorithms/iterative/Arnoldi.h>
|
||||
#include <Grid/algorithms/iterative/LanczosBidiagonalization.h>
|
||||
#include <Grid/algorithms/iterative/RestartedLanczosBidiagonalization.h>
|
||||
//#include <Grid/algorithms/iterative/GCR.h>
|
||||
#include <Grid/algorithms/iterative/MultiSplittingPreconditionedCG.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,960 +0,0 @@
|
||||
// blockZaxpy in bockPromote - 3s, 5%
|
||||
// noncoalesced linalg in Preconditionoer ~ 3s 5%
|
||||
// Lancos tuning or replace 10-20s ~ 25%, open ended
|
||||
// setup tuning 5s ~ 8%
|
||||
// -- e.g. ordermin, orderstep tunables.
|
||||
// MdagM path without norm in LinOp code. few seconds
|
||||
|
||||
// Mdir calc blocking kernels
|
||||
// Fuse kernels in blockMaskedInnerProduct
|
||||
// preallocate Vectors in Cayley 5D ~ few percent few seconds
|
||||
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/CoarsenedMatrix.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_ALGORITHM_COARSENED_MATRIX_H
|
||||
#define GRID_ALGORITHM_COARSENED_MATRIX_H
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class vobj,class CComplex>
|
||||
inline void blockMaskedInnerProduct(Lattice<CComplex> &CoarseInner,
|
||||
const Lattice<decltype(innerProduct(vobj(),vobj()))> &FineMask,
|
||||
const Lattice<vobj> &fineX,
|
||||
const Lattice<vobj> &fineY)
|
||||
{
|
||||
typedef decltype(innerProduct(vobj(),vobj())) dotp;
|
||||
|
||||
GridBase *coarse(CoarseInner.Grid());
|
||||
GridBase *fine (fineX.Grid());
|
||||
|
||||
Lattice<dotp> fine_inner(fine); fine_inner.Checkerboard() = fineX.Checkerboard();
|
||||
Lattice<dotp> fine_inner_msk(fine);
|
||||
|
||||
// Multiply could be fused with innerProduct
|
||||
// Single block sum kernel could do both masks.
|
||||
fine_inner = localInnerProduct(fineX,fineY);
|
||||
mult(fine_inner_msk, fine_inner,FineMask);
|
||||
blockSum(CoarseInner,fine_inner_msk);
|
||||
}
|
||||
|
||||
|
||||
class Geometry {
|
||||
public:
|
||||
int npoint;
|
||||
std::vector<int> directions ;
|
||||
std::vector<int> displacements;
|
||||
|
||||
Geometry(int _d) {
|
||||
|
||||
int base = (_d==5) ? 1:0;
|
||||
|
||||
// make coarse grid stencil for 4d , not 5d
|
||||
if ( _d==5 ) _d=4;
|
||||
|
||||
npoint = 2*_d+1;
|
||||
directions.resize(npoint);
|
||||
displacements.resize(npoint);
|
||||
for(int d=0;d<_d;d++){
|
||||
directions[d ] = d+base;
|
||||
directions[d+_d] = d+base;
|
||||
displacements[d ] = +1;
|
||||
displacements[d+_d]= -1;
|
||||
}
|
||||
directions [2*_d]=0;
|
||||
displacements[2*_d]=0;
|
||||
|
||||
//// report back
|
||||
std::cout<<GridLogMessage<<"directions :";
|
||||
for(int d=0;d<npoint;d++) std::cout<< directions[d]<< " ";
|
||||
std::cout<<std::endl;
|
||||
std::cout<<GridLogMessage<<"displacements :";
|
||||
for(int d=0;d<npoint;d++) std::cout<< displacements[d]<< " ";
|
||||
std::cout<<std::endl;
|
||||
}
|
||||
|
||||
/*
|
||||
// Original cleaner code
|
||||
Geometry(int _d) : dimension(_d), npoint(2*_d+1), directions(npoint), displacements(npoint) {
|
||||
for(int d=0;d<dimension;d++){
|
||||
directions[2*d ] = d;
|
||||
directions[2*d+1] = d;
|
||||
displacements[2*d ] = +1;
|
||||
displacements[2*d+1] = -1;
|
||||
}
|
||||
directions [2*dimension]=0;
|
||||
displacements[2*dimension]=0;
|
||||
}
|
||||
std::vector<int> GetDelta(int point) {
|
||||
std::vector<int> delta(dimension,0);
|
||||
delta[directions[point]] = displacements[point];
|
||||
return delta;
|
||||
};
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class Aggregation {
|
||||
public:
|
||||
typedef iVector<CComplex,nbasis > siteVector;
|
||||
typedef Lattice<siteVector> CoarseVector;
|
||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||
|
||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||
typedef Lattice<Fobj > FineField;
|
||||
|
||||
GridBase *CoarseGrid;
|
||||
GridBase *FineGrid;
|
||||
std::vector<Lattice<Fobj> > subspace;
|
||||
int checkerboard;
|
||||
int Checkerboard(void){return checkerboard;}
|
||||
Aggregation(GridBase *_CoarseGrid,GridBase *_FineGrid,int _checkerboard) :
|
||||
CoarseGrid(_CoarseGrid),
|
||||
FineGrid(_FineGrid),
|
||||
subspace(nbasis,_FineGrid),
|
||||
checkerboard(_checkerboard)
|
||||
{
|
||||
};
|
||||
|
||||
void Orthogonalise(void){
|
||||
CoarseScalar InnerProd(CoarseGrid);
|
||||
std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl;
|
||||
blockOrthogonalise(InnerProd,subspace);
|
||||
// std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 2"<<std::endl; // Really have to do twice? Yuck
|
||||
// blockOrthogonalise(InnerProd,subspace);
|
||||
// std::cout << GridLogMessage <<" Gramm-Schmidt checking orthogonality"<<std::endl;
|
||||
// CheckOrthogonal();
|
||||
}
|
||||
void CheckOrthogonal(void){
|
||||
CoarseVector iProj(CoarseGrid);
|
||||
CoarseVector eProj(CoarseGrid);
|
||||
for(int i=0;i<nbasis;i++){
|
||||
blockProject(iProj,subspace[i],subspace);
|
||||
eProj=Zero();
|
||||
accelerator_for(ss, CoarseGrid->oSites(),1,{
|
||||
eProj[ss](i)=CComplex(1.0);
|
||||
});
|
||||
eProj=eProj - iProj;
|
||||
std::cout<<GridLogMessage<<"Orthog check error "<<i<<" " << norm2(eProj)<<std::endl;
|
||||
}
|
||||
std::cout<<GridLogMessage <<"CheckOrthog done"<<std::endl;
|
||||
}
|
||||
void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
||||
blockProject(CoarseVec,FineVec,subspace);
|
||||
}
|
||||
void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
||||
FineVec.Checkerboard() = subspace[0].Checkerboard();
|
||||
blockPromote(CoarseVec,FineVec,subspace);
|
||||
}
|
||||
void CreateSubspaceRandom(GridParallelRNG &RNG){
|
||||
for(int i=0;i<nbasis;i++){
|
||||
random(RNG,subspace[i]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void CreateSubspace(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,int nn=nbasis) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
ConjugateGradient<FineField> CG(1.0e-2,100,false);
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
|
||||
for(int b=0;b<nn;b++){
|
||||
|
||||
subspace[b] = Zero();
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
for(int i=0;i<1;i++){
|
||||
|
||||
CG(hermop,noise,subspace[b]);
|
||||
|
||||
noise = subspace[b];
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
}
|
||||
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl;
|
||||
subspace[b] = noise;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// World of possibilities here. But have tried quite a lot of experiments (250+ jobs run on Summit)
|
||||
// and this is the best I found
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if 1
|
||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
double lo,
|
||||
int orderfilter,
|
||||
int ordermin,
|
||||
int orderstep,
|
||||
double filterlo
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
int b =0;
|
||||
{
|
||||
// Filter
|
||||
Chebyshev<FineField> Cheb(lo,hi,orderfilter);
|
||||
Cheb(hermop,noise,Mn);
|
||||
// normalise
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
b++;
|
||||
}
|
||||
|
||||
// Generate a full sequence of Chebyshevs
|
||||
{
|
||||
lo=filterlo;
|
||||
noise=Mn;
|
||||
|
||||
FineField T0(FineGrid); T0 = noise;
|
||||
FineField T1(FineGrid);
|
||||
FineField T2(FineGrid);
|
||||
FineField y(FineGrid);
|
||||
|
||||
FineField *Tnm = &T0;
|
||||
FineField *Tn = &T1;
|
||||
FineField *Tnp = &T2;
|
||||
|
||||
// Tn=T1 = (xscale M + mscale)in
|
||||
RealD xscale = 2.0/(hi-lo);
|
||||
RealD mscale = -(hi+lo)/(hi-lo);
|
||||
hermop.HermOp(T0,y);
|
||||
T1=y*xscale+noise*mscale;
|
||||
|
||||
for(int n=2;n<=ordermin+orderstep*(nn-2);n++){
|
||||
|
||||
hermop.HermOp(*Tn,y);
|
||||
|
||||
auto y_v = y.View();
|
||||
auto Tn_v = Tn->View();
|
||||
auto Tnp_v = Tnp->View();
|
||||
auto Tnm_v = Tnm->View();
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
accelerator_forNB(ss, FineGrid->oSites(), Nsimd, {
|
||||
coalescedWrite(y_v[ss],xscale*y_v(ss)+mscale*Tn_v(ss));
|
||||
coalescedWrite(Tnp_v[ss],2.0*y_v(ss)-Tnm_v(ss));
|
||||
});
|
||||
|
||||
// Possible more fine grained control is needed than a linear sweep,
|
||||
// but huge productivity gain if this is simple algorithm and not a tunable
|
||||
int m =1;
|
||||
if ( n>=ordermin ) m=n-ordermin;
|
||||
if ( (m%orderstep)==0 ) {
|
||||
Mn=*Tnp;
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << n<<" filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
b++;
|
||||
}
|
||||
|
||||
// Cycle pointers to avoid copies
|
||||
FineField *swizzle = Tnm;
|
||||
Tnm =Tn;
|
||||
Tn =Tnp;
|
||||
Tnp =swizzle;
|
||||
|
||||
}
|
||||
}
|
||||
assert(b==nn);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
double lo,
|
||||
int orderfilter,
|
||||
int ordermin,
|
||||
int orderstep,
|
||||
double filterlo
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
FineField combined(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
int b =0;
|
||||
#define FILTERb(llo,hhi,oorder) \
|
||||
{ \
|
||||
Chebyshev<FineField> Cheb(llo,hhi,oorder); \
|
||||
Cheb(hermop,noise,Mn); \
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale; \
|
||||
subspace[b] = Mn; \
|
||||
hermop.Op(Mn,tmp); \
|
||||
std::cout<<GridLogMessage << oorder<< " Cheb filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl; \
|
||||
b++; \
|
||||
}
|
||||
|
||||
// JacobiPolynomial<FineField> Cheb(0.002,60.0,1500,-0.5,3.5); \
|
||||
|
||||
RealD alpha=-0.8;
|
||||
RealD beta =-0.8;
|
||||
#define FILTER(llo,hhi,oorder) \
|
||||
{ \
|
||||
Chebyshev<FineField> Cheb(llo,hhi,oorder); \
|
||||
/* JacobiPolynomial<FineField> Cheb(0.0,60.0,oorder,alpha,beta);*/\
|
||||
Cheb(hermop,noise,Mn); \
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale; \
|
||||
subspace[b] = Mn; \
|
||||
hermop.Op(Mn,tmp); \
|
||||
std::cout<<GridLogMessage << oorder<< "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl; \
|
||||
b++; \
|
||||
}
|
||||
|
||||
#define FILTERc(llo,hhi,oorder) \
|
||||
{ \
|
||||
Chebyshev<FineField> Cheb(llo,hhi,oorder); \
|
||||
Cheb(hermop,noise,combined); \
|
||||
}
|
||||
|
||||
double node = 0.000;
|
||||
FILTERb(lo,hi,orderfilter);// 0
|
||||
// FILTERc(node,hi,51);// 0
|
||||
noise = Mn;
|
||||
int base = 0;
|
||||
int mult = 100;
|
||||
FILTER(node,hi,base+1*mult);
|
||||
FILTER(node,hi,base+2*mult);
|
||||
FILTER(node,hi,base+3*mult);
|
||||
FILTER(node,hi,base+4*mult);
|
||||
FILTER(node,hi,base+5*mult);
|
||||
FILTER(node,hi,base+6*mult);
|
||||
FILTER(node,hi,base+7*mult);
|
||||
FILTER(node,hi,base+8*mult);
|
||||
FILTER(node,hi,base+9*mult);
|
||||
FILTER(node,hi,base+10*mult);
|
||||
FILTER(node,hi,base+11*mult);
|
||||
FILTER(node,hi,base+12*mult);
|
||||
FILTER(node,hi,base+13*mult);
|
||||
FILTER(node,hi,base+14*mult);
|
||||
FILTER(node,hi,base+15*mult);
|
||||
assert(b==nn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
double lo,
|
||||
int orderfilter,
|
||||
int ordermin,
|
||||
int orderstep,
|
||||
double filterlo
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
FineField combined(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
int b =0;
|
||||
{
|
||||
Chebyshev<FineField> JacobiPoly(0.005,60.,1500);
|
||||
// JacobiPolynomial<FineField> JacobiPoly(0.002,60.0,1500,-0.5,3.5);
|
||||
//JacobiPolynomial<FineField> JacobiPoly(0.03,60.0,500,-0.5,3.5);
|
||||
// JacobiPolynomial<FineField> JacobiPoly(0.00,60.0,1000,-0.5,3.5);
|
||||
JacobiPoly(hermop,noise,Mn);
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
b++;
|
||||
// scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
||||
// subspace[b] = tmp; b++;
|
||||
// }
|
||||
}
|
||||
|
||||
#define FILTER(lambda) \
|
||||
{ \
|
||||
hermop.HermOp(subspace[0],tmp); \
|
||||
tmp = tmp - lambda *subspace[0]; \
|
||||
scale = std::pow(norm2(tmp),-0.5); \
|
||||
tmp=tmp*scale; \
|
||||
subspace[b] = tmp; \
|
||||
hermop.Op(subspace[b],tmp); \
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl; \
|
||||
b++; \
|
||||
}
|
||||
// scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
||||
// subspace[b] = tmp; b++;
|
||||
// }
|
||||
|
||||
FILTER(2.0e-5);
|
||||
FILTER(2.0e-4);
|
||||
FILTER(4.0e-4);
|
||||
FILTER(8.0e-4);
|
||||
FILTER(8.0e-4);
|
||||
|
||||
FILTER(2.0e-3);
|
||||
FILTER(3.0e-3);
|
||||
FILTER(4.0e-3);
|
||||
FILTER(5.0e-3);
|
||||
FILTER(6.0e-3);
|
||||
|
||||
FILTER(2.5e-3);
|
||||
FILTER(3.5e-3);
|
||||
FILTER(4.5e-3);
|
||||
FILTER(5.5e-3);
|
||||
FILTER(6.5e-3);
|
||||
|
||||
// FILTER(6.0e-5);//6
|
||||
// FILTER(7.0e-5);//8
|
||||
// FILTER(8.0e-5);//9
|
||||
// FILTER(9.0e-5);//3
|
||||
|
||||
/*
|
||||
// FILTER(1.0e-4);//10
|
||||
FILTER(2.0e-4);//11
|
||||
// FILTER(3.0e-4);//12
|
||||
// FILTER(4.0e-4);//13
|
||||
FILTER(5.0e-4);//14
|
||||
|
||||
FILTER(6.0e-3);//4
|
||||
FILTER(7.0e-4);//1
|
||||
FILTER(8.0e-4);//7
|
||||
FILTER(9.0e-4);//15
|
||||
FILTER(1.0e-3);//2
|
||||
|
||||
FILTER(2.0e-3);//2
|
||||
FILTER(3.0e-3);//2
|
||||
FILTER(4.0e-3);//2
|
||||
FILTER(5.0e-3);//2
|
||||
FILTER(6.0e-3);//2
|
||||
|
||||
FILTER(7.0e-3);//2
|
||||
FILTER(8.0e-3);//2
|
||||
FILTER(1.0e-2);//2
|
||||
*/
|
||||
std::cout << GridLogMessage <<"Jacobi filtering done" <<std::endl;
|
||||
assert(b==nn);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
// Fine Object == (per site) type of fine field
|
||||
// nbasis == number of deflation vectors
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class CoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
||||
public:
|
||||
|
||||
typedef iVector<CComplex,nbasis > siteVector;
|
||||
typedef Lattice<CComplex > CoarseComplexField;
|
||||
typedef Lattice<siteVector> CoarseVector;
|
||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||
typedef Lattice<Fobj > FineField;
|
||||
|
||||
////////////////////
|
||||
// Data members
|
||||
////////////////////
|
||||
Geometry geom;
|
||||
GridBase * _grid;
|
||||
int hermitian;
|
||||
|
||||
CartesianStencil<siteVector,siteVector,int> Stencil;
|
||||
|
||||
std::vector<CoarseMatrix> A;
|
||||
|
||||
///////////////////////
|
||||
// Interface
|
||||
///////////////////////
|
||||
GridBase * Grid(void) { return _grid; }; // this is all the linalg routines need to know
|
||||
|
||||
void M (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
conformable(_grid,in.Grid());
|
||||
conformable(in.Grid(),out.Grid());
|
||||
|
||||
SimpleCompressor<siteVector> compressor;
|
||||
|
||||
Stencil.HaloExchange(in,compressor);
|
||||
|
||||
auto in_v = in.View();
|
||||
auto out_v = out.View();
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
|
||||
Vector<Aview> AcceleratorViewContainer;
|
||||
for(int p=0;p<geom.npoint;p++) AcceleratorViewContainer.push_back(A[p].View());
|
||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
int osites=Grid()->oSites();
|
||||
|
||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
int lane=SIMTlane(Nsimd);
|
||||
for(int point=0;point<geom.npoint;point++){
|
||||
|
||||
SE=Stencil.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute,lane);
|
||||
} else {
|
||||
nbr = coalescedRead(Stencil.CommBuf()[SE->_offset],lane);
|
||||
}
|
||||
synchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res,lane);
|
||||
});
|
||||
};
|
||||
|
||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
if(hermitian) {
|
||||
// corresponds to Petrov-Galerkin coarsening
|
||||
return M(in,out);
|
||||
} else {
|
||||
// corresponds to Galerkin coarsening
|
||||
CoarseVector tmp(Grid());
|
||||
G5C(tmp, in);
|
||||
M(tmp, out);
|
||||
G5C(out, out);
|
||||
}
|
||||
};
|
||||
void MdirComms(const CoarseVector &in)
|
||||
{
|
||||
SimpleCompressor<siteVector> compressor;
|
||||
Stencil.HaloExchange(in,compressor);
|
||||
}
|
||||
void MdirCalc(const CoarseVector &in, CoarseVector &out, int point)
|
||||
{
|
||||
conformable(_grid,in.Grid());
|
||||
conformable(_grid,out.Grid());
|
||||
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
Vector<Aview> AcceleratorViewContainer;
|
||||
for(int p=0;p<geom.npoint;p++) AcceleratorViewContainer.push_back(A[p].View());
|
||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
||||
|
||||
auto out_v = out.View();
|
||||
auto in_v = in.View();
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
int lane=SIMTlane(Nsimd);
|
||||
SE=Stencil.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute,lane);
|
||||
} else {
|
||||
nbr = coalescedRead(Stencil.CommBuf()[SE->_offset],lane);
|
||||
}
|
||||
synchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res,lane);
|
||||
});
|
||||
#if 0
|
||||
accelerator_for(ss,Grid()->oSites(),1,{
|
||||
|
||||
siteVector res = Zero();
|
||||
siteVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
SE=Stencil.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local&&SE->_permute) {
|
||||
permute(nbr,in_v[SE->_offset],ptype);
|
||||
} else if(SE->_is_local) {
|
||||
nbr = in_v[SE->_offset];
|
||||
} else {
|
||||
nbr = Stencil.CommBuf()[SE->_offset];
|
||||
}
|
||||
synchronise();
|
||||
|
||||
res = res + Aview_p[point][ss]*nbr;
|
||||
|
||||
out_v[ss]=res;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
void MdirAll(const CoarseVector &in,std::vector<CoarseVector> &out)
|
||||
{
|
||||
this->MdirComms(in);
|
||||
int ndir=geom.npoint-1;
|
||||
if ((out.size()!=ndir)&&(out.size()!=ndir+1)) {
|
||||
std::cout <<"MdirAll out size "<< out.size()<<std::endl;
|
||||
std::cout <<"MdirAll ndir "<< ndir<<std::endl;
|
||||
assert(0);
|
||||
}
|
||||
for(int p=0;p<ndir;p++){
|
||||
MdirCalc(in,out[p],p);
|
||||
}
|
||||
};
|
||||
void Mdir(const CoarseVector &in, CoarseVector &out, int dir, int disp){
|
||||
|
||||
this->MdirComms(in);
|
||||
|
||||
int ndim = in.Grid()->Nd();
|
||||
|
||||
//////////////
|
||||
// 4D action like wilson
|
||||
// 0+ => 0
|
||||
// 0- => 1
|
||||
// 1+ => 2
|
||||
// 1- => 3
|
||||
// etc..
|
||||
//////////////
|
||||
// 5D action like DWF
|
||||
// 1+ => 0
|
||||
// 1- => 1
|
||||
// 2+ => 2
|
||||
// 2- => 3
|
||||
// etc..
|
||||
auto point = [dir, disp, ndim](){
|
||||
if(dir == 0 and disp == 0)
|
||||
return 8;
|
||||
else if ( ndim==4 ) {
|
||||
return (4 * dir + 1 - disp) / 2;
|
||||
} else {
|
||||
return (4 * (dir-1) + 1 - disp) / 2;
|
||||
}
|
||||
}();
|
||||
|
||||
MdirCalc(in,out,point);
|
||||
|
||||
};
|
||||
|
||||
void Mdiag(const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
int point=geom.npoint-1;
|
||||
MdirCalc(in, out, point); // No comms
|
||||
};
|
||||
|
||||
|
||||
CoarsenedMatrix(GridCartesian &CoarseGrid, int hermitian_=0) :
|
||||
|
||||
_grid(&CoarseGrid),
|
||||
geom(CoarseGrid._ndimension),
|
||||
hermitian(hermitian_),
|
||||
Stencil(&CoarseGrid,geom.npoint,Even,geom.directions,geom.displacements,0),
|
||||
A(geom.npoint,&CoarseGrid)
|
||||
{
|
||||
};
|
||||
|
||||
void CoarsenOperator(GridBase *FineGrid,LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
||||
{
|
||||
typedef Lattice<typename Fobj::tensor_reduced> FineComplexField;
|
||||
typedef typename Fobj::scalar_type scalar_type;
|
||||
|
||||
FineComplexField one(FineGrid); one=scalar_type(1.0,0.0);
|
||||
FineComplexField zero(FineGrid); zero=scalar_type(0.0,0.0);
|
||||
|
||||
std::vector<FineComplexField> masks(geom.npoint,FineGrid);
|
||||
FineComplexField imask(FineGrid); // contributions from within this block
|
||||
FineComplexField omask(FineGrid); // contributions from outwith this block
|
||||
|
||||
FineComplexField evenmask(FineGrid);
|
||||
FineComplexField oddmask(FineGrid);
|
||||
|
||||
FineField phi(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
FineField zz(FineGrid); zz=Zero();
|
||||
FineField Mphi(FineGrid);
|
||||
FineField Mphie(FineGrid);
|
||||
FineField Mphio(FineGrid);
|
||||
std::vector<FineField> Mphi_p(geom.npoint,FineGrid);
|
||||
|
||||
Lattice<iScalar<vInteger> > coor (FineGrid);
|
||||
Lattice<iScalar<vInteger> > bcoor(FineGrid);
|
||||
Lattice<iScalar<vInteger> > bcb (FineGrid); bcb = Zero();
|
||||
|
||||
CoarseVector iProj(Grid());
|
||||
CoarseVector oProj(Grid());
|
||||
CoarseVector SelfProj(Grid());
|
||||
CoarseComplexField iZProj(Grid());
|
||||
CoarseComplexField oZProj(Grid());
|
||||
|
||||
CoarseScalar InnerProd(Grid());
|
||||
|
||||
// Orthogonalise the subblocks over the basis
|
||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
||||
|
||||
// Compute the matrix elements of linop between this orthonormal
|
||||
// set of vectors.
|
||||
int self_stencil=-1;
|
||||
for(int p=0;p<geom.npoint;p++)
|
||||
{
|
||||
int dir = geom.directions[p];
|
||||
int disp = geom.displacements[p];
|
||||
A[p]=Zero();
|
||||
if( geom.displacements[p]==0){
|
||||
self_stencil=p;
|
||||
}
|
||||
|
||||
Integer block=(FineGrid->_rdimensions[dir])/(Grid()->_rdimensions[dir]);
|
||||
|
||||
LatticeCoordinate(coor,dir);
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Work out even and odd block checkerboarding for fast diagonal term
|
||||
///////////////////////////////////////////////////////
|
||||
if ( disp==1 ) {
|
||||
bcb = bcb + div(coor,block);
|
||||
}
|
||||
|
||||
if ( disp==0 ) {
|
||||
masks[p]= Zero();
|
||||
} else if ( disp==1 ) {
|
||||
masks[p] = where(mod(coor,block)==(block-1),one,zero);
|
||||
} else if ( disp==-1 ) {
|
||||
masks[p] = where(mod(coor,block)==(Integer)0,one,zero);
|
||||
}
|
||||
}
|
||||
evenmask = where(mod(bcb,2)==(Integer)0,one,zero);
|
||||
oddmask = one-evenmask;
|
||||
|
||||
assert(self_stencil!=-1);
|
||||
|
||||
for(int i=0;i<nbasis;i++){
|
||||
|
||||
phi=Subspace.subspace[i];
|
||||
|
||||
// std::cout << GridLogMessage<< "CoarsenMatrix vector "<<i << std::endl;
|
||||
linop.OpDirAll(phi,Mphi_p);
|
||||
linop.OpDiag (phi,Mphi_p[geom.npoint-1]);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
|
||||
Mphi = Mphi_p[p];
|
||||
|
||||
int dir = geom.directions[p];
|
||||
int disp = geom.displacements[p];
|
||||
|
||||
if ( (disp==-1) || (!hermitian ) ) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Pick out contributions coming from this cell and neighbour cell
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
omask = masks[p];
|
||||
imask = one-omask;
|
||||
|
||||
for(int j=0;j<nbasis;j++){
|
||||
|
||||
blockMaskedInnerProduct(oZProj,omask,Subspace.subspace[j],Mphi);
|
||||
|
||||
auto iZProj_v = iZProj.View() ;
|
||||
auto oZProj_v = oZProj.View() ;
|
||||
auto A_p = A[p].View();
|
||||
auto A_self = A[self_stencil].View();
|
||||
|
||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{ coalescedWrite(A_p[ss](j,i),oZProj_v(ss)); });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Faster alternate self coupling.. use hermiticity to save 2x
|
||||
///////////////////////////////////////////
|
||||
{
|
||||
mult(tmp,phi,evenmask); linop.Op(tmp,Mphie);
|
||||
mult(tmp,phi,oddmask ); linop.Op(tmp,Mphio);
|
||||
|
||||
{
|
||||
auto tmp_ = tmp.View();
|
||||
auto evenmask_ = evenmask.View();
|
||||
auto oddmask_ = oddmask.View();
|
||||
auto Mphie_ = Mphie.View();
|
||||
auto Mphio_ = Mphio.View();
|
||||
accelerator_for(ss, FineGrid->oSites(), Fobj::Nsimd(),{
|
||||
coalescedWrite(tmp_[ss],evenmask_(ss)*Mphie_(ss) + oddmask_(ss)*Mphio_(ss));
|
||||
});
|
||||
}
|
||||
|
||||
blockProject(SelfProj,tmp,Subspace.subspace);
|
||||
|
||||
auto SelfProj_ = SelfProj.View();
|
||||
auto A_self = A[self_stencil].View();
|
||||
|
||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{
|
||||
for(int j=0;j<nbasis;j++){
|
||||
coalescedWrite(A_self[ss](j,i), SelfProj_(ss)(j));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
if(hermitian) {
|
||||
std::cout << GridLogMessage << " ForceHermitian, new code "<<std::endl;
|
||||
ForceHermitian();
|
||||
}
|
||||
// AssertHermitian();
|
||||
// ForceDiagonal();
|
||||
}
|
||||
|
||||
#if 0
|
||||
///////////////////////////
|
||||
// test code worth preserving in if block
|
||||
///////////////////////////
|
||||
std::cout<<GridLogMessage<< " Computed matrix elements "<< self_stencil <<std::endl;
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
std::cout<<GridLogMessage<< "A["<<p<<"]" << std::endl;
|
||||
std::cout<<GridLogMessage<< A[p] << std::endl;
|
||||
}
|
||||
std::cout<<GridLogMessage<< " picking by block0 "<< self_stencil <<std::endl;
|
||||
|
||||
phi=Subspace.subspace[0];
|
||||
std::vector<int> bc(FineGrid->_ndimension,0);
|
||||
|
||||
blockPick(Grid(),phi,tmp,bc); // Pick out a block
|
||||
linop.Op(tmp,Mphi); // Apply big dop
|
||||
blockProject(iProj,Mphi,Subspace.subspace); // project it and print it
|
||||
std::cout<<GridLogMessage<< " Computed matrix elements from block zero only "<<std::endl;
|
||||
std::cout<<GridLogMessage<< iProj <<std::endl;
|
||||
std::cout<<GridLogMessage<<"Computed Coarse Operator"<<std::endl;
|
||||
#endif
|
||||
|
||||
|
||||
void ForceHermitian(void) {
|
||||
CoarseMatrix Diff (Grid());
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
int dir = geom.directions[p];
|
||||
int disp = geom.displacements[p];
|
||||
if(disp==-1) {
|
||||
// Find the opposite link
|
||||
for(int pp=0;pp<geom.npoint;pp++){
|
||||
int dirp = geom.directions[pp];
|
||||
int dispp = geom.displacements[pp];
|
||||
if ( (dirp==dir) && (dispp==1) ){
|
||||
// Diff = adj(Cshift(A[p],dir,1)) - A[pp];
|
||||
// std::cout << GridLogMessage<<" Replacing stencil leg "<<pp<<" with leg "<<p<< " diff "<<norm2(Diff) <<std::endl;
|
||||
A[pp] = adj(Cshift(A[p],dir,1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void AssertHermitian(void) {
|
||||
CoarseMatrix AA (Grid());
|
||||
CoarseMatrix AAc (Grid());
|
||||
CoarseMatrix Diff (Grid());
|
||||
for(int d=0;d<4;d++){
|
||||
|
||||
int dd=d+1;
|
||||
AAc = Cshift(A[2*d+1],dd,1);
|
||||
AA = A[2*d];
|
||||
|
||||
Diff = AA - adj(AAc);
|
||||
|
||||
std::cout<<GridLogMessage<<"Norm diff dim "<<d<<" "<< norm2(Diff)<<std::endl;
|
||||
std::cout<<GridLogMessage<<"Norm dim "<<d<<" "<< norm2(AA)<<std::endl;
|
||||
|
||||
}
|
||||
Diff = A[8] - adj(A[8]);
|
||||
std::cout<<GridLogMessage<<"Norm diff local "<< norm2(Diff)<<std::endl;
|
||||
std::cout<<GridLogMessage<<"Norm local "<< norm2(A[8])<<std::endl;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
+330
-123
@@ -1,4 +1,3 @@
|
||||
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
@@ -29,96 +28,206 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
#ifndef _GRID_FFT_H_
|
||||
#define _GRID_FFT_H_
|
||||
|
||||
#ifdef GRID_CUDA
|
||||
#include <cufft.h>
|
||||
#endif
|
||||
|
||||
#ifdef GRID_HIP
|
||||
#include <hipfft/hipfft.h>
|
||||
#endif
|
||||
|
||||
#if !defined(GRID_CUDA) && !defined(GRID_HIP)
|
||||
#ifdef HAVE_FFTW
|
||||
#ifdef USE_MKL
|
||||
#if defined(USE_MKL) || defined(GRID_SYCL)
|
||||
#include <fftw/fftw3.h>
|
||||
#else
|
||||
#include <fftw3.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class scalar> struct FFTW { };
|
||||
#ifndef FFTW_FORWARD
|
||||
#define FFTW_FORWARD (-1)
|
||||
#define FFTW_BACKWARD (+1)
|
||||
#define FFTW_ESTIMATE (0)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFTW
|
||||
template<class scalar> struct FFTW {
|
||||
};
|
||||
|
||||
#ifdef GRID_HIP
|
||||
template<> struct FFTW<ComplexD> {
|
||||
public:
|
||||
static const int forward=FFTW_FORWARD;
|
||||
static const int backward=FFTW_BACKWARD;
|
||||
typedef hipfftDoubleComplex FFTW_scalar;
|
||||
typedef hipfftHandle FFTW_plan;
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||
FFTW_scalar *in, int *inembed,
|
||||
int istride, int idist,
|
||||
FFTW_scalar *out, int *onembed,
|
||||
int ostride, int odist,
|
||||
int sign, unsigned flags) {
|
||||
FFTW_plan p;
|
||||
auto rv = hipfftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,HIPFFT_Z2Z,howmany);
|
||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||
hipfftResult rv;
|
||||
if ( sign == forward ) rv =hipfftExecZ2Z(p,in,out,HIPFFT_FORWARD);
|
||||
else rv =hipfftExecZ2Z(p,in,out,HIPFFT_BACKWARD);
|
||||
accelerator_barrier();
|
||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||
}
|
||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||
hipfftDestroy(p);
|
||||
}
|
||||
};
|
||||
template<> struct FFTW<ComplexF> {
|
||||
public:
|
||||
static const int forward=FFTW_FORWARD;
|
||||
static const int backward=FFTW_BACKWARD;
|
||||
typedef hipfftComplex FFTW_scalar;
|
||||
typedef hipfftHandle FFTW_plan;
|
||||
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||
FFTW_scalar *in, int *inembed,
|
||||
int istride, int idist,
|
||||
FFTW_scalar *out, int *onembed,
|
||||
int ostride, int odist,
|
||||
int sign, unsigned flags) {
|
||||
FFTW_plan p;
|
||||
auto rv = hipfftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,HIPFFT_C2C,howmany);
|
||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||
hipfftResult rv;
|
||||
if ( sign == forward ) rv =hipfftExecC2C(p,in,out,HIPFFT_FORWARD);
|
||||
else rv =hipfftExecC2C(p,in,out,HIPFFT_BACKWARD);
|
||||
accelerator_barrier();
|
||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||
}
|
||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||
hipfftDestroy(p);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef GRID_CUDA
|
||||
template<> struct FFTW<ComplexD> {
|
||||
public:
|
||||
static const int forward=FFTW_FORWARD;
|
||||
static const int backward=FFTW_BACKWARD;
|
||||
typedef cufftDoubleComplex FFTW_scalar;
|
||||
typedef cufftHandle FFTW_plan;
|
||||
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||
FFTW_scalar *in, int *inembed,
|
||||
int istride, int idist,
|
||||
FFTW_scalar *out, int *onembed,
|
||||
int ostride, int odist,
|
||||
int sign, unsigned flags) {
|
||||
FFTW_plan p;
|
||||
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_Z2Z,howmany);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||
if ( sign == forward ) cufftExecZ2Z(p,in,out,CUFFT_FORWARD);
|
||||
else cufftExecZ2Z(p,in,out,CUFFT_INVERSE);
|
||||
accelerator_barrier();
|
||||
}
|
||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||
cufftDestroy(p);
|
||||
}
|
||||
};
|
||||
template<> struct FFTW<ComplexF> {
|
||||
public:
|
||||
static const int forward=FFTW_FORWARD;
|
||||
static const int backward=FFTW_BACKWARD;
|
||||
typedef cufftComplex FFTW_scalar;
|
||||
typedef cufftHandle FFTW_plan;
|
||||
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||
FFTW_scalar *in, int *inembed,
|
||||
int istride, int idist,
|
||||
FFTW_scalar *out, int *onembed,
|
||||
int ostride, int odist,
|
||||
int sign, unsigned flags) {
|
||||
FFTW_plan p;
|
||||
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_C2C,howmany);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||
if ( sign == forward ) cufftExecC2C(p,in,out,CUFFT_FORWARD);
|
||||
else cufftExecC2C(p,in,out,CUFFT_INVERSE);
|
||||
accelerator_barrier();
|
||||
}
|
||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||
cufftDestroy(p);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !defined(GRID_CUDA) && !defined(GRID_HIP)
|
||||
#ifdef HAVE_FFTW
|
||||
template<> struct FFTW<ComplexD> {
|
||||
public:
|
||||
typedef fftw_complex FFTW_scalar;
|
||||
typedef fftw_plan FFTW_plan;
|
||||
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, const int *n,int howmany,
|
||||
FFTW_scalar *in, const int *inembed,
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||
FFTW_scalar *in, int *inembed,
|
||||
int istride, int idist,
|
||||
FFTW_scalar *out, const int *onembed,
|
||||
FFTW_scalar *out, int *onembed,
|
||||
int ostride, int odist,
|
||||
int sign, unsigned flags) {
|
||||
return ::fftw_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
||||
}
|
||||
|
||||
static void fftw_flops(const FFTW_plan p,double *add, double *mul, double *fmas){
|
||||
::fftw_flops(p,add,mul,fmas);
|
||||
}
|
||||
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out) {
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||
::fftw_execute_dft(p,in,out);
|
||||
}
|
||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||
::fftw_destroy_plan(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FFTW<ComplexF> {
|
||||
public:
|
||||
|
||||
typedef fftwf_complex FFTW_scalar;
|
||||
typedef fftwf_plan FFTW_plan;
|
||||
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, const int *n,int howmany,
|
||||
FFTW_scalar *in, const int *inembed,
|
||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||
FFTW_scalar *in, int *inembed,
|
||||
int istride, int idist,
|
||||
FFTW_scalar *out, const int *onembed,
|
||||
FFTW_scalar *out, int *onembed,
|
||||
int ostride, int odist,
|
||||
int sign, unsigned flags) {
|
||||
return ::fftwf_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
||||
}
|
||||
|
||||
static void fftw_flops(const FFTW_plan p,double *add, double *mul, double *fmas){
|
||||
::fftwf_flops(p,add,mul,fmas);
|
||||
}
|
||||
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out) {
|
||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||
::fftwf_execute_dft(p,in,out);
|
||||
}
|
||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||
::fftwf_destroy_plan(p);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef FFTW_FORWARD
|
||||
#define FFTW_FORWARD (-1)
|
||||
#define FFTW_BACKWARD (+1)
|
||||
#endif
|
||||
|
||||
class FFT {
|
||||
private:
|
||||
|
||||
GridCartesian *vgrid;
|
||||
GridCartesian *sgrid;
|
||||
|
||||
int Nd;
|
||||
double flops;
|
||||
double flops_call;
|
||||
uint64_t usec;
|
||||
|
||||
Coordinate dimensions;
|
||||
Coordinate processors;
|
||||
Coordinate processor_coor;
|
||||
|
||||
public:
|
||||
|
||||
static const int forward=FFTW_FORWARD;
|
||||
@@ -128,31 +237,25 @@ public:
|
||||
double MFlops(void) {return flops/usec;}
|
||||
double USec(void) {return (double)usec;}
|
||||
|
||||
FFT ( GridCartesian * grid ) :
|
||||
vgrid(grid),
|
||||
Nd(grid->_ndimension),
|
||||
dimensions(grid->_fdimensions),
|
||||
processors(grid->_processors),
|
||||
processor_coor(grid->_processor_coor)
|
||||
FFT ( GridCartesian * grid )
|
||||
{
|
||||
flops=0;
|
||||
usec =0;
|
||||
Coordinate layout(Nd,1);
|
||||
sgrid = new GridCartesian(dimensions,layout,processors);
|
||||
};
|
||||
|
||||
~FFT ( void) {
|
||||
delete sgrid;
|
||||
// delete sgrid;
|
||||
}
|
||||
|
||||
template<class vobj>
|
||||
void FFT_dim_mask(Lattice<vobj> &result,const Lattice<vobj> &source,Coordinate mask,int sign){
|
||||
|
||||
conformable(result.Grid(),vgrid);
|
||||
conformable(source.Grid(),vgrid);
|
||||
Lattice<vobj> tmp(vgrid);
|
||||
tmp = source;
|
||||
for(int d=0;d<Nd;d++){
|
||||
// vgrid=result.Grid();
|
||||
// conformable(result.Grid(),vgrid);
|
||||
// conformable(source.Grid(),vgrid);
|
||||
const int Ndim = source.Grid()->Nd();
|
||||
Lattice<vobj> tmp = source;
|
||||
for(int d=0;d<Ndim;d++){
|
||||
if( mask[d] ) {
|
||||
FFT_dim(result,tmp,d,sign);
|
||||
tmp=result;
|
||||
@@ -162,59 +265,70 @@ public:
|
||||
|
||||
template<class vobj>
|
||||
void FFT_all_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int sign){
|
||||
Coordinate mask(Nd,1);
|
||||
const int Ndim = source.Grid()->Nd();
|
||||
Coordinate mask(Ndim,1);
|
||||
FFT_dim_mask(result,source,mask,sign);
|
||||
}
|
||||
|
||||
|
||||
template<class vobj>
|
||||
void FFT_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int dim, int sign){
|
||||
#ifndef HAVE_FFTW
|
||||
assert(0);
|
||||
#else
|
||||
conformable(result.Grid(),vgrid);
|
||||
conformable(source.Grid(),vgrid);
|
||||
const int Ndim = source.Grid()->Nd();
|
||||
GridBase *grid = source.Grid();
|
||||
conformable(result.Grid(),source.Grid());
|
||||
|
||||
int L = vgrid->_ldimensions[dim];
|
||||
int G = vgrid->_fdimensions[dim];
|
||||
|
||||
Coordinate layout(Nd,1);
|
||||
Coordinate pencil_gd(vgrid->_fdimensions);
|
||||
|
||||
pencil_gd[dim] = G*processors[dim];
|
||||
|
||||
// Pencil global vol LxLxGxLxL per node
|
||||
GridCartesian pencil_g(pencil_gd,layout,processors);
|
||||
int L = grid->_ldimensions[dim];
|
||||
int G = grid->_fdimensions[dim];
|
||||
|
||||
Coordinate layout(Ndim,1);
|
||||
|
||||
// Construct pencils
|
||||
typedef typename vobj::scalar_object sobj;
|
||||
typedef typename sobj::scalar_type scalar;
|
||||
typedef typename vobj::scalar_type scalar;
|
||||
typedef typename vobj::scalar_type scalar_type;
|
||||
typedef typename vobj::vector_type vector_type;
|
||||
|
||||
Lattice<sobj> pgbuf(&pencil_g);
|
||||
auto pgbuf_v = pgbuf.View();
|
||||
|
||||
//std::cout << "CPU view" << std::endl;
|
||||
|
||||
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
||||
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
|
||||
|
||||
int Ncomp = sizeof(sobj)/sizeof(scalar);
|
||||
int Nlow = 1;
|
||||
int64_t Nlow = 1;
|
||||
int64_t Nhigh = 1;
|
||||
|
||||
for(int d=0;d<dim;d++){
|
||||
Nlow*=vgrid->_ldimensions[d];
|
||||
Nlow*=grid->_ldimensions[d];
|
||||
}
|
||||
for(int d=dim+1;d<Ndim;d++){
|
||||
Nhigh*=grid->_ldimensions[d];
|
||||
}
|
||||
int64_t Nperp=Nlow*Nhigh;
|
||||
|
||||
deviceVector<scalar> pgbuf; // Layout is [perp][component][dim]
|
||||
pgbuf.resize(Nperp*Ncomp*G);
|
||||
scalar *pgbuf_v = &pgbuf[0];
|
||||
|
||||
int rank = 1; /* 1d transforms */
|
||||
int n[] = {G}; /* 1d transforms of length G */
|
||||
int howmany = Ncomp;
|
||||
int howmany = Ncomp * Nperp;
|
||||
int odist,idist,istride,ostride;
|
||||
idist = odist = 1; /* Distance between consecutive FT's */
|
||||
istride = ostride = Ncomp*Nlow; /* distance between two elements in the same FT */
|
||||
idist = odist = G; /* Distance between consecutive FT's */
|
||||
istride = ostride = 1; /* Distance between two elements in the same FT */
|
||||
int *inembed = n, *onembed = n;
|
||||
|
||||
scalar div;
|
||||
if ( sign == backward ) div = 1.0/G;
|
||||
else if ( sign == forward ) div = 1.0;
|
||||
else assert(0);
|
||||
|
||||
else GRID_ASSERT(0);
|
||||
|
||||
double t_pencil=0;
|
||||
double t_fft =0;
|
||||
double t_total =-usecond();
|
||||
// std::cout << GridLogPerformance<<"Making FFTW plan" << std::endl;
|
||||
/*
|
||||
*
|
||||
*/
|
||||
FFTW_plan p;
|
||||
{
|
||||
FFTW_scalar *in = (FFTW_scalar *)&pgbuf_v[0];
|
||||
@@ -228,61 +342,154 @@ public:
|
||||
}
|
||||
|
||||
// Barrel shift and collect global pencil
|
||||
Coordinate lcoor(Nd), gcoor(Nd);
|
||||
// std::cout << GridLogPerformance<<"Making pencil" << std::endl;
|
||||
Coordinate lcoor(Ndim), gcoor(Ndim);
|
||||
double t_copy=0;
|
||||
double t_shift=0;
|
||||
t_pencil = -usecond();
|
||||
result = source;
|
||||
int pc = processor_coor[dim];
|
||||
int pc = grid->_processor_coor[dim];
|
||||
|
||||
const Coordinate ldims = grid->_ldimensions;
|
||||
const Coordinate rdims = grid->_rdimensions;
|
||||
const Coordinate sdims = grid->_simd_layout;
|
||||
|
||||
Coordinate processors = grid->_processors;
|
||||
Coordinate pgdims(Ndim);
|
||||
pgdims[0] = G;
|
||||
for(int d=0, dd=1;d<Ndim;d++){
|
||||
if ( d!=dim ) pgdims[dd++] = ldims[d];
|
||||
}
|
||||
int64_t pgvol=1;
|
||||
for(int d=0;d<Ndim;d++) pgvol*=pgdims[d];
|
||||
|
||||
const int Nsimd = vobj::Nsimd();
|
||||
for(int p=0;p<processors[dim];p++) {
|
||||
thread_for(idx, sgrid->lSites(),{
|
||||
Coordinate cbuf(Nd);
|
||||
sobj s;
|
||||
sgrid->LocalIndexToLocalCoor(idx,cbuf);
|
||||
peekLocalSite(s,result,cbuf);
|
||||
cbuf[dim]+=((pc+p) % processors[dim])*L;
|
||||
// cbuf[dim]+=p*L;
|
||||
pokeLocalSite(s,pgbuf,cbuf);
|
||||
t_copy-=usecond();
|
||||
autoView(r_v,result,AcceleratorRead);
|
||||
accelerator_for(idx, grid->oSites(), vobj::Nsimd(), {
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<Nsimd;lane++) {
|
||||
#endif
|
||||
Coordinate icoor;
|
||||
Coordinate ocoor;
|
||||
Coordinate pgcoor;
|
||||
|
||||
Lexicographic::CoorFromIndex(icoor,lane,sdims);
|
||||
Lexicographic::CoorFromIndex(ocoor,idx,rdims);
|
||||
|
||||
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + ((pc+p)%processors[dim])*L;
|
||||
for(int d=0,dd=1;d<Ndim;d++){
|
||||
if ( d!=dim ) {
|
||||
pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d];
|
||||
dd++;
|
||||
}
|
||||
}
|
||||
|
||||
// Map coordinates in lattice layout to FFTW index
|
||||
int64_t pgidx;
|
||||
Lexicographic::IndexFromCoor(pgcoor,pgidx,pgdims);
|
||||
|
||||
vector_type *from = (vector_type *)&r_v[idx];
|
||||
scalar_type stmp;
|
||||
for(int w=0;w<Ncomp;w++){
|
||||
int64_t pg_idx = pgidx + w*pgvol;
|
||||
stmp = getlane(from[w], lane);
|
||||
pgbuf_v[pg_idx] = stmp;
|
||||
}
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
|
||||
t_copy+=usecond();
|
||||
if (p != processors[dim] - 1) {
|
||||
result = Cshift(result,dim,L);
|
||||
Lattice<vobj> temp(grid);
|
||||
t_shift-=usecond();
|
||||
temp = Cshift(result,dim,L); result = temp;
|
||||
t_shift+=usecond();
|
||||
}
|
||||
}
|
||||
t_pencil += usecond();
|
||||
|
||||
// Loop over orthog coords
|
||||
int NN=pencil_g.lSites();
|
||||
GridStopWatch timer;
|
||||
timer.Start();
|
||||
thread_for( idx,NN,{
|
||||
Coordinate cbuf(Nd);
|
||||
pencil_g.LocalIndexToLocalCoor(idx, cbuf);
|
||||
if ( cbuf[dim] == 0 ) { // restricts loop to plane at lcoor[dim]==0
|
||||
FFTW_scalar *in = (FFTW_scalar *)&pgbuf_v[idx];
|
||||
FFTW_scalar *out= (FFTW_scalar *)&pgbuf_v[idx];
|
||||
FFTW<scalar>::fftw_execute_dft(p,in,out);
|
||||
}
|
||||
});
|
||||
timer.Stop();
|
||||
|
||||
FFTW_scalar *in = (FFTW_scalar *)pgbuf_v;
|
||||
FFTW_scalar *out= (FFTW_scalar *)pgbuf_v;
|
||||
t_fft = -usecond();
|
||||
FFTW<scalar>::fftw_execute_dft(p,in,out,sign);
|
||||
t_fft += usecond();
|
||||
|
||||
// performance counting
|
||||
double add,mul,fma;
|
||||
FFTW<scalar>::fftw_flops(p,&add,&mul,&fma);
|
||||
flops_call = add+mul+2.0*fma;
|
||||
usec += timer.useconds();
|
||||
flops+= flops_call*NN;
|
||||
|
||||
// writing out result
|
||||
thread_for(idx,sgrid->lSites(),{
|
||||
Coordinate clbuf(Nd), cgbuf(Nd);
|
||||
sobj s;
|
||||
sgrid->LocalIndexToLocalCoor(idx,clbuf);
|
||||
cgbuf = clbuf;
|
||||
cgbuf[dim] = clbuf[dim]+L*pc;
|
||||
peekLocalSite(s,pgbuf,cgbuf);
|
||||
pokeLocalSite(s,result,clbuf);
|
||||
});
|
||||
flops_call = 5.0*howmany*G*log2(G);
|
||||
usec = t_fft;
|
||||
flops= flops_call;
|
||||
|
||||
result = Zero();
|
||||
|
||||
double t_insert = -usecond();
|
||||
{
|
||||
autoView(r_v,result,AcceleratorWrite);
|
||||
accelerator_for(idx,grid->oSites(),Nsimd,{
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<Nsimd;lane++) {
|
||||
#endif
|
||||
Coordinate icoor(Ndim);
|
||||
Coordinate ocoor(Ndim);
|
||||
Coordinate pgcoor(Ndim);
|
||||
|
||||
Lexicographic::CoorFromIndex(icoor,lane,sdims);
|
||||
Lexicographic::CoorFromIndex(ocoor,idx,rdims);
|
||||
|
||||
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + pc*L;
|
||||
for(int d=0,dd=1;d<Ndim;d++){
|
||||
if ( d!=dim ) {
|
||||
pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d];
|
||||
dd++;
|
||||
}
|
||||
}
|
||||
// Map coordinates in lattice layout to FFTW index
|
||||
int64_t pgidx;
|
||||
Lexicographic::IndexFromCoor(pgcoor,pgidx,pgdims);
|
||||
|
||||
vector_type *to = (vector_type *)&r_v[idx];
|
||||
scalar_type stmp;
|
||||
for(int w=0;w<Ncomp;w++){
|
||||
int64_t pg_idx = pgidx + w*pgvol;
|
||||
stmp = pgbuf_v[pg_idx];
|
||||
putlane(to[w], stmp, lane);
|
||||
}
|
||||
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
result = result*div;
|
||||
|
||||
|
||||
t_insert +=usecond();
|
||||
|
||||
// destroying plan
|
||||
FFTW<scalar>::fftw_destroy_plan(p);
|
||||
#endif
|
||||
|
||||
t_total +=usecond();
|
||||
|
||||
std::cout <<GridLogPerformance<< " FFT took "<<t_total/1.0e6 <<" s" << std::endl;
|
||||
std::cout <<GridLogPerformance<< " FFT pencil "<<t_pencil/1.0e6 <<" s" << std::endl;
|
||||
std::cout <<GridLogPerformance<< " of which copy "<<t_copy/1.0e6 <<" s" << std::endl;
|
||||
std::cout <<GridLogPerformance<< " of which shift"<<t_shift/1.0e6 <<" s" << std::endl;
|
||||
std::cout <<GridLogPerformance<< " FFT kernels "<<t_fft/1.0e6 <<" s" << std::endl;
|
||||
std::cout <<GridLogPerformance<< " FFT insert "<<t_insert/1.0e6 <<" s" << std::endl;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
virtual void AdjOp (const Field &in, Field &out) = 0; // Abstract base
|
||||
virtual void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)=0;
|
||||
virtual void HermOp(const Field &in, Field &out)=0;
|
||||
virtual ~LinearOperatorBase(){};
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +64,7 @@ public:
|
||||
//
|
||||
// I'm not entirely happy with implementation; to share the Schur code between herm and non-herm
|
||||
// while still having a "OpAndNorm" in the abstract base I had to implement it in both cases
|
||||
// with an assert trap in the non-herm. This isn't right; there must be a better C++ way to
|
||||
// with an GRID_ASSERT trap in the non-herm. This isn't right; there must be a better C++ way to
|
||||
// do it, but I fear it required multiple inheritance and mixed in abstract base classes
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -102,6 +103,38 @@ public:
|
||||
_Mat.MdagM(in,out);
|
||||
}
|
||||
};
|
||||
template<class Matrix,class Field>
|
||||
class MMdagLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
public:
|
||||
MMdagLinearOperator(Matrix &Mat): _Mat(Mat){};
|
||||
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
_Mat.Mdiag(in,out);
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
_Mat.Mdir(in,out,dir,disp);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
_Mat.MdirAll(in,out);
|
||||
};
|
||||
void Op (const Field &in, Field &out){
|
||||
_Mat.M(in,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
_Mat.Mdag(in,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
_Mat.MMdag(in,out);
|
||||
ComplexD dot = innerProduct(in,out);
|
||||
n1=real(dot);
|
||||
n2=norm2(out);
|
||||
}
|
||||
void HermOp(const Field &in, Field &out){
|
||||
_Mat.MMdag(in,out);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Construct herm op and shift it for mgrid smoother
|
||||
@@ -115,22 +148,22 @@ public:
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
_Mat.Mdiag(in,out);
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
_Mat.Mdir(in,out,dir,disp);
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
};
|
||||
void Op (const Field &in, Field &out){
|
||||
_Mat.M(in,out);
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
_Mat.Mdag(in,out);
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
HermOp(in,out);
|
||||
@@ -144,6 +177,44 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Create a shifted HermOp
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class Field>
|
||||
class ShiftedHermOpLinearOperator : public LinearOperatorBase<Field> {
|
||||
LinearOperatorBase<Field> &_Mat;
|
||||
RealD _shift;
|
||||
public:
|
||||
ShiftedHermOpLinearOperator(LinearOperatorBase<Field> &Mat,RealD shift): _Mat(Mat), _shift(shift){};
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
GRID_ASSERT(0);
|
||||
};
|
||||
void Op (const Field &in, Field &out){
|
||||
HermOp(in,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
HermOp(in,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
HermOp(in,out);
|
||||
ComplexD dot = innerProduct(in,out);
|
||||
n1=real(dot);
|
||||
n2=norm2(out);
|
||||
}
|
||||
void HermOp(const Field &in, Field &out){
|
||||
_Mat.HermOp(in,out);
|
||||
out = out + _shift*in;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Wrap an already herm matrix
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@@ -200,10 +271,42 @@ public:
|
||||
_Mat.Mdag(in,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void HermOp(const Field &in, Field &out){
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
};
|
||||
template<class Matrix,class Field>
|
||||
class ShiftedNonHermitianLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
RealD shift;
|
||||
public:
|
||||
ShiftedNonHermitianLinearOperator(Matrix &Mat,RealD shft): _Mat(Mat),shift(shft){};
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
_Mat.Mdiag(in,out);
|
||||
out = out + shift*in;
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
_Mat.Mdir(in,out,dir,disp);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
_Mat.MdirAll(in,out);
|
||||
};
|
||||
void Op (const Field &in, Field &out){
|
||||
_Mat.M(in,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
_Mat.Mdag(in,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void HermOp(const Field &in, Field &out){
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -242,13 +345,13 @@ class SchurOperatorBase : public LinearOperatorBase<Field> {
|
||||
}
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
assert(0); // must coarsen the unpreconditioned system
|
||||
GRID_ASSERT(0); // must coarsen the unpreconditioned system
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
};
|
||||
};
|
||||
template<class Matrix,class Field>
|
||||
@@ -344,10 +447,10 @@ class NonHermitianSchurOperatorBase : public LinearOperatorBase<Field>
|
||||
MpcDag(tmp,out);
|
||||
}
|
||||
virtual void HermOpAndNorm(const Field& in, Field& out, RealD& n1, RealD& n2) {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
virtual void HermOp(const Field& in, Field& out) {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void Op(const Field& in, Field& out) {
|
||||
Mpc(in, out);
|
||||
@@ -357,13 +460,13 @@ class NonHermitianSchurOperatorBase : public LinearOperatorBase<Field>
|
||||
}
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag(const Field& in, Field& out) {
|
||||
assert(0); // must coarsen the unpreconditioned system
|
||||
GRID_ASSERT(0); // must coarsen the unpreconditioned system
|
||||
}
|
||||
void OpDir(const Field& in, Field& out, int dir, int disp) {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
void OpDirAll(const Field& in, std::vector<Field>& out){
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -477,7 +580,7 @@ class SchurStaggeredOperator : public SchurOperatorBase<Field> {
|
||||
public:
|
||||
SchurStaggeredOperator (Matrix &Mat): _Mat(Mat), tmp(_Mat.RedBlackGrid())
|
||||
{
|
||||
assert( _Mat.isTrivialEE() );
|
||||
GRID_ASSERT( _Mat.isTrivialEE() );
|
||||
mass = _Mat.Mass();
|
||||
}
|
||||
virtual void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
@@ -507,8 +610,8 @@ class SchurStaggeredOperator : public SchurOperatorBase<Field> {
|
||||
virtual void MpcDag (const Field &in, Field &out){
|
||||
Mpc(in,out);
|
||||
}
|
||||
virtual void MpcDagMpc(const Field &in, Field &out,RealD &ni,RealD &no) {
|
||||
assert(0);// Never need with staggered
|
||||
virtual void MpcDagMpc(const Field &in, Field &out) {
|
||||
GRID_ASSERT(0);// Never need with staggered
|
||||
}
|
||||
};
|
||||
template<class Matrix,class Field> using SchurStagOperator = SchurStaggeredOperator<Matrix,Field>;
|
||||
@@ -520,16 +623,28 @@ template<class Field> class OperatorFunction {
|
||||
public:
|
||||
virtual void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) = 0;
|
||||
virtual void operator() (LinearOperatorBase<Field> &Linop, const std::vector<Field> &in,std::vector<Field> &out) {
|
||||
assert(in.size()==out.size());
|
||||
GRID_ASSERT(in.size()==out.size());
|
||||
for(int k=0;k<in.size();k++){
|
||||
(*this)(Linop,in[k],out[k]);
|
||||
}
|
||||
};
|
||||
virtual ~OperatorFunction(){};
|
||||
};
|
||||
|
||||
template<class Field> class LinearFunction {
|
||||
public:
|
||||
virtual void operator() (const Field &in, Field &out) = 0;
|
||||
|
||||
virtual void operator() (const std::vector<Field> &in, std::vector<Field> &out)
|
||||
{
|
||||
GRID_ASSERT(in.size() == out.size());
|
||||
|
||||
for (unsigned int i = 0; i < in.size(); ++i)
|
||||
{
|
||||
(*this)(in[i], out[i]);
|
||||
}
|
||||
}
|
||||
virtual ~LinearFunction(){};
|
||||
};
|
||||
|
||||
template<class Field> class IdentityLinearFunction : public LinearFunction<Field> {
|
||||
@@ -575,6 +690,7 @@ class HermOpOperatorFunction : public OperatorFunction<Field> {
|
||||
template<typename Field>
|
||||
class PlainHermOp : public LinearFunction<Field> {
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
LinearOperatorBase<Field> &_Linop;
|
||||
|
||||
PlainHermOp(LinearOperatorBase<Field>& linop) : _Linop(linop)
|
||||
@@ -588,6 +704,7 @@ public:
|
||||
template<typename Field>
|
||||
class FunctionHermOp : public LinearFunction<Field> {
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
OperatorFunction<Field> & _poly;
|
||||
LinearOperatorBase<Field> &_Linop;
|
||||
|
||||
|
||||
@@ -30,13 +30,19 @@ Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class Field> class Preconditioner : public LinearFunction<Field> {
|
||||
template<class Field> using Preconditioner = LinearFunction<Field> ;
|
||||
|
||||
/*
|
||||
template<class Field> class Preconditioner : public LinearFunction<Field> {
|
||||
using LinearFunction<Field>::operator();
|
||||
virtual void operator()(const Field &src, Field & psi)=0;
|
||||
};
|
||||
*/
|
||||
|
||||
template<class Field> class TrivialPrecon : public Preconditioner<Field> {
|
||||
public:
|
||||
void operator()(const Field &src, Field & psi){
|
||||
using Preconditioner<Field>::operator();
|
||||
virtual void operator()(const Field &src, Field & psi){
|
||||
psi = src;
|
||||
}
|
||||
TrivialPrecon(void){};
|
||||
|
||||
@@ -45,9 +45,15 @@ public:
|
||||
M(in,tmp);
|
||||
Mdag(tmp,out);
|
||||
}
|
||||
virtual void MMdag(const Field &in, Field &out) {
|
||||
Field tmp (in.Grid());
|
||||
Mdag(in,tmp);
|
||||
M(tmp,out);
|
||||
}
|
||||
virtual void Mdiag (const Field &in, Field &out)=0;
|
||||
virtual void Mdir (const Field &in, Field &out,int dir, int disp)=0;
|
||||
virtual void MdirAll (const Field &in, std::vector<Field> &out)=0;
|
||||
virtual ~SparseMatrixBase() {};
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -72,7 +78,7 @@ public:
|
||||
virtual void MeooeDag (const Field &in, Field &out)=0;
|
||||
virtual void MooeeDag (const Field &in, Field &out)=0;
|
||||
virtual void MooeeInvDag (const Field &in, Field &out)=0;
|
||||
|
||||
virtual ~CheckerBoardedSparseMatrixBase() {};
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
RealD diff = hi-lo;
|
||||
RealD delta = diff*1.0e-9;
|
||||
for (RealD x=lo; x<hi; x+=delta) {
|
||||
delta*=1.1;
|
||||
delta*=1.02;
|
||||
RealD f = approx(x);
|
||||
out<< x<<" "<<f<<std::endl;
|
||||
}
|
||||
@@ -90,9 +90,8 @@ public:
|
||||
order=_order;
|
||||
|
||||
if(order < 2) exit(-1);
|
||||
Coeffs.resize(order);
|
||||
Coeffs.assign(0.,order);
|
||||
Coeffs[order-1] = 1.;
|
||||
Coeffs.resize(order,0.0);
|
||||
Coeffs[order-1] = 1.0;
|
||||
};
|
||||
|
||||
// PB - more efficient low pass drops high modes above the low as 1/x uses all Chebyshev's.
|
||||
@@ -132,6 +131,26 @@ public:
|
||||
Coeffs[j] = s * 2.0/order;
|
||||
}
|
||||
};
|
||||
template<class functor>
|
||||
void Init(RealD _lo,RealD _hi,int _order, functor & func)
|
||||
{
|
||||
lo=_lo;
|
||||
hi=_hi;
|
||||
order=_order;
|
||||
|
||||
if(order < 2) exit(-1);
|
||||
Coeffs.resize(order);
|
||||
for(int j=0;j<order;j++){
|
||||
RealD s=0;
|
||||
for(int k=0;k<order;k++){
|
||||
RealD y=std::cos(M_PI*(k+0.5)/order);
|
||||
RealD x=0.5*(y*(hi-lo)+(hi+lo));
|
||||
RealD f=func(x);
|
||||
s=s+f*std::cos( j*M_PI*(k+0.5)/order );
|
||||
}
|
||||
Coeffs[j] = s * 2.0/order;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void JacksonSmooth(void){
|
||||
@@ -250,7 +269,9 @@ public:
|
||||
RealD xscale = 2.0/(hi-lo);
|
||||
RealD mscale = -(hi+lo)/(hi-lo);
|
||||
Linop.HermOp(T0,y);
|
||||
grid->Barrier();
|
||||
axpby(T1,xscale,mscale,y,in);
|
||||
grid->Barrier();
|
||||
|
||||
// sum = .5 c[0] T0 + c[1] T1
|
||||
// out = ()*T0 + Coeffs[1]*T1;
|
||||
@@ -258,26 +279,12 @@ public:
|
||||
for(int n=2;n<order;n++){
|
||||
|
||||
Linop.HermOp(*Tn,y);
|
||||
#if 0
|
||||
auto y_v = y.View();
|
||||
auto Tn_v = Tn->View();
|
||||
auto Tnp_v = Tnp->View();
|
||||
auto Tnm_v = Tnm->View();
|
||||
constexpr int Nsimd = vector_type::Nsimd();
|
||||
accelerator_forNB(ss, in.Grid()->oSites(), Nsimd, {
|
||||
coalescedWrite(y_v[ss],xscale*y_v(ss)+mscale*Tn_v(ss));
|
||||
coalescedWrite(Tnp_v[ss],2.0*y_v(ss)-Tnm_v(ss));
|
||||
});
|
||||
if ( Coeffs[n] != 0.0) {
|
||||
axpy(out,Coeffs[n],*Tnp,out);
|
||||
}
|
||||
#else
|
||||
axpby(y,xscale,mscale,y,(*Tn));
|
||||
axpby(*Tnp,2.0,-1.0,y,(*Tnm));
|
||||
if ( Coeffs[n] != 0.0) {
|
||||
axpy(out,Coeffs[n],*Tnp,out);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Cycle pointers to avoid copies
|
||||
Field *swizzle = Tnm;
|
||||
Tnm =Tn;
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
RealD norm;
|
||||
RealD lo,hi;
|
||||
|
||||
MultiShiftFunction(int n,RealD _lo,RealD _hi): poles(n), residues(n), lo(_lo), hi(_hi) {;};
|
||||
MultiShiftFunction(int n,RealD _lo,RealD _hi): poles(n), residues(n), tolerances(n), lo(_lo), hi(_hi) {;};
|
||||
RealD approx(RealD x);
|
||||
void csv(std::ostream &out);
|
||||
void gnuplot(std::ostream &out);
|
||||
|
||||
@@ -121,7 +121,7 @@ double AlgRemez::generateApprox(int num_degree, int den_degree,
|
||||
// Reallocate arrays, since degree has changed
|
||||
if (num_degree != n || den_degree != d) allocate(num_degree,den_degree);
|
||||
|
||||
assert(a_len<=SUM_MAX);
|
||||
GRID_ASSERT(a_len<=SUM_MAX);
|
||||
|
||||
step = new bigfloat[num_degree+den_degree+2];
|
||||
|
||||
@@ -151,9 +151,9 @@ double AlgRemez::generateApprox(int num_degree, int den_degree,
|
||||
equations();
|
||||
if (delta < tolerance) {
|
||||
std::cout<<"Delta too small, try increasing precision\n";
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
};
|
||||
assert( delta>= tolerance);
|
||||
GRID_ASSERT( delta>= tolerance);
|
||||
|
||||
search(step);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class AlgRemez
|
||||
virtual ~AlgRemez();
|
||||
|
||||
int getDegree(void){
|
||||
assert(n==d);
|
||||
GRID_ASSERT(n==d);
|
||||
return n;
|
||||
}
|
||||
// Reset the bounds of the approximation
|
||||
|
||||
@@ -28,11 +28,11 @@ void AlgRemezGeneral::setupPolyProperties(int num_degree, int den_degree, PolyTy
|
||||
pow_n = num_degree;
|
||||
pow_d = den_degree;
|
||||
|
||||
if(pow_n % 2 == 0 && num_type_in == PolyType::Odd) assert(0);
|
||||
if(pow_n % 2 == 1 && num_type_in == PolyType::Even) assert(0);
|
||||
if(pow_n % 2 == 0 && num_type_in == PolyType::Odd) GRID_ASSERT(0);
|
||||
if(pow_n % 2 == 1 && num_type_in == PolyType::Even) GRID_ASSERT(0);
|
||||
|
||||
if(pow_d % 2 == 0 && den_type_in == PolyType::Odd) assert(0);
|
||||
if(pow_d % 2 == 1 && den_type_in == PolyType::Even) assert(0);
|
||||
if(pow_d % 2 == 0 && den_type_in == PolyType::Odd) GRID_ASSERT(0);
|
||||
if(pow_d % 2 == 1 && den_type_in == PolyType::Even) GRID_ASSERT(0);
|
||||
|
||||
num_type = num_type_in;
|
||||
den_type = den_type_in;
|
||||
@@ -112,9 +112,9 @@ double AlgRemezGeneral::generateApprox(const int num_degree, const int den_degre
|
||||
equations();
|
||||
if (delta < tolerance) {
|
||||
std::cout<<"Iteration " << iter-1 << " delta too small (" << delta << "<" << tolerance << "), try increasing precision\n";
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
};
|
||||
assert( delta>= tolerance );
|
||||
GRID_ASSERT( delta>= tolerance );
|
||||
|
||||
search();
|
||||
}
|
||||
@@ -278,7 +278,7 @@ void AlgRemezGeneral::equations(){
|
||||
if(num_pows[j] != -1){ *aa++ = z; t++; }
|
||||
z *= x;
|
||||
}
|
||||
assert(t == n+1);
|
||||
GRID_ASSERT(t == n+1);
|
||||
|
||||
z = (bigfloat)1l;
|
||||
t = 0;
|
||||
@@ -286,7 +286,7 @@ void AlgRemezGeneral::equations(){
|
||||
if(den_pows[j] != -1){ *aa++ = -y * z; t++; }
|
||||
z *= x;
|
||||
}
|
||||
assert(t == d);
|
||||
GRID_ASSERT(t == d);
|
||||
|
||||
B[i] = y * z; // Right hand side vector
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class AlgRemezGeneral{
|
||||
bigfloat (*f)(bigfloat x, void *data), void *data);
|
||||
|
||||
inline int getDegree(void) const{
|
||||
assert(n==d);
|
||||
GRID_ASSERT(n==d);
|
||||
return n;
|
||||
}
|
||||
// Reset the bounds of the approximation
|
||||
|
||||
@@ -74,7 +74,7 @@ bigfloat epsilonMobius(bigfloat x, void* data){
|
||||
void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out,
|
||||
const std::vector<RealD> &omega_in, const int Ls_in,
|
||||
const RealD lambda_bound){
|
||||
assert(omega_in.size() == Ls_in);
|
||||
GRID_ASSERT(omega_in.size() == Ls_in);
|
||||
omega_out.resize(Ls_out);
|
||||
|
||||
//Use the Remez algorithm to generate the appropriate rational polynomial
|
||||
|
||||
@@ -293,7 +293,7 @@ static void sncndnFK(INTERNAL_PRECISION u, INTERNAL_PRECISION k,
|
||||
* Set type = 0 for the Zolotarev approximation, which is zero at x = 0, and
|
||||
* type = 1 for the approximation which is infinite at x = 0. */
|
||||
|
||||
zolotarev_data* zolotarev(PRECISION epsilon, int n, int type) {
|
||||
zolotarev_data* zolotarev(ZOLO_PRECISION epsilon, int n, int type) {
|
||||
INTERNAL_PRECISION A, c, cp, kp, ksq, sn, cn, dn, Kp, Kj, z, z0, t, M, F,
|
||||
l, invlambda, xi, xisq, *tv, s, opl;
|
||||
int m, czero, ts;
|
||||
@@ -375,12 +375,12 @@ zolotarev_data* zolotarev(PRECISION epsilon, int n, int type) {
|
||||
construct_partfrac(d);
|
||||
construct_contfrac(d);
|
||||
|
||||
/* Converting everything to PRECISION for external use only */
|
||||
/* Converting everything to ZOLO_PRECISION for external use only */
|
||||
|
||||
zd = (zolotarev_data*) malloc(sizeof(zolotarev_data));
|
||||
zd -> A = (PRECISION) d -> A;
|
||||
zd -> Delta = (PRECISION) d -> Delta;
|
||||
zd -> epsilon = (PRECISION) d -> epsilon;
|
||||
zd -> A = (ZOLO_PRECISION) d -> A;
|
||||
zd -> Delta = (ZOLO_PRECISION) d -> Delta;
|
||||
zd -> epsilon = (ZOLO_PRECISION) d -> epsilon;
|
||||
zd -> n = d -> n;
|
||||
zd -> type = d -> type;
|
||||
zd -> dn = d -> dn;
|
||||
@@ -390,24 +390,24 @@ zolotarev_data* zolotarev(PRECISION epsilon, int n, int type) {
|
||||
zd -> deg_num = d -> deg_num;
|
||||
zd -> deg_denom = d -> deg_denom;
|
||||
|
||||
zd -> a = (PRECISION*) malloc(zd -> dn * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (PRECISION) d -> a[m];
|
||||
zd -> a = (ZOLO_PRECISION*) malloc(zd -> dn * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (ZOLO_PRECISION) d -> a[m];
|
||||
free(d -> a);
|
||||
|
||||
zd -> ap = (PRECISION*) malloc(zd -> dd * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (PRECISION) d -> ap[m];
|
||||
zd -> ap = (ZOLO_PRECISION*) malloc(zd -> dd * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (ZOLO_PRECISION) d -> ap[m];
|
||||
free(d -> ap);
|
||||
|
||||
zd -> alpha = (PRECISION*) malloc(zd -> da * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (PRECISION) d -> alpha[m];
|
||||
zd -> alpha = (ZOLO_PRECISION*) malloc(zd -> da * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (ZOLO_PRECISION) d -> alpha[m];
|
||||
free(d -> alpha);
|
||||
|
||||
zd -> beta = (PRECISION*) malloc(zd -> db * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (PRECISION) d -> beta[m];
|
||||
zd -> beta = (ZOLO_PRECISION*) malloc(zd -> db * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (ZOLO_PRECISION) d -> beta[m];
|
||||
free(d -> beta);
|
||||
|
||||
zd -> gamma = (PRECISION*) malloc(zd -> n * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (PRECISION) d -> gamma[m];
|
||||
zd -> gamma = (ZOLO_PRECISION*) malloc(zd -> n * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (ZOLO_PRECISION) d -> gamma[m];
|
||||
free(d -> gamma);
|
||||
|
||||
free(d);
|
||||
@@ -426,7 +426,7 @@ void zolotarev_free(zolotarev_data *zdata)
|
||||
}
|
||||
|
||||
|
||||
zolotarev_data* higham(PRECISION epsilon, int n) {
|
||||
zolotarev_data* higham(ZOLO_PRECISION epsilon, int n) {
|
||||
INTERNAL_PRECISION A, M, c, cp, z, z0, t, epssq;
|
||||
int m, czero;
|
||||
zolotarev_data *zd;
|
||||
@@ -481,9 +481,9 @@ zolotarev_data* higham(PRECISION epsilon, int n) {
|
||||
/* Converting everything to PRECISION for external use only */
|
||||
|
||||
zd = (zolotarev_data*) malloc(sizeof(zolotarev_data));
|
||||
zd -> A = (PRECISION) d -> A;
|
||||
zd -> Delta = (PRECISION) d -> Delta;
|
||||
zd -> epsilon = (PRECISION) d -> epsilon;
|
||||
zd -> A = (ZOLO_PRECISION) d -> A;
|
||||
zd -> Delta = (ZOLO_PRECISION) d -> Delta;
|
||||
zd -> epsilon = (ZOLO_PRECISION) d -> epsilon;
|
||||
zd -> n = d -> n;
|
||||
zd -> type = d -> type;
|
||||
zd -> dn = d -> dn;
|
||||
@@ -493,24 +493,24 @@ zolotarev_data* higham(PRECISION epsilon, int n) {
|
||||
zd -> deg_num = d -> deg_num;
|
||||
zd -> deg_denom = d -> deg_denom;
|
||||
|
||||
zd -> a = (PRECISION*) malloc(zd -> dn * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (PRECISION) d -> a[m];
|
||||
zd -> a = (ZOLO_PRECISION*) malloc(zd -> dn * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (ZOLO_PRECISION) d -> a[m];
|
||||
free(d -> a);
|
||||
|
||||
zd -> ap = (PRECISION*) malloc(zd -> dd * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (PRECISION) d -> ap[m];
|
||||
zd -> ap = (ZOLO_PRECISION*) malloc(zd -> dd * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (ZOLO_PRECISION) d -> ap[m];
|
||||
free(d -> ap);
|
||||
|
||||
zd -> alpha = (PRECISION*) malloc(zd -> da * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (PRECISION) d -> alpha[m];
|
||||
zd -> alpha = (ZOLO_PRECISION*) malloc(zd -> da * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (ZOLO_PRECISION) d -> alpha[m];
|
||||
free(d -> alpha);
|
||||
|
||||
zd -> beta = (PRECISION*) malloc(zd -> db * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (PRECISION) d -> beta[m];
|
||||
zd -> beta = (ZOLO_PRECISION*) malloc(zd -> db * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (ZOLO_PRECISION) d -> beta[m];
|
||||
free(d -> beta);
|
||||
|
||||
zd -> gamma = (PRECISION*) malloc(zd -> n * sizeof(PRECISION));
|
||||
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (PRECISION) d -> gamma[m];
|
||||
zd -> gamma = (ZOLO_PRECISION*) malloc(zd -> n * sizeof(ZOLO_PRECISION));
|
||||
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (ZOLO_PRECISION) d -> gamma[m];
|
||||
free(d -> gamma);
|
||||
|
||||
free(d);
|
||||
@@ -523,17 +523,17 @@ NAMESPACE_END(Grid);
|
||||
#ifdef TEST
|
||||
|
||||
#undef ZERO
|
||||
#define ZERO ((PRECISION) 0)
|
||||
#define ZERO ((ZOLO_PRECISION) 0)
|
||||
#undef ONE
|
||||
#define ONE ((PRECISION) 1)
|
||||
#define ONE ((ZOLO_PRECISION) 1)
|
||||
#undef TWO
|
||||
#define TWO ((PRECISION) 2)
|
||||
#define TWO ((ZOLO_PRECISION) 2)
|
||||
|
||||
/* Evaluate the rational approximation R(x) using the factored form */
|
||||
|
||||
static PRECISION zolotarev_eval(PRECISION x, zolotarev_data* rdata) {
|
||||
static ZOLO_PRECISION zolotarev_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
||||
int m;
|
||||
PRECISION R;
|
||||
ZOLO_PRECISION R;
|
||||
|
||||
if (rdata -> type == 0) {
|
||||
R = rdata -> A * x;
|
||||
@@ -551,9 +551,9 @@ static PRECISION zolotarev_eval(PRECISION x, zolotarev_data* rdata) {
|
||||
|
||||
/* Evaluate the rational approximation R(x) using the partial fraction form */
|
||||
|
||||
static PRECISION zolotarev_partfrac_eval(PRECISION x, zolotarev_data* rdata) {
|
||||
static ZOLO_PRECISION zolotarev_partfrac_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
||||
int m;
|
||||
PRECISION R = rdata -> alpha[rdata -> da - 1];
|
||||
ZOLO_PRECISION R = rdata -> alpha[rdata -> da - 1];
|
||||
for (m = 0; m < rdata -> dd; m++)
|
||||
R += rdata -> alpha[m] / (x * x - rdata -> ap[m]);
|
||||
if (rdata -> type == 1) R += rdata -> alpha[rdata -> dd] / (x * x);
|
||||
@@ -568,18 +568,18 @@ static PRECISION zolotarev_partfrac_eval(PRECISION x, zolotarev_data* rdata) {
|
||||
* non-signalling overflow this will work correctly since 1/(1/0) = 1/INF = 0,
|
||||
* but with signalling overflow you will get an error message. */
|
||||
|
||||
static PRECISION zolotarev_contfrac_eval(PRECISION x, zolotarev_data* rdata) {
|
||||
static ZOLO_PRECISION zolotarev_contfrac_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
||||
int m;
|
||||
PRECISION R = rdata -> beta[0] * x;
|
||||
ZOLO_PRECISION R = rdata -> beta[0] * x;
|
||||
for (m = 1; m < rdata -> db; m++) R = rdata -> beta[m] * x + ONE / R;
|
||||
return R;
|
||||
}
|
||||
|
||||
/* Evaluate the rational approximation R(x) using Cayley form */
|
||||
|
||||
static PRECISION zolotarev_cayley_eval(PRECISION x, zolotarev_data* rdata) {
|
||||
static ZOLO_PRECISION zolotarev_cayley_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
||||
int m;
|
||||
PRECISION T;
|
||||
ZOLO_PRECISION T;
|
||||
|
||||
T = rdata -> type == 0 ? ONE : -ONE;
|
||||
for (m = 0; m < rdata -> n; m++)
|
||||
@@ -607,7 +607,7 @@ int main(int argc, char** argv) {
|
||||
int m, n, plotpts = 5000, type = 0;
|
||||
float eps, x, ypferr, ycferr, ycaylerr, maxypferr, maxycferr, maxycaylerr;
|
||||
zolotarev_data *rdata;
|
||||
PRECISION y;
|
||||
ZOLO_PRECISION y;
|
||||
FILE *plot_function, *plot_error,
|
||||
*plot_partfrac, *plot_contfrac, *plot_cayley;
|
||||
|
||||
@@ -626,13 +626,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
rdata = type == 2
|
||||
? higham((PRECISION) eps, n)
|
||||
: zolotarev((PRECISION) eps, n, type);
|
||||
? higham((ZOLO_PRECISION) eps, n)
|
||||
: zolotarev((ZOLO_PRECISION) eps, n, type);
|
||||
|
||||
printf("Zolotarev Test: R(epsilon = %g, n = %d, type = %d)\n\t"
|
||||
STRINGIFY(VERSION) "\n\t" STRINGIFY(HVERSION)
|
||||
"\n\tINTERNAL_PRECISION = " STRINGIFY(INTERNAL_PRECISION)
|
||||
"\tPRECISION = " STRINGIFY(PRECISION)
|
||||
"\tZOLO_PRECISION = " STRINGIFY(ZOLO_PRECISION)
|
||||
"\n\n\tRational approximation of degree (%d,%d), %s at x = 0\n"
|
||||
"\tDelta = %g (maximum error)\n\n"
|
||||
"\tA = %g (overall factor)\n",
|
||||
@@ -681,15 +681,15 @@ int main(int argc, char** argv) {
|
||||
x = 2.4 * (float) m / plotpts - 1.2;
|
||||
if (rdata -> type == 0 || fabs(x) * (float) plotpts > 1.0) {
|
||||
/* skip x = 0 for type 1, as R(0) is singular */
|
||||
y = zolotarev_eval((PRECISION) x, rdata);
|
||||
y = zolotarev_eval((ZOLO_PRECISION) x, rdata);
|
||||
fprintf(plot_function, "%g %g\n", x, (float) y);
|
||||
fprintf(plot_error, "%g %g\n",
|
||||
x, (float)((y - ((x > 0.0 ? ONE : -ONE))) / rdata -> Delta));
|
||||
ypferr = (float)((zolotarev_partfrac_eval((PRECISION) x, rdata) - y)
|
||||
ypferr = (float)((zolotarev_partfrac_eval((ZOLO_PRECISION) x, rdata) - y)
|
||||
/ rdata -> Delta);
|
||||
ycferr = (float)((zolotarev_contfrac_eval((PRECISION) x, rdata) - y)
|
||||
ycferr = (float)((zolotarev_contfrac_eval((ZOLO_PRECISION) x, rdata) - y)
|
||||
/ rdata -> Delta);
|
||||
ycaylerr = (float)((zolotarev_cayley_eval((PRECISION) x, rdata) - y)
|
||||
ycaylerr = (float)((zolotarev_cayley_eval((ZOLO_PRECISION) x, rdata) - y)
|
||||
/ rdata -> Delta);
|
||||
if (fabs(x) < 1.0 && fabs(x) > rdata -> epsilon) {
|
||||
maxypferr = MAX(maxypferr, fabs(ypferr));
|
||||
|
||||
@@ -9,10 +9,10 @@ NAMESPACE_BEGIN(Approx);
|
||||
#define HVERSION Header Time-stamp: <14-OCT-2004 09:26:51.00 adk@MISSCONTRARY>
|
||||
|
||||
#ifndef ZOLOTAREV_INTERNAL
|
||||
#ifndef PRECISION
|
||||
#define PRECISION double
|
||||
#ifndef ZOLO_PRECISION
|
||||
#define ZOLO_PRECISION double
|
||||
#endif
|
||||
#define ZPRECISION PRECISION
|
||||
#define ZPRECISION ZOLO_PRECISION
|
||||
#define ZOLOTAREV_DATA zolotarev_data
|
||||
#endif
|
||||
|
||||
@@ -77,8 +77,8 @@ typedef struct {
|
||||
* zolotarev_data structure. The arguments must satisfy the constraints that
|
||||
* epsilon > 0, n > 0, and type = 0 or 1. */
|
||||
|
||||
ZOLOTAREV_DATA* higham(PRECISION epsilon, int n) ;
|
||||
ZOLOTAREV_DATA* zolotarev(PRECISION epsilon, int n, int type);
|
||||
ZOLOTAREV_DATA* higham(ZOLO_PRECISION epsilon, int n) ;
|
||||
ZOLOTAREV_DATA* zolotarev(ZOLO_PRECISION epsilon, int n, int type);
|
||||
void zolotarev_free(zolotarev_data *zdata);
|
||||
#endif
|
||||
|
||||
@@ -86,3 +86,4 @@ void zolotarev_free(zolotarev_data *zdata);
|
||||
NAMESPACE_END(Approx);
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: BatchedBlas.h
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#include <Grid/GridCore.h>
|
||||
#include <Grid/algorithms/blas/BatchedBlas.h>
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
gridblasHandle_t GridBLAS::gridblasHandle;
|
||||
int GridBLAS::gridblasInit;
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,300 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: MomentumProject.h
|
||||
|
||||
Copyright (C) 2025
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
/*
|
||||
MultiMomProject
|
||||
|
||||
Import vectors -> nxyz x (ncomponent x nt)
|
||||
Import complex phases -> nmom x nxy
|
||||
|
||||
apply = via (possibly batched) GEMM
|
||||
*/
|
||||
template<class Field, class ComplexField>
|
||||
class MomentumProject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename Field::scalar_type scalar;
|
||||
typedef typename Field::scalar_object scalar_object;
|
||||
|
||||
GridBase *grid;
|
||||
uint64_t nmom;
|
||||
uint64_t nxyz;
|
||||
uint64_t nt;
|
||||
uint64_t nbtw;
|
||||
uint64_t words;
|
||||
|
||||
deviceVector<scalar> BLAS_V; //
|
||||
deviceVector<scalar> BLAS_M; //
|
||||
deviceVector<scalar> BLAS_P; //
|
||||
|
||||
MomentumProject(){};
|
||||
~MomentumProject(){ Deallocate(); };
|
||||
|
||||
void Deallocate(void)
|
||||
{
|
||||
grid=nullptr;
|
||||
nmom=0;
|
||||
nxyz=0;
|
||||
nt=0;
|
||||
nbtw=0;
|
||||
words=0;
|
||||
BLAS_V.resize(0);
|
||||
BLAS_M.resize(0);
|
||||
BLAS_P.resize(0);
|
||||
}
|
||||
void Allocate(int _nmom,GridBase *_grid)
|
||||
{
|
||||
grid=_grid;
|
||||
Coordinate ldims = grid->LocalDimensions();
|
||||
|
||||
nmom=_nmom;
|
||||
nt = ldims[grid->Nd()-1];
|
||||
nxyz = grid->lSites()/nt;
|
||||
words = sizeof(scalar_object)/sizeof(scalar);
|
||||
nbtw = nt * words;
|
||||
|
||||
BLAS_V.resize (nxyz * nt * words );
|
||||
BLAS_M.resize (nmom * nxyz );
|
||||
BLAS_P.resize (nmom * nt * words );
|
||||
}
|
||||
void ImportMomenta(const std::vector <ComplexField> &momenta)
|
||||
{
|
||||
GRID_ASSERT(momenta.size()==nmom);
|
||||
// might as well just make the momenta here
|
||||
typedef typename Field::vector_object vobj;
|
||||
|
||||
int nd = grid->_ndimension;
|
||||
|
||||
uint64_t sz = BLAS_M.size();
|
||||
|
||||
GRID_ASSERT(momenta.size()==nmom)
|
||||
GRID_ASSERT(momenta[0].Grid()==grid);
|
||||
GRID_ASSERT(sz = nxyz * nmom);
|
||||
|
||||
Coordinate rdimensions = grid->_rdimensions;
|
||||
Coordinate ldims = grid->LocalDimensions();
|
||||
int64_t osites = grid->oSites();
|
||||
Coordinate simd = grid->_simd_layout;
|
||||
const int Nsimd = vobj::Nsimd();
|
||||
uint64_t lwords = words; // local variable for copy in to GPU
|
||||
int64_t Nxyz = nxyz;
|
||||
auto blasData_p = &BLAS_M[0];
|
||||
for(int m=0;m<momenta.size();m++){
|
||||
|
||||
autoView( Data , momenta[m], AcceleratorRead);
|
||||
auto Data_p = &Data[0];
|
||||
|
||||
accelerator_for(xyz,nxyz,1,{
|
||||
//////////////////////////////////////////
|
||||
// isite -- map lane within buffer to lane within lattice
|
||||
////////////////////////////////////////////
|
||||
Coordinate lcoor(nd,0);
|
||||
Lexicographic::CoorFromIndex(lcoor,xyz,ldims);
|
||||
|
||||
Coordinate icoor(nd);
|
||||
Coordinate ocoor(nd);
|
||||
for (int d = 0; d < nd; d++) {
|
||||
icoor[d] = lcoor[d]/rdimensions[d];
|
||||
ocoor[d] = lcoor[d]%rdimensions[d];
|
||||
}
|
||||
int64_t osite;
|
||||
int64_t isite;
|
||||
Lexicographic::IndexFromCoor(ocoor,osite,rdimensions);
|
||||
Lexicographic::IndexFromCoor(icoor,isite,simd);
|
||||
|
||||
// BLAS_M[nmom][slice_vol]
|
||||
// Fortran Column major BLAS layout is M_xyz,mom
|
||||
scalar data = extractLane(isite,Data[osite]);
|
||||
uint64_t idx = xyz+m*Nxyz;
|
||||
blasData_p[idx] = data;
|
||||
});
|
||||
}
|
||||
}
|
||||
void ImportVector(Field &vec)
|
||||
{
|
||||
typedef typename Field::vector_object vobj;
|
||||
|
||||
int nd = grid->_ndimension;
|
||||
|
||||
uint64_t sz = BLAS_V.size();
|
||||
|
||||
GRID_ASSERT(sz = nxyz * words * nt);
|
||||
|
||||
Coordinate rdimensions = grid->_rdimensions;
|
||||
Coordinate ldims= grid->LocalDimensions();
|
||||
int64_t osites = grid->oSites();
|
||||
Coordinate simd = grid->_simd_layout;
|
||||
const int Nsimd = vobj::Nsimd();
|
||||
uint64_t lwords= words; // local variable for copy in to GPU
|
||||
|
||||
auto blasData_p = &BLAS_V[0];
|
||||
autoView( Data , vec, AcceleratorRead);
|
||||
auto Data_p = &Data[0];
|
||||
|
||||
int64_t nwords = words;// for capture
|
||||
int64_t Nt = nt;// for capture
|
||||
|
||||
accelerator_for(sf,osites,Nsimd,{
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<Nsimd;lane++) {
|
||||
#endif
|
||||
//////////////////////////////////////////
|
||||
// isite -- map lane within buffer to lane within lattice
|
||||
////////////////////////////////////////////
|
||||
Coordinate lcoor(nd,0);
|
||||
Coordinate icoor(nd);
|
||||
Coordinate ocoor(nd);
|
||||
|
||||
Lexicographic::CoorFromIndex(icoor,lane,simd);
|
||||
Lexicographic::CoorFromIndex(ocoor,sf,rdimensions);
|
||||
|
||||
|
||||
int64_t l_xyz = 0;
|
||||
for (int d = 0; d < nd; d++) {
|
||||
lcoor[d] = rdimensions[d]*icoor[d] + ocoor[d];
|
||||
}
|
||||
uint64_t l_t = lcoor[nd-1];
|
||||
|
||||
Coordinate xyz_coor = lcoor;
|
||||
xyz_coor[nd-1] =0;
|
||||
Lexicographic::IndexFromCoor(xyz_coor,l_xyz,ldims);
|
||||
|
||||
|
||||
scalar_object data = extractLane(lane,Data[sf]);
|
||||
scalar *data_words = (scalar *) &data;
|
||||
for(int w = 0 ; w < nwords; w++) {
|
||||
// BLAS_V[slice_vol][nt][words]
|
||||
// Fortran Column major BLAS layout is V_(t,w)_xyz
|
||||
uint64_t idx = w+l_t*nwords + l_xyz * nwords * Nt;
|
||||
blasData_p[idx] = data_words[w];
|
||||
}
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
void ExportMomentumProjection(std::vector<typename Field::scalar_object> &projection)
|
||||
{
|
||||
projection.resize(nmom*nt);
|
||||
acceleratorCopyFromDevice(&BLAS_P[0],(scalar *)&projection[0],BLAS_P.size()*sizeof(scalar));
|
||||
// Could decide on a layout late?
|
||||
}
|
||||
|
||||
// Row major layout "C" order:
|
||||
// BLAS_V[slice_vol][nt][words]
|
||||
// BLAS_M[nmom][slice_vol]
|
||||
// BLAS_P[nmom][nt][words]
|
||||
//
|
||||
// Fortran Column major BLAS layout is V_(w,t)_xyz
|
||||
// Fortran Column major BLAS layout is M_xyz,mom
|
||||
// Fortran Column major BLAS layout is P_(w,t),mom
|
||||
//
|
||||
// Projected
|
||||
//
|
||||
// P = (V * M)_(w,t),mom
|
||||
//
|
||||
void Project(Field &data,std::vector< typename Field::scalar_object > & projected_gdata)
|
||||
{
|
||||
double t_import=0;
|
||||
double t_export=0;
|
||||
double t_gemm =0;
|
||||
double t_allreduce=0;
|
||||
t_import-=usecond();
|
||||
this->ImportVector(data);
|
||||
|
||||
std::vector< typename Field::scalar_object > projected_planes;
|
||||
|
||||
deviceVector<scalar *> Vd(1);
|
||||
deviceVector<scalar *> Md(1);
|
||||
deviceVector<scalar *> Pd(1);
|
||||
|
||||
scalar * Vh = & BLAS_V[0];
|
||||
scalar * Mh = & BLAS_M[0];
|
||||
scalar * Ph = & BLAS_P[0];
|
||||
|
||||
acceleratorPut(Vd[0],Vh);
|
||||
acceleratorPut(Md[0],Mh);
|
||||
acceleratorPut(Pd[0],Ph);
|
||||
t_import+=usecond();
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
/////////////////////////////////////////
|
||||
// P_im = VMmx . Vxi
|
||||
/////////////////////////////////////////
|
||||
t_gemm-=usecond();
|
||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
||||
words*nt,nmom,nxyz,
|
||||
scalar(1.0),
|
||||
Vd,
|
||||
Md,
|
||||
scalar(0.0), // wipe out result
|
||||
Pd);
|
||||
BLAS.synchronise();
|
||||
t_gemm+=usecond();
|
||||
|
||||
t_export-=usecond();
|
||||
ExportMomentumProjection(projected_planes); // resizes
|
||||
t_export+=usecond();
|
||||
|
||||
/////////////////////////////////
|
||||
// Reduce across MPI ranks
|
||||
/////////////////////////////////
|
||||
int nd = grid->Nd();
|
||||
int gt = grid->GlobalDimensions()[nd-1];
|
||||
int lt = grid->LocalDimensions()[nd-1];
|
||||
projected_gdata.resize(gt*nmom);
|
||||
for(int t=0;t<gt*nmom;t++){ // global Nt array with zeroes for stuff not on this node
|
||||
projected_gdata[t]=Zero();
|
||||
}
|
||||
for(int t=0;t<lt;t++){
|
||||
for(int m=0;m<nmom;m++){
|
||||
int st = grid->LocalStarts()[nd-1];
|
||||
projected_gdata[t+st + gt*m] = projected_planes[t+lt*m];
|
||||
}}
|
||||
t_allreduce-=usecond();
|
||||
grid->GlobalSumVector((scalar *)&projected_gdata[0],gt*nmom*words);
|
||||
t_allreduce+=usecond();
|
||||
|
||||
std::cout << GridLogPerformance<<" MomentumProject t_import "<<t_import<<"us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<" MomentumProject t_export "<<t_export<<"us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<" MomentumProject t_gemm "<<t_gemm<<"us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<" MomentumProject t_reduce "<<t_allreduce<<"us"<<std::endl;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -33,16 +33,19 @@ namespace Grid {
|
||||
template<class Field>
|
||||
class ZeroGuesser: public LinearFunction<Field> {
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
virtual void operator()(const Field &src, Field &guess) { guess = Zero(); };
|
||||
};
|
||||
template<class Field>
|
||||
class DoNothingGuesser: public LinearFunction<Field> {
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
virtual void operator()(const Field &src, Field &guess) { };
|
||||
};
|
||||
template<class Field>
|
||||
class SourceGuesser: public LinearFunction<Field> {
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
virtual void operator()(const Field &src, Field &guess) { guess = src; };
|
||||
};
|
||||
|
||||
@@ -54,15 +57,24 @@ class DeflatedGuesser: public LinearFunction<Field> {
|
||||
private:
|
||||
const std::vector<Field> &evec;
|
||||
const std::vector<RealD> &eval;
|
||||
const unsigned int N;
|
||||
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
|
||||
DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval) : evec(_evec), eval(_eval) {};
|
||||
DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval)
|
||||
: DeflatedGuesser(_evec, _eval, _evec.size())
|
||||
{}
|
||||
|
||||
DeflatedGuesser(const std::vector<Field> & _evec, const std::vector<RealD> & _eval, const unsigned int _N)
|
||||
: evec(_evec), eval(_eval), N(_N)
|
||||
{
|
||||
GRID_ASSERT(evec.size()==eval.size());
|
||||
GRID_ASSERT(N <= evec.size());
|
||||
}
|
||||
|
||||
virtual void operator()(const Field &src,Field &guess) {
|
||||
guess = Zero();
|
||||
assert(evec.size()==eval.size());
|
||||
auto N = evec.size();
|
||||
for (int i=0;i<N;i++) {
|
||||
const Field& tmp = evec[i];
|
||||
axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess);
|
||||
@@ -79,6 +91,7 @@ private:
|
||||
const std::vector<RealD> &eval_coarse;
|
||||
public:
|
||||
|
||||
using LinearFunction<FineField>::operator();
|
||||
LocalCoherenceDeflatedGuesser(const std::vector<FineField> &_subspace,
|
||||
const std::vector<CoarseField> &_evec_coarse,
|
||||
const std::vector<RealD> &_eval_coarse)
|
||||
@@ -100,7 +113,42 @@ public:
|
||||
blockPromote(guess_coarse,guess,subspace);
|
||||
guess.Checkerboard() = src.Checkerboard();
|
||||
};
|
||||
};
|
||||
|
||||
void operator()(const std::vector<FineField> &src,std::vector<FineField> &guess) {
|
||||
int Nevec = (int)evec_coarse.size();
|
||||
int Nsrc = (int)src.size();
|
||||
// make temp variables
|
||||
std::vector<CoarseField> src_coarse(Nsrc,evec_coarse[0].Grid());
|
||||
std::vector<CoarseField> guess_coarse(Nsrc,evec_coarse[0].Grid());
|
||||
//Preporcessing
|
||||
std::cout << GridLogMessage << "Start BlockProject for loop" << std::endl;
|
||||
for (int j=0;j<Nsrc;j++)
|
||||
{
|
||||
guess_coarse[j] = Zero();
|
||||
std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl;
|
||||
blockProject(src_coarse[j],src[j],subspace);
|
||||
}
|
||||
//deflation set up for eigen vector batchsize 1 and source batch size equal number of sources
|
||||
std::cout << GridLogMessage << "Start ProjectAccum for loop" << std::endl;
|
||||
for (int i=0;i<Nevec;i++)
|
||||
{
|
||||
std::cout << GridLogMessage << "ProjectAccum Nvec: " << i << std::endl;
|
||||
const CoarseField & tmp = evec_coarse[i];
|
||||
for (int j=0;j<Nsrc;j++)
|
||||
{
|
||||
axpy(guess_coarse[j],TensorRemove(innerProduct(tmp,src_coarse[j])) / eval_coarse[i],tmp,guess_coarse[j]);
|
||||
}
|
||||
}
|
||||
//postprocessing
|
||||
std::cout << GridLogMessage << "Start BlockPromote for loop" << std::endl;
|
||||
for (int j=0;j<Nsrc;j++) {
|
||||
std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl;
|
||||
blockPromote(guess_coarse[j],guess[j],subspace);
|
||||
guess[j].Checkerboard() = src[j].Checkerboard();
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: MultiRHSBlockCGLinalg.h
|
||||
|
||||
Copyright (C) 2024
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
|
||||
/* Need helper object for BLAS accelerated mrhs blockCG */
|
||||
template<class Field>
|
||||
class MultiRHSBlockCGLinalg
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename Field::scalar_type scalar;
|
||||
typedef typename Field::scalar_object scalar_object;
|
||||
typedef typename Field::vector_object vector_object;
|
||||
|
||||
deviceVector<scalar> BLAS_X; // nrhs x vol -- the sources
|
||||
deviceVector<scalar> BLAS_Y; // nrhs x vol -- the result
|
||||
deviceVector<scalar> BLAS_C; // nrhs x nrhs -- the coefficients
|
||||
deviceVector<scalar> BLAS_Cred; // nrhs x nrhs x oSites -- reduction buffer
|
||||
deviceVector<scalar *> Xdip;
|
||||
deviceVector<scalar *> Ydip;
|
||||
deviceVector<scalar *> Cdip;
|
||||
|
||||
MultiRHSBlockCGLinalg() {};
|
||||
~MultiRHSBlockCGLinalg(){ Deallocate(); };
|
||||
|
||||
void Deallocate(void)
|
||||
{
|
||||
Xdip.resize(0);
|
||||
Ydip.resize(0);
|
||||
Cdip.resize(0);
|
||||
BLAS_Cred.resize(0);
|
||||
BLAS_C.resize(0);
|
||||
BLAS_X.resize(0);
|
||||
BLAS_Y.resize(0);
|
||||
}
|
||||
void MaddMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X,const std::vector<Field> &Y,RealD scale=1.0)
|
||||
{
|
||||
std::vector<Field> Y_copy(AP.size(),AP[0].Grid());
|
||||
for(int r=0;r<AP.size();r++){
|
||||
Y_copy[r] = Y[r];
|
||||
}
|
||||
MulMatrix(AP,m,X);
|
||||
for(int r=0;r<AP.size();r++){
|
||||
AP[r] = scale*AP[r]+Y_copy[r];
|
||||
}
|
||||
}
|
||||
void MulMatrix(std::vector<Field> &Y, Eigen::MatrixXcd &m , const std::vector<Field> &X)
|
||||
{
|
||||
typedef typename Field::scalar_type scomplex;
|
||||
GridBase *grid;
|
||||
uint64_t vol;
|
||||
uint64_t words;
|
||||
|
||||
int nrhs = Y.size();
|
||||
grid = X[0].Grid();
|
||||
vol = grid->lSites();
|
||||
words = sizeof(scalar_object)/sizeof(scalar);
|
||||
int64_t vw = vol * words;
|
||||
|
||||
RealD t0 = usecond();
|
||||
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_Y.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_C.resize(nrhs * nrhs);// cost free if size doesn't change
|
||||
RealD t1 = usecond();
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Copy in the multi-rhs sources
|
||||
/////////////////////////////////////////////
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int64_t offset = r*vw;
|
||||
autoView(x_v,X[r],AcceleratorRead);
|
||||
acceleratorCopyDeviceToDevice(&x_v[0],&BLAS_X[offset],sizeof(scalar_object)*vol);
|
||||
}
|
||||
|
||||
// Assumes Eigen storage contiguous
|
||||
acceleratorCopyToDevice(&m(0,0),&BLAS_C[0],BLAS_C.size()*sizeof(scalar));
|
||||
|
||||
/*
|
||||
* in Fortran column major notation (cuBlas order)
|
||||
*
|
||||
* Xxr = [X1(x)][..][Xn(x)]
|
||||
* Yxr = [Y1(x)][..][Ym(x)]
|
||||
* Y = X . C
|
||||
*/
|
||||
deviceVector<scalar *> Xd(1);
|
||||
deviceVector<scalar *> Yd(1);
|
||||
deviceVector<scalar *> Cd(1);
|
||||
|
||||
scalar * Xh = & BLAS_X[0];
|
||||
scalar * Yh = & BLAS_Y[0];
|
||||
scalar * Ch = & BLAS_C[0];
|
||||
|
||||
acceleratorPut(Xd[0],Xh);
|
||||
acceleratorPut(Yd[0],Yh);
|
||||
acceleratorPut(Cd[0],Ch);
|
||||
|
||||
RealD t2 = usecond();
|
||||
GridBLAS BLAS;
|
||||
/////////////////////////////////////////
|
||||
// Y = X*C (transpose?)
|
||||
/////////////////////////////////////////
|
||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
||||
vw,nrhs,nrhs,
|
||||
scalar(1.0),
|
||||
Xd,
|
||||
Cd,
|
||||
scalar(0.0), // wipe out Y
|
||||
Yd);
|
||||
BLAS.synchronise();
|
||||
RealD t3 = usecond();
|
||||
|
||||
// Copy back Y = m X
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int64_t offset = r*vw;
|
||||
autoView(y_v,Y[r],AcceleratorWrite);
|
||||
acceleratorCopyDeviceToDevice(&BLAS_Y[offset],&y_v[0],sizeof(scalar_object)*vol);
|
||||
}
|
||||
RealD t4 = usecond();
|
||||
std::cout <<GridLogPerformance << "MulMatrix alloc took "<< t1-t0<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "MulMatrix preamble took "<< t2-t1<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "MulMatrix blas took "<< t3-t2<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "MulMatrix copy took "<< t4-t3<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "MulMatrix total "<< t4-t0<<" us"<<std::endl;
|
||||
}
|
||||
|
||||
void InnerProductMatrix(Eigen::MatrixXcd &m , const std::vector<Field> &X, const std::vector<Field> &Y)
|
||||
{
|
||||
#if 0
|
||||
int nrhs;
|
||||
GridBase *grid;
|
||||
uint64_t vol;
|
||||
uint64_t words;
|
||||
|
||||
nrhs = X.size();
|
||||
GRID_ASSERT(X.size()==Y.size());
|
||||
conformable(X[0],Y[0]);
|
||||
|
||||
grid = X[0].Grid();
|
||||
vol = grid->lSites();
|
||||
words = sizeof(scalar_object)/sizeof(scalar);
|
||||
int64_t vw = vol * words;
|
||||
|
||||
RealD t0 = usecond();
|
||||
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_Y.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_C.resize(nrhs * nrhs);// cost free if size doesn't change
|
||||
RealD t1 = usecond();
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Copy in the multi-rhs sources
|
||||
/////////////////////////////////////////////
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int64_t offset = r*vw;
|
||||
autoView(x_v,X[r],AcceleratorRead);
|
||||
acceleratorCopyDeviceToDevice(&x_v[0],&BLAS_X[offset],sizeof(scalar_object)*vol);
|
||||
autoView(y_v,Y[r],AcceleratorRead);
|
||||
acceleratorCopyDeviceToDevice(&y_v[0],&BLAS_Y[offset],sizeof(scalar_object)*vol);
|
||||
}
|
||||
RealD t2 = usecond();
|
||||
|
||||
/*
|
||||
* in Fortran column major notation (cuBlas order)
|
||||
*
|
||||
* Xxr = [X1(x)][..][Xn(x)]
|
||||
*
|
||||
* Yxr = [Y1(x)][..][Ym(x)]
|
||||
*
|
||||
* C_rs = X^dag Y
|
||||
*/
|
||||
deviceVector<scalar *> Xd(1);
|
||||
deviceVector<scalar *> Yd(1);
|
||||
deviceVector<scalar *> Cd(1);
|
||||
|
||||
scalar * Xh = & BLAS_X[0];
|
||||
scalar * Yh = & BLAS_Y[0];
|
||||
scalar * Ch = & BLAS_C[0];
|
||||
|
||||
acceleratorPut(Xd[0],Xh);
|
||||
acceleratorPut(Yd[0],Yh);
|
||||
acceleratorPut(Cd[0],Ch);
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
RealD t3 = usecond();
|
||||
/////////////////////////////////////////
|
||||
// C_rs = X^dag Y
|
||||
/////////////////////////////////////////
|
||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
||||
nrhs,nrhs,vw,
|
||||
ComplexD(1.0),
|
||||
Xd,
|
||||
Yd,
|
||||
ComplexD(0.0), // wipe out C
|
||||
Cd);
|
||||
BLAS.synchronise();
|
||||
RealD t4 = usecond();
|
||||
|
||||
std::vector<scalar> HOST_C(BLAS_C.size()); // nrhs . nrhs -- the coefficients
|
||||
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
|
||||
grid->GlobalSumVector(&HOST_C[0],nrhs*nrhs);
|
||||
|
||||
RealD t5 = usecond();
|
||||
for(int rr=0;rr<nrhs;rr++){
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int off = r+nrhs*rr;
|
||||
m(r,rr)=HOST_C[off];
|
||||
}
|
||||
}
|
||||
RealD t6 = usecond();
|
||||
uint64_t M=nrhs;
|
||||
uint64_t N=nrhs;
|
||||
uint64_t K=vw;
|
||||
RealD bytes = 1.0*sizeof(ComplexD)*(M*N*2+N*K+M*K);
|
||||
RealD flops = 8.0*M*N*K;
|
||||
flops = flops/(t4-t3)/1.e3;
|
||||
bytes = bytes/(t4-t3)/1.e3;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix m,n,k "<< M<<","<<N<<","<<K<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix alloc t1 "<< t1-t0<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t2 "<< t2-t1<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix setup t3 "<< t3-t2<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas t4 "<< t4-t3<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< flops<<" GF/s"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< bytes<<" GB/s"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix gsum t5 "<< t5-t4<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t6 "<< t6-t5<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix took "<< t6-t0<<" us"<<std::endl;
|
||||
#else
|
||||
int nrhs;
|
||||
GridBase *grid;
|
||||
uint64_t vol;
|
||||
uint64_t words;
|
||||
|
||||
nrhs = X.size();
|
||||
GRID_ASSERT(X.size()==Y.size());
|
||||
conformable(X[0],Y[0]);
|
||||
|
||||
grid = X[0].Grid();
|
||||
int rd0 = grid->_rdimensions[0] * grid->_rdimensions[1];
|
||||
vol = grid->oSites()/rd0;
|
||||
words = rd0*sizeof(vector_object)/sizeof(scalar);
|
||||
int64_t vw = vol * words;
|
||||
GRID_ASSERT(vw == grid->lSites()*sizeof(scalar_object)/sizeof(scalar));
|
||||
|
||||
RealD t0 = usecond();
|
||||
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_Y.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_Cred.resize(nrhs * nrhs * vol);// cost free if size doesn't change
|
||||
RealD t1 = usecond();
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Copy in the multi-rhs sources -- layout batched BLAS ready
|
||||
/////////////////////////////////////////////
|
||||
for(int r=0;r<nrhs;r++){
|
||||
autoView(x_v,X[r],AcceleratorRead);
|
||||
autoView(y_v,Y[r],AcceleratorRead);
|
||||
scalar *from_x=(scalar *)&x_v[0];
|
||||
scalar *from_y=(scalar *)&y_v[0];
|
||||
scalar *BX = &BLAS_X[0];
|
||||
scalar *BY = &BLAS_Y[0];
|
||||
accelerator_for(ssw,vw,1,{
|
||||
uint64_t ss=ssw/words;
|
||||
uint64_t w=ssw%words;
|
||||
uint64_t offset = w+r*words+ss*nrhs*words; // [ss][rhs][words]
|
||||
BX[offset] = from_x[ssw];
|
||||
BY[offset] = from_y[ssw];
|
||||
});
|
||||
}
|
||||
RealD t2 = usecond();
|
||||
|
||||
/*
|
||||
* in Fortran column major notation (cuBlas order)
|
||||
*
|
||||
* Xxr = [X1(x)][..][Xn(x)]
|
||||
*
|
||||
* Yxr = [Y1(x)][..][Ym(x)]
|
||||
*
|
||||
* C_rs = X^dag Y
|
||||
*/
|
||||
Xdip.resize(vol);
|
||||
Ydip.resize(vol);
|
||||
Cdip.resize(vol);
|
||||
std::vector<scalar *> Xh(vol);
|
||||
std::vector<scalar *> Yh(vol);
|
||||
std::vector<scalar *> Ch(vol);
|
||||
for(uint64_t ss=0;ss<vol;ss++){
|
||||
|
||||
Xh[ss] = & BLAS_X[ss*nrhs*words];
|
||||
Yh[ss] = & BLAS_Y[ss*nrhs*words];
|
||||
Ch[ss] = & BLAS_Cred[ss*nrhs*nrhs];
|
||||
|
||||
}
|
||||
acceleratorCopyToDevice(&Xh[0],&Xdip[0],vol*sizeof(scalar *));
|
||||
acceleratorCopyToDevice(&Yh[0],&Ydip[0],vol*sizeof(scalar *));
|
||||
acceleratorCopyToDevice(&Ch[0],&Cdip[0],vol*sizeof(scalar *));
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
RealD t3 = usecond();
|
||||
/////////////////////////////////////////
|
||||
// C_rs = X^dag Y
|
||||
/////////////////////////////////////////
|
||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
||||
nrhs,nrhs,words,
|
||||
ComplexD(1.0),
|
||||
Xdip,
|
||||
Ydip,
|
||||
ComplexD(0.0), // wipe out C
|
||||
Cdip);
|
||||
BLAS.synchronise();
|
||||
RealD t4 = usecond();
|
||||
|
||||
std::vector<scalar> HOST_C(BLAS_Cred.size()); // nrhs . nrhs -- the coefficients
|
||||
acceleratorCopyFromDevice(&BLAS_Cred[0],&HOST_C[0],BLAS_Cred.size()*sizeof(scalar));
|
||||
|
||||
RealD t5 = usecond();
|
||||
m = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
for(int ss=0;ss<vol;ss++){
|
||||
Eigen::Map<Eigen::MatrixXcd> eC((std::complex<double> *)&HOST_C[ss*nrhs*nrhs],nrhs,nrhs);
|
||||
m = m + eC;
|
||||
}
|
||||
RealD t6l = usecond();
|
||||
grid->GlobalSumVector((scalar *) &m(0,0),nrhs*nrhs);
|
||||
RealD t6 = usecond();
|
||||
uint64_t M=nrhs;
|
||||
uint64_t N=nrhs;
|
||||
uint64_t K=vw;
|
||||
RealD xybytes = grid->lSites()*sizeof(scalar_object);
|
||||
RealD bytes = 1.0*sizeof(ComplexD)*(M*N*2+N*K+M*K);
|
||||
RealD flops = 8.0*M*N*K;
|
||||
flops = flops/(t4-t3)/1.e3;
|
||||
bytes = bytes/(t4-t3)/1.e3;
|
||||
xybytes = 4*xybytes/(t2-t1)/1.e3;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix m,n,k "<< M<<","<<N<<","<<K<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix alloc t1 "<< t1-t0<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t2 "<< t2-t1<<" us "<<xybytes<<" GB/s"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix setup t3 "<< t3-t2<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas t4 "<< t4-t3<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< flops<<" GF/s"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< bytes<<" GB/s"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t5 "<< t5-t4<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix lsum t6l "<< t6l-t5<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix gsum t6 "<< t6-t6l<<" us"<<std::endl;
|
||||
std::cout <<GridLogPerformance<< "InnerProductMatrix took "<< t6-t0<<" us"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -0,0 +1,513 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: MultiRHSDeflation.h
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
|
||||
/*
|
||||
MultiRHS block projection
|
||||
|
||||
Import basis -> nblock x nbasis x (block x internal)
|
||||
Import vector of fine lattice objects -> nblock x nrhs x (block x internal)
|
||||
|
||||
=> coarse_(nrhs x nbasis )^block = via batched GEMM
|
||||
|
||||
//template<class vobj,class CComplex,int nbasis,class VLattice>
|
||||
//inline void blockProject(Lattice<iVector<CComplex,nbasis > > &coarseData,
|
||||
// const VLattice &fineData,
|
||||
// const VLattice &Basis)
|
||||
*/
|
||||
|
||||
template<class Field>
|
||||
class MultiRHSBlockProject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename Field::scalar_type scalar;
|
||||
typedef typename Field::scalar_object scalar_object;
|
||||
typedef Field Fermion;
|
||||
|
||||
int nbasis;
|
||||
GridBase *coarse_grid;
|
||||
GridBase *fine_grid;
|
||||
uint64_t block_vol;
|
||||
uint64_t fine_vol;
|
||||
uint64_t coarse_vol;
|
||||
uint64_t words;
|
||||
|
||||
// Row major layout "C" order:
|
||||
// BLAS_V[coarse_vol][nbasis][block_vol][words]
|
||||
// BLAS_F[coarse_vol][nrhs][block_vol][words]
|
||||
// BLAS_C[coarse_vol][nrhs][nbasis]
|
||||
/*
|
||||
* in Fortran column major notation (cuBlas order)
|
||||
*
|
||||
* Vxb = [v1(x)][..][vn(x)] ... x coarse vol
|
||||
*
|
||||
* Fxr = [r1(x)][..][rm(x)] ... x coarse vol
|
||||
*
|
||||
* Block project:
|
||||
* C_br = V^dag F x coarse vol
|
||||
*
|
||||
* Block promote:
|
||||
* F_xr = Vxb Cbr x coarse_vol
|
||||
*/
|
||||
deviceVector<scalar> BLAS_V; // words * block_vol * nbasis x coarse_vol
|
||||
deviceVector<scalar> BLAS_F; // nrhs x fine_vol * words -- the sources
|
||||
deviceVector<scalar> BLAS_C; // nrhs x coarse_vol * nbasis -- the coarse coeffs
|
||||
|
||||
RealD blasNorm2(deviceVector<scalar> &blas)
|
||||
{
|
||||
scalar ss(0.0);
|
||||
std::vector<scalar> tmp(blas.size());
|
||||
acceleratorCopyFromDevice(&blas[0],&tmp[0],blas.size()*sizeof(scalar));
|
||||
for(int64_t s=0;s<blas.size();s++){
|
||||
ss=ss+tmp[s]*adj(tmp[s]);
|
||||
}
|
||||
coarse_grid->GlobalSum(ss);
|
||||
return real(ss);
|
||||
}
|
||||
|
||||
MultiRHSBlockProject(){};
|
||||
~MultiRHSBlockProject(){ Deallocate(); };
|
||||
|
||||
void Deallocate(void)
|
||||
{
|
||||
nbasis=0;
|
||||
coarse_grid=nullptr;
|
||||
fine_grid=nullptr;
|
||||
fine_vol=0;
|
||||
block_vol=0;
|
||||
coarse_vol=0;
|
||||
words=0;
|
||||
BLAS_V.resize(0);
|
||||
BLAS_F.resize(0);
|
||||
BLAS_C.resize(0);
|
||||
}
|
||||
void Allocate(int _nbasis,GridBase *_fgrid,GridBase *_cgrid)
|
||||
{
|
||||
nbasis=_nbasis;
|
||||
|
||||
fine_grid=_fgrid;
|
||||
coarse_grid=_cgrid;
|
||||
|
||||
fine_vol = fine_grid->lSites();
|
||||
coarse_vol = coarse_grid->lSites();
|
||||
block_vol = fine_vol/coarse_vol;
|
||||
|
||||
words = sizeof(scalar_object)/sizeof(scalar);
|
||||
|
||||
BLAS_V.resize (fine_vol * words * nbasis );
|
||||
}
|
||||
void ImportFineGridVectors(std::vector <Field > &vecs, deviceVector<scalar> &blas)
|
||||
{
|
||||
int nvec = vecs.size();
|
||||
typedef typename Field::vector_object vobj;
|
||||
// std::cout << GridLogMessage <<" BlockProjector importing "<<nvec<< " fine grid vectors" <<std::endl;
|
||||
|
||||
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
||||
|
||||
subdivides(coarse_grid,fine_grid); // require they map
|
||||
|
||||
int _ndimension = coarse_grid->_ndimension;
|
||||
GRID_ASSERT(block_vol == fine_grid->oSites() / coarse_grid->oSites());
|
||||
|
||||
Coordinate block_r (_ndimension);
|
||||
for(int d=0 ; d<_ndimension;d++){
|
||||
block_r[d] = fine_grid->_rdimensions[d] / coarse_grid->_rdimensions[d];
|
||||
}
|
||||
|
||||
uint64_t sz = blas.size();
|
||||
|
||||
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
||||
|
||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||
int64_t bv= block_vol;
|
||||
for(int v=0;v<vecs.size();v++){
|
||||
|
||||
// std::cout << " BlockProjector importing vector"<<v<<" "<<norm2(vecs[v])<<std::endl;
|
||||
autoView( fineData , vecs[v], AcceleratorRead);
|
||||
|
||||
auto blasData_p = &blas[0];
|
||||
auto fineData_p = &fineData[0];
|
||||
|
||||
int64_t osites = fine_grid->oSites();
|
||||
|
||||
// loop over fine sites
|
||||
const int Nsimd = vobj::Nsimd();
|
||||
// std::cout << "sz "<<sz<<std::endl;
|
||||
// std::cout << "prod "<<Nsimd * coarse_grid->oSites() * block_vol * nvec * words<<std::endl;
|
||||
GRID_ASSERT(sz == Nsimd * coarse_grid->oSites() * block_vol * nvec * words);
|
||||
uint64_t lwords= words; // local variable for copy in to GPU
|
||||
accelerator_for(sf,osites,Nsimd,{
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<Nsimd;lane++) {
|
||||
#endif
|
||||
// One thread per fine site
|
||||
Coordinate coor_f(_ndimension);
|
||||
Coordinate coor_b(_ndimension);
|
||||
Coordinate coor_c(_ndimension);
|
||||
|
||||
// Fine site to fine coor
|
||||
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
|
||||
|
||||
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_r[d];
|
||||
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_r[d];
|
||||
|
||||
int sc;// coarse site
|
||||
int sb;// block site
|
||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_rdimensions);
|
||||
Lexicographic::IndexFromCoor(coor_b,sb,block_r);
|
||||
|
||||
scalar_object data = extractLane(lane,fineData[sf]);
|
||||
|
||||
// BLAS layout address calculation
|
||||
// words * block_vol * nbasis x coarse_vol
|
||||
// coarse oSite x block vole x lanes
|
||||
int64_t site = (lane*osites + sc*bv)*nvec
|
||||
+ v*bv
|
||||
+ sb;
|
||||
|
||||
// GRID_ASSERT(site*lwords<sz);
|
||||
|
||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
||||
|
||||
*ptr = data;
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
// std::cout << " import fine Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||
// std::cout << " BlockProjector imported vector"<<v<<std::endl;
|
||||
}
|
||||
}
|
||||
void ExportFineGridVectors(std::vector <Field> &vecs, deviceVector<scalar> &blas)
|
||||
{
|
||||
typedef typename Field::vector_object vobj;
|
||||
|
||||
int nvec = vecs.size();
|
||||
|
||||
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
||||
|
||||
subdivides(coarse_grid,fine_grid); // require they map
|
||||
|
||||
int _ndimension = coarse_grid->_ndimension;
|
||||
GRID_ASSERT(block_vol == fine_grid->oSites() / coarse_grid->oSites());
|
||||
|
||||
Coordinate block_r (_ndimension);
|
||||
for(int d=0 ; d<_ndimension;d++){
|
||||
block_r[d] = fine_grid->_rdimensions[d] / coarse_grid->_rdimensions[d];
|
||||
}
|
||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||
|
||||
// std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||
|
||||
int64_t bv= block_vol;
|
||||
for(int v=0;v<vecs.size();v++){
|
||||
|
||||
autoView( fineData , vecs[v], AcceleratorWrite);
|
||||
|
||||
auto blasData_p = &blas[0];
|
||||
auto fineData_p = &fineData[0];
|
||||
|
||||
int64_t osites = fine_grid->oSites();
|
||||
uint64_t lwords = words;
|
||||
// std::cout << " Nsimd is "<<vobj::Nsimd() << std::endl;
|
||||
// std::cout << " lwords is "<<lwords << std::endl;
|
||||
// std::cout << " sizeof(scalar_object) is "<<sizeof(scalar_object) << std::endl;
|
||||
// loop over fine sites
|
||||
accelerator_for(sf,osites,vobj::Nsimd(),{
|
||||
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(vobj::Nsimd()); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<vobj::Nsimd();lane++) {
|
||||
#endif
|
||||
// One thread per fine site
|
||||
Coordinate coor_f(_ndimension);
|
||||
Coordinate coor_b(_ndimension);
|
||||
Coordinate coor_c(_ndimension);
|
||||
|
||||
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
|
||||
|
||||
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_r[d];
|
||||
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_r[d];
|
||||
|
||||
int sc;
|
||||
int sb;
|
||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_rdimensions);
|
||||
Lexicographic::IndexFromCoor(coor_b,sb,block_r);
|
||||
|
||||
// BLAS layout address calculation
|
||||
// words * block_vol * nbasis x coarse_vol
|
||||
int64_t site = (lane*osites + sc*bv)*nvec
|
||||
+ v*bv
|
||||
+ sb;
|
||||
|
||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
||||
|
||||
scalar_object data = *ptr;
|
||||
|
||||
insertLane(lane,fineData[sf],data);
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
}
|
||||
template<class vobj>
|
||||
void ImportCoarseGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas)
|
||||
{
|
||||
int nvec = vecs.size();
|
||||
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||
|
||||
// std::cout << " BlockProjector importing "<<nvec<< " coarse grid vectors" <<std::endl;
|
||||
|
||||
GRID_ASSERT(vecs[0].Grid()==coarse_grid);
|
||||
|
||||
int _ndimension = coarse_grid->_ndimension;
|
||||
|
||||
uint64_t sz = blas.size();
|
||||
|
||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||
|
||||
for(int v=0;v<vecs.size();v++){
|
||||
|
||||
// std::cout << " BlockProjector importing coarse vector"<<v<<" "<<norm2(vecs[v])<<std::endl;
|
||||
autoView( coarseData , vecs[v], AcceleratorRead);
|
||||
|
||||
auto blasData_p = &blas[0];
|
||||
auto coarseData_p = &coarseData[0];
|
||||
|
||||
int64_t osites = coarse_grid->oSites();
|
||||
|
||||
// loop over fine sites
|
||||
const int Nsimd = vobj::Nsimd();
|
||||
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
||||
GRID_ASSERT(cwords==nbasis);
|
||||
|
||||
accelerator_for(sc,osites,Nsimd,{
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<Nsimd;lane++) {
|
||||
#endif
|
||||
// C_br per site
|
||||
int64_t blas_site = (lane*osites + sc)*nvec*cwords + v*cwords;
|
||||
|
||||
coarse_scalar_object data = extractLane(lane,coarseData[sc]);
|
||||
|
||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
||||
|
||||
*ptr = data;
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
// std::cout << " import coarsee Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||
}
|
||||
}
|
||||
template<class vobj>
|
||||
void ExportCoarseGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas)
|
||||
{
|
||||
int nvec = vecs.size();
|
||||
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||
// std::cout << GridLogMessage<<" BlockProjector exporting "<<nvec<< " coarse grid vectors" <<std::endl;
|
||||
|
||||
GRID_ASSERT(vecs[0].Grid()==coarse_grid);
|
||||
|
||||
int _ndimension = coarse_grid->_ndimension;
|
||||
|
||||
uint64_t sz = blas.size();
|
||||
|
||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||
|
||||
// std::cout << " export coarsee Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||
for(int v=0;v<vecs.size();v++){
|
||||
|
||||
// std::cout << " BlockProjector exporting coarse vector"<<v<<std::endl;
|
||||
autoView( coarseData , vecs[v], AcceleratorWrite);
|
||||
|
||||
auto blasData_p = &blas[0];
|
||||
auto coarseData_p = &coarseData[0];
|
||||
|
||||
int64_t osites = coarse_grid->oSites();
|
||||
|
||||
// loop over fine sites
|
||||
const int Nsimd = vobj::Nsimd();
|
||||
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
||||
GRID_ASSERT(cwords==nbasis);
|
||||
|
||||
accelerator_for(sc,osites,Nsimd,{
|
||||
// Wrap in a macro "FOR_ALL_LANES(lane,{ ... });
|
||||
#ifdef GRID_SIMT
|
||||
{
|
||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||
#else
|
||||
for(int lane=0;lane<Nsimd;lane++) {
|
||||
#endif
|
||||
int64_t blas_site = (lane*osites + sc)*nvec*cwords + v*cwords;
|
||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
||||
coarse_scalar_object data = *ptr;
|
||||
insertLane(lane,coarseData[sc],data);
|
||||
#ifdef GRID_SIMT
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
}
|
||||
void ImportBasis(std::vector < Field > &vecs)
|
||||
{
|
||||
// std::cout << " BlockProjector Import basis size "<<vecs.size()<<std::endl;
|
||||
ImportFineGridVectors(vecs,BLAS_V);
|
||||
}
|
||||
|
||||
template<class cobj>
|
||||
void blockProject(std::vector<Field> &fine,std::vector< Lattice<cobj> > & coarse)
|
||||
{
|
||||
int nrhs=fine.size();
|
||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||
// std::cout << "blockProject nbasis " <<nbasis<<" " << _nbasis<<std::endl;
|
||||
GRID_ASSERT(nbasis==_nbasis);
|
||||
|
||||
BLAS_F.resize (fine_vol * words * nrhs );
|
||||
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Copy in the multi-rhs sources to same data layout
|
||||
/////////////////////////////////////////////
|
||||
// std::cout << "BlockProject import fine"<<std::endl;
|
||||
ImportFineGridVectors(fine,BLAS_F);
|
||||
|
||||
deviceVector<scalar *> Vd(coarse_vol);
|
||||
deviceVector<scalar *> Fd(coarse_vol);
|
||||
deviceVector<scalar *> Cd(coarse_vol);
|
||||
|
||||
// std::cout << "BlockProject pointers"<<std::endl;
|
||||
for(int c=0;c<coarse_vol;c++){
|
||||
// BLAS_V[coarse_vol][nbasis][block_vol][words]
|
||||
// BLAS_F[coarse_vol][nrhs][block_vol][words]
|
||||
// BLAS_C[coarse_vol][nrhs][nbasis]
|
||||
scalar * Vh = & BLAS_V[c*nbasis*block_vol*words];
|
||||
scalar * Fh = & BLAS_F[c*nrhs*block_vol*words];
|
||||
scalar * Ch = & BLAS_C[c*nrhs*nbasis];
|
||||
|
||||
acceleratorPut(Vd[c],Vh);
|
||||
acceleratorPut(Fd[c],Fh);
|
||||
acceleratorPut(Cd[c],Ch);
|
||||
}
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
// std::cout << "BlockProject BLAS"<<std::endl;
|
||||
int64_t vw = block_vol * words;
|
||||
/////////////////////////////////////////
|
||||
// C_br = V^dag R
|
||||
/////////////////////////////////////////
|
||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
||||
nbasis,nrhs,vw,
|
||||
scalar(1.0),
|
||||
Vd,
|
||||
Fd,
|
||||
scalar(0.0), // wipe out C
|
||||
Cd);
|
||||
BLAS.synchronise();
|
||||
// std::cout << "BlockProject done"<<std::endl;
|
||||
ExportCoarseGridVectors(coarse, BLAS_C);
|
||||
// std::cout << "BlockProject done"<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
template<class cobj>
|
||||
void blockPromote(std::vector<Field> &fine,std::vector<Lattice<cobj> > & coarse)
|
||||
{
|
||||
int nrhs=fine.size();
|
||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||
GRID_ASSERT(nbasis==_nbasis);
|
||||
|
||||
BLAS_F.resize (fine_vol * words * nrhs );
|
||||
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||
|
||||
ImportCoarseGridVectors(coarse, BLAS_C);
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
deviceVector<scalar *> Vd(coarse_vol);
|
||||
deviceVector<scalar *> Fd(coarse_vol);
|
||||
deviceVector<scalar *> Cd(coarse_vol);
|
||||
|
||||
for(int c=0;c<coarse_vol;c++){
|
||||
// BLAS_V[coarse_vol][nbasis][block_vol][words]
|
||||
// BLAS_F[coarse_vol][nrhs][block_vol][words]
|
||||
// BLAS_C[coarse_vol][nrhs][nbasis]
|
||||
scalar * Vh = & BLAS_V[c*nbasis*block_vol*words];
|
||||
scalar * Fh = & BLAS_F[c*nrhs*block_vol*words];
|
||||
scalar * Ch = & BLAS_C[c*nrhs*nbasis];
|
||||
acceleratorPut(Vd[c],Vh);
|
||||
acceleratorPut(Fd[c],Fh);
|
||||
acceleratorPut(Cd[c],Ch);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Block promote:
|
||||
// F_xr = Vxb Cbr (x coarse_vol)
|
||||
/////////////////////////////////////////
|
||||
|
||||
int64_t vw = block_vol * words;
|
||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
||||
vw,nrhs,nbasis,
|
||||
scalar(1.0),
|
||||
Vd,
|
||||
Cd,
|
||||
scalar(0.0), // wipe out C
|
||||
Fd);
|
||||
BLAS.synchronise();
|
||||
// std::cout << " blas call done"<<std::endl;
|
||||
|
||||
ExportFineGridVectors(fine, BLAS_F);
|
||||
// std::cout << " exported "<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -0,0 +1,233 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: MultiRHSDeflation.h
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
|
||||
/* Need helper object for BLAS accelerated mrhs projection
|
||||
|
||||
i) MultiRHS Deflation
|
||||
|
||||
Import Evecs -> nev x vol x internal
|
||||
Import vector of Lattice objects -> nrhs x vol x internal
|
||||
=> Cij (nrhs x Nev) via GEMM.
|
||||
=> Guess (nrhs x vol x internal) = C x evecs (via GEMM)
|
||||
Export
|
||||
|
||||
|
||||
ii) MultiRHS block projection
|
||||
|
||||
Import basis -> nblock x nbasis x (block x internal)
|
||||
Import vector of fine lattice objects -> nblock x nrhs x (block x internal)
|
||||
|
||||
=> coarse_(nrhs x nbasis )^block = via batched GEMM
|
||||
|
||||
iii) Alternate interface:
|
||||
Import higher dim Lattice object-> vol x nrhs layout
|
||||
|
||||
*/
|
||||
template<class Field>
|
||||
class MultiRHSDeflation
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename Field::scalar_type scalar;
|
||||
typedef typename Field::scalar_object scalar_object;
|
||||
|
||||
int nev;
|
||||
std::vector<RealD> eval;
|
||||
GridBase *grid;
|
||||
uint64_t vol;
|
||||
uint64_t words;
|
||||
|
||||
deviceVector<scalar> BLAS_E; // nev x vol -- the eigenbasis (up to a 1/sqrt(lambda))
|
||||
deviceVector<scalar> BLAS_R; // nrhs x vol -- the sources
|
||||
deviceVector<scalar> BLAS_G; // nrhs x vol -- the guess
|
||||
deviceVector<scalar> BLAS_C; // nrhs x nev -- the coefficients
|
||||
|
||||
MultiRHSDeflation(){};
|
||||
~MultiRHSDeflation(){ Deallocate(); };
|
||||
|
||||
void Deallocate(void)
|
||||
{
|
||||
nev=0;
|
||||
grid=nullptr;
|
||||
vol=0;
|
||||
words=0;
|
||||
BLAS_E.resize(0);
|
||||
BLAS_R.resize(0);
|
||||
BLAS_C.resize(0);
|
||||
BLAS_G.resize(0);
|
||||
}
|
||||
void Allocate(int _nev,GridBase *_grid)
|
||||
{
|
||||
nev=_nev;
|
||||
grid=_grid;
|
||||
vol = grid->lSites();
|
||||
words = sizeof(scalar_object)/sizeof(scalar);
|
||||
eval.resize(nev);
|
||||
BLAS_E.resize (vol * words * nev );
|
||||
std::cout << GridLogMessage << " Allocate for "<<nev<<" eigenvectors and volume "<<vol<<std::endl;
|
||||
}
|
||||
void ImportEigenVector(Field &evec,RealD &_eval, int ev)
|
||||
{
|
||||
// std::cout << " ev " <<ev<<" eval "<<_eval<< std::endl;
|
||||
GRID_ASSERT(ev<eval.size());
|
||||
eval[ev] = _eval;
|
||||
|
||||
int64_t offset = ev*vol*words;
|
||||
autoView(v,evec,AcceleratorRead);
|
||||
acceleratorCopyDeviceToDevice(&v[0],&BLAS_E[offset],sizeof(scalar_object)*vol);
|
||||
|
||||
}
|
||||
void ImportEigenBasis(std::vector<Field> &evec,std::vector<RealD> &_eval)
|
||||
{
|
||||
ImportEigenBasis(evec,_eval,0,evec.size());
|
||||
}
|
||||
// Could use to import a batch of eigenvectors
|
||||
void ImportEigenBasis(std::vector<Field> &evec,std::vector<RealD> &_eval, int _ev0, int _nev)
|
||||
{
|
||||
GRID_ASSERT(_ev0+_nev<=evec.size());
|
||||
|
||||
Allocate(_nev,evec[0].Grid());
|
||||
|
||||
// Imports a sub-batch of eigenvectors, _ev0, ..., _ev0+_nev-1
|
||||
for(int e=0;e<nev;e++){
|
||||
std::cout << "Importing eigenvector "<<e<<" evalue "<<_eval[_ev0+e]<<std::endl;
|
||||
ImportEigenVector(evec[_ev0+e],_eval[_ev0+e],e);
|
||||
}
|
||||
}
|
||||
void DeflateSources(std::vector<Field> &source,std::vector<Field> & guess)
|
||||
{
|
||||
int nrhs = source.size();
|
||||
GRID_ASSERT(source.size()==guess.size());
|
||||
GRID_ASSERT(grid == guess[0].Grid());
|
||||
conformable(guess[0],source[0]);
|
||||
|
||||
int64_t vw = vol * words;
|
||||
|
||||
RealD t0 = usecond();
|
||||
BLAS_R.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_G.resize(nrhs * vw); // cost free if size doesn't change
|
||||
BLAS_C.resize(nev * nrhs);// cost free if size doesn't change
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Copy in the multi-rhs sources
|
||||
/////////////////////////////////////////////
|
||||
// for(int r=0;r<nrhs;r++){
|
||||
// std::cout << " source["<<r<<"] = "<<norm2(source[r])<<std::endl;
|
||||
// }
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int64_t offset = r*vw;
|
||||
autoView(v,source[r],AcceleratorRead);
|
||||
acceleratorCopyDeviceToDevice(&v[0],&BLAS_R[offset],sizeof(scalar_object)*vol);
|
||||
}
|
||||
|
||||
/*
|
||||
* in Fortran column major notation (cuBlas order)
|
||||
*
|
||||
* Exe = [e1(x)][..][en(x)]
|
||||
*
|
||||
* Rxr = [r1(x)][..][rm(x)]
|
||||
*
|
||||
* C_er = E^dag R
|
||||
* C_er = C_er / lambda_e
|
||||
* G_xr = Exe Cer
|
||||
*/
|
||||
deviceVector<scalar *> Ed(1);
|
||||
deviceVector<scalar *> Rd(1);
|
||||
deviceVector<scalar *> Cd(1);
|
||||
deviceVector<scalar *> Gd(1);
|
||||
|
||||
scalar * Eh = & BLAS_E[0];
|
||||
scalar * Rh = & BLAS_R[0];
|
||||
scalar * Ch = & BLAS_C[0];
|
||||
scalar * Gh = & BLAS_G[0];
|
||||
|
||||
acceleratorPut(Ed[0],Eh);
|
||||
acceleratorPut(Rd[0],Rh);
|
||||
acceleratorPut(Cd[0],Ch);
|
||||
acceleratorPut(Gd[0],Gh);
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
/////////////////////////////////////////
|
||||
// C_er = E^dag R
|
||||
/////////////////////////////////////////
|
||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
||||
nev,nrhs,vw,
|
||||
scalar(1.0),
|
||||
Ed,
|
||||
Rd,
|
||||
scalar(0.0), // wipe out C
|
||||
Cd);
|
||||
BLAS.synchronise();
|
||||
|
||||
GRID_ASSERT(BLAS_C.size()==nev*nrhs);
|
||||
|
||||
std::vector<scalar> HOST_C(BLAS_C.size()); // nrhs . nev -- the coefficients
|
||||
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
|
||||
grid->GlobalSumVector(&HOST_C[0],nev*nrhs);
|
||||
for(int e=0;e<nev;e++){
|
||||
RealD lam(1.0/eval[e]);
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int off = e+nev*r;
|
||||
HOST_C[off]=HOST_C[off] * lam;
|
||||
// std::cout << "C["<<e<<"]["<<r<<"] ="<<HOST_C[off]<< " eval[e] "<<eval[e] <<std::endl;
|
||||
}
|
||||
}
|
||||
acceleratorCopyToDevice(&HOST_C[0],&BLAS_C[0],BLAS_C.size()*sizeof(scalar));
|
||||
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Guess G_xr = Exe Cer
|
||||
/////////////////////////////////////////
|
||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
||||
vw,nrhs,nev,
|
||||
scalar(1.0),
|
||||
Ed, // x . nev
|
||||
Cd, // nev . nrhs
|
||||
scalar(0.0),
|
||||
Gd);
|
||||
BLAS.synchronise();
|
||||
|
||||
///////////////////////////////////////
|
||||
// Copy out the multirhs
|
||||
///////////////////////////////////////
|
||||
for(int r=0;r<nrhs;r++){
|
||||
int64_t offset = r*vw;
|
||||
autoView(v,guess[r],AcceleratorWrite);
|
||||
acceleratorCopyDeviceToDevice(&BLAS_G[offset],&v[0],sizeof(scalar_object)*vol);
|
||||
}
|
||||
RealD t1 = usecond();
|
||||
std::cout << GridLogMessage << "MultiRHSDeflation for "<<nrhs<<" sources with "<<nev<<" eigenvectors took " << (t1-t0)/1e3 <<" ms"<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -33,109 +33,111 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
* Script A = SolverMatrix
|
||||
* Script P = Preconditioner
|
||||
*
|
||||
* Deflation methods considered
|
||||
* -- Solve P A x = P b [ like Luscher ]
|
||||
* DEF-1 M P A x = M P b [i.e. left precon]
|
||||
* DEF-2 P^T M A x = P^T M b
|
||||
* ADEF-1 Preconditioner = M P + Q [ Q + M + M A Q]
|
||||
* ADEF-2 Preconditioner = P^T M + Q
|
||||
* BNN Preconditioner = P^T M P + Q
|
||||
* BNN2 Preconditioner = M P + P^TM +Q - M P A M
|
||||
*
|
||||
* Implement ADEF-2
|
||||
*
|
||||
* Vstart = P^Tx + Qb
|
||||
* M1 = P^TM + Q
|
||||
* M2=M3=1
|
||||
* Vout = x
|
||||
*/
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
// abstract base
|
||||
template<class Field, class CoarseField>
|
||||
class TwoLevelFlexiblePcg : public LinearFunction<Field>
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelCG : public LinearFunction<Field>
|
||||
{
|
||||
public:
|
||||
int verbose;
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
const int mmax = 5;
|
||||
GridBase *grid;
|
||||
GridBase *coarsegrid;
|
||||
|
||||
LinearOperatorBase<Field> *_Linop
|
||||
OperatorFunction<Field> *_Smoother,
|
||||
LinearFunction<CoarseField> *_CoarseSolver;
|
||||
|
||||
// Need somthing that knows how to get from Coarse to fine and back again
|
||||
// Fine operator, Smoother, CoarseSolver
|
||||
LinearOperatorBase<Field> &_FineLinop;
|
||||
LinearFunction<Field> &_Smoother;
|
||||
|
||||
// more most opertor functions
|
||||
TwoLevelFlexiblePcg(RealD tol,
|
||||
Integer maxit,
|
||||
LinearOperatorBase<Field> *Linop,
|
||||
LinearOperatorBase<Field> *SmootherLinop,
|
||||
OperatorFunction<Field> *Smoother,
|
||||
OperatorFunction<CoarseField> CoarseLinop
|
||||
) :
|
||||
TwoLevelCG(RealD tol,
|
||||
Integer maxit,
|
||||
LinearOperatorBase<Field> &FineLinop,
|
||||
LinearFunction<Field> &Smoother,
|
||||
GridBase *fine) :
|
||||
Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
_Linop(Linop),
|
||||
_PreconditionerLinop(PrecLinop),
|
||||
_Preconditioner(Preconditioner)
|
||||
{
|
||||
verbose=0;
|
||||
_FineLinop(FineLinop),
|
||||
_Smoother(Smoother)
|
||||
{
|
||||
grid = fine;
|
||||
};
|
||||
|
||||
// The Pcg routine is common to all, but the various matrices differ from derived
|
||||
// implementation to derived implmentation
|
||||
void operator() (const Field &src, Field &psi){
|
||||
void operator() (const Field &src, Field &psi){
|
||||
|
||||
psi.Checkerboard() = src.Checkerboard();
|
||||
grid = src.Grid();
|
||||
|
||||
|
||||
virtual void operator() (const Field &src, Field &x)
|
||||
{
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg starting single RHS"<<std::endl;
|
||||
RealD f;
|
||||
RealD rtzp,rtz,a,d,b;
|
||||
RealD rptzp;
|
||||
RealD tn;
|
||||
RealD guess = norm2(psi);
|
||||
RealD ssq = norm2(src);
|
||||
RealD rsq = ssq*Tolerance*Tolerance;
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Set up history vectors
|
||||
/////////////////////////////
|
||||
std::vector<Field> p (mmax,grid);
|
||||
int mmax = 5;
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocating"<<std::endl;
|
||||
std::vector<Field> p(mmax,grid);
|
||||
std::vector<Field> mmp(mmax,grid);
|
||||
std::vector<RealD> pAp(mmax);
|
||||
|
||||
Field x (grid); x = psi;
|
||||
Field z (grid);
|
||||
Field z(grid);
|
||||
Field tmp(grid);
|
||||
Field r (grid);
|
||||
Field mu (grid);
|
||||
|
||||
Field mp (grid);
|
||||
Field r (grid);
|
||||
Field mu (grid);
|
||||
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated"<<std::endl;
|
||||
//Initial residual computation & set up
|
||||
RealD guess = norm2(x);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg guess nrm "<<guess<<std::endl;
|
||||
RealD src_nrm = norm2(src);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg src nrm "<<src_nrm<<std::endl;
|
||||
|
||||
if ( src_nrm == 0.0 ) {
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg given trivial source norm "<<src_nrm<<std::endl;
|
||||
x=Zero();
|
||||
}
|
||||
RealD tn;
|
||||
|
||||
GridStopWatch HDCGTimer;
|
||||
HDCGTimer.Start();
|
||||
//////////////////////////
|
||||
// x0 = Vstart -- possibly modify guess
|
||||
//////////////////////////
|
||||
x=src;
|
||||
Vstart(x,src);
|
||||
|
||||
|
||||
// r0 = b -A x0
|
||||
HermOp(x,mmp); // Shouldn't this be something else?
|
||||
_FineLinop.HermOp(x,mmp[0]);
|
||||
axpy (r, -1.0,mmp[0], src); // Recomputes r=src-Ax0
|
||||
{
|
||||
double n1 = norm2(x);
|
||||
double n2 = norm2(mmp[0]);
|
||||
double n3 = norm2(r);
|
||||
std::cout<<GridLogMessage<<"x,vstart,r = "<<n1<<" "<<n2<<" "<<n3<<std::endl;
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Compute z = M1 x
|
||||
//////////////////////////////////
|
||||
M1(r,z,tmp,mp,SmootherMirs);
|
||||
PcgM1(r,z);
|
||||
rtzp =real(innerProduct(r,z));
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// Solve for Mss mu = P A z and set p = z-mu
|
||||
// Def2: p = 1 - Q Az = Pright z
|
||||
// Def2 p = 1 - Q Az = Pright z
|
||||
// Other algos M2 is trivial
|
||||
///////////////////////////////////////
|
||||
M2(z,p[0]);
|
||||
PcgM2(z,p[0]);
|
||||
|
||||
RealD ssq = norm2(src);
|
||||
RealD rsq = ssq*Tolerance*Tolerance;
|
||||
|
||||
std::cout << GridLogMessage<<"HDCG: k=0 residual "<<rtzp<<" rsq "<<rsq<<"\n";
|
||||
|
||||
Field pp(grid);
|
||||
|
||||
for (int k=0;k<=MaxIterations;k++){
|
||||
|
||||
@@ -143,31 +145,46 @@ class TwoLevelFlexiblePcg : public LinearFunction<Field>
|
||||
int peri_kp = (k+1) % mmax;
|
||||
|
||||
rtz=rtzp;
|
||||
d= M3(p[peri_k],mp,mmp[peri_k],tmp);
|
||||
d= PcgM3(p[peri_k],mmp[peri_k]);
|
||||
a = rtz/d;
|
||||
|
||||
// Memorise this
|
||||
pAp[peri_k] = d;
|
||||
|
||||
|
||||
axpy(x,a,p[peri_k],x);
|
||||
RealD rn = axpy_norm(r,-a,mmp[peri_k],r);
|
||||
|
||||
// Compute z = M x
|
||||
M1(r,z,tmp,mp);
|
||||
|
||||
PcgM1(r,z);
|
||||
|
||||
{
|
||||
RealD n1,n2;
|
||||
n1=norm2(r);
|
||||
n2=norm2(z);
|
||||
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : vector r,z "<<n1<<" "<<n2<<"\n";
|
||||
}
|
||||
rtzp =real(innerProduct(r,z));
|
||||
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : inner rtzp "<<rtzp<<"\n";
|
||||
|
||||
M2(z,mu); // ADEF-2 this is identity. Axpy possible to eliminate
|
||||
// PcgM2(z,p[0]);
|
||||
PcgM2(z,mu); // ADEF-2 this is identity. Axpy possible to eliminate
|
||||
|
||||
p[peri_kp]=mu;
|
||||
|
||||
p[peri_kp]=p[peri_k];
|
||||
|
||||
// Standard search direction p -> z + b p ; b =
|
||||
// Standard search direction p -> z + b p
|
||||
b = (rtzp)/rtz;
|
||||
|
||||
|
||||
int northog;
|
||||
// k=zero <=> peri_kp=1; northog = 1
|
||||
// k=1 <=> peri_kp=2; northog = 2
|
||||
// ... ... ...
|
||||
// k=mmax-2<=> peri_kp=mmax-1; northog = mmax-1
|
||||
// k=mmax-1<=> peri_kp=0; northog = 1
|
||||
|
||||
// northog = (peri_kp==0)?1:peri_kp; // This is the fCG(mmax) algorithm
|
||||
northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
||||
|
||||
std::cout<<GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : orthogonalising to last "<<northog<<" vectors\n";
|
||||
for(int back=0; back < northog; back++){
|
||||
int peri_back = (k-back)%mmax;
|
||||
RealD pbApk= real(innerProduct(mmp[peri_back],p[peri_kp]));
|
||||
@@ -176,75 +193,324 @@ class TwoLevelFlexiblePcg : public LinearFunction<Field>
|
||||
}
|
||||
|
||||
RealD rrn=sqrt(rn/ssq);
|
||||
std::cout<<GridLogMessage<<"TwoLevelfPcg: k= "<<k<<" residual = "<<rrn<<std::endl;
|
||||
RealD rtn=sqrt(rtz/ssq);
|
||||
RealD rtnp=sqrt(rtzp/ssq);
|
||||
|
||||
std::cout<<GridLogMessage<<"HDCG: fPcg k= "<<k<<" residual = "<<rrn<<"\n";
|
||||
|
||||
// Stopping condition
|
||||
if ( rn <= rsq ) {
|
||||
|
||||
HermOp(x,mmp); // Shouldn't this be something else?
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
||||
|
||||
_FineLinop.HermOp(x,mmp[0]);
|
||||
axpy(tmp,-1.0,src,mmp[0]);
|
||||
|
||||
RealD psinorm = sqrt(norm2(x));
|
||||
RealD srcnorm = sqrt(norm2(src));
|
||||
RealD tmpnorm = sqrt(norm2(tmp));
|
||||
RealD true_residual = tmpnorm/srcnorm;
|
||||
std::cout<<GridLogMessage<<"TwoLevelfPcg: true residual is "<<true_residual<<std::endl;
|
||||
std::cout<<GridLogMessage<<"TwoLevelfPcg: target residual was"<<Tolerance<<std::endl;
|
||||
return k;
|
||||
RealD mmpnorm = sqrt(norm2(mmp[0]));
|
||||
RealD xnorm = sqrt(norm2(x));
|
||||
RealD srcnorm = sqrt(norm2(src));
|
||||
RealD tmpnorm = sqrt(norm2(tmp));
|
||||
RealD true_residual = tmpnorm/srcnorm;
|
||||
std::cout<<GridLogMessage
|
||||
<<"HDCG: true residual is "<<true_residual
|
||||
<<" solution "<<xnorm
|
||||
<<" source "<<srcnorm
|
||||
<<" mmp "<<mmpnorm
|
||||
<<std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
// Non-convergence
|
||||
assert(0);
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
||||
RealD xnorm = sqrt(norm2(x));
|
||||
RealD srcnorm = sqrt(norm2(src));
|
||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
|
||||
{
|
||||
std::cout << GridLogMessage<<"HDCG: mrhs fPcg starting"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
int nrhs = src.size();
|
||||
std::vector<RealD> f(nrhs);
|
||||
std::vector<RealD> rtzp(nrhs);
|
||||
std::vector<RealD> rtz(nrhs);
|
||||
std::vector<RealD> a(nrhs);
|
||||
std::vector<RealD> d(nrhs);
|
||||
std::vector<RealD> b(nrhs);
|
||||
std::vector<RealD> rptzp(nrhs);
|
||||
/////////////////////////////
|
||||
// Set up history vectors
|
||||
/////////////////////////////
|
||||
int mmax = 3;
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocating"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
std::vector<std::vector<Field> > p(nrhs); for(int r=0;r<nrhs;r++) p[r].resize(mmax,grid);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated p"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
std::vector<std::vector<Field> > mmp(nrhs); for(int r=0;r<nrhs;r++) mmp[r].resize(mmax,grid);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated mmp"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
std::vector<std::vector<RealD> > pAp(nrhs); for(int r=0;r<nrhs;r++) pAp[r].resize(mmax);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated pAp"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
std::vector<Field> z(nrhs,grid);
|
||||
std::vector<Field> mp (nrhs,grid);
|
||||
std::vector<Field> r (nrhs,grid);
|
||||
std::vector<Field> mu (nrhs,grid);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated z,mp,r,mu"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
|
||||
//Initial residual computation & set up
|
||||
std::vector<RealD> src_nrm(nrhs);
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
src_nrm[rhs]=norm2(src[rhs]);
|
||||
GRID_ASSERT(src_nrm[rhs]!=0.0);
|
||||
}
|
||||
std::vector<RealD> tn(nrhs);
|
||||
|
||||
GridStopWatch HDCGTimer;
|
||||
HDCGTimer.Start();
|
||||
//////////////////////////
|
||||
// x0 = Vstart -- possibly modify guess
|
||||
//////////////////////////
|
||||
Vstart(x,src);
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
// r0 = b -A x0
|
||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
||||
axpy (r[rhs], -1.0,mmp[rhs][0], src[rhs]); // Recomputes r=src-Ax0
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Compute z = M1 x
|
||||
//////////////////////////////////
|
||||
// This needs a multiRHS version for acceleration
|
||||
PcgM1(r,z);
|
||||
|
||||
std::vector<RealD> ssq(nrhs);
|
||||
std::vector<RealD> rsq(nrhs);
|
||||
std::vector<Field> pp(nrhs,grid);
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
||||
p[rhs][0]=z[rhs];
|
||||
ssq[rhs]=norm2(src[rhs]);
|
||||
rsq[rhs]= ssq[rhs]*Tolerance*Tolerance;
|
||||
std::cout << GridLogMessage<<"mrhs HDCG: "<<rhs<<" k=0 residual "<<rtzp[rhs]<<" rsq "<<rsq[rhs]<<"\n";
|
||||
}
|
||||
|
||||
std::vector<RealD> rn(nrhs);
|
||||
for (int k=0;k<=MaxIterations;k++){
|
||||
|
||||
int peri_k = k % mmax;
|
||||
int peri_kp = (k+1) % mmax;
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
rtz[rhs]=rtzp[rhs];
|
||||
d[rhs]= PcgM3(p[rhs][peri_k],mmp[rhs][peri_k]);
|
||||
a[rhs] = rtz[rhs]/d[rhs];
|
||||
|
||||
// Memorise this
|
||||
pAp[rhs][peri_k] = d[rhs];
|
||||
|
||||
axpy(x[rhs],a[rhs],p[rhs][peri_k],x[rhs]);
|
||||
rn[rhs] = axpy_norm(r[rhs],-a[rhs],mmp[rhs][peri_k],r[rhs]);
|
||||
}
|
||||
|
||||
// Compute z = M x (for *all* RHS)
|
||||
PcgM1(r,z);
|
||||
std::cout << GridLogMessage<<"HDCG::fPcg M1 complete"<<std::endl;
|
||||
grid->Barrier();
|
||||
|
||||
RealD max_rn=0.0;
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
|
||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
||||
|
||||
std::cout << GridLogMessage<<"HDCG::fPcg rhs"<<rhs<<" iteration "<<k<<" : inner rtzp "<<rtzp[rhs]<<"\n";
|
||||
|
||||
mu[rhs]=z[rhs];
|
||||
|
||||
p[rhs][peri_kp]=mu[rhs];
|
||||
|
||||
// Standard search direction p == z + b p
|
||||
b[rhs] = (rtzp[rhs])/rtz[rhs];
|
||||
|
||||
int northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
||||
std::cout<<GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : orthogonalising to last "<<northog<<" vectors\n";
|
||||
for(int back=0; back < northog; back++){
|
||||
int peri_back = (k-back)%mmax;
|
||||
RealD pbApk= real(innerProduct(mmp[rhs][peri_back],p[rhs][peri_kp]));
|
||||
RealD beta = -pbApk/pAp[rhs][peri_back];
|
||||
axpy(p[rhs][peri_kp],beta,p[rhs][peri_back],p[rhs][peri_kp]);
|
||||
}
|
||||
|
||||
RealD rrn=sqrt(rn[rhs]/ssq[rhs]);
|
||||
RealD rtn=sqrt(rtz[rhs]/ssq[rhs]);
|
||||
RealD rtnp=sqrt(rtzp[rhs]/ssq[rhs]);
|
||||
|
||||
std::cout<<GridLogMessage<<"HDCG: rhs "<<rhs<<"fPcg k= "<<k<<" residual = "<<rrn<<"\n";
|
||||
if ( rrn > max_rn ) max_rn = rrn;
|
||||
}
|
||||
|
||||
// Stopping condition based on worst case
|
||||
if ( max_rn <= Tolerance ) {
|
||||
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
||||
Field tmp(grid);
|
||||
axpy(tmp,-1.0,src[rhs],mmp[rhs][0]);
|
||||
|
||||
RealD mmpnorm = sqrt(norm2(mmp[rhs][0]));
|
||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
||||
RealD tmpnorm = sqrt(norm2(tmp));
|
||||
RealD true_residual = tmpnorm/srcnorm;
|
||||
std::cout<<GridLogMessage
|
||||
<<"HDCG: true residual ["<<rhs<<"] is "<<true_residual
|
||||
<<" solution "<<xnorm
|
||||
<<" source "<<srcnorm
|
||||
<<" mmp "<<mmpnorm
|
||||
<<std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
virtual void M(Field & in,Field & out,Field & tmp) {
|
||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out)
|
||||
{
|
||||
std::cout << "PcgM1 default (cheat) mrhs version"<<std::endl;
|
||||
for(int rhs=0;rhs<in.size();rhs++){
|
||||
this->PcgM1(in[rhs],out[rhs]);
|
||||
}
|
||||
}
|
||||
virtual void PcgM1(Field & in, Field & out) =0;
|
||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src)
|
||||
{
|
||||
std::cout << "Vstart default (cheat) mrhs version"<<std::endl;
|
||||
for(int rhs=0;rhs<x.size();rhs++){
|
||||
this->Vstart(x[rhs],src[rhs]);
|
||||
}
|
||||
}
|
||||
virtual void Vstart(Field & x,const Field & src)=0;
|
||||
|
||||
virtual void PcgM2(const Field & in, Field & out) {
|
||||
out=in;
|
||||
}
|
||||
|
||||
virtual void M1(Field & in, Field & out) {// the smoother
|
||||
virtual RealD PcgM3(const Field & p, Field & mmp){
|
||||
RealD dd;
|
||||
_FineLinop.HermOp(p,mmp);
|
||||
ComplexD dot = innerProduct(p,mmp);
|
||||
dd=real(dot);
|
||||
return dd;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Only Def1 has non-trivial Vout.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
};
|
||||
|
||||
template<class Field, class CoarseField, class Aggregation>
|
||||
class TwoLevelADEF2 : public TwoLevelCG<Field>
|
||||
{
|
||||
public:
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Need something that knows how to get from Coarse to fine and back again
|
||||
// void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
||||
// void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
GridBase *coarsegrid;
|
||||
Aggregation &_Aggregates;
|
||||
LinearFunction<CoarseField> &_CoarseSolver;
|
||||
LinearFunction<CoarseField> &_CoarseSolverPrecise;
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// more most opertor functions
|
||||
TwoLevelADEF2(RealD tol,
|
||||
Integer maxit,
|
||||
LinearOperatorBase<Field> &FineLinop,
|
||||
LinearFunction<Field> &Smoother,
|
||||
LinearFunction<CoarseField> &CoarseSolver,
|
||||
LinearFunction<CoarseField> &CoarseSolverPrecise,
|
||||
Aggregation &Aggregates
|
||||
) :
|
||||
TwoLevelCG<Field>(tol,maxit,FineLinop,Smoother,Aggregates.FineGrid),
|
||||
_CoarseSolver(CoarseSolver),
|
||||
_CoarseSolverPrecise(CoarseSolverPrecise),
|
||||
_Aggregates(Aggregates)
|
||||
{
|
||||
coarsegrid = Aggregates.CoarseGrid;
|
||||
};
|
||||
|
||||
virtual void PcgM1(Field & in, Field & out)
|
||||
{
|
||||
GRID_TRACE("MultiGridPreconditioner ");
|
||||
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
||||
Field tmp(grid);
|
||||
Field Min(grid);
|
||||
|
||||
PcgM(in,Min); // Smoother call
|
||||
Field tmp(this->grid);
|
||||
Field Min(this->grid);
|
||||
CoarseField PleftProj(this->coarsegrid);
|
||||
CoarseField PleftMss_proj(this->coarsegrid);
|
||||
|
||||
HermOp(Min,out);
|
||||
GridStopWatch SmootherTimer;
|
||||
GridStopWatch MatrixTimer;
|
||||
SmootherTimer.Start();
|
||||
this->_Smoother(in,Min);
|
||||
SmootherTimer.Stop();
|
||||
|
||||
MatrixTimer.Start();
|
||||
this->_FineLinop.HermOp(Min,out);
|
||||
MatrixTimer.Stop();
|
||||
axpy(tmp,-1.0,out,in); // tmp = in - A Min
|
||||
|
||||
ProjectToSubspace(tmp,PleftProj);
|
||||
ApplyInverse(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
||||
PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
||||
GridStopWatch ProjTimer;
|
||||
GridStopWatch CoarseTimer;
|
||||
GridStopWatch PromTimer;
|
||||
ProjTimer.Start();
|
||||
this->_Aggregates.ProjectToSubspace(PleftProj,tmp);
|
||||
ProjTimer.Stop();
|
||||
CoarseTimer.Start();
|
||||
this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
||||
CoarseTimer.Stop();
|
||||
PromTimer.Start();
|
||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
||||
PromTimer.Stop();
|
||||
std::cout << GridLogPerformance << "PcgM1 breakdown "<<std::endl;
|
||||
std::cout << GridLogPerformance << "\tSmoother " << SmootherTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tProj " << ProjTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tCoarse " << CoarseTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tProm " << PromTimer.Elapsed() <<std::endl;
|
||||
|
||||
axpy(out,1.0,Min,tmp); // Min+tmp
|
||||
}
|
||||
|
||||
virtual void M2(const Field & in, Field & out) {
|
||||
out=in;
|
||||
// Must override for Def2 only
|
||||
// case PcgDef2:
|
||||
// Pright(in,out);
|
||||
// break;
|
||||
}
|
||||
|
||||
virtual RealD M3(const Field & p, Field & mmp){
|
||||
double d,dd;
|
||||
HermOpAndNorm(p,mmp,d,dd);
|
||||
return dd;
|
||||
// Must override for Def1 only
|
||||
// case PcgDef1:
|
||||
// d=linop_d->Mprec(p,mmp,tmp,0,1);// Dag no
|
||||
// linop_d->Mprec(mmp,mp,tmp,1);// Dag yes
|
||||
// Pleft(mp,mmp);
|
||||
// d=real(linop_d->inner(p,mmp));
|
||||
}
|
||||
|
||||
virtual void VstartDef2(Field & xconst Field & src){
|
||||
//case PcgDef2:
|
||||
//case PcgAdef2:
|
||||
//case PcgAdef2f:
|
||||
//case PcgV11f:
|
||||
virtual void Vstart(Field & x,const Field & src)
|
||||
{
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart "<<std::endl;
|
||||
///////////////////////////////////
|
||||
// Choose x_0 such that
|
||||
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
||||
@@ -256,142 +522,78 @@ class TwoLevelFlexiblePcg : public LinearFunction<Field>
|
||||
// = src_s - (A guess)_s - src_s + (A guess)_s
|
||||
// = 0
|
||||
///////////////////////////////////
|
||||
Field r(grid);
|
||||
Field mmp(grid);
|
||||
|
||||
HermOp(x,mmp);
|
||||
axpy (r, -1.0, mmp, src); // r_{-1} = src - A x
|
||||
ProjectToSubspace(r,PleftProj);
|
||||
ApplyInverseCG(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
||||
PromoteFromSubspace(PleftMss_proj,mmp);
|
||||
x=x+mmp;
|
||||
Field r(this->grid);
|
||||
Field mmp(this->grid);
|
||||
CoarseField PleftProj(this->coarsegrid);
|
||||
CoarseField PleftMss_proj(this->coarsegrid);
|
||||
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart projecting "<<std::endl;
|
||||
this->_Aggregates.ProjectToSubspace(PleftProj,src);
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart coarse solve "<<std::endl;
|
||||
this->_CoarseSolverPrecise(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart promote "<<std::endl;
|
||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,x);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelADEF1defl : public TwoLevelCG<Field>
|
||||
{
|
||||
public:
|
||||
const std::vector<Field> &evec;
|
||||
const std::vector<RealD> &eval;
|
||||
|
||||
TwoLevelADEF1defl(RealD tol,
|
||||
Integer maxit,
|
||||
LinearOperatorBase<Field> &FineLinop,
|
||||
LinearFunction<Field> &Smoother,
|
||||
std::vector<Field> &_evec,
|
||||
std::vector<RealD> &_eval) :
|
||||
TwoLevelCG<Field>(tol,maxit,FineLinop,Smoother,_evec[0].Grid()),
|
||||
evec(_evec),
|
||||
eval(_eval)
|
||||
{};
|
||||
|
||||
// Can just inherit existing M2
|
||||
// Can just inherit existing M3
|
||||
|
||||
// Simple vstart - do nothing
|
||||
virtual void Vstart(Field & x,const Field & src){
|
||||
return;
|
||||
x=src; // Could apply Q
|
||||
};
|
||||
|
||||
// Override PcgM1
|
||||
virtual void PcgM1(Field & in, Field & out)
|
||||
{
|
||||
GRID_TRACE("EvecPreconditioner ");
|
||||
int N=evec.size();
|
||||
Field Pin(this->grid);
|
||||
Field Qin(this->grid);
|
||||
|
||||
//MP + Q = M(1-AQ) + Q = M
|
||||
// // If we are eigenvector deflating in coarse space
|
||||
// // Q = Sum_i |phi_i> 1/lambda_i <phi_i|
|
||||
// // A Q = Sum_i |phi_i> <phi_i|
|
||||
// // M(1-AQ) = M(1-proj) + Q
|
||||
Qin.Checkerboard()=in.Checkerboard();
|
||||
Qin = Zero();
|
||||
Pin = in;
|
||||
for (int i=0;i<N;i++) {
|
||||
const Field& tmp = evec[i];
|
||||
auto ip = TensorRemove(innerProduct(tmp,in));
|
||||
axpy(Qin, ip / eval[i],tmp,Qin);
|
||||
axpy(Pin, -ip ,tmp,Pin);
|
||||
}
|
||||
|
||||
this->_Smoother(Pin,out);
|
||||
|
||||
out = out + Qin;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Only Def1 has non-trivial Vout. Override in Def1
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
virtual void Vout (Field & in, Field & out,Field & src){
|
||||
out = in;
|
||||
//case PcgDef1:
|
||||
// //Qb + PT x
|
||||
// ProjectToSubspace(src,PleftProj);
|
||||
// ApplyInverse(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
||||
// PromoteFromSubspace(PleftMss_proj,tmp);
|
||||
//
|
||||
// Pright(in,out);
|
||||
//
|
||||
// linop_d->axpy(out,tmp,out,1.0);
|
||||
// break;
|
||||
}
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Pright and Pleft are common to all implementations
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
virtual void Pright(Field & in,Field & out){
|
||||
// P_R = [ 1 0 ]
|
||||
// [ -Mss^-1 Msb 0 ]
|
||||
Field in_sbar(grid);
|
||||
|
||||
ProjectToSubspace(in,PleftProj);
|
||||
PromoteFromSubspace(PleftProj,out);
|
||||
axpy(in_sbar,-1.0,out,in); // in_sbar = in - in_s
|
||||
|
||||
HermOp(in_sbar,out);
|
||||
ProjectToSubspace(out,PleftProj); // Mssbar in_sbar (project)
|
||||
|
||||
ApplyInverse (PleftProj,PleftMss_proj); // Mss^{-1} Mssbar
|
||||
PromoteFromSubspace(PleftMss_proj,out); //
|
||||
|
||||
axpy(out,-1.0,out,in_sbar); // in_sbar - Mss^{-1} Mssbar in_sbar
|
||||
}
|
||||
virtual void Pleft (Field & in,Field & out){
|
||||
// P_L = [ 1 -Mbs Mss^-1]
|
||||
// [ 0 0 ]
|
||||
Field in_sbar(grid);
|
||||
Field tmp2(grid);
|
||||
Field Mtmp(grid);
|
||||
|
||||
ProjectToSubspace(in,PleftProj);
|
||||
PromoteFromSubspace(PleftProj,out);
|
||||
axpy(in_sbar,-1.0,out,in); // in_sbar = in - in_s
|
||||
|
||||
ApplyInverse(PleftProj,PleftMss_proj); // Mss^{-1} in_s
|
||||
PromoteFromSubspace(PleftMss_proj,out);
|
||||
|
||||
HermOp(out,Mtmp);
|
||||
|
||||
ProjectToSubspace(Mtmp,PleftProj); // Msbar s Mss^{-1}
|
||||
PromoteFromSubspace(PleftProj,tmp2);
|
||||
|
||||
axpy(out,-1.0,tmp2,Mtmp);
|
||||
axpy(out,-1.0,out,in_sbar); // in_sbar - Msbars Mss^{-1} in_s
|
||||
}
|
||||
}
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelFlexiblePcgADef2 : public TwoLevelFlexiblePcg<Field> {
|
||||
public:
|
||||
virtual void M(Field & in,Field & out,Field & tmp){
|
||||
|
||||
}
|
||||
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp){
|
||||
|
||||
}
|
||||
virtual void M2(Field & in, Field & out){
|
||||
|
||||
}
|
||||
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp){
|
||||
|
||||
}
|
||||
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp){
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
template<class Field>
|
||||
class TwoLevelFlexiblePcgAD : public TwoLevelFlexiblePcg<Field> {
|
||||
public:
|
||||
virtual void M(Field & in,Field & out,Field & tmp);
|
||||
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||
virtual void M2(Field & in, Field & out);
|
||||
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||
}
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelFlexiblePcgDef1 : public TwoLevelFlexiblePcg<Field> {
|
||||
public:
|
||||
virtual void M(Field & in,Field & out,Field & tmp);
|
||||
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||
virtual void M2(Field & in, Field & out);
|
||||
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||
virtual void Vout (Field & in, Field & out,Field & src,Field & tmp);
|
||||
}
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelFlexiblePcgDef2 : public TwoLevelFlexiblePcg<Field> {
|
||||
public:
|
||||
virtual void M(Field & in,Field & out,Field & tmp);
|
||||
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||
virtual void M2(Field & in, Field & out);
|
||||
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||
}
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelFlexiblePcgV11: public TwoLevelFlexiblePcg<Field> {
|
||||
public:
|
||||
virtual void M(Field & in,Field & out,Field & tmp);
|
||||
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||
virtual void M2(Field & in, Field & out);
|
||||
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,734 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/AdefGeneric.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
|
||||
/*
|
||||
* Compared to Tang-2009: P=Pleft. P^T = PRight Q=MssInv.
|
||||
* Script A = SolverMatrix
|
||||
* Script P = Preconditioner
|
||||
*
|
||||
* Implement ADEF-2
|
||||
*
|
||||
* Vstart = P^Tx + Qb
|
||||
* M1 = P^TM + Q
|
||||
* M2=M3=1
|
||||
*/
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
|
||||
template<class Field>
|
||||
class TwoLevelCGmrhs
|
||||
{
|
||||
public:
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
GridBase *grid;
|
||||
|
||||
// Fine operator, Smoother, CoarseSolver
|
||||
LinearOperatorBase<Field> &_FineLinop;
|
||||
LinearFunction<Field> &_Smoother;
|
||||
MultiRHSBlockCGLinalg<Field> _BlockCGLinalg;
|
||||
|
||||
GridStopWatch ProjectTimer;
|
||||
GridStopWatch PromoteTimer;
|
||||
GridStopWatch DeflateTimer;
|
||||
GridStopWatch CoarseTimer;
|
||||
GridStopWatch FineTimer;
|
||||
GridStopWatch SmoothTimer;
|
||||
GridStopWatch InsertTimer;
|
||||
|
||||
/*
|
||||
Field rrr;
|
||||
Field sss;
|
||||
Field qqq;
|
||||
Field zzz;
|
||||
*/
|
||||
// more most opertor functions
|
||||
TwoLevelCGmrhs(RealD tol,
|
||||
Integer maxit,
|
||||
LinearOperatorBase<Field> &FineLinop,
|
||||
LinearFunction<Field> &Smoother,
|
||||
GridBase *fine) :
|
||||
Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
_FineLinop(FineLinop),
|
||||
_Smoother(Smoother)
|
||||
/*
|
||||
rrr(fine),
|
||||
sss(fine),
|
||||
qqq(fine),
|
||||
zzz(fine)
|
||||
*/
|
||||
{
|
||||
grid = fine;
|
||||
};
|
||||
|
||||
// Vector case
|
||||
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
|
||||
{
|
||||
SolveSingleSystem(src,x);
|
||||
// SolvePrecBlockCG(src,x);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Thin QR factorisation (google it)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//Dimensions
|
||||
// R_{ferm x Nblock} = Q_{ferm x Nblock} x C_{Nblock x Nblock} -> ferm x Nblock
|
||||
//
|
||||
// Rdag R = m_rr = Herm = L L^dag <-- Cholesky decomposition (LLT routine in Eigen)
|
||||
//
|
||||
// Q C = R => Q = R C^{-1}
|
||||
//
|
||||
// Want Ident = Q^dag Q = C^{-dag} R^dag R C^{-1} = C^{-dag} L L^dag C^{-1} = 1_{Nblock x Nblock}
|
||||
//
|
||||
// Set C = L^{dag}, and then Q^dag Q = ident
|
||||
//
|
||||
// Checks:
|
||||
// Cdag C = Rdag R ; passes.
|
||||
// QdagQ = 1 ; passes
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void ThinQRfact (Eigen::MatrixXcd &m_zz,
|
||||
Eigen::MatrixXcd &C,
|
||||
Eigen::MatrixXcd &Cinv,
|
||||
std::vector<Field> & Q,
|
||||
std::vector<Field> & MQ,
|
||||
const std::vector<Field> & Z,
|
||||
const std::vector<Field> & MZ)
|
||||
{
|
||||
RealD t0=usecond();
|
||||
_BlockCGLinalg.InnerProductMatrix(m_zz,MZ,Z);
|
||||
RealD t1=usecond();
|
||||
|
||||
m_zz = 0.5*(m_zz+m_zz.adjoint());
|
||||
|
||||
Eigen::MatrixXcd L = m_zz.llt().matrixL();
|
||||
|
||||
C = L.adjoint();
|
||||
Cinv = C.inverse();
|
||||
|
||||
RealD t3=usecond();
|
||||
_BlockCGLinalg.MulMatrix( Q,Cinv,Z);
|
||||
_BlockCGLinalg.MulMatrix(MQ,Cinv,MZ);
|
||||
RealD t4=usecond();
|
||||
std::cout << " ThinQRfact IP :"<< t1-t0<<" us"<<std::endl;
|
||||
std::cout << " ThinQRfact Eigen :"<< t3-t1<<" us"<<std::endl;
|
||||
std::cout << " ThinQRfact MulMat:"<< t4-t3<<" us"<<std::endl;
|
||||
}
|
||||
|
||||
virtual void SolvePrecBlockCG (std::vector<Field> &src, std::vector<Field> &X)
|
||||
{
|
||||
std::cout << GridLogMessage<<"HDCG: mrhs fPrecBlockcg starting"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
int nrhs = src.size();
|
||||
// std::vector<RealD> f(nrhs);
|
||||
// std::vector<RealD> rtzp(nrhs);
|
||||
// std::vector<RealD> rtz(nrhs);
|
||||
// std::vector<RealD> a(nrhs);
|
||||
// std::vector<RealD> d(nrhs);
|
||||
// std::vector<RealD> b(nrhs);
|
||||
// std::vector<RealD> rptzp(nrhs);
|
||||
|
||||
////////////////////////////////////////////
|
||||
//Initial residual computation & set up
|
||||
////////////////////////////////////////////
|
||||
std::vector<RealD> ssq(nrhs);
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
ssq[rhs]=norm2(src[rhs]); GRID_ASSERT(ssq[rhs]!=0.0);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// Fields -- eliminate duplicates between fPcg and block cg
|
||||
///////////////////////////
|
||||
std::vector<Field> Mtmp(nrhs,grid);
|
||||
std::vector<Field> tmp(nrhs,grid);
|
||||
std::vector<Field> Z(nrhs,grid); // Rename Z to R
|
||||
std::vector<Field> MZ(nrhs,grid); // Rename MZ to Z
|
||||
std::vector<Field> Q(nrhs,grid); //
|
||||
std::vector<Field> MQ(nrhs,grid); // Rename to P
|
||||
std::vector<Field> D(nrhs,grid);
|
||||
std::vector<Field> AD(nrhs,grid);
|
||||
|
||||
/************************************************************************
|
||||
* Preconditioned Block conjugate gradient rQ
|
||||
* Generalise Sebastien Birk Thesis, after Dubrulle 2001.
|
||||
* Introduce preconditioning following Saad Ch9
|
||||
************************************************************************
|
||||
* Dimensions:
|
||||
*
|
||||
* X,B etc... ==(Nferm x nrhs)
|
||||
* Matrix A==(Nferm x Nferm)
|
||||
*
|
||||
* Nferm = Nspin x Ncolour x Ncomplex x Nlattice_site
|
||||
* QC => Thin QR factorisation (google it)
|
||||
*
|
||||
* R = B-AX
|
||||
* Z = Mi R
|
||||
* QC = Z
|
||||
* D = Q
|
||||
* for k:
|
||||
* R = AD
|
||||
* Z = Mi R
|
||||
* M = [D^dag R]^{-1}
|
||||
* X = X + D M C
|
||||
* QS = Q - Z.M
|
||||
* D = Q + D S^dag
|
||||
* C = S C
|
||||
*/
|
||||
Eigen::MatrixXcd m_DZ = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_M = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_zz = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_rr = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
|
||||
Eigen::MatrixXcd m_C = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_Cinv = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_S = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_Sinv = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
||||
|
||||
Eigen::MatrixXcd m_tmp = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
||||
Eigen::MatrixXcd m_tmp1 = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
||||
|
||||
GridStopWatch HDCGTimer;
|
||||
|
||||
//////////////////////////
|
||||
// x0 = Vstart -- possibly modify guess
|
||||
//////////////////////////
|
||||
Vstart(X,src);
|
||||
|
||||
//////////////////////////
|
||||
// R = B-AX
|
||||
//////////////////////////
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
// r0 = b -A x0
|
||||
_FineLinop.HermOp(X[rhs],tmp[rhs]);
|
||||
axpy (Z[rhs], -1.0,tmp[rhs], src[rhs]); // Computes R=Z=src - A X0
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Compute MZ = M1 Z = M1 B - M1 A x0
|
||||
//////////////////////////////////
|
||||
PcgM1(Z,MZ);
|
||||
|
||||
//////////////////////////////////
|
||||
// QC = Z
|
||||
//////////////////////////////////
|
||||
ThinQRfact (m_zz, m_C, m_Cinv, Q, MQ, Z, MZ);
|
||||
|
||||
//////////////////////////////////
|
||||
// D=MQ
|
||||
//////////////////////////////////
|
||||
for(int b=0;b<nrhs;b++) D[b]=MQ[b]; // LLT rotation of the MZ basis of search dirs
|
||||
|
||||
std::cout << GridLogMessage<<"PrecBlockCGrQ vec computed initial residual and QR fact " <<std::endl;
|
||||
|
||||
ProjectTimer.Reset();
|
||||
PromoteTimer.Reset();
|
||||
DeflateTimer.Reset();
|
||||
CoarseTimer.Reset();
|
||||
SmoothTimer.Reset();
|
||||
FineTimer.Reset();
|
||||
InsertTimer.Reset();
|
||||
|
||||
GridStopWatch M1Timer;
|
||||
GridStopWatch M2Timer;
|
||||
GridStopWatch M3Timer;
|
||||
GridStopWatch LinalgTimer;
|
||||
GridStopWatch InnerProdTimer;
|
||||
|
||||
HDCGTimer.Start();
|
||||
|
||||
std::vector<RealD> rn(nrhs);
|
||||
for (int k=0;k<=MaxIterations;k++){
|
||||
|
||||
////////////////////
|
||||
// Z = AD
|
||||
////////////////////
|
||||
M3Timer.Start();
|
||||
for(int b=0;b<nrhs;b++) _FineLinop.HermOp(D[b], Z[b]);
|
||||
M3Timer.Stop();
|
||||
|
||||
////////////////////
|
||||
// MZ = M1 Z <==== the Multigrid preconditioner
|
||||
////////////////////
|
||||
M1Timer.Start();
|
||||
PcgM1(Z,MZ);
|
||||
M1Timer.Stop();
|
||||
|
||||
FineTimer.Start();
|
||||
////////////////////
|
||||
// M = [D^dag Z]^{-1} = (<Ddag MZ>_M)^{-1} inner prod, generalising Saad derivation of Precon CG
|
||||
////////////////////
|
||||
InnerProdTimer.Start();
|
||||
_BlockCGLinalg.InnerProductMatrix(m_DZ,D,Z);
|
||||
InnerProdTimer.Stop();
|
||||
m_M = m_DZ.inverse();
|
||||
|
||||
///////////////////////////
|
||||
// X = X + D MC
|
||||
///////////////////////////
|
||||
m_tmp = m_M * m_C;
|
||||
LinalgTimer.Start();
|
||||
_BlockCGLinalg.MaddMatrix(X,m_tmp, D,X); // D are the search directions and X takes the updates
|
||||
LinalgTimer.Stop();
|
||||
|
||||
///////////////////////////
|
||||
// QS = Q - M Z
|
||||
// (MQ) S = MQ - M (M1Z)
|
||||
///////////////////////////
|
||||
LinalgTimer.Start();
|
||||
_BlockCGLinalg.MaddMatrix(tmp ,m_M, Z, Q,-1.0);
|
||||
_BlockCGLinalg.MaddMatrix(Mtmp,m_M,MZ,MQ,-1.0);
|
||||
ThinQRfact (m_zz, m_S, m_Sinv, Q, MQ, tmp, Mtmp);
|
||||
LinalgTimer.Stop();
|
||||
|
||||
////////////////////////////
|
||||
// D = MQ + D S^dag
|
||||
////////////////////////////
|
||||
m_tmp = m_S.adjoint();
|
||||
LinalgTimer.Start();
|
||||
_BlockCGLinalg.MaddMatrix(D,m_tmp,D,MQ);
|
||||
LinalgTimer.Stop();
|
||||
|
||||
////////////////////////////
|
||||
// C = S C
|
||||
////////////////////////////
|
||||
m_C = m_S*m_C;
|
||||
|
||||
////////////////////////////
|
||||
// convergence monitor
|
||||
////////////////////////////
|
||||
m_rr = m_C.adjoint() * m_C;
|
||||
|
||||
FineTimer.Stop();
|
||||
|
||||
RealD max_resid=0;
|
||||
RealD rrsum=0;
|
||||
RealD sssum=0;
|
||||
RealD rr;
|
||||
|
||||
for(int b=0;b<nrhs;b++) {
|
||||
rrsum+=real(m_rr(b,b));
|
||||
sssum+=ssq[b];
|
||||
rr = real(m_rr(b,b))/ssq[b];
|
||||
if ( rr > max_resid ) max_resid = rr;
|
||||
}
|
||||
std::cout << GridLogMessage <<
|
||||
"\t Prec BlockCGrQ Iteration "<<k<<" ave resid "<< std::sqrt(rrsum/sssum) << " max "<< std::sqrt(max_resid) <<std::endl;
|
||||
|
||||
|
||||
if ( max_resid < Tolerance*Tolerance ) {
|
||||
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : fine H "<<M3Timer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"**** M1 breakdown:"<<std::endl;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Project "<<ProjectTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Promote "<<PromoteTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Deflate "<<DeflateTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Coarse "<<CoarseTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Fine "<<FineTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Smooth "<<SmoothTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Insert "<<InsertTimer.Elapsed()<<std::endl;;
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
|
||||
_FineLinop.HermOp(X[rhs],tmp[rhs]);
|
||||
|
||||
Field mytmp(grid);
|
||||
axpy(mytmp,-1.0,src[rhs],tmp[rhs]);
|
||||
|
||||
RealD xnorm = sqrt(norm2(X[rhs]));
|
||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
||||
RealD tmpnorm = sqrt(norm2(mytmp));
|
||||
RealD true_residual = tmpnorm/srcnorm;
|
||||
std::cout<<GridLogMessage
|
||||
<<"HDCG: true residual ["<<rhs<<"] is "<<true_residual
|
||||
<<" solution "<<xnorm
|
||||
<<" source "<<srcnorm
|
||||
<<std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: PrecBlockCGrQ not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
virtual void SolveSingleSystem (std::vector<Field> &src, std::vector<Field> &x)
|
||||
{
|
||||
std::cout << GridLogMessage<<"HDCG: mrhs fPcg starting"<<std::endl;
|
||||
src[0].Grid()->Barrier();
|
||||
int nrhs = src.size();
|
||||
std::vector<RealD> f(nrhs);
|
||||
std::vector<RealD> rtzp(nrhs);
|
||||
std::vector<RealD> rtz(nrhs);
|
||||
std::vector<RealD> a(nrhs);
|
||||
std::vector<RealD> d(nrhs);
|
||||
std::vector<RealD> b(nrhs);
|
||||
std::vector<RealD> rptzp(nrhs);
|
||||
/////////////////////////////
|
||||
// Set up history vectors
|
||||
/////////////////////////////
|
||||
int mmax = 3;
|
||||
|
||||
std::vector<std::vector<Field> > p(nrhs); for(int r=0;r<nrhs;r++) p[r].resize(mmax,grid);
|
||||
std::vector<std::vector<Field> > mmp(nrhs); for(int r=0;r<nrhs;r++) mmp[r].resize(mmax,grid);
|
||||
std::vector<std::vector<RealD> > pAp(nrhs); for(int r=0;r<nrhs;r++) pAp[r].resize(mmax);
|
||||
|
||||
std::vector<Field> z(nrhs,grid);
|
||||
std::vector<Field> mp (nrhs,grid);
|
||||
std::vector<Field> r (nrhs,grid);
|
||||
std::vector<Field> mu (nrhs,grid);
|
||||
|
||||
//Initial residual computation & set up
|
||||
std::vector<RealD> src_nrm(nrhs);
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
src_nrm[rhs]=norm2(src[rhs]);
|
||||
GRID_ASSERT(src_nrm[rhs]!=0.0);
|
||||
}
|
||||
std::vector<RealD> tn(nrhs);
|
||||
|
||||
GridStopWatch HDCGTimer;
|
||||
//////////////////////////
|
||||
// x0 = Vstart -- possibly modify guess
|
||||
//////////////////////////
|
||||
Vstart(x,src);
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
// r0 = b -A x0
|
||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
||||
axpy (r[rhs], -1.0,mmp[rhs][0], src[rhs]); // Recomputes r=src-Ax0
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Compute z = M1 x
|
||||
//////////////////////////////////
|
||||
// This needs a multiRHS version for acceleration
|
||||
PcgM1(r,z);
|
||||
|
||||
std::vector<RealD> ssq(nrhs);
|
||||
std::vector<RealD> rsq(nrhs);
|
||||
std::vector<Field> pp(nrhs,grid);
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
||||
p[rhs][0]=z[rhs];
|
||||
ssq[rhs]=norm2(src[rhs]);
|
||||
rsq[rhs]= ssq[rhs]*Tolerance*Tolerance;
|
||||
// std::cout << GridLogMessage<<"mrhs HDCG: "<<rhs<<" k=0 residual "<<rtzp[rhs]<<" rsq "<<rsq[rhs]<<"\n";
|
||||
}
|
||||
|
||||
ProjectTimer.Reset();
|
||||
PromoteTimer.Reset();
|
||||
DeflateTimer.Reset();
|
||||
CoarseTimer.Reset();
|
||||
SmoothTimer.Reset();
|
||||
FineTimer.Reset();
|
||||
InsertTimer.Reset();
|
||||
|
||||
GridStopWatch M1Timer;
|
||||
GridStopWatch M2Timer;
|
||||
GridStopWatch M3Timer;
|
||||
GridStopWatch LinalgTimer;
|
||||
|
||||
HDCGTimer.Start();
|
||||
|
||||
std::vector<RealD> rn(nrhs);
|
||||
for (int k=0;k<=MaxIterations;k++){
|
||||
|
||||
int peri_k = k % mmax;
|
||||
int peri_kp = (k+1) % mmax;
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
rtz[rhs]=rtzp[rhs];
|
||||
M3Timer.Start();
|
||||
d[rhs]= PcgM3(p[rhs][peri_k],mmp[rhs][peri_k]);
|
||||
M3Timer.Stop();
|
||||
a[rhs] = rtz[rhs]/d[rhs];
|
||||
|
||||
LinalgTimer.Start();
|
||||
// Memorise this
|
||||
pAp[rhs][peri_k] = d[rhs];
|
||||
|
||||
axpy(x[rhs],a[rhs],p[rhs][peri_k],x[rhs]);
|
||||
rn[rhs] = axpy_norm(r[rhs],-a[rhs],mmp[rhs][peri_k],r[rhs]);
|
||||
LinalgTimer.Stop();
|
||||
}
|
||||
|
||||
// Compute z = M x (for *all* RHS)
|
||||
M1Timer.Start();
|
||||
PcgM1(r,z);
|
||||
M1Timer.Stop();
|
||||
|
||||
RealD max_rn=0.0;
|
||||
LinalgTimer.Start();
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
|
||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
||||
|
||||
// std::cout << GridLogMessage<<"HDCG::fPcg rhs"<<rhs<<" iteration "<<k<<" : inner rtzp "<<rtzp[rhs]<<"\n";
|
||||
mu[rhs]=z[rhs];
|
||||
|
||||
p[rhs][peri_kp]=mu[rhs];
|
||||
|
||||
// Standard search direction p == z + b p
|
||||
b[rhs] = (rtzp[rhs])/rtz[rhs];
|
||||
|
||||
int northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
||||
for(int back=0; back < northog; back++){
|
||||
int peri_back = (k-back)%mmax;
|
||||
RealD pbApk= real(innerProduct(mmp[rhs][peri_back],p[rhs][peri_kp]));
|
||||
RealD beta = -pbApk/pAp[rhs][peri_back];
|
||||
axpy(p[rhs][peri_kp],beta,p[rhs][peri_back],p[rhs][peri_kp]);
|
||||
}
|
||||
|
||||
RealD rrn=sqrt(rn[rhs]/ssq[rhs]);
|
||||
RealD rtn=sqrt(rtz[rhs]/ssq[rhs]);
|
||||
RealD rtnp=sqrt(rtzp[rhs]/ssq[rhs]);
|
||||
|
||||
std::cout<<GridLogMessage<<"HDCG:fPcg rhs "<<rhs<<" k= "<<k<<" residual = "<<rrn<<"\n";
|
||||
if ( rrn > max_rn ) max_rn = rrn;
|
||||
}
|
||||
LinalgTimer.Stop();
|
||||
|
||||
// Stopping condition based on worst case
|
||||
if ( max_rn <= Tolerance ) {
|
||||
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : fine M3 "<<M3Timer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"**** M1 breakdown:"<<std::endl;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Project "<<ProjectTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Promote "<<PromoteTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Deflate "<<DeflateTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Coarse "<<CoarseTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Fine "<<FineTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Smooth "<<SmoothTimer.Elapsed()<<std::endl;;
|
||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Insert "<<InsertTimer.Elapsed()<<std::endl;;
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
||||
Field tmp(grid);
|
||||
axpy(tmp,-1.0,src[rhs],mmp[rhs][0]);
|
||||
|
||||
RealD mmpnorm = sqrt(norm2(mmp[rhs][0]));
|
||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
||||
RealD tmpnorm = sqrt(norm2(tmp));
|
||||
RealD true_residual = tmpnorm/srcnorm;
|
||||
std::cout<<GridLogMessage
|
||||
<<"HDCG: true residual ["<<rhs<<"] is "<<true_residual
|
||||
<<" solution "<<xnorm
|
||||
<<" source "<<srcnorm
|
||||
<<" mmp "<<mmpnorm
|
||||
<<std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
HDCGTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"HDCG: not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
||||
for(int rhs=0;rhs<nrhs;rhs++){
|
||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out) = 0;
|
||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src) = 0;
|
||||
virtual void PcgM2(const Field & in, Field & out) {
|
||||
out=in;
|
||||
}
|
||||
|
||||
virtual RealD PcgM3(const Field & p, Field & mmp){
|
||||
RealD dd;
|
||||
_FineLinop.HermOp(p,mmp);
|
||||
ComplexD dot = innerProduct(p,mmp);
|
||||
dd=real(dot);
|
||||
return dd;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<class Field, class CoarseField>
|
||||
class TwoLevelADEF2mrhs : public TwoLevelCGmrhs<Field>
|
||||
{
|
||||
public:
|
||||
GridBase *coarsegrid;
|
||||
GridBase *coarsegridmrhs;
|
||||
LinearFunction<CoarseField> &_CoarseSolverMrhs;
|
||||
LinearFunction<CoarseField> &_CoarseSolverPreciseMrhs;
|
||||
MultiRHSBlockProject<Field> &_Projector;
|
||||
MultiRHSDeflation<CoarseField> &_Deflator;
|
||||
|
||||
|
||||
TwoLevelADEF2mrhs(RealD tol,
|
||||
Integer maxit,
|
||||
LinearOperatorBase<Field> &FineLinop,
|
||||
LinearFunction<Field> &Smoother,
|
||||
LinearFunction<CoarseField> &CoarseSolverMrhs,
|
||||
LinearFunction<CoarseField> &CoarseSolverPreciseMrhs,
|
||||
MultiRHSBlockProject<Field> &Projector,
|
||||
MultiRHSDeflation<CoarseField> &Deflator,
|
||||
GridBase *_coarsemrhsgrid) :
|
||||
TwoLevelCGmrhs<Field>(tol, maxit,FineLinop,Smoother,Projector.fine_grid),
|
||||
_CoarseSolverMrhs(CoarseSolverMrhs),
|
||||
_CoarseSolverPreciseMrhs(CoarseSolverPreciseMrhs),
|
||||
_Projector(Projector),
|
||||
_Deflator(Deflator)
|
||||
{
|
||||
coarsegrid = Projector.coarse_grid;
|
||||
coarsegridmrhs = _coarsemrhsgrid;// Thi could be in projector
|
||||
};
|
||||
|
||||
// Override Vstart
|
||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src)
|
||||
{
|
||||
int nrhs=x.size();
|
||||
///////////////////////////////////
|
||||
// Choose x_0 such that
|
||||
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
||||
// = [1 - Ass_inv A] Guess + Assinv src
|
||||
// = P^T guess + Assinv src
|
||||
// = Vstart [Tang notation]
|
||||
// This gives:
|
||||
// W^T (src - A x_0) = src_s - A guess_s - r_s
|
||||
// = src_s - (A guess)_s - src_s + (A guess)_s
|
||||
// = 0
|
||||
///////////////////////////////////
|
||||
std::vector<CoarseField> PleftProj(nrhs,this->coarsegrid);
|
||||
std::vector<CoarseField> PleftMss_proj(nrhs,this->coarsegrid);
|
||||
CoarseField PleftProjMrhs(this->coarsegridmrhs);
|
||||
CoarseField PleftMss_projMrhs(this->coarsegridmrhs);
|
||||
|
||||
this->_Projector.blockProject(src,PleftProj);
|
||||
this->_Deflator.DeflateSources(PleftProj,PleftMss_proj);
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
InsertSliceFast(PleftProj[rhs],PleftProjMrhs,rhs,0);
|
||||
InsertSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0); // the guess
|
||||
}
|
||||
|
||||
this->_CoarseSolverPreciseMrhs(PleftProjMrhs,PleftMss_projMrhs); // Ass^{-1} r_s
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
ExtractSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0);
|
||||
}
|
||||
this->_Projector.blockPromote(x,PleftMss_proj);
|
||||
}
|
||||
|
||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out){
|
||||
|
||||
int nrhs=in.size();
|
||||
|
||||
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
||||
std::vector<Field> tmp(nrhs,this->grid);
|
||||
std::vector<Field> Min(nrhs,this->grid);
|
||||
|
||||
std::vector<CoarseField> PleftProj(nrhs,this->coarsegrid);
|
||||
std::vector<CoarseField> PleftMss_proj(nrhs,this->coarsegrid);
|
||||
|
||||
CoarseField PleftProjMrhs(this->coarsegridmrhs);
|
||||
CoarseField PleftMss_projMrhs(this->coarsegridmrhs);
|
||||
|
||||
// this->rrr=in[0];
|
||||
|
||||
#undef SMOOTHER_BLOCK_SOLVE
|
||||
#if SMOOTHER_BLOCK_SOLVE
|
||||
this->SmoothTimer.Start();
|
||||
this->_Smoother(in,Min);
|
||||
this->SmoothTimer.Stop();
|
||||
#else
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
this->SmoothTimer.Start();
|
||||
this->_Smoother(in[rhs],Min[rhs]);
|
||||
this->SmoothTimer.Stop();
|
||||
}
|
||||
#endif
|
||||
// this->sss=Min[0];
|
||||
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
|
||||
this->FineTimer.Start();
|
||||
this->_FineLinop.HermOp(Min[rhs],out[rhs]);
|
||||
axpy(tmp[rhs],-1.0,out[rhs],in[rhs]); // resid = in - A Min
|
||||
this->FineTimer.Stop();
|
||||
|
||||
}
|
||||
|
||||
this->ProjectTimer.Start();
|
||||
this->_Projector.blockProject(tmp,PleftProj);
|
||||
this->ProjectTimer.Stop();
|
||||
this->DeflateTimer.Start();
|
||||
this->_Deflator.DeflateSources(PleftProj,PleftMss_proj);
|
||||
this->DeflateTimer.Stop();
|
||||
this->InsertTimer.Start();
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
InsertSliceFast(PleftProj[rhs],PleftProjMrhs,rhs,0);
|
||||
InsertSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0); // the guess
|
||||
}
|
||||
this->InsertTimer.Stop();
|
||||
|
||||
this->CoarseTimer.Start();
|
||||
this->_CoarseSolverMrhs(PleftProjMrhs,PleftMss_projMrhs); // Ass^{-1} [in - A Min]_s
|
||||
this->CoarseTimer.Stop();
|
||||
|
||||
this->InsertTimer.Start();
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
ExtractSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0);
|
||||
}
|
||||
this->InsertTimer.Stop();
|
||||
this->PromoteTimer.Start();
|
||||
this->_Projector.blockPromote(tmp,PleftMss_proj);// tmp= Q[in - A Min]
|
||||
this->PromoteTimer.Stop();
|
||||
this->FineTimer.Start();
|
||||
// this->qqq=tmp[0];
|
||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
||||
axpy(out[rhs],1.0,Min[rhs],tmp[rhs]); // Min+tmp
|
||||
}
|
||||
// this->zzz=out[0];
|
||||
this->FineTimer.Stop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/Arnoldi.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_ARNOLDI_H
|
||||
#define GRID_ARNOLDI_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
//Moved to KrylovSchur
|
||||
#if 0
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* Options for which Ritz values to keep in implicit restart.
|
||||
*/
|
||||
enum RitzFilter {
|
||||
EvalNormSmall, // Keep evals with smallest norm
|
||||
EvalNormLarge, // Keep evals with largest norm
|
||||
EvalReSmall, // Keep evals with smallest real part
|
||||
EvalReLarge // Keep evals with largest real part
|
||||
};
|
||||
|
||||
// Select comparison function from RitzFilter
|
||||
struct ComplexComparator
|
||||
{
|
||||
RitzFilter f;
|
||||
ComplexComparator (RitzFilter _f) : f(_f) {}
|
||||
bool operator()(std::complex<double> z1, std::complex<double> z2) {
|
||||
switch (f) {
|
||||
RealD tmp1, tmp2;
|
||||
tmp1=std::abs(std::imag(z1));
|
||||
tmp2=std::abs(std::imag(z2));
|
||||
case EvalNormSmall:
|
||||
return std::abs(z1) < std::abs(z2);
|
||||
case EvalNormLarge:
|
||||
return std::abs(z1) > std::abs(z2);
|
||||
// Terrible hack
|
||||
// return std::abs(std::real(z1)) < std::abs(std::real(z2));
|
||||
// if ( std::abs(std::real(z1)) >4.) tmp1 +=1.;
|
||||
// if ( std::abs(std::real(z2)) >4.) tmp2 +=1.;
|
||||
case EvalReSmall:
|
||||
return tmp1 < tmp2;
|
||||
// return std::abs(std::imag(z1)) < std::abs(std::imag(z2));
|
||||
case EvalReLarge:
|
||||
return tmp1 > tmp2;
|
||||
// return std::abs(std::real(z1)) > std::abs(std::real(z2));
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
=======
|
||||
>>>>>>> 68af1bba67dd62881ead5ab1e54962a5486a0791
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of the Arnoldi algorithm.
|
||||
*/
|
||||
template<class Field>
|
||||
class Arnoldi {
|
||||
|
||||
private:
|
||||
|
||||
std::string cname = std::string("Arnoldi");
|
||||
int MaxIter; // Max iterations
|
||||
RealD Tolerance;
|
||||
RealD ssq;
|
||||
RealD rtol;
|
||||
int Nm; // Number of basis vectors to track (equals MaxIter if no restart)
|
||||
int Nk; // Number of basis vectors to keep every restart (equals -1 if no restart)
|
||||
int Nstop; // Stop after converging Nstop eigenvectors.
|
||||
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
RealD approxLambdaMax;
|
||||
RealD beta_k;
|
||||
Field f;
|
||||
std::vector<Field> basis; // orthonormal Arnoldi basis
|
||||
Eigen::MatrixXcd Hess; // Hessenberg matrix of size Nbasis (after construction)
|
||||
Eigen::MatrixXcd Qt; // Transpose of basis rotation which projects out high modes.
|
||||
|
||||
Eigen::VectorXcd evals; // evals of Hess
|
||||
Eigen::MatrixXcd littleEvecs; // Nm x Nm evecs matrix
|
||||
std::vector<Field> evecs; // Vector of evec fields
|
||||
|
||||
RitzFilter ritzFilter; // how to sort evals
|
||||
|
||||
public:
|
||||
|
||||
Arnoldi(LinearOperatorBase<Field> &_Linop, GridBase *_Grid, RealD _Tolerance, RitzFilter filter = EvalReSmall)
|
||||
: Linop(_Linop), Grid(_Grid), Tolerance(_Tolerance), ritzFilter(filter), f(_Grid), MaxIter(-1), Nm(-1), Nk(-1),
|
||||
Nstop (-1), evals (0), evecs (), ssq (0.0), rtol (0.0), beta_k (0.0), approxLambdaMax (0.0)
|
||||
{
|
||||
f = Zero();
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the Arnoldi loop with(out) implicit restarting. For each iteration:
|
||||
* - Runs an Arnoldi step.
|
||||
* - Computes the eigensystem of the Hessenberg matrix.
|
||||
* - Performs implicit restarting.
|
||||
*/
|
||||
void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, bool doubleOrthog = false) {
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm; Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
|
||||
ssq = norm2(v0);
|
||||
RealD approxLambdaMax = approxMaxEval(v0);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
|
||||
ComplexComparator compareComplex (ritzFilter);
|
||||
std::cout << GridLogMessage << "Comparing Ritz values with: " << ritzFilter << std::endl;
|
||||
|
||||
int start = 1;
|
||||
Field startVec = v0;
|
||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
for (int i = 0; i < MaxIter; i++) {
|
||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
||||
|
||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
||||
startVec = f;
|
||||
|
||||
// compute eigensystem and sort evals
|
||||
// compute_eigensystem();
|
||||
compute_eigensystem(Hess);
|
||||
std::cout << GridLogMessage << "Eigenvalues after Arnoldi step: " << std::endl << evals << std::endl;
|
||||
|
||||
std::sort(evals.begin(), evals.end(), compareComplex);
|
||||
std::cout << GridLogMessage << "Ritz values after sorting (first Nk preserved): " << std::endl << evals << std::endl;
|
||||
// SU(N)::tepidConfiguration
|
||||
|
||||
// Implicit restart to de-weight unwanted eigenvalues
|
||||
implicitRestart(_Nm, _Nk); // probably can delete _Nm and _Nk from function args
|
||||
start = Nk;
|
||||
|
||||
// check convergence and return if needed.
|
||||
int Nconv = converged();
|
||||
std::cout << GridLogMessage << "Number of evecs converged: " << Nconv << std::endl;
|
||||
if (Nconv >= Nstop || i == MaxIter - 1) {
|
||||
std::cout << GridLogMessage << "Converged with " << Nconv << " / " << Nstop << " eigenvectors on iteration "
|
||||
<< i << "." << std::endl;
|
||||
basisRotate(evecs, Qt, 0, Nk, 0, Nk, Nm);
|
||||
std::cout << GridLogMessage << "Eigenvalues [first " << Nconv << " converged]: " << std::endl << evals << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the maximum eigenvalue of Linop.Op to normalize the residual and test for convergence.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Field& v0
|
||||
* Source field to start with. Must have non-zero norm.
|
||||
* int MAX_ITER (default = 50)
|
||||
* Maximum number of iterations for power approximation.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* RealD lamApprox
|
||||
* Approximation of largest eigenvalue.
|
||||
*/
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50) {
|
||||
assert (norm2(v0) > 1e-8); // must have relatively large source norm to start
|
||||
RealD lamApprox = 0.0;
|
||||
RealD denom = 1.0; RealD num = 1.0;
|
||||
Field v0cp (Grid); Field tmp (Grid);
|
||||
v0cp = v0;
|
||||
denom = std::sqrt(norm2(v0cp));
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(v0cp, tmp); // CAREFUL: do not do Op(tmp, tmp)
|
||||
v0cp = tmp;
|
||||
num = std::sqrt(norm2(v0cp)); // num = |A^{n+1} v0|
|
||||
lamApprox = num / denom; // lam = |A^{n+1} v0| / |A^n v0|
|
||||
std::cout << GridLogDebug << "Approx for max eval: " << lamApprox << std::endl;
|
||||
denom = num; // denom = |A^{n} v0|
|
||||
}
|
||||
return lamApprox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the Arnoldi basis for the Krylov space K_n(D, src). (TODO make private)
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* v0 : Field&
|
||||
* Source to generate Krylov basis.
|
||||
* Nm : int
|
||||
* Final size of the basis desired. If the basis becomes complete before a basis of size Nm is constructed
|
||||
* (determined by relative tolerance Tolerance), stops iteration there.
|
||||
* doubleOrthog : bool (default = false)
|
||||
* Whether to double orthogonalize the basis (for numerical cancellations) or not.
|
||||
* start : int (default = 0)
|
||||
* If non-zero, assumes part of the Arnoldi basis has already been constructed.
|
||||
*/
|
||||
void arnoldiIteration(const Field& v0, int Nm, int start = 1, bool doubleOrthog = false)
|
||||
{
|
||||
|
||||
ComplexD coeff;
|
||||
Field w (Grid); // A acting on last Krylov vector.
|
||||
|
||||
if (start == 1) { // initialize everything that we need.
|
||||
RealD v0Norm = 1 / std::sqrt(ssq);
|
||||
basis.push_back(v0Norm * v0); // normalized source
|
||||
|
||||
Hess = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
f = Zero();
|
||||
} else {
|
||||
assert( start == basis.size() ); // should be starting at the end of basis (start = Nk)
|
||||
Eigen::MatrixXcd HessCp = Hess;
|
||||
Hess = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
Hess(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = HessCp;
|
||||
}
|
||||
|
||||
// Construct next Arnoldi vector by normalizing w_i = Dv_i - \sum_j v_j h_{ji}
|
||||
for (int i = start - 1; i < Nm; i++) {
|
||||
|
||||
Linop.Op(basis.back(), w);
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
coeff = innerProduct(basis[j], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after.
|
||||
Hess(j, i) = coeff;
|
||||
w -= coeff * basis[j];
|
||||
}
|
||||
|
||||
if (doubleOrthog) {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
// add w_i to the pile
|
||||
if (i < Nm - 1) {
|
||||
coeff = std::sqrt(norm2(w));
|
||||
Hess(i+1, i) = coeff;
|
||||
basis.push_back(
|
||||
(1.0/coeff) * w
|
||||
);
|
||||
}
|
||||
|
||||
// after iterations, update f and beta_k = ||f||
|
||||
f = w; // make sure f is not normalized
|
||||
beta_k = std::sqrt(norm2(f)); // beta_k = ||f_k|| determines convergence.
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "|f|^2 after Arnoldi step = " << norm2(f) << std::endl;
|
||||
std::cout << GridLogDebug << "Computed Hessenberg matrix = " << std::endl << Hess << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the eigensystem of the linear operator by computing the eigensystem of
|
||||
* the Hessenberg matrix. Assumes that the Hessenberg matrix has already been constructed (by
|
||||
* calling the operator() function).
|
||||
*
|
||||
* TODO implement in parent class eventually.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Eigen::MatrixXcd& S
|
||||
* Schur matrix (upper triangular) similar to original Rayleigh quotient.
|
||||
*/
|
||||
void compute_eigensystem(Eigen::MatrixXcd& S)
|
||||
{
|
||||
|
||||
std::cout << GridLogMessage << "Computing eigenvalues." << std::endl;
|
||||
|
||||
evecs.clear();
|
||||
|
||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
||||
es.compute(S);
|
||||
evals = es.eigenvalues();
|
||||
littleEvecs = es.eigenvectors();
|
||||
|
||||
// Convert evecs to lattice fields
|
||||
for (int k = 0; k < evals.size(); k++) {
|
||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
||||
Field tmp (basis[0].Grid());
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
tmp = tmp + vec[j] * basis[j];
|
||||
}
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << std::endl << evals << std::endl;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the factorization DV = V^\dag H + f e^\dag with the last-computed
|
||||
* V, H, f.
|
||||
*/
|
||||
// RealD verifyFactorization() {
|
||||
// int k = basis.size(); // number of basis vectors, also the size of H.
|
||||
// std::vector<Field> factorized (k, Zero());
|
||||
// Field tmp (FGrid); tmp = Zero();
|
||||
// for (int i = 0; i < basis.size(); i++) {
|
||||
// Linop.Op(basis[i], tmp);
|
||||
// }
|
||||
// // basisRotate(basis, Q, 0, Nk, 0, Nk, Nm);
|
||||
// // Linop.Op(, )
|
||||
// }
|
||||
|
||||
/* Getters */
|
||||
Eigen::MatrixXcd getHessenbergMat() { return Hess; }
|
||||
Field getF() { return f; }
|
||||
std::vector<Field> getBasis() { return basis; }
|
||||
Eigen::VectorXcd getEvals() { return evals; }
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
|
||||
/**
|
||||
* Implements implicit restarting for Arnoldi. Assumes eigenvalues are sorted.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* int _Nm
|
||||
* Size of basis to keep (Hessenberg is MxM).
|
||||
* int Nk
|
||||
* Number of basis vectors to keep at each restart.
|
||||
*/
|
||||
void implicitRestart(int _Nm, int _Nk) {
|
||||
assert ( _Nk <= _Nm );
|
||||
Nm = _Nm; Nk = _Nk;
|
||||
int Np = Nm - Nk; // keep Nk smallest (or largest, depends on sort function) evecs
|
||||
|
||||
std::cout << GridLogMessage << "Computing QR Factorizations." << std::endl;
|
||||
|
||||
Eigen::MatrixXcd Q = Eigen::MatrixXcd::Identity(Nm, Nm);
|
||||
Eigen::MatrixXcd Qi (Nm, Nm);
|
||||
Eigen::MatrixXcd R (Nm, Nm);
|
||||
|
||||
for (int i = Nk; i < Nm; i++) { // keep the first Nk eigenvalues and iterate through the last Np. Should loop Np times
|
||||
|
||||
// Useful debugging output
|
||||
std::cout << GridLogDebug << "Computing QR factorization for i = " << i << std::endl;
|
||||
std::cout << GridLogDebug << "Eval shift = " << evals[i] << std::endl;
|
||||
std::cout << GridLogDebug << "Hess before rotation: " << Hess << std::endl;
|
||||
|
||||
// QR factorize
|
||||
Eigen::HouseholderQR<Eigen::MatrixXcd> QR (Hess - evals[i] * Eigen::MatrixXcd::Identity(Nm, Nm));
|
||||
Qi = QR.householderQ();
|
||||
Q = Q * Qi;
|
||||
Hess = Qi.adjoint() * Hess * Qi;
|
||||
|
||||
std::cout << GridLogDebug << "Qt up to i = " << Q.transpose() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
std::cout << GridLogDebug << "Hess after all rotations: " << std::endl << Hess << std::endl;
|
||||
|
||||
// form Arnoldi vector f: f is normal to the basis vectors and its norm \beta is used to determine the Ritz estimate.
|
||||
std::complex<double> beta = Hess(Nk, Nk-1);
|
||||
std::complex<double> sigma = Q(Nm-1, Nk-1);
|
||||
f = basis[Nk] * beta + f * sigma;
|
||||
RealD betak = std::sqrt(norm2(f));
|
||||
std::cout << GridLogMessage << "|f|^2 after implicit restart = " << norm2(f) << std::endl;
|
||||
|
||||
// Rotate basis by Qt
|
||||
Qt = Q.transpose();
|
||||
basisRotate(basis, Qt, 0, Nk + 1, 0, Nm, Nm);
|
||||
|
||||
// rotate
|
||||
basisRotate(evecs, Qt, 0, Nk + 1, 0, Nm, Nm);
|
||||
|
||||
// Truncate the basis and restart
|
||||
basis = std::vector<Field> (basis.begin(), basis.begin() + Nk);
|
||||
// evecs = std::vector<Field> (evecs.begin(), evecs.begin() + Nk);
|
||||
Hess = Hess(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
|
||||
std::cout << "evecs size: " << evecs.size() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of Arnoldi eigenvectors that have converged. An eigenvector s is considered converged
|
||||
* for a tolerance epsilon if
|
||||
* r(s) := |\beta e_m^T s| < epsilon
|
||||
* where beta is the norm of f_{m+1}.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* int : Number of converged eigenvectors.
|
||||
*/
|
||||
int converged() {
|
||||
int Nconv = 0;
|
||||
for (int k = 0; k < evecs.size(); k++) {
|
||||
RealD emTs = std::abs(littleEvecs(Nm - 1, k)); // e_m^T s
|
||||
RealD ritzEstimate = beta_k * emTs;
|
||||
// TODO should be ritzEstimate < Tolerance * lambda_max
|
||||
std::cout << GridLogMessage << "Ritz estimate for evec " << k << " = " << ritzEstimate << std::endl;
|
||||
if (ritzEstimate < rtol) {
|
||||
Nconv++;
|
||||
}
|
||||
}
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -47,7 +47,7 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
public:
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
||||
// Defaults true.
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
@@ -77,7 +77,7 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
|
||||
// Initial residual computation & set up
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
Linop.Op(psi, v);
|
||||
b = norm2(v);
|
||||
@@ -122,12 +122,14 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
|
||||
LinearCombTimer.Start();
|
||||
bo = beta * omega;
|
||||
auto p_v = p.View();
|
||||
auto r_v = r.View();
|
||||
auto v_v = v.View();
|
||||
accelerator_for(ss, p_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(p_v[ss], beta*p_v(ss) - bo*v_v(ss) + r_v(ss));
|
||||
});
|
||||
{
|
||||
autoView( p_v , p, AcceleratorWrite);
|
||||
autoView( r_v , r, AcceleratorRead);
|
||||
autoView( v_v , v, AcceleratorRead);
|
||||
accelerator_for(ss, p_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(p_v[ss], beta*p_v(ss) - bo*v_v(ss) + r_v(ss));
|
||||
});
|
||||
}
|
||||
LinearCombTimer.Stop();
|
||||
LinalgTimer.Stop();
|
||||
|
||||
@@ -142,16 +144,20 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
alpha = rho / Calpha.real();
|
||||
|
||||
LinearCombTimer.Start();
|
||||
auto h_v = h.View();
|
||||
auto psi_v = psi.View();
|
||||
accelerator_for(ss, h_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(h_v[ss], alpha*p_v(ss) + psi_v(ss));
|
||||
});
|
||||
|
||||
auto s_v = s.View();
|
||||
accelerator_for(ss, s_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(s_v[ss], -alpha*v_v(ss) + r_v(ss));
|
||||
});
|
||||
{
|
||||
autoView( p_v , p, AcceleratorRead);
|
||||
autoView( r_v , r, AcceleratorRead);
|
||||
autoView( v_v , v, AcceleratorRead);
|
||||
autoView( psi_v,psi, AcceleratorRead);
|
||||
autoView( h_v , h, AcceleratorWrite);
|
||||
autoView( s_v , s, AcceleratorWrite);
|
||||
accelerator_for(ss, h_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(h_v[ss], alpha*p_v(ss) + psi_v(ss));
|
||||
});
|
||||
accelerator_for(ss, s_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(s_v[ss], -alpha*v_v(ss) + r_v(ss));
|
||||
});
|
||||
}
|
||||
LinearCombTimer.Stop();
|
||||
LinalgTimer.Stop();
|
||||
|
||||
@@ -166,13 +172,19 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
omega = Comega.real() / norm2(t);
|
||||
|
||||
LinearCombTimer.Start();
|
||||
auto t_v = t.View();
|
||||
accelerator_for(ss, psi_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(psi_v[ss], h_v(ss) + omega * s_v(ss));
|
||||
coalescedWrite(r_v[ss], -omega * t_v(ss) + s_v(ss));
|
||||
});
|
||||
{
|
||||
autoView( psi_v,psi, AcceleratorWrite);
|
||||
autoView( r_v , r, AcceleratorWrite);
|
||||
autoView( h_v , h, AcceleratorRead);
|
||||
autoView( s_v , s, AcceleratorRead);
|
||||
autoView( t_v , t, AcceleratorRead);
|
||||
accelerator_for(ss, psi_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(psi_v[ss], h_v(ss) + omega * s_v(ss));
|
||||
coalescedWrite(r_v[ss], -omega * t_v(ss) + s_v(ss));
|
||||
});
|
||||
}
|
||||
LinearCombTimer.Stop();
|
||||
|
||||
|
||||
cp = norm2(r);
|
||||
LinalgTimer.Stop();
|
||||
|
||||
@@ -202,7 +214,7 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
std::cout << GridLogMessage << "\tAxpyNorm " << AxpyNormTimer.Elapsed() << std::endl;
|
||||
std::cout << GridLogMessage << "\tLinearComb " << LinearCombTimer.Elapsed() << std::endl;
|
||||
|
||||
if(ErrorOnNoConverge){ assert(true_residual / Tolerance < 10000.0); }
|
||||
if(ErrorOnNoConverge){ GRID_ASSERT(true_residual / Tolerance < 10000.0); }
|
||||
|
||||
IterationsToComplete = k;
|
||||
|
||||
@@ -212,7 +224,7 @@ class BiCGSTAB : public OperatorFunction<Field>
|
||||
|
||||
std::cout << GridLogMessage << "BiCGSTAB did NOT converge" << std::endl;
|
||||
|
||||
if(ErrorOnNoConverge){ assert(0); }
|
||||
if(ErrorOnNoConverge){ GRID_ASSERT(0); }
|
||||
IterationsToComplete = k;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,7 +36,8 @@ NAMESPACE_BEGIN(Grid);
|
||||
template<class FieldD, class FieldF, typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0, typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||
class MixedPrecisionBiCGSTAB : public LinearFunction<FieldD>
|
||||
{
|
||||
public:
|
||||
public:
|
||||
using LinearFunction<FieldD>::operator();
|
||||
RealD Tolerance;
|
||||
RealD InnerTolerance; // Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
||||
Integer MaxInnerIterations;
|
||||
|
||||
@@ -31,6 +31,58 @@ directory
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class Field>
|
||||
void InnerProductMatrix(Eigen::MatrixXcd &m , const std::vector<Field> &X, const std::vector<Field> &Y){
|
||||
typedef typename Field::scalar_type scomplex;
|
||||
int Nblock = X.size();
|
||||
for(int b=0;b<Nblock;b++){
|
||||
for(int bp=0;bp<Nblock;bp++) {
|
||||
m(b,bp) = innerProduct(X[b],Y[bp]);
|
||||
}}
|
||||
}
|
||||
template<class Field>
|
||||
void MaddMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X,const std::vector<Field> &Y,RealD scale=1.0){
|
||||
// Should make this cache friendly with site outermost, parallel_for
|
||||
// Deal with case AP aliases with either Y or X
|
||||
//
|
||||
//Could pack "X" and "AP" into a Nblock x Volume dense array.
|
||||
// AP(Nrhs x vol) = Y(Nrhs x vol) + scale * m(nrhs x nrhs) * X(nrhs*vol)
|
||||
typedef typename Field::scalar_type scomplex;
|
||||
int Nblock = AP.size();
|
||||
std::vector<Field> tmp(Nblock,X[0]);
|
||||
for(int b=0;b<Nblock;b++){
|
||||
tmp[b] = Y[b];
|
||||
for(int bp=0;bp<Nblock;bp++) {
|
||||
tmp[b] = tmp[b] +scomplex(scale*m(bp,b))*X[bp];
|
||||
}
|
||||
}
|
||||
for(int b=0;b<Nblock;b++){
|
||||
AP[b] = tmp[b];
|
||||
}
|
||||
}
|
||||
template<class Field>
|
||||
void MulMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X){
|
||||
// Should make this cache friendly with site outermost, parallel_for
|
||||
typedef typename Field::scalar_type scomplex;
|
||||
int Nblock = AP.size();
|
||||
for(int b=0;b<Nblock;b++){
|
||||
AP[b] = Zero();
|
||||
for(int bp=0;bp<Nblock;bp++) {
|
||||
AP[b] += scomplex(m(bp,b))*X[bp];
|
||||
}
|
||||
}
|
||||
}
|
||||
template<class Field>
|
||||
double normv(const std::vector<Field> &P){
|
||||
int Nblock = P.size();
|
||||
double nn = 0.0;
|
||||
for(int b=0;b<Nblock;b++) {
|
||||
nn+=norm2(P[b]);
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
|
||||
enum BlockCGtype { BlockCG, BlockCGrQ, CGmultiRHS, BlockCGVec, BlockCGrQVec };
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -46,7 +98,7 @@ class BlockConjugateGradient : public OperatorFunction<Field> {
|
||||
int Nblock;
|
||||
|
||||
BlockCGtype CGtype;
|
||||
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
||||
// Defaults true.
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
@@ -87,10 +139,19 @@ void ThinQRfact (Eigen::MatrixXcd &m_rr,
|
||||
sliceInnerProductMatrix(m_rr,R,R,Orthog);
|
||||
|
||||
// Force manifest hermitian to avoid rounding related
|
||||
/*
|
||||
int rank=m_rr.rows();
|
||||
for(int r=0;r<rank;r++){
|
||||
for(int s=0;s<rank;s++){
|
||||
std::cout << "QR m_rr["<<r<<","<<s<<"] "<<m_rr(r,s)<<std::endl;
|
||||
}}
|
||||
*/
|
||||
m_rr = 0.5*(m_rr+m_rr.adjoint());
|
||||
|
||||
Eigen::MatrixXcd L = m_rr.llt().matrixL();
|
||||
|
||||
// ComplexD det = L.determinant();
|
||||
// std::cout << " Det m_rr "<<det<<std::endl;
|
||||
C = L.adjoint();
|
||||
Cinv = C.inverse();
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -110,11 +171,20 @@ void ThinQRfact (Eigen::MatrixXcd &m_rr,
|
||||
const std::vector<Field> & R)
|
||||
{
|
||||
InnerProductMatrix(m_rr,R,R);
|
||||
|
||||
/*
|
||||
int rank=m_rr.rows();
|
||||
for(int r=0;r<rank;r++){
|
||||
for(int s=0;s<rank;s++){
|
||||
std::cout << "QRvec m_rr["<<r<<","<<s<<"] "<<m_rr(r,s)<<std::endl;
|
||||
}}
|
||||
*/
|
||||
m_rr = 0.5*(m_rr+m_rr.adjoint());
|
||||
|
||||
Eigen::MatrixXcd L = m_rr.llt().matrixL();
|
||||
|
||||
// ComplexD det = L.determinant();
|
||||
// std::cout << " Det m_rr "<<det<<std::endl;
|
||||
|
||||
C = L.adjoint();
|
||||
Cinv = C.inverse();
|
||||
|
||||
@@ -131,7 +201,7 @@ void operator()(LinearOperatorBase<Field> &Linop, const Field &Src, Field &Psi)
|
||||
} else if (CGtype == CGmultiRHS ) {
|
||||
CGmultiRHSsolve(Linop,Src,Psi);
|
||||
} else {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
}
|
||||
virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Field> &Src, std::vector<Field> &Psi)
|
||||
@@ -139,7 +209,7 @@ virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Fiel
|
||||
if ( CGtype == BlockCGrQVec ) {
|
||||
BlockCGrQsolveVec(Linop,Src,Psi);
|
||||
} else {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,12 +256,13 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
||||
sliceNorm(ssq,B,Orthog);
|
||||
RealD sssum=0;
|
||||
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
||||
for(int b=0;b<Nblock;b++) std::cout << "src["<<b<<"]" << ssq[b] <<std::endl;
|
||||
|
||||
sliceNorm(residuals,B,Orthog);
|
||||
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
||||
|
||||
sliceNorm(residuals,X,Orthog);
|
||||
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
||||
|
||||
/************************************************************************
|
||||
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
|
||||
@@ -221,6 +292,9 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
||||
Linop.HermOp(X, AD);
|
||||
tmp = B - AD;
|
||||
|
||||
sliceNorm(residuals,tmp,Orthog);
|
||||
for(int b=0;b<Nblock;b++) std::cout << "res["<<b<<"]" << residuals[b] <<std::endl;
|
||||
|
||||
ThinQRfact (m_rr, m_C, m_Cinv, Q, tmp);
|
||||
D=Q;
|
||||
|
||||
@@ -236,6 +310,8 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
||||
GridStopWatch SolverTimer;
|
||||
SolverTimer.Start();
|
||||
|
||||
RealD max_resid=0;
|
||||
|
||||
int k;
|
||||
for (k = 1; k <= MaxIterations; k++) {
|
||||
|
||||
@@ -280,7 +356,7 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
||||
*/
|
||||
m_rr = m_C.adjoint() * m_C;
|
||||
|
||||
RealD max_resid=0;
|
||||
max_resid=0;
|
||||
RealD rrsum=0;
|
||||
RealD rr;
|
||||
|
||||
@@ -322,9 +398,11 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
||||
}
|
||||
|
||||
}
|
||||
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge "<<k<<" / "<<MaxIterations
|
||||
<<" residual "<< std::sqrt(max_resid)<< std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
||||
IterationsToComplete = k;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -360,10 +438,10 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
|
||||
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
||||
|
||||
sliceNorm(residuals,Src,Orthog);
|
||||
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
||||
|
||||
sliceNorm(residuals,Psi,Orthog);
|
||||
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
||||
|
||||
// Initial search dir is guess
|
||||
Linop.HermOp(Psi, AP);
|
||||
@@ -462,47 +540,10 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
|
||||
}
|
||||
std::cout << GridLogMessage << "MultiRHSConjugateGradient did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
||||
IterationsToComplete = k;
|
||||
}
|
||||
|
||||
void InnerProductMatrix(Eigen::MatrixXcd &m , const std::vector<Field> &X, const std::vector<Field> &Y){
|
||||
for(int b=0;b<Nblock;b++){
|
||||
for(int bp=0;bp<Nblock;bp++) {
|
||||
m(b,bp) = innerProduct(X[b],Y[bp]);
|
||||
}}
|
||||
}
|
||||
void MaddMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X,const std::vector<Field> &Y,RealD scale=1.0){
|
||||
// Should make this cache friendly with site outermost, parallel_for
|
||||
// Deal with case AP aliases with either Y or X
|
||||
std::vector<Field> tmp(Nblock,X[0]);
|
||||
for(int b=0;b<Nblock;b++){
|
||||
tmp[b] = Y[b];
|
||||
for(int bp=0;bp<Nblock;bp++) {
|
||||
tmp[b] = tmp[b] + scomplex(scale*m(bp,b))*X[bp];
|
||||
}
|
||||
}
|
||||
for(int b=0;b<Nblock;b++){
|
||||
AP[b] = tmp[b];
|
||||
}
|
||||
}
|
||||
void MulMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X){
|
||||
// Should make this cache friendly with site outermost, parallel_for
|
||||
for(int b=0;b<Nblock;b++){
|
||||
AP[b] = Zero();
|
||||
for(int bp=0;bp<Nblock;bp++) {
|
||||
AP[b] += scomplex(m(bp,b))*X[bp];
|
||||
}
|
||||
}
|
||||
}
|
||||
double normv(const std::vector<Field> &P){
|
||||
double nn = 0.0;
|
||||
for(int b=0;b<Nblock;b++) {
|
||||
nn+=norm2(P[b]);
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// BlockCGrQvec implementation:
|
||||
//--------------------------
|
||||
@@ -513,7 +554,7 @@ double normv(const std::vector<Field> &P){
|
||||
void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field> &B, std::vector<Field> &X)
|
||||
{
|
||||
Nblock = B.size();
|
||||
assert(Nblock == X.size());
|
||||
GRID_ASSERT(Nblock == X.size());
|
||||
|
||||
std::cout<<GridLogMessage<<" Block Conjugate Gradient Vec rQ : Nblock "<<Nblock<<std::endl;
|
||||
|
||||
@@ -549,13 +590,14 @@ void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field
|
||||
|
||||
RealD sssum=0;
|
||||
for(int b=0;b<Nblock;b++){ ssq[b] = norm2(B[b]);}
|
||||
for(int b=0;b<Nblock;b++){ std::cout << "ssq["<<b<<"] "<<ssq[b]<<std::endl;}
|
||||
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
||||
|
||||
for(int b=0;b<Nblock;b++){ residuals[b] = norm2(B[b]);}
|
||||
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
||||
|
||||
for(int b=0;b<Nblock;b++){ residuals[b] = norm2(X[b]);}
|
||||
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
||||
|
||||
/************************************************************************
|
||||
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
|
||||
@@ -585,6 +627,7 @@ void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field
|
||||
for(int b=0;b<Nblock;b++) {
|
||||
Linop.HermOp(X[b], AD[b]);
|
||||
tmp[b] = B[b] - AD[b];
|
||||
std::cout << "r0["<<b<<"] "<<norm2(tmp[b])<<std::endl;
|
||||
}
|
||||
|
||||
ThinQRfact (m_rr, m_C, m_Cinv, Q, tmp);
|
||||
@@ -688,7 +731,7 @@ void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field
|
||||
}
|
||||
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
||||
IterationsToComplete = k;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,878 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/BlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_BLOCKED_KRYLOV_SCHUR_H
|
||||
#define GRID_BLOCKED_KRYLOV_SCHUR_H
|
||||
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Block (block-Arnoldi) restarted Krylov-Schur eigensolver for
|
||||
* general non-Hermitian operators.
|
||||
*
|
||||
* Algorithm
|
||||
* ---------
|
||||
* Uses a block Arnoldi factorisation of block size Nblock:
|
||||
*
|
||||
* A V_k = V_k H_k + F_k B_k^dag
|
||||
*
|
||||
* where
|
||||
* V_k = Nm orthonormal basis vectors (stored flat in basis[])
|
||||
* H_k = Nm x Nm upper block-Hessenberg Rayleigh quotient
|
||||
* F_k = Nblock residual vectors (the next block beyond V_k)
|
||||
* B_k = Nm x Nblock coupling matrix (non-zero only in last Nblock rows)
|
||||
*
|
||||
* Each block Arnoldi step applies A to each of the Nblock vectors in the
|
||||
* current block, orthogonalises against all previous basis vectors, and
|
||||
* reduces the residual block to upper-triangular form via Householder QR
|
||||
* (implemented here as modified Gram-Schmidt within the block).
|
||||
*
|
||||
* The restart is a thick restart via the Schur decomposition of H_k:
|
||||
* H_k = Q^dag S Q
|
||||
* The leading Nk Schur vectors (chosen by RitzFilter) are retained,
|
||||
* the basis and Rayleigh quotient are truncated, and block Arnoldi continues
|
||||
* from the (Nk/Nblock)-th block.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Nblock : block size p
|
||||
* Nm : total Krylov dimension (must be divisible by Nblock)
|
||||
* Nk : total vectors to keep after each restart (must be divisible by Nblock, Nk < Nm)
|
||||
* Nstop : declare convergence when this many eigenpairs have converged
|
||||
* MaxIter : maximum number of outer (restart) iterations
|
||||
* Tolerance : relative convergence tolerance (||r|| < Tolerance * |lambda_max|)
|
||||
*/
|
||||
template<class Field>
|
||||
class BlockKrylovSchur {
|
||||
|
||||
protected:
|
||||
//--------------------------------------------------------------------
|
||||
// Types
|
||||
//--------------------------------------------------------------------
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Parameters (set by operator())
|
||||
//--------------------------------------------------------------------
|
||||
int Nblock; // block size
|
||||
int Nm; // total Krylov dimension (multiple of Nblock)
|
||||
int Nk; // total vectors retained after restart (multiple of Nblock)
|
||||
int Nstop;
|
||||
int MaxIter;
|
||||
RealD Tolerance;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Internal state
|
||||
//--------------------------------------------------------------------
|
||||
LinearOperatorBase<Field>& Linop;
|
||||
GridBase* Grid_;
|
||||
RitzFilter ritzFilter;
|
||||
|
||||
// Prefix used in log messages; derived classes override in their constructor
|
||||
// (e.g. "HarmonicBlockKrylovSchur") so the shared code reports the right name.
|
||||
std::string className = "BlockKrylovSchur";
|
||||
|
||||
// Flat storage: basis[s*Nblock + t] is the t-th vector of block s
|
||||
// After construction: basis has Nm entries
|
||||
std::vector<Field> basis;
|
||||
|
||||
// Rayleigh quotient Nm x Nm
|
||||
CMat H;
|
||||
|
||||
// Residual block: Nblock vectors (the (Nm/Nblock+1)-th block, unnormalised before
|
||||
// QR; normalised and orthogonalised as part of block Arnoldi)
|
||||
std::vector<Field> F;
|
||||
|
||||
// Coupling matrix B: Nm x Nblock.
|
||||
// In exact arithmetic only the last Nblock rows are non-zero:
|
||||
// B(Nm - Nblock + t, s) = H_{Nm/Nblock+1, Nm/Nblock}(t, s) (the subdiagonal block)
|
||||
// We keep it as a full matrix for generality after restarts.
|
||||
CMat B;
|
||||
|
||||
RealD beta_k; // Frobenius norm of the last subdiagonal block
|
||||
RealD rtol; // absolute tolerance = Tolerance * approxLambdaMax
|
||||
|
||||
// Output
|
||||
CVec evals;
|
||||
CMat littleEvecs; // Nm columns
|
||||
std::vector<RealD> ritzEstimates;
|
||||
|
||||
public:
|
||||
std::vector<Field> evecs;
|
||||
bool doEvalCheck = false;
|
||||
// When true (and Nblock even), only Nblock/2 starting vectors are required.
|
||||
// The remaining Nblock/2 slots are filled by pairing each supplied vector
|
||||
// with its parity-flipped partner (sign negated on odd checkerboard sites).
|
||||
bool useParityFlip = false;
|
||||
// Same pairing behaviour but the partner is produced by gamma5Func(v).
|
||||
// gamma5Func must be set by the caller before operator() is called.
|
||||
// It is a std::function so that BlockKrylovSchur.h does not need to know
|
||||
// about the Grid QCD Gamma class (which is defined at a later include layer).
|
||||
bool useGamma5 = false;
|
||||
std::function<void(const Field&, Field&)> gamma5Func;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------------------------------------
|
||||
BlockKrylovSchur(LinearOperatorBase<Field>& _Linop, GridBase* _Grid,
|
||||
RealD _Tolerance, RitzFilter _rf = EvalReSmall)
|
||||
: Linop(_Linop), Grid_(_Grid), Tolerance(_Tolerance), ritzFilter(_rf),
|
||||
Nblock(-1), Nm(-1), Nk(-1), Nstop(-1), MaxIter(-1),
|
||||
beta_k(0.0), rtol(0.0)
|
||||
{}
|
||||
|
||||
virtual ~BlockKrylovSchur() = default;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Run the blocked Krylov-Schur algorithm.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* v0 : block of Nblock starting vectors (size >= Nblock)
|
||||
* _maxIter : maximum outer (restart) iterations
|
||||
* _Nm : total Krylov dimension (must be divisible by _Nblock)
|
||||
* _Nk : total vectors to keep after restart (must be divisible by _Nblock, _Nk < _Nm)
|
||||
* _Nstop : stop after _Nstop eigenvalues converged
|
||||
* _Nblock : block size
|
||||
*/
|
||||
virtual void operator()(const std::vector<Field>& v0, int _maxIter, int _Nm, int _Nk,
|
||||
int _Nstop, int _Nblock = 1, bool doubleOrthog = true,
|
||||
bool doVerify = false)
|
||||
{
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm;
|
||||
Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
Nblock = _Nblock;
|
||||
|
||||
{
|
||||
int divisor = 1;
|
||||
if (useParityFlip) divisor *= 2;
|
||||
if (useGamma5) divisor *= 2;
|
||||
assert(Nblock % divisor == 0 && (int)v0.size() >= Nblock / divisor);
|
||||
std::cout << GridLogMessage << className << ": divisor = " << divisor << std::endl;
|
||||
}
|
||||
assert(Nm % Nblock == 0);
|
||||
assert(Nk % Nblock == 0);
|
||||
assert(Nk < Nm);
|
||||
if (useGamma5) assert(gamma5Func && "useGamma5: gamma5Func must be set");
|
||||
preRun(); // hook: derived classes add parameter assertions here
|
||||
|
||||
int N = Nm; // total Krylov dimension
|
||||
|
||||
// Approximate largest eigenvalue for tolerance normalisation
|
||||
RealD approxLambdaMax = approxMaxEval(v0[0]);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage << className << ": approx max eval = "
|
||||
<< approxLambdaMax << ", rtol = " << rtol << std::endl;
|
||||
|
||||
// Initialise
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
int start = 0;
|
||||
std::vector<Field> startBlock = expandStartBlock(v0);
|
||||
|
||||
for (int iter = 0; iter < MaxIter; iter++) {
|
||||
std::cout << GridLogMessage << className << ": restart iteration " << iter << std::endl;
|
||||
|
||||
// ---- Block Arnoldi: extend from block start to block Nm/Nblock ----
|
||||
blockArnoldiIteration(startBlock, Nm/Nblock, start, doubleOrthog);
|
||||
|
||||
// After first full cycle start from block Nk/Nblock
|
||||
start = Nk/Nblock;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after Arnoldi";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Restart rotation: compute Q and the rotated+reordered Rayleigh
|
||||
// quotient Hnew. Base class uses the plain Schur decomposition of
|
||||
// H; derived classes (e.g. HarmonicBlockKrylovSchur) override this
|
||||
// to target a shift.
|
||||
CMat Q, Hnew;
|
||||
restartRotation(Q, Hnew);
|
||||
CMat Qt = Q.adjoint();
|
||||
|
||||
// Rotate Krylov basis: basis_new[i] = sum_j basis[j] * Qt(j,i)
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, N);
|
||||
basis = basis2;
|
||||
|
||||
// Update B and H
|
||||
B = Q * B;
|
||||
H = Hnew;
|
||||
|
||||
// ---- Truncate to Nk ----
|
||||
int Nkeep = Nk;
|
||||
|
||||
CMat Htmp = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
H = CMat::Zero(N, N);
|
||||
H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep)) = Htmp;
|
||||
|
||||
std::vector<Field> basisTmp(basis.begin(), basis.begin() + Nkeep);
|
||||
basis = basisTmp;
|
||||
|
||||
CMat Btmp = B(Eigen::seqN(0, Nkeep), Eigen::all);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
B(Eigen::seqN(0, Nkeep), Eigen::all) = Btmp;
|
||||
|
||||
// beta_k = Frobenius norm of the effective coupling
|
||||
beta_k = Btmp.norm();
|
||||
std::cout << GridLogMessage << className << ": beta_k = " << beta_k << std::endl;
|
||||
|
||||
// Restart: the new starting block is F (the residual block from Arnoldi)
|
||||
startBlock = F;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after restart+truncation";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Compute eigensystem of truncated H for convergence check ----
|
||||
CMat Hk = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
computeEigensystem(Hk, Nkeep);
|
||||
|
||||
int Nconv = converged(Nkeep);
|
||||
std::cout << GridLogMessage << className << ": converged " << Nconv
|
||||
<< " / " << Nstop << std::endl;
|
||||
|
||||
if (Nconv >= Nstop || iter == MaxIter - 1) {
|
||||
std::cout << GridLogMessage << className << ": done after " << iter
|
||||
<< " restarts, " << Nconv << " converged." << std::endl;
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << evals << std::endl;
|
||||
|
||||
if (doEvalCheck) {
|
||||
Field w(Grid_);
|
||||
for (int k = 0; k < (int)evecs.size(); k++) {
|
||||
Linop.Op(evecs[k], w);
|
||||
ComplexD eval_est = toStdCmplx(innerProduct(evecs[k], w));
|
||||
w -= eval_est * evecs[k];
|
||||
RealD res = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage << className << ": evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
CVec getEvals() { return evals; }
|
||||
std::vector<RealD> getRitzEstimates() { return ritzEstimates; }
|
||||
|
||||
// Negate the field on all odd-parity (checkerboard) sites.
|
||||
// Uses pickCheckerboard/setCheckerboard so the SIMD layout is handled
|
||||
// correctly and the negation runs in parallel.
|
||||
static void parityFlippedField(const Field& v, Field& out) {
|
||||
GridCartesian* cgrid = dynamic_cast<GridCartesian*>(v.Grid());
|
||||
assert(cgrid != nullptr);
|
||||
GridRedBlackCartesian rbgrid(cgrid);
|
||||
Field veven(&rbgrid), vodd(&rbgrid);
|
||||
pickCheckerboard(Even, veven, v);
|
||||
pickCheckerboard(Odd, vodd, v);
|
||||
vodd = -vodd;
|
||||
setCheckerboard(out, veven);
|
||||
setCheckerboard(out, vodd);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Verification: print H and B, check A V = V H + F B^dag explicitly
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Checks the block Arnoldi / Krylov-Schur decomposition
|
||||
*
|
||||
* A V = V H + F B^dag (KS)
|
||||
*
|
||||
* by explicit operator applications. For each basis vector j:
|
||||
*
|
||||
* w_j = A basis[j]
|
||||
*
|
||||
* The nBasis × nBasis matrix M of inner products is computed:
|
||||
*
|
||||
* M[i, j] = <basis[i] | A basis[j]>
|
||||
*
|
||||
* and compared column-by-column against H. Separately, the nBasis × Nblock
|
||||
* residual coupling matrix R is computed:
|
||||
*
|
||||
* R[j, t] = <basis[j] | F[t]> * ||F[t]|| (scaled by F-block norms)
|
||||
*
|
||||
* but since F is already normalised, R[j,t] = <basis[j] | F[t]>.
|
||||
*
|
||||
* The KS relation for column j reads:
|
||||
* w_j = sum_i basis[i] H[i,j] + sum_t F[t] B[j,t]*
|
||||
* so the deviation in column j is
|
||||
* dev_j = w_j - sum_i basis[i] M[i,j] (should be zero for exact arithmetic)
|
||||
* augmented by the F B^dag term in the last block.
|
||||
*
|
||||
* Prints:
|
||||
* - H (current Rayleigh quotient, nBasis × nBasis)
|
||||
* - B (coupling matrix, nBasis × Nblock)
|
||||
* - M (explicit inner product matrix <V | A V>)
|
||||
* - max |H[i,j] - M[i,j]| (should be O(machine epsilon))
|
||||
* - for each basis column j: || A v_j - V H[:,j] - F B[j,:]^* ||
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* label : string printed at the start (e.g. "after restart 2")
|
||||
*/
|
||||
void verify(const std::string& label = "")
|
||||
{
|
||||
int nBasis = (int)basis.size();
|
||||
int nF = (int)F.size();
|
||||
|
||||
if (nBasis == 0) {
|
||||
std::cout << GridLogMessage
|
||||
<< className << "::verify [" << label
|
||||
<< "]: basis is empty." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Full allocated dimension of H (= Nm); may be larger than nBasis after restart+truncation
|
||||
int Nfull = (int)H.rows();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== " << className << "::verify [" << label << "] ========" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " nBasis = " << nBasis << " Nfull = " << Nfull
|
||||
<< " Nblock = " << Nblock << " nF = " << nF << std::endl;
|
||||
|
||||
// ---- Print H (full Nm x Nm matrix) ----
|
||||
// After restart+truncation nBasis < Nfull; entries outside [0:nBasis,0:nBasis]
|
||||
// should be ~0 — printing the full matrix makes this visible.
|
||||
std::cout << GridLogMessage << "H (" << Nfull << " x " << Nfull << "):" << std::endl;
|
||||
for (int i = 0; i < Nfull; i++) {
|
||||
for (int j = 0; j < Nfull; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << H(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Print B (full Nm x Nblock matrix) ----
|
||||
std::cout << GridLogMessage << "B (" << Nfull << " x " << nF << "):" << std::endl;
|
||||
for (int i = 0; i < Nfull; i++) {
|
||||
for (int t = 0; t < nF; t++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << B(i, t);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Compute M[i,j] = <basis[i] | A basis[j]> ----
|
||||
CMat M = CMat::Zero(nBasis, nBasis);
|
||||
Field w(Grid_);
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
M(i, j) = toStdCmplx(innerProduct(basis[i], w));
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "M = <V|AV> (" << nBasis << " x " << nBasis << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << M(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- max |H - M| (for the nBasis x nBasis Rayleigh-quotient block) ----
|
||||
RealD maxHM = 0.0;
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
maxHM = std::max(maxHM, std::abs(H(i,j) - M(i,j)));
|
||||
std::cout << GridLogMessage
|
||||
<< " max |H[i,j] - M[i,j]| (i,j < nBasis) = " << maxHM << std::endl;
|
||||
|
||||
// ---- Check entries of H OUTSIDE the nBasis x nBasis block ----
|
||||
// These live in rows i >= nBasis or columns j >= nBasis.
|
||||
// They must be ~0 after restart+truncation (before the coupling rows are
|
||||
// re-filled by the next blockArnoldiIteration call).
|
||||
// During a full Arnoldi run (nBasis == Nfull) this loop is a no-op.
|
||||
RealD maxOutside = 0.0;
|
||||
for (int i = 0; i < Nfull; i++)
|
||||
for (int j = 0; j < Nfull; j++)
|
||||
if (i >= nBasis || j >= nBasis)
|
||||
maxOutside = std::max(maxOutside, std::abs(H(i,j)));
|
||||
if (Nfull > nBasis)
|
||||
std::cout << GridLogMessage
|
||||
<< " max |H[i,j]| outside [0:" << nBasis << ",0:" << nBasis
|
||||
<< "] block = " << maxOutside
|
||||
<< " (should be ~0 after restart+truncation)" << std::endl;
|
||||
|
||||
// ---- Check orthonormality of basis ----
|
||||
CMat G = CMat::Zero(nBasis, nBasis);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
G(i, j) = toStdCmplx(innerProduct(basis[i], basis[j]));
|
||||
CMat Gerr = G - CMat::Identity(nBasis, nBasis);
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i|V_j> - delta_ij| = " << Gerr.cwiseAbs().maxCoeff() << std::endl;
|
||||
|
||||
// ---- Per-column residual: || A v_j - V H[:,j] - F B[j,:]^* || ----
|
||||
// For each basis vector j, compute A v_j then subtract V H[:,j] and F B[j,:]^*
|
||||
RealD maxColDev = 0.0;
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
|
||||
// subtract V H[:,j]
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
w -= basis[i] * H(i, j);
|
||||
|
||||
// subtract F B[j,:]^* (F[t] * conj(B[j,t]))
|
||||
for (int t = 0; t < nF; t++)
|
||||
w -= F[t] * std::conj(B(j, t));
|
||||
|
||||
RealD dev = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage
|
||||
<< " || A v[" << j << "] - V H[:,j] - F B[j,:]* || = " << dev << std::endl;
|
||||
maxColDev = std::max(maxColDev, dev);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max column deviation = " << maxColDev << std::endl;
|
||||
|
||||
// ---- Check F block orthogonality against basis ----
|
||||
if (nF > 0) {
|
||||
RealD maxFV = 0.0;
|
||||
for (int t = 0; t < nF; t++)
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
RealD ip = std::abs(toStdCmplx(innerProduct(basis[i], F[t])));
|
||||
maxFV = std::max(maxFV, ip);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i | F_t>| (should be ~0) = " << maxFV << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== end verify ========" << std::endl;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Extension hooks for derived classes
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Apply the operator (or poly-filtered operator) to the current Arnoldi
|
||||
* block and return the results in W.
|
||||
*
|
||||
* Base implementation: W[t] = Linop.Op(basis[kBase + t]).
|
||||
* Derived classes (e.g. SplitGridBlockKrylovSchur) override this to
|
||||
* apply poly(A) via Grid_split / Grid_unsplit instead.
|
||||
*/
|
||||
virtual void applyBlock(std::vector<Field>& W, int kBase)
|
||||
{
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
Linop.Op(basis[kBase + t], W[t]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once at the start of operator(), after parameters are set.
|
||||
* Base implementation is a no-op; derived classes override to add
|
||||
* parameter assertions (e.g. Nblock % mrhs == 0).
|
||||
*/
|
||||
virtual void preRun() {}
|
||||
|
||||
/**
|
||||
* Compute the restart rotation for the current Rayleigh quotient H.
|
||||
*
|
||||
* Returns Q (unitary) and Hnew = Q H Q^dag reordered so that the wanted
|
||||
* Nk eigenvalues occupy the leading block, ready for truncation.
|
||||
*
|
||||
* Base implementation: the plain Schur decomposition of H, reordered by
|
||||
* ritzFilter. HarmonicBlockKrylovSchur overrides this to Schur-decompose
|
||||
* the shifted quotient (H - sigma I) so that eigenvalues nearest sigma are
|
||||
* selected.
|
||||
*/
|
||||
virtual void restartRotation(CMat& Q, CMat& Hnew)
|
||||
{
|
||||
ComplexSchurDecomposition schur(H, false, ritzFilter);
|
||||
std::cout << GridLogMessage << className << ": Schur decomposed." << std::endl;
|
||||
schur.schurReorder(Nk);
|
||||
std::cout << GridLogMessage << className << ": Schur reordered." << std::endl;
|
||||
Q = schur.getMatrixQ();
|
||||
Hnew = schur.getMatrixS();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Starting-block expansion (parity flip / gamma5 partners)
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Expand Nblock/divisor seed vectors into the full Nblock starting block by
|
||||
* pairing each seed with its parity-flipped and/or gamma5 partners. Order:
|
||||
* {v, flip(v)} then doubled by {., g5(.)} giving {v, flip(v), g5(v),
|
||||
* g5(flip(v))} when both flags are set. Requires Nblock, Grid_ and the
|
||||
* flags to be set.
|
||||
*/
|
||||
std::vector<Field> expandStartBlock(const std::vector<Field>& v0)
|
||||
{
|
||||
int divisor = (useParityFlip ? 2 : 1) * (useGamma5 ? 2 : 1);
|
||||
std::vector<Field> startBlock;
|
||||
startBlock.reserve(Nblock);
|
||||
for (int i = 0; i < Nblock / divisor; i++) {
|
||||
std::vector<Field> group;
|
||||
group.push_back(v0[i]);
|
||||
if (useParityFlip) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field fp(Grid_);
|
||||
parityFlippedField(group[j], fp);
|
||||
group.push_back(std::move(fp));
|
||||
}
|
||||
}
|
||||
if (useGamma5) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field g5v(Grid_);
|
||||
gamma5Func(group[j], g5v);
|
||||
group.push_back(std::move(g5v));
|
||||
}
|
||||
}
|
||||
for (auto& f : group) startBlock.push_back(std::move(f));
|
||||
}
|
||||
return startBlock;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block Arnoldi iteration
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Extends the block Arnoldi factorisation from block index 'start' to
|
||||
* block index 'Nm'.
|
||||
*
|
||||
* On entry (start > 0): basis[0..start*Nblock-1] already set,
|
||||
* H[0..start*Nblock-1, 0..start*Nblock-1] already set,
|
||||
* B[start*Nblock-1, :] set (coupling from prior residual block).
|
||||
* startBlock = the normalised residual block F from the previous cycle.
|
||||
*
|
||||
* On entry (start == 0): initialises everything from startBlock.
|
||||
*/
|
||||
void blockArnoldiIteration(std::vector<Field>& startBlock, int endBlock,
|
||||
int startIdx, bool doubleOrthog)
|
||||
{
|
||||
int N = Nm;
|
||||
|
||||
if (startIdx == 0) {
|
||||
basis.clear();
|
||||
F.clear();
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
// Orthonormalise starting block via modified Gram-Schmidt
|
||||
std::vector<Field> V0 = startBlock;
|
||||
blockOrthonormalise(V0);
|
||||
for (auto& v : V0) basis.push_back(v);
|
||||
} else {
|
||||
// Append residual block (startBlock = F_old) to basis.
|
||||
// The truncated KS relation after restart is:
|
||||
//
|
||||
// A V_k = V_k S_k + F_old B_old^dag (*)
|
||||
//
|
||||
// where V_k = basis[0:Nkeep], S_k is stored in H[0:Nkeep,0:Nkeep],
|
||||
// B_old = B[0:Nkeep,:], F_old = startBlock.
|
||||
//
|
||||
// Once F_old is appended as basis[Nkeep:Nkeep+Nblock], (*) becomes
|
||||
// a statement about the extended H matrix:
|
||||
//
|
||||
// H[Nkeep+t, j] = (B_old^dag)[t,j] = conj(B_old[j,t])
|
||||
// for t=0..Nblock-1, j=0..Nkeep-1
|
||||
//
|
||||
// These entries are the "restart coupling rows" that connect the new
|
||||
// block to all retained Schur vectors and must be set before Arnoldi
|
||||
// continues, otherwise A V_k = V_k H[:,0:Nkeep] would be missing the
|
||||
// F_old B_old^dag term for those columns.
|
||||
|
||||
int Nkeep = startIdx * Nblock;
|
||||
for (auto& v : startBlock) basis.push_back(v);
|
||||
|
||||
// Fill restart coupling rows into H
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
H(Nkeep + t, j) = std::conj(B(j, t));
|
||||
|
||||
// Zero out B for the retained columns now that the coupling is in H
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
B(j, t) = 0.0;
|
||||
}
|
||||
|
||||
// Main block Arnoldi loop
|
||||
for (int k = startIdx; k < endBlock; k++) {
|
||||
blockArnoldiStep(k, doubleOrthog);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* One block Arnoldi step: extends by one block (Nblock vectors).
|
||||
*
|
||||
* Computes block column k of H and the next basis block V_{k+1}.
|
||||
*
|
||||
* Layout of basis (flat):
|
||||
* basis[j*Nblock + t] = t-th vector of j-th block, j = 0..k
|
||||
*
|
||||
* After this call:
|
||||
* H[i, k*Nblock : (k+1)*Nblock] filled for i = 0..(k+1)*Nblock - 1
|
||||
* basis[k*Nblock .. (k+1)*Nblock - 1] normalised (already set on entry)
|
||||
* F = residual block (to become V_{k+1} after this step if k < Nm-1)
|
||||
*
|
||||
* If k < Nm-1, also:
|
||||
* H[(k+1)*Nblock : (k+2)*Nblock, k*Nblock : (k+1)*Nblock] = subdiag block (from QR of residual)
|
||||
* basis extended by Nblock (the normalised residual vectors)
|
||||
*/
|
||||
void blockArnoldiStep(int k, bool doubleOrthog)
|
||||
{
|
||||
int kBase = k * Nblock; // first flat index of current block
|
||||
int prevN = kBase + Nblock; // number of basis vectors so far after this step
|
||||
int N = Nm;
|
||||
|
||||
// W[t] = op(basis[kBase + t]) — dispatches to applyBlock() virtual
|
||||
std::vector<Field> W(Nblock, Field(Grid_));
|
||||
applyBlock(W, kBase);
|
||||
|
||||
// Orthogonalise W against all current basis vectors (full reorthogonalisation)
|
||||
// H[i, kBase + t] = <basis[i] | W[t]>
|
||||
for (int pass = 0; pass < (doubleOrthog ? 2 : 1); pass++) {
|
||||
for (int i = 0; i < prevN; i++) {
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
ComplexD coeff = innerProduct(basis[i], W[t]);
|
||||
if (pass == 0)
|
||||
H(i, kBase + t) = toStdCmplx(coeff);
|
||||
else
|
||||
H(i, kBase + t) += toStdCmplx(coeff);
|
||||
W[t] -= coeff * basis[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store residual block F
|
||||
F = W;
|
||||
|
||||
if (k == Nm/Nblock - 1) {
|
||||
// Last block: compute coupling matrix B for KS decomp.
|
||||
//
|
||||
// blockQR modifies F in-place (F → Q orthonormal) and returns R
|
||||
// such that W_orig[t] = sum_s F[s] * R[s,t] (W_orig = F_after * R).
|
||||
//
|
||||
// The KS relation for column j = kBase+t requires the coefficient of F[s]
|
||||
// to be (B†)[s,j] = conj(B[j,s]). Matching with R[s,t]:
|
||||
// conj(B[kBase+t, s]) = R[s,t] → B[kBase+t, s] = conj(R[s,t])
|
||||
//
|
||||
// Equivalently the last Nblock rows of B are R^H (Hermitian conjugate of R).
|
||||
// Note: for Nblock=1, R is scalar real positive, so this reduces to B = R. ✓
|
||||
CMat R = blockQR(F); // F is modified in-place to become Q; returns R
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int s = 0; s < Nblock; s++)
|
||||
B(kBase + t, s) = std::conj(R(s, t)); // B_block = R^H
|
||||
|
||||
beta_k = R.norm();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not last block: QR-decompose residual to get V_{k+1}
|
||||
CMat R = blockQR(F); // F orthonormalised in-place, R is upper triangular
|
||||
|
||||
// Subdiagonal block of H: H[(k+1)*Nblock : (k+2)*Nblock, kBase : kBase+Nblock] = R
|
||||
int nextBase = (k + 1) * Nblock;
|
||||
for (int i = 0; i < Nblock; i++)
|
||||
for (int j = 0; j < Nblock; j++)
|
||||
H(nextBase + i, kBase + j) = R(i, j);
|
||||
|
||||
// Append normalised residual block to basis
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
basis.push_back(F[t]);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block QR via modified Gram-Schmidt within the block
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Given a block of Nblock vectors W (not necessarily orthonormal),
|
||||
* orthonormalises them in-place and returns the upper-triangular R
|
||||
* such that W_in = W_out * R.
|
||||
*
|
||||
* Handles (near-)linear dependence by zeroing vectors below threshold.
|
||||
*/
|
||||
CMat blockQR(std::vector<Field>& W)
|
||||
{
|
||||
CMat R = CMat::Zero(Nblock, Nblock);
|
||||
const RealD deflThresh = 1e-14;
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
// Orthogonalise W[j] against W[0..j-1]
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD coeff = innerProduct(W[i], W[j]);
|
||||
R(i, j) = toStdCmplx(coeff);
|
||||
W[j] -= coeff * W[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(W[j]));
|
||||
R(j, j) = nrm;
|
||||
if (nrm > deflThresh) {
|
||||
W[j] *= (1.0 / nrm);
|
||||
} else {
|
||||
// deflation: zero this vector
|
||||
W[j] = Zero();
|
||||
std::cout << GridLogMessage
|
||||
<< className << ": deflation at block column " << j
|
||||
<< " (norm = " << nrm << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Orthonormalise a block against itself (no prior basis)
|
||||
//--------------------------------------------------------------------
|
||||
void blockOrthonormalise(std::vector<Field>& V)
|
||||
{
|
||||
for (int j = 0; j < (int)V.size(); j++) {
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD c = innerProduct(V[i], V[j]);
|
||||
V[j] -= c * V[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(V[j]));
|
||||
assert(nrm > 1e-14);
|
||||
V[j] *= (1.0 / nrm);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Basis rotation: UR[i] = sum_j U[j] * R(j, i)
|
||||
//--------------------------------------------------------------------
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field>& U,
|
||||
CMat& R, int N)
|
||||
{
|
||||
UR.clear();
|
||||
Field tmp(Grid_);
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < N; j++)
|
||||
tmp += U[j] * R(j, i);
|
||||
UR.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Eigensystem of the truncated Rayleigh quotient
|
||||
//--------------------------------------------------------------------
|
||||
void computeEigensystem(CMat& Hk, int Nkeep)
|
||||
{
|
||||
Eigen::ComplexEigenSolver<CMat> es;
|
||||
es.compute(Hk);
|
||||
|
||||
// Sort to match schurReorder ordering.
|
||||
int n = es.eigenvalues().size();
|
||||
ComplexComparator cComp(ritzFilter);
|
||||
std::vector<int> idx(n);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(es.eigenvalues()(a)), toStdCmplx(es.eigenvalues()(b)));
|
||||
});
|
||||
|
||||
evals.resize(n);
|
||||
littleEvecs.resize(n, n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
evals(k) = es.eigenvalues()(idx[k]);
|
||||
littleEvecs.col(k) = es.eigenvectors().col(idx[k]);
|
||||
}
|
||||
|
||||
evecs.clear();
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec vec = littleEvecs.col(k);
|
||||
Field tmp(Grid_);
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)basis.size() && j < Nkeep; j++)
|
||||
tmp += vec[j] * basis[j];
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Convergence check
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* An eigenpair (lambda, y) is converged if the Ritz estimate
|
||||
* r = || B^dag y ||
|
||||
* satisfies r < rtol. Here B is the (Nkeep x Nblock) coupling matrix
|
||||
* and y is the little eigenvector (Nkeep-vector) of H.
|
||||
*/
|
||||
int converged(int Nkeep)
|
||||
{
|
||||
ritzEstimates.clear();
|
||||
int Nconv = 0;
|
||||
|
||||
CMat Bk = B(Eigen::seqN(0, Nkeep), Eigen::all); // Nkeep x Nblock
|
||||
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec yk = littleEvecs.col(k); // Nkeep-vector
|
||||
CVec Bty = Bk.adjoint() * yk; // Nblock-vector
|
||||
RealD res = Bty.norm();
|
||||
ritzEstimates.push_back(res);
|
||||
std::cout << GridLogMessage << className << ": Ritz estimate[" << k
|
||||
<< "] = " << res << " eval = " << evals[k] << std::endl;
|
||||
if (res < rtol) Nconv++;
|
||||
else break;
|
||||
}
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Approximate maximum eigenvalue via power iteration
|
||||
//--------------------------------------------------------------------
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50)
|
||||
{
|
||||
assert(norm2(v0) > 1e-8);
|
||||
RealD lam = 0.0, denom = std::sqrt(norm2(v0));
|
||||
Field vcur(Grid_), vtmp(Grid_);
|
||||
vcur = v0;
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(vcur, vtmp);
|
||||
vcur = vtmp;
|
||||
RealD num = std::sqrt(norm2(vcur));
|
||||
lam = num / denom;
|
||||
denom = num;
|
||||
}
|
||||
return lam;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_BLOCKED_KRYLOV_SCHUR_H
|
||||
@@ -36,7 +36,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
|
||||
public:
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // Throw an assert when CAGMRES fails to converge,
|
||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when CAGMRES fails to converge,
|
||||
// defaults to true
|
||||
|
||||
RealD Tolerance;
|
||||
@@ -82,7 +82,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
|
||||
conformable(psi, src);
|
||||
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
RealD cp;
|
||||
RealD ssq = norm2(src);
|
||||
@@ -137,7 +137,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
|
||||
std::cout << GridLogMessage << "CommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
||||
@@ -185,7 +185,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
|
||||
}
|
||||
}
|
||||
|
||||
assert(0); // Never reached
|
||||
GRID_ASSERT(0); // Never reached
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,13 +38,14 @@ NAMESPACE_BEGIN(Grid);
|
||||
// single input vec, single output vec.
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template <class Field>
|
||||
class ConjugateGradient : public OperatorFunction<Field> {
|
||||
public:
|
||||
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||
|
||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
||||
// Defaults true.
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
@@ -54,10 +55,26 @@ public:
|
||||
ConjugateGradient(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
||||
: Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
ErrorOnNoConverge(err_on_no_conv){};
|
||||
ErrorOnNoConverge(err_on_no_conv)
|
||||
{};
|
||||
|
||||
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
||||
virtual void LogIteration(int k,RealD a,RealD b){
|
||||
// std::cout << "ConjugageGradient::LogIteration() "<<std::endl;
|
||||
};
|
||||
virtual void LogBegin(void){
|
||||
std::cout << "ConjugageGradient::LogBegin() "<<std::endl;
|
||||
};
|
||||
|
||||
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
||||
|
||||
this->LogBegin();
|
||||
|
||||
GRID_TRACE("ConjugateGradient");
|
||||
GridStopWatch PreambleTimer;
|
||||
GridStopWatch ConstructTimer;
|
||||
GridStopWatch NormTimer;
|
||||
GridStopWatch AssignTimer;
|
||||
PreambleTimer.Start();
|
||||
psi.Checkerboard() = src.Checkerboard();
|
||||
|
||||
conformable(psi, src);
|
||||
@@ -65,22 +82,32 @@ public:
|
||||
RealD cp, c, a, d, b, ssq, qq;
|
||||
//RealD b_pred;
|
||||
|
||||
Field p(src);
|
||||
Field mmp(src);
|
||||
Field r(src);
|
||||
// Was doing copies
|
||||
ConstructTimer.Start();
|
||||
Field p (src.Grid());
|
||||
Field mmp(src.Grid());
|
||||
Field r (src.Grid());
|
||||
ConstructTimer.Stop();
|
||||
|
||||
// Initial residual computation & set up
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
|
||||
Linop.HermOpAndNorm(psi, mmp, d, b);
|
||||
|
||||
r = src - mmp;
|
||||
p = r;
|
||||
|
||||
a = norm2(p);
|
||||
cp = a;
|
||||
NormTimer.Start();
|
||||
ssq = norm2(src);
|
||||
RealD guess = norm2(psi);
|
||||
NormTimer.Stop();
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
AssignTimer.Start();
|
||||
if ( guess == 0.0 ) {
|
||||
r = src;
|
||||
p = r;
|
||||
a = ssq;
|
||||
} else {
|
||||
Linop.HermOpAndNorm(psi, mmp, d, b);
|
||||
r = src - mmp;
|
||||
p = r;
|
||||
a = norm2(p);
|
||||
}
|
||||
cp = a;
|
||||
AssignTimer.Stop();
|
||||
|
||||
// Handle trivial case of zero src
|
||||
if (ssq == 0.){
|
||||
@@ -110,6 +137,7 @@ public:
|
||||
std::cout << GridLogIterative << std::setprecision(8)
|
||||
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
||||
|
||||
PreambleTimer.Stop();
|
||||
GridStopWatch LinalgTimer;
|
||||
GridStopWatch InnerTimer;
|
||||
GridStopWatch AxpyNormTimer;
|
||||
@@ -117,9 +145,13 @@ public:
|
||||
GridStopWatch MatrixTimer;
|
||||
GridStopWatch SolverTimer;
|
||||
|
||||
RealD usecs = -usecond();
|
||||
SolverTimer.Start();
|
||||
int k;
|
||||
for (k = 1; k <= MaxIterations; k++) {
|
||||
|
||||
GridStopWatch IterationTimer;
|
||||
IterationTimer.Start();
|
||||
c = cp;
|
||||
|
||||
MatrixTimer.Start();
|
||||
@@ -140,43 +172,57 @@ public:
|
||||
b = cp / c;
|
||||
|
||||
LinearCombTimer.Start();
|
||||
auto psi_v = psi.View();
|
||||
auto p_v = p.View();
|
||||
auto r_v = r.View();
|
||||
accelerator_for(ss,p_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(psi_v[ss], a * p_v(ss) + psi_v(ss));
|
||||
coalescedWrite(p_v[ss] , b * p_v(ss) + r_v (ss));
|
||||
});
|
||||
{
|
||||
autoView( psi_v , psi, AcceleratorWrite);
|
||||
autoView( p_v , p, AcceleratorWrite);
|
||||
autoView( r_v , r, AcceleratorWrite);
|
||||
accelerator_for(ss,p_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(psi_v[ss], a * p_v(ss) + psi_v(ss));
|
||||
coalescedWrite(p_v[ss] , b * p_v(ss) + r_v (ss));
|
||||
});
|
||||
}
|
||||
LinearCombTimer.Stop();
|
||||
LinalgTimer.Stop();
|
||||
LogIteration(k,a,b);
|
||||
|
||||
std::cout << GridLogIterative << "ConjugateGradient: Iteration " << k
|
||||
IterationTimer.Stop();
|
||||
if ( (k % 500) == 0 ) {
|
||||
std::cout << GridLogMessage << "ConjugateGradient: Iteration " << k
|
||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << std::endl;
|
||||
} else {
|
||||
std::cout << GridLogIterative << "ConjugateGradient: Iteration " << k
|
||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << " took " << IterationTimer.Elapsed() << std::endl;
|
||||
}
|
||||
|
||||
// Stopping condition
|
||||
if (cp <= rsq) {
|
||||
usecs +=usecond();
|
||||
SolverTimer.Stop();
|
||||
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
p = mmp - src;
|
||||
|
||||
GridBase *grid = src.Grid();
|
||||
RealD DwfFlops = (1452. )*grid->gSites()*4*k
|
||||
+ (8+4+8+4+4)*12*grid->gSites()*k; // CG linear algebra
|
||||
RealD srcnorm = std::sqrt(norm2(src));
|
||||
RealD resnorm = std::sqrt(norm2(p));
|
||||
RealD true_residual = resnorm / srcnorm;
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradient Converged on iteration " << k
|
||||
<< "\tComputed residual " << std::sqrt(cp / ssq)
|
||||
<< "\tTrue residual " << true_residual
|
||||
<< "\tTarget " << Tolerance << std::endl;
|
||||
|
||||
std::cout << GridLogIterative << "Time breakdown "<<std::endl;
|
||||
std::cout << GridLogIterative << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogIterative << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogIterative << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogIterative << "\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogIterative << "\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogIterative << "\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver Elapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "Time breakdown "<<std::endl;
|
||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
||||
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) GRID_ASSERT(true_residual / Tolerance < 10000.0);
|
||||
|
||||
IterationsToComplete = k;
|
||||
TrueResidual = true_residual;
|
||||
@@ -185,17 +231,143 @@ public:
|
||||
}
|
||||
}
|
||||
// Failed. Calculate true residual before giving up
|
||||
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
p = mmp - src;
|
||||
// Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
// p = mmp - src;
|
||||
//TrueResidual = sqrt(norm2(p)/ssq);
|
||||
// TrueResidual = 1;
|
||||
|
||||
TrueResidual = sqrt(norm2(p)/ssq);
|
||||
std::cout << GridLogMessage << "ConjugateGradient did NOT converge "<<k<<" / "<< MaxIterations
|
||||
<<" residual "<< std::sqrt(cp / ssq)<< std::endl;
|
||||
SolverTimer.Stop();
|
||||
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "Solver breakdown "<<std::endl;
|
||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage<< "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradient did NOT converge "<<k<<" / "<< MaxIterations<< std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
||||
IterationsToComplete = k;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class Field>
|
||||
class ConjugateGradientPolynomial : public ConjugateGradient<Field> {
|
||||
public:
|
||||
// Optionally record the CG polynomial
|
||||
std::vector<double> ak;
|
||||
std::vector<double> bk;
|
||||
std::vector<double> poly_p;
|
||||
std::vector<double> poly_r;
|
||||
std::vector<double> poly_Ap;
|
||||
std::vector<double> polynomial;
|
||||
|
||||
public:
|
||||
ConjugateGradientPolynomial(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
||||
: ConjugateGradient<Field>(tol,maxit,err_on_no_conv)
|
||||
{ };
|
||||
void PolyHermOp(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi)
|
||||
{
|
||||
Field tmp(src.Grid());
|
||||
Field AtoN(src.Grid());
|
||||
AtoN = src;
|
||||
psi=AtoN*polynomial[0];
|
||||
for(int n=1;n<polynomial.size();n++){
|
||||
tmp = AtoN;
|
||||
Linop.HermOp(tmp,AtoN);
|
||||
psi = psi + polynomial[n]*AtoN;
|
||||
}
|
||||
}
|
||||
void CGsequenceHermOp(LinearOperatorBase<Field> &Linop, const Field &src, Field &x)
|
||||
{
|
||||
Field Ap(src.Grid());
|
||||
Field r(src.Grid());
|
||||
Field p(src.Grid());
|
||||
p=src;
|
||||
r=src;
|
||||
x=Zero();
|
||||
x.Checkerboard()=src.Checkerboard();
|
||||
for(int k=0;k<ak.size();k++){
|
||||
x = x + ak[k]*p;
|
||||
Linop.HermOp(p,Ap);
|
||||
r = r - ak[k] * Ap;
|
||||
p = r + bk[k] * p;
|
||||
}
|
||||
}
|
||||
void Solve(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi)
|
||||
{
|
||||
psi=Zero();
|
||||
this->operator ()(Linop,src,psi);
|
||||
}
|
||||
virtual void LogBegin(void)
|
||||
{
|
||||
std::cout << "ConjugageGradientPolynomial::LogBegin() "<<std::endl;
|
||||
ak.resize(0);
|
||||
bk.resize(0);
|
||||
polynomial.resize(0);
|
||||
poly_Ap.resize(0);
|
||||
poly_Ap.resize(0);
|
||||
poly_p.resize(1);
|
||||
poly_r.resize(1);
|
||||
poly_p[0]=1.0;
|
||||
poly_r[0]=1.0;
|
||||
};
|
||||
virtual void LogIteration(int k,RealD a,RealD b)
|
||||
{
|
||||
// With zero guess,
|
||||
// p = r = src
|
||||
//
|
||||
// iterate:
|
||||
// x = x + a p
|
||||
// r = r - a A p
|
||||
// p = r + b p
|
||||
//
|
||||
// [0]
|
||||
// r = x
|
||||
// p = x
|
||||
// Ap=0
|
||||
//
|
||||
// [1]
|
||||
// Ap = A x + 0 ==> shift poly P right by 1 and add 0.
|
||||
// x = x + a p ==> add polynomials term by term
|
||||
// r = r - a A p ==> add polynomials term by term
|
||||
// p = r + b p ==> add polynomials term by term
|
||||
//
|
||||
std::cout << "ConjugageGradientPolynomial::LogIteration() "<<k<<std::endl;
|
||||
ak.push_back(a);
|
||||
bk.push_back(b);
|
||||
// Ap= right_shift(p)
|
||||
poly_Ap.resize(k+1);
|
||||
poly_Ap[0]=0.0;
|
||||
for(int i=0;i<k;i++){
|
||||
poly_Ap[i+1]=poly_p[i];
|
||||
}
|
||||
|
||||
// x = x + a p
|
||||
polynomial.resize(k);
|
||||
polynomial[k-1]=0.0;
|
||||
for(int i=0;i<k;i++){
|
||||
polynomial[i] = polynomial[i] + a * poly_p[i];
|
||||
}
|
||||
|
||||
// r = r - a Ap
|
||||
// p = r + b p
|
||||
poly_r.resize(k+1);
|
||||
poly_p.resize(k+1);
|
||||
poly_r[k] = poly_p[k] = 0.0;
|
||||
for(int i=0;i<k+1;i++){
|
||||
poly_r[i] = poly_r[i] - a * poly_Ap[i];
|
||||
poly_p[i] = poly_r[i] + b * poly_p[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
|
||||
@@ -35,7 +35,8 @@ NAMESPACE_BEGIN(Grid);
|
||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||
class MixedPrecisionConjugateGradient : public LinearFunction<FieldD> {
|
||||
public:
|
||||
public:
|
||||
using LinearFunction<FieldD>::operator();
|
||||
RealD Tolerance;
|
||||
RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
||||
Integer MaxInnerIterations;
|
||||
@@ -48,6 +49,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
Integer TotalInnerIterations; //Number of inner CG iterations
|
||||
Integer TotalOuterIterations; //Number of restarts
|
||||
Integer TotalFinalStepIterations; //Number of CG iterations in final patch-up step
|
||||
RealD TrueResidual;
|
||||
|
||||
//Option to speed up *inner single precision* solves using a LinearFunction that produces a guess
|
||||
LinearFunction<FieldF> *guesser;
|
||||
@@ -67,6 +69,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
}
|
||||
|
||||
void operator() (const FieldD &src_d_in, FieldD &sol_d){
|
||||
std::cout << GridLogMessage << "MixedPrecisionConjugateGradient: Starting mixed precision CG with outer tolerance " << Tolerance << " and inner tolerance " << InnerTolerance << std::endl;
|
||||
TotalInnerIterations = 0;
|
||||
|
||||
GridStopWatch TotalTimer;
|
||||
@@ -96,6 +99,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
FieldF sol_f(SinglePrecGrid);
|
||||
sol_f.Checkerboard() = cb;
|
||||
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Starting initial inner CG with tolerance " << inner_tol << std::endl;
|
||||
ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations);
|
||||
CG_f.ErrorOnNoConverge = false;
|
||||
|
||||
@@ -104,22 +108,25 @@ NAMESPACE_BEGIN(Grid);
|
||||
GridStopWatch PrecChangeTimer;
|
||||
|
||||
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
|
||||
|
||||
|
||||
precisionChangeWorkspace pc_wk_sp_to_dp(DoublePrecGrid, SinglePrecGrid);
|
||||
precisionChangeWorkspace pc_wk_dp_to_sp(SinglePrecGrid, DoublePrecGrid);
|
||||
|
||||
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){
|
||||
//Compute double precision rsd and also new RHS vector.
|
||||
Linop_d.HermOp(sol_d, tmp_d);
|
||||
RealD norm = axpy_norm(src_d, -1., tmp_d, src_d_in); //src_d is residual vector
|
||||
|
||||
std::cout<<GridLogMessage<<" rsd norm "<<norm<<std::endl;
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration " <<outer_iter<<" residual "<< norm<< " target "<< stop<<std::endl;
|
||||
|
||||
if(norm < OuterLoopNormMult * stop){
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration converged on iteration " <<outer_iter <<std::endl;
|
||||
break;
|
||||
}
|
||||
while(norm * inner_tol * inner_tol < stop) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ??
|
||||
while(norm * inner_tol * inner_tol < stop*1.01) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ??
|
||||
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(src_f, src_d);
|
||||
precisionChange(src_f, src_d, pc_wk_dp_to_sp);
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
sol_f = Zero();
|
||||
@@ -129,6 +136,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
(*guesser)(src_f, sol_f);
|
||||
|
||||
//Inner CG
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration " << outer_iter << " starting inner CG with tolerance " << inner_tol << std::endl;
|
||||
CG_f.Tolerance = inner_tol;
|
||||
InnerCGtimer.Start();
|
||||
CG_f(Linop_f, src_f, sol_f);
|
||||
@@ -137,7 +145,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
|
||||
//Convert sol back to double and add to double prec solution
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(tmp_d, sol_f);
|
||||
precisionChange(tmp_d, sol_f, pc_wk_sp_to_dp);
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
axpy(sol_d, 1.0, tmp_d, sol_d);
|
||||
@@ -149,6 +157,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
ConjugateGradient<FieldD> CG_d(Tolerance, MaxInnerIterations);
|
||||
CG_d(Linop_d, src_d_in, sol_d);
|
||||
TotalFinalStepIterations = CG_d.IterationsToComplete;
|
||||
TrueResidual = CG_d.TrueResidual;
|
||||
|
||||
TotalTimer.Stop();
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Inner CG iterations " << TotalInnerIterations << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations << std::endl;
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ConjugateGradientMixedPrecBatched.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Raoul Hodgson <raoul.hodgson@ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_CONJUGATE_GRADIENT_MIXED_PREC_BATCHED_H
|
||||
#define GRID_CONJUGATE_GRADIENT_MIXED_PREC_BATCHED_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
//Mixed precision restarted defect correction CG
|
||||
template<class FieldD,class FieldF,
|
||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||
class MixedPrecisionConjugateGradientBatched : public LinearFunction<FieldD> {
|
||||
public:
|
||||
using LinearFunction<FieldD>::operator();
|
||||
RealD Tolerance;
|
||||
RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
||||
Integer MaxInnerIterations;
|
||||
Integer MaxOuterIterations;
|
||||
Integer MaxPatchupIterations;
|
||||
GridBase* SinglePrecGrid; //Grid for single-precision fields
|
||||
RealD OuterLoopNormMult; //Stop the outer loop and move to a final double prec solve when the residual is OuterLoopNormMult * Tolerance
|
||||
LinearOperatorBase<FieldF> &Linop_f;
|
||||
LinearOperatorBase<FieldD> &Linop_d;
|
||||
|
||||
//Option to speed up *inner single precision* solves using a LinearFunction that produces a guess
|
||||
LinearFunction<FieldF> *guesser;
|
||||
bool updateResidual;
|
||||
|
||||
MixedPrecisionConjugateGradientBatched(RealD tol,
|
||||
Integer maxinnerit,
|
||||
Integer maxouterit,
|
||||
Integer maxpatchit,
|
||||
GridBase* _sp_grid,
|
||||
LinearOperatorBase<FieldF> &_Linop_f,
|
||||
LinearOperatorBase<FieldD> &_Linop_d,
|
||||
bool _updateResidual=true) :
|
||||
Linop_f(_Linop_f), Linop_d(_Linop_d),
|
||||
Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), MaxPatchupIterations(maxpatchit), SinglePrecGrid(_sp_grid),
|
||||
OuterLoopNormMult(100.), guesser(NULL), updateResidual(_updateResidual) { };
|
||||
|
||||
void useGuesser(LinearFunction<FieldF> &g){
|
||||
guesser = &g;
|
||||
}
|
||||
|
||||
void operator() (const FieldD &src_d_in, FieldD &sol_d){
|
||||
std::vector<FieldD> srcs_d_in{src_d_in};
|
||||
std::vector<FieldD> sols_d{sol_d};
|
||||
|
||||
(*this)(srcs_d_in,sols_d);
|
||||
|
||||
sol_d = sols_d[0];
|
||||
}
|
||||
|
||||
void operator() (const std::vector<FieldD> &src_d_in, std::vector<FieldD> &sol_d){
|
||||
GRID_ASSERT(src_d_in.size() == sol_d.size());
|
||||
int NBatch = src_d_in.size();
|
||||
|
||||
std::cout << GridLogMessage << "NBatch = " << NBatch << std::endl;
|
||||
|
||||
Integer TotalOuterIterations = 0; //Number of restarts
|
||||
std::vector<Integer> TotalInnerIterations(NBatch,0); //Number of inner CG iterations
|
||||
std::vector<Integer> TotalFinalStepIterations(NBatch,0); //Number of CG iterations in final patch-up step
|
||||
|
||||
GridStopWatch TotalTimer;
|
||||
TotalTimer.Start();
|
||||
|
||||
GridStopWatch InnerCGtimer;
|
||||
GridStopWatch PrecChangeTimer;
|
||||
|
||||
int cb = src_d_in[0].Checkerboard();
|
||||
|
||||
std::vector<RealD> src_norm;
|
||||
std::vector<RealD> norm;
|
||||
std::vector<RealD> stop;
|
||||
|
||||
GridBase* DoublePrecGrid = src_d_in[0].Grid();
|
||||
FieldD tmp_d(DoublePrecGrid);
|
||||
tmp_d.Checkerboard() = cb;
|
||||
|
||||
FieldD tmp2_d(DoublePrecGrid);
|
||||
tmp2_d.Checkerboard() = cb;
|
||||
|
||||
std::vector<FieldD> src_d;
|
||||
std::vector<FieldF> src_f;
|
||||
std::vector<FieldF> sol_f;
|
||||
|
||||
for (int i=0; i<NBatch; i++) {
|
||||
sol_d[i].Checkerboard() = cb;
|
||||
|
||||
src_norm.push_back(norm2(src_d_in[i]));
|
||||
norm.push_back(0.);
|
||||
stop.push_back(src_norm[i] * Tolerance*Tolerance);
|
||||
|
||||
src_d.push_back(src_d_in[i]); //source for next inner iteration, computed from residual during operation
|
||||
|
||||
src_f.push_back(SinglePrecGrid);
|
||||
src_f[i].Checkerboard() = cb;
|
||||
|
||||
sol_f.push_back(SinglePrecGrid);
|
||||
sol_f[i].Checkerboard() = cb;
|
||||
}
|
||||
|
||||
RealD inner_tol = InnerTolerance;
|
||||
|
||||
ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations);
|
||||
CG_f.ErrorOnNoConverge = false;
|
||||
|
||||
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
|
||||
|
||||
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout << GridLogMessage << "Outer iteration " << outer_iter << std::endl;
|
||||
|
||||
bool allConverged = true;
|
||||
|
||||
for (int i=0; i<NBatch; i++) {
|
||||
//Compute double precision rsd and also new RHS vector.
|
||||
Linop_d.HermOp(sol_d[i], tmp_d);
|
||||
norm[i] = axpy_norm(src_d[i], -1., tmp_d, src_d_in[i]); //src_d is residual vector
|
||||
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Outer iteration " << outer_iter <<" solve " << i << " residual "<< norm[i] << " target "<< stop[i] <<std::endl;
|
||||
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(src_f[i], src_d[i]);
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
sol_f[i] = Zero();
|
||||
|
||||
if(norm[i] > OuterLoopNormMult * stop[i]) {
|
||||
allConverged = false;
|
||||
}
|
||||
}
|
||||
if (allConverged) break;
|
||||
|
||||
if (updateResidual) {
|
||||
RealD normMax = *std::max_element(std::begin(norm), std::end(norm));
|
||||
RealD stopMax = *std::max_element(std::begin(stop), std::end(stop));
|
||||
while( normMax * inner_tol * inner_tol < stopMax) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ??
|
||||
CG_f.Tolerance = inner_tol;
|
||||
}
|
||||
|
||||
//Optionally improve inner solver guess (eg using known eigenvectors)
|
||||
if(guesser != NULL) {
|
||||
(*guesser)(src_f, sol_f);
|
||||
}
|
||||
|
||||
for (int i=0; i<NBatch; i++) {
|
||||
//Inner CG
|
||||
InnerCGtimer.Start();
|
||||
CG_f(Linop_f, src_f[i], sol_f[i]);
|
||||
InnerCGtimer.Stop();
|
||||
TotalInnerIterations[i] += CG_f.IterationsToComplete;
|
||||
|
||||
//Convert sol back to double and add to double prec solution
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(tmp_d, sol_f[i]);
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
axpy(sol_d[i], 1.0, tmp_d, sol_d[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Final trial CG
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Starting final patch-up double-precision solve"<<std::endl;
|
||||
|
||||
for (int i=0; i<NBatch; i++) {
|
||||
ConjugateGradient<FieldD> CG_d(Tolerance, MaxPatchupIterations);
|
||||
CG_d(Linop_d, src_d_in[i], sol_d[i]);
|
||||
TotalFinalStepIterations[i] += CG_d.IterationsToComplete;
|
||||
}
|
||||
|
||||
TotalTimer.Stop();
|
||||
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
for (int i=0; i<NBatch; i++) {
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: solve " << i << " Inner CG iterations " << TotalInnerIterations[i] << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations[i] << std::endl;
|
||||
}
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
RealD Tolerance;
|
||||
// RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||
std::vector<int> IterationsToCompleteShift; // Iterations for this shift
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
MultiShiftFunction shifts;
|
||||
std::vector<RealD> TrueResidualShift;
|
||||
|
||||
ConjugateGradientMultiShift(Integer maxit,MultiShiftFunction &_shifts) :
|
||||
ConjugateGradientMultiShift(Integer maxit, const MultiShiftFunction &_shifts) :
|
||||
MaxIterations(maxit),
|
||||
shifts(_shifts)
|
||||
{
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
|
||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, std::vector<Field> &psi)
|
||||
{
|
||||
GRID_TRACE("ConjugateGradientMultiShift");
|
||||
|
||||
GridBase *grid = src.Grid();
|
||||
|
||||
@@ -97,15 +98,15 @@ public:
|
||||
std::vector<RealD> alpha(nshift,1.0);
|
||||
std::vector<Field> ps(nshift,grid);// Search directions
|
||||
|
||||
assert(psi.size()==nshift);
|
||||
assert(mass.size()==nshift);
|
||||
assert(mresidual.size()==nshift);
|
||||
GRID_ASSERT(psi.size()==nshift);
|
||||
GRID_ASSERT(mass.size()==nshift);
|
||||
GRID_ASSERT(mresidual.size()==nshift);
|
||||
|
||||
// dynamic sized arrays on stack; 2d is a pain with vector
|
||||
RealD bs[nshift];
|
||||
RealD rsq[nshift];
|
||||
RealD z[nshift][2];
|
||||
int converged[nshift];
|
||||
// remove dynamic sized arrays on stack; 2d is a pain with vector
|
||||
std::vector<RealD> bs(nshift);
|
||||
std::vector<RealD> rsq(nshift);
|
||||
std::vector<std::array<RealD,2> > z(nshift);
|
||||
std::vector<int> converged(nshift);
|
||||
|
||||
const int primary =0;
|
||||
|
||||
@@ -121,7 +122,7 @@ public:
|
||||
|
||||
// Check lightest mass
|
||||
for(int s=0;s<nshift;s++){
|
||||
assert( mass[s]>= mass[primary] );
|
||||
GRID_ASSERT( mass[s]>= mass[primary] );
|
||||
converged[s]=0;
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ public:
|
||||
for(int s=0;s<nshift;s++){
|
||||
rsq[s] = cp * mresidual[s] * mresidual[s];
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShift: shift "<<s
|
||||
<<" target resid "<<rsq[s]<<std::endl;
|
||||
<<" target resid^2 "<<rsq[s]<<std::endl;
|
||||
ps[s] = src;
|
||||
}
|
||||
// r and p for primary
|
||||
@@ -182,6 +183,9 @@ public:
|
||||
for(int s=0;s<nshift;s++) {
|
||||
axpby(psi[s],0.,-bs[s]*alpha[s],src,src);
|
||||
}
|
||||
|
||||
std::cout << GridLogIterative << "ConjugateGradientMultiShift: initial rn (|src|^2) =" << rn << " qq (|MdagM src|^2) =" << qq << " d ( dot(src, [MdagM + m_0]src) ) =" << d << " c=" << c << std::endl;
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// Timers
|
||||
@@ -321,8 +325,8 @@ public:
|
||||
|
||||
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tMarix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
||||
|
||||
IterationsToComplete = k;
|
||||
@@ -334,7 +338,7 @@ public:
|
||||
}
|
||||
// ugly hack
|
||||
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
||||
// assert(0);
|
||||
// GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ConjugateGradientMultiShift.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Christopher Kelly <ckelly@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
//CK 2020: A variant of the multi-shift conjugate gradient with the matrix multiplication in single precision.
|
||||
//The residual is stored in single precision, but the search directions and solution are stored in double precision.
|
||||
//Every update_freq iterations the residual is corrected in double precision.
|
||||
//For safety the a final regular CG is applied to clean up if necessary
|
||||
|
||||
//PB Pure single, then double fixup
|
||||
|
||||
template<class FieldD, class FieldF,
|
||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||
class ConjugateGradientMultiShiftMixedPrecCleanup : public OperatorMultiFunction<FieldD>,
|
||||
public OperatorFunction<FieldD>
|
||||
{
|
||||
public:
|
||||
|
||||
using OperatorFunction<FieldD>::operator();
|
||||
|
||||
RealD Tolerance;
|
||||
Integer MaxIterationsMshift;
|
||||
Integer MaxIterations;
|
||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||
std::vector<int> IterationsToCompleteShift; // Iterations for this shift
|
||||
int verbose;
|
||||
MultiShiftFunction shifts;
|
||||
std::vector<RealD> TrueResidualShift;
|
||||
|
||||
int ReliableUpdateFreq; //number of iterations between reliable updates
|
||||
|
||||
GridBase* SinglePrecGrid; //Grid for single-precision fields
|
||||
LinearOperatorBase<FieldF> &Linop_f; //single precision
|
||||
|
||||
ConjugateGradientMultiShiftMixedPrecCleanup(Integer maxit, const MultiShiftFunction &_shifts,
|
||||
GridBase* _SinglePrecGrid, LinearOperatorBase<FieldF> &_Linop_f,
|
||||
int _ReliableUpdateFreq) :
|
||||
MaxIterationsMshift(maxit), shifts(_shifts), SinglePrecGrid(_SinglePrecGrid), Linop_f(_Linop_f), ReliableUpdateFreq(_ReliableUpdateFreq),
|
||||
MaxIterations(20000)
|
||||
{
|
||||
verbose=1;
|
||||
IterationsToCompleteShift.resize(_shifts.order);
|
||||
TrueResidualShift.resize(_shifts.order);
|
||||
}
|
||||
|
||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, FieldD &psi)
|
||||
{
|
||||
GridBase *grid = src.Grid();
|
||||
int nshift = shifts.order;
|
||||
std::vector<FieldD> results(nshift,grid);
|
||||
(*this)(Linop,src,results,psi);
|
||||
}
|
||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, std::vector<FieldD> &results, FieldD &psi)
|
||||
{
|
||||
int nshift = shifts.order;
|
||||
|
||||
(*this)(Linop,src,results);
|
||||
|
||||
psi = shifts.norm*src;
|
||||
for(int i=0;i<nshift;i++){
|
||||
psi = psi + shifts.residues[i]*results[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void operator() (LinearOperatorBase<FieldD> &Linop_d, const FieldD &src_d, std::vector<FieldD> &psi_d)
|
||||
{
|
||||
GRID_TRACE("ConjugateGradientMultiShiftMixedPrecCleanup");
|
||||
GridBase *DoublePrecGrid = src_d.Grid();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Convenience references to the info stored in "MultiShiftFunction"
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
int nshift = shifts.order;
|
||||
|
||||
std::vector<RealD> &mass(shifts.poles); // Make references to array in "shifts"
|
||||
std::vector<RealD> &mresidual(shifts.tolerances);
|
||||
std::vector<RealD> alpha(nshift,1.0);
|
||||
|
||||
//Double precision search directions
|
||||
FieldD p_d(DoublePrecGrid);
|
||||
std::vector<FieldF> ps_f (nshift, SinglePrecGrid);// Search directions (single precision)
|
||||
std::vector<FieldF> psi_f(nshift, SinglePrecGrid);// solutions (single precision)
|
||||
|
||||
FieldD tmp_d(DoublePrecGrid);
|
||||
FieldD r_d(DoublePrecGrid);
|
||||
FieldF r_f(SinglePrecGrid);
|
||||
FieldD mmp_d(DoublePrecGrid);
|
||||
|
||||
GRID_ASSERT(psi_d.size()==nshift);
|
||||
GRID_ASSERT(mass.size()==nshift);
|
||||
GRID_ASSERT(mresidual.size()==nshift);
|
||||
|
||||
// dynamic sized arrays on stack; 2d is a pain with vector
|
||||
std::vector<RealD> bs(nshift);
|
||||
std::vector<RealD> rsq(nshift);
|
||||
std::vector<RealD> rsqf(nshift);
|
||||
std::vector<std::array<RealD,2> > z(nshift);
|
||||
std::vector<int> converged(nshift);
|
||||
|
||||
const int primary =0;
|
||||
|
||||
//Primary shift fields CG iteration
|
||||
RealD a,b,c,d;
|
||||
RealD cp,bp,qq; //prev
|
||||
|
||||
// Matrix mult fields
|
||||
FieldF p_f(SinglePrecGrid);
|
||||
FieldF mmp_f(SinglePrecGrid);
|
||||
|
||||
// Check lightest mass
|
||||
for(int s=0;s<nshift;s++){
|
||||
GRID_ASSERT( mass[s]>= mass[primary] );
|
||||
converged[s]=0;
|
||||
}
|
||||
|
||||
// Wire guess to zero
|
||||
// Residuals "r" are src
|
||||
// First search direction "p" is also src
|
||||
cp = norm2(src_d);
|
||||
|
||||
// Handle trivial case of zero src.
|
||||
if( cp == 0. ){
|
||||
for(int s=0;s<nshift;s++){
|
||||
psi_d[s] = Zero();
|
||||
psi_f[s] = Zero();
|
||||
IterationsToCompleteShift[s] = 1;
|
||||
TrueResidualShift[s] = 0.;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(int s=0;s<nshift;s++){
|
||||
rsq[s] = cp * mresidual[s] * mresidual[s];
|
||||
rsqf[s] =rsq[s];
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup: shift "<< s <<" target resid "<<rsq[s]<<std::endl;
|
||||
// ps_d[s] = src_d;
|
||||
precisionChange(ps_f[s],src_d);
|
||||
}
|
||||
// r and p for primary
|
||||
p_d = src_d; //primary copy --- make this a reference to ps_d to save axpys
|
||||
r_d = p_d;
|
||||
|
||||
//MdagM+m[0]
|
||||
precisionChange(p_f,p_d);
|
||||
Linop_f.HermOpAndNorm(p_f,mmp_f,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
||||
precisionChange(tmp_d,mmp_f);
|
||||
Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
||||
tmp_d = tmp_d - mmp_d;
|
||||
std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl;
|
||||
// GRID_ASSERT(norm2(tmp_d)< 1.0e-4);
|
||||
|
||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
||||
RealD rn = norm2(p_d);
|
||||
d += rn*mass[0];
|
||||
|
||||
b = -cp /d;
|
||||
|
||||
// Set up the various shift variables
|
||||
int iz=0;
|
||||
z[0][1-iz] = 1.0;
|
||||
z[0][iz] = 1.0;
|
||||
bs[0] = b;
|
||||
for(int s=1;s<nshift;s++){
|
||||
z[s][1-iz] = 1.0;
|
||||
z[s][iz] = 1.0/( 1.0 - b*(mass[s]-mass[0]));
|
||||
bs[s] = b*z[s][iz];
|
||||
}
|
||||
|
||||
// r += b[0] A.p[0]
|
||||
// c= norm(r)
|
||||
c=axpy_norm(r_d,b,mmp_d,r_d);
|
||||
|
||||
for(int s=0;s<nshift;s++) {
|
||||
axpby(psi_d[s],0.,-bs[s]*alpha[s],src_d,src_d);
|
||||
precisionChange(psi_f[s],psi_d[s]);
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// Timers
|
||||
///////////////////////////////////////
|
||||
GridStopWatch AXPYTimer, ShiftTimer, QRTimer, MatrixTimer, SolverTimer, PrecChangeTimer, CleanupTimer;
|
||||
|
||||
SolverTimer.Start();
|
||||
|
||||
// Iteration loop
|
||||
int k;
|
||||
|
||||
for (k=1;k<=MaxIterationsMshift;k++){
|
||||
|
||||
a = c /cp;
|
||||
AXPYTimer.Start();
|
||||
axpy(p_d,a,p_d,r_d);
|
||||
AXPYTimer.Stop();
|
||||
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(r_f, r_d);
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
AXPYTimer.Start();
|
||||
for(int s=0;s<nshift;s++){
|
||||
if ( ! converged[s] ) {
|
||||
if (s==0){
|
||||
axpy(ps_f[s],a,ps_f[s],r_f);
|
||||
} else{
|
||||
RealD as =a *z[s][iz]*bs[s] /(z[s][1-iz]*b);
|
||||
axpby(ps_f[s],z[s][iz],as,r_f,ps_f[s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
AXPYTimer.Stop();
|
||||
|
||||
cp=c;
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(p_f, p_d); //get back single prec search direction for linop
|
||||
PrecChangeTimer.Stop();
|
||||
MatrixTimer.Start();
|
||||
Linop_f.HermOp(p_f,mmp_f);
|
||||
MatrixTimer.Stop();
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(mmp_d, mmp_f); // From Float to Double
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
d=real(innerProduct(p_d,mmp_d));
|
||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
||||
RealD rn = norm2(p_d);
|
||||
d += rn*mass[0];
|
||||
|
||||
bp=b;
|
||||
b=-cp/d;
|
||||
|
||||
// Toggle the recurrence history
|
||||
bs[0] = b;
|
||||
iz = 1-iz;
|
||||
ShiftTimer.Start();
|
||||
for(int s=1;s<nshift;s++){
|
||||
if((!converged[s])){
|
||||
RealD z0 = z[s][1-iz];
|
||||
RealD z1 = z[s][iz];
|
||||
z[s][iz] = z0*z1*bp
|
||||
/ (b*a*(z1-z0) + z1*bp*(1- (mass[s]-mass[0])*b));
|
||||
bs[s] = b*z[s][iz]/z0; // NB sign rel to Mike
|
||||
}
|
||||
}
|
||||
ShiftTimer.Stop();
|
||||
|
||||
//Update single precision solutions
|
||||
AXPYTimer.Start();
|
||||
for(int s=0;s<nshift;s++){
|
||||
int ss = s;
|
||||
if( (!converged[s]) ) {
|
||||
axpy(psi_f[ss],-bs[s]*alpha[s],ps_f[s],psi_f[ss]);
|
||||
}
|
||||
}
|
||||
c = axpy_norm(r_d,b,mmp_d,r_d);
|
||||
AXPYTimer.Stop();
|
||||
|
||||
// Convergence checks
|
||||
int all_converged = 1;
|
||||
for(int s=0;s<nshift;s++){
|
||||
|
||||
if ( (!converged[s]) ){
|
||||
IterationsToCompleteShift[s] = k;
|
||||
|
||||
RealD css = c * z[s][iz]* z[s][iz];
|
||||
|
||||
if(css<rsqf[s]){
|
||||
if ( ! converged[s] )
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup k="<<k<<" Shift "<<s<<" has converged"<<std::endl;
|
||||
converged[s]=1;
|
||||
} else {
|
||||
all_converged=0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( all_converged || k == MaxIterationsMshift-1){
|
||||
|
||||
SolverTimer.Stop();
|
||||
|
||||
for(int s=0;s<nshift;s++){
|
||||
precisionChange(psi_d[s],psi_f[s]);
|
||||
}
|
||||
|
||||
|
||||
if ( all_converged ){
|
||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrecCleanup: All shifts have converged iteration "<<k<<std::endl;
|
||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrecCleanup: Checking solutions"<<std::endl;
|
||||
} else {
|
||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrecCleanup: Not all shifts have converged iteration "<<k<<std::endl;
|
||||
}
|
||||
|
||||
// Check answers
|
||||
for(int s=0; s < nshift; s++) {
|
||||
Linop_d.HermOpAndNorm(psi_d[s],mmp_d,d,qq);
|
||||
axpy(tmp_d,mass[s],psi_d[s],mmp_d);
|
||||
axpy(r_d,-alpha[s],src_d,tmp_d);
|
||||
RealD rn = norm2(r_d);
|
||||
RealD cn = norm2(src_d);
|
||||
TrueResidualShift[s] = std::sqrt(rn/cn);
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup: shift["<<s<<"] true residual "<< TrueResidualShift[s] << " target " << mresidual[s] << std::endl;
|
||||
|
||||
//If we have not reached the desired tolerance, do a (mixed precision) CG cleanup
|
||||
if(rn >= rsq[s]){
|
||||
CleanupTimer.Start();
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup: performing cleanup step for shift " << s << std::endl;
|
||||
|
||||
//Setup linear operators for final cleanup
|
||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldD> Linop_shift_d(Linop_d, mass[s]);
|
||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldF> Linop_shift_f(Linop_f, mass[s]);
|
||||
|
||||
MixedPrecisionConjugateGradient<FieldD,FieldF> cg(mresidual[s], MaxIterations, MaxIterations, SinglePrecGrid, Linop_shift_f, Linop_shift_d);
|
||||
cg(src_d, psi_d[s]);
|
||||
|
||||
TrueResidualShift[s] = cg.TrueResidual;
|
||||
CleanupTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradientMultiShiftMixedPrecCleanup: Time Breakdown for body"<<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tPrecision Change " << PrecChangeTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tFinal Cleanup " << CleanupTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver+Cleanup " << SolverTimer.Elapsed() + CleanupTimer.Elapsed() << std::endl;
|
||||
|
||||
IterationsToComplete = k;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
};
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ConjugateGradientMultiShift.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Christopher Kelly <ckelly@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_CONJUGATE_GRADIENT_MULTI_SHIFT_MIXEDPREC_H
|
||||
#define GRID_CONJUGATE_GRADIENT_MULTI_SHIFT_MIXEDPREC_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
//CK 2020: A variant of the multi-shift conjugate gradient with the matrix multiplication in single precision.
|
||||
//The residual is stored in single precision, but the search directions and solution are stored in double precision.
|
||||
//Every update_freq iterations the residual is corrected in double precision.
|
||||
|
||||
//For safety the a final regular CG is applied to clean up if necessary
|
||||
|
||||
//Linop to add shift to input linop, used in cleanup CG
|
||||
namespace ConjugateGradientMultiShiftMixedPrecSupport{
|
||||
template<typename Field>
|
||||
class ShiftedLinop: public LinearOperatorBase<Field>{
|
||||
public:
|
||||
LinearOperatorBase<Field> &linop_base;
|
||||
RealD shift;
|
||||
|
||||
ShiftedLinop(LinearOperatorBase<Field> &_linop_base, RealD _shift): linop_base(_linop_base), shift(_shift){}
|
||||
|
||||
void OpDiag (const Field &in, Field &out){ GRID_ASSERT(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp){ GRID_ASSERT(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ GRID_ASSERT(0); }
|
||||
|
||||
void Op (const Field &in, Field &out){ GRID_ASSERT(0); }
|
||||
void AdjOp (const Field &in, Field &out){ GRID_ASSERT(0); }
|
||||
|
||||
void HermOp(const Field &in, Field &out){
|
||||
linop_base.HermOp(in, out);
|
||||
axpy(out, shift, in, out);
|
||||
}
|
||||
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
HermOp(in,out);
|
||||
ComplexD dot = innerProduct(in,out);
|
||||
n1=real(dot);
|
||||
n2=norm2(out);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template<class FieldD, class FieldF,
|
||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||
class ConjugateGradientMultiShiftMixedPrec : public OperatorMultiFunction<FieldD>,
|
||||
public OperatorFunction<FieldD>
|
||||
{
|
||||
public:
|
||||
|
||||
using OperatorFunction<FieldD>::operator();
|
||||
|
||||
RealD Tolerance;
|
||||
Integer MaxIterationsMshift;
|
||||
Integer MaxIterations;
|
||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||
std::vector<int> IterationsToCompleteShift; // Iterations for this shift
|
||||
int verbose;
|
||||
MultiShiftFunction shifts;
|
||||
std::vector<RealD> TrueResidualShift;
|
||||
|
||||
int ReliableUpdateFreq; //number of iterations between reliable updates
|
||||
|
||||
GridBase* SinglePrecGrid; //Grid for single-precision fields
|
||||
LinearOperatorBase<FieldF> &Linop_f; //single precision
|
||||
|
||||
ConjugateGradientMultiShiftMixedPrec(Integer maxit, const MultiShiftFunction &_shifts,
|
||||
GridBase* _SinglePrecGrid, LinearOperatorBase<FieldF> &_Linop_f,
|
||||
int _ReliableUpdateFreq) :
|
||||
MaxIterationsMshift(maxit), shifts(_shifts), SinglePrecGrid(_SinglePrecGrid), Linop_f(_Linop_f), ReliableUpdateFreq(_ReliableUpdateFreq),
|
||||
MaxIterations(20000)
|
||||
{
|
||||
verbose=1;
|
||||
IterationsToCompleteShift.resize(_shifts.order);
|
||||
TrueResidualShift.resize(_shifts.order);
|
||||
}
|
||||
|
||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, FieldD &psi)
|
||||
{
|
||||
GridBase *grid = src.Grid();
|
||||
int nshift = shifts.order;
|
||||
std::vector<FieldD> results(nshift,grid);
|
||||
(*this)(Linop,src,results,psi);
|
||||
}
|
||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, std::vector<FieldD> &results, FieldD &psi)
|
||||
{
|
||||
int nshift = shifts.order;
|
||||
|
||||
(*this)(Linop,src,results);
|
||||
|
||||
psi = shifts.norm*src;
|
||||
for(int i=0;i<nshift;i++){
|
||||
psi = psi + shifts.residues[i]*results[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void operator() (LinearOperatorBase<FieldD> &Linop_d, const FieldD &src_d, std::vector<FieldD> &psi_d)
|
||||
{
|
||||
GRID_TRACE("ConjugateGradientMultiShiftMixedPrec");
|
||||
GridBase *DoublePrecGrid = src_d.Grid();
|
||||
|
||||
precisionChangeWorkspace pc_wk_s_to_d(DoublePrecGrid,SinglePrecGrid);
|
||||
precisionChangeWorkspace pc_wk_d_to_s(SinglePrecGrid,DoublePrecGrid);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Convenience references to the info stored in "MultiShiftFunction"
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
int nshift = shifts.order;
|
||||
|
||||
std::vector<RealD> &mass(shifts.poles); // Make references to array in "shifts"
|
||||
std::vector<RealD> &mresidual(shifts.tolerances);
|
||||
std::vector<RealD> alpha(nshift,1.0);
|
||||
|
||||
//Double precision search directions
|
||||
FieldD p_d(DoublePrecGrid);
|
||||
std::vector<FieldD> ps_d(nshift, DoublePrecGrid);// Search directions (double precision)
|
||||
|
||||
FieldD tmp_d(DoublePrecGrid);
|
||||
FieldD r_d(DoublePrecGrid);
|
||||
FieldD mmp_d(DoublePrecGrid);
|
||||
|
||||
GRID_ASSERT(psi_d.size()==nshift);
|
||||
GRID_ASSERT(mass.size()==nshift);
|
||||
GRID_ASSERT(mresidual.size()==nshift);
|
||||
|
||||
// dynamic sized arrays on stack; 2d is a pain with vector
|
||||
std::vector<RealD> bs(nshift);
|
||||
std::vector<RealD> rsq(nshift);
|
||||
std::vector<RealD> rsqf(nshift);
|
||||
std::vector<std::array<RealD,2> > z(nshift);
|
||||
std::vector<int> converged(nshift);
|
||||
|
||||
const int primary =0;
|
||||
|
||||
//Primary shift fields CG iteration
|
||||
RealD a,b,c,d;
|
||||
RealD cp,bp,qq; //prev
|
||||
|
||||
// Matrix mult fields
|
||||
FieldF p_f(SinglePrecGrid);
|
||||
FieldF mmp_f(SinglePrecGrid);
|
||||
|
||||
// Check lightest mass
|
||||
for(int s=0;s<nshift;s++){
|
||||
GRID_ASSERT( mass[s]>= mass[primary] );
|
||||
converged[s]=0;
|
||||
}
|
||||
|
||||
// Wire guess to zero
|
||||
// Residuals "r" are src
|
||||
// First search direction "p" is also src
|
||||
cp = norm2(src_d);
|
||||
|
||||
// Handle trivial case of zero src.
|
||||
if( cp == 0. ){
|
||||
for(int s=0;s<nshift;s++){
|
||||
psi_d[s] = Zero();
|
||||
IterationsToCompleteShift[s] = 1;
|
||||
TrueResidualShift[s] = 0.;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(int s=0;s<nshift;s++){
|
||||
rsq[s] = cp * mresidual[s] * mresidual[s];
|
||||
rsqf[s] =rsq[s];
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec: shift "<< s <<" target resid "<<rsq[s]<<std::endl;
|
||||
ps_d[s] = src_d;
|
||||
}
|
||||
// r and p for primary
|
||||
p_d = src_d; //primary copy --- make this a reference to ps_d to save axpys
|
||||
r_d = p_d;
|
||||
|
||||
//MdagM+m[0]
|
||||
precisionChange(p_f, p_d, pc_wk_d_to_s);
|
||||
|
||||
Linop_f.HermOpAndNorm(p_f,mmp_f,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
||||
precisionChange(tmp_d, mmp_f, pc_wk_s_to_d);
|
||||
Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
||||
tmp_d = tmp_d - mmp_d;
|
||||
std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl;
|
||||
GRID_ASSERT(norm2(tmp_d)< 1.0);
|
||||
|
||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
||||
RealD rn = norm2(p_d);
|
||||
d += rn*mass[0];
|
||||
|
||||
b = -cp /d;
|
||||
|
||||
// Set up the various shift variables
|
||||
int iz=0;
|
||||
z[0][1-iz] = 1.0;
|
||||
z[0][iz] = 1.0;
|
||||
bs[0] = b;
|
||||
for(int s=1;s<nshift;s++){
|
||||
z[s][1-iz] = 1.0;
|
||||
z[s][iz] = 1.0/( 1.0 - b*(mass[s]-mass[0]));
|
||||
bs[s] = b*z[s][iz];
|
||||
}
|
||||
|
||||
// r += b[0] A.p[0]
|
||||
// c= norm(r)
|
||||
c=axpy_norm(r_d,b,mmp_d,r_d);
|
||||
|
||||
for(int s=0;s<nshift;s++) {
|
||||
axpby(psi_d[s],0.,-bs[s]*alpha[s],src_d,src_d);
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// Timers
|
||||
///////////////////////////////////////
|
||||
GridStopWatch AXPYTimer, ShiftTimer, QRTimer, MatrixTimer, SolverTimer, PrecChangeTimer, CleanupTimer;
|
||||
|
||||
SolverTimer.Start();
|
||||
|
||||
// Iteration loop
|
||||
int k;
|
||||
|
||||
for (k=1;k<=MaxIterationsMshift;k++){
|
||||
|
||||
a = c /cp;
|
||||
AXPYTimer.Start();
|
||||
axpy(p_d,a,p_d,r_d);
|
||||
|
||||
for(int s=0;s<nshift;s++){
|
||||
if ( ! converged[s] ) {
|
||||
if (s==0){
|
||||
axpy(ps_d[s],a,ps_d[s],r_d);
|
||||
} else{
|
||||
RealD as =a *z[s][iz]*bs[s] /(z[s][1-iz]*b);
|
||||
axpby(ps_d[s],z[s][iz],as,r_d,ps_d[s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
AXPYTimer.Stop();
|
||||
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(p_f, p_d, pc_wk_d_to_s); //get back single prec search direction for linop
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
cp=c;
|
||||
MatrixTimer.Start();
|
||||
Linop_f.HermOp(p_f,mmp_f);
|
||||
MatrixTimer.Stop();
|
||||
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(mmp_d, mmp_f, pc_wk_s_to_d); // From Float to Double
|
||||
PrecChangeTimer.Stop();
|
||||
|
||||
AXPYTimer.Start();
|
||||
d=real(innerProduct(p_d,mmp_d));
|
||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
||||
AXPYTimer.Stop();
|
||||
RealD rn = norm2(p_d);
|
||||
d += rn*mass[0];
|
||||
|
||||
bp=b;
|
||||
b=-cp/d;
|
||||
|
||||
// Toggle the recurrence history
|
||||
bs[0] = b;
|
||||
iz = 1-iz;
|
||||
ShiftTimer.Start();
|
||||
for(int s=1;s<nshift;s++){
|
||||
if((!converged[s])){
|
||||
RealD z0 = z[s][1-iz];
|
||||
RealD z1 = z[s][iz];
|
||||
z[s][iz] = z0*z1*bp
|
||||
/ (b*a*(z1-z0) + z1*bp*(1- (mass[s]-mass[0])*b));
|
||||
bs[s] = b*z[s][iz]/z0; // NB sign rel to Mike
|
||||
}
|
||||
}
|
||||
ShiftTimer.Stop();
|
||||
|
||||
//Update double precision solutions
|
||||
AXPYTimer.Start();
|
||||
for(int s=0;s<nshift;s++){
|
||||
int ss = s;
|
||||
if( (!converged[s]) ) {
|
||||
axpy(psi_d[ss],-bs[s]*alpha[s],ps_d[s],psi_d[ss]);
|
||||
}
|
||||
}
|
||||
|
||||
//Perform reliable update if necessary; otherwise update residual from single-prec mmp
|
||||
c = axpy_norm(r_d,b,mmp_d,r_d);
|
||||
|
||||
AXPYTimer.Stop();
|
||||
|
||||
if(k % ReliableUpdateFreq == 0){
|
||||
RealD c_old = c;
|
||||
//Replace r with true residual
|
||||
MatrixTimer.Start();
|
||||
Linop_d.HermOp(psi_d[0],mmp_d);
|
||||
MatrixTimer.Stop();
|
||||
|
||||
AXPYTimer.Start();
|
||||
axpy(mmp_d,mass[0],psi_d[0],mmp_d);
|
||||
|
||||
c = axpy_norm(r_d, -1.0, mmp_d, src_d);
|
||||
AXPYTimer.Stop();
|
||||
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec k="<<k<< ", replaced |r|^2 = "<<c_old <<" with |r|^2 = "<<c<<std::endl;
|
||||
}
|
||||
|
||||
// Convergence checks
|
||||
int all_converged = 1;
|
||||
for(int s=0;s<nshift;s++){
|
||||
|
||||
if ( (!converged[s]) ){
|
||||
IterationsToCompleteShift[s] = k;
|
||||
|
||||
RealD css = c * z[s][iz]* z[s][iz];
|
||||
|
||||
if(css<rsqf[s]){
|
||||
if ( ! converged[s] )
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec k="<<k<<" Shift "<<s<<" has converged"<<std::endl;
|
||||
converged[s]=1;
|
||||
} else {
|
||||
all_converged=0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( all_converged || k == MaxIterationsMshift-1){
|
||||
|
||||
SolverTimer.Stop();
|
||||
|
||||
if ( all_converged ){
|
||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrec: All shifts have converged iteration "<<k<<std::endl;
|
||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrec: Checking solutions"<<std::endl;
|
||||
} else {
|
||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrec: Not all shifts have converged iteration "<<k<<std::endl;
|
||||
}
|
||||
|
||||
// Check answers
|
||||
for(int s=0; s < nshift; s++) {
|
||||
Linop_d.HermOpAndNorm(psi_d[s],mmp_d,d,qq);
|
||||
axpy(tmp_d,mass[s],psi_d[s],mmp_d);
|
||||
axpy(r_d,-alpha[s],src_d,tmp_d);
|
||||
RealD rn = norm2(r_d);
|
||||
RealD cn = norm2(src_d);
|
||||
TrueResidualShift[s] = std::sqrt(rn/cn);
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec: shift["<<s<<"] true residual "<< TrueResidualShift[s] << " target " << mresidual[s] << std::endl;
|
||||
|
||||
//If we have not reached the desired tolerance, do a (mixed precision) CG cleanup
|
||||
if(rn >= rsq[s]){
|
||||
CleanupTimer.Start();
|
||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec: performing cleanup step for shift " << s << std::endl;
|
||||
|
||||
//Setup linear operators for final cleanup
|
||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldD> Linop_shift_d(Linop_d, mass[s]);
|
||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldF> Linop_shift_f(Linop_f, mass[s]);
|
||||
|
||||
MixedPrecisionConjugateGradient<FieldD,FieldF> cg(mresidual[s], MaxIterations, MaxIterations, SinglePrecGrid, Linop_shift_f, Linop_shift_d);
|
||||
cg(src_d, psi_d[s]);
|
||||
|
||||
TrueResidualShift[s] = cg.TrueResidual;
|
||||
CleanupTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradientMultiShiftMixedPrec: Time Breakdown for body"<<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\t\tPrecision Change " << PrecChangeTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tFinal Cleanup " << CleanupTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver+Cleanup " << SolverTimer.Elapsed() + CleanupTimer.Elapsed() << std::endl;
|
||||
|
||||
IterationsToComplete = k;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
};
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -35,7 +35,7 @@ template<class FieldD,class FieldF,
|
||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||
class ConjugateGradientReliableUpdate : public LinearFunction<FieldD> {
|
||||
public:
|
||||
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
||||
// Defaults true.
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
LinearOperatorBase<FieldF> &Linop_f;
|
||||
LinearOperatorBase<FieldD> &Linop_d;
|
||||
GridBase* SinglePrecGrid;
|
||||
RealD Delta; //reliable update parameter
|
||||
RealD Delta; //reliable update parameter. A reliable update is performed when the residual drops by a factor of Delta relative to its value at the last update
|
||||
|
||||
//Optional ability to switch to a different linear operator once the tolerance reaches a certain point. Useful for single/half -> single/single
|
||||
LinearOperatorBase<FieldF> *Linop_fallback;
|
||||
@@ -65,7 +65,9 @@ public:
|
||||
ErrorOnNoConverge(err_on_no_conv),
|
||||
DoFinalCleanup(true),
|
||||
Linop_fallback(NULL)
|
||||
{};
|
||||
{
|
||||
GRID_ASSERT(Delta > 0. && Delta < 1. && "Expect 0 < Delta < 1");
|
||||
};
|
||||
|
||||
void setFallbackLinop(LinearOperatorBase<FieldF> &_Linop_fallback, const RealD _fallback_transition_tol){
|
||||
Linop_fallback = &_Linop_fallback;
|
||||
@@ -73,6 +75,7 @@ public:
|
||||
}
|
||||
|
||||
void operator()(const FieldD &src, FieldD &psi) {
|
||||
GRID_TRACE("ConjugateGradientReliableUpdate");
|
||||
LinearOperatorBase<FieldF> *Linop_f_use = &Linop_f;
|
||||
bool using_fallback = false;
|
||||
|
||||
@@ -87,7 +90,7 @@ public:
|
||||
|
||||
// Initial residual computation & set up
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
Linop_d.HermOpAndNorm(psi, mmp, d, b);
|
||||
|
||||
@@ -115,9 +118,12 @@ public:
|
||||
}
|
||||
|
||||
//Single prec initialization
|
||||
precisionChangeWorkspace pc_wk_sp_to_dp(src.Grid(), SinglePrecGrid);
|
||||
precisionChangeWorkspace pc_wk_dp_to_sp(SinglePrecGrid, src.Grid());
|
||||
|
||||
FieldF r_f(SinglePrecGrid);
|
||||
r_f.Checkerboard() = r.Checkerboard();
|
||||
precisionChange(r_f, r);
|
||||
precisionChange(r_f, r, pc_wk_dp_to_sp);
|
||||
|
||||
FieldF psi_f(r_f);
|
||||
psi_f = Zero();
|
||||
@@ -133,7 +139,8 @@ public:
|
||||
GridStopWatch LinalgTimer;
|
||||
GridStopWatch MatrixTimer;
|
||||
GridStopWatch SolverTimer;
|
||||
|
||||
GridStopWatch PrecChangeTimer;
|
||||
|
||||
SolverTimer.Start();
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
@@ -172,7 +179,9 @@ public:
|
||||
// Stopping condition
|
||||
if (cp <= rsq) {
|
||||
//Although not written in the paper, I assume that I have to add on the final solution
|
||||
precisionChange(mmp, psi_f);
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(mmp, psi_f, pc_wk_sp_to_dp);
|
||||
PrecChangeTimer.Stop();
|
||||
psi = psi + mmp;
|
||||
|
||||
|
||||
@@ -193,7 +202,10 @@ public:
|
||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tPrecChange " << PrecChangeTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tPrecChange avg time " << PrecChangeTimer.Elapsed()/(2*l+1) <<std::endl;
|
||||
|
||||
|
||||
IterationsToComplete = k;
|
||||
ReliableUpdatesPerformed = l;
|
||||
|
||||
@@ -205,7 +217,7 @@ public:
|
||||
CG(Linop_d,src,psi);
|
||||
IterationsToCleanup = CG.IterationsToComplete;
|
||||
}
|
||||
else if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
||||
else if (ErrorOnNoConverge) GRID_ASSERT(true_residual / Tolerance < 10000.0);
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate complete.\n";
|
||||
return;
|
||||
@@ -213,14 +225,21 @@ public:
|
||||
else if(cp < Delta * MaxResidSinceLastRelUp) { //reliable update
|
||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate "
|
||||
<< cp << "(residual) < " << Delta << "(Delta) * " << MaxResidSinceLastRelUp << "(MaxResidSinceLastRelUp) on iteration " << k << " : performing reliable update\n";
|
||||
precisionChange(mmp, psi_f);
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(mmp, psi_f, pc_wk_sp_to_dp);
|
||||
PrecChangeTimer.Stop();
|
||||
psi = psi + mmp;
|
||||
|
||||
MatrixTimer.Start();
|
||||
Linop_d.HermOpAndNorm(psi, mmp, d, qq);
|
||||
MatrixTimer.Stop();
|
||||
|
||||
r = src - mmp;
|
||||
|
||||
psi_f = Zero();
|
||||
precisionChange(r_f, r);
|
||||
PrecChangeTimer.Start();
|
||||
precisionChange(r_f, r, pc_wk_dp_to_sp);
|
||||
PrecChangeTimer.Stop();
|
||||
cp = norm2(r);
|
||||
MaxResidSinceLastRelUp = cp;
|
||||
|
||||
@@ -244,7 +263,7 @@ public:
|
||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate did NOT converge"
|
||||
<< std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
||||
IterationsToComplete = k;
|
||||
ReliableUpdatesPerformed = l;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ConjugateGradientTimeslice.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution
|
||||
directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_CONJUGATE_GRADIENT_TIMESLICE_H
|
||||
#define GRID_CONJUGATE_GRADIENT_TIMESLICE_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Base classes for iterative processes based on operators
|
||||
// single input vec, single output vec.
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Simple modification of conjugate gradient that outputs the residual as a function
|
||||
* of time, in order to study the large wavelength behavior of the solver.
|
||||
*/
|
||||
|
||||
|
||||
template <class Field>
|
||||
class ConjugateGradientTimeslice : public OperatorFunction<Field> {
|
||||
public:
|
||||
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||
// Defaults true.
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||
RealD TrueResidual;
|
||||
|
||||
ConjugateGradientTimeslice(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
||||
: Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
ErrorOnNoConverge(err_on_no_conv)
|
||||
{};
|
||||
|
||||
virtual void LogIteration(int k,RealD a,RealD b){
|
||||
// std::cout << "ConjugageGradient::LogIteration() "<<std::endl;
|
||||
};
|
||||
virtual void LogBegin(void){
|
||||
std::cout << "ConjugageGradient::LogBegin() "<<std::endl;
|
||||
};
|
||||
|
||||
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
||||
|
||||
this->LogBegin();
|
||||
|
||||
GRID_TRACE("ConjugateGradientTimeslice");
|
||||
GridStopWatch PreambleTimer;
|
||||
GridStopWatch ConstructTimer;
|
||||
GridStopWatch NormTimer;
|
||||
GridStopWatch AssignTimer;
|
||||
PreambleTimer.Start();
|
||||
psi.Checkerboard() = src.Checkerboard();
|
||||
|
||||
conformable(psi, src);
|
||||
|
||||
RealD cp, c, a, d, b, ssq, qq;
|
||||
//RealD b_pred;
|
||||
|
||||
// Was doing copies
|
||||
ConstructTimer.Start();
|
||||
Field p (src.Grid());
|
||||
Field mmp(src.Grid());
|
||||
Field r (src.Grid());
|
||||
ConstructTimer.Stop();
|
||||
|
||||
// Initial residual computation & set up
|
||||
NormTimer.Start();
|
||||
ssq = norm2(src); // Norm of source vector ||b||^2
|
||||
|
||||
ssqtx = localNorm2(src); // Norm |b(x, t)|^2 as a field
|
||||
std::vector<RealD> ssqt; // Norm of source not summed over time slices, ssq(t) = \sum_x |b(x, t)|^2
|
||||
sliceSum(ssqtx, ssqt, Tdir); // TODO make sure Tdir is globally defined
|
||||
|
||||
RealD guess = norm2(psi); // Norm of initial guess ||psi||^2
|
||||
NormTimer.Stop();
|
||||
assert(std::isnan(guess) == 0);
|
||||
AssignTimer.Start();
|
||||
if ( guess == 0.0 ) {
|
||||
r = src;
|
||||
p = r;
|
||||
a = ssq;
|
||||
} else {
|
||||
Linop.HermOpAndNorm(psi, mmp, d, b); //
|
||||
r = src - mmp; // Initial residual r0 = b - A guess
|
||||
p = r; // initial conj vector p0 = r0
|
||||
a = norm2(p);
|
||||
}
|
||||
cp = a;
|
||||
AssignTimer.Stop();
|
||||
|
||||
// Handle trivial case of zero src
|
||||
if (ssq == 0.){
|
||||
psi = Zero();
|
||||
IterationsToComplete = 1;
|
||||
TrueResidual = 0.;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: guess " << guess << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: src " << ssq << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: mp " << d << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: mmp " << b << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: cp,r " << cp << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: p " << a << std::endl;
|
||||
|
||||
RealD rsq = Tolerance * Tolerance * ssq;
|
||||
|
||||
// Check if guess is really REALLY good :)
|
||||
if (cp <= rsq) {
|
||||
TrueResidual = std::sqrt(a/ssq);
|
||||
std::cout << GridLogMessage << "ConjugateGradient guess is converged already " << std::endl;
|
||||
IterationsToComplete = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << GridLogIterative << std::setprecision(8)
|
||||
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
||||
|
||||
PreambleTimer.Stop();
|
||||
GridStopWatch LinalgTimer;
|
||||
GridStopWatch InnerTimer;
|
||||
GridStopWatch AxpyNormTimer;
|
||||
GridStopWatch LinearCombTimer;
|
||||
GridStopWatch MatrixTimer;
|
||||
GridStopWatch SolverTimer;
|
||||
|
||||
RealD usecs = -usecond();
|
||||
SolverTimer.Start();
|
||||
int k;
|
||||
for (k = 1; k <= MaxIterations; k++) {
|
||||
|
||||
GridStopWatch IterationTimer;
|
||||
IterationTimer.Start();
|
||||
c = cp;
|
||||
|
||||
MatrixTimer.Start();
|
||||
Linop.HermOp(p, mmp); // Computes mmp = Ap
|
||||
MatrixTimer.Stop();
|
||||
|
||||
LinalgTimer.Start();
|
||||
|
||||
InnerTimer.Start();
|
||||
ComplexD dc = innerProduct(p,mmp); // p^\dagger A p
|
||||
InnerTimer.Stop();
|
||||
d = dc.real();
|
||||
a = c / d;
|
||||
|
||||
// What is axpy? Some accelerator or something? Check Lattice_arith.h
|
||||
AxpyNormTimer.Start();
|
||||
|
||||
// axpy_norm computes ax+by for vectors x and y compatible with a GPU. Here b is set to 1 (see the function in Lattice_reduction.h).
|
||||
// The first argument passes r by reference, so it stores r --> -a * Ap + 1 * r, i.e. it performs an update on
|
||||
// r_k --> r_{k+1} = r_k - \alpha_k A p_k. The function returns the norm squared of the first variable, i.e. ||r_{k+1}||^2.
|
||||
cp = axpy_norm(r, -a, mmp, r);
|
||||
AxpyNormTimer.Stop();
|
||||
b = cp / c;
|
||||
|
||||
LinearCombTimer.Start();
|
||||
{
|
||||
autoView( psi_v , psi, AcceleratorWrite);
|
||||
autoView( p_v , p, AcceleratorWrite);
|
||||
autoView( r_v , r, AcceleratorWrite);
|
||||
accelerator_for(ss,p_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(psi_v[ss], a * p_v(ss) + psi_v(ss));
|
||||
coalescedWrite(p_v[ss] , b * p_v(ss) + r_v (ss));
|
||||
});
|
||||
}
|
||||
LinearCombTimer.Stop();
|
||||
LinalgTimer.Stop();
|
||||
LogIteration(k,a,b);
|
||||
|
||||
IterationTimer.Stop();
|
||||
if ( (k % 500) == 0 ) {
|
||||
std::cout << GridLogMessage << "ConjugateGradient: Iteration " << k
|
||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << std::endl;
|
||||
} else {
|
||||
std::cout << GridLogIterative << "ConjugateGradient: Iteration " << k
|
||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << " took " << IterationTimer.Elapsed() << std::endl;
|
||||
}
|
||||
|
||||
// Stopping condition
|
||||
if (cp <= rsq) {
|
||||
usecs +=usecond();
|
||||
SolverTimer.Stop();
|
||||
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
p = mmp - src;
|
||||
GridBase *grid = src.Grid();
|
||||
RealD DwfFlops = (1452. )*grid->gSites()*4*k
|
||||
+ (8+4+8+4+4)*12*grid->gSites()*k; // CG linear algebra
|
||||
RealD srcnorm = std::sqrt(norm2(src));
|
||||
RealD resnorm = std::sqrt(norm2(p));
|
||||
RealD true_residual = resnorm / srcnorm;
|
||||
std::cout << GridLogMessage << "ConjugateGradient Converged on iteration " << k
|
||||
<< "\tComputed residual " << std::sqrt(cp / ssq)
|
||||
<< "\tTrue residual " << true_residual
|
||||
<< "\tTarget " << Tolerance << std::endl;
|
||||
|
||||
// GridLogMessage logs the message to the terminal output; GridLogPerformance probably writes to a log file?
|
||||
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver Elapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "Time breakdown "<<std::endl;
|
||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
|
||||
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
||||
|
||||
IterationsToComplete = k;
|
||||
TrueResidual = true_residual;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Failed. Calculate true residual before giving up
|
||||
// Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
// p = mmp - src;
|
||||
//TrueResidual = sqrt(norm2(p)/ssq);
|
||||
// TrueResidual = 1;
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradient did NOT converge "<<k<<" / "<< MaxIterations
|
||||
<<" residual "<< std::sqrt(cp / ssq)<< std::endl;
|
||||
SolverTimer.Stop();
|
||||
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "Solver breakdown "<<std::endl;
|
||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage<< "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
IterationsToComplete = k;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
}
|
||||
|
||||
std::cout<<GridLogMessage<<"ConjugateResidual did NOT converge"<<std::endl;
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
};
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -36,7 +36,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
|
||||
public:
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // Throw an assert when FCAGMRES fails to converge,
|
||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when FCAGMRES fails to converge,
|
||||
// defaults to true
|
||||
|
||||
RealD Tolerance;
|
||||
@@ -87,7 +87,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
|
||||
conformable(psi, src);
|
||||
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
RealD cp;
|
||||
RealD ssq = norm2(src);
|
||||
@@ -144,7 +144,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
|
||||
std::cout << GridLogMessage << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
||||
@@ -191,7 +191,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
|
||||
}
|
||||
}
|
||||
|
||||
assert(0); // Never reached
|
||||
GRID_ASSERT(0); // Never reached
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
public:
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // Throw an assert when FGMRES fails to converge,
|
||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when FGMRES fails to converge,
|
||||
// defaults to true
|
||||
|
||||
RealD Tolerance;
|
||||
@@ -85,7 +85,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
conformable(psi, src);
|
||||
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
RealD cp;
|
||||
RealD ssq = norm2(src);
|
||||
@@ -142,7 +142,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
std::cout << GridLogMessage << "FlexibleGeneralisedMinimalResidual did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
||||
@@ -189,7 +189,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
}
|
||||
}
|
||||
|
||||
assert(0); // Never reached
|
||||
GRID_ASSERT(0); // Never reached
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,230 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
γ5-Scalar Lanczos algorithm for γ5-Hermitian operators (Wilson Dirac).
|
||||
|
||||
Single-vector Lanczos using the γ5-inner product (u,v)_{γ5} = u†γ5v.
|
||||
Builds the Krylov space {q, D_W q, D_W² q, ...} and projects D_W onto it,
|
||||
producing a real tridiagonal matrix T_m whose eigenvalues approximate those
|
||||
of D_W. Complex conjugate pairs of D_W eigenvalues appear as complex
|
||||
eigenvalues of the real non-symmetric T_m.
|
||||
|
||||
Recurrence (G_k = sign(q_k†γ5 q_k) = ±1):
|
||||
r_k = D_W q_k − α_k q_k − β_sup[k-1] q_{k-1}
|
||||
β_sub[k] = √|r_k†γ5 r_k| (lower subdiag T_{k+1,k})
|
||||
β_sup[k] = β_sub[k] G_{k+1}/G_k (upper superdiag T_{k,k+1})
|
||||
q_{k+1} = r_k / β_sub[k]
|
||||
|
||||
T_m is real but not symmetric in general; eigenvalues via EigenSolver.
|
||||
|
||||
*************************************************************************************/
|
||||
#ifndef GRID_GAMMA5_SCALAR_LANCZOS_H
|
||||
#define GRID_GAMMA5_SCALAR_LANCZOS_H
|
||||
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <iomanip>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class Field>
|
||||
class Gamma5ScalarLanczos {
|
||||
public:
|
||||
using Gamma5Func = std::function<void(const Field&, Field&)>;
|
||||
|
||||
private:
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
typedef Eigen::MatrixXd RMat;
|
||||
|
||||
LinearOperatorBase<Field>& Linop;
|
||||
GridBase* Grid_;
|
||||
Gamma5Func applyGamma5;
|
||||
RealD Tolerance;
|
||||
int nSteps;
|
||||
|
||||
std::vector<Field> basis;
|
||||
std::vector<RealD> alpha_; // diagonal of T_m
|
||||
std::vector<RealD> beta_sub_; // lower subdiagonal: T_{k+1,k}
|
||||
std::vector<RealD> beta_sup_; // upper superdiagonal: T_{k,k+1}
|
||||
std::vector<int> gsign_; // G_k = ±1
|
||||
|
||||
CVec evals_;
|
||||
std::vector<Field> evecs_;
|
||||
std::vector<RealD> residuals_;
|
||||
|
||||
public:
|
||||
bool doEvalCheck = false;
|
||||
|
||||
Gamma5ScalarLanczos(LinearOperatorBase<Field>& op, GridBase* grid,
|
||||
Gamma5Func g5, RealD tol = 1e-8)
|
||||
: Linop(op), Grid_(grid), applyGamma5(g5), Tolerance(tol), nSteps(0)
|
||||
{}
|
||||
|
||||
CVec getEvals() { return evals_; }
|
||||
std::vector<Field> getEvecs() { return evecs_; }
|
||||
std::vector<RealD> getResiduals() { return residuals_; }
|
||||
|
||||
// v0 : starting vector
|
||||
// maxSteps : maximum Lanczos steps
|
||||
// Nstop : target Ritz pairs to return (0 = all)
|
||||
// reorthog : full γ5-reorthogonalisation at each step
|
||||
// filter : eigenvalue selection criterion
|
||||
void operator()(const Field& v0, int maxSteps, int Nstop,
|
||||
bool reorthog = false, RitzFilter filter = EvalImNormSmall)
|
||||
{
|
||||
basis.clear(); alpha_.clear(); beta_sub_.clear(); beta_sup_.clear(); gsign_.clear();
|
||||
nSteps = 0;
|
||||
|
||||
// Normalise starting vector in γ5-norm: q_0 = v0 / √|v0†γ5 v0|
|
||||
Field q(Grid_); q = v0;
|
||||
Field g5q(Grid_);
|
||||
applyGamma5(q, g5q);
|
||||
RealD omega = std::real(toStdCmplx(innerProduct(g5q, q)));
|
||||
RealD beta0 = std::sqrt(std::abs(omega));
|
||||
assert(beta0 > 1e-14 && "starting vector has zero γ5-norm");
|
||||
q *= (1.0 / beta0);
|
||||
gsign_.push_back(omega >= 0 ? +1 : -1);
|
||||
basis.push_back(q);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: start (v0†γ5 v0)=" << omega
|
||||
<< " G[0]=" << gsign_[0] << std::endl;
|
||||
|
||||
for (int k = 0; k < maxSteps; k++) {
|
||||
// ── Apply operator ─────────────────────────────────────────────────
|
||||
Field p(Grid_);
|
||||
Linop.Op(basis[k], p);
|
||||
|
||||
// ── α_k = (q_k†γ5 D_W q_k) / G_k (real) ─────────────────────────
|
||||
applyGamma5(basis[k], g5q);
|
||||
RealD a = std::real(toStdCmplx(innerProduct(g5q, p))) / RealD(gsign_[k]);
|
||||
alpha_.push_back(a);
|
||||
|
||||
// ── r = D_W q_k − α_k q_k − β_sup[k-1] q_{k-1} ───────────────────
|
||||
Field r(Grid_); r = p;
|
||||
r -= basis[k] * a;
|
||||
if (k > 0)
|
||||
r -= basis[k-1] * beta_sup_[k-1];
|
||||
|
||||
// ── Optional γ5-reorthogonalisation ────────────────────────────────
|
||||
if (reorthog) {
|
||||
for (int i = 0; i <= k; i++) {
|
||||
applyGamma5(basis[i], g5q);
|
||||
ComplexD c = toStdCmplx(innerProduct(g5q, r)) / RealD(gsign_[i]);
|
||||
r -= basis[i] * c;
|
||||
}
|
||||
}
|
||||
|
||||
// ── β_sub[k] = √|r†γ5 r| ───────────────────────────────────────────
|
||||
applyGamma5(r, g5q);
|
||||
omega = std::real(toStdCmplx(innerProduct(g5q, r)));
|
||||
RealD beta = std::sqrt(std::abs(omega));
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: k=" << k
|
||||
<< " alpha=" << a
|
||||
<< " beta=" << beta
|
||||
<< " G[k]=" << gsign_[k]
|
||||
<< " (r†γ5r)=" << omega << std::endl;
|
||||
|
||||
beta_sub_.push_back(beta);
|
||||
|
||||
if (beta < Tolerance) {
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: invariant subspace at step " << k << std::endl;
|
||||
nSteps = k + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
int G_next = (omega >= 0) ? +1 : -1;
|
||||
gsign_.push_back(G_next);
|
||||
// β_sup[k] = T_{k,k+1} = β_sub[k] * G_{k+1} / G_k
|
||||
beta_sup_.push_back(beta * RealD(G_next) / RealD(gsign_[k]));
|
||||
|
||||
Field qnew(Grid_); qnew = r; qnew *= (1.0 / beta);
|
||||
basis.push_back(qnew);
|
||||
nSteps = k + 1;
|
||||
}
|
||||
|
||||
if (nSteps == 0) return;
|
||||
computeRitzPairs(nSteps, Nstop, filter);
|
||||
}
|
||||
|
||||
private:
|
||||
void computeRitzPairs(int m, int Nstop, RitzFilter filter)
|
||||
{
|
||||
// Assemble real tridiagonal T_m
|
||||
RMat Tm = RMat::Zero(m, m);
|
||||
for (int k = 0; k < m; k++) {
|
||||
Tm(k, k) = alpha_[k];
|
||||
if (k + 1 < m) {
|
||||
Tm(k+1, k) = beta_sub_[k];
|
||||
Tm(k, k+1) = beta_sup_[k];
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: T_m (" << m << "×" << m << "):" << std::endl;
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < m; j++)
|
||||
std::cout << " " << std::setprecision(8) << std::setw(16) << Tm(i,j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
Eigen::EigenSolver<RMat> es(Tm);
|
||||
CVec lambdas = es.eigenvalues();
|
||||
CMat Y = es.eigenvectors();
|
||||
|
||||
// Sort by filter criterion
|
||||
ComplexComparator cComp(filter);
|
||||
std::vector<int> idx(m);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(lambdas(a)), toStdCmplx(lambdas(b)));
|
||||
});
|
||||
|
||||
int Nout = (Nstop > 0 && Nstop < m) ? Nstop : m;
|
||||
evals_.resize(Nout);
|
||||
evecs_.clear(); evecs_.reserve(Nout);
|
||||
residuals_.clear(); residuals_.reserve(Nout);
|
||||
|
||||
// Trailing β for residual estimate: β_sub[m-1] * |y_j[m-1]|
|
||||
RealD beta_trail = (m - 1 < (int)beta_sub_.size()) ? beta_sub_[m-1] : 0.0;
|
||||
|
||||
for (int ji = 0; ji < Nout; ji++) {
|
||||
int j = idx[ji];
|
||||
evals_(ji) = lambdas(j);
|
||||
CVec yj = Y.col(j);
|
||||
|
||||
// Ritz vector: u_j = Σ_k q_k y_j[k]
|
||||
Field uj(Grid_); uj = Zero();
|
||||
for (int k = 0; k < m && k < (int)basis.size(); k++)
|
||||
uj += basis[k] * toStdCmplx(yj(k));
|
||||
evecs_.push_back(uj);
|
||||
|
||||
RealD res = beta_trail * std::abs(toStdCmplx(yj(m-1)));
|
||||
if (!std::isfinite(res)) res = std::numeric_limits<RealD>::infinity();
|
||||
residuals_.push_back(res);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< " Ritz[" << std::setw(3) << ji << "]"
|
||||
<< " lambda=" << evals_(ji)
|
||||
<< " |res|=" << res;
|
||||
|
||||
if (doEvalCheck) {
|
||||
Field w(Grid_);
|
||||
Linop.Op(uj, w);
|
||||
w -= uj * evals_(ji);
|
||||
RealD actual = std::sqrt(norm2(w));
|
||||
std::cout << " ||D_W u - λu||=" << actual;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_GAMMA5_SCALAR_LANCZOS_H
|
||||
@@ -36,7 +36,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
public:
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // Throw an assert when GMRES fails to converge,
|
||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when GMRES fails to converge,
|
||||
// defaults to true
|
||||
|
||||
RealD Tolerance;
|
||||
@@ -80,7 +80,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
conformable(psi, src);
|
||||
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
RealD cp;
|
||||
RealD ssq = norm2(src);
|
||||
@@ -135,7 +135,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
std::cout << GridLogMessage << "GeneralisedMinimalResidual did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
||||
@@ -181,7 +181,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
|
||||
}
|
||||
}
|
||||
|
||||
assert(0); // Never reached
|
||||
GRID_ASSERT(0); // Never reached
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,719 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/HarmonicBlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
#define GRID_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Block shift-targeted Krylov-Schur eigensolver.
|
||||
*
|
||||
* Algorithm
|
||||
* ---------
|
||||
* Uses a block Arnoldi factorisation:
|
||||
*
|
||||
* A V = V H + F B^dag (1)
|
||||
*
|
||||
* with V orthonormal (Nm columns), H the Nm×Nm block upper-Hessenberg
|
||||
* Rayleigh quotient, F the Nblock residual vectors and B the Nm×Nblock
|
||||
* coupling matrix.
|
||||
*
|
||||
* Thick restart
|
||||
* -------------
|
||||
* To target eigenvalues near shift σ, the Schur decomposition is computed
|
||||
* for the shifted Rayleigh quotient:
|
||||
*
|
||||
* (H - σI) = Q^dag S Q (2)
|
||||
*
|
||||
* Sorting the Schur values of (H - σI) by smallest |S(i,i)| = |λ - σ| and
|
||||
* retaining the leading Nk is equivalent to selecting the Ritz values of H
|
||||
* closest to σ. Since Q diagonalises H - σI (and hence H itself), the
|
||||
* rotated Rayleigh quotient is exactly upper triangular:
|
||||
*
|
||||
* H_new = Q H Q^dag = S + σI (upper triangular) (3)
|
||||
*
|
||||
* Truncation to Nk is therefore exact: the off-diagonal coupling block
|
||||
* H_new[Nk:, :Nk] = 0 by triangularity. The Krylov-Schur relation after
|
||||
* restart is exact and block Arnoldi resumes cleanly from F.
|
||||
*
|
||||
* Convergence
|
||||
* -----------
|
||||
* Convergence is declared when || B^H y_k || < Tolerance * approxLambdaMax
|
||||
* for each Ritz pair (λ_k, y_k) of the truncated H.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* shift : target shift σ (default 0.0); Schur values sorted by |λ - σ|
|
||||
* Nblock : block size p
|
||||
* Nm : total Krylov dimension (must be divisible by Nblock)
|
||||
* Nk : total vectors to retain after each restart (must be divisible by Nblock, Nk < Nm)
|
||||
* Nstop : stop when this many eigenpairs converge
|
||||
* MaxIter : maximum outer (restart) iterations
|
||||
* Tolerance: relative convergence tolerance
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
* HarmonicBlockKrylovSchur<Field> hbks(LinOp, Grid, tol, shift, EvalNormSmall);
|
||||
* std::vector<Field> v0(Nblock, Field(Grid));
|
||||
* // fill v0 with random starting vectors
|
||||
* hbks(v0, maxIter, Nm, Nk, Nstop, Nblock);
|
||||
* auto evals = hbks.getEvals();
|
||||
* auto evecs = hbks.getEvecs();
|
||||
*/
|
||||
template<class Field>
|
||||
class HarmonicBlockKrylovSchur {
|
||||
|
||||
protected:
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Parameters
|
||||
//--------------------------------------------------------------------
|
||||
int Nblock;
|
||||
int Nm; // total Krylov dimension (multiple of Nblock)
|
||||
int Nk; // total vectors retained after restart (multiple of Nblock)
|
||||
int Nstop;
|
||||
int MaxIter;
|
||||
RealD Tolerance;
|
||||
ComplexD shift; // target shift σ
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Internal state
|
||||
//--------------------------------------------------------------------
|
||||
LinearOperatorBase<Field>& Linop;
|
||||
GridBase* Grid_;
|
||||
RitzFilter ritzFilter;
|
||||
|
||||
std::vector<Field> basis; // Nm*Nblock flat basis
|
||||
CMat H; // (Nm*Nblock)² block-Hessenberg Rayleigh quotient
|
||||
std::vector<Field> F; // Nblock residual vectors
|
||||
CMat B; // (Nm*Nblock) × Nblock coupling matrix
|
||||
RealD beta_k;
|
||||
RealD rtol;
|
||||
|
||||
CVec evals;
|
||||
CMat littleEvecs;
|
||||
std::vector<RealD> ritzEstimates;
|
||||
|
||||
public:
|
||||
std::vector<Field> evecs;
|
||||
bool doEvalCheck = false;
|
||||
bool useParityFlip = false;
|
||||
bool useGamma5 = false;
|
||||
std::function<void(const Field&, Field&)> gamma5Func;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------------------------------------
|
||||
HarmonicBlockKrylovSchur(LinearOperatorBase<Field>& _Linop, GridBase* _Grid,
|
||||
RealD _Tolerance, ComplexD _shift = 0.0,
|
||||
RitzFilter _rf = EvalNormSmall)
|
||||
: Linop(_Linop), Grid_(_Grid), Tolerance(_Tolerance), shift(_shift),
|
||||
ritzFilter(_rf),
|
||||
Nblock(-1), Nm(-1), Nk(-1), Nstop(-1), MaxIter(-1),
|
||||
beta_k(0.0), rtol(0.0)
|
||||
{}
|
||||
|
||||
virtual ~HarmonicBlockKrylovSchur() = default;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point
|
||||
//--------------------------------------------------------------------
|
||||
virtual void operator()(const std::vector<Field>& v0, int _maxIter, int _Nm, int _Nk,
|
||||
int _Nstop, int _Nblock = 1, bool doubleOrthog = true,
|
||||
bool doVerify = false)
|
||||
{
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm;
|
||||
Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
Nblock = _Nblock;
|
||||
|
||||
{
|
||||
int divisor = 1;
|
||||
if (useParityFlip) divisor *= 2;
|
||||
if (useGamma5) divisor *= 2;
|
||||
assert(Nblock % divisor == 0 && (int)v0.size() >= Nblock / divisor);
|
||||
std::cout << GridLogMessage << "divisor= " << divisor << std::endl;
|
||||
|
||||
}
|
||||
assert(Nm % Nblock == 0);
|
||||
assert(Nk % Nblock == 0);
|
||||
assert(Nk < Nm);
|
||||
if (useGamma5) assert(gamma5Func && "useGamma5: gamma5Func must be set");
|
||||
|
||||
int N = Nm;
|
||||
|
||||
RealD approxLambdaMax = approxMaxEval(v0[0]);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: approx max eval = " << approxLambdaMax
|
||||
<< ", rtol = " << rtol
|
||||
<< ", shift = " << shift << std::endl;
|
||||
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
int start = 0;
|
||||
std::vector<Field> startBlock = expandStartBlock(v0);
|
||||
|
||||
for (int iter = 0; iter < MaxIter; iter++) {
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: restart iteration " << iter << std::endl;
|
||||
|
||||
// ---- Block Arnoldi: extend from block 'start' to block Nm/Nblock ----
|
||||
blockArnoldiIteration(startBlock, Nm/Nblock, start, doubleOrthog);
|
||||
start = Nk/Nblock;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after Arnoldi";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Schur decompose (H - σI) to select Schur vectors closest to σ ----
|
||||
// Sorting the Schur values of (H - σI) by |S(i,i)| = |λ - σ| gives the
|
||||
// Ritz values of H nearest the target shift without any matrix inversion.
|
||||
// Because Q diagonalises (H - σI), it also diagonalises H:
|
||||
// H_new = Q H Q^dag = S + σI (upper triangular)
|
||||
// Truncation is therefore exact.
|
||||
CMat Hshift = H - shift * CMat::Identity(N, N);
|
||||
ComplexSchurDecomposition schur(Hshift, false, ritzFilter);
|
||||
schur.schurReorder(Nk);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: Ritz values nearest shift (first Nk):" << std::endl;
|
||||
CMat S = schur.getMatrixS();
|
||||
for (int i = 0; i < Nk; i++)
|
||||
std::cout << GridLogMessage << " [" << i << "] " << S(i, i) + shift << std::endl;
|
||||
|
||||
CMat Q = schur.getMatrixQ();
|
||||
CMat Qt = Q.adjoint();
|
||||
|
||||
// ---- Rotate Krylov basis ----
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, N);
|
||||
basis = basis2;
|
||||
|
||||
// ---- Update H and B ----
|
||||
// H_new = S + σI is upper triangular; off-diagonal block H_new[Nk:,:Nk] = 0
|
||||
H = S + shift * CMat::Identity(N, N);
|
||||
B = Q * B;
|
||||
|
||||
// ---- Truncate to Nk (exact: H upper triangular) ----
|
||||
int Nkeep = Nk;
|
||||
|
||||
CMat Htmp = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
H = CMat::Zero(N, N);
|
||||
H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep)) = Htmp;
|
||||
|
||||
std::vector<Field> basisTmp(basis.begin(), basis.begin() + Nkeep);
|
||||
basis = basisTmp;
|
||||
|
||||
CMat Btmp = B(Eigen::seqN(0, Nkeep), Eigen::all);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
B(Eigen::seqN(0, Nkeep), Eigen::all) = Btmp;
|
||||
|
||||
beta_k = Btmp.norm();
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: beta_k = " << beta_k << std::endl;
|
||||
|
||||
// Restart from F (exact: no discarded-basis correction needed)
|
||||
startBlock = F;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after restart+truncation";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Eigensystem of truncated H for convergence ----
|
||||
CMat Hk = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
computeEigensystem(Hk, Nkeep);
|
||||
|
||||
int Nconv = converged(Nkeep);
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: converged " << Nconv
|
||||
<< " / " << Nstop << std::endl;
|
||||
|
||||
if (Nconv >= Nstop || iter == MaxIter - 1) {
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: done after " << iter
|
||||
<< " restarts, " << Nconv << " converged." << std::endl;
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << evals.transpose() << std::endl;
|
||||
|
||||
if (doEvalCheck) {
|
||||
Field w(Grid_);
|
||||
for (int k = 0; k < (int)evecs.size(); k++) {
|
||||
Linop.Op(evecs[k], w);
|
||||
ComplexD eval_est = toStdCmplx(innerProduct(evecs[k], w));
|
||||
w -= eval_est * evecs[k];
|
||||
RealD res = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage << "HarmonicBlockKrylovSchur: evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
CVec getEvals() { return evals; }
|
||||
std::vector<RealD> getRitzEstimates() { return ritzEstimates; }
|
||||
|
||||
static void parityFlippedField(const Field& v, Field& out) {
|
||||
GridCartesian* cgrid = dynamic_cast<GridCartesian*>(v.Grid());
|
||||
assert(cgrid != nullptr);
|
||||
GridRedBlackCartesian rbgrid(cgrid);
|
||||
Field veven(&rbgrid), vodd(&rbgrid);
|
||||
pickCheckerboard(Even, veven, v);
|
||||
pickCheckerboard(Odd, vodd, v);
|
||||
vodd = -vodd;
|
||||
setCheckerboard(out, veven);
|
||||
setCheckerboard(out, vodd);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Verification: check A V = V H + F B^dag explicitly
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Checks the block Arnoldi / Krylov-Schur decomposition
|
||||
*
|
||||
* A V = V H + F B^dag (KS)
|
||||
*
|
||||
* by explicit operator applications. H here is the standard Rayleigh
|
||||
* quotient, so the KS relation is the same as for BlockKrylovSchur.
|
||||
*
|
||||
* Prints:
|
||||
* - H (current Rayleigh quotient, nBasis × nBasis)
|
||||
* - B (coupling matrix, nBasis × Nblock)
|
||||
* - M (explicit inner product matrix <V | A V>)
|
||||
* - max |H[i,j] - M[i,j]| (should be O(machine epsilon))
|
||||
* - max |<V_i|V_j> - delta_ij| (orthonormality check)
|
||||
* - for each basis column j: || A v_j - V H[:,j] - F B[j,:]^* ||
|
||||
* - max |<V_i | F_t>| (F orthogonal to basis)
|
||||
*/
|
||||
void verify(const std::string& label = "")
|
||||
{
|
||||
int nBasis = (int)basis.size();
|
||||
int nF = (int)F.size();
|
||||
|
||||
if (nBasis == 0) {
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur::verify [" << label
|
||||
<< "]: basis is empty." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== HarmonicBlockKrylovSchur::verify [" << label << "] ========" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " nBasis = " << nBasis << " Nblock = " << Nblock
|
||||
<< " nF = " << nF << std::endl;
|
||||
|
||||
// ---- Print H ----
|
||||
std::cout << GridLogMessage << "H (" << nBasis << " x " << nBasis << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << H(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Print B ----
|
||||
std::cout << GridLogMessage << "B (" << nBasis << " x " << nF << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int t = 0; t < nF; t++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << B(i, t);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Compute M[i,j] = <basis[i] | A basis[j]> ----
|
||||
CMat M = CMat::Zero(nBasis, nBasis);
|
||||
Field w(Grid_);
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
M(i, j) = toStdCmplx(innerProduct(basis[i], w));
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "M = <V|AV> (" << nBasis << " x " << nBasis << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << M(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- max |H - M| ----
|
||||
RealD maxHM = 0.0;
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
maxHM = std::max(maxHM, std::abs(H(i,j) - M(i,j)));
|
||||
std::cout << GridLogMessage
|
||||
<< " max |H[i,j] - M[i,j]| = " << maxHM << std::endl;
|
||||
|
||||
// ---- Check orthonormality of basis ----
|
||||
CMat G = CMat::Zero(nBasis, nBasis);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
G(i, j) = toStdCmplx(innerProduct(basis[i], basis[j]));
|
||||
CMat Gerr = G - CMat::Identity(nBasis, nBasis);
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i|V_j> - delta_ij| = " << Gerr.cwiseAbs().maxCoeff() << std::endl;
|
||||
|
||||
// ---- Per-column residual: || A v_j - V H[:,j] - F B[j,:]^* || ----
|
||||
RealD maxColDev = 0.0;
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
|
||||
// subtract V H[:,j]
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
w -= basis[i] * H(i, j);
|
||||
|
||||
// subtract F B[j,:]^* (F[t] * conj(B[j,t]))
|
||||
for (int t = 0; t < nF; t++)
|
||||
w -= F[t] * std::conj(B(j, t));
|
||||
|
||||
RealD dev = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage
|
||||
<< " || A v[" << j << "] - V H[:,j] - F B[j,:]* || = " << dev << std::endl;
|
||||
maxColDev = std::max(maxColDev, dev);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max column deviation = " << maxColDev << std::endl;
|
||||
|
||||
// ---- Check F block orthogonality against basis ----
|
||||
if (nF > 0) {
|
||||
RealD maxFV = 0.0;
|
||||
for (int t = 0; t < nF; t++)
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
RealD ip = std::abs(toStdCmplx(innerProduct(basis[i], F[t])));
|
||||
maxFV = std::max(maxFV, ip);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i | F_t>| (should be ~0) = " << maxFV << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== end verify ========" << std::endl;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Starting-block expansion (parity flip / gamma5 partners)
|
||||
//--------------------------------------------------------------------
|
||||
// Expands Nblock/divisor seed vectors into the full Nblock starting
|
||||
// block, pairing each seed with its parity-flipped and/or gamma5
|
||||
// partners. Requires Nblock, Grid_ and the flags to be set.
|
||||
std::vector<Field> expandStartBlock(const std::vector<Field>& v0)
|
||||
{
|
||||
int divisor = (useParityFlip ? 2 : 1) * (useGamma5 ? 2 : 1);
|
||||
std::vector<Field> startBlock;
|
||||
startBlock.reserve(Nblock);
|
||||
for (int i = 0; i < Nblock / divisor; i++) {
|
||||
std::vector<Field> group;
|
||||
group.push_back(v0[i]);
|
||||
if (useParityFlip) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field fp(Grid_);
|
||||
parityFlippedField(group[j], fp);
|
||||
group.push_back(std::move(fp));
|
||||
}
|
||||
}
|
||||
if (useGamma5) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field g5v(Grid_);
|
||||
gamma5Func(group[j], g5v);
|
||||
group.push_back(std::move(g5v));
|
||||
}
|
||||
}
|
||||
for (auto& f : group) startBlock.push_back(std::move(f));
|
||||
}
|
||||
return startBlock;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block Arnoldi iteration
|
||||
//--------------------------------------------------------------------
|
||||
void blockArnoldiIteration(std::vector<Field>& startBlock, int endBlock,
|
||||
int startIdx, bool doubleOrthog)
|
||||
{
|
||||
int N = Nm;
|
||||
|
||||
if (startIdx == 0) {
|
||||
basis.clear();
|
||||
F.clear();
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
std::vector<Field> V0 = startBlock;
|
||||
blockOrthonormalise(V0);
|
||||
for (auto& v : V0) basis.push_back(v);
|
||||
} else {
|
||||
// Append the new starting block to the retained basis.
|
||||
//
|
||||
// The exact truncated KS relation is A V_k = V_k H_k + F B_k^dag,
|
||||
// so the coupling rows are H[Nkeep+t, j] = conj(B_k[j,t]).
|
||||
// Since H_new = S + σI is upper triangular, the off-diagonal block
|
||||
// H_new[Nkeep:, :Nkeep] = 0 and the restart from F is exact.
|
||||
int Nkeep = startIdx * Nblock;
|
||||
for (auto& v : startBlock) basis.push_back(v);
|
||||
|
||||
// Fill restart coupling rows into H (exact: H_new is upper triangular,
|
||||
// so B encodes the only non-zero coupling to the new block).
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
H(Nkeep + t, j) = std::conj(B(j, t));
|
||||
|
||||
// Zero out B for the retained columns now that the coupling is in H
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
B(j, t) = 0.0;
|
||||
}
|
||||
|
||||
for (int k = startIdx; k < endBlock; k++)
|
||||
blockArnoldiStep(k, doubleOrthog);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// One block Arnoldi step
|
||||
//--------------------------------------------------------------------
|
||||
void blockArnoldiStep(int k, bool doubleOrthog)
|
||||
{
|
||||
int kBase = k * Nblock;
|
||||
int prevN = kBase + Nblock;
|
||||
int N = Nm;
|
||||
|
||||
std::vector<Field> W(Nblock, Field(Grid_));
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
Linop.Op(basis[kBase + t], W[t]);
|
||||
|
||||
// Full reorthogonalisation against all current basis vectors
|
||||
for (int pass = 0; pass < (doubleOrthog ? 2 : 1); pass++) {
|
||||
for (int i = 0; i < prevN; i++) {
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
ComplexD coeff = innerProduct(basis[i], W[t]);
|
||||
if (pass == 0)
|
||||
H(i, kBase + t) = toStdCmplx(coeff);
|
||||
else
|
||||
H(i, kBase + t) += toStdCmplx(coeff);
|
||||
W[t] -= coeff * basis[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
F = W;
|
||||
|
||||
if (k == Nm/Nblock - 1) {
|
||||
// Last block: record coupling in B as R^H (Hermitian conjugate of QR factor)
|
||||
// KS relation requires B[kBase+t, s] = conj(R[s,t])
|
||||
CMat R = blockQR(F);
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int s = 0; s < Nblock; s++)
|
||||
B(kBase + t, s) = std::conj(R(s, t)); // B_block = R^H
|
||||
beta_k = R.norm();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not last: QR the residual, extend basis
|
||||
CMat R = blockQR(F);
|
||||
|
||||
int nextBase = (k + 1) * Nblock;
|
||||
for (int i = 0; i < Nblock; i++)
|
||||
for (int j = 0; j < Nblock; j++)
|
||||
H(nextBase + i, kBase + j) = R(i, j);
|
||||
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
basis.push_back(F[t]);
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block QR (modified Gram-Schmidt within the block)
|
||||
//--------------------------------------------------------------------
|
||||
CMat blockQR(std::vector<Field>& W)
|
||||
{
|
||||
CMat R = CMat::Zero(Nblock, Nblock);
|
||||
const RealD deflThresh = 1e-14;
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD coeff = innerProduct(W[i], W[j]);
|
||||
R(i, j) = toStdCmplx(coeff);
|
||||
W[j] -= coeff * W[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(W[j]));
|
||||
R(j, j) = nrm;
|
||||
if (nrm > deflThresh) {
|
||||
W[j] *= (1.0 / nrm);
|
||||
} else {
|
||||
W[j] = Zero();
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: deflation at block column " << j
|
||||
<< " (norm = " << nrm << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Orthonormalise starting block
|
||||
//--------------------------------------------------------------------
|
||||
void blockOrthonormalise(std::vector<Field>& V)
|
||||
{
|
||||
for (int j = 0; j < (int)V.size(); j++) {
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD c = innerProduct(V[i], V[j]);
|
||||
V[j] -= c * V[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(V[j]));
|
||||
assert(nrm > 1e-14);
|
||||
V[j] *= (1.0 / nrm);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Basis rotation: UR[i] = sum_j U[j] * R(j,i)
|
||||
//--------------------------------------------------------------------
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field>& U,
|
||||
CMat& R, int N)
|
||||
{
|
||||
UR.clear();
|
||||
Field tmp(Grid_);
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < N; j++)
|
||||
tmp += U[j] * R(j, i);
|
||||
UR.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Eigensystem of the truncated H (not Hhat)
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Eigenvalues of H_k are the standard Ritz values in the retained
|
||||
* subspace. After convergence has been declared via harmonic estimates,
|
||||
* the final reported eigenvalues and vectors come from H_k (not Hhat_k),
|
||||
* since H_k contains the true projected operator.
|
||||
*/
|
||||
void computeEigensystem(CMat& Hk, int Nkeep)
|
||||
{
|
||||
Eigen::ComplexEigenSolver<CMat> es;
|
||||
es.compute(Hk);
|
||||
|
||||
// Sort to match schurReorder ordering.
|
||||
int n = es.eigenvalues().size();
|
||||
ComplexComparator cComp(ritzFilter);
|
||||
std::vector<int> idx(n);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(es.eigenvalues()(a)), toStdCmplx(es.eigenvalues()(b)));
|
||||
});
|
||||
|
||||
evals.resize(n);
|
||||
littleEvecs.resize(n, n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
evals(k) = es.eigenvalues()(idx[k]);
|
||||
littleEvecs.col(k) = es.eigenvectors().col(idx[k]);
|
||||
}
|
||||
|
||||
evecs.clear();
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec vec = littleEvecs.col(k);
|
||||
Field tmp(Grid_);
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)basis.size() && j < Nkeep; j++)
|
||||
tmp += vec[j] * basis[j];
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Convergence check
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Ritz estimate for eigenpair k: || B^H y_k ||
|
||||
* where y_k is the k-th eigenvector of the truncated H.
|
||||
* The same bound applies whether using Ritz or harmonic Ritz restart.
|
||||
*/
|
||||
int converged(int Nkeep)
|
||||
{
|
||||
ritzEstimates.clear();
|
||||
int Nconv = 0;
|
||||
|
||||
CMat Bk = B(Eigen::seqN(0, Nkeep), Eigen::all);
|
||||
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec yk = littleEvecs.col(k);
|
||||
CVec Bty = Bk.adjoint() * yk;
|
||||
RealD res = Bty.norm();
|
||||
ritzEstimates.push_back(res);
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: Ritz estimate[" << k
|
||||
<< "] = " << res << " eval = " << evals[k] << std::endl;
|
||||
if (res < rtol) Nconv++;
|
||||
else break;
|
||||
}
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Approximate maximum eigenvalue (power iteration)
|
||||
//--------------------------------------------------------------------
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50)
|
||||
{
|
||||
assert(norm2(v0) > 1e-8);
|
||||
RealD lam = 0.0, denom = std::sqrt(norm2(v0));
|
||||
Field vcur(Grid_), vtmp(Grid_);
|
||||
vcur = v0;
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(vcur, vtmp);
|
||||
vcur = vtmp;
|
||||
RealD num = std::sqrt(norm2(vcur));
|
||||
lam = num / denom;
|
||||
denom = num;
|
||||
}
|
||||
return lam;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,18 @@ enum IRLdiagonalisation {
|
||||
IRLdiagonaliseWithEigen
|
||||
};
|
||||
|
||||
enum IRLeigsort {
|
||||
IRLeigsortMax,
|
||||
IRLeigsortSqMin
|
||||
};
|
||||
|
||||
#if 0
|
||||
bool square_comp(RealD a, RealD b){
|
||||
if (a*a<b*b) return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Field> class ImplicitlyRestartedLanczosHermOpTester : public ImplicitlyRestartedLanczosTester<Field>
|
||||
{
|
||||
public:
|
||||
@@ -79,14 +91,16 @@ template<class Field> class ImplicitlyRestartedLanczosHermOpTester : public Imp
|
||||
RealD vv = norm2(v) / ::pow(evalMaxApprox,2.0);
|
||||
|
||||
std::cout.precision(13);
|
||||
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
||||
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||
<<std::endl;
|
||||
|
||||
int conv=0;
|
||||
if( (vv<eresid*eresid) ) conv = 1;
|
||||
|
||||
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
||||
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||
<<" target " << eresid*eresid << " conv " <<conv
|
||||
<<std::endl;
|
||||
|
||||
return conv;
|
||||
}
|
||||
};
|
||||
@@ -117,9 +131,10 @@ class ImplicitlyRestartedLanczos {
|
||||
/////////////////////////
|
||||
// Constructor
|
||||
/////////////////////////
|
||||
|
||||
public:
|
||||
public:
|
||||
IRLeigsort EigSort;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// PAB:
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -152,6 +167,7 @@ public:
|
||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||
eresid(_eresid), betastp(_betastp),
|
||||
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
||||
EigSort(IRLeigsortMax),
|
||||
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
||||
|
||||
ImplicitlyRestartedLanczos(LinearFunction<Field> & PolyOp,
|
||||
@@ -168,6 +184,7 @@ public:
|
||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||
eresid(_eresid), betastp(_betastp),
|
||||
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
||||
EigSort(IRLeigsortMax),
|
||||
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
||||
|
||||
////////////////////////////////
|
||||
@@ -209,7 +226,7 @@ until convergence
|
||||
void calc(std::vector<RealD>& eval, std::vector<Field>& evec, const Field& src, int& Nconv, bool reverse=false)
|
||||
{
|
||||
GridBase *grid = src.Grid();
|
||||
assert(grid == evec[0].Grid());
|
||||
GRID_ASSERT(grid == evec[0].Grid());
|
||||
|
||||
// GridLogIRL.TimingMode(1);
|
||||
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
||||
@@ -229,7 +246,7 @@ until convergence
|
||||
}
|
||||
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
||||
|
||||
assert(Nm <= evec.size() && Nm <= eval.size());
|
||||
GRID_ASSERT(Nm <= evec.size() && Nm <= eval.size());
|
||||
|
||||
// quickly get an idea of the largest eigenvalue to more properly normalize the residuum
|
||||
RealD evalMaxApprox = 0.0;
|
||||
@@ -243,9 +260,10 @@ until convergence
|
||||
_HermOp(src_n,tmp);
|
||||
// std::cout << GridLogMessage<< tmp<<std::endl; exit(0);
|
||||
// std::cout << GridLogIRL << " _HermOp " << norm2(tmp) << std::endl;
|
||||
RealD vnum = real(innerProduct(src_n,tmp)); // HermOp.
|
||||
// RealD vnum = real(innerProduct(src_n,tmp)); // HermOp.
|
||||
RealD vnum = real(innerProduct(tmp,tmp)); // HermOp^2.
|
||||
RealD vden = norm2(src_n);
|
||||
RealD na = vnum/vden;
|
||||
RealD na = std::sqrt(vnum/vden);
|
||||
if (fabs(evalMaxApprox/na - 1.0) < 0.0001)
|
||||
i=_MAX_ITER_IRL_MEVAPP_;
|
||||
evalMaxApprox = na;
|
||||
@@ -253,6 +271,7 @@ until convergence
|
||||
src_n = tmp;
|
||||
}
|
||||
}
|
||||
std::cout << GridLogIRL << " Final evalMaxApprox " << evalMaxApprox << std::endl;
|
||||
|
||||
std::vector<RealD> lme(Nm);
|
||||
std::vector<RealD> lme2(Nm);
|
||||
@@ -312,8 +331,12 @@ until convergence
|
||||
// sorting
|
||||
//////////////////////////////////
|
||||
eval2_copy = eval2;
|
||||
// if (EigSort==IRLeigsortMax)
|
||||
// std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),square_comp);
|
||||
// else
|
||||
std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),std::greater<RealD>());
|
||||
std::cout<<GridLogIRL <<" evals sorted "<<std::endl;
|
||||
// eval2_copy = eval2;
|
||||
const int chunk=8;
|
||||
for(int io=0; io<k2;io+=chunk){
|
||||
std::cout<<GridLogIRL << "eval "<< std::setw(3) << io ;
|
||||
@@ -329,11 +352,12 @@ until convergence
|
||||
//////////////////////////////////
|
||||
Qt = Eigen::MatrixXd::Identity(Nm,Nm);
|
||||
for(int ip=k2; ip<Nm; ++ip){
|
||||
// std::cout<<GridLogIRL <<"QR decompose "<<eval2[ip]<<std::endl;
|
||||
QR_decomp(eval,lme,Nm,Nm,Qt,eval2[ip],k1,Nm);
|
||||
}
|
||||
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
|
||||
|
||||
assert(k2<Nm); assert(k2<Nm); assert(k1>0);
|
||||
GRID_ASSERT(k2<Nm); GRID_ASSERT(k2<Nm); GRID_ASSERT(k1>0);
|
||||
|
||||
basisRotate(evec,Qt,k1-1,k2+1,0,Nm,Nm); /// big constraint on the basis
|
||||
std::cout<<GridLogIRL <<"basisRotated by Qt *"<<k1-1<<","<<k2+1<<")"<<std::endl;
|
||||
@@ -371,7 +395,8 @@ until convergence
|
||||
|
||||
// power of two search pattern; not every evalue in eval2 is assessed.
|
||||
int allconv =1;
|
||||
for(int jj = 1; jj<=Nstop; jj*=2){
|
||||
// for(int jj = 1; jj<=Nstop; jj*=2){
|
||||
for(int jj = 1; jj<=Nstop; jj++){
|
||||
int j = Nstop-jj;
|
||||
RealD e = eval2_copy[j]; // Discard the evalue
|
||||
basisRotateJ(B,evec,Qt,j,0,Nk,Nm);
|
||||
@@ -419,14 +444,15 @@ until convergence
|
||||
}
|
||||
}
|
||||
|
||||
if ( Nconv < Nstop )
|
||||
if ( Nconv < Nstop ) {
|
||||
std::cout << GridLogIRL << "Nconv ("<<Nconv<<") < Nstop ("<<Nstop<<")"<<std::endl;
|
||||
|
||||
std::cout << GridLogIRL << "returning Nstop vectors, the last "<< Nstop-Nconv << "of which might meet convergence criterion only approximately" <<std::endl;
|
||||
}
|
||||
eval=eval2;
|
||||
|
||||
//Keep only converged
|
||||
eval.resize(Nconv);// Nstop?
|
||||
evec.resize(Nconv,grid);// Nstop?
|
||||
eval.resize(Nstop);// was Nconv
|
||||
evec.resize(Nstop,grid);// was Nconv
|
||||
basisSortInPlace(evec,eval,reverse);
|
||||
|
||||
}
|
||||
@@ -456,15 +482,15 @@ until convergence
|
||||
std::vector<Field>& evec,
|
||||
Field& w,int Nm,int k)
|
||||
{
|
||||
std::cout<<GridLogIRL << "Lanczos step " <<k<<std::endl;
|
||||
std::cout<<GridLogDebug << "Lanczos step " <<k<<std::endl;
|
||||
const RealD tiny = 1.0e-20;
|
||||
assert( k< Nm );
|
||||
GRID_ASSERT( k< Nm );
|
||||
|
||||
GridStopWatch gsw_op,gsw_o;
|
||||
|
||||
Field& evec_k = evec[k];
|
||||
|
||||
_PolyOp(evec_k,w); std::cout<<GridLogIRL << "PolyOp" <<std::endl;
|
||||
_PolyOp(evec_k,w); std::cout<<GridLogDebug << "PolyOp" <<std::endl;
|
||||
|
||||
if(k>0) w -= lme[k-1] * evec[k-1];
|
||||
|
||||
@@ -479,18 +505,18 @@ until convergence
|
||||
lme[k] = beta;
|
||||
|
||||
if ( (k>0) && ( (k % orth_period) == 0 )) {
|
||||
std::cout<<GridLogIRL << "Orthogonalising " <<k<<std::endl;
|
||||
std::cout<<GridLogDebug << "Orthogonalising " <<k<<std::endl;
|
||||
orthogonalize(w,evec,k); // orthonormalise
|
||||
std::cout<<GridLogIRL << "Orthogonalised " <<k<<std::endl;
|
||||
std::cout<<GridLogDebug << "Orthogonalised " <<k<<std::endl;
|
||||
}
|
||||
|
||||
if(k < Nm-1) evec[k+1] = w;
|
||||
|
||||
std::cout<<GridLogIRL << "alpha[" << k << "] = " << zalph << " beta[" << k << "] = "<<beta<<std::endl;
|
||||
std::cout<<GridLogIRL << "Lanczos step alpha[" << k << "] = " << zalph << " beta[" << k << "] = "<<beta<<std::endl;
|
||||
if ( beta < tiny )
|
||||
std::cout<<GridLogIRL << " beta is tiny "<<beta<<std::endl;
|
||||
|
||||
std::cout<<GridLogIRL << "Lanczos step complete " <<k<<std::endl;
|
||||
std::cout<<GridLogDebug << "Lanczos step complete " <<k<<std::endl;
|
||||
}
|
||||
|
||||
void diagonalize_Eigen(std::vector<RealD>& lmd, std::vector<RealD>& lme,
|
||||
@@ -592,7 +618,7 @@ until convergence
|
||||
} else if ( diagonalisation == IRLdiagonaliseWithEigen ) {
|
||||
diagonalize_Eigen(lmd,lme,Nk,Nm,Qt,grid);
|
||||
} else {
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +708,7 @@ void diagonalize_lapack(std::vector<RealD>& lmd,
|
||||
}
|
||||
}
|
||||
#else
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,994 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/KrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@bnl.gov>
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_KRYLOVSCHUR_H
|
||||
#define GRID_KRYLOVSCHUR_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
#if defined(GRID_CUDA) || defined(GRID_HIP)
|
||||
using thrust::abs;
|
||||
using thrust::conj;
|
||||
inline std::complex<double> toStdCmplx(const ComplexD& c) { return {c.real(), c.imag()}; }
|
||||
#else
|
||||
using std::abs;
|
||||
using std::conj;
|
||||
inline const ComplexD& toStdCmplx(const ComplexD& c) { return c; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Small dense coefficient matrix of ComplexD (thrust::complex on GPU builds)
|
||||
* for passing to basisRotate, whose device kernel cannot multiply Grid tensors
|
||||
* by std::complex. Must be namespace-scope: nvcc rejects function-local types
|
||||
* as template arguments to functions containing extended device lambdas.
|
||||
*/
|
||||
struct KSCoeffMat {
|
||||
std::vector<ComplexD> a; int n;
|
||||
KSCoeffMat(int _n) : a(_n*_n), n(_n) {}
|
||||
ComplexD& operator()(int i, int j) { return a[i*n+j]; }
|
||||
const ComplexD& operator()(int i, int j) const { return a[i*n+j]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for which Ritz values to keep in implicit restart. TODO move this and utilities into a new file
|
||||
*/
|
||||
enum RitzFilter {
|
||||
EvalNormSmall, // Keep evals with smallest norm
|
||||
EvalNormLarge, // Keep evals with largest norm
|
||||
EvalReSmall, // Keep evals with smallest real part
|
||||
EvalReLarge, // Keep evals with largest real part
|
||||
EvalReNormLarge, // Keep evals with largest real part
|
||||
EvalImSmall, // Keep evals with smallest imaginary part
|
||||
EvalImLarge, // Keep evals with largest imaginary part
|
||||
EvalImNormSmall, // Keep evals with smallest |imaginary| part
|
||||
EvalImNormLarge, // Keep evals with largest |imaginary| part
|
||||
};
|
||||
|
||||
/** Selects the RitzFilter corresponding to the input string. */
|
||||
inline RitzFilter selectRitzFilter(std::string s) {
|
||||
if (s == "EvalNormSmall") { return EvalNormSmall; } else
|
||||
if (s == "EvalNormLarge") { return EvalNormLarge; } else
|
||||
if (s == "EvalReSmall") { return EvalReSmall; } else
|
||||
if (s == "EvalReLarge") { return EvalReLarge; } else
|
||||
if (s == "EvalReNormLarge") { return EvalReNormLarge; } else
|
||||
if (s == "EvalImSmall") { return EvalImSmall; } else
|
||||
if (s == "EvalImLarge") { return EvalImLarge; } else
|
||||
if (s == "EvalImNormSmall") { return EvalImNormSmall; } else
|
||||
if (s == "EvalImNormLarge") { return EvalImNormLarge; } else
|
||||
{ assert(0); }
|
||||
}
|
||||
|
||||
/** Returns a string saying which RitzFilter it is. */
|
||||
inline std::string rfToString(RitzFilter RF) {
|
||||
switch (RF) {
|
||||
case EvalNormSmall:
|
||||
return "EvalNormSmall";
|
||||
case EvalNormLarge:
|
||||
return "EvalNormLarge";
|
||||
case EvalReSmall:
|
||||
return "EvalReSmall";
|
||||
case EvalReLarge:
|
||||
return "EvalReLarge";
|
||||
case EvalReNormLarge:
|
||||
return "EvalReNormLarge";
|
||||
case EvalImSmall:
|
||||
return "EvalImSmall";
|
||||
case EvalImLarge:
|
||||
return "EvalImLarge";
|
||||
case EvalImNormSmall:
|
||||
return "EvalImNormSmall";
|
||||
case EvalImNormLarge:
|
||||
return "EvalImNormLarge";
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Select comparison function from RitzFilter
|
||||
struct ComplexComparator
|
||||
{
|
||||
RitzFilter RF;
|
||||
ComplexComparator (RitzFilter _rf) : RF(_rf) {}
|
||||
bool operator()(std::complex<double> z1, std::complex<double> z2) {
|
||||
RealD tmp1=std::abs(std::imag(z1));
|
||||
RealD tmp2=std::abs(std::imag(z2));
|
||||
if ( std::abs(std::real(z1)) >2.) tmp1 += 100.;
|
||||
if ( std::abs(std::real(z2)) >2.) tmp2 += 100.;
|
||||
switch (RF) {
|
||||
case EvalNormSmall:
|
||||
return std::abs(z1) < std::abs(z2);
|
||||
case EvalNormLarge:
|
||||
return std::abs(z1) > std::abs(z2);
|
||||
case EvalReSmall:
|
||||
return std::real(z1) < std::real(z2); // DELETE THE ABS HERE!!!
|
||||
case EvalReLarge:
|
||||
return std::real(z1) > std::real(z2);
|
||||
case EvalReNormLarge:
|
||||
return std::abs(std::real(z1)) < std::abs(std::real(z2));
|
||||
case EvalImSmall:
|
||||
return std::imag(z1) < std::imag(z2);
|
||||
case EvalImLarge:
|
||||
return std::imag(z1) > std::imag(z2);
|
||||
case EvalImNormSmall:
|
||||
// return std::abs(std::imag(z1)) < std::abs(std::imag(z2));
|
||||
return tmp1 < tmp2 ;
|
||||
case EvalImNormLarge:
|
||||
return std::abs(std::imag(z1)) > std::abs(std::imag(z2));
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes a complex Schur decomposition of a complex matrix A using Eigen's matrix library. The Schur decomposition,
|
||||
* A = Q^\dag S Q
|
||||
* factorizes A into a unitary matrix Q and an upper triangular matrix S. The eigenvalues of A lie on the diagonal of the upper triangular matrix S.
|
||||
* The Schur decomposition is not unique: in particular, any ordering of the eigenvalues of A can be used as the diagonal of the matrix S.
|
||||
* This class supports eigenvalue reordering by swapping two adjacent eigenvalues with a unitary transformation.
|
||||
*/
|
||||
class ComplexSchurDecomposition {
|
||||
|
||||
private:
|
||||
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
|
||||
CMat A; // Matrix to decompose, A = Q^\dag S Q
|
||||
CMat Q; // Unitary matrix Q
|
||||
CMat S; // Upper triangular matrix S
|
||||
|
||||
// Placeholders for Givens rotation
|
||||
CMat Givens; // Givens rotation
|
||||
ComplexD s; // off-diagonal element
|
||||
ComplexD lam1; // First eval for swap
|
||||
ComplexD lam2; // Second eval for swap
|
||||
ComplexD phi; // phase of s
|
||||
RealD r; // norm of s and lam2 - lam1
|
||||
|
||||
int Nm; // size of matrix problem
|
||||
|
||||
ComplexComparator cCompare; // function to sort the Schur matrix.
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* If the input matrix _A is in Hessenberg form (upper triangular + first subdiagonal non-zero), then the Schur
|
||||
* decomposition is easier to compute.
|
||||
*/
|
||||
ComplexSchurDecomposition(CMat _A, bool isHess, RitzFilter ritzFilter = EvalReSmall) : A(_A), Nm (_A.rows()), cCompare (ritzFilter)
|
||||
{
|
||||
Eigen::ComplexSchur<CMat> schur (Nm);
|
||||
if (isHess) {
|
||||
schur.computeFromHessenberg(_A, CMat::Identity(Nm, Nm), true);
|
||||
} else {
|
||||
schur.compute(_A, true);
|
||||
}
|
||||
S = schur.matrixT();
|
||||
Q = schur.matrixU().adjoint(); // Eigen computes A = Q S Q^\dag, we want A = Q^\dag S Q
|
||||
}
|
||||
|
||||
// Getters
|
||||
int getNm() { return Nm; } // size of matrix problem
|
||||
CMat getMatrixA() { return A; } // matrix for decomposition
|
||||
CMat getMatrixQ() { return Q; } // unitary matrix Q
|
||||
CMat getMatrixS() { return S; } // Schur matrix (upper triangular) S
|
||||
CMat getRitz() { return S.diagonal(); }
|
||||
|
||||
/**
|
||||
* Checks the Schur decomposition A = Q^\dag S Q holds for the computed matrices. Returns if the relative
|
||||
* Frobenius norm || A - Q^\dag S Q || / || A || is less than rtol.
|
||||
*/
|
||||
bool checkDecomposition(RealD rtol = 1e-8) {
|
||||
RealD Anorm = A.norm();
|
||||
if (Anorm < rtol) {
|
||||
std::cout << GridLogMessage << "Zero matrix" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cout << GridLogDebug << "S = " << std::endl << S << std::endl;
|
||||
std::cout << GridLogDebug << "Q = " << std::endl << Q << std::endl;
|
||||
|
||||
CMat A2 = Q.adjoint() * S * Q;
|
||||
std::cout << GridLogDebug << "Q^dag S Q = " << std::endl << A2 << std::endl;
|
||||
RealD dA = (A - A2).norm() / Anorm;
|
||||
return (dA < rtol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the components on the diagonal of the Schur matrix at index i with index i + 1.
|
||||
* Updates the orthogonal matrix Q accordingly.
|
||||
*/
|
||||
void swapEvals(int i) {
|
||||
|
||||
assert(0 <= i && i <= Nm - 1); // can only swap blocks with upper left index between 0 and Nm - 1
|
||||
|
||||
// get parameters for rotation
|
||||
s = S(i, i+1);
|
||||
lam1 = S(i, i);
|
||||
lam2 = S(i+1, i+1);
|
||||
phi = s / abs(s);
|
||||
r = std::sqrt(std::pow(abs(s), 2) + std::pow(abs(lam2 - lam1), 2));
|
||||
|
||||
// // Original code which performs Givens rotations by manual matrix multiplication
|
||||
// // compute Givens rotation corresponding to these parameters
|
||||
// Givens = CMat::Identity(Nm, Nm);
|
||||
// Givens(i, i) = std::abs(s) / r;
|
||||
// Givens(i+1, i+1) = Givens(i, i);
|
||||
// Givens(i, i+1) = (phi / r) * std::conj(lam2 - lam1);
|
||||
// Givens(i+1, i) = -std::conj(Givens(i, i+1));
|
||||
// // rotate Schur matrix and unitary change of basis matrix Q
|
||||
// S = Givens * S * Givens.adjoint();
|
||||
// Q = Givens * Q;
|
||||
|
||||
// Modified code
|
||||
Givens = CMat::Identity(2, 2);
|
||||
Givens(0, 0) = abs(s) / r;
|
||||
Givens(1, 1) = Givens(0, 0);
|
||||
Givens(0, 1) = (phi / r) * conj(lam2 - lam1);
|
||||
Givens(1, 0) = -conj(Givens(0, 1));
|
||||
|
||||
// TODO: make sure these are correct
|
||||
Eigen::MatrixXcd tmp;
|
||||
tmp = Givens * S.block(i, 0, 2, Nm);
|
||||
S.block(i, 0, 2, Nm) = tmp;
|
||||
|
||||
// S_{:, i:i+2} = S.block<Nm, 2>(0, i);
|
||||
tmp = S.block(0, i, Nm, 2) * Givens.adjoint();
|
||||
S.block(0, i, Nm, 2) = tmp;
|
||||
|
||||
// Q_{i:i+2, :} = Q.block<2, Nm>(i, 0);
|
||||
tmp = Givens * Q.block(i, 0, 2, Nm);
|
||||
Q.block(i, 0, 2, Nm) = tmp;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders a Schur matrix &Schur to have the Ritz values that we would like to keep for
|
||||
* restart as the first Nk elements on the diagonal.
|
||||
*
|
||||
* This algorithm is implemented as Nk iterations of a a reverse bubble sort with comparator compare.
|
||||
* TODO: pass in compare function as an argument, default to compare with <.
|
||||
*/
|
||||
// void schurReorder(int Nk, std::function compare) {
|
||||
void schurReorder(int Nk) {
|
||||
std::cout << GridLogMessage << "schurReorder start " << std::endl;
|
||||
int nswap=0;
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
for (int k = 0; k <= Nm - 2; k++) {
|
||||
int idx = Nm - 2 - k;
|
||||
// TODO use RitzFilter enum here
|
||||
// if (std::abs(S(idx, idx)) < std::abs(S(idx+1, idx+1))) { // sort by largest modulus of eigenvalue
|
||||
// if (std::real(S(idx, idx)) > std::real(S(idx+1, idx+1))) { // sort by smallest real eigenvalue
|
||||
if ( cCompare(S(idx+1, idx+1), S(idx, idx)) ) { // sort by largest modulus of eigenvalue
|
||||
swapEvals(idx);
|
||||
nswap++;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << GridLogMessage << "schurReorder end "<< nswap << std::endl;
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
std::cout << GridLogMessage << "schurR "<<i<<" "<<S(i,i) << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void schurReorderBlock() {
|
||||
// TODO method stub
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// template<class Field>
|
||||
// inline void writeFile(const Field &field, const std::string &fname) {
|
||||
// emptyUserRecord record;
|
||||
// ScidacWriter WR(field.Grid()->IsBoss());
|
||||
// WR.open(fname);
|
||||
// WR.writeScidacFieldRecord(field, record, 0); // 0 = Lexico
|
||||
// WR.close();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Implementation of the Krylov-Schur algorithm.
|
||||
*/
|
||||
template<class Field>
|
||||
class KrylovSchur {
|
||||
|
||||
private:
|
||||
|
||||
std::string cname = std::string("KrylovSchur");
|
||||
int MaxIter; // Max iterations
|
||||
RealD Tolerance;
|
||||
RealD ssq;
|
||||
RealD rtol;
|
||||
int Nm; // Number of basis vectors to track (equals MaxIter if no restart)
|
||||
int Nk; // Number of basis vectors to keep every restart (equals -1 if no restart)
|
||||
int Nstop; // Stop after converging Nstop eigenvectors.
|
||||
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
RealD approxLambdaMax;
|
||||
RealD beta_k;
|
||||
Field u; // Residual vector perpendicular to Krylov space (u_{k+1} in notes)
|
||||
Eigen::VectorXcd b; // b vector in Schur decomposition (e_{k+1} in Arnoldi).
|
||||
std::vector<Field> basis; // orthonormal Krylov basis
|
||||
Eigen::MatrixXcd Rayleigh; // Rayleigh quotient of size Nbasis (after construction)
|
||||
Eigen::MatrixXcd Qt; // Transpose of basis rotation which projects out high modes.
|
||||
|
||||
Eigen::VectorXcd evals; // evals of Rayleigh quotient
|
||||
std::vector<RealD> ritzEstimates; // corresponding ritz estimates for evals
|
||||
Eigen::MatrixXcd littleEvecs; // Nm x Nm evecs matrix
|
||||
|
||||
RitzFilter ritzFilter; // how to sort evals
|
||||
|
||||
public:
|
||||
bool doEvalCheck = false;
|
||||
|
||||
KrylovSchur(LinearOperatorBase<Field> &_Linop, GridBase *_Grid, RealD _Tolerance, RitzFilter filter = EvalReSmall)
|
||||
: Linop(_Linop), Grid(_Grid), Tolerance(_Tolerance), ritzFilter(filter), u(_Grid), MaxIter(-1), Nm(-1), Nk(-1), Nstop (-1),
|
||||
evals (0), ritzEstimates (), evecs (), ssq (0.0), rtol (0.0), beta_k (0.0), approxLambdaMax (0.0)
|
||||
{
|
||||
u = Zero();
|
||||
};
|
||||
std::vector<Field> evecs; // Vector of evec fields
|
||||
|
||||
|
||||
/* Getters */
|
||||
int getNk() { return Nk; }
|
||||
Eigen::MatrixXcd getRayleighQuotient() { return Rayleigh; }
|
||||
Field getU() { return u; }
|
||||
std::vector<Field> getBasis() { return basis; }
|
||||
Eigen::VectorXcd getEvals() { return evals; }
|
||||
std::vector<RealD> getRitzEstimates() { return ritzEstimates; }
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
|
||||
/**
|
||||
* Runs the non-harmonic Krylov-Schur loop.
|
||||
* - Runs an Arnoldi step to generate the Rayleigh quotient and Krylov basis.
|
||||
* - Schur decompose the Rayleigh quotient.
|
||||
* - Permutes the Rayleigh quotient according to the eigenvalues.
|
||||
* - Truncate the Krylov-Schur expansion.
|
||||
*/
|
||||
void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, bool doubleOrthog = true) {
|
||||
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm; Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
|
||||
ssq = norm2(v0);
|
||||
RealD approxLambdaMax = approxMaxEval(v0);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage << "Approximate max eigenvalue: " << approxLambdaMax << std::endl;
|
||||
|
||||
b = Eigen::VectorXcd::Zero(Nm); // start as e_{k+1}
|
||||
b(Nm-1) = 1.0;
|
||||
|
||||
int start = 0;
|
||||
Field startVec = v0;
|
||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
for (int i = 0; i < MaxIter; i++) {
|
||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
||||
|
||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
||||
startVec = u;
|
||||
start = Nk;
|
||||
|
||||
std::cout << GridLogDebug << "b after Arnoldi " << b << std::endl;
|
||||
|
||||
// --- Restart: Schur-decompose Rayleigh, reorder by ritzFilter, rotate
|
||||
// the basis into the Schur vectors, and truncate to the leading Nk.
|
||||
ComplexSchurDecomposition schur (Rayleigh, false, ritzFilter);
|
||||
std::cout << GridLogDebug << "Schur decomp holds? " << schur.checkDecomposition() << std::endl;
|
||||
|
||||
// Rearrange Schur matrix so wanted evals are on top left (like MATLAB's ordschur)
|
||||
std::cout << GridLogMessage << "Reordering Schur eigenvalues" << std::endl;
|
||||
schur.schurReorder(Nk);
|
||||
std::cout << GridLogMessage << "Schur eigenvalues reordered." << std::endl;
|
||||
|
||||
Eigen::MatrixXcd Q = schur.getMatrixQ();
|
||||
Qt = Q.adjoint(); // TODO should Q be real?
|
||||
|
||||
std::cout << GridLogMessage << "*** ROTATING TO SCHUR BASIS *** " << std::endl;
|
||||
|
||||
// Rotate Krylov basis, b vector, redefine Rayleigh quotient and evecs, and truncate.
|
||||
Rayleigh = schur.getMatrixS();
|
||||
b = Q * b; // b^\dag = b^\dag * Q^\dag <==> b = Q*b
|
||||
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, Nm,Nk);
|
||||
|
||||
std::cout << GridLogMessage << "*** TRUNCATING FOR RESTART *** " << std::endl;
|
||||
std::cout << GridLogDebug << "Rayleigh before truncation: " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
Eigen::MatrixXcd RayTmp = Rayleigh(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
Rayleigh = RayTmp;
|
||||
|
||||
basis = std::vector<Field> (basis2.begin(), basis2.begin() + Nk);
|
||||
|
||||
b = b.head(Nk).eval();
|
||||
|
||||
std::cout << GridLogDebug << "Rayleigh after truncation: " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
checkKSDecomposition();
|
||||
|
||||
// Compute eigensystem of Rayleigh. Note the eigenvectors correspond to the sorted eigenvalues.
|
||||
computeEigensystem(Rayleigh);
|
||||
std::cout << GridLogMessage << "Eigenvalues (first Nk sorted): " << std::endl << evals << std::endl;
|
||||
|
||||
if (checkConvergedAndReport(i)) return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the harmonic (shifted) Krylov-Schur loop: extracts Ritz values of a
|
||||
* shift-augmented Rayleigh quotient so that eigenvalues near `*_shift` are
|
||||
* reordered to the top instead of the extremal ones.
|
||||
*/
|
||||
void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, RealD *_shift, bool doubleOrthog = true) {
|
||||
|
||||
assert(_shift && "harmonic KrylovSchur: shift must be non-null");
|
||||
RealD shiftVal = *_shift;
|
||||
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm; Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
|
||||
ssq = norm2(v0);
|
||||
RealD approxLambdaMax = approxMaxEval(v0);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage << "Approximate max eigenvalue: " << approxLambdaMax << std::endl;
|
||||
|
||||
b = Eigen::VectorXcd::Zero(Nm); // start as e_{k+1}
|
||||
b(Nm-1) = 1.0;
|
||||
|
||||
int start = 0;
|
||||
Field startVec = v0;
|
||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
for (int i = 0; i < MaxIter; i++) {
|
||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
||||
|
||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
||||
startVec = u;
|
||||
start = Nk;
|
||||
|
||||
std::cout << GridLogDebug << "b after Arnoldi " << b << std::endl;
|
||||
|
||||
// --- Harmonic restart: Schur-decompose the shift-augmented Rayleigh
|
||||
// quotient so Ritz values close to shiftVal are reordered to the
|
||||
// top, then rotate and truncate as in the non-harmonic case.
|
||||
Eigen::MatrixXcd temp = Rayleigh;
|
||||
for (int m=0;m<Nm;m++) temp(m,m) -= shiftVal;
|
||||
Eigen::MatrixXcd RayleighS = temp.inverse(); // (B-tI)^-1
|
||||
Eigen::MatrixXcd temp2 = RayleighS.adjoint(); //(B-tI)^-1*
|
||||
Eigen::VectorXcd g = temp2*b; //g = (B-tI)^-1* * b
|
||||
Eigen::MatrixXcd Btilde = Rayleigh + g*(b.adjoint());
|
||||
|
||||
Field utilde(Grid);
|
||||
utilde = u;
|
||||
for (int j = 0; j<Nm; j++){
|
||||
utilde -= basis[j]*g(j);
|
||||
}
|
||||
|
||||
ComplexSchurDecomposition schurS (Btilde, false, ritzFilter);
|
||||
std::cout << GridLogMessage << "Shifted Schur eigenvalues shift = "<<shiftVal << std::endl;
|
||||
schurS.schurReorder(Nk);
|
||||
|
||||
Eigen::MatrixXcd Q_s = schurS.getMatrixQ();
|
||||
Eigen::MatrixXcd Qt_s = Q_s.adjoint(); // TODO should Q be real?
|
||||
|
||||
Btilde=schurS.getMatrixS();
|
||||
Eigen::VectorXcd b_s = Q_s*b; // Q is Qt in SlepC, b_s=bhat
|
||||
|
||||
std::vector<Field> basis2_s;
|
||||
constructUR(basis2_s, basis, Qt_s, Nm,Nk);
|
||||
|
||||
Eigen::MatrixXcd RayTmp_s = Btilde(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
Btilde = RayTmp_s;
|
||||
|
||||
basis2_s = std::vector<Field> (basis2_s.begin(), basis2_s.begin() + Nk);
|
||||
|
||||
b_s = b_s.head(Nk).eval();
|
||||
|
||||
Eigen::VectorXcd ghat = -Q_s * g;
|
||||
ghat = ghat.head(Nk).eval();
|
||||
|
||||
Field uhat(Grid);
|
||||
uhat = utilde;
|
||||
for (int j = 0; j<Nk; j++){
|
||||
uhat -= basis2_s[j]*ghat(j);
|
||||
}
|
||||
|
||||
RealD gamma = std::sqrt(norm2(uhat));
|
||||
uhat *= 1.0/gamma;
|
||||
std::cout << GridLogMessage << " gamma "<<gamma << std::endl;
|
||||
|
||||
Btilde += ghat*(b_s.adjoint());
|
||||
b_s *=gamma;
|
||||
|
||||
Rayleigh = Btilde;
|
||||
basis= basis2_s;
|
||||
b = b_s;
|
||||
beta_k = gamma;
|
||||
u= uhat;
|
||||
|
||||
checkKSDecomposition();
|
||||
computeEigensystem(Rayleigh);
|
||||
std::cout << GridLogMessage << "Eigenvalues (first Nk sorted): " << std::endl << evals << std::endl;
|
||||
|
||||
if (checkConvergedAndReport(i)) return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Shared post-restart convergence check. On convergence (or the final
|
||||
* iteration) reports eigenvalues and optionally the explicit residual
|
||||
* check. Returns true if operator() should return.
|
||||
*/
|
||||
bool checkConvergedAndReport(int i) {
|
||||
int Nconv = converged();
|
||||
std::cout << GridLogMessage << "Number of evecs converged: " << Nconv << std::endl;
|
||||
if (Nconv >= Nstop || i == MaxIter - 1) {
|
||||
std::cout << GridLogMessage << "Converged with " << Nconv << " / " << Nstop << " eigenvectors on iteration "
|
||||
<< i << "." << std::endl;
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << std::endl << evals << std::endl;
|
||||
|
||||
if (doEvalCheck) {
|
||||
Field w(Grid);
|
||||
for (int k = 0; k < (int)evecs.size(); k++) {
|
||||
Linop.Op(evecs[k], w);
|
||||
ComplexD eval_est = toStdCmplx(innerProduct(evecs[k], w));
|
||||
w -= eval_est * evecs[k];
|
||||
RealD res = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage << "KrylovSchur: evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructs the Arnoldi basis for the Krylov space K_n(D, src). (TODO make private)
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* v0 : Field&
|
||||
* Source to generate Krylov basis.
|
||||
* Nm : int
|
||||
* Final size of the basis desired. If the basis becomes complete before a basis of size Nm is constructed
|
||||
* (determined by relative tolerance Tolerance), stops iteration there.
|
||||
* doubleOrthog : bool (default = false)
|
||||
* Whether to double orthogonalize the basis (for numerical cancellations) or not.
|
||||
* start : int (default = 0)
|
||||
* If non-zero, assumes part of the Arnoldi basis has already been constructed.
|
||||
*/
|
||||
void arnoldiIteration(const Field& v0, int Nm, int start = 0, bool doubleOrthog = true)
|
||||
{
|
||||
|
||||
ComplexD coeff;
|
||||
Field w (Grid); // A acting on last Krylov vector.
|
||||
|
||||
// basis.reserve(Nm);
|
||||
// for (int i = start; i < Nm; i++) {
|
||||
// basis.emplace_back(Grid);
|
||||
// }
|
||||
// basis.assign(Nm, Field(Grid));
|
||||
// basis.resize(Nm);
|
||||
// for (int i = start; i < Nm; i++) {
|
||||
// basis[i] = Field(Grid);
|
||||
// }
|
||||
|
||||
if (start == 0) { // initialize everything that we need.
|
||||
RealD v0Norm = 1 / std::sqrt(ssq);
|
||||
basis.push_back(v0Norm * v0); // normalized source
|
||||
// basis[0] = v0Norm * v0; // normalized source
|
||||
|
||||
Rayleigh = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
u = Zero();
|
||||
} else {
|
||||
// assert( start == basis.size() ); // should be starting at the end of basis (start = Nk)
|
||||
std::cout << GridLogMessage << "Resetting Rayleigh and b" << std::endl;
|
||||
Eigen::MatrixXcd RayleighCp = Rayleigh;
|
||||
Rayleigh = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
Rayleigh(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = RayleighCp;
|
||||
|
||||
// append b^\dag to Rayleigh, add u to basis
|
||||
Rayleigh(Nk, Eigen::seqN(0, Nk)) = b.adjoint();
|
||||
basis.push_back(u);
|
||||
// basis[start] = u; // TODO make sure this is correct
|
||||
b = Eigen::VectorXcd::Zero(Nm);
|
||||
}
|
||||
|
||||
// Construct next Arnoldi vector by normalizing w_i = Dv_i - \sum_j v_j h_{ji}
|
||||
for (int i = start; i < Nm; i++) {
|
||||
Linop.Op(basis.back(), w);
|
||||
// Linop.Op(basis[i], w);
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
coeff = innerProduct(basis[j], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after.
|
||||
Rayleigh(j, i) = toStdCmplx(coeff);
|
||||
w -= coeff * basis[j];
|
||||
}
|
||||
|
||||
if (doubleOrthog) {
|
||||
std::cout << GridLogDebug << "Double orthogonalizing." << std::endl;
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
coeff = innerProduct(basis[j], w); // see if there is any residual component in basis[j] direction
|
||||
Rayleigh(j, i) += toStdCmplx(coeff); // if coeff is non-zero, adjust Rayleigh
|
||||
w -= coeff * basis[j];
|
||||
}
|
||||
}
|
||||
|
||||
// add w_i to the pile
|
||||
if (i < Nm - 1) {
|
||||
coeff = std::sqrt(norm2(w));
|
||||
Rayleigh(i+1, i) = toStdCmplx(coeff);
|
||||
basis.push_back(
|
||||
(1.0/coeff) * w
|
||||
);
|
||||
// basis[i+1] = (1.0/coeff) * w;
|
||||
}
|
||||
|
||||
// after iterations, update u and beta_k = ||u|| before norm
|
||||
u = w; // make sure u is normalized
|
||||
beta_k = std::sqrt(norm2(u)); // beta_k = ||f_k|| determines convergence.
|
||||
u = (1/beta_k) * u;
|
||||
}
|
||||
|
||||
b(Nm - 1) = beta_k;
|
||||
|
||||
// std::cout << GridLogMessage << "|f|^2 after Arnoldi step = " << norm2(f) << std::endl;
|
||||
std::cout << GridLogMessage << "beta_k = |u| (before norm) after Arnoldi step = " << beta_k << std::endl;
|
||||
std::cout << GridLogDebug << "Computed Rayleigh quotient = " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the eigensystem of the linear operator by computing the eigensystem of
|
||||
* the Rayleigh quotient. Assumes that the Rayleigh quotient has already been constructed (by
|
||||
* calling the operator() function).
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Eigen::MatrixXcd& S
|
||||
* Schur matrix (upper triangular) similar to original Rayleigh quotient.
|
||||
*/
|
||||
void computeEigensystem(Eigen::MatrixXcd& S)
|
||||
{
|
||||
std::cout << GridLogMessage << "Computing eigenvalues." << std::endl;
|
||||
|
||||
evecs.clear();
|
||||
|
||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
||||
es.compute(S);
|
||||
|
||||
// Sort eigenvalues/evecs to match the schurReorder ordering.
|
||||
int n = es.eigenvalues().size();
|
||||
ComplexComparator cComp(ritzFilter);
|
||||
std::vector<int> idx(n);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(es.eigenvalues()(a)), toStdCmplx(es.eigenvalues()(b)));
|
||||
});
|
||||
|
||||
evals.resize(n);
|
||||
littleEvecs.resize(n, n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
evals(k) = es.eigenvalues()(idx[k]);
|
||||
littleEvecs.col(k) = es.eigenvectors().col(idx[k]);
|
||||
}
|
||||
|
||||
// Convert evecs to lattice fields
|
||||
for (int k = 0; k < n; k++) {
|
||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
||||
Field tmp (basis[0].Grid());
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)basis.size(); j++) {
|
||||
tmp = tmp + vec[j] * basis[j];
|
||||
}
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the maximum eigenvalue of Linop.Op to normalize the residual and test for convergence.
|
||||
*
|
||||
* TODO implement in parent class eventually
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Field& v0
|
||||
* Source field to start with. Must have non-zero norm.
|
||||
* int MAX_ITER (default = 50)
|
||||
* Maximum number of iterations for power approximation.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* RealD lamApprox
|
||||
* Approximation of largest eigenvalue.
|
||||
*/
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50) {
|
||||
assert (norm2(v0) > 1e-8); // must have relatively large source norm to start
|
||||
RealD lamApprox = 0.0;
|
||||
RealD denom = 1.0; RealD num = 1.0;
|
||||
Field v0cp (Grid); Field tmp (Grid);
|
||||
v0cp = v0;
|
||||
denom = std::sqrt(norm2(v0cp));
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(v0cp, tmp); // CAREFUL: do not do Op(tmp, tmp)
|
||||
v0cp = tmp;
|
||||
num = std::sqrt(norm2(v0cp)); // num = |A^{n+1} v0|
|
||||
lamApprox = num / denom; // lam = |A^{n+1} v0| / |A^n v0|
|
||||
std::cout << GridLogDebug << "Approx for max eval: " << lamApprox << std::endl;
|
||||
denom = num; // denom = |A^{n} v0|
|
||||
}
|
||||
return lamApprox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of Krylov-Schur eigenvectors that have converged. An eigenvector s is considered converged
|
||||
* for a tolerance epsilon if
|
||||
* r(s) := |\beta e_m^T s| < epsilon
|
||||
* where beta is the norm of f_{m+1}.
|
||||
*
|
||||
* TODO implement in parent class eventually
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* int : Number of converged eigenvectors.
|
||||
*/
|
||||
int converged() {
|
||||
int Nconv = 0;
|
||||
int _Nm = evecs.size();
|
||||
std::cout << GridLogDebug << "b: " << b << std::endl;
|
||||
Field tmp (Grid); Field fullEvec (Grid);
|
||||
ritzEstimates.clear();
|
||||
// ritzEstimates.resize(_Nm);
|
||||
for (int k = 0; k < _Nm; k++) {
|
||||
Eigen::VectorXcd evec_k = littleEvecs.col(k);
|
||||
RealD ritzEstimate = std::abs(b.dot(evec_k)); // b^\dagger s
|
||||
ritzEstimates.push_back(ritzEstimate);
|
||||
std::cout << GridLogMessage << "Ritz estimate for evec " << k << " = " << ritzEstimate << std::endl;
|
||||
if (ritzEstimate < rtol) {
|
||||
Nconv++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check that Ritz estimate is explicitly || D (Uy) - lambda (Uy) ||
|
||||
// checkRitzEstimate();
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Krylov-Schur decomposition DU = UR + f b^\dag with the last-computed
|
||||
* U, R, f, and b.
|
||||
*/
|
||||
bool checkKSDecomposition(RealD tol = 1e-8) {
|
||||
|
||||
std::cout << GridLogMessage << "*** CHECKING KRYLOV-SCHUR DECOMPOSITION *** " << std::endl;
|
||||
|
||||
int k = basis.size(); // number of basis vectors, also the size of Rayleigh.
|
||||
|
||||
// rotate basis by Rayleigh to construct UR
|
||||
// std::vector<Field> rotated;
|
||||
|
||||
// std::cout << GridLogDebug << "Rayleigh in KSDecomposition: " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
std::vector<Field> rotated = basis;
|
||||
constructUR(rotated, basis, Rayleigh, k,k); // manually rotate
|
||||
// Eigen::MatrixXcd Rt = Rayleigh.adjoint();
|
||||
// basisRotate(rotated, Rt, 0, k, 0, k, k); // UR
|
||||
|
||||
// TODO: make a new function that I'm positive does what this is doing
|
||||
// just take the basis U = (u1 u2 ... uNm) and form the linear combination UR from R
|
||||
|
||||
// For each i, form D u(i) and subtract off (US - u b^\dag)(i)
|
||||
RealD delta = 0.0; RealD deltaSum = 0;
|
||||
Field tmp (Grid); tmp = Zero();
|
||||
for (int i = 0; i < k; i++) {
|
||||
Linop.Op(basis[i], tmp); // tmp = D u(i)
|
||||
delta = norm2(tmp - rotated[i] - u * std::conj(b(i)));
|
||||
delta = delta / norm2(tmp); // relative tolerance
|
||||
deltaSum += delta;
|
||||
|
||||
// std::cout << GridLogDebug << "Iteration " << i << std::endl;
|
||||
// std::cout << GridLogDebug << "Du = " << norm2(tmp) << std::endl;
|
||||
// std::cout << GridLogDebug << "rotated = " << norm2(rotated[i]) << std::endl;
|
||||
// std::cout << GridLogDebug << "b[i] = " << b(i) << std::endl;
|
||||
std::cout << GridLogMessage << "Deviation in decomp, column " << i << ": " << delta << std::endl;
|
||||
}
|
||||
std::cout << GridLogMessage << "Squared sum of relative deviations in decomposition: " << deltaSum << std::endl;
|
||||
|
||||
// std::cout << "[DEBUG] testing basis rotate" << std::endl;
|
||||
// std::vector<Field> rotated2;
|
||||
// constructUR(rotated2, basis, Rayleigh, k);
|
||||
// for (int i = 0; i < k; i++) {
|
||||
// std::cout << "rotated[i] - UR[i] = " << norm2(rotated[i] - rotated2[i]) << std::endl;
|
||||
// }
|
||||
|
||||
return deltaSum < tol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Ritz vector s was rotated correctly by explicitly recomputing the
|
||||
* eigenvectors of the rotated Rayleigh quotient.
|
||||
*/
|
||||
bool checkRitzRotation(RealD tol = 1e-8) {
|
||||
std::cout << GridLogMessage << "*** CHECKING RITZ VECTOR ROTATION *** " << std::endl;
|
||||
|
||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
||||
es.compute(Rayleigh);
|
||||
Eigen::MatrixXcd littleEvecs2 = es.eigenvectors();
|
||||
RealD dLittle = (littleEvecs2 - littleEvecs).norm() / littleEvecs.norm();
|
||||
std::cout << GridLogMessage << "|littleEvecs2 - littleEvecs| / |littleEvecs| = " << dLittle << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Forming full eigenvectors" << std::endl;
|
||||
RealD delta = 0.0; RealD deltaSum = 0;
|
||||
for (int k = 0; k < evals.size(); k++) {
|
||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
||||
Field tmpEvec (Grid);
|
||||
tmpEvec = Zero();
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
tmpEvec = tmpEvec + vec[j] * basis[j];
|
||||
}
|
||||
delta = norm2(tmpEvec - evecs[k]) / norm2(evecs[k]);
|
||||
std::cout << GridLogMessage << "Deviation in evec " << k << ": " << delta << std::endl;
|
||||
deltaSum += delta;
|
||||
}
|
||||
return deltaSum < tol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Ritz estimate R(s) is indeed the deviation of a Ritz eigenvector from being a true eigenvector.
|
||||
*/
|
||||
void checkRitzEstimate(RealD tol = 1e-8) {
|
||||
std::cout << GridLogMessage << "*** CHECKING RITZ ESTIMATE *** " << std::endl;
|
||||
|
||||
// The issue was that the Eigen::eigensolver occasionally returned the complex conjugate pairs in the wrong
|
||||
// order compared to the diagonal, which is how I was reading them out. When this happened, the Ritz estimate would
|
||||
// be wrong. So, just need to be more careful and actually read out the eigenvalues.
|
||||
|
||||
Field tmp (Grid);
|
||||
// std::cout << "n evecs: " << evecs.size() << std::endl;
|
||||
for (int k = 0; k < evecs.size(); k++) {
|
||||
tmp = Zero();
|
||||
Linop.Op(evecs[k], tmp); // D evec
|
||||
RealD ritz = std::sqrt(norm2(tmp - evals[k] * evecs[k]));
|
||||
std::cout << "Ritz estimate " << k << " = " << ritz << std::endl;
|
||||
|
||||
// Checking little Ritz estimate
|
||||
// Eigen::VectorXcd littleEvec = littleEvecs.col(k);
|
||||
// Eigen::VectorXcd dev = Rayleigh * littleEvec - evals[k] * littleEvec;
|
||||
// std::cout << GridLogMessage << "Little Ritz estimate = " << dev.norm() << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a vector of fields U (equivalently, a LxN matrix, where L is the number of degrees of
|
||||
* freedom on the lattice field) and an NxN matrix R, forms the product UR.
|
||||
* Only the first N2 output columns are computed; columns [N2, N) are Zero.
|
||||
*
|
||||
* Implemented with the accelerator basisRotate kernel, which computes
|
||||
* basis[j] <- sum_k Qt(j,k) basis[k] in place, so we copy U into UR and
|
||||
* pass R^T. The coefficients are staged as ComplexD (thrust::complex on
|
||||
* GPU builds) since std::complex has no device arithmetic with Grid tensors.
|
||||
*/
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field> &U, Eigen::MatrixXcd& R, int N, int N2) {
|
||||
KSCoeffMat Rt(N);
|
||||
for (int i = 0; i < N; i++)
|
||||
for (int j = 0; j < N; j++)
|
||||
Rt(i, j) = ComplexD(R(j, i).real(), R(j, i).imag());
|
||||
|
||||
UR = U;
|
||||
basisRotate(UR, Rt, 0, N2, 0, N, N);
|
||||
for (int i = N2; i < N; i++) UR[i] = Zero();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as constructUR but for the product order RU.
|
||||
*/
|
||||
void constructRU(std::vector<Field>& RU, std::vector<Field> &U, Eigen::MatrixXcd& R, int N) {
|
||||
Field tmp (Grid);
|
||||
RU.clear();
|
||||
// RU.resize(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < N; j++) {
|
||||
tmp = tmp + R(i, j) * U[j];
|
||||
}
|
||||
RU.push_back(tmp);
|
||||
// RU[i] = tmp;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// void writeEvec(Field& in, std::string const fname){
|
||||
// #ifdef HAVE_LIME
|
||||
// // Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
// std::cout << GridLogMessage << "Writing evec to: " << fname << std::endl;
|
||||
// Grid::emptyUserRecord record;
|
||||
// Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
// WR.open(fname);
|
||||
// WR.writeScidacFieldRecord(in,record,0); // Lexico
|
||||
// WR.close();
|
||||
// #endif
|
||||
// // What is the appropriate way to throw error?
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Writes the eigensystem of a Krylov Schur object to a directory.
|
||||
// *
|
||||
// * Parameters
|
||||
// * ----------
|
||||
// * std::string path
|
||||
// * Directory to write to.
|
||||
// */
|
||||
// void writeEigensystem(std::string outDir) {
|
||||
// std::cout << GridLogMessage << "Writing output to directory: " << outDir << std::endl;
|
||||
// // TODO write a scidac density file so that we can easily integrate with visualization toolkit
|
||||
// std::string evalPath = outDir + "/evals.txt";
|
||||
// std::ofstream fEval;
|
||||
// fEval.open(evalPath);
|
||||
// for (int i = 0; i < Nk; i++) {
|
||||
// // write Eigenvalues
|
||||
// fEval << i << " " << evals(i);
|
||||
// if (i < Nk - 1) { fEval << "\n"; }
|
||||
// }
|
||||
// fEval.close();
|
||||
|
||||
// for (int i = 0; i < Nk; i++) {
|
||||
// std::string fName = outDir + "/evec" + std::to_string(i);
|
||||
// // writeFile(evecs[i], fName); // using method from Grid/HMC/ComputeWilsonFlow.cc
|
||||
// // writeEvec(evecs[i], fName);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -0,0 +1,276 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./Grid/algorithms/iterative/LanczosBidiagonalization.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_LANCZOS_BIDIAGONALIZATION_H
|
||||
#define GRID_LANCZOS_BIDIAGONALIZATION_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Lanczos Bidiagonalization (Golub-Kahan)
|
||||
*
|
||||
* For a linear operator A with adjoint A^dag, constructs the bidiagonal
|
||||
* decomposition:
|
||||
*
|
||||
* A V_m = U_m B_m
|
||||
* A^dag U_m = V_m B_m^T + beta_{m+1} v_{m+1} e_m^T
|
||||
*
|
||||
* where:
|
||||
* V_m = [v_1, ..., v_m] right Lanczos vectors (orthonormal)
|
||||
* U_m = [u_1, ..., u_m] left Lanczos vectors (orthonormal)
|
||||
* B_m is upper bidiagonal with diag(alpha_1,...,alpha_m) and
|
||||
* superdiag(beta_2,...,beta_m)
|
||||
*
|
||||
* The singular values of A are approximated by those of B_m.
|
||||
* The singular values of B_m are the square roots of the eigenvalues of
|
||||
* the symmetric tridiagonal matrix B_m^T B_m.
|
||||
*
|
||||
* Usage:
|
||||
* LanczosBidiagonalization<Field> lb(Linop, grid);
|
||||
* lb.run(src, Nm, tol);
|
||||
* // Access results via getters.
|
||||
*/
|
||||
template <class Field>
|
||||
class LanczosBidiagonalization {
|
||||
|
||||
public:
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
int Nm; // number of Lanczos steps taken
|
||||
RealD Tolerance; // convergence threshold on beta_{k+1} / alpha_k
|
||||
|
||||
std::vector<Field> V; // right Lanczos vectors v_1 ... v_m
|
||||
std::vector<Field> U; // left Lanczos vectors u_1 ... u_m
|
||||
std::vector<RealD> alpha; // diagonal of bidiagonal matrix
|
||||
std::vector<RealD> beta; // super-diagonal (beta[k] couples u_k and v_{k+1})
|
||||
|
||||
// SVD of the bidiagonal matrix (filled after computeSVD())
|
||||
Eigen::VectorXd singularValues;
|
||||
Eigen::MatrixXd leftSVecs; // columns are left singular vectors of B
|
||||
Eigen::MatrixXd rightSVecs; // columns are right singular vectors of B
|
||||
|
||||
public:
|
||||
|
||||
LanczosBidiagonalization(LinearOperatorBase<Field> &_Linop, GridBase *_Grid,
|
||||
RealD _tol = 1.0e-8)
|
||||
: Linop(_Linop), Grid(_Grid), Tolerance(_tol), Nm(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Run the Golub-Kahan Lanczos bidiagonalization.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* src : starting vector (need not be normalised)
|
||||
* Nmax : maximum number of Lanczos steps
|
||||
* reorth : if true, full reorthogonalisation of both V and U bases
|
||||
*/
|
||||
void run(const Field &src, int Nmax, bool reorth = true)
|
||||
{
|
||||
assert(norm2(src) > 0.0);
|
||||
|
||||
V.clear(); U.clear();
|
||||
alpha.clear(); beta.clear();
|
||||
Nm = 0;
|
||||
|
||||
Field p(Grid), r(Grid);
|
||||
|
||||
// --- initialise: v_1 = src / ||src|| ---
|
||||
Field v(Grid);
|
||||
v = src;
|
||||
RealD nrm = std::sqrt(norm2(v));
|
||||
v = (1.0 / nrm) * v;
|
||||
V.push_back(v);
|
||||
|
||||
for (int k = 0; k < Nmax; ++k) {
|
||||
|
||||
// p = A v_k
|
||||
Linop.Op(V[k], p);
|
||||
|
||||
// p = p - beta_k * u_{k-1} (remove previous left vector)
|
||||
if (k > 0) {
|
||||
p = p - beta[k-1] * U[k-1];
|
||||
}
|
||||
|
||||
// alpha_k = ||p||
|
||||
RealD ak = std::sqrt(norm2(p));
|
||||
if (ak < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization: lucky breakdown at step "
|
||||
<< k << " (alpha = " << ak << ")" << std::endl;
|
||||
break;
|
||||
}
|
||||
alpha.push_back(ak);
|
||||
|
||||
// u_k = p / alpha_k
|
||||
Field u(Grid);
|
||||
u = (1.0 / ak) * p;
|
||||
|
||||
// full reorthogonalisation of u against previous U
|
||||
if (reorth) {
|
||||
for (int j = 0; j < (int)U.size(); ++j) {
|
||||
ComplexD ip = innerProduct(U[j], u);
|
||||
u = u - ip * U[j];
|
||||
}
|
||||
RealD unrm = std::sqrt(norm2(u));
|
||||
if (unrm > 1.0e-14) u = (1.0 / unrm) * u;
|
||||
}
|
||||
U.push_back(u);
|
||||
|
||||
// r = A^dag u_k - alpha_k * v_k
|
||||
Linop.AdjOp(U[k], r);
|
||||
r = r - ak * V[k];
|
||||
|
||||
// full reorthogonalisation of r against previous V
|
||||
if (reorth) {
|
||||
for (int j = 0; j < (int)V.size(); ++j) {
|
||||
ComplexD ip = innerProduct(V[j], r);
|
||||
r = r - ip * V[j];
|
||||
}
|
||||
}
|
||||
|
||||
// beta_{k+1} = ||r||
|
||||
RealD bk = std::sqrt(norm2(r));
|
||||
beta.push_back(bk);
|
||||
|
||||
Nm = k + 1;
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization step " << k
|
||||
<< " alpha = " << ak
|
||||
<< " beta = " << bk << std::endl;
|
||||
|
||||
// convergence: residual beta / alpha small enough
|
||||
if (bk / ak < Tolerance) {
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization converged at step " << k
|
||||
<< " (beta/alpha = " << bk / ak << ")" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (k == Nmax - 1) break; // no v_{k+2} needed after last step
|
||||
|
||||
// v_{k+1} = r / beta_{k+1}
|
||||
Field vnext(Grid);
|
||||
vnext = (1.0 / bk) * r;
|
||||
V.push_back(vnext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the SVD of the bidiagonal matrix B using Eigen.
|
||||
* Singular values are stored in descending order.
|
||||
*/
|
||||
void computeSVD()
|
||||
{
|
||||
int m = Nm;
|
||||
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(m, m);
|
||||
|
||||
for (int k = 0; k < m; ++k) {
|
||||
B(k, k) = alpha[k];
|
||||
if (k + 1 < m && k < (int)beta.size())
|
||||
B(k, k+1) = beta[k];
|
||||
}
|
||||
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
|
||||
singularValues = svd.singularValues(); // already sorted descending
|
||||
leftSVecs = svd.matrixU();
|
||||
rightSVecs = svd.matrixV();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization: singular values of B_" << m
|
||||
<< std::endl;
|
||||
for (int k = 0; k < m; ++k)
|
||||
std::cout << GridLogMessage << " sigma[" << k << "] = "
|
||||
<< singularValues(k) << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the k-th approximate left singular vector of A in the full
|
||||
* lattice space. computeSVD() must have been called first.
|
||||
*/
|
||||
Field leftSingularVector(int k)
|
||||
{
|
||||
assert(k < (int)leftSVecs.cols());
|
||||
Field svec(Grid);
|
||||
svec = Zero();
|
||||
for (int j = 0; j < Nm; ++j)
|
||||
svec = svec + leftSVecs(j, k) * U[j];
|
||||
return svec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the k-th approximate right singular vector of A in the full
|
||||
* lattice space. computeSVD() must have been called first.
|
||||
*/
|
||||
Field rightSingularVector(int k)
|
||||
{
|
||||
assert(k < (int)rightSVecs.cols());
|
||||
Field svec(Grid);
|
||||
svec = Zero();
|
||||
for (int j = 0; j < Nm; ++j)
|
||||
svec = svec + rightSVecs(j, k) * V[j];
|
||||
return svec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the bidiagonalization: returns max residual
|
||||
* max_k || A v_k - alpha_k u_k - beta_k u_{k-1} ||
|
||||
*/
|
||||
RealD verify()
|
||||
{
|
||||
Field tmp(Grid);
|
||||
RealD maxres = 0.0;
|
||||
for (int k = 0; k < Nm; ++k) {
|
||||
Linop.Op(V[k], tmp);
|
||||
tmp = tmp - alpha[k] * U[k];
|
||||
if (k > 0 && k-1 < (int)beta.size())
|
||||
tmp = tmp - beta[k-1] * U[k-1];
|
||||
RealD res = std::sqrt(norm2(tmp));
|
||||
if (res > maxres) maxres = res;
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization verify step " << k
|
||||
<< " ||A v_k - alpha_k u_k - beta_{k-1} u_{k-1}|| = "
|
||||
<< res << std::endl;
|
||||
}
|
||||
return maxres;
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
int getNm() const { return Nm; }
|
||||
const std::vector<Field>& getV() const { return V; }
|
||||
const std::vector<Field>& getU() const { return U; }
|
||||
const std::vector<RealD>& getAlpha() const { return alpha; }
|
||||
const std::vector<RealD>& getBeta() const { return beta; }
|
||||
Eigen::VectorXd getSingularValues() const { return singularValues; }
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
int, MinRes); // Must restart
|
||||
};
|
||||
|
||||
//This class is the input parameter class for some testing programs
|
||||
struct LocalCoherenceLanczosParams : Serializable {
|
||||
public:
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LocalCoherenceLanczosParams,
|
||||
@@ -67,6 +68,7 @@ public:
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class ProjectedHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > {
|
||||
public:
|
||||
using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator();
|
||||
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
||||
typedef Lattice<CoarseSiteVector> CoarseField;
|
||||
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
||||
@@ -78,7 +80,7 @@ public:
|
||||
ProjectedHermOp(LinearOperatorBase<FineField>& linop, std::vector<FineField> & _subspace) :
|
||||
_Linop(linop), subspace(_subspace)
|
||||
{
|
||||
assert(subspace.size() >0);
|
||||
GRID_ASSERT(subspace.size() >0);
|
||||
};
|
||||
|
||||
void operator()(const CoarseField& in, CoarseField& out) {
|
||||
@@ -97,6 +99,7 @@ public:
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class ProjectedFunctionHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > {
|
||||
public:
|
||||
using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator();
|
||||
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
||||
typedef Lattice<CoarseSiteVector> CoarseField;
|
||||
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
||||
@@ -143,16 +146,24 @@ public:
|
||||
LinearOperatorBase<FineField> &_Linop;
|
||||
RealD _coarse_relax_tol;
|
||||
std::vector<FineField> &_subspace;
|
||||
|
||||
int _largestEvalIdxForReport; //The convergence of the LCL is based on the evals of the coarse grid operator, not those of the underlying fine grid operator
|
||||
//As a result we do not know what the eval range of the fine operator is until the very end, making tuning the Cheby bounds very difficult
|
||||
//To work around this issue, every restart we separately reconstruct the fine operator eval for the lowest and highest evec and print these
|
||||
//out alongside the evals of the coarse operator. To do so we need to know the index of the largest eval (i.e. Nstop-1)
|
||||
//NOTE: If largestEvalIdxForReport=-1 (default) then this is not performed
|
||||
|
||||
ImplicitlyRestartedLanczosSmoothedTester(LinearFunction<CoarseField> &Poly,
|
||||
OperatorFunction<FineField> &smoother,
|
||||
LinearOperatorBase<FineField> &Linop,
|
||||
std::vector<FineField> &subspace,
|
||||
RealD coarse_relax_tol=5.0e3)
|
||||
RealD coarse_relax_tol=5.0e3,
|
||||
int largestEvalIdxForReport=-1)
|
||||
: _smoother(smoother), _Linop(Linop), _Poly(Poly), _subspace(subspace),
|
||||
_coarse_relax_tol(coarse_relax_tol)
|
||||
_coarse_relax_tol(coarse_relax_tol), _largestEvalIdxForReport(largestEvalIdxForReport)
|
||||
{ };
|
||||
|
||||
//evalMaxApprox: approximation of largest eval of the fine Chebyshev operator (suitably wrapped by block projection)
|
||||
int TestConvergence(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
||||
{
|
||||
CoarseField v(B);
|
||||
@@ -175,12 +186,26 @@ public:
|
||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||
<<std::endl;
|
||||
|
||||
if(_largestEvalIdxForReport != -1 && (j==0 || j==_largestEvalIdxForReport)){
|
||||
std::cout<<GridLogIRL << "Estimating true eval of fine grid operator for eval idx " << j << std::endl;
|
||||
RealD tmp_eval;
|
||||
ReconstructEval(j,eresid,B,tmp_eval,1.0); //don't use evalMaxApprox of coarse operator! (cf below)
|
||||
}
|
||||
|
||||
int conv=0;
|
||||
if( (vv<eresid*eresid) ) conv = 1;
|
||||
return conv;
|
||||
}
|
||||
int ReconstructEval(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
||||
|
||||
//This function is called at the end of the coarse grid Lanczos. It promotes the coarse eigenvector 'B' to the fine grid,
|
||||
//applies a smoother to the result then computes the computes the *fine grid* eigenvalue (output as 'eval').
|
||||
|
||||
//evalMaxApprox should be the approximation of the largest eval of the fine Hermop. However when this function is called by IRL it actually passes the largest eval of the *Chebyshev* operator (as this is the max approx used for the TestConvergence above)
|
||||
//As the largest eval of the Chebyshev is typically several orders of magnitude larger this makes the convergence test pass even when it should not.
|
||||
//We therefore ignore evalMaxApprox here and use a value of 1.0 (note this value is already used by TestCoarse)
|
||||
int ReconstructEval(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
||||
{
|
||||
evalMaxApprox = 1.0; //cf above
|
||||
GridBase *FineGrid = _subspace[0].Grid();
|
||||
int checkerboard = _subspace[0].Checkerboard();
|
||||
FineField fB(FineGrid);fB.Checkerboard() =checkerboard;
|
||||
@@ -199,13 +224,13 @@ public:
|
||||
eval = vnum/vden;
|
||||
fv -= eval*fB;
|
||||
RealD vv = norm2(fv) / ::pow(evalMaxApprox,2.0);
|
||||
|
||||
if ( j > nbasis ) eresid = eresid*_coarse_relax_tol;
|
||||
|
||||
std::cout.precision(13);
|
||||
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
||||
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv << " target " << eresid*eresid
|
||||
<<std::endl;
|
||||
if ( j > nbasis ) eresid = eresid*_coarse_relax_tol;
|
||||
if( (vv<eresid*eresid) ) return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -283,6 +308,10 @@ public:
|
||||
evals_coarse.resize(0);
|
||||
};
|
||||
|
||||
//The block inner product is the inner product on the fine grid locally summed over the blocks
|
||||
//to give a Lattice<Scalar> on the coarse grid. This function orthnormalizes the fine-grid subspace
|
||||
//vectors under the block inner product. This step must be performed after computing the fine grid
|
||||
//eigenvectors and before computing the coarse grid eigenvectors.
|
||||
void Orthogonalise(void ) {
|
||||
CoarseScalar InnerProd(_CoarseGrid);
|
||||
std::cout << GridLogMessage <<" Gramm-Schmidt pass 1"<<std::endl;
|
||||
@@ -317,19 +346,21 @@ public:
|
||||
|
||||
void testFine(RealD resid)
|
||||
{
|
||||
assert(evals_fine.size() == nbasis);
|
||||
assert(subspace.size() == nbasis);
|
||||
GRID_ASSERT(evals_fine.size() == nbasis);
|
||||
GRID_ASSERT(subspace.size() == nbasis);
|
||||
PlainHermOp<FineField> Op(_FineOp);
|
||||
ImplicitlyRestartedLanczosHermOpTester<FineField> SimpleTester(Op);
|
||||
for(int k=0;k<nbasis;k++){
|
||||
assert(SimpleTester.ReconstructEval(k,resid,subspace[k],evals_fine[k],1.0)==1);
|
||||
GRID_ASSERT(SimpleTester.ReconstructEval(k,resid,subspace[k],evals_fine[k],1.0)==1);
|
||||
}
|
||||
}
|
||||
|
||||
//While this method serves to check the coarse eigenvectors, it also recomputes the eigenvalues from the smoothed reconstructed eigenvectors
|
||||
//hence the smoother can be tuned after running the coarse Lanczos by using a different smoother here
|
||||
void testCoarse(RealD resid,ChebyParams cheby_smooth,RealD relax)
|
||||
{
|
||||
assert(evals_fine.size() == nbasis);
|
||||
assert(subspace.size() == nbasis);
|
||||
GRID_ASSERT(evals_fine.size() == nbasis);
|
||||
GRID_ASSERT(subspace.size() == nbasis);
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -349,7 +380,7 @@ public:
|
||||
void calcFine(ChebyParams cheby_parms,int Nstop,int Nk,int Nm,RealD resid,
|
||||
RealD MaxIt, RealD betastp, int MinRes)
|
||||
{
|
||||
assert(nbasis<=Nm);
|
||||
GRID_ASSERT(nbasis<=Nm);
|
||||
Chebyshev<FineField> Cheby(cheby_parms);
|
||||
FunctionHermOp<FineField> ChebyOp(Cheby,_FineOp);
|
||||
PlainHermOp<FineField> Op(_FineOp);
|
||||
@@ -369,40 +400,54 @@ public:
|
||||
IRL.calc(evals_fine,subspace,src,Nconv,false);
|
||||
|
||||
// Shrink down to number saved
|
||||
assert(Nstop>=nbasis);
|
||||
assert(Nconv>=nbasis);
|
||||
GRID_ASSERT(Nstop>=nbasis);
|
||||
GRID_ASSERT(Nconv>=nbasis);
|
||||
evals_fine.resize(nbasis);
|
||||
subspace.resize(nbasis,_FineGrid);
|
||||
}
|
||||
|
||||
|
||||
//cheby_op: Parameters of the fine grid Chebyshev polynomial used for the Lanczos acceleration
|
||||
//cheby_smooth: Parameters of a separate Chebyshev polynomial used after the Lanczos has completed to smooth out high frequency noise in the reconstructed fine grid eigenvectors prior to computing the eigenvalue
|
||||
//relax: Reconstructed eigenvectors (post smoothing) are naturally not as precise as true eigenvectors. This factor acts as a multiplier on the stopping condition when determining whether the results satisfy the user provided stopping condition
|
||||
void calcCoarse(ChebyParams cheby_op,ChebyParams cheby_smooth,RealD relax,
|
||||
int Nstop, int Nk, int Nm,RealD resid,
|
||||
RealD MaxIt, RealD betastp, int MinRes)
|
||||
{
|
||||
Chebyshev<FineField> Cheby(cheby_op);
|
||||
ProjectedHermOp<Fobj,CComplex,nbasis> Op(_FineOp,subspace);
|
||||
ProjectedFunctionHermOp<Fobj,CComplex,nbasis> ChebyOp (Cheby,_FineOp,subspace);
|
||||
Chebyshev<FineField> Cheby(cheby_op); //Chebyshev of fine operator on fine grid
|
||||
ProjectedHermOp<Fobj,CComplex,nbasis> Op(_FineOp,subspace); //Fine operator on coarse grid with intermediate fine grid conversion
|
||||
ProjectedFunctionHermOp<Fobj,CComplex,nbasis> ChebyOp (Cheby,_FineOp,subspace); //Chebyshev of fine operator on coarse grid with intermediate fine grid conversion
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Chebyshev<FineField> ChebySmooth(cheby_smooth);
|
||||
ImplicitlyRestartedLanczosSmoothedTester<Fobj,CComplex,nbasis> ChebySmoothTester(ChebyOp,ChebySmooth,_FineOp,subspace,relax);
|
||||
Chebyshev<FineField> ChebySmooth(cheby_smooth); //lower order Chebyshev of fine operator on fine grid used to smooth regenerated eigenvectors
|
||||
ImplicitlyRestartedLanczosSmoothedTester<Fobj,CComplex,nbasis> ChebySmoothTester(ChebyOp,ChebySmooth,_FineOp,subspace,relax,Nstop-1);
|
||||
|
||||
evals_coarse.resize(Nm);
|
||||
evec_coarse.resize(Nm,_CoarseGrid);
|
||||
|
||||
CoarseField src(_CoarseGrid); src=1.0;
|
||||
|
||||
//Note the "tester" here is also responsible for generating the fine grid eigenvalues which are output into the "evals_coarse" array
|
||||
ImplicitlyRestartedLanczos<CoarseField> IRL(ChebyOp,ChebyOp,ChebySmoothTester,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes);
|
||||
int Nconv=0;
|
||||
IRL.calc(evals_coarse,evec_coarse,src,Nconv,false);
|
||||
assert(Nconv>=Nstop);
|
||||
GRID_ASSERT(Nconv>=Nstop);
|
||||
evals_coarse.resize(Nstop);
|
||||
evec_coarse.resize (Nstop,_CoarseGrid);
|
||||
for (int i=0;i<Nstop;i++){
|
||||
std::cout << i << " Coarse eval = " << evals_coarse[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//Get the fine eigenvector 'i' by reconstruction
|
||||
void getFineEvecEval(FineField &evec, RealD &eval, const int i) const{
|
||||
blockPromote(evec_coarse[i],evec,subspace);
|
||||
eval = evals_coarse[i];
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -35,7 +35,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
|
||||
public:
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // throw an assert when the MR fails to converge.
|
||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the MR fails to converge.
|
||||
// Defaults true.
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
@@ -59,7 +59,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
|
||||
|
||||
// Initial residual computation & set up
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
RealD ssq = norm2(src);
|
||||
RealD rsq = Tolerance * Tolerance * ssq;
|
||||
@@ -136,7 +136,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
|
||||
std::cout << GridLogMessage << "MR Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(true_residual / Tolerance < 10000.0);
|
||||
GRID_ASSERT(true_residual / Tolerance < 10000.0);
|
||||
|
||||
IterationsToComplete = k;
|
||||
|
||||
@@ -148,7 +148,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
|
||||
<< std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
|
||||
IterationsToComplete = k;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
|
||||
|
||||
using OperatorFunction<FieldD>::operator();
|
||||
|
||||
bool ErrorOnNoConverge; // Throw an assert when MPFGMRES fails to converge,
|
||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when MPFGMRES fails to converge,
|
||||
// defaults to true
|
||||
|
||||
RealD Tolerance;
|
||||
@@ -91,7 +91,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
|
||||
conformable(psi, src);
|
||||
|
||||
RealD guess = norm2(psi);
|
||||
assert(std::isnan(guess) == 0);
|
||||
GRID_ASSERT(std::isnan(guess) == 0);
|
||||
|
||||
RealD cp;
|
||||
RealD ssq = norm2(src);
|
||||
@@ -150,7 +150,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
|
||||
std::cout << GridLogMessage << "MPFGMRES did NOT converge" << std::endl;
|
||||
|
||||
if (ErrorOnNoConverge)
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD outerLoopBody(LinearOperatorBase<FieldD> &LinOp, const FieldD &src, FieldD &psi, RealD rsq) {
|
||||
@@ -197,7 +197,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
|
||||
}
|
||||
}
|
||||
|
||||
assert(0); // Never reached
|
||||
GRID_ASSERT(0); // Never reached
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Take a matrix and form an NE solver calling a Herm solver
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template<class Field> class NormalEquations {
|
||||
template<class Field> class NormalEquations : public LinearFunction<Field>{
|
||||
private:
|
||||
SparseMatrixBase<Field> & _Matrix;
|
||||
OperatorFunction<Field> & _HermitianSolver;
|
||||
@@ -60,7 +60,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<class Field> class HPDSolver {
|
||||
template<class Field> class NormalResidual : public LinearFunction<Field>{
|
||||
private:
|
||||
SparseMatrixBase<Field> & _Matrix;
|
||||
OperatorFunction<Field> & _HermitianSolver;
|
||||
LinearFunction<Field> & _Guess;
|
||||
public:
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// Wrap the usual normal equations trick
|
||||
/////////////////////////////////////////////////////
|
||||
NormalResidual(SparseMatrixBase<Field> &Matrix, OperatorFunction<Field> &HermitianSolver,
|
||||
LinearFunction<Field> &Guess)
|
||||
: _Matrix(Matrix), _HermitianSolver(HermitianSolver), _Guess(Guess) {};
|
||||
|
||||
void operator() (const Field &in, Field &out){
|
||||
|
||||
Field res(in.Grid());
|
||||
Field tmp(in.Grid());
|
||||
|
||||
MMdagLinearOperator<SparseMatrixBase<Field>,Field> MMdagOp(_Matrix);
|
||||
_Guess(in,res);
|
||||
_HermitianSolver(MMdagOp,in,res); // M Mdag res = in ;
|
||||
_Matrix.Mdag(res,out); // out = Mdag res
|
||||
}
|
||||
};
|
||||
|
||||
template<class Field> class HPDSolver : public LinearFunction<Field> {
|
||||
private:
|
||||
LinearOperatorBase<Field> & _Matrix;
|
||||
OperatorFunction<Field> & _HermitianSolver;
|
||||
@@ -78,13 +104,13 @@ public:
|
||||
void operator() (const Field &in, Field &out){
|
||||
|
||||
_Guess(in,out);
|
||||
_HermitianSolver(_Matrix,in,out); // Mdag M out = Mdag in
|
||||
_HermitianSolver(_Matrix,in,out); //M out = in
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class Field> class MdagMSolver {
|
||||
template<class Field> class MdagMSolver : public LinearFunction<Field> {
|
||||
private:
|
||||
SparseMatrixBase<Field> & _Matrix;
|
||||
OperatorFunction<Field> & _HermitianSolver;
|
||||
|
||||
@@ -20,7 +20,7 @@ template<class Field> class PowerMethod
|
||||
RealD evalMaxApprox = 0.0;
|
||||
auto src_n = src;
|
||||
auto tmp = src;
|
||||
const int _MAX_ITER_EST_ = 50;
|
||||
const int _MAX_ITER_EST_ = 200;
|
||||
|
||||
for (int i=0;i<_MAX_ITER_EST_;i++) {
|
||||
|
||||
@@ -29,17 +29,18 @@ template<class Field> class PowerMethod
|
||||
RealD vnum = real(innerProduct(src_n,tmp)); // HermOp.
|
||||
RealD vden = norm2(src_n);
|
||||
RealD na = vnum/vden;
|
||||
|
||||
std::cout << GridLogMessage << "PowerMethod: Current approximation of largest eigenvalue " << na << std::endl;
|
||||
|
||||
if ( (fabs(evalMaxApprox/na - 1.0) < 0.001) || (i==_MAX_ITER_EST_-1) ) {
|
||||
evalMaxApprox = na;
|
||||
std::cout << GridLogMessage << " Approximation of largest eigenvalue: " << evalMaxApprox << std::endl;
|
||||
return evalMaxApprox;
|
||||
}
|
||||
// if ( (fabs(evalMaxApprox/na - 1.0) < 0.0001) || (i==_MAX_ITER_EST_-1) ) {
|
||||
// evalMaxApprox = na;
|
||||
// return evalMaxApprox;
|
||||
// }
|
||||
evalMaxApprox = na;
|
||||
src_n = tmp;
|
||||
}
|
||||
assert(0);
|
||||
return 0;
|
||||
std::cout << GridLogMessage << " Approximation of largest eigenvalue: " << evalMaxApprox << std::endl;
|
||||
return evalMaxApprox;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
namespace Grid {
|
||||
|
||||
class Band
|
||||
{
|
||||
RealD lo, hi;
|
||||
public:
|
||||
Band(RealD _lo,RealD _hi)
|
||||
{
|
||||
lo=_lo;
|
||||
hi=_hi;
|
||||
}
|
||||
RealD operator() (RealD x){
|
||||
if ( x>lo && x<hi ){
|
||||
return 1.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PowerSpectrum
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename T> static RealD normalise(T& v)
|
||||
{
|
||||
RealD nn = norm2(v);
|
||||
nn = sqrt(nn);
|
||||
v = v * (1.0/nn);
|
||||
return nn;
|
||||
}
|
||||
|
||||
std::vector<RealD> ranges;
|
||||
std::vector<int> order;
|
||||
|
||||
PowerSpectrum( std::vector<RealD> &bins, std::vector<int> &_order ) : ranges(bins), order(_order) { };
|
||||
|
||||
template<class Field>
|
||||
RealD operator()(LinearOperatorBase<Field> &HermOp, const Field &src)
|
||||
{
|
||||
GridBase *grid = src.Grid();
|
||||
int N=ranges.size();
|
||||
RealD hi = ranges[N-1];
|
||||
|
||||
RealD lo_band = 0.0;
|
||||
RealD hi_band;
|
||||
RealD nn=norm2(src);
|
||||
RealD ss=0.0;
|
||||
|
||||
Field tmp = src;
|
||||
|
||||
for(int b=0;b<N;b++){
|
||||
hi_band = ranges[b];
|
||||
Band Notch(lo_band,hi_band);
|
||||
|
||||
Chebyshev<Field> polynomial;
|
||||
polynomial.Init(0.0,hi,order[b],Notch);
|
||||
polynomial.JacksonSmooth();
|
||||
|
||||
polynomial(HermOp,src,tmp) ;
|
||||
|
||||
RealD p=norm2(tmp);
|
||||
ss=ss+p;
|
||||
std::cout << GridLogMessage << " PowerSpectrum Band["<<lo_band<<","<<hi_band<<"] power "<<norm2(tmp)/nn<<std::endl;
|
||||
|
||||
lo_band=hi_band;
|
||||
}
|
||||
std::cout << GridLogMessage << " PowerSpectrum total power "<<ss/nn<<std::endl;
|
||||
std::cout << GridLogMessage << " PowerSpectrum total power (unnormalised) "<<nn<<std::endl;
|
||||
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
}
|
||||
|
||||
std::cout<<GridLogMessage<<"PrecConjugateResidual did NOT converge"<<std::endl;
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
};
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -43,7 +43,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
template<class Field>
|
||||
class PrecGeneralisedConjugateResidual : public LinearFunction<Field> {
|
||||
public:
|
||||
|
||||
using LinearFunction<Field>::operator();
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
int verbose;
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
|
||||
}
|
||||
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
|
||||
// assert(0);
|
||||
// GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
|
||||
@@ -221,7 +221,7 @@ public:
|
||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
||||
for(int back=0;back<northog;back++){
|
||||
|
||||
int peri_back=(k-back)%mmax; assert((k-back)>=0);
|
||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
||||
|
||||
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
||||
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
||||
LinalgTimer.Stop();
|
||||
}
|
||||
assert(0); // never reached
|
||||
GRID_ASSERT(0); // never reached
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/PrecGeneralisedConjugateResidual.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_PREC_GCR_NON_HERM_H
|
||||
#define GRID_PREC_GCR_NON_HERM_H
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//VPGCR Abe and Zhang, 2005.
|
||||
//INTERNATIONAL JOURNAL OF NUMERICAL ANALYSIS AND MODELING
|
||||
//Computing and Information Volume 2, Number 2, Pages 147-161
|
||||
//NB. Likely not original reference since they are focussing on a preconditioner variant.
|
||||
// but VPGCR was nicely written up in their paper
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< " Level "<<level<<" "
|
||||
|
||||
template<class Field>
|
||||
class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> {
|
||||
public:
|
||||
using LinearFunction<Field>::operator();
|
||||
RealD Tolerance;
|
||||
Integer MaxIterations;
|
||||
int verbose;
|
||||
int mmax;
|
||||
int nstep;
|
||||
int steps;
|
||||
int level;
|
||||
GridStopWatch PrecTimer;
|
||||
GridStopWatch MatTimer;
|
||||
GridStopWatch LinalgTimer;
|
||||
|
||||
LinearFunction<Field> &Preconditioner;
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
|
||||
void Level(int lv) { level=lv; };
|
||||
|
||||
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax, int _nstep) :
|
||||
Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
Linop(_Linop),
|
||||
Preconditioner(Prec),
|
||||
mmax(_mmax),
|
||||
nstep(_nstep) // what is nstep vs mmax? one is the number of inner iterations
|
||||
{
|
||||
level=1;
|
||||
verbose=1;
|
||||
};
|
||||
|
||||
// virtual method stubs for updating GCR polynomial
|
||||
virtual void LogBegin(void){
|
||||
std::cout << "GCR::LogBegin() "<<std::endl;
|
||||
};
|
||||
virtual void LogIteration(int k, ComplexD a, std::vector<ComplexD> betas){
|
||||
std::cout << "GCR::LogIteration() "<<std::endl;
|
||||
};
|
||||
virtual void LogComplete(std::vector<ComplexD>& alphas, std::vector<std::vector<ComplexD>>& betas) {
|
||||
std::cout << "GCR::LogComplete() "<<std::endl;
|
||||
};
|
||||
|
||||
void operator() (const Field &src, Field &psi){
|
||||
|
||||
// psi=Zero();
|
||||
RealD cp, ssq,rsq;
|
||||
ssq=norm2(src);
|
||||
rsq=Tolerance*Tolerance*ssq;
|
||||
|
||||
Field r(src.Grid());
|
||||
|
||||
PrecTimer.Reset();
|
||||
MatTimer.Reset();
|
||||
LinalgTimer.Reset();
|
||||
|
||||
GridStopWatch SolverTimer;
|
||||
SolverTimer.Start();
|
||||
|
||||
steps=0;
|
||||
for(int k=0;k<MaxIterations;k++){
|
||||
|
||||
cp=GCRnStep(src,psi,rsq);
|
||||
|
||||
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<" target "<<rsq <<std::endl;
|
||||
|
||||
if(cp<rsq) {
|
||||
SolverTimer.Stop();
|
||||
|
||||
Linop.Op(psi,r);
|
||||
axpy(r,-1.0,src,r);
|
||||
RealD tr = norm2(r);
|
||||
GCRLogLevel<<"PGCR: Converged on iteration " <<steps
|
||||
<< " computed residual "<<sqrt(cp/ssq)
|
||||
<< " true residual " <<sqrt(tr/ssq)
|
||||
<< " target " <<Tolerance <<std::endl;
|
||||
|
||||
GCRLogLevel<<"PGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
|
||||
// GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
|
||||
|
||||
RealD cp;
|
||||
ComplexD a, b;
|
||||
// ComplexD zAz;
|
||||
RealD zAAz;
|
||||
ComplexD rq;
|
||||
|
||||
GridBase *grid = src.Grid();
|
||||
|
||||
Field r(grid);
|
||||
Field z(grid);
|
||||
Field tmp(grid);
|
||||
Field ttmp(grid);
|
||||
Field Az(grid);
|
||||
|
||||
////////////////////////////////
|
||||
// history for flexible orthog
|
||||
////////////////////////////////
|
||||
std::vector<Field> q(mmax,grid); // q = Ap
|
||||
std::vector<Field> p(mmax,grid); // store mmax conjugate momenta
|
||||
std::vector<RealD> qq(mmax); // qq = (Ap)^2 = <p|A^\dagger A |p> (denom of \alpha)
|
||||
|
||||
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
|
||||
|
||||
//////////////////////////////////
|
||||
// initial guess x0 is taken as nonzero.
|
||||
// r0=src-A x0 = src
|
||||
//////////////////////////////////
|
||||
MatTimer.Start();
|
||||
Linop.Op(psi,Az);
|
||||
// zAz = innerProduct(Az,psi);
|
||||
zAAz= norm2(Az);
|
||||
MatTimer.Stop();
|
||||
|
||||
|
||||
LinalgTimer.Start();
|
||||
r=src-Az;
|
||||
LinalgTimer.Stop();
|
||||
GCRLogLevel<< "PGCR true residual r = src - A psi "<< norm2(r) <<std::endl;
|
||||
|
||||
this->LogBegin(); // initialize polynomial GCR if needed (TODO think about placement of this)
|
||||
|
||||
/////////////////////
|
||||
// p = Prec(r)
|
||||
/////////////////////
|
||||
|
||||
PrecTimer.Start();
|
||||
Preconditioner(r,z);
|
||||
PrecTimer.Stop();
|
||||
|
||||
MatTimer.Start();
|
||||
Linop.Op(z,Az);
|
||||
MatTimer.Stop();
|
||||
|
||||
LinalgTimer.Start();
|
||||
|
||||
// zAz = innerProduct(Az,psi);
|
||||
zAAz= norm2(Az);
|
||||
|
||||
//p[0],q[0],qq[0]
|
||||
p[0]= z;
|
||||
q[0]= Az;
|
||||
qq[0]= zAAz;
|
||||
|
||||
std::cout << "||init p - src||: " << norm2(p[0] - src) << std::endl; // for debugging
|
||||
|
||||
cp =norm2(r);
|
||||
LinalgTimer.Stop();
|
||||
|
||||
std::vector<ComplexD> all_alphas;
|
||||
std::vector<std::vector<ComplexD>> all_betas;
|
||||
|
||||
for(int k=0;k<nstep;k++){
|
||||
|
||||
steps++;
|
||||
|
||||
int kp = k+1;
|
||||
int peri_k = k %mmax; // only store mmax vectors; just roll around if needed
|
||||
int peri_kp= kp%mmax;
|
||||
|
||||
// std::cout << "peri_kp = " << peri_kp << std::endl;
|
||||
|
||||
LinalgTimer.Start();
|
||||
rq= innerProduct(q[peri_k],r); // what if rAr not real?
|
||||
a = rq/qq[peri_k]; // compute alpha_j
|
||||
|
||||
all_alphas.push_back(a);
|
||||
|
||||
axpy(psi,a,p[peri_k],psi); // update psi --> psi + \alpha p
|
||||
|
||||
cp = axpy_norm(r,-a,q[peri_k],r); // update r --> r - \alpha D p. Note q = Dp
|
||||
LinalgTimer.Stop();
|
||||
|
||||
// LogIterationA(k + 1, a);
|
||||
|
||||
GCRLogLevel<< "GCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
|
||||
|
||||
// moving this to end of loop so that it doesn't exit beforehand
|
||||
// TODO if I want to uncomment this, I have to split the LogIteration again and put LogIterationA() beforehand
|
||||
// if((k==nstep-1)||(cp<rsq)){
|
||||
// return cp;
|
||||
// }
|
||||
|
||||
|
||||
PrecTimer.Start();
|
||||
Preconditioner(r,z);// solve Az = r
|
||||
PrecTimer.Stop();
|
||||
|
||||
MatTimer.Start();
|
||||
Linop.Op(z,Az);
|
||||
MatTimer.Stop();
|
||||
// zAz = innerProduct(Az,psi);
|
||||
zAAz= norm2(Az);
|
||||
|
||||
LinalgTimer.Start();
|
||||
|
||||
q[peri_kp]=Az;
|
||||
p[peri_kp]=z;
|
||||
|
||||
// Field Dsrc (grid);
|
||||
// Linop.Op(src, Dsrc);
|
||||
// std::cout << "||q[peri_kp] - D(src)||: " << norm2(q[peri_kp] - Dsrc) << std::endl; // for debugging
|
||||
|
||||
// // delete after testing
|
||||
// std::cout << "Testing Dsq on one for GCR: " << std::endl;
|
||||
// Field myField (grid);
|
||||
// myField = 1.0;
|
||||
// Field out1 (grid); Field out2 (grid);
|
||||
// Linop.HermOp(myField, out1);
|
||||
// Linop.Op(myField, out2);
|
||||
// std::cout << "Dsq.Hermop(ones) has norm " << norm2(out1) << std::endl;
|
||||
// std::cout << "Dsq.Op(ones) has norm " << norm2(out2) << std::endl;
|
||||
|
||||
// basically northog = k+1 if mmax is large
|
||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
||||
// std::cout << "northog: " << northog << std::endl;
|
||||
std::vector<ComplexD> betas (northog);
|
||||
// std::cout << "peri_kp: " << peri_kp << std::endl;
|
||||
// we iterate backwards counting down from the current k+1 index (peri_kp) because we
|
||||
for(int back=0;back<northog;back++){
|
||||
|
||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
||||
|
||||
// b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
||||
b=-(innerProduct(q[peri_back],Az))/qq[peri_back]; // TODO try complex beta
|
||||
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
||||
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
||||
|
||||
// LogIterationB(peri_back, b);
|
||||
// betas[back] = b; // may need to change the indexing if I ever do it with restarts
|
||||
// std::cout << "[DEBUG] pushing beta for back = " << back << ", peri_back = " << peri_back << std::endl;
|
||||
|
||||
betas[peri_back] = b; // may need to change the indexing if I ever do it with restarts
|
||||
|
||||
}
|
||||
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
||||
LinalgTimer.Stop();
|
||||
|
||||
// log iteration and update GCR polynomial if necessary.
|
||||
all_betas.push_back(betas);
|
||||
LogIteration(k + 1, a, betas);
|
||||
|
||||
// finish if necessary
|
||||
if((k==nstep-1)||(cp<rsq)){
|
||||
std::cout << "All alphas: " << std::endl << all_alphas << std::endl;
|
||||
std::cout << "All betas: " << std::endl << all_betas << std::endl;
|
||||
LogComplete(all_alphas, all_betas);
|
||||
std::cout << "Exiting GCR." << std::endl;
|
||||
return cp;
|
||||
}
|
||||
|
||||
}
|
||||
GRID_ASSERT(0); // never reached
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
|
||||
class PolynomialFile: Serializable {
|
||||
public:
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(PolynomialFile,
|
||||
std::vector<std::vector<std::complex<double>>>, data,
|
||||
std::vector<std::vector<std::complex<double>>>, betas,
|
||||
std::vector<std::complex<double>>, alphas
|
||||
);
|
||||
};
|
||||
|
||||
// Optionally record the GCR polynomial. [PO]: TODO
|
||||
template <class Field>
|
||||
class PGCRPolynomial : public PrecGeneralisedConjugateResidualNonHermitian<Field> {
|
||||
public:
|
||||
std::vector<ComplexD> ak;
|
||||
std::vector<std::vector<ComplexD>> bk;
|
||||
// std::vector<ComplexD> poly_p;
|
||||
std::vector<std::vector<ComplexD>> poly_p;
|
||||
std::vector<ComplexD> poly_Ap; // polynomial in Ap_j (only store it for last p)
|
||||
std::vector<ComplexD> poly_r;
|
||||
std::vector<ComplexD> polynomial;
|
||||
|
||||
PolynomialFile& PF;
|
||||
|
||||
public:
|
||||
PGCRPolynomial(RealD tol, Integer maxit,LinearOperatorBase<Field> &_Linop, LinearFunction<Field> &Prec, int _mmax, int _nstep, PolynomialFile& _PF)
|
||||
: PrecGeneralisedConjugateResidualNonHermitian<Field>(tol, maxit, _Linop, Prec, _mmax, _nstep), PF(_PF)
|
||||
{};
|
||||
|
||||
// think this applies the polynomial in A = Linop to a field src. The coeffs are
|
||||
// stored in the vector `polynomial`.
|
||||
void PolyOp(const Field &src, Field &psi)
|
||||
{
|
||||
Field tmp(src.Grid());
|
||||
Field AtoN(src.Grid());
|
||||
AtoN = src;
|
||||
psi=AtoN*polynomial[0];
|
||||
for(int n=1;n<polynomial.size();n++){
|
||||
tmp = AtoN;
|
||||
this->Linop.Op(tmp,AtoN); // iterate A^n
|
||||
psi = psi + polynomial[n]*AtoN; // psi += poly_n A^n src
|
||||
}
|
||||
}
|
||||
|
||||
// [PO TODO] debug this
|
||||
void PGCRsequence(const Field &src, Field &x)
|
||||
{
|
||||
Field Ap(src.Grid());
|
||||
Field r(src.Grid());
|
||||
// Field p(src.Grid());
|
||||
// p=src;
|
||||
std::vector<Field> p;
|
||||
p.push_back(src);
|
||||
r=src;
|
||||
x=Zero();
|
||||
x.Checkerboard()=src.Checkerboard();
|
||||
for(int k=0;k<ak.size();k++){
|
||||
x = x + ak[k]*p[k];
|
||||
this->Linop.Op(p[k], Ap);
|
||||
r = r - ak[k] * Ap;
|
||||
// p[k] = r;
|
||||
p.push_back(r);
|
||||
for (int i = 0; i < k; i++) { // [PO TODO] check indices
|
||||
p[k+1] += bk[i, k+1] * p[i];
|
||||
}
|
||||
// p = r + bk[k] * p;
|
||||
}
|
||||
}
|
||||
|
||||
void Solve(const Field &src, Field &psi)
|
||||
{
|
||||
psi=Zero();
|
||||
this->operator()(src, psi);
|
||||
}
|
||||
|
||||
virtual void LogBegin(void)
|
||||
{
|
||||
std::cout << "PGCR::LogBegin() "<<std::endl;
|
||||
ak.resize(0);
|
||||
bk.resize(0);
|
||||
polynomial.resize(0);
|
||||
poly_Ap.push_back(0.0); // start with (0.0); during first iteration should change to (0.0, 1.0)
|
||||
std::vector<ComplexD> p0_tmp;
|
||||
p0_tmp.push_back(1.0);
|
||||
poly_p.push_back(p0_tmp);
|
||||
poly_r.push_back(1.0);
|
||||
};
|
||||
|
||||
// Updates vector psi and r and initializes vector p[k+1]
|
||||
virtual void LogIteration(int k, ComplexD a, std::vector<ComplexD> betas){
|
||||
std::cout << "PGCR::LogIteration(k = " << k << ")" << std::endl;
|
||||
ak.push_back(a);
|
||||
bk.push_back(betas);
|
||||
|
||||
// update Ap by pushing p[k] to the right
|
||||
poly_Ap.push_back(0.0); // need to pad the end with an element
|
||||
poly_Ap[0] = 0.0; // technically this should be unnecessary, as the first component is never set
|
||||
for(int i = 0; i < k; i++){
|
||||
poly_Ap[i+1]=poly_p[k-1][i]; // A\vec{p} = (0, \vec{p}) bc A shifts components of p to the right
|
||||
}
|
||||
|
||||
// update psi_{k+1} --> psi_k + a_k p_k
|
||||
polynomial.push_back(0.0);
|
||||
for(int i = 0; i < k; i++) {
|
||||
polynomial[i] += a * poly_p[k-1][i];
|
||||
}
|
||||
{
|
||||
std::vector<std::complex<double>> poly_stdcmplx(polynomial.begin(), polynomial.end());
|
||||
PF.data.push_back(poly_stdcmplx);
|
||||
}
|
||||
|
||||
// r_{k+1} --> r_k - a_k A p_k
|
||||
// p_{k+1} --> r_k + \sum_{i=0}^k \beta_{ik} p_i, input betas = (\beta_{ik})_i
|
||||
poly_r.push_back(0.0); // should be of size k+1 if we start with k = 1
|
||||
std::vector<ComplexD> p_next (k + 1, ComplexD(0.0)); // p_{k+1} = same size as r_{k+1}
|
||||
for(int i = 0; i < k + 1; i++){
|
||||
poly_r[i] = poly_r[i] - a * poly_Ap[i]; // update r_{k+1} --> r_k - \alpha_k A p_k
|
||||
p_next[i] = poly_r[i]; // init new vector as r_{k+1}
|
||||
}
|
||||
|
||||
// p_{k+1} --> p_{k+1} + \sum_i \beta_{ij} p_i
|
||||
int nbeta = betas.size();
|
||||
std::cout << "Betas: " << betas << std::endl;
|
||||
for (int j = 0; j < nbeta; j++) {
|
||||
for (int i = 0; i < j+1; i++) {
|
||||
p_next[i] += betas[j] * poly_p[j][i];
|
||||
}
|
||||
}
|
||||
poly_p.push_back(p_next); // add p_{k+1} to the list of p's
|
||||
}
|
||||
|
||||
virtual void LogComplete(std::vector<ComplexD>& alphas, std::vector<std::vector<ComplexD>>& betas) {
|
||||
/** Logs all alphas and betas to complete the iterations. */
|
||||
std::cout << "PGCR::LogComplete() "<<std::endl;
|
||||
for (int i = 0; i < alphas.size(); i++) {
|
||||
PF.alphas.push_back(std::complex<double>(alphas[i].real(), alphas[i].imag()));
|
||||
std::vector<std::complex<double>> beta_stdcmplx(betas[i].begin(), betas[i].end());
|
||||
PF.betas.push_back(beta_stdcmplx);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -79,7 +79,7 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
|
||||
|
||||
LinOp.Op(x,r); r = b - r;
|
||||
|
||||
assert(normb> 0.0);
|
||||
GRID_ASSERT(normb> 0.0);
|
||||
|
||||
resid = norm2(r)/normb;
|
||||
if (resid <= Tolerance) {
|
||||
@@ -105,8 +105,8 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
|
||||
for (int i = 1; i <= MaxIterations; i++) {
|
||||
|
||||
// Breakdown tests
|
||||
assert( rho != 0.0);
|
||||
assert( xi != 0.0);
|
||||
GRID_ASSERT( rho != 0.0);
|
||||
GRID_ASSERT( xi != 0.0);
|
||||
|
||||
v = (1. / rho) * v_tld;
|
||||
y = (1. / rho) * y;
|
||||
@@ -134,10 +134,10 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
|
||||
ep=Zep.real();
|
||||
std::cout << "Zep "<<Zep <<std::endl;
|
||||
// Complex Audit
|
||||
assert(abs(ep)>0);
|
||||
GRID_ASSERT(abs(ep)>0);
|
||||
|
||||
beta = ep / delta;
|
||||
assert(abs(beta)>0);
|
||||
GRID_ASSERT(abs(beta)>0);
|
||||
|
||||
v_tld = p_tld - beta * v;
|
||||
y = v_tld;
|
||||
@@ -158,7 +158,7 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
|
||||
std::cout << "theta "<<theta<<std::endl;
|
||||
std::cout << "gamma "<<gamma<<std::endl;
|
||||
|
||||
assert(abs(gamma)> 0.0);
|
||||
GRID_ASSERT(abs(gamma)> 0.0);
|
||||
|
||||
eta = -eta * rho_1 * gamma* gamma / (beta * gamma_1 * gamma_1);
|
||||
|
||||
@@ -178,7 +178,7 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
|
||||
}
|
||||
std::cout << "Iteration "<<i<<" resid " << resid<<std::endl;
|
||||
}
|
||||
assert(0);
|
||||
GRID_ASSERT(0);
|
||||
return; // no convergence
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,753 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./Grid/algorithms/iterative/RestartedLanczosBidiagonalization.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_RESTARTED_LANCZOS_BIDIAGONALIZATION_H
|
||||
#define GRID_RESTARTED_LANCZOS_BIDIAGONALIZATION_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Implicitly Restarted Lanczos Bidiagonalization (IRLBA)
|
||||
*
|
||||
* Computes the p largest (or p smallest) singular triplets of a linear
|
||||
* operator A using the Golub-Kahan-Lanczos bidiagonalization with implicit
|
||||
* restart via thick-restart / QR shifts.
|
||||
*
|
||||
* Algorithm (Baglama & Reichel, SIAM J. Sci. Comput. 27(1):19-42, 2005):
|
||||
*
|
||||
* Outer loop:
|
||||
* 1. Extend the p-step (or seed) bidiagonalization to k steps:
|
||||
* A V_k = U_k B_k
|
||||
* A^dag U_k = V_k B_k^T + beta_{k+1} v_{k+1} e_k^T
|
||||
* 2. Compute SVD: B_k = X Sigma Y^T
|
||||
* 3. Check convergence of the p desired singular values via
|
||||
* |beta_{k+1} * y_{k,i}| < tol * sigma_i
|
||||
* where y_{k,i} is the last component of the i-th right singular vector.
|
||||
* 4. Apply k-p implicit QR shifts to implicitly compress the basis
|
||||
* to p steps (Sorensen-Lehoucq thick restart):
|
||||
* B_p^+ = X_p^T B_k Y_p (upper bidiagonal, p x p)
|
||||
* and update the lattice vectors:
|
||||
* V_p^+ = V_k Y_p
|
||||
* U_p^+ = U_k X_p
|
||||
* The new residual coupling is
|
||||
* beta_p^+ v_{p+1}^+ = beta_{k+1} v_{k+1} * (e_k^T Y_p)_p
|
||||
* + B_k(p,p+1) * (orthogonal tail from QR)
|
||||
* 5. Go to step 1.
|
||||
*
|
||||
* Template parameter
|
||||
* ------------------
|
||||
* Field : lattice field type (must support Grid algebra operations)
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
* RestartedLanczosBidiagonalization<Field> irlba(Linop, grid, p, k, tol, maxIter);
|
||||
* irlba.run(src);
|
||||
* // Results available via getters.
|
||||
*/
|
||||
template <class Field>
|
||||
class RestartedLanczosBidiagonalization {
|
||||
|
||||
public:
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
int Nk; // number of desired singular triplets
|
||||
int Nm; // Lanczos basis size (Nm > Nk)
|
||||
RealD Tolerance;
|
||||
int MaxIter;
|
||||
bool largest; // if true, target largest singular values; otherwise smallest
|
||||
|
||||
// Converged singular triplets (filled after run())
|
||||
std::vector<RealD> singularValues; // sigma_0 >= sigma_1 >= ...
|
||||
std::vector<Field> leftVectors; // approximate left singular vectors
|
||||
std::vector<Field> rightVectors; // approximate right singular vectors
|
||||
|
||||
private:
|
||||
// Working bases (size up to Nm+1)
|
||||
std::vector<Field> V; // right Lanczos vectors
|
||||
std::vector<Field> U; // left Lanczos vectors
|
||||
std::vector<RealD> alpha;
|
||||
std::vector<RealD> beta;
|
||||
|
||||
// After a thick restart, the column at index restart_col of U^dag A V
|
||||
// has extra non-zero entries (rows 0..restart_col-2) beyond what the
|
||||
// upper bidiagonal captures. fvec[j] = <U[j] | A V[restart_col]> for
|
||||
// j = 0..restart_col-1. (fvec[restart_col-1] == beta[restart_col-1].)
|
||||
// reset_col == -1 means no restart has occurred yet (pure bidiagonal).
|
||||
std::vector<RealD> fvec;
|
||||
int restart_col;
|
||||
|
||||
public:
|
||||
|
||||
RestartedLanczosBidiagonalization(LinearOperatorBase<Field> &_Linop,
|
||||
GridBase *_Grid,
|
||||
int _Nk, int _Nm,
|
||||
RealD _tol = 1.0e-8,
|
||||
int _maxIt = 300,
|
||||
bool _largest = true)
|
||||
: Linop(_Linop), Grid(_Grid),
|
||||
Nk(_Nk), Nm(_Nm),
|
||||
Tolerance(_tol), MaxIter(_maxIt),
|
||||
largest(_largest)
|
||||
{
|
||||
assert(Nm > Nk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run IRLBA starting from src.
|
||||
* On exit, singularValues, leftVectors, rightVectors are filled with
|
||||
* the Nk converged singular triplets.
|
||||
*/
|
||||
void run(const Field &src)
|
||||
{
|
||||
assert(norm2(src) > 0.0);
|
||||
|
||||
singularValues.clear();
|
||||
leftVectors.clear();
|
||||
rightVectors.clear();
|
||||
|
||||
// Allocate working bases
|
||||
V.clear(); U.clear();
|
||||
alpha.clear(); beta.clear();
|
||||
fvec.clear(); restart_col = -1;
|
||||
V.reserve(Nm + 1);
|
||||
U.reserve(Nm);
|
||||
|
||||
// Seed: v_0 = src / ||src||
|
||||
Field vtmp(Grid);
|
||||
vtmp = src;
|
||||
RealD nrm = std::sqrt(norm2(vtmp));
|
||||
vtmp = (1.0 / nrm) * vtmp;
|
||||
V.push_back(vtmp);
|
||||
|
||||
int pStart = 0; // current basis size at start of extension
|
||||
RealD betaRestart = 0.0; // coupling from previous restart
|
||||
|
||||
for (int iter = 0; iter < MaxIter; ++iter) {
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 1: extend from pStart steps to Nm steps
|
||||
// ----------------------------------------------------------------
|
||||
extendBasis(pStart, Nm, betaRestart);
|
||||
// verify();
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 2: SVD of the Nm x Nm B matrix.
|
||||
// iter=0 (pStart==0): B is exactly bidiagonal — use buildBidiagonal.
|
||||
// iter>0 (pStart==Nk): after a thick restart, column restart_col of
|
||||
// U^dag A V has extra off-diagonal entries captured by fvec; use
|
||||
// buildFullB so the Ritz values and restart vectors are computed from
|
||||
// the exact projected matrix A V = U B_full.
|
||||
// ----------------------------------------------------------------
|
||||
Eigen::MatrixXd B = (pStart == 0) ? buildBidiagonal(Nm) : buildFullB(Nm);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
|
||||
Eigen::VectorXd sigma = svd.singularValues(); // descending
|
||||
Eigen::MatrixXd X = svd.matrixU(); // Nm x Nm left SVecs of B
|
||||
Eigen::MatrixXd Y = svd.matrixV(); // Nm x Nm right SVecs of B
|
||||
|
||||
// If targeting smallest, reorder so desired ones come first
|
||||
Eigen::VectorXi order = sortOrder(sigma);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 3: check convergence of the Nk desired singular values
|
||||
// ----------------------------------------------------------------
|
||||
RealD betaK = beta.back(); // beta_{k+1}
|
||||
// In our convention A V = U B (exact), the residual is in the A^dag
|
||||
// direction: A^dag u_j - sigma_j v_j = betaK * X[Nm-1,j] * V[Nm].
|
||||
// Convergence criterion: |betaK * X[Nm-1, idx]| < tol * sigma_idx.
|
||||
int nconv = 0;
|
||||
for (int i = 0; i < Nk; ++i) {
|
||||
int idx = order(i);
|
||||
RealD res = std::abs(betaK * X(Nm - 1, idx));
|
||||
RealD thr = Tolerance * std::max(sigma(idx), 1.0e-14);
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA iter " << iter
|
||||
<< " sigma[" << i << "] = " << sigma(idx)
|
||||
<< " res = " << res
|
||||
<< " thr = " << thr << std::endl;
|
||||
if (res < thr) ++nconv;
|
||||
else break; // residuals not strictly ordered but break is conservative
|
||||
}
|
||||
|
||||
if (nconv >= Nk) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA converged: " << nconv << " singular values after "
|
||||
<< iter + 1 << " iterations." << std::endl;
|
||||
// Collect converged triplets
|
||||
extractTriplets(Nm, sigma, X, Y, order, Nk);
|
||||
return;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 4: implicit restart — compress to Nk steps
|
||||
// ----------------------------------------------------------------
|
||||
implicitRestart(Nm, Nk, sigma, X, Y, order, betaK, betaRestart);
|
||||
// verify();
|
||||
|
||||
// Lucky breakdown: exact invariant subspace found; convergence is exact.
|
||||
// B_p^+ = diag(alpha[0..Nk-1]); extract directly from restart basis.
|
||||
if (betaRestart < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA: lucky breakdown after restart (betaRestart = 0)."
|
||||
<< " Extracting " << Nk << " exact Ritz triplets." << std::endl;
|
||||
// Re-run SVD on the p-step diagonal B^+ to get sorted Ritz triplets.
|
||||
Eigen::MatrixXd Bp = buildBidiagonal(Nk);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svdp(Bp,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
Eigen::VectorXi ordp = sortOrder(svdp.singularValues());
|
||||
extractTriplets(Nk, svdp.singularValues(), svdp.matrixU(),
|
||||
svdp.matrixV(), ordp, Nk);
|
||||
return;
|
||||
}
|
||||
|
||||
pStart = Nk;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA: did not converge in " << MaxIter
|
||||
<< " iterations. Returning best approximations." << std::endl;
|
||||
|
||||
// Return best available approximations
|
||||
Eigen::MatrixXd B = buildFullB((int)alpha.size());
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
Eigen::VectorXd sigma = svd.singularValues();
|
||||
Eigen::MatrixXd X = svd.matrixU();
|
||||
Eigen::MatrixXd Y = svd.matrixV();
|
||||
Eigen::VectorXi order = sortOrder(sigma);
|
||||
int nout = std::min(Nk, (int)alpha.size());
|
||||
extractTriplets((int)alpha.size(), sigma, X, Y, order, nout);
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
int getNk() const { return (int)singularValues.size(); }
|
||||
const std::vector<RealD>& getSingularValues() const { return singularValues; }
|
||||
const std::vector<Field>& getLeftVectors() const { return leftVectors; }
|
||||
const std::vector<Field>& getRightVectors() const { return rightVectors; }
|
||||
|
||||
/**
|
||||
* Print B_k and U^dag A V to verify the bidiagonalization relation
|
||||
* A V_m = U_m B_m (exact in our GK convention)
|
||||
* On the first call (pStart=0), max|B - U^dag A V| should be ~machine epsilon.
|
||||
* After a restart and extension, the column p of U^dag A V deviates from B
|
||||
* by O(betaK): this is expected because the thick restart breaks the Krylov
|
||||
* structure at column p, introducing off-diagonal terms proportional to betaK.
|
||||
* These terms vanish as betaK -> 0 (convergence), so the algorithm is correct.
|
||||
*/
|
||||
void verify()
|
||||
{
|
||||
int m = (int)alpha.size();
|
||||
int nU = (int)U.size();
|
||||
int nV = (int)V.size();
|
||||
if (m == 0) { std::cout << GridLogMessage << "IRLBA verify: empty basis" << std::endl; return; }
|
||||
|
||||
// Build reference matrix Bref (nU x nV):
|
||||
// Columns 0..m-1 : buildFullB(m) (bidiagonal + fvec column at restart_col)
|
||||
// Column m : residual column, two cases:
|
||||
// (a) restart_col == m (right after implicitRestart, before extendBasis):
|
||||
// V[m] = sgn*V_old[Nm], so <U[i]|A|V[m]> = fvec[i] for all i
|
||||
// (b) otherwise (pure GK or after extendBasis):
|
||||
// only entry (m-1, m) = beta[m-1] (GK recurrence residual)
|
||||
Eigen::MatrixXd Bref = Eigen::MatrixXd::Zero(nU, nV);
|
||||
{
|
||||
Eigen::MatrixXd Bfull = buildFullB(m);
|
||||
int cols = std::min(m, nV);
|
||||
Bref.block(0, 0, m, cols) = Bfull.block(0, 0, m, cols);
|
||||
}
|
||||
if (nV > m && m > 0) {
|
||||
if (restart_col == m && (int)fvec.size() == m) {
|
||||
// Case (a): right after implicitRestart
|
||||
for (int i = 0; i < m; ++i) Bref(i, m) = fvec[i];
|
||||
} else if ((int)beta.size() >= m) {
|
||||
// Case (b): standard GK residual column
|
||||
Bref(m - 1, m) = beta[m - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute M[i,j] = <U[i] | A | V[j]>
|
||||
Eigen::MatrixXd M = Eigen::MatrixXd::Zero(nU, nV);
|
||||
Field Avj(Grid);
|
||||
for (int j = 0; j < nV; ++j) {
|
||||
Linop.Op(V[j], Avj);
|
||||
for (int i = 0; i < nU; ++i) {
|
||||
ComplexD ip = innerProduct(U[i], Avj);
|
||||
M(i, j) = ip.real();
|
||||
}
|
||||
}
|
||||
|
||||
// Print Bref
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA verify: Bref (" << nU << "x" << nV << "):" << std::endl;
|
||||
for (int i = 0; i < nU; ++i) {
|
||||
std::cout << GridLogMessage << " row " << i << ": ";
|
||||
for (int j = 0; j < nV; ++j) std::cout << Bref(i,j) << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Print U^dag A V
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA verify: U^dag A V (" << nU << "x" << nV << "):" << std::endl;
|
||||
for (int i = 0; i < nU; ++i) {
|
||||
std::cout << GridLogMessage << " row " << i << ": ";
|
||||
for (int j = 0; j < nV; ++j) std::cout << M(i,j) << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Max deviation over the full nU x nV matrix
|
||||
RealD maxdev = (Bref - M).cwiseAbs().maxCoeff();
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA verify: max|Bref - U^dag A V| = " << maxdev << std::endl;
|
||||
|
||||
// Beta
|
||||
std::cout << GridLogMessage << "IRLBA verify: beta[0.." << (int)beta.size()-1 << "] = ";
|
||||
for (auto b : beta) std::cout << b << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Build the m x m upper-bidiagonal matrix from alpha[0..m-1], beta[0..m-2]
|
||||
// ------------------------------------------------------------------
|
||||
Eigen::MatrixXd buildBidiagonal(int m) const
|
||||
{
|
||||
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(m, m);
|
||||
for (int k = 0; k < m; ++k) {
|
||||
B(k, k) = alpha[k];
|
||||
if (k + 1 < m && k < (int)beta.size())
|
||||
B(k, k + 1) = beta[k];
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Build the full m x m B matrix, including the non-bidiagonal column
|
||||
// at restart_col that arises after a thick restart.
|
||||
//
|
||||
// After restart, A V[restart_col] has projections onto all U[0..restart_col-1]
|
||||
// (not just U[restart_col-1]). These are stored in fvec[0..restart_col-1]
|
||||
// and make column restart_col of U^dag A V non-bidiagonal.
|
||||
// ------------------------------------------------------------------
|
||||
Eigen::MatrixXd buildFullB(int m) const
|
||||
{
|
||||
Eigen::MatrixXd B = buildBidiagonal(m);
|
||||
if (restart_col >= 0 && restart_col < m && (int)fvec.size() > 0) {
|
||||
for (int j = 0; j < restart_col && j < (int)fvec.size(); ++j){
|
||||
B(j, restart_col) = fvec[j];
|
||||
std::cout << GridLogDebug << "buildFullB: B " <<j<<" "<<restart_col<<B(j, restart_col)<<std::endl;
|
||||
}
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Return a permutation vector that puts the desired Nk singular values
|
||||
// first (largest first if largest==true, smallest first otherwise).
|
||||
// Eigen's JacobiSVD already returns sigma in descending order, so for
|
||||
// largest we just return 0,1,...,m-1; for smallest we reverse.
|
||||
// ------------------------------------------------------------------
|
||||
Eigen::VectorXi sortOrder(const Eigen::VectorXd &sigma) const
|
||||
{
|
||||
int m = (int)sigma.size();
|
||||
Eigen::VectorXi ord(m);
|
||||
if (largest) {
|
||||
for (int i = 0; i < m; ++i) ord(i) = i;
|
||||
} else {
|
||||
for (int i = 0; i < m; ++i) ord(i) = m - 1 - i;
|
||||
}
|
||||
return ord;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Extend the Lanczos bidiagonalization from pStart to kEnd steps.
|
||||
// On first call pStart==0 (V[0] already set).
|
||||
// On restart calls V[0..pStart], U[0..pStart-1], alpha[0..pStart-1],
|
||||
// beta[0..pStart-1] are already set; betaRestart is the coupling
|
||||
// beta_{pStart} that drives the first new U step.
|
||||
// ------------------------------------------------------------------
|
||||
void extendBasis(int pStart, int kEnd, RealD betaRestart)
|
||||
{
|
||||
// Truncate containers to pStart (Lattice has no default constructor)
|
||||
if ((int)V.size() > pStart + 1) V.erase(V.begin() + pStart + 1, V.end());
|
||||
if ((int)U.size() > pStart) U.erase(U.begin() + pStart, U.end());
|
||||
alpha.resize(pStart);
|
||||
beta.resize(pStart);
|
||||
|
||||
Field p(Grid), r(Grid);
|
||||
|
||||
for (int k = pStart; k < kEnd; ++k) {
|
||||
|
||||
// p = A v_k
|
||||
Linop.Op(V[k], p);
|
||||
|
||||
// Remove previous left vector coupling
|
||||
if (k > 0) {
|
||||
p = p - beta[k - 1] * U[k - 1];
|
||||
}
|
||||
// On the first step after a restart, beta[pStart-1] was already set;
|
||||
// but V[pStart] was already constructed including the beta correction,
|
||||
// so no extra subtraction needed here beyond the standard recurrence.
|
||||
|
||||
// Reorthogonalize p against U, then alpha_k = ||p||, u_k = p/alpha_k
|
||||
reorthogonalize(p, U);
|
||||
RealD ak = std::sqrt(norm2(p));
|
||||
if (ak < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA extendBasis: lucky breakdown at step " << k
|
||||
<< " (alpha = " << ak << ")" << std::endl;
|
||||
alpha.push_back(ak);
|
||||
Field zero(Grid); zero = Zero();
|
||||
U.push_back(zero);
|
||||
beta.push_back(0.0);
|
||||
V.push_back(zero);
|
||||
break;
|
||||
}
|
||||
alpha.push_back(ak);
|
||||
|
||||
Field u(Grid);
|
||||
u = (1.0 / ak) * p;
|
||||
U.push_back(u);
|
||||
|
||||
// r = A^dag u_k - alpha_k v_k, reorthogonalize, then beta_{k+1} = ||r||
|
||||
Linop.AdjOp(U[k], r);
|
||||
r = r - ak * V[k];
|
||||
reorthogonalize(r, V);
|
||||
|
||||
RealD bk = std::sqrt(norm2(r));
|
||||
beta.push_back(bk);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA extend step " << k
|
||||
<< " alpha = " << ak
|
||||
<< " beta = " << bk << std::endl;
|
||||
|
||||
// Always push v_{k+1} (needed as residual direction for restart)
|
||||
if (bk < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA extendBasis: lucky breakdown (beta = 0) at step "
|
||||
<< k << std::endl;
|
||||
Field zero(Grid); zero = Zero();
|
||||
V.push_back(zero);
|
||||
break;
|
||||
}
|
||||
Field vnext(Grid);
|
||||
vnext = (1.0 / bk) * r;
|
||||
V.push_back(vnext);
|
||||
|
||||
if (k == kEnd - 1) break; // v_{k+1} pushed above; stop here
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------
|
||||
// Block reorthogonalization helpers.
|
||||
// Declared public because CUDA extended lambdas cannot live inside
|
||||
// private/protected member functions.
|
||||
//
|
||||
// batchInnerProducts: computes c[j] = <basis[j], vec> for all j
|
||||
// in a single GPU pass (one accelerator_barrier instead of n).
|
||||
// Queues n pairs of (per-site kernel, reduceKernel) to computeStream
|
||||
// without intermediate CPU syncs, then syncs once at the end.
|
||||
//
|
||||
// batchUpdate: computes vec -= sum_j c[j]*basis[j] in one GPU kernel.
|
||||
//
|
||||
// reorthogonalize: two-pass Classical Gram-Schmidt (CGS2) using the
|
||||
// two helpers above. Each pass costs 2 GPU syncs (1 IP + 1 update)
|
||||
// instead of 2n syncs per pass in the old sequential MGS.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
void batchInnerProducts(const Field &vec,
|
||||
const std::vector<Field> &basis,
|
||||
std::vector<ComplexD> &c)
|
||||
{
|
||||
int n = (int)basis.size();
|
||||
c.resize(n);
|
||||
if (n == 0) return;
|
||||
|
||||
typedef typename Field::vector_object vobj;
|
||||
typedef decltype(innerProduct(vobj(), vobj())) inner_t;
|
||||
typedef decltype(basis[0].View(AcceleratorRead)) View;
|
||||
|
||||
GridBase *grid = vec.Grid();
|
||||
uint64_t oSites = grid->oSites();
|
||||
uint64_t nsimd = grid->Nsimd();
|
||||
|
||||
// all_ip[j * oSites + ss] = per-site inner product of basis[j] and vec at site ss.
|
||||
// Layout: n contiguous blocks of oSites each.
|
||||
deviceVector<inner_t> all_ip((uint64_t)n * oSites);
|
||||
inner_t *all_ip_p = &all_ip[0];
|
||||
|
||||
hostVector<View> h_basis_v(n);
|
||||
deviceVector<View> d_basis_v(n);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
h_basis_v[j] = basis[j].View(AcceleratorRead);
|
||||
acceleratorPut(d_basis_v[j], h_basis_v[j]);
|
||||
}
|
||||
View *basis_vp = &d_basis_v[0];
|
||||
|
||||
// Queue n per-site kernels to the accelerator stream — no intermediate barriers.
|
||||
autoView(vec_v, vec, AcceleratorRead);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
int jj = j;
|
||||
uint64_t oSites_ = oSites;
|
||||
accelerator_for(ss, oSites, nsimd, {
|
||||
auto x = coalescedRead(basis_vp[jj][ss]);
|
||||
auto y = coalescedRead(vec_v[ss]);
|
||||
coalescedWrite(all_ip_p[jj * oSites_ + ss], innerProduct(x, y));
|
||||
});
|
||||
}
|
||||
|
||||
// ONE sync after all n kernels
|
||||
accelerator_barrier();
|
||||
|
||||
// Copy all per-site results to host
|
||||
hostVector<inner_t> all_ip_h((uint64_t)n * oSites);
|
||||
acceleratorCopyFromDevice(all_ip_p, &all_ip_h[0], (uint64_t)n * oSites * sizeof(inner_t));
|
||||
|
||||
// Reduce on host: sum over oSites, then collapse SIMD lanes via Reduce(TensorRemove(...))
|
||||
// TensorRemove strips the iSinglet tensor wrapper to expose the SIMD scalar type.
|
||||
// Reduce sums all nsimd lanes and returns a plain scalar (RealD or ComplexD).
|
||||
std::vector<ComplexD> raw(n);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
inner_t sum = Zero();
|
||||
for (uint64_t ss = 0; ss < oSites; ++ss)
|
||||
sum += all_ip_h[(uint64_t)j * oSites + ss];
|
||||
raw[j] = ComplexD(Reduce(TensorRemove(sum)));
|
||||
}
|
||||
grid->GlobalSumVector(&raw[0], n);
|
||||
for (int j = 0; j < n; ++j) c[j] = raw[j];
|
||||
|
||||
for (int j = 0; j < n; ++j) h_basis_v[j].ViewClose();
|
||||
}
|
||||
|
||||
void batchUpdate(Field &vec,
|
||||
const std::vector<Field> &basis,
|
||||
const std::vector<ComplexD> &c)
|
||||
{
|
||||
int n = (int)basis.size();
|
||||
if (n == 0) return;
|
||||
|
||||
typedef typename Field::vector_object vobj;
|
||||
typedef decltype(basis[0].View(AcceleratorRead)) View;
|
||||
|
||||
GridBase *grid = vec.Grid();
|
||||
uint64_t oSites = grid->oSites();
|
||||
uint64_t nsimd = grid->Nsimd();
|
||||
|
||||
// Split complex coefficients into real/imag double arrays on device.
|
||||
// Using doubles avoids potential ComplexD-device-code compatibility issues.
|
||||
hostVector<double> h_re(n), h_im(n);
|
||||
deviceVector<double> d_re(n), d_im(n);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
h_re[k] = c[k].real();
|
||||
h_im[k] = c[k].imag();
|
||||
}
|
||||
acceleratorCopyToDevice(&h_re[0], &d_re[0], n * sizeof(double));
|
||||
acceleratorCopyToDevice(&h_im[0], &d_im[0], n * sizeof(double));
|
||||
double *re_p = &d_re[0];
|
||||
double *im_p = &d_im[0];
|
||||
|
||||
// Basis views
|
||||
hostVector<View> h_basis_v(n);
|
||||
deviceVector<View> d_basis_v(n);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
h_basis_v[k] = basis[k].View(AcceleratorRead);
|
||||
acceleratorPut(d_basis_v[k], h_basis_v[k]);
|
||||
}
|
||||
View *basis_vp = &d_basis_v[0];
|
||||
|
||||
// Single kernel: vec[ss] -= sum_k (re[k] + i*im[k]) * basis[k][ss]
|
||||
autoView(vec_v, vec, AcceleratorWrite);
|
||||
accelerator_for(ss, oSites, nsimd, {
|
||||
auto v = coalescedRead(vec_v[ss]);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
auto b = coalescedRead(basis_vp[k][ss]);
|
||||
v = v - re_p[k] * b - timesI(im_p[k] * b);
|
||||
}
|
||||
coalescedWrite(vec_v[ss], v);
|
||||
});
|
||||
|
||||
for (int k = 0; k < n; ++k) h_basis_v[k].ViewClose();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Full reorthogonalization using two-pass Classical Gram-Schmidt (CGS2).
|
||||
// Each pass calls batchInnerProducts (1 GPU sync) + batchUpdate (1 sync),
|
||||
// replacing the old 2n GPU syncs per pass from sequential MGS.
|
||||
// ------------------------------------------------------------------
|
||||
void reorthogonalize(Field &vec, const std::vector<Field> &basis)
|
||||
{
|
||||
if (basis.empty()) return;
|
||||
std::vector<ComplexD> c;
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
batchInnerProducts(vec, basis, c);
|
||||
batchUpdate(vec, basis, c);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Implicit restart: given the Nm-step bidiagonalization and its SVD,
|
||||
// compress to Nk steps via implicit QR shifts applied to B_k.
|
||||
//
|
||||
// The "shifts" are the Nm - Nk singular values we want to deflate
|
||||
// (those NOT in the desired set). We apply them as implicit QR steps
|
||||
// to the bidiagonal matrix, then update the lattice bases accordingly.
|
||||
//
|
||||
// After this call:
|
||||
// V[0..Nk], U[0..Nk-1], alpha[0..Nk-1], beta[0..Nk-1] are updated.
|
||||
// betaRestart ← new beta_Nk coupling for the next extension.
|
||||
// ------------------------------------------------------------------
|
||||
void implicitRestart(int k, int p,
|
||||
const Eigen::VectorXd &sigma,
|
||||
const Eigen::MatrixXd &X,
|
||||
const Eigen::MatrixXd &Y,
|
||||
const Eigen::VectorXi &order,
|
||||
RealD betaK,
|
||||
RealD &betaRestart)
|
||||
{
|
||||
// Thick restart (Baglama & Reichel, Sec. 2.2):
|
||||
//
|
||||
// Given B_k = X Sigma Y^T, define the new p-step basis by:
|
||||
// V^+_i = V_k * y_{order(i)} (right sing. vec. of B_k)
|
||||
// U^+_i = U_k * x_{order(i)} (left sing. vec. of B_k)
|
||||
//
|
||||
// Then A V^+_i = A V_k y_{order(i)} = U_k B_k y_{order(i)}
|
||||
// = sigma_{order(i)} U_k x_{order(i)} = sigma_{order(i)} U^+_i
|
||||
//
|
||||
// So B_p^+ = diag(sigma_{order(0)}, ..., sigma_{order(p-1)}) — DIAGONAL,
|
||||
// all internal betas are zero.
|
||||
//
|
||||
// The residual coupling comes from A^dag U_k = V_k B_k^T + betaK V[k] e_{k-1}^T:
|
||||
// A^dag U^+_{p-1} - sigma_{order(p-1)} V^+_{p-1}
|
||||
// = V_k (B_k^T x_{order(p-1)} - sigma_{order(p-1)} y_{order(p-1)})
|
||||
// + betaK * X(k-1, order(p-1)) * V[k]
|
||||
// = betaK * X(k-1, order(p-1)) * V[k] (since B_k^T x_j = sigma_j y_j)
|
||||
//
|
||||
// Therefore: betaRestart = |betaK * X(k-1, order(p-1))|
|
||||
// V[p] = sign(X(k-1, order(p-1))) * V[k]
|
||||
|
||||
// ---- Build new lattice vectors ----
|
||||
std::vector<Field> Vnew, Unew;
|
||||
Vnew.reserve(p + 1);
|
||||
Unew.reserve(p);
|
||||
|
||||
for (int i = 0; i < p; ++i) {
|
||||
int idx = order(i);
|
||||
Field vi(Grid); vi = Zero();
|
||||
for (int j = 0; j < k; ++j)
|
||||
vi = vi + Y(j, idx) * V[j];
|
||||
Vnew.push_back(vi);
|
||||
}
|
||||
|
||||
for (int i = 0; i < p; ++i) {
|
||||
int idx = order(i);
|
||||
Field ui(Grid); ui = Zero();
|
||||
for (int j = 0; j < k; ++j)
|
||||
ui = ui + X(j, idx) * U[j];
|
||||
Unew.push_back(ui);
|
||||
}
|
||||
|
||||
// New v_{p} (0-indexed: V[p]) = sign * V[k]
|
||||
// From A^dag U_k = V_k B_k^T + betaK V[k] e_{k-1}^T:
|
||||
// A^dag U^+_j - sigma_j V^+_j = betaK * X(k-1, order(j)) * V[k]
|
||||
// The last Ritz pair (j=p-1) defines betaRestart and the sign of V[p].
|
||||
// All p couplings (j=0..p-1) are stored in fvec so that buildFullB can
|
||||
// reconstruct the exact column p of U^dag A V after the next extension.
|
||||
RealD coeff = betaK * X(k - 1, order(p - 1));
|
||||
betaRestart = std::abs(coeff);
|
||||
RealD sgn = (coeff >= 0.0) ? 1.0 : -1.0;
|
||||
|
||||
fvec.resize(p);
|
||||
for (int j = 0; j < p; ++j)
|
||||
fvec[j] = betaK * X(k - 1, order(j)) * sgn;
|
||||
// fvec[p-1] == betaRestart by construction
|
||||
restart_col = p;
|
||||
|
||||
Field vp(Grid);
|
||||
if (betaRestart > 1.0e-14) {
|
||||
vp = sgn * V[k];
|
||||
} else {
|
||||
betaRestart = 0.0;
|
||||
vp = Zero();
|
||||
}
|
||||
Vnew.push_back(vp); // V[p]
|
||||
|
||||
// ---- New alpha, beta ----
|
||||
// B_p^+ is diagonal: alpha^+_i = sigma_{order(i)}, all internal beta = 0
|
||||
std::vector<RealD> alpha_new(p), beta_new(p);
|
||||
for (int i = 0; i < p; ++i) alpha_new[i] = sigma(order(i));
|
||||
for (int i = 0; i < p - 1; ++i) beta_new[i] = 0.0;
|
||||
beta_new[p - 1] = betaRestart;
|
||||
|
||||
// ---- Commit new state ----
|
||||
V = Vnew;
|
||||
U = Unew;
|
||||
alpha = alpha_new;
|
||||
beta = beta_new;
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA restart: compressed to " << p << " steps,"
|
||||
<< " new beta_p = " << betaRestart << std::endl;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Extract the desired singular triplets into the public output vectors.
|
||||
// ------------------------------------------------------------------
|
||||
void extractTriplets(int m,
|
||||
const Eigen::VectorXd &sigma,
|
||||
const Eigen::MatrixXd &X,
|
||||
const Eigen::MatrixXd &Y,
|
||||
const Eigen::VectorXi &order,
|
||||
int nout)
|
||||
{
|
||||
singularValues.resize(nout);
|
||||
leftVectors.clear(); leftVectors.reserve(nout);
|
||||
rightVectors.clear(); rightVectors.reserve(nout);
|
||||
|
||||
for (int i = 0; i < nout; ++i) {
|
||||
int idx = order(i);
|
||||
singularValues[i] = sigma(idx);
|
||||
|
||||
// Left singular vector of A: svec_L = U_m * x_i
|
||||
Field svL(Grid); svL = Zero();
|
||||
for (int j = 0; j < m && j < (int)U.size(); ++j)
|
||||
svL = svL + X(j, idx) * U[j];
|
||||
leftVectors.push_back(svL);
|
||||
|
||||
// Right singular vector of A: svec_R = V_m * y_i
|
||||
Field svR(Grid); svR = Zero();
|
||||
for (int j = 0; j < m && j < (int)V.size(); ++j)
|
||||
svR = svR + Y(j, idx) * V[j];
|
||||
rightVectors.push_back(svR);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -132,6 +132,31 @@ namespace Grid {
|
||||
(*this)(_Matrix,in,out,guess);
|
||||
}
|
||||
|
||||
void RedBlackSource(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &src_o)
|
||||
{
|
||||
GridBase *grid = _Matrix.RedBlackGrid();
|
||||
Field tmp(grid);
|
||||
int nblock = in.size();
|
||||
for(int b=0;b<nblock;b++){
|
||||
RedBlackSource(_Matrix,in[b],tmp,src_o[b]);
|
||||
}
|
||||
}
|
||||
// James can write his own deflated guesser
|
||||
// with optimised code for the inner products
|
||||
// RedBlackSolveSplitGrid();
|
||||
// RedBlackSolve(_Matrix,src_o,sol_o);
|
||||
|
||||
void RedBlackSolution(Matrix &_Matrix, const std::vector<Field> &in, const std::vector<Field> &sol_o, std::vector<Field> &out)
|
||||
{
|
||||
GridBase *grid = _Matrix.RedBlackGrid();
|
||||
Field tmp(grid);
|
||||
int nblock = in.size();
|
||||
for(int b=0;b<nblock;b++) {
|
||||
pickCheckerboard(Even,tmp,in[b]);
|
||||
RedBlackSolution(_Matrix,sol_o[b],tmp,out[b]);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Guesser>
|
||||
void operator()(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &out,Guesser &guess)
|
||||
{
|
||||
@@ -150,24 +175,29 @@ namespace Grid {
|
||||
////////////////////////////////////////////////
|
||||
// Prepare RedBlack source
|
||||
////////////////////////////////////////////////
|
||||
for(int b=0;b<nblock;b++){
|
||||
RedBlackSource(_Matrix,in[b],tmp,src_o[b]);
|
||||
}
|
||||
RedBlackSource(_Matrix,in,src_o);
|
||||
// for(int b=0;b<nblock;b++){
|
||||
// RedBlackSource(_Matrix,in[b],tmp,src_o[b]);
|
||||
// }
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Make the guesses
|
||||
////////////////////////////////////////////////
|
||||
if ( subGuess ) guess_save.resize(nblock,grid);
|
||||
|
||||
for(int b=0;b<nblock;b++){
|
||||
if(useSolnAsInitGuess) {
|
||||
|
||||
if(useSolnAsInitGuess) {
|
||||
for(int b=0;b<nblock;b++){
|
||||
pickCheckerboard(Odd, sol_o[b], out[b]);
|
||||
} else {
|
||||
guess(src_o[b],sol_o[b]);
|
||||
}
|
||||
} else {
|
||||
guess(src_o, sol_o);
|
||||
}
|
||||
|
||||
if ( subGuess ) {
|
||||
guess_save[b] = sol_o[b];
|
||||
}
|
||||
if ( subGuess ) {
|
||||
for(int b=0;b<nblock;b++){
|
||||
guess_save[b] = sol_o[b];
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Call the block solver
|
||||
@@ -297,9 +327,9 @@ namespace Grid {
|
||||
/////////////////////////////////////////////////////
|
||||
// src_o = (source_o - Moe MeeInv source_e)
|
||||
/////////////////////////////////////////////////////
|
||||
_Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
|
||||
tmp=src_o-Mtmp; assert( tmp.Checkerboard() ==Odd);
|
||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
||||
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
||||
|
||||
_Matrix.Mooee(tmp,src_o); // Extra factor of "m" in source from dumb choice of matrix norm.
|
||||
}
|
||||
@@ -317,17 +347,17 @@ namespace Grid {
|
||||
///////////////////////////////////////////////////
|
||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||
///////////////////////////////////////////////////
|
||||
_Matrix.Meooe(sol_o,tmp); assert( tmp.Checkerboard() ==Even);
|
||||
src_e = src_e-tmp; assert( src_e.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(src_e,sol_e); assert( sol_e.Checkerboard() ==Even);
|
||||
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
src_e = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(src_e,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
|
||||
setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o); assert( sol_o.Checkerboard() ==Odd );
|
||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd );
|
||||
}
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
||||
{
|
||||
SchurStaggeredOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.Checkerboard()==Odd);
|
||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); GRID_ASSERT(sol_o.Checkerboard()==Odd);
|
||||
};
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
||||
{
|
||||
@@ -366,13 +396,13 @@ namespace Grid {
|
||||
/////////////////////////////////////////////////////
|
||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||
/////////////////////////////////////////////////////
|
||||
_Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
|
||||
tmp=src_o-Mtmp; assert( tmp.Checkerboard() ==Odd);
|
||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
||||
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
||||
|
||||
// get the right MpcDag
|
||||
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||
_HermOpEO.MpcDag(tmp,src_o); assert(src_o.Checkerboard() ==Odd);
|
||||
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd);
|
||||
|
||||
}
|
||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
|
||||
@@ -386,17 +416,17 @@ namespace Grid {
|
||||
///////////////////////////////////////////////////
|
||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||
///////////////////////////////////////////////////
|
||||
_Matrix.Meooe(sol_o,tmp); assert( tmp.Checkerboard() ==Even);
|
||||
src_e_i = src_e-tmp; assert( src_e_i.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(src_e_i,sol_e); assert( sol_e.Checkerboard() ==Even);
|
||||
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
src_e_i = src_e-tmp; GRID_ASSERT( src_e_i.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(src_e_i,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
|
||||
setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o); assert( sol_o.Checkerboard() ==Odd );
|
||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd );
|
||||
}
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
||||
{
|
||||
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.Checkerboard()==Odd);
|
||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); GRID_ASSERT(sol_o.Checkerboard()==Odd);
|
||||
};
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
||||
{
|
||||
@@ -431,9 +461,9 @@ namespace Grid {
|
||||
/////////////////////////////////////////////////////
|
||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||
/////////////////////////////////////////////////////
|
||||
_Matrix.MooeeInv(src_e, tmp); assert( tmp.Checkerboard() == Even );
|
||||
_Matrix.Meooe (tmp, Mtmp); assert( Mtmp.Checkerboard() == Odd );
|
||||
src_o -= Mtmp; assert( src_o.Checkerboard() == Odd );
|
||||
_Matrix.MooeeInv(src_e, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
||||
_Matrix.Meooe (tmp, Mtmp); GRID_ASSERT( Mtmp.Checkerboard() == Odd );
|
||||
src_o -= Mtmp; GRID_ASSERT( src_o.Checkerboard() == Odd );
|
||||
}
|
||||
|
||||
virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol)
|
||||
@@ -448,18 +478,18 @@ namespace Grid {
|
||||
///////////////////////////////////////////////////
|
||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||
///////////////////////////////////////////////////
|
||||
_Matrix.Meooe(sol_o, tmp); assert( tmp.Checkerboard() == Even );
|
||||
src_e_i = src_e - tmp; assert( src_e_i.Checkerboard() == Even );
|
||||
_Matrix.MooeeInv(src_e_i, sol_e); assert( sol_e.Checkerboard() == Even );
|
||||
_Matrix.Meooe(sol_o, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
||||
src_e_i = src_e - tmp; GRID_ASSERT( src_e_i.Checkerboard() == Even );
|
||||
_Matrix.MooeeInv(src_e_i, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
||||
|
||||
setCheckerboard(sol, sol_e); assert( sol_e.Checkerboard() == Even );
|
||||
setCheckerboard(sol, sol_o); assert( sol_o.Checkerboard() == Odd );
|
||||
setCheckerboard(sol, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
||||
setCheckerboard(sol, sol_o); GRID_ASSERT( sol_o.Checkerboard() == Odd );
|
||||
}
|
||||
|
||||
virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o)
|
||||
{
|
||||
NonHermitianSchurDiagMooeeOperator<Matrix,Field> _OpEO(_Matrix);
|
||||
this->_HermitianRBSolver(_OpEO, src_o, sol_o); assert(sol_o.Checkerboard() == Odd);
|
||||
this->_HermitianRBSolver(_OpEO, src_o, sol_o); GRID_ASSERT(sol_o.Checkerboard() == Odd);
|
||||
}
|
||||
|
||||
virtual void RedBlackSolve(Matrix& _Matrix, const std::vector<Field>& src_o, std::vector<Field>& sol_o)
|
||||
@@ -469,6 +499,87 @@ namespace Grid {
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Site diagonal is identity, left preconditioned by Mee^inv
|
||||
// ( 1 - Mee^inv Meo Moo^inv Moe ) phi = Mee_inv ( Mee - Meo Moo^inv Moe Mee^inv ) phi = Mee_inv eta
|
||||
//
|
||||
// Solve:
|
||||
// ( 1 - Mee^inv Meo Moo^inv Moe )^dag ( 1 - Mee^inv Meo Moo^inv Moe ) phi = ( 1 - Mee^inv Meo Moo^inv Moe )^dag Mee_inv eta
|
||||
//
|
||||
// Old notation e<->o
|
||||
//
|
||||
// Left precon by Moo^-1
|
||||
// b) (Doo^{dag} M_oo^-dag) (Moo^-1 Doo) psi_o = [ (D_oo)^dag M_oo^-dag ] Moo^-1 L^{-1} eta_o
|
||||
// eta_o' = (D_oo)^dag M_oo^-dag Moo^-1 (eta_o - Moe Mee^{-1} eta_e)
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template<class Field> class SchurRedBlackDiagOneSolve : public SchurRedBlackBase<Field> {
|
||||
public:
|
||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// Wrap the usual normal equations Schur trick
|
||||
/////////////////////////////////////////////////////
|
||||
SchurRedBlackDiagOneSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false,
|
||||
const bool _solnAsInitGuess = false)
|
||||
: SchurRedBlackBase<Field>(HermitianRBSolver,initSubGuess,_solnAsInitGuess) {};
|
||||
|
||||
virtual void RedBlackSource(Matrix & _Matrix,const Field &src, Field &src_e,Field &src_o)
|
||||
{
|
||||
GridBase *grid = _Matrix.RedBlackGrid();
|
||||
GridBase *fgrid= _Matrix.Grid();
|
||||
|
||||
SchurDiagOneOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||
|
||||
Field tmp(grid);
|
||||
Field Mtmp(grid);
|
||||
|
||||
pickCheckerboard(Even,src_e,src);
|
||||
pickCheckerboard(Odd ,src_o,src);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// src_o = Mpcdag *MooeeInv * (source_o - Moe MeeInv source_e)
|
||||
/////////////////////////////////////////////////////
|
||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
||||
Mtmp=src_o-Mtmp;
|
||||
_Matrix.MooeeInv(Mtmp,tmp); GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
||||
|
||||
// get the right MpcDag
|
||||
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd);
|
||||
}
|
||||
|
||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
|
||||
{
|
||||
GridBase *grid = _Matrix.RedBlackGrid();
|
||||
GridBase *fgrid= _Matrix.Grid();
|
||||
|
||||
Field tmp(grid);
|
||||
Field sol_e(grid);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||
///////////////////////////////////////////////////
|
||||
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
tmp = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(tmp,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
|
||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd );
|
||||
};
|
||||
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
||||
{
|
||||
SchurDiagOneOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
||||
};
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
||||
{
|
||||
SchurDiagOneOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Site diagonal is identity, right preconditioned by Mee^inv
|
||||
// ( 1 - Meo Moo^inv Moe Mee^inv ) phi =( 1 - Meo Moo^inv Moe Mee^inv ) Mee psi = = eta = eta
|
||||
@@ -501,12 +612,12 @@ namespace Grid {
|
||||
/////////////////////////////////////////////////////
|
||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||
/////////////////////////////////////////////////////
|
||||
_Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
|
||||
tmp=src_o-Mtmp; assert( tmp.Checkerboard() ==Odd);
|
||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
||||
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
||||
|
||||
// get the right MpcDag
|
||||
_HermOpEO.MpcDag(tmp,src_o); assert(src_o.Checkerboard() ==Odd);
|
||||
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd);
|
||||
}
|
||||
|
||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
|
||||
@@ -527,12 +638,12 @@ namespace Grid {
|
||||
///////////////////////////////////////////////////
|
||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||
///////////////////////////////////////////////////
|
||||
_Matrix.Meooe(sol_o_i,tmp); assert( tmp.Checkerboard() ==Even);
|
||||
tmp = src_e-tmp; assert( src_e.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(tmp,sol_e); assert( sol_e.Checkerboard() ==Even);
|
||||
_Matrix.Meooe(sol_o_i,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
||||
tmp = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even);
|
||||
_Matrix.MooeeInv(tmp,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
|
||||
setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o_i); assert( sol_o_i.Checkerboard() ==Odd );
|
||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
||||
setCheckerboard(sol,sol_o_i); GRID_ASSERT( sol_o_i.Checkerboard() ==Odd );
|
||||
};
|
||||
|
||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
||||
@@ -573,9 +684,9 @@ namespace Grid {
|
||||
/////////////////////////////////////////////////////
|
||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||
/////////////////////////////////////////////////////
|
||||
_Matrix.MooeeInv(src_e, tmp); assert( tmp.Checkerboard() == Even );
|
||||
_Matrix.Meooe (tmp, Mtmp); assert( Mtmp.Checkerboard() == Odd );
|
||||
src_o -= Mtmp; assert( src_o.Checkerboard() == Odd );
|
||||
_Matrix.MooeeInv(src_e, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
||||
_Matrix.Meooe (tmp, Mtmp); GRID_ASSERT( Mtmp.Checkerboard() == Odd );
|
||||
src_o -= Mtmp; GRID_ASSERT( src_o.Checkerboard() == Odd );
|
||||
}
|
||||
|
||||
virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol)
|
||||
@@ -596,12 +707,12 @@ namespace Grid {
|
||||
///////////////////////////////////////////////////
|
||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||
///////////////////////////////////////////////////
|
||||
_Matrix.Meooe(sol_o_i, tmp); assert( tmp.Checkerboard() == Even );
|
||||
tmp = src_e - tmp; assert( src_e.Checkerboard() == Even );
|
||||
_Matrix.MooeeInv(tmp, sol_e); assert( sol_e.Checkerboard() == Even );
|
||||
_Matrix.Meooe(sol_o_i, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
||||
tmp = src_e - tmp; GRID_ASSERT( src_e.Checkerboard() == Even );
|
||||
_Matrix.MooeeInv(tmp, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
||||
|
||||
setCheckerboard(sol, sol_e); assert( sol_e.Checkerboard() == Even );
|
||||
setCheckerboard(sol, sol_o_i); assert( sol_o_i.Checkerboard() == Odd );
|
||||
setCheckerboard(sol, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
||||
setCheckerboard(sol, sol_o_i); GRID_ASSERT( sol_o_i.Checkerboard() == Odd );
|
||||
};
|
||||
|
||||
virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o)
|
||||
|
||||
@@ -0,0 +1,931 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ImplicitlyRestartedLanczos.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_LANC_H
|
||||
#define GRID_LANC_H
|
||||
|
||||
#include <string.h> //memset
|
||||
|
||||
#ifdef USE_LAPACK
|
||||
#ifdef USE_MKL
|
||||
#include<mkl_lapack.h>
|
||||
#else
|
||||
void LAPACK_dstegr (char *jobz, char *range, int *n, double *d, double *e,
|
||||
double *vl, double *vu, int *il, int *iu, double *abstol,
|
||||
int *m, double *w, double *z, int *ldz, int *isuppz,
|
||||
double *work, int *lwork, int *iwork, int *liwork,
|
||||
int *info);
|
||||
//#include <lapacke/lapacke.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//#include <Grid/algorithms/densematrix/DenseMatrix.h>
|
||||
|
||||
// eliminate temorary vector in calc()
|
||||
#define MEM_SAVE
|
||||
|
||||
namespace Grid
|
||||
{
|
||||
|
||||
struct Bisection
|
||||
{
|
||||
|
||||
#if 0
|
||||
static void get_eig2 (int row_num, std::vector < RealD > &ALPHA,
|
||||
std::vector < RealD > &BETA,
|
||||
std::vector < RealD > &eig)
|
||||
{
|
||||
int i, j;
|
||||
std::vector < RealD > evec1 (row_num + 3);
|
||||
std::vector < RealD > evec2 (row_num + 3);
|
||||
RealD eps2;
|
||||
ALPHA[1] = 0.;
|
||||
BETHA[1] = 0.;
|
||||
for (i = 0; i < row_num - 1; i++)
|
||||
{
|
||||
ALPHA[i + 1] = A[i * (row_num + 1)].real ();
|
||||
BETHA[i + 2] = A[i * (row_num + 1) + 1].real ();
|
||||
}
|
||||
ALPHA[row_num] = A[(row_num - 1) * (row_num + 1)].real ();
|
||||
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-10, 1e-10, evec1, eps2);
|
||||
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-16, 1e-16, evec2, eps2);
|
||||
|
||||
// Do we really need to sort here?
|
||||
int begin = 1;
|
||||
int end = row_num;
|
||||
int swapped = 1;
|
||||
while (swapped)
|
||||
{
|
||||
swapped = 0;
|
||||
for (i = begin; i < end; i++)
|
||||
{
|
||||
if (mag (evec2[i]) > mag (evec2[i + 1]))
|
||||
{
|
||||
swap (evec2 + i, evec2 + i + 1);
|
||||
swapped = 1;
|
||||
}
|
||||
}
|
||||
end--;
|
||||
for (i = end - 1; i >= begin; i--)
|
||||
{
|
||||
if (mag (evec2[i]) > mag (evec2[i + 1]))
|
||||
{
|
||||
swap (evec2 + i, evec2 + i + 1);
|
||||
swapped = 1;
|
||||
}
|
||||
}
|
||||
begin++;
|
||||
}
|
||||
|
||||
for (i = 0; i < row_num; i++)
|
||||
{
|
||||
for (j = 0; j < row_num; j++)
|
||||
{
|
||||
if (i == j)
|
||||
H[i * row_num + j] = evec2[i + 1];
|
||||
else
|
||||
H[i * row_num + j] = 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bisec (std::vector < RealD > &c,
|
||||
std::vector < RealD > &b,
|
||||
int n,
|
||||
int m1,
|
||||
int m2,
|
||||
RealD eps1,
|
||||
RealD relfeh, std::vector < RealD > &x, RealD & eps2)
|
||||
{
|
||||
std::vector < RealD > wu (n + 2);
|
||||
|
||||
RealD h, q, x1, xu, x0, xmin, xmax;
|
||||
int i, a, k;
|
||||
|
||||
b[1] = 0.0;
|
||||
xmin = c[n] - fabs (b[n]);
|
||||
xmax = c[n] + fabs (b[n]);
|
||||
for (i = 1; i < n; i++)
|
||||
{
|
||||
h = fabs (b[i]) + fabs (b[i + 1]);
|
||||
if (c[i] + h > xmax)
|
||||
xmax = c[i] + h;
|
||||
if (c[i] - h < xmin)
|
||||
xmin = c[i] - h;
|
||||
}
|
||||
xmax *= 2.;
|
||||
|
||||
eps2 = relfeh * ((xmin + xmax) > 0.0 ? xmax : -xmin);
|
||||
if (eps1 <= 0.0)
|
||||
eps1 = eps2;
|
||||
eps2 = 0.5 * eps1 + 7.0 * (eps2);
|
||||
x0 = xmax;
|
||||
for (i = m1; i <= m2; i++)
|
||||
{
|
||||
x[i] = xmax;
|
||||
wu[i] = xmin;
|
||||
}
|
||||
|
||||
for (k = m2; k >= m1; k--)
|
||||
{
|
||||
xu = xmin;
|
||||
i = k;
|
||||
do
|
||||
{
|
||||
if (xu < wu[i])
|
||||
{
|
||||
xu = wu[i];
|
||||
i = m1 - 1;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
while (i >= m1);
|
||||
if (x0 > x[k])
|
||||
x0 = x[k];
|
||||
while ((x0 - xu) > 2 * relfeh * (fabs (xu) + fabs (x0)) + eps1)
|
||||
{
|
||||
x1 = (xu + x0) / 2;
|
||||
|
||||
a = 0;
|
||||
q = 1.0;
|
||||
for (i = 1; i <= n; i++)
|
||||
{
|
||||
q =
|
||||
c[i] - x1 -
|
||||
((q != 0.0) ? b[i] * b[i] / q : fabs (b[i]) / relfeh);
|
||||
if (q < 0)
|
||||
a++;
|
||||
}
|
||||
// printf("x1=%0.14e a=%d\n",x1,a);
|
||||
if (a < k)
|
||||
{
|
||||
if (a < m1)
|
||||
{
|
||||
xu = x1;
|
||||
wu[m1] = x1;
|
||||
}
|
||||
else
|
||||
{
|
||||
xu = x1;
|
||||
wu[a + 1] = x1;
|
||||
if (x[a] > x1)
|
||||
x[a] = x1;
|
||||
}
|
||||
}
|
||||
else
|
||||
x0 = x1;
|
||||
}
|
||||
printf ("x0=%0.14e xu=%0.14e k=%d\n", x0, xu, k);
|
||||
x[k] = (x0 + xu) / 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Implicitly restarted lanczos
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template < class Field > class SimpleLanczos
|
||||
{
|
||||
|
||||
const RealD small = 1.0e-16;
|
||||
public:
|
||||
int lock;
|
||||
int get;
|
||||
int Niter;
|
||||
int converged;
|
||||
|
||||
int Nstop; // Number of evecs checked for convergence
|
||||
int Nk; // Number of converged sought
|
||||
int Np; // Np -- Number of spare vecs in kryloc space
|
||||
int Nm; // Nm -- total number of vectors
|
||||
|
||||
|
||||
RealD OrthoTime;
|
||||
|
||||
RealD eresid;
|
||||
|
||||
// SortEigen < Field > _sort;
|
||||
|
||||
LinearFunction < Field > &_Linop;
|
||||
|
||||
// OperatorFunction < Field > &_poly;
|
||||
|
||||
/////////////////////////
|
||||
// Constructor
|
||||
/////////////////////////
|
||||
void init (void)
|
||||
{
|
||||
};
|
||||
// void Abort (int ff, std::vector < RealD > &evals, DenseVector < Denstd::vector < RealD > >&evecs);
|
||||
|
||||
SimpleLanczos (LinearFunction < Field > &Linop, // op
|
||||
// OperatorFunction < Field > &poly, // polynmial
|
||||
int _Nstop, // sought vecs
|
||||
int _Nk, // sought vecs
|
||||
int _Nm, // spare vecs
|
||||
RealD _eresid, // resid in lmdue deficit
|
||||
int _Niter): // Max iterations
|
||||
|
||||
_Linop (Linop),
|
||||
// _poly (poly),
|
||||
Nstop (_Nstop), Nk (_Nk), Nm (_Nm), eresid (_eresid), Niter (_Niter)
|
||||
{
|
||||
Np = Nm - Nk;
|
||||
assert (Np > 0);
|
||||
};
|
||||
|
||||
/////////////////////////
|
||||
// Sanity checked this routine (step) against Saad.
|
||||
/////////////////////////
|
||||
void RitzMatrix (std::vector < Field > &evec, int k)
|
||||
{
|
||||
|
||||
if (1)
|
||||
return;
|
||||
|
||||
GridBase *grid = evec[0].Grid();
|
||||
Field w (grid);
|
||||
std::cout << GridLogMessage << "RitzMatrix " << std::endl;
|
||||
for (int i = 0; i < k; i++)
|
||||
{
|
||||
_Linop(evec[i], w);
|
||||
// _poly(_Linop,evec[i],w);
|
||||
std::cout << GridLogMessage << "[" << i << "] ";
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
ComplexD in = innerProduct (evec[j], w);
|
||||
if (fabs ((double) i - j) > 1)
|
||||
{
|
||||
if (abs (in) > 1.0e-9)
|
||||
{
|
||||
std::cout << GridLogMessage << "oops" << std::endl;
|
||||
abort ();
|
||||
}
|
||||
else
|
||||
std::cout << GridLogMessage << " 0 ";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << GridLogMessage << " " << in << " ";
|
||||
}
|
||||
}
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void step (std::vector < RealD > &lmd,
|
||||
std::vector < RealD > &lme,
|
||||
Field & last, Field & current, Field & next, uint64_t k)
|
||||
{
|
||||
if (lmd.size () <= k)
|
||||
lmd.resize (k + Nm);
|
||||
if (lme.size () <= k)
|
||||
lme.resize (k + Nm);
|
||||
|
||||
|
||||
// _poly(_Linop,current,next ); // 3. wk:=Avk−βkv_{k−1}
|
||||
_Linop(current, next); // 3. wk:=Avk−βkv_{k−1}
|
||||
if (k > 0)
|
||||
{
|
||||
next -= lme[k - 1] * last;
|
||||
}
|
||||
// std::cout<<GridLogMessage << "<last|next>" << innerProduct(last,next) <<std::endl;
|
||||
|
||||
ComplexD zalph = innerProduct (current, next); // 4. αk:=(wk,vk)
|
||||
RealD alph = real (zalph);
|
||||
|
||||
next = next - alph * current; // 5. wk:=wk−αkvk
|
||||
// std::cout<<GridLogMessage << "<current|next>" << innerProduct(current,next) <<std::endl;
|
||||
|
||||
RealD beta = normalise (next); // 6. βk+1 := ∥wk∥2. If βk+1 = 0 then Stop
|
||||
// 7. vk+1 := wk/βk+1
|
||||
// norm=beta;
|
||||
|
||||
int interval = Nm / 100 + 1;
|
||||
if ((k % interval) == 0)
|
||||
std::
|
||||
cout << GridLogMessage << k << " : alpha = " << zalph << " beta " <<
|
||||
beta << std::endl;
|
||||
const RealD tiny = 1.0e-20;
|
||||
if (beta < tiny)
|
||||
{
|
||||
std::cout << GridLogMessage << " beta is tiny " << beta << std::
|
||||
endl;
|
||||
}
|
||||
lmd[k] = alph;
|
||||
lme[k] = beta;
|
||||
|
||||
}
|
||||
|
||||
void qr_decomp (std::vector < RealD > &lmd,
|
||||
std::vector < RealD > &lme,
|
||||
int Nk,
|
||||
int Nm,
|
||||
std::vector < RealD > &Qt, RealD Dsh, int kmin, int kmax)
|
||||
{
|
||||
int k = kmin - 1;
|
||||
RealD x;
|
||||
|
||||
RealD Fden = 1.0 / hypot (lmd[k] - Dsh, lme[k]);
|
||||
RealD c = (lmd[k] - Dsh) * Fden;
|
||||
RealD s = -lme[k] * Fden;
|
||||
|
||||
RealD tmpa1 = lmd[k];
|
||||
RealD tmpa2 = lmd[k + 1];
|
||||
RealD tmpb = lme[k];
|
||||
|
||||
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
|
||||
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
|
||||
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
|
||||
x = -s * lme[k + 1];
|
||||
lme[k + 1] = c * lme[k + 1];
|
||||
|
||||
for (int i = 0; i < Nk; ++i)
|
||||
{
|
||||
RealD Qtmp1 = Qt[i + Nm * k];
|
||||
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
|
||||
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
|
||||
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
|
||||
}
|
||||
|
||||
// Givens transformations
|
||||
for (int k = kmin; k < kmax - 1; ++k)
|
||||
{
|
||||
|
||||
RealD Fden = 1.0 / hypot (x, lme[k - 1]);
|
||||
RealD c = lme[k - 1] * Fden;
|
||||
RealD s = -x * Fden;
|
||||
|
||||
RealD tmpa1 = lmd[k];
|
||||
RealD tmpa2 = lmd[k + 1];
|
||||
RealD tmpb = lme[k];
|
||||
|
||||
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
|
||||
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
|
||||
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
|
||||
lme[k - 1] = c * lme[k - 1] - s * x;
|
||||
|
||||
if (k != kmax - 2)
|
||||
{
|
||||
x = -s * lme[k + 1];
|
||||
lme[k + 1] = c * lme[k + 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < Nk; ++i)
|
||||
{
|
||||
RealD Qtmp1 = Qt[i + Nm * k];
|
||||
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
|
||||
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
|
||||
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifdef USE_LAPACK
|
||||
#ifdef USE_MKL
|
||||
#define LAPACK_INT MKL_INT
|
||||
#else
|
||||
#define LAPACK_INT long long
|
||||
#endif
|
||||
void diagonalize_lapack (std::vector < RealD > &lmd, std::vector < RealD > &lme, int N1, // all
|
||||
int N2, // get
|
||||
GridBase * grid)
|
||||
{
|
||||
const int size = Nm;
|
||||
LAPACK_INT NN = N1;
|
||||
double evals_tmp[NN];
|
||||
double DD[NN];
|
||||
double EE[NN];
|
||||
for (int i = 0; i < NN; i++)
|
||||
for (int j = i - 1; j <= i + 1; j++)
|
||||
if (j < NN && j >= 0)
|
||||
{
|
||||
if (i == j)
|
||||
DD[i] = lmd[i];
|
||||
if (i == j)
|
||||
evals_tmp[i] = lmd[i];
|
||||
if (j == (i - 1))
|
||||
EE[j] = lme[j];
|
||||
}
|
||||
LAPACK_INT evals_found;
|
||||
LAPACK_INT lwork =
|
||||
((18 * NN) >
|
||||
(1 + 4 * NN + NN * NN) ? (18 * NN) : (1 + 4 * NN + NN * NN));
|
||||
LAPACK_INT liwork = 3 + NN * 10;
|
||||
LAPACK_INT iwork[liwork];
|
||||
double work[lwork];
|
||||
LAPACK_INT isuppz[2 * NN];
|
||||
char jobz = 'N'; // calculate evals only
|
||||
char range = 'I'; // calculate il-th to iu-th evals
|
||||
// char range = 'A'; // calculate all evals
|
||||
char uplo = 'U'; // refer to upper half of original matrix
|
||||
char compz = 'I'; // Compute eigenvectors of tridiagonal matrix
|
||||
int ifail[NN];
|
||||
LAPACK_INT info;
|
||||
// int total = QMP_get_number_of_nodes();
|
||||
// int node = QMP_get_node_number();
|
||||
// GridBase *grid = evec[0]._grid;
|
||||
int total = grid->_Nprocessors;
|
||||
int node = grid->_processor;
|
||||
int interval = (NN / total) + 1;
|
||||
double vl = 0.0, vu = 0.0;
|
||||
LAPACK_INT il = interval * node + 1, iu = interval * (node + 1);
|
||||
if (iu > NN)
|
||||
iu = NN;
|
||||
double tol = 0.0;
|
||||
if (1)
|
||||
{
|
||||
memset (evals_tmp, 0, sizeof (double) * NN);
|
||||
if (il <= NN)
|
||||
{
|
||||
printf ("total=%d node=%d il=%d iu=%d\n", total, node, il, iu);
|
||||
#ifdef USE_MKL
|
||||
dstegr (&jobz, &range, &NN,
|
||||
#else
|
||||
LAPACK_dstegr (&jobz, &range, &NN,
|
||||
#endif
|
||||
(double *) DD, (double *) EE, &vl, &vu, &il, &iu, // these four are ignored if second parameteris 'A'
|
||||
&tol, // tolerance
|
||||
&evals_found, evals_tmp, (double *) NULL, &NN,
|
||||
isuppz, work, &lwork, iwork, &liwork, &info);
|
||||
for (int i = iu - 1; i >= il - 1; i--)
|
||||
{
|
||||
printf ("node=%d evals_found=%d evals_tmp[%d] = %g\n", node,
|
||||
evals_found, i - (il - 1), evals_tmp[i - (il - 1)]);
|
||||
evals_tmp[i] = evals_tmp[i - (il - 1)];
|
||||
if (il > 1)
|
||||
evals_tmp[i - (il - 1)] = 0.;
|
||||
}
|
||||
}
|
||||
{
|
||||
grid->GlobalSumVector (evals_tmp, NN);
|
||||
}
|
||||
}
|
||||
// cheating a bit. It is better to sort instead of just reversing it, but the document of the routine says evals are sorted in increasing order. qr gives evals in decreasing order.
|
||||
}
|
||||
#undef LAPACK_INT
|
||||
#endif
|
||||
|
||||
|
||||
void diagonalize (std::vector < RealD > &lmd,
|
||||
std::vector < RealD > &lme,
|
||||
int N2, int N1, GridBase * grid)
|
||||
{
|
||||
|
||||
#ifdef USE_LAPACK
|
||||
const int check_lapack = 0; // just use lapack if 0, check against lapack if 1
|
||||
|
||||
if (!check_lapack)
|
||||
return diagonalize_lapack (lmd, lme, N2, N1, grid);
|
||||
|
||||
// diagonalize_lapack(lmd2,lme2,Nm2,Nm,Qt,grid);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static RealD normalise (Field & v)
|
||||
{
|
||||
RealD nn = norm2 (v);
|
||||
nn = sqrt (nn);
|
||||
v = v * (1.0 / nn);
|
||||
return nn;
|
||||
}
|
||||
|
||||
void orthogonalize (Field & w, std::vector < Field > &evec, int k)
|
||||
{
|
||||
double t0 = -usecond () / 1e6;
|
||||
typedef typename Field::scalar_type MyComplex;
|
||||
MyComplex ip;
|
||||
|
||||
if (0)
|
||||
{
|
||||
for (int j = 0; j < k; ++j)
|
||||
{
|
||||
normalise (evec[j]);
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
ip = innerProduct (evec[i], evec[j]); // are the evecs normalised? ; this assumes so.
|
||||
evec[j] = evec[j] - ip * evec[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < k; ++j)
|
||||
{
|
||||
ip = innerProduct (evec[j], w); // are the evecs normalised? ; this assumes so.
|
||||
w = w - ip * evec[j];
|
||||
}
|
||||
normalise (w);
|
||||
t0 += usecond () / 1e6;
|
||||
OrthoTime += t0;
|
||||
}
|
||||
|
||||
void setUnit_Qt (int Nm, std::vector < RealD > &Qt)
|
||||
{
|
||||
for (int i = 0; i < Qt.size (); ++i)
|
||||
Qt[i] = 0.0;
|
||||
for (int k = 0; k < Nm; ++k)
|
||||
Qt[k + k * Nm] = 1.0;
|
||||
}
|
||||
|
||||
|
||||
void calc (std::vector < RealD > &eval, const Field & src, int &Nconv)
|
||||
{
|
||||
|
||||
GridBase *grid = src.Grid();
|
||||
// assert(grid == src._grid);
|
||||
|
||||
std::
|
||||
cout << GridLogMessage << " -- Nk = " << Nk << " Np = " << Np << std::
|
||||
endl;
|
||||
std::cout << GridLogMessage << " -- Nm = " << Nm << std::endl;
|
||||
std::cout << GridLogMessage << " -- size of eval = " << eval.
|
||||
size () << std::endl;
|
||||
|
||||
// assert(c.size() && Nm == eval.size());
|
||||
|
||||
std::vector < RealD > lme (Nm);
|
||||
std::vector < RealD > lmd (Nm);
|
||||
|
||||
|
||||
Field current (grid);
|
||||
Field last (grid);
|
||||
Field next (grid);
|
||||
|
||||
Nconv = 0;
|
||||
|
||||
RealD beta_k;
|
||||
|
||||
// Set initial vector
|
||||
// (uniform vector) Why not src??
|
||||
// evec[0] = 1.0;
|
||||
current = src;
|
||||
std::cout << GridLogMessage << "norm2(src)= " << norm2 (src) << std::
|
||||
endl;
|
||||
normalise (current);
|
||||
std::
|
||||
cout << GridLogMessage << "norm2(evec[0])= " << norm2 (current) <<
|
||||
std::endl;
|
||||
|
||||
// Initial Nk steps
|
||||
OrthoTime = 0.;
|
||||
double t0 = usecond () / 1e6;
|
||||
RealD norm; // sqrt norm of last vector
|
||||
|
||||
uint64_t iter = 0;
|
||||
|
||||
bool initted = false;
|
||||
std::vector < RealD > low (Nstop * 10);
|
||||
std::vector < RealD > high (Nstop * 10);
|
||||
RealD cont = 0.;
|
||||
while (1) {
|
||||
cont = 0.;
|
||||
std::vector < RealD > lme2 (Nm);
|
||||
std::vector < RealD > lmd2 (Nm);
|
||||
for (uint64_t k = 0; k < Nm; ++k, iter++) {
|
||||
step (lmd, lme, last, current, next, iter);
|
||||
last = current;
|
||||
current = next;
|
||||
}
|
||||
double t1 = usecond () / 1e6;
|
||||
std::cout << GridLogMessage << "IRL::Initial steps: " << t1 -
|
||||
t0 << "seconds" << std::endl;
|
||||
t0 = t1;
|
||||
std::
|
||||
cout << GridLogMessage << "IRL::Initial steps:OrthoTime " <<
|
||||
OrthoTime << "seconds" << std::endl;
|
||||
|
||||
// getting eigenvalues
|
||||
lmd2.resize (iter + 2);
|
||||
lme2.resize (iter + 2);
|
||||
for (uint64_t k = 0; k < iter; ++k) {
|
||||
lmd2[k + 1] = lmd[k];
|
||||
lme2[k + 2] = lme[k];
|
||||
}
|
||||
t1 = usecond () / 1e6;
|
||||
std::cout << GridLogMessage << "IRL:: copy: " << t1 -
|
||||
t0 << "seconds" << std::endl;
|
||||
t0 = t1;
|
||||
{
|
||||
int total = grid->_Nprocessors;
|
||||
int node = grid->_processor;
|
||||
int interval = (Nstop / total) + 1;
|
||||
int iu = (iter + 1) - (interval * node + 1);
|
||||
int il = (iter + 1) - (interval * (node + 1));
|
||||
std::vector < RealD > eval2 (iter + 3);
|
||||
RealD eps2;
|
||||
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
|
||||
eps2);
|
||||
// diagonalize(eval2,lme2,iter,Nk,grid);
|
||||
RealD diff = 0.;
|
||||
for (int i = il; i <= iu; i++) {
|
||||
if (initted)
|
||||
diff =
|
||||
fabs (eval2[i] - high[iu-i]) / (fabs (eval2[i]) +
|
||||
fabs (high[iu-i]));
|
||||
if (initted && (diff > eresid))
|
||||
cont = 1.;
|
||||
if (initted)
|
||||
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
|
||||
high[iu-i], diff);
|
||||
high[iu-i] = eval2[i];
|
||||
}
|
||||
il = (interval * node + 1);
|
||||
iu = (interval * (node + 1));
|
||||
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
|
||||
eps2);
|
||||
for (int i = il; i <= iu; i++) {
|
||||
if (initted)
|
||||
diff =
|
||||
fabs (eval2[i] - low[i]) / (fabs (eval2[i]) +
|
||||
fabs (low[i]));
|
||||
if (initted && (diff > eresid))
|
||||
cont = 1.;
|
||||
if (initted)
|
||||
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
|
||||
low[i], diff);
|
||||
low[i] = eval2[i];
|
||||
}
|
||||
t1 = usecond () / 1e6;
|
||||
std::cout << GridLogMessage << "IRL:: diagonalize: " << t1 -
|
||||
t0 << "seconds" << std::endl;
|
||||
t0 = t1;
|
||||
}
|
||||
|
||||
for (uint64_t k = 0; k < Nk; ++k) {
|
||||
// eval[k] = eval2[k];
|
||||
}
|
||||
if (initted)
|
||||
{
|
||||
grid->GlobalSumVector (&cont, 1);
|
||||
if (cont < 1.) return;
|
||||
}
|
||||
initted = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
There is some matrix Q such that for any vector y
|
||||
Q.e_1 = y and Q is unitary.
|
||||
**/
|
||||
template < class T >
|
||||
static T orthQ (DenseMatrix < T > &Q, std::vector < T > y)
|
||||
{
|
||||
int N = y.size (); //Matrix Size
|
||||
Fill (Q, 0.0);
|
||||
T tau;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
Q[i][0] = y[i];
|
||||
}
|
||||
T sig = conj (y[0]) * y[0];
|
||||
T tau0 = fabs (sqrt (sig));
|
||||
|
||||
for (int j = 1; j < N; j++)
|
||||
{
|
||||
sig += conj (y[j]) * y[j];
|
||||
tau = abs (sqrt (sig));
|
||||
|
||||
if (abs (tau0) > 0.0)
|
||||
{
|
||||
|
||||
T gam = conj ((y[j] / tau) / tau0);
|
||||
for (int k = 0; k <= j - 1; k++)
|
||||
{
|
||||
Q[k][j] = -gam * y[k];
|
||||
}
|
||||
Q[j][j] = tau0 / tau;
|
||||
}
|
||||
else
|
||||
{
|
||||
Q[j - 1][j] = 1.0;
|
||||
}
|
||||
tau0 = tau;
|
||||
}
|
||||
return tau;
|
||||
}
|
||||
|
||||
/**
|
||||
There is some matrix Q such that for any vector y
|
||||
Q.e_k = y and Q is unitary.
|
||||
**/
|
||||
template < class T >
|
||||
static T orthU (DenseMatrix < T > &Q, std::vector < T > y)
|
||||
{
|
||||
T tau = orthQ (Q, y);
|
||||
SL (Q);
|
||||
return tau;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Wind up with a matrix with the first con rows untouched
|
||||
|
||||
say con = 2
|
||||
Q is such that Qdag H Q has {x, x, val, 0, 0, 0, 0, ...} as 1st colum
|
||||
and the matrix is upper hessenberg
|
||||
and with f and Q appropriately modidied with Q is the arnoldi factorization
|
||||
|
||||
**/
|
||||
|
||||
template < class T > static void Lock (DenseMatrix < T > &H, ///Hess mtx
|
||||
DenseMatrix < T > &Q, ///Lock Transform
|
||||
T val, ///value to be locked
|
||||
int con, ///number already locked
|
||||
RealD small, int dfg, bool herm)
|
||||
{
|
||||
//ForceTridiagonal(H);
|
||||
|
||||
int M = H.dim;
|
||||
DenseVector < T > vec;
|
||||
Resize (vec, M - con);
|
||||
|
||||
DenseMatrix < T > AH;
|
||||
Resize (AH, M - con, M - con);
|
||||
AH = GetSubMtx (H, con, M, con, M);
|
||||
|
||||
DenseMatrix < T > QQ;
|
||||
Resize (QQ, M - con, M - con);
|
||||
|
||||
Unity (Q);
|
||||
Unity (QQ);
|
||||
|
||||
DenseVector < T > evals;
|
||||
Resize (evals, M - con);
|
||||
DenseMatrix < T > evecs;
|
||||
Resize (evecs, M - con, M - con);
|
||||
|
||||
Wilkinson < T > (AH, evals, evecs, small);
|
||||
|
||||
int k = 0;
|
||||
RealD cold = abs (val - evals[k]);
|
||||
for (int i = 1; i < M - con; i++)
|
||||
{
|
||||
RealD cnew = abs (val - evals[i]);
|
||||
if (cnew < cold)
|
||||
{
|
||||
k = i;
|
||||
cold = cnew;
|
||||
}
|
||||
}
|
||||
vec = evecs[k];
|
||||
|
||||
ComplexD tau;
|
||||
orthQ (QQ, vec);
|
||||
//orthQM(QQ,AH,vec);
|
||||
|
||||
AH = Hermitian (QQ) * AH;
|
||||
AH = AH * QQ;
|
||||
|
||||
for (int i = con; i < M; i++)
|
||||
{
|
||||
for (int j = con; j < M; j++)
|
||||
{
|
||||
Q[i][j] = QQ[i - con][j - con];
|
||||
H[i][j] = AH[i - con][j - con];
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = M - 1; j > con + 2; j--)
|
||||
{
|
||||
|
||||
DenseMatrix < T > U;
|
||||
Resize (U, j - 1 - con, j - 1 - con);
|
||||
DenseVector < T > z;
|
||||
Resize (z, j - 1 - con);
|
||||
T nm = norm (z);
|
||||
for (int k = con + 0; k < j - 1; k++)
|
||||
{
|
||||
z[k - con] = conj (H (j, k + 1));
|
||||
}
|
||||
normalise (z);
|
||||
|
||||
RealD tmp = 0;
|
||||
for (int i = 0; i < z.size () - 1; i++)
|
||||
{
|
||||
tmp = tmp + abs (z[i]);
|
||||
}
|
||||
|
||||
if (tmp < small / ((RealD) z.size () - 1.0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tau = orthU (U, z);
|
||||
|
||||
DenseMatrix < T > Hb;
|
||||
Resize (Hb, j - 1 - con, M);
|
||||
|
||||
for (int a = 0; a < M; a++)
|
||||
{
|
||||
for (int b = 0; b < j - 1 - con; b++)
|
||||
{
|
||||
T sum = 0;
|
||||
for (int c = 0; c < j - 1 - con; c++)
|
||||
{
|
||||
sum += H[a][con + 1 + c] * U[c][b];
|
||||
} //sum += H(a,con+1+c)*U(c,b);}
|
||||
Hb[b][a] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = con + 1; k < j; k++)
|
||||
{
|
||||
for (int l = 0; l < M; l++)
|
||||
{
|
||||
H[l][k] = Hb[k - 1 - con][l];
|
||||
}
|
||||
} //H(Hb[k-1-con][l] , l,k);}}
|
||||
|
||||
DenseMatrix < T > Qb;
|
||||
Resize (Qb, M, M);
|
||||
|
||||
for (int a = 0; a < M; a++)
|
||||
{
|
||||
for (int b = 0; b < j - 1 - con; b++)
|
||||
{
|
||||
T sum = 0;
|
||||
for (int c = 0; c < j - 1 - con; c++)
|
||||
{
|
||||
sum += Q[a][con + 1 + c] * U[c][b];
|
||||
} //sum += Q(a,con+1+c)*U(c,b);}
|
||||
Qb[b][a] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = con + 1; k < j; k++)
|
||||
{
|
||||
for (int l = 0; l < M; l++)
|
||||
{
|
||||
Q[l][k] = Qb[k - 1 - con][l];
|
||||
}
|
||||
} //Q(Qb[k-1-con][l] , l,k);}}
|
||||
|
||||
DenseMatrix < T > Hc;
|
||||
Resize (Hc, M, M);
|
||||
|
||||
for (int a = 0; a < j - 1 - con; a++)
|
||||
{
|
||||
for (int b = 0; b < M; b++)
|
||||
{
|
||||
T sum = 0;
|
||||
for (int c = 0; c < j - 1 - con; c++)
|
||||
{
|
||||
sum += conj (U[c][a]) * H[con + 1 + c][b];
|
||||
} //sum += conj( U(c,a) )*H(con+1+c,b);}
|
||||
Hc[b][a] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < M; k++)
|
||||
{
|
||||
for (int l = con + 1; l < j; l++)
|
||||
{
|
||||
H[l][k] = Hc[k][l - 1 - con];
|
||||
}
|
||||
} //H(Hc[k][l-1-con] , l,k);}}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/SplitGridBlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_SPLIT_BLOCK_KRYLOV_SCHUR_H
|
||||
#define GRID_SPLIT_BLOCK_KRYLOV_SCHUR_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Block Krylov-Schur eigensolver with Split-Grid batched operator application.
|
||||
*
|
||||
* Derives from BlockKrylovSchur<Field> and overrides only the operator
|
||||
* application: instead of calling Linop.Op() once per vector, mrhs vectors
|
||||
* are packed onto a smaller split grid with Grid_split, the polynomial-
|
||||
* filtered operator poly(SLinop) is applied once to the combined field, and
|
||||
* the results are unpacked with Grid_unsplit. All other algorithmic logic
|
||||
* (Arnoldi orthogonalisation, Schur restart, convergence test, etc.) is
|
||||
* inherited unchanged from the base class.
|
||||
*
|
||||
* Constructor extras (beyond BlockKrylovSchur)
|
||||
* -------------------------------------------
|
||||
* SLinop : split-grid linear operator used inside poly
|
||||
* poly : polynomial filter OperatorFunction applied on the split grid
|
||||
* SGrid : split-grid GridBase (mrhs fields packed here)
|
||||
* mrhs : RHS batched per split-grid call; Nblock must be divisible by mrhs
|
||||
*
|
||||
* Notes
|
||||
* -----
|
||||
* - The Rayleigh quotient H is built from poly(A), so its Ritz values are
|
||||
* eigenvalues of poly(A). Convergence is assessed against the full-grid
|
||||
* Linop (inherited from base), giving true A-eigenvalues.
|
||||
* - Grid_ (inherited) is the full grid; SGrid is the split grid.
|
||||
*/
|
||||
template<class Field>
|
||||
class SplitGridBlockKrylovSchur : public BlockKrylovSchur<Field> {
|
||||
|
||||
using Base = BlockKrylovSchur<Field>;
|
||||
|
||||
// Bring protected base members into scope
|
||||
using Base::Nblock;
|
||||
using Base::Grid_;
|
||||
using Base::basis;
|
||||
|
||||
// Split-grid extras
|
||||
LinearOperatorBase<Field>& SLinop;
|
||||
OperatorFunction<Field>& poly;
|
||||
GridBase* SGrid;
|
||||
int mrhs;
|
||||
|
||||
public:
|
||||
|
||||
SplitGridBlockKrylovSchur(LinearOperatorBase<Field>& _Linop,
|
||||
LinearOperatorBase<Field>& _SLinop,
|
||||
OperatorFunction<Field>& _poly,
|
||||
GridBase* _FGrid,
|
||||
GridBase* _SGrid,
|
||||
int _mrhs,
|
||||
RealD _Tolerance,
|
||||
RitzFilter _rf = EvalReSmall)
|
||||
: Base(_Linop, _FGrid, _Tolerance, _rf),
|
||||
SLinop(_SLinop), poly(_poly), SGrid(_SGrid), mrhs(_mrhs)
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
// Validate mrhs divisibility before the Arnoldi loop starts
|
||||
void preRun() override
|
||||
{
|
||||
assert(Nblock % mrhs == 0 && "Nblock must be divisible by mrhs");
|
||||
}
|
||||
|
||||
// Apply poly(A) to basis[kBase .. kBase+Nblock-1] via Grid_split / Grid_unsplit,
|
||||
// batching mrhs vectors per split-grid call.
|
||||
void applyBlock(std::vector<Field>& W, int kBase) override
|
||||
{
|
||||
std::vector<Field> in(mrhs, Field(Grid_));
|
||||
Field s_in(SGrid);
|
||||
Field s_out(SGrid);
|
||||
|
||||
int k_start = 0;
|
||||
while (k_start < Nblock) {
|
||||
for (int u = 0; u < mrhs; u++)
|
||||
in[u] = basis[kBase + k_start + u];
|
||||
|
||||
Grid_split(in, s_in);
|
||||
poly(SLinop, s_in, s_out);
|
||||
Grid_unsplit(in, s_out);
|
||||
|
||||
for (int u = 0; u < mrhs; u++)
|
||||
W[k_start + u] = in[u];
|
||||
|
||||
k_start += mrhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_SPLIT_BLOCK_KRYLOV_SCHUR_H
|
||||
@@ -0,0 +1,366 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/TrueHarmonicBlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_TRUE_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
#define GRID_TRUE_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
|
||||
#include <Grid/algorithms/iterative/HarmonicBlockKrylovSchur.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* True harmonic-Ritz block Krylov-Schur eigensolver.
|
||||
*
|
||||
* Difference from the base class
|
||||
* ------------------------------
|
||||
* HarmonicBlockKrylovSchur is a *shift-targeted* Krylov-Schur: it Schur-
|
||||
* decomposes (H - sigma I) and sorts the standard Ritz values by |lambda -
|
||||
* sigma|. For interior eigenvalues of Hermitian indefinite operators the
|
||||
* standard Ritz values can be spurious ("ghosts"), so the sort can select
|
||||
* junk. This variant performs genuine harmonic Ritz extraction instead.
|
||||
*
|
||||
* Harmonic extraction
|
||||
* -------------------
|
||||
* From the exact block Arnoldi relation
|
||||
*
|
||||
* A V = V H + F B^dag (1)
|
||||
*
|
||||
* harmonic Ritz pairs (theta, g) w.r.t. shift sigma satisfy the Petrov-
|
||||
* Galerkin condition (A - sigma)Vg - (theta - sigma)Vg perp (A - sigma)V,
|
||||
* which reduces (with Hs = H - sigma I) to the small eigenproblem
|
||||
*
|
||||
* Hhat g = (theta - sigma) g, Hhat = Hs + Hs^{-H} B B^dag (2)
|
||||
*
|
||||
* Exact thick restart
|
||||
* -------------------
|
||||
* From (2), H g = theta g - Hs^{-H} B (B^dag g), so the residual of every
|
||||
* harmonic pair lies in the Nblock-dimensional column space of the single
|
||||
* block
|
||||
*
|
||||
* W = F - V Hs^{-H} B :
|
||||
* A (V g) - theta (V g) = W (B^dag g) (3)
|
||||
*
|
||||
* Hence {V g_1 .. g_Nk} + cols(W) admits an EXACT truncated Krylov-Schur
|
||||
* relation A Y = Y H_k + What Bc^dag with dense H_k, and block Arnoldi
|
||||
* resumes cleanly from What. Krylov-Schur (Stewart, SIMAX 23(3), 2001)
|
||||
* does not require H to be triangular after restart -- only that the
|
||||
* decomposition holds exactly, which (3) guarantees.
|
||||
*
|
||||
* This is the block analogue of the GMRES-DR restart (Morgan, SISC 24(1),
|
||||
* 2002; block version: Morgan, Appl. Numer. Math. 54, 2005). The exact
|
||||
* composition "block harmonic Krylov-Schur" is a derivation from these
|
||||
* ingredients rather than a published algorithm; the relation (1) is
|
||||
* checked explicitly at every restart when doVerify=true, and identity (3)
|
||||
* was validated to machine precision in a standalone Eigen prototype.
|
||||
*
|
||||
* Caveats
|
||||
* -------
|
||||
* - If sigma is (numerically) an eigenvalue of H, Hs^{-H} is singular;
|
||||
* an assert fires -- perturb the shift slightly.
|
||||
* - Reported eigenvalues come from the eigensystem of the retained H_k
|
||||
* (the true projected Rayleigh quotient Y^dag A Y), as in the base
|
||||
* class, so convergence tests and reporting are inherited unchanged.
|
||||
*
|
||||
* Usage: identical to HarmonicBlockKrylovSchur.
|
||||
*/
|
||||
template<class Field>
|
||||
class TrueHarmonicBlockKrylovSchur : public HarmonicBlockKrylovSchur<Field> {
|
||||
|
||||
typedef HarmonicBlockKrylovSchur<Field> Base;
|
||||
typedef typename Base::CMat CMat;
|
||||
typedef typename Base::CVec CVec;
|
||||
|
||||
// Members of the dependent base class need explicit scope.
|
||||
using Base::Nblock;
|
||||
using Base::Nm;
|
||||
using Base::Nk;
|
||||
using Base::Nstop;
|
||||
using Base::MaxIter;
|
||||
using Base::Tolerance;
|
||||
using Base::shift;
|
||||
using Base::Linop;
|
||||
using Base::Grid_;
|
||||
using Base::ritzFilter;
|
||||
using Base::basis;
|
||||
using Base::H;
|
||||
using Base::F;
|
||||
using Base::B;
|
||||
using Base::beta_k;
|
||||
using Base::rtol;
|
||||
using Base::evals;
|
||||
using Base::littleEvecs;
|
||||
using Base::ritzEstimates;
|
||||
|
||||
using Base::expandStartBlock;
|
||||
using Base::blockArnoldiIteration;
|
||||
using Base::blockQR;
|
||||
using Base::computeEigensystem;
|
||||
using Base::converged;
|
||||
using Base::approxMaxEval;
|
||||
|
||||
public:
|
||||
using Base::evecs;
|
||||
using Base::doEvalCheck;
|
||||
using Base::useParityFlip;
|
||||
using Base::useGamma5;
|
||||
using Base::gamma5Func;
|
||||
using Base::verify;
|
||||
|
||||
TrueHarmonicBlockKrylovSchur(LinearOperatorBase<Field>& _Linop, GridBase* _Grid,
|
||||
RealD _Tolerance, ComplexD _shift = 0.0,
|
||||
RitzFilter _rf = EvalNormSmall)
|
||||
: Base(_Linop, _Grid, _Tolerance, _shift, _rf)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point (same signature and outer structure as the base;
|
||||
// the Schur-sort restart is replaced by harmonicRestart()).
|
||||
//--------------------------------------------------------------------
|
||||
virtual void operator()(const std::vector<Field>& v0, int _maxIter, int _Nm, int _Nk,
|
||||
int _Nstop, int _Nblock = 1, bool doubleOrthog = true,
|
||||
bool doVerify = false) override
|
||||
{
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm;
|
||||
Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
Nblock = _Nblock;
|
||||
|
||||
{
|
||||
int divisor = 1;
|
||||
if (useParityFlip) divisor *= 2;
|
||||
if (useGamma5) divisor *= 2;
|
||||
assert(Nblock % divisor == 0 && (int)v0.size() >= Nblock / divisor);
|
||||
}
|
||||
assert(Nm % Nblock == 0);
|
||||
assert(Nk % Nblock == 0);
|
||||
assert(Nk < Nm);
|
||||
if (useGamma5) assert(gamma5Func && "useGamma5: gamma5Func must be set");
|
||||
|
||||
int N = Nm;
|
||||
|
||||
RealD approxLambdaMax = approxMaxEval(v0[0]);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: approx max eval = " << approxLambdaMax
|
||||
<< ", rtol = " << rtol
|
||||
<< ", shift = " << shift << std::endl;
|
||||
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
int start = 0;
|
||||
std::vector<Field> startBlock = expandStartBlock(v0);
|
||||
|
||||
for (int iter = 0; iter < MaxIter; iter++) {
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: restart iteration " << iter << std::endl;
|
||||
|
||||
// ---- Block Arnoldi: extend from block 'start' to block Nm/Nblock ----
|
||||
blockArnoldiIteration(startBlock, Nm/Nblock, start, doubleOrthog);
|
||||
std::cout << GridLogMessage << "blockArnoldiIteration done " << std::endl;
|
||||
start = Nk/Nblock;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after Arnoldi";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Harmonic extraction + exact thick restart ----
|
||||
harmonicRestart();
|
||||
std::cout << GridLogMessage << "harmonicRestart done " << std::endl;
|
||||
|
||||
// Restart from the residual block W (exact by identity (3))
|
||||
startBlock = F;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after harmonic restart";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Eigensystem of retained H_k = Y^dag A Y for convergence ----
|
||||
CMat Hk = H(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
computeEigensystem(Hk, Nk);
|
||||
|
||||
int Nconv = converged(Nk);
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: converged " << Nconv
|
||||
<< " / " << Nstop << std::endl;
|
||||
|
||||
if (Nconv >= Nstop || iter == MaxIter - 1) {
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: done after " << iter
|
||||
<< " restarts, " << Nconv << " converged." << std::endl;
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << evals.transpose() << std::endl;
|
||||
|
||||
if (doEvalCheck) {
|
||||
Field w(Grid_);
|
||||
for (int k = 0; k < (int)evecs.size(); k++) {
|
||||
Linop.Op(evecs[k], w);
|
||||
ComplexD eval_est = toStdCmplx(innerProduct(evecs[k], w));
|
||||
w -= eval_est * evecs[k];
|
||||
RealD res = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage << "TrueHarmonicBlockKrylovSchur: evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Harmonic thick restart
|
||||
//--------------------------------------------------------------------
|
||||
// On entry: A V = V H + F B^dag exact, V = basis (Nm fields), F Nblock
|
||||
// residual fields, H/B the Nm-sized small matrices.
|
||||
// On exit: basis = Y (Nk orthonormal fields spanning the harmonic
|
||||
// Ritz space), F = What (Nblock orthonormal fields, perp Y),
|
||||
// H top-left Nk x Nk = Y^dag A Y (dense), B top Nk rows the
|
||||
// new coupling; relation A Y = Y H_k + F B^dag again exact.
|
||||
void harmonicRestart()
|
||||
{
|
||||
int N = Nm;
|
||||
|
||||
// ---- Small harmonic eigenproblem: Hhat g = (theta - sigma) g ----
|
||||
CMat Hs = H - shift * CMat::Identity(N, N);
|
||||
Eigen::FullPivLU<CMat> lu(Hs.adjoint());
|
||||
assert(lu.isInvertible() &&
|
||||
"TrueHarmonicBlockKrylovSchur: H - shift*I singular; perturb the shift");
|
||||
CMat M = lu.solve(B); // N x Nblock : Hs^{-H} B
|
||||
CMat Hhat = Hs + M * B.adjoint(); // N x N
|
||||
|
||||
Eigen::ComplexEigenSolver<CMat> es(Hhat);
|
||||
|
||||
// Sort harmonic values (theta - sigma) by the Ritz filter
|
||||
// (EvalNormSmall -> closest to the shift).
|
||||
ComplexComparator cComp(ritzFilter);
|
||||
std::vector<int> idx(N);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(es.eigenvalues()(a)), toStdCmplx(es.eigenvalues()(b)));
|
||||
});
|
||||
|
||||
CMat G(N, Nk);
|
||||
CVec theta(Nk);
|
||||
for (int k = 0; k < Nk; k++) {
|
||||
G.col(k) = es.eigenvectors().col(idx[k]);
|
||||
theta(k) = es.eigenvalues()(idx[k]) + toStdCmplx(shift);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: harmonic Ritz values nearest shift:" << std::endl;
|
||||
for (int k = 0; k < Nk; k++)
|
||||
std::cout << GridLogMessage << " [" << k << "] " << theta(k) << std::endl;
|
||||
|
||||
// ---- Orthonormalise the little vectors: G = Gt Rg ----
|
||||
Eigen::HouseholderQR<CMat> qr(G);
|
||||
CMat Gt = qr.householderQ() * CMat::Identity(N, Nk);
|
||||
CMat Rg = qr.matrixQR().topLeftCorner(Nk, Nk).template triangularView<Eigen::Upper>();
|
||||
for (int k = 0; k < Nk; k++)
|
||||
assert(std::abs(Rg(k, k)) > 1e-14 &&
|
||||
"TrueHarmonicBlockKrylovSchur: harmonic vectors linearly dependent");
|
||||
CMat RgInv = Rg.inverse();
|
||||
|
||||
// A (V G) = (V G) diag(theta) + W (B^dag G), W = F - V M
|
||||
// => A (V Gt) = (V Gt) T + W Ct
|
||||
CMat T = Rg * theta.asDiagonal() * RgInv; // Nk x Nk
|
||||
CMat Ct = (B.adjoint() * G) * RgInv; // Nblock x Nk
|
||||
|
||||
// ---- Large-field work ----
|
||||
// Y = V Gt : new orthonormal basis (Nk fields)
|
||||
std::vector<Field> Y;
|
||||
constructThin(Y, basis, Gt, Nk);
|
||||
|
||||
// W = F - V M : Nblock residual directions containing every harmonic
|
||||
// residual (identity (3) in the class comment)
|
||||
std::vector<Field> W = F;
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int j = 0; j < N; j++)
|
||||
W[t] -= basis[j] * M(j, t);
|
||||
|
||||
// Orthogonalise W against Y (two passes), recording P = Y^dag W
|
||||
CMat P = CMat::Zero(Nk, Nblock);
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
for (int j = 0; j < Nk; j++) {
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
ComplexD c = innerProduct(Y[j], W[t]);
|
||||
P(j, t) += toStdCmplx(c);
|
||||
W[t] -= c * Y[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block QR of the projected residual: W <- What, returns Rw
|
||||
CMat Rw = blockQR(W);
|
||||
|
||||
// ---- Reassemble the exact Krylov-Schur relation ----
|
||||
// A Y = Y (T + P Ct) + What (Rw Ct)
|
||||
CMat Hk = T + P * Ct; // Nk x Nk, dense
|
||||
CMat Bc = Rw * Ct; // Nblock x Nk
|
||||
|
||||
H = CMat::Zero(N, N);
|
||||
H(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = Hk;
|
||||
B = CMat::Zero(N, Nblock);
|
||||
for (int j = 0; j < Nk; j++)
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
B(j, t) = std::conj(Bc(t, j));
|
||||
|
||||
basis = Y;
|
||||
F = W;
|
||||
beta_k = Bc.norm();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: beta_k = " << beta_k << std::endl;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Thin basis combination: out[i] = sum_j U[j] * R(j,i), i < ncol
|
||||
//--------------------------------------------------------------------
|
||||
void constructThin(std::vector<Field>& out, const std::vector<Field>& U,
|
||||
const CMat& R, int ncol)
|
||||
{
|
||||
out.clear();
|
||||
Field tmp(Grid_);
|
||||
for (int i = 0; i < ncol; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)U.size(); j++)
|
||||
tmp += U[j] * R(j, i);
|
||||
out.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_TRUE_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
@@ -0,0 +1,611 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/Aggregates.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
inline RealD AggregatePowerLaw(RealD x)
|
||||
{
|
||||
// return std::pow(x,-4);
|
||||
// return std::pow(x,-3);
|
||||
return std::pow(x,-5);
|
||||
}
|
||||
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class Aggregation {
|
||||
public:
|
||||
constexpr int Nbasis(void) { return nbasis; };
|
||||
|
||||
typedef iVector<CComplex,nbasis > siteVector;
|
||||
typedef Lattice<siteVector> CoarseVector;
|
||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||
|
||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||
typedef Lattice<Fobj > FineField;
|
||||
|
||||
GridBase *CoarseGrid;
|
||||
GridBase *FineGrid;
|
||||
std::vector<Lattice<Fobj> > subspace;
|
||||
int checkerboard;
|
||||
int Checkerboard(void){return checkerboard;}
|
||||
Aggregation(GridBase *_CoarseGrid,GridBase *_FineGrid,int _checkerboard) :
|
||||
CoarseGrid(_CoarseGrid),
|
||||
FineGrid(_FineGrid),
|
||||
subspace(nbasis,_FineGrid),
|
||||
checkerboard(_checkerboard)
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
void Orthogonalise(void){
|
||||
CoarseScalar InnerProd(CoarseGrid);
|
||||
// std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl;
|
||||
blockOrthogonalise(InnerProd,subspace);
|
||||
}
|
||||
void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
||||
blockProject(CoarseVec,FineVec,subspace);
|
||||
}
|
||||
void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
||||
FineVec.Checkerboard() = subspace[0].Checkerboard();
|
||||
blockPromote(CoarseVec,FineVec,subspace);
|
||||
}
|
||||
|
||||
virtual void CreateSubspaceRandom(GridParallelRNG &RNG) {
|
||||
int nn=nbasis;
|
||||
RealD scale;
|
||||
FineField noise(FineGrid);
|
||||
for(int b=0;b<nn;b++){
|
||||
subspace[b] = Zero();
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
subspace[b] = noise;
|
||||
}
|
||||
}
|
||||
virtual void CreateSubspace(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,int nn=nbasis)
|
||||
{
|
||||
|
||||
RealD scale;
|
||||
|
||||
ConjugateGradient<FineField> CG(1.0e-4,2000,false);
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
|
||||
for(int b=0;b<nn;b++){
|
||||
|
||||
subspace[b] = Zero();
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
for(int i=0;i<4;i++){
|
||||
|
||||
CG(hermop,noise,subspace[b]);
|
||||
|
||||
noise = subspace[b];
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
}
|
||||
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl;
|
||||
subspace[b] = noise;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
virtual void CreateSubspaceGCR(GridParallelRNG &RNG,LinearOperatorBase<FineField> &DiracOp,int nn=nbasis)
|
||||
{
|
||||
RealD scale;
|
||||
|
||||
TrivialPrecon<FineField> simple_fine;
|
||||
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,30,30);
|
||||
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,12,12);
|
||||
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,12,12);
|
||||
PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,10,10);
|
||||
FineField noise(FineGrid);
|
||||
FineField src(FineGrid);
|
||||
FineField guess(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
|
||||
for(int b=0;b<nn;b++){
|
||||
|
||||
subspace[b] = Zero();
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|Op|n> "<<innerProduct(noise,Mn)<<std::endl;
|
||||
|
||||
for(int i=0;i<3;i++){
|
||||
// void operator() (const Field &src, Field &psi){
|
||||
#if 1
|
||||
if (i==0)std::cout << GridLogMessage << " inverting on noise "<<std::endl;
|
||||
src = noise;
|
||||
guess=Zero();
|
||||
GCR(src,guess);
|
||||
subspace[b] = guess;
|
||||
#else
|
||||
if (i==0)std::cout << GridLogMessage << " inverting on zero "<<std::endl;
|
||||
src=Zero();
|
||||
guess = noise;
|
||||
GCR(src,guess);
|
||||
subspace[b] = guess;
|
||||
#endif
|
||||
noise = subspace[b];
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
}
|
||||
|
||||
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<" <f|OpDagOp|f>"<<norm2(Mn)<<std::endl;
|
||||
subspace[b] = noise;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// World of possibilities here. But have tried quite a lot of experiments (250+ jobs run on Summit)
|
||||
// and this is the best I found
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
double lo,
|
||||
int orderfilter,
|
||||
int ordermin,
|
||||
int orderstep,
|
||||
double filterlo
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
std::cout << GridLogMessage<<" Chebyshev subspace pass-1 : ord "<<orderfilter<<" ["<<lo<<","<<hi<<"]"<<std::endl;
|
||||
std::cout << GridLogMessage<<" Chebyshev subspace pass-2 : nbasis"<<nn<<" min "
|
||||
<<ordermin<<" step "<<orderstep
|
||||
<<" lo"<<filterlo<<std::endl;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
int b =0;
|
||||
{
|
||||
ComplexD ip;
|
||||
// Filter
|
||||
Chebyshev<FineField> Cheb(lo,hi,orderfilter);
|
||||
Cheb(hermop,noise,Mn);
|
||||
// normalise
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
|
||||
hermop.Op(Mn,tmp);
|
||||
ip= innerProduct(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|Op|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
||||
|
||||
hermop.AdjOp(Mn,tmp);
|
||||
ip = innerProduct(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|AdjOp|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
||||
b++;
|
||||
}
|
||||
|
||||
// Generate a full sequence of Chebyshevs
|
||||
{
|
||||
lo=filterlo;
|
||||
noise=Mn;
|
||||
|
||||
FineField T0(FineGrid); T0 = noise;
|
||||
FineField T1(FineGrid);
|
||||
FineField T2(FineGrid);
|
||||
FineField y(FineGrid);
|
||||
|
||||
FineField *Tnm = &T0;
|
||||
FineField *Tn = &T1;
|
||||
FineField *Tnp = &T2;
|
||||
|
||||
// Tn=T1 = (xscale M + mscale)in
|
||||
RealD xscale = 2.0/(hi-lo);
|
||||
RealD mscale = -(hi+lo)/(hi-lo);
|
||||
hermop.HermOp(T0,y);
|
||||
T1=y*xscale+noise*mscale;
|
||||
|
||||
for(int n=2;n<=ordermin+orderstep*(nn-2);n++){
|
||||
|
||||
hermop.HermOp(*Tn,y);
|
||||
|
||||
autoView( y_v , y, AcceleratorWrite);
|
||||
autoView( Tn_v , (*Tn), AcceleratorWrite);
|
||||
autoView( Tnp_v , (*Tnp), AcceleratorWrite);
|
||||
autoView( Tnm_v , (*Tnm), AcceleratorWrite);
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
accelerator_for(ss, FineGrid->oSites(), Nsimd, {
|
||||
coalescedWrite(y_v[ss],xscale*y_v(ss)+mscale*Tn_v(ss));
|
||||
coalescedWrite(Tnp_v[ss],2.0*y_v(ss)-Tnm_v(ss));
|
||||
});
|
||||
|
||||
// Possible more fine grained control is needed than a linear sweep,
|
||||
// but huge productivity gain if this is simple algorithm and not a tunable
|
||||
int m =1;
|
||||
if ( n>=ordermin ) m=n-ordermin;
|
||||
if ( (m%orderstep)==0 ) {
|
||||
Mn=*Tnp;
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
|
||||
|
||||
ComplexD ip;
|
||||
|
||||
hermop.Op(Mn,tmp);
|
||||
ip= innerProduct(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|Op|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
||||
|
||||
hermop.AdjOp(Mn,tmp);
|
||||
ip = innerProduct(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|AdjOp|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
||||
|
||||
b++;
|
||||
}
|
||||
|
||||
// Cycle pointers to avoid copies
|
||||
FineField *swizzle = Tnm;
|
||||
Tnm =Tn;
|
||||
Tn =Tnp;
|
||||
Tnp =swizzle;
|
||||
|
||||
}
|
||||
}
|
||||
GRID_ASSERT(b==nn);
|
||||
}
|
||||
|
||||
|
||||
virtual void CreateSubspacePolyCheby(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
double lo1,
|
||||
int orderfilter,
|
||||
double lo2,
|
||||
int orderstep)
|
||||
{
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
std::cout << GridLogMessage<<" CreateSubspacePolyCheby "<<std::endl;
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn);
|
||||
std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
int b =0;
|
||||
{
|
||||
// Filter
|
||||
std::cout << GridLogMessage << "Cheby "<<lo1<<","<<hi<<" "<<orderstep<<std::endl;
|
||||
Chebyshev<FineField> Cheb(lo1,hi,orderfilter);
|
||||
Cheb(hermop,noise,Mn);
|
||||
// normalise
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|n> "<<norm2(Mn)<<std::endl;
|
||||
}
|
||||
|
||||
// Generate a full sequence of Chebyshevs
|
||||
for(int n=1;n<nn;n++){
|
||||
std::cout << GridLogMessage << "Cheby "<<lo2<<","<<hi<<" "<<orderstep<<std::endl;
|
||||
Chebyshev<FineField> Cheb(lo2,hi,orderstep);
|
||||
Cheb(hermop,subspace[n-1],Mn);
|
||||
|
||||
for(int m=0;m<n;m++){
|
||||
ComplexD c = innerProduct(subspace[m],Mn);
|
||||
Mn = Mn - c*subspace[m];
|
||||
}
|
||||
|
||||
// normalise
|
||||
scale = std::pow(norm2(Mn),-0.5);
|
||||
Mn=Mn*scale;
|
||||
|
||||
subspace[n]=Mn;
|
||||
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<n<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
std::cout<<GridLogMessage << "filt ["<<n<<"] <n|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
double lo,
|
||||
int orderfilter
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : ord "<<orderfilter<<" ["<<lo<<","<<hi<<"]"<<std::endl;
|
||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : nbasis "<<nn<<std::endl;
|
||||
|
||||
|
||||
for(int b =0;b<nbasis;b++)
|
||||
{
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn);
|
||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
// Filter
|
||||
Chebyshev<FineField> Cheb(lo,hi,orderfilter);
|
||||
Cheb(hermop,noise,Mn);
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
|
||||
// Refine
|
||||
Chebyshev<FineField> PowerLaw(lo,hi,1000,AggregatePowerLaw);
|
||||
noise = Mn;
|
||||
PowerLaw(hermop,noise,Mn);
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
|
||||
// normalise
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void CreateSubspaceChebyshevPowerLaw(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
int nn,
|
||||
double hi,
|
||||
int orderfilter
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : ord "<<orderfilter<<" [0,"<<hi<<"]"<<std::endl;
|
||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : nbasis "<<nn<<std::endl;
|
||||
|
||||
for(int b =0;b<nbasis;b++)
|
||||
{
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn);
|
||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
// Filter
|
||||
Chebyshev<FineField> Cheb(0.0,hi,orderfilter,AggregatePowerLaw);
|
||||
Cheb(hermop,noise,Mn);
|
||||
// normalise
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
virtual void CreateSubspaceChebyshevNew(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
double hi
|
||||
) {
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
for(int b =0;b<nbasis;b++)
|
||||
{
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn);
|
||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
// Filter
|
||||
//#opt2(x) = acheb(x,3,90,300)* acheb(x,1,90,50) * acheb(x,0.5,90,200) * acheb(x,0.05,90,400) * acheb(x,0.01,90,1500)
|
||||
/*266
|
||||
Chebyshev<FineField> Cheb1(3.0,hi,300);
|
||||
Chebyshev<FineField> Cheb2(1.0,hi,50);
|
||||
Chebyshev<FineField> Cheb3(0.5,hi,300);
|
||||
Chebyshev<FineField> Cheb4(0.05,hi,500);
|
||||
Chebyshev<FineField> Cheb5(0.01,hi,2000);
|
||||
*/
|
||||
/* 242 */
|
||||
/*
|
||||
Chebyshev<FineField> Cheb3(0.1,hi,300);
|
||||
Chebyshev<FineField> Cheb2(0.02,hi,1000);
|
||||
Chebyshev<FineField> Cheb1(0.003,hi,2000);
|
||||
8?
|
||||
*/
|
||||
/* How many??
|
||||
*/
|
||||
Chebyshev<FineField> Cheb2(0.001,hi,2500); // 169 iters on HDCG after refine
|
||||
Chebyshev<FineField> Cheb1(0.02,hi,600);
|
||||
|
||||
// Chebyshev<FineField> Cheb2(0.001,hi,1500);
|
||||
// Chebyshev<FineField> Cheb1(0.02,hi,600);
|
||||
Cheb1(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
||||
hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb1 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
Cheb2(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
||||
hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb2 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
// Cheb3(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
||||
// hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb3 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
// Cheb4(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
||||
// hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb4 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
// Cheb5(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
||||
// hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb5 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
subspace[b] = noise;
|
||||
hermop.Op(subspace[b],tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<< " norm " << norm2(noise)<<std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void CreateSubspaceMultishift(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
||||
double Lo,double tol,int maxit)
|
||||
{
|
||||
|
||||
RealD scale;
|
||||
|
||||
FineField noise(FineGrid);
|
||||
FineField Mn(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
|
||||
// New normalised noise
|
||||
std::cout << GridLogMessage<<" Multishift subspace : Lo "<<Lo<<std::endl;
|
||||
|
||||
// Filter
|
||||
// [ 1/6(x+Lo) - 1/2(x+2Lo) + 1/2(x+3Lo) -1/6(x+4Lo) = Lo^3 /[ (x+1Lo)(x+2Lo)(x+3Lo)(x+4Lo) ]
|
||||
//
|
||||
// 1/(x+Lo) - 1/(x+2 Lo)
|
||||
double epsilon = Lo/3;
|
||||
std::vector<RealD> alpha({1.0/6.0,-1.0/2.0,1.0/2.0,-1.0/6.0});
|
||||
std::vector<RealD> shifts({Lo,Lo+epsilon,Lo+2*epsilon,Lo+3*epsilon});
|
||||
std::vector<RealD> tols({tol,tol,tol,tol});
|
||||
std::cout << "sizes "<<alpha.size()<<" "<<shifts.size()<<" "<<tols.size()<<std::endl;
|
||||
|
||||
MultiShiftFunction msf(4,0.0,95.0);
|
||||
std::cout << "msf constructed "<<std::endl;
|
||||
msf.poles=shifts;
|
||||
msf.residues=alpha;
|
||||
msf.tolerances=tols;
|
||||
msf.norm=0.0;
|
||||
msf.order=alpha.size();
|
||||
ConjugateGradientMultiShift<FineField> MSCG(maxit,msf);
|
||||
|
||||
for(int b =0;b<nbasis;b++)
|
||||
{
|
||||
gaussian(RNG,noise);
|
||||
scale = std::pow(norm2(noise),-0.5);
|
||||
noise=noise*scale;
|
||||
|
||||
// Initial matrix element
|
||||
hermop.Op(noise,Mn);
|
||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||
|
||||
MSCG(hermop,noise,Mn);
|
||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
||||
subspace[b] = Mn;
|
||||
hermop.Op(Mn,tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
virtual void RefineSubspace(LinearOperatorBase<FineField> &hermop,
|
||||
double Lo,double tol,int maxit)
|
||||
{
|
||||
FineField tmp(FineGrid);
|
||||
for(int b =0;b<nbasis;b++)
|
||||
{
|
||||
ConjugateGradient<FineField> CGsloppy(tol,maxit,false);
|
||||
ShiftedHermOpLinearOperator<FineField> ShiftedFineHermOp(hermop,Lo);
|
||||
tmp=Zero();
|
||||
CGsloppy(hermop,subspace[b],tmp);
|
||||
RealD scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
||||
subspace[b]=tmp;
|
||||
hermop.Op(subspace[b],tmp);
|
||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
}
|
||||
}
|
||||
virtual void RefineSubspaceHDCG(LinearOperatorBase<FineField> &hermop,
|
||||
TwoLevelADEF2mrhs<FineField,CoarseVector> & theHDCG,
|
||||
int nrhs)
|
||||
{
|
||||
std::vector<FineField> src_mrhs(nrhs,FineGrid);
|
||||
std::vector<FineField> res_mrhs(nrhs,FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
for(int b =0;b<nbasis;b+=nrhs)
|
||||
{
|
||||
tmp = subspace[b];
|
||||
RealD scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
||||
subspace[b] =tmp;
|
||||
hermop.Op(subspace[b],tmp);
|
||||
std::cout<<GridLogMessage << "before filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
|
||||
for(int r=0;r<MIN(nbasis-b,nrhs);r++){
|
||||
src_mrhs[r] = subspace[b+r];
|
||||
}
|
||||
for(int r=0;r<nrhs;r++){
|
||||
res_mrhs[r] = Zero();
|
||||
}
|
||||
theHDCG(src_mrhs,res_mrhs);
|
||||
|
||||
for(int r=0;r<MIN(nbasis-b,nrhs);r++){
|
||||
tmp = res_mrhs[r];
|
||||
RealD scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
||||
subspace[b+r]=tmp;
|
||||
}
|
||||
hermop.Op(subspace[b],tmp);
|
||||
std::cout<<GridLogMessage << "after filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -0,0 +1,837 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/CoarsenedMatrix.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_ALGORITHM_COARSENED_MATRIX_H
|
||||
#define GRID_ALGORITHM_COARSENED_MATRIX_H
|
||||
|
||||
#include <Grid/qcd/QCD.h> // needed for Dagger(Yes|No), Inverse(Yes|No)
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class vobj,class CComplex>
|
||||
inline void blockMaskedInnerProduct(Lattice<CComplex> &CoarseInner,
|
||||
const Lattice<decltype(innerProduct(vobj(),vobj()))> &FineMask,
|
||||
const Lattice<vobj> &fineX,
|
||||
const Lattice<vobj> &fineY)
|
||||
{
|
||||
typedef decltype(innerProduct(vobj(),vobj())) dotp;
|
||||
|
||||
GridBase *coarse(CoarseInner.Grid());
|
||||
GridBase *fine (fineX.Grid());
|
||||
|
||||
Lattice<dotp> fine_inner(fine); fine_inner.Checkerboard() = fineX.Checkerboard();
|
||||
Lattice<dotp> fine_inner_msk(fine);
|
||||
|
||||
// Multiply could be fused with innerProduct
|
||||
// Single block sum kernel could do both masks.
|
||||
fine_inner = localInnerProduct(fineX,fineY);
|
||||
mult(fine_inner_msk, fine_inner,FineMask);
|
||||
blockSum(CoarseInner,fine_inner_msk);
|
||||
}
|
||||
|
||||
// Fine Object == (per site) type of fine field
|
||||
// nbasis == number of deflation vectors
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class CoarsenedMatrix : public CheckerBoardedSparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
||||
public:
|
||||
|
||||
typedef iVector<CComplex,nbasis > siteVector;
|
||||
typedef Lattice<CComplex > CoarseComplexField;
|
||||
typedef Lattice<siteVector> CoarseVector;
|
||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||
typedef Lattice<Fobj > FineField;
|
||||
typedef CoarseVector FermionField;
|
||||
|
||||
// enrich interface, use default implementation as in FermionOperator ///////
|
||||
void Dminus(CoarseVector const& in, CoarseVector& out) { out = in; }
|
||||
void DminusDag(CoarseVector const& in, CoarseVector& out) { out = in; }
|
||||
void ImportPhysicalFermionSource(CoarseVector const& input, CoarseVector& imported) { imported = input; }
|
||||
void ImportUnphysicalFermion(CoarseVector const& input, CoarseVector& imported) { imported = input; }
|
||||
void ExportPhysicalFermionSolution(CoarseVector const& solution, CoarseVector& exported) { exported = solution; };
|
||||
void ExportPhysicalFermionSource(CoarseVector const& solution, CoarseVector& exported) { exported = solution; };
|
||||
|
||||
////////////////////
|
||||
// Data members
|
||||
////////////////////
|
||||
Geometry geom;
|
||||
GridBase * _grid;
|
||||
GridBase* _cbgrid;
|
||||
int hermitian;
|
||||
|
||||
CartesianStencil<siteVector,siteVector,DefaultImplParams> Stencil;
|
||||
CartesianStencil<siteVector,siteVector,DefaultImplParams> StencilEven;
|
||||
CartesianStencil<siteVector,siteVector,DefaultImplParams> StencilOdd;
|
||||
|
||||
std::vector<CoarseMatrix> A;
|
||||
std::vector<CoarseMatrix> Aeven;
|
||||
std::vector<CoarseMatrix> Aodd;
|
||||
|
||||
CoarseMatrix AselfInv;
|
||||
CoarseMatrix AselfInvEven;
|
||||
CoarseMatrix AselfInvOdd;
|
||||
|
||||
deviceVector<RealD> dag_factor;
|
||||
|
||||
///////////////////////
|
||||
// Interface
|
||||
///////////////////////
|
||||
GridBase * Grid(void) { return _grid; }; // this is all the linalg routines need to know
|
||||
GridBase * RedBlackGrid() { return _cbgrid; };
|
||||
|
||||
int ConstEE() { return 0; }
|
||||
|
||||
void M (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
conformable(_grid,in.Grid());
|
||||
conformable(in.Grid(),out.Grid());
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
|
||||
SimpleCompressor<siteVector> compressor;
|
||||
|
||||
Stencil.HaloExchange(in,compressor);
|
||||
autoView( in_v , in, AcceleratorRead);
|
||||
autoView( out_v , out, AcceleratorWrite);
|
||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
||||
int npoint = geom.npoint;
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
|
||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++) {
|
||||
hAcceleratorViewContainer[p] = A[p].View(AcceleratorRead);
|
||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
||||
}
|
||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
int osites=Grid()->oSites();
|
||||
|
||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
for(int point=0;point<npoint;point++){
|
||||
|
||||
SE=Stencil_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
|
||||
for(int p=0;p<geom.npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
||||
};
|
||||
|
||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
if(hermitian) {
|
||||
// corresponds to Petrov-Galerkin coarsening
|
||||
return M(in,out);
|
||||
} else {
|
||||
// corresponds to Galerkin coarsening
|
||||
return MdagNonHermitian(in, out);
|
||||
}
|
||||
};
|
||||
|
||||
void MdagNonHermitian(const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
conformable(_grid,in.Grid());
|
||||
conformable(in.Grid(),out.Grid());
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
|
||||
SimpleCompressor<siteVector> compressor;
|
||||
|
||||
Stencil.HaloExchange(in,compressor);
|
||||
autoView( in_v , in, AcceleratorRead);
|
||||
autoView( out_v , out, AcceleratorWrite);
|
||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
||||
int npoint = geom.npoint;
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
|
||||
|
||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++) {
|
||||
hAcceleratorViewContainer[p] = A[p].View(AcceleratorRead);
|
||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
||||
}
|
||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
int osites=Grid()->oSites();
|
||||
|
||||
deviceVector<int> points(geom.npoint);
|
||||
for(int p=0; p<geom.npoint; p++) {
|
||||
acceleratorPut(points[p],geom.points_dagger[p]);
|
||||
}
|
||||
auto points_p = &points[0];
|
||||
|
||||
RealD* dag_factor_p = &dag_factor[0];
|
||||
|
||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
for(int p=0;p<npoint;p++){
|
||||
int point = points_p[p];
|
||||
|
||||
SE=Stencil_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + dag_factor_p[b*nbasis+bb]*coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
|
||||
for(int p=0;p<geom.npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
||||
}
|
||||
|
||||
void MdirComms(const CoarseVector &in)
|
||||
{
|
||||
SimpleCompressor<siteVector> compressor;
|
||||
Stencil.HaloExchange(in,compressor);
|
||||
}
|
||||
void MdirCalc(const CoarseVector &in, CoarseVector &out, int point)
|
||||
{
|
||||
conformable(_grid,in.Grid());
|
||||
conformable(_grid,out.Grid());
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
|
||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++) {
|
||||
hAcceleratorViewContainer[p] = A[p].View(AcceleratorRead);
|
||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
||||
}
|
||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
||||
|
||||
autoView( out_v , out, AcceleratorWrite);
|
||||
autoView( in_v , in, AcceleratorRead);
|
||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
SE=Stencil_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
for(int p=0;p<geom.npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
||||
}
|
||||
void MdirAll(const CoarseVector &in,std::vector<CoarseVector> &out)
|
||||
{
|
||||
this->MdirComms(in);
|
||||
int ndir=geom.npoint-1;
|
||||
if ((out.size()!=ndir)&&(out.size()!=ndir+1)) {
|
||||
std::cout <<"MdirAll out size "<< out.size()<<std::endl;
|
||||
std::cout <<"MdirAll ndir "<< ndir<<std::endl;
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
for(int p=0;p<ndir;p++){
|
||||
MdirCalc(in,out[p],p);
|
||||
}
|
||||
};
|
||||
void Mdir(const CoarseVector &in, CoarseVector &out, int dir, int disp){
|
||||
|
||||
this->MdirComms(in);
|
||||
|
||||
MdirCalc(in,out,geom.point(dir,disp));
|
||||
};
|
||||
|
||||
void Mdiag(const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
int point=geom.npoint-1;
|
||||
MdirCalc(in, out, point); // No comms
|
||||
};
|
||||
|
||||
void Mooee(const CoarseVector &in, CoarseVector &out) {
|
||||
MooeeInternal(in, out, DaggerNo, InverseNo);
|
||||
}
|
||||
|
||||
void MooeeInv(const CoarseVector &in, CoarseVector &out) {
|
||||
MooeeInternal(in, out, DaggerNo, InverseYes);
|
||||
}
|
||||
|
||||
void MooeeDag(const CoarseVector &in, CoarseVector &out) {
|
||||
MooeeInternal(in, out, DaggerYes, InverseNo);
|
||||
}
|
||||
|
||||
void MooeeInvDag(const CoarseVector &in, CoarseVector &out) {
|
||||
MooeeInternal(in, out, DaggerYes, InverseYes);
|
||||
}
|
||||
|
||||
void Meooe(const CoarseVector &in, CoarseVector &out) {
|
||||
if(in.Checkerboard() == Odd) {
|
||||
DhopEO(in, out, DaggerNo);
|
||||
} else {
|
||||
DhopOE(in, out, DaggerNo);
|
||||
}
|
||||
}
|
||||
|
||||
void MeooeDag(const CoarseVector &in, CoarseVector &out) {
|
||||
if(in.Checkerboard() == Odd) {
|
||||
DhopEO(in, out, DaggerYes);
|
||||
} else {
|
||||
DhopOE(in, out, DaggerYes);
|
||||
}
|
||||
}
|
||||
|
||||
void Dhop(const CoarseVector &in, CoarseVector &out, int dag) {
|
||||
conformable(in.Grid(), _grid); // verifies full grid
|
||||
conformable(in.Grid(), out.Grid());
|
||||
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
|
||||
DhopInternal(Stencil, A, in, out, dag);
|
||||
}
|
||||
|
||||
void DhopOE(const CoarseVector &in, CoarseVector &out, int dag) {
|
||||
conformable(in.Grid(), _cbgrid); // verifies half grid
|
||||
conformable(in.Grid(), out.Grid()); // drops the cb check
|
||||
|
||||
GRID_ASSERT(in.Checkerboard() == Even);
|
||||
out.Checkerboard() = Odd;
|
||||
|
||||
DhopInternal(StencilEven, Aodd, in, out, dag);
|
||||
}
|
||||
|
||||
void DhopEO(const CoarseVector &in, CoarseVector &out, int dag) {
|
||||
conformable(in.Grid(), _cbgrid); // verifies half grid
|
||||
conformable(in.Grid(), out.Grid()); // drops the cb check
|
||||
|
||||
GRID_ASSERT(in.Checkerboard() == Odd);
|
||||
out.Checkerboard() = Even;
|
||||
|
||||
DhopInternal(StencilOdd, Aeven, in, out, dag);
|
||||
}
|
||||
|
||||
void MooeeInternal(const CoarseVector &in, CoarseVector &out, int dag, int inv) {
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
GRID_ASSERT(in.Checkerboard() == Odd || in.Checkerboard() == Even);
|
||||
|
||||
CoarseMatrix *Aself = nullptr;
|
||||
if(in.Grid()->_isCheckerBoarded) {
|
||||
if(in.Checkerboard() == Odd) {
|
||||
Aself = (inv) ? &AselfInvOdd : &Aodd[geom.npoint-1];
|
||||
DselfInternal(StencilOdd, *Aself, in, out, dag);
|
||||
} else {
|
||||
Aself = (inv) ? &AselfInvEven : &Aeven[geom.npoint-1];
|
||||
DselfInternal(StencilEven, *Aself, in, out, dag);
|
||||
}
|
||||
} else {
|
||||
Aself = (inv) ? &AselfInv : &A[geom.npoint-1];
|
||||
DselfInternal(Stencil, *Aself, in, out, dag);
|
||||
}
|
||||
GRID_ASSERT(Aself != nullptr);
|
||||
}
|
||||
|
||||
void DselfInternal(CartesianStencil<siteVector,siteVector,DefaultImplParams> &st, CoarseMatrix &a,
|
||||
const CoarseVector &in, CoarseVector &out, int dag) {
|
||||
int point = geom.npoint-1;
|
||||
autoView( out_v, out, AcceleratorWrite);
|
||||
autoView( in_v, in, AcceleratorRead);
|
||||
autoView( st_v, st, AcceleratorRead);
|
||||
autoView( a_v, a, AcceleratorRead);
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
RealD* dag_factor_p = &dag_factor[0];
|
||||
|
||||
if(dag) {
|
||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
SE=st_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + dag_factor_p[b*nbasis+bb]*coalescedRead(a_v[ss](b,bb))*nbr(bb);
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
} else {
|
||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
SE=st_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(a_v[ss](b,bb))*nbr(bb);
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DhopInternal(CartesianStencil<siteVector,siteVector,DefaultImplParams> &st, std::vector<CoarseMatrix> &a,
|
||||
const CoarseVector &in, CoarseVector &out, int dag) {
|
||||
SimpleCompressor<siteVector> compressor;
|
||||
|
||||
st.HaloExchange(in,compressor);
|
||||
autoView( in_v, in, AcceleratorRead);
|
||||
autoView( out_v, out, AcceleratorWrite);
|
||||
autoView( st_v , st, AcceleratorRead);
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
|
||||
// determine in what order we need the points
|
||||
int npoint = geom.npoint-1;
|
||||
deviceVector<int> points(npoint);
|
||||
for(int p=0; p<npoint; p++) {
|
||||
int val = (dag && !hermitian) ? geom.points_dagger[p] : p;
|
||||
acceleratorPut(points[p], val);
|
||||
}
|
||||
auto points_p = &points[0];
|
||||
|
||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++) {
|
||||
hAcceleratorViewContainer[p] = a[p].View(AcceleratorRead);
|
||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
||||
}
|
||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
|
||||
RealD* dag_factor_p = &dag_factor[0];
|
||||
|
||||
if(dag) {
|
||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
for(int p=0;p<npoint;p++){
|
||||
int point = points_p[p];
|
||||
SE=st_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + dag_factor_p[b*nbasis+bb]*coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
} else {
|
||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
||||
int ss = sss/nbasis;
|
||||
int b = sss%nbasis;
|
||||
calcComplex res = Zero();
|
||||
calcVector nbr;
|
||||
int ptype;
|
||||
StencilEntry *SE;
|
||||
|
||||
for(int p=0;p<npoint;p++){
|
||||
int point = points_p[p];
|
||||
SE=st_v.GetEntry(ptype,point,ss);
|
||||
|
||||
if(SE->_is_local) {
|
||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
||||
} else {
|
||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
||||
}
|
||||
acceleratorSynchronise();
|
||||
|
||||
for(int bb=0;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
||||
}
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
}
|
||||
|
||||
for(int p=0;p<npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
||||
}
|
||||
|
||||
CoarsenedMatrix(GridCartesian &CoarseGrid, int hermitian_=0) :
|
||||
_grid(&CoarseGrid),
|
||||
_cbgrid(new GridRedBlackCartesian(&CoarseGrid)),
|
||||
geom(CoarseGrid._ndimension),
|
||||
hermitian(hermitian_),
|
||||
Stencil(&CoarseGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
||||
StencilEven(_cbgrid,geom.npoint,Even,geom.directions,geom.displacements),
|
||||
StencilOdd(_cbgrid,geom.npoint,Odd,geom.directions,geom.displacements),
|
||||
A(geom.npoint,&CoarseGrid),
|
||||
Aeven(geom.npoint,_cbgrid),
|
||||
Aodd(geom.npoint,_cbgrid),
|
||||
AselfInv(&CoarseGrid),
|
||||
AselfInvEven(_cbgrid),
|
||||
AselfInvOdd(_cbgrid),
|
||||
dag_factor(nbasis*nbasis)
|
||||
{
|
||||
fillFactor();
|
||||
};
|
||||
|
||||
CoarsenedMatrix(GridCartesian &CoarseGrid, GridRedBlackCartesian &CoarseRBGrid, int hermitian_=0) :
|
||||
|
||||
_grid(&CoarseGrid),
|
||||
_cbgrid(&CoarseRBGrid),
|
||||
geom(CoarseGrid._ndimension),
|
||||
hermitian(hermitian_),
|
||||
Stencil(&CoarseGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
||||
StencilEven(&CoarseRBGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
||||
StencilOdd(&CoarseRBGrid,geom.npoint,Odd,geom.directions,geom.displacements),
|
||||
A(geom.npoint,&CoarseGrid),
|
||||
Aeven(geom.npoint,&CoarseRBGrid),
|
||||
Aodd(geom.npoint,&CoarseRBGrid),
|
||||
AselfInv(&CoarseGrid),
|
||||
AselfInvEven(&CoarseRBGrid),
|
||||
AselfInvOdd(&CoarseRBGrid),
|
||||
dag_factor(nbasis*nbasis)
|
||||
{
|
||||
fillFactor();
|
||||
};
|
||||
|
||||
void fillFactor() {
|
||||
Eigen::MatrixXd dag_factor_eigen = Eigen::MatrixXd::Ones(nbasis, nbasis);
|
||||
if(!hermitian) {
|
||||
const int nb = nbasis/2;
|
||||
dag_factor_eigen.block(0,nb,nb,nb) *= -1.0;
|
||||
dag_factor_eigen.block(nb,0,nb,nb) *= -1.0;
|
||||
}
|
||||
|
||||
// GPU readable prefactor
|
||||
std::vector<RealD> h_dag_factor(nbasis*nbasis);
|
||||
thread_for(i, nbasis*nbasis, {
|
||||
int j = i/nbasis;
|
||||
int k = i%nbasis;
|
||||
h_dag_factor[i] = dag_factor_eigen(j, k);
|
||||
});
|
||||
acceleratorCopyToDevice(&h_dag_factor[0],&dag_factor[0],dag_factor.size()*sizeof(RealD));
|
||||
}
|
||||
|
||||
void CoarsenOperator(GridBase *FineGrid,LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
||||
{
|
||||
typedef Lattice<typename Fobj::tensor_reduced> FineComplexField;
|
||||
typedef typename Fobj::scalar_type scalar_type;
|
||||
|
||||
std::cout << GridLogMessage<< "CoarsenMatrix "<< std::endl;
|
||||
|
||||
FineComplexField one(FineGrid); one=scalar_type(1.0,0.0);
|
||||
FineComplexField zero(FineGrid); zero=scalar_type(0.0,0.0);
|
||||
|
||||
std::vector<FineComplexField> masks(geom.npoint,FineGrid);
|
||||
FineComplexField imask(FineGrid); // contributions from within this block
|
||||
FineComplexField omask(FineGrid); // contributions from outwith this block
|
||||
|
||||
FineComplexField evenmask(FineGrid);
|
||||
FineComplexField oddmask(FineGrid);
|
||||
|
||||
FineField phi(FineGrid);
|
||||
FineField tmp(FineGrid);
|
||||
FineField zz(FineGrid); zz=Zero();
|
||||
FineField Mphi(FineGrid);
|
||||
FineField Mphie(FineGrid);
|
||||
FineField Mphio(FineGrid);
|
||||
std::vector<FineField> Mphi_p(geom.npoint,FineGrid);
|
||||
|
||||
Lattice<iScalar<vInteger> > coor (FineGrid);
|
||||
Lattice<iScalar<vInteger> > bcoor(FineGrid);
|
||||
Lattice<iScalar<vInteger> > bcb (FineGrid); bcb = Zero();
|
||||
|
||||
CoarseVector iProj(Grid());
|
||||
CoarseVector oProj(Grid());
|
||||
CoarseVector SelfProj(Grid());
|
||||
CoarseComplexField iZProj(Grid());
|
||||
CoarseComplexField oZProj(Grid());
|
||||
|
||||
CoarseScalar InnerProd(Grid());
|
||||
|
||||
std::cout << GridLogMessage<< "CoarsenMatrix Orthog "<< std::endl;
|
||||
// Orthogonalise the subblocks over the basis
|
||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
||||
|
||||
// Compute the matrix elements of linop between this orthonormal
|
||||
// set of vectors.
|
||||
std::cout << GridLogMessage<< "CoarsenMatrix masks "<< std::endl;
|
||||
int self_stencil=-1;
|
||||
for(int p=0;p<geom.npoint;p++)
|
||||
{
|
||||
int dir = geom.directions[p];
|
||||
int disp = geom.displacements[p];
|
||||
A[p]=Zero();
|
||||
if( geom.displacements[p]==0){
|
||||
self_stencil=p;
|
||||
}
|
||||
|
||||
Integer block=(FineGrid->_rdimensions[dir])/(Grid()->_rdimensions[dir]);
|
||||
|
||||
LatticeCoordinate(coor,dir);
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Work out even and odd block checkerboarding for fast diagonal term
|
||||
///////////////////////////////////////////////////////
|
||||
if ( disp==1 ) {
|
||||
bcb = bcb + div(coor,block);
|
||||
}
|
||||
|
||||
if ( disp==0 ) {
|
||||
masks[p]= Zero();
|
||||
} else if ( disp==1 ) {
|
||||
masks[p] = where(mod(coor,block)==(block-1),one,zero);
|
||||
} else if ( disp==-1 ) {
|
||||
masks[p] = where(mod(coor,block)==(Integer)0,one,zero);
|
||||
}
|
||||
}
|
||||
evenmask = where(mod(bcb,2)==(Integer)0,one,zero);
|
||||
oddmask = one-evenmask;
|
||||
|
||||
GRID_ASSERT(self_stencil!=-1);
|
||||
|
||||
for(int i=0;i<nbasis;i++){
|
||||
|
||||
phi=Subspace.subspace[i];
|
||||
|
||||
std::cout << GridLogMessage<< "CoarsenMatrix vector "<<i << std::endl;
|
||||
linop.OpDirAll(phi,Mphi_p);
|
||||
linop.OpDiag (phi,Mphi_p[geom.npoint-1]);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
|
||||
Mphi = Mphi_p[p];
|
||||
|
||||
int dir = geom.directions[p];
|
||||
int disp = geom.displacements[p];
|
||||
|
||||
if ( (disp==-1) || (!hermitian ) ) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Pick out contributions coming from this cell and neighbour cell
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
omask = masks[p];
|
||||
imask = one-omask;
|
||||
|
||||
for(int j=0;j<nbasis;j++){
|
||||
|
||||
blockMaskedInnerProduct(oZProj,omask,Subspace.subspace[j],Mphi);
|
||||
|
||||
autoView( iZProj_v , iZProj, AcceleratorRead) ;
|
||||
autoView( oZProj_v , oZProj, AcceleratorRead) ;
|
||||
autoView( A_p , A[p], AcceleratorWrite);
|
||||
autoView( A_self , A[self_stencil], AcceleratorWrite);
|
||||
|
||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{ coalescedWrite(A_p[ss](j,i),oZProj_v(ss)); });
|
||||
if ( hermitian && (disp==-1) ) {
|
||||
for(int pp=0;pp<geom.npoint;pp++){// Find the opposite link and set <j|A|i> = <i|A|j>*
|
||||
int dirp = geom.directions[pp];
|
||||
int dispp = geom.displacements[pp];
|
||||
if ( (dirp==dir) && (dispp==1) ){
|
||||
auto sft = conjugate(Cshift(oZProj,dir,1));
|
||||
autoView( sft_v , sft , AcceleratorWrite);
|
||||
autoView( A_pp , A[pp], AcceleratorWrite);
|
||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{ coalescedWrite(A_pp[ss](i,j),sft_v(ss)); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Faster alternate self coupling.. use hermiticity to save 2x
|
||||
///////////////////////////////////////////
|
||||
{
|
||||
mult(tmp,phi,evenmask); linop.Op(tmp,Mphie);
|
||||
mult(tmp,phi,oddmask ); linop.Op(tmp,Mphio);
|
||||
|
||||
{
|
||||
autoView( tmp_ , tmp, AcceleratorWrite);
|
||||
autoView( evenmask_ , evenmask, AcceleratorRead);
|
||||
autoView( oddmask_ , oddmask, AcceleratorRead);
|
||||
autoView( Mphie_ , Mphie, AcceleratorRead);
|
||||
autoView( Mphio_ , Mphio, AcceleratorRead);
|
||||
accelerator_for(ss, FineGrid->oSites(), Fobj::Nsimd(),{
|
||||
coalescedWrite(tmp_[ss],evenmask_(ss)*Mphie_(ss) + oddmask_(ss)*Mphio_(ss));
|
||||
});
|
||||
}
|
||||
|
||||
blockProject(SelfProj,tmp,Subspace.subspace);
|
||||
|
||||
autoView( SelfProj_ , SelfProj, AcceleratorRead);
|
||||
autoView( A_self , A[self_stencil], AcceleratorWrite);
|
||||
|
||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{
|
||||
for(int j=0;j<nbasis;j++){
|
||||
coalescedWrite(A_self[ss](j,i), SelfProj_(ss)(j));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
if(hermitian) {
|
||||
std::cout << GridLogMessage << " ForceHermitian, new code "<<std::endl;
|
||||
}
|
||||
|
||||
InvertSelfStencilLink(); std::cout << GridLogMessage << "Coarse self link inverted" << std::endl;
|
||||
FillHalfCbs(); std::cout << GridLogMessage << "Coarse half checkerboards filled" << std::endl;
|
||||
}
|
||||
|
||||
void InvertSelfStencilLink() {
|
||||
std::cout << GridLogDebug << "CoarsenedMatrix::InvertSelfStencilLink" << std::endl;
|
||||
int localVolume = Grid()->lSites();
|
||||
|
||||
typedef typename Cobj::scalar_object scalar_object;
|
||||
|
||||
autoView(Aself_v, A[geom.npoint-1], CpuRead);
|
||||
autoView(AselfInv_v, AselfInv, CpuWrite);
|
||||
thread_for(site, localVolume, { // NOTE: Not able to bring this to GPU because of Eigen + peek/poke
|
||||
Eigen::MatrixXcd selfLinkEigen = Eigen::MatrixXcd::Zero(nbasis, nbasis);
|
||||
Eigen::MatrixXcd selfLinkInvEigen = Eigen::MatrixXcd::Zero(nbasis, nbasis);
|
||||
|
||||
scalar_object selfLink = Zero();
|
||||
scalar_object selfLinkInv = Zero();
|
||||
|
||||
Coordinate lcoor;
|
||||
|
||||
Grid()->LocalIndexToLocalCoor(site, lcoor);
|
||||
peekLocalSite(selfLink, Aself_v, lcoor);
|
||||
|
||||
for (int i = 0; i < nbasis; ++i)
|
||||
for (int j = 0; j < nbasis; ++j)
|
||||
selfLinkEigen(i, j) = static_cast<ComplexD>(TensorRemove(selfLink(i, j)));
|
||||
|
||||
selfLinkInvEigen = selfLinkEigen.inverse();
|
||||
|
||||
for(int i = 0; i < nbasis; ++i)
|
||||
for(int j = 0; j < nbasis; ++j)
|
||||
selfLinkInv(i, j) = selfLinkInvEigen(i, j);
|
||||
|
||||
pokeLocalSite(selfLinkInv, AselfInv_v, lcoor);
|
||||
});
|
||||
}
|
||||
|
||||
void FillHalfCbs() {
|
||||
std::cout << GridLogDebug << "CoarsenedMatrix::FillHalfCbs" << std::endl;
|
||||
for(int p = 0; p < geom.npoint; ++p) {
|
||||
pickCheckerboard(Even, Aeven[p], A[p]);
|
||||
pickCheckerboard(Odd, Aodd[p], A[p]);
|
||||
}
|
||||
pickCheckerboard(Even, AselfInvEven, AselfInv);
|
||||
pickCheckerboard(Odd, AselfInvOdd, AselfInv);
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -0,0 +1,629 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrix.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
#include <Grid/qcd/QCD.h> // needed for Dagger(Yes|No), Inverse(Yes|No)
|
||||
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
// Fine Object == (per site) type of fine field
|
||||
// nbasis == number of deflation vectors
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class GeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
||||
public:
|
||||
|
||||
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
||||
typedef iVector<CComplex,nbasis > siteVector;
|
||||
typedef iMatrix<CComplex,nbasis > siteMatrix;
|
||||
typedef Lattice<iScalar<CComplex> > CoarseComplexField;
|
||||
typedef Lattice<siteVector> CoarseVector;
|
||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
||||
typedef iVector<CComplex,nbasis > Cvec;
|
||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||
typedef Lattice<Fobj > FineField;
|
||||
typedef Lattice<CComplex > FineComplexField;
|
||||
typedef CoarseVector Field;
|
||||
////////////////////
|
||||
// Data members
|
||||
////////////////////
|
||||
int hermitian;
|
||||
GridBase * _FineGrid;
|
||||
GridCartesian * _CoarseGrid;
|
||||
NonLocalStencilGeometry &geom;
|
||||
PaddedCell Cell;
|
||||
GeneralLocalStencil Stencil;
|
||||
|
||||
std::vector<CoarseMatrix> _A;
|
||||
std::vector<CoarseMatrix> _Adag;
|
||||
std::vector<CoarseVector> MultTemporaries;
|
||||
|
||||
///////////////////////
|
||||
// Interface
|
||||
///////////////////////
|
||||
GridBase * Grid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
|
||||
GridBase * FineGrid(void) { return _FineGrid; }; // this is all the linalg routines need to know
|
||||
GridCartesian * CoarseGrid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
|
||||
|
||||
/* void ShiftMatrix(RealD shift)
|
||||
{
|
||||
int Nd=_FineGrid->Nd();
|
||||
Coordinate zero_shift(Nd,0);
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
if ( zero_shift==geom.shifts[p] ) {
|
||||
_A[p] = _A[p]+shift;
|
||||
// _Adag[p] = _Adag[p]+shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
void ProjectNearestNeighbour(RealD shift, GeneralCoarseOp &CopyMe)
|
||||
{
|
||||
int nfound=0;
|
||||
std::cout << GridLogMessage <<"GeneralCoarsenedMatrix::ProjectNearestNeighbour "<< CopyMe._A[0].Grid()<<std::endl;
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
for(int pp=0;pp<CopyMe.geom.npoint;pp++){
|
||||
// Search for the same relative shift
|
||||
// Avoids brutal handling of Grid pointers
|
||||
if ( CopyMe.geom.shifts[pp]==geom.shifts[p] ) {
|
||||
_A[p] = CopyMe.Cell.Extract(CopyMe._A[pp]);
|
||||
// _Adag[p] = CopyMe.Cell.Extract(CopyMe._Adag[pp]);
|
||||
nfound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
GRID_ASSERT(nfound==geom.npoint);
|
||||
ExchangeCoarseLinks();
|
||||
}
|
||||
*/
|
||||
|
||||
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid)
|
||||
: geom(_geom),
|
||||
_FineGrid(FineGrid),
|
||||
_CoarseGrid(CoarseGrid),
|
||||
hermitian(1),
|
||||
Cell(_geom.Depth(),_CoarseGrid),
|
||||
Stencil(Cell.grids.back(),geom.shifts)
|
||||
{
|
||||
{
|
||||
int npoint = _geom.npoint;
|
||||
}
|
||||
_A.resize(geom.npoint,CoarseGrid);
|
||||
// _Adag.resize(geom.npoint,CoarseGrid);
|
||||
}
|
||||
void M (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
Mult(_A,in,out);
|
||||
}
|
||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
GRID_ASSERT(hermitian);
|
||||
Mult(_A,in,out);
|
||||
// if ( hermitian ) M(in,out);
|
||||
// else Mult(_Adag,in,out);
|
||||
}
|
||||
void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
RealD tviews=0; RealD ttot=0; RealD tmult=0; RealD texch=0; RealD text=0; RealD ttemps=0; RealD tcopy=0;
|
||||
RealD tmult2=0;
|
||||
|
||||
ttot=-usecond();
|
||||
conformable(CoarseGrid(),in.Grid());
|
||||
conformable(in.Grid(),out.Grid());
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
CoarseVector tin=in;
|
||||
|
||||
texch-=usecond();
|
||||
CoarseVector pin = Cell.ExchangePeriodic(tin);
|
||||
texch+=usecond();
|
||||
|
||||
CoarseVector pout(pin.Grid());
|
||||
|
||||
int npoint = geom.npoint;
|
||||
typedef LatticeView<Cobj> Aview;
|
||||
typedef LatticeView<Cvec> Vview;
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
|
||||
int64_t osites=pin.Grid()->oSites();
|
||||
|
||||
RealD flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
|
||||
RealD bytes = 1.0*osites*sizeof(siteMatrix)*npoint
|
||||
+ 2.0*osites*sizeof(siteVector)*npoint;
|
||||
|
||||
{
|
||||
tviews-=usecond();
|
||||
autoView( in_v , pin, AcceleratorRead);
|
||||
autoView( out_v , pout, AcceleratorWriteDiscard);
|
||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
||||
tviews+=usecond();
|
||||
|
||||
// Static and prereserve to keep UVM region live and not resized across multiple calls
|
||||
ttemps-=usecond();
|
||||
MultTemporaries.resize(npoint,pin.Grid());
|
||||
ttemps+=usecond();
|
||||
std::vector<Aview> AcceleratorViewContainer_h;
|
||||
std::vector<Vview> AcceleratorVecViewContainer_h;
|
||||
|
||||
tviews-=usecond();
|
||||
for(int p=0;p<npoint;p++) {
|
||||
AcceleratorViewContainer_h.push_back( A[p].View(AcceleratorRead));
|
||||
AcceleratorVecViewContainer_h.push_back(MultTemporaries[p].View(AcceleratorWrite));
|
||||
}
|
||||
tviews+=usecond();
|
||||
|
||||
static deviceVector<Aview> AcceleratorViewContainer; AcceleratorViewContainer.resize(npoint);
|
||||
static deviceVector<Vview> AcceleratorVecViewContainer; AcceleratorVecViewContainer.resize(npoint);
|
||||
|
||||
auto Aview_p = &AcceleratorViewContainer[0];
|
||||
auto Vview_p = &AcceleratorVecViewContainer[0];
|
||||
tcopy-=usecond();
|
||||
acceleratorCopyToDevice(&AcceleratorViewContainer_h[0],&AcceleratorViewContainer[0],npoint *sizeof(Aview));
|
||||
acceleratorCopyToDevice(&AcceleratorVecViewContainer_h[0],&AcceleratorVecViewContainer[0],npoint *sizeof(Vview));
|
||||
tcopy+=usecond();
|
||||
|
||||
tmult-=usecond();
|
||||
accelerator_for(spb, osites*nbasis*npoint, Nsimd, {
|
||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
||||
int32_t ss = spb/(nbasis*npoint);
|
||||
int32_t bp = spb%(nbasis*npoint);
|
||||
int32_t point= bp/nbasis;
|
||||
int32_t b = bp%nbasis;
|
||||
auto SE = Stencil_v.GetEntry(point,ss);
|
||||
auto nbr = coalescedReadGeneralPermute(in_v[SE->_offset],SE->_permute,Nd);
|
||||
auto res = coalescedRead(Aview_p[point][ss](0,b))*nbr(0);
|
||||
for(int bb=1;bb<nbasis;bb++) {
|
||||
res = res + coalescedRead(Aview_p[point][ss](bb,b))*nbr(bb);
|
||||
}
|
||||
coalescedWrite(Vview_p[point][ss](b),res);
|
||||
});
|
||||
tmult2-=usecond();
|
||||
accelerator_for(sb, osites*nbasis, Nsimd, {
|
||||
int ss = sb/nbasis;
|
||||
int b = sb%nbasis;
|
||||
auto res = coalescedRead(Vview_p[0][ss](b));
|
||||
for(int point=1;point<npoint;point++){
|
||||
res = res + coalescedRead(Vview_p[point][ss](b));
|
||||
}
|
||||
coalescedWrite(out_v[ss](b),res);
|
||||
});
|
||||
tmult2+=usecond();
|
||||
tmult+=usecond();
|
||||
for(int p=0;p<npoint;p++) {
|
||||
AcceleratorViewContainer_h[p].ViewClose();
|
||||
AcceleratorVecViewContainer_h[p].ViewClose();
|
||||
}
|
||||
}
|
||||
|
||||
text-=usecond();
|
||||
out = Cell.Extract(pout);
|
||||
text+=usecond();
|
||||
ttot+=usecond();
|
||||
|
||||
std::cout << GridLogPerformance<<"Coarse 1rhs Mult Aviews "<<tviews<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Mult exch "<<texch<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Mult mult "<<tmult<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<" of which mult2 "<<tmult2<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Mult ext "<<text<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Mult temps "<<ttemps<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Mult copy "<<tcopy<<" us"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Mult tot "<<ttot<<" us"<<std::endl;
|
||||
// std::cout << GridLogPerformance<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Kernel flops "<< flops<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Kernel flop/s "<< flops/tmult<<" mflop/s"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse Kernel bytes/s "<< bytes/tmult<<" MB/s"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse overall flops/s "<< flops/ttot<<" mflop/s"<<std::endl;
|
||||
std::cout << GridLogPerformance<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
|
||||
|
||||
};
|
||||
|
||||
void PopulateAdag(void)
|
||||
{
|
||||
for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){
|
||||
Coordinate bcoor;
|
||||
CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor);
|
||||
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
Coordinate scoor = bcoor;
|
||||
for(int mu=0;mu<bcoor.size();mu++){
|
||||
int L = CoarseGrid()->GlobalDimensions()[mu];
|
||||
scoor[mu] = (bcoor[mu] - geom.shifts[p][mu] + L) % L; // Modulo arithmetic
|
||||
}
|
||||
// Flip to poke/peekLocalSite and not too bad
|
||||
auto link = peekSite(_A[p],scoor);
|
||||
int pp = geom.Reverse(p);
|
||||
pokeSite(adj(link),_Adag[pp],bcoor);
|
||||
}
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A) Only reduced flops option is to use a padded cell of depth 4
|
||||
// and apply MpcDagMpc in the padded cell.
|
||||
//
|
||||
// Makes for ONE application of MpcDagMpc per vector instead of 30 or 80.
|
||||
// With the effective cell size around (B+8)^4 perhaps 12^4/4^4 ratio
|
||||
// Cost is 81x more, same as stencil size.
|
||||
//
|
||||
// But: can eliminate comms and do as local dirichlet.
|
||||
//
|
||||
// Local exchange gauge field once.
|
||||
// Apply to all vectors, local only computation.
|
||||
// Must exchange ghost subcells in reverse process of PaddedCell to take inner products
|
||||
//
|
||||
// B) Can reduce cost: pad by 1, apply Deo (4^4+6^4+8^4+8^4 )/ (4x 4^4)
|
||||
// pad by 2, apply Doe
|
||||
// pad by 3, apply Deo
|
||||
// then break out 8x directions; cost is ~10x MpcDagMpc per vector
|
||||
//
|
||||
// => almost factor of 10 in setup cost, excluding data rearrangement
|
||||
//
|
||||
// Intermediates -- ignore the corner terms, leave approximate and force Hermitian
|
||||
// Intermediates -- pad by 2 and apply 1+8+24 = 33 times.
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// BFM HDCG style approach: Solve a system of equations to get Aij
|
||||
//////////////////////////////////////////////////////////
|
||||
/*
|
||||
* Here, k,l index which possible shift within the 3^Nd "ball" connected by MdagM.
|
||||
*
|
||||
* conj(phases[block]) proj[k][ block*Nvec+j ] = \sum_ball e^{i q_k . delta} < phi_{block,j} | MdagM | phi_{(block+delta),i} >
|
||||
* = \sum_ball e^{iqk.delta} A_ji
|
||||
*
|
||||
* Must invert matrix M_k,l = e^[i q_k . delta_l]
|
||||
*
|
||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||
*/
|
||||
#if 0
|
||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
||||
{
|
||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrix "<< std::endl;
|
||||
GridBase *grid = FineGrid();
|
||||
|
||||
RealD tproj=0.0;
|
||||
RealD teigen=0.0;
|
||||
RealD tmat=0.0;
|
||||
RealD tphase=0.0;
|
||||
RealD tinv=0.0;
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Orthogonalise the subblocks over the basis
|
||||
/////////////////////////////////////////////////////////////
|
||||
CoarseScalar InnerProd(CoarseGrid());
|
||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
||||
|
||||
const int npoint = geom.npoint;
|
||||
|
||||
Coordinate clatt = CoarseGrid()->GlobalDimensions();
|
||||
int Nd = CoarseGrid()->Nd();
|
||||
|
||||
/*
|
||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
||||
* Matrix index i is mapped to this shift via
|
||||
* geom.shifts[i]
|
||||
*
|
||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
||||
* = M_{kl} A_ji^{b.b+l}
|
||||
*
|
||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
||||
*
|
||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||
*
|
||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
||||
*/
|
||||
teigen-=usecond();
|
||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
ComplexD ci(0.0,1.0);
|
||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
||||
|
||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
||||
ComplexD phase(0.0,0.0);
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
phase=phase+TwoPiL*geom.shifts[k][mu]*geom.shifts[l][mu];
|
||||
}
|
||||
phase=exp(phase*ci);
|
||||
Mkl(k,l) = phase;
|
||||
}
|
||||
}
|
||||
invMkl = Mkl.inverse();
|
||||
teigen+=usecond();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Now compute the matrix elements of linop between the orthonormal
|
||||
// set of vectors.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
FineField phaV(grid); // Phased block basis vector
|
||||
FineField MphaV(grid);// Matrix applied
|
||||
CoarseVector coarseInner(CoarseGrid());
|
||||
|
||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid());
|
||||
std::vector<CoarseVector> FT(npoint,CoarseGrid());
|
||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||
/////////////////////////////////////////////////////
|
||||
// Stick a phase on every block
|
||||
/////////////////////////////////////////////////////
|
||||
tphase-=usecond();
|
||||
CoarseComplexField coor(CoarseGrid());
|
||||
CoarseComplexField pha(CoarseGrid()); pha=Zero();
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
LatticeCoordinate(coor,mu);
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
pha = pha + (TwoPiL * geom.shifts[p][mu]) * coor;
|
||||
}
|
||||
pha =exp(pha*ci);
|
||||
phaV=Zero();
|
||||
blockZAXPY(phaV,pha,Subspace.subspace[i],phaV);
|
||||
tphase+=usecond();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Multiple phased subspace vector by matrix and project to subspace
|
||||
// Remove local bulk phase to leave relative phases
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
tmat-=usecond();
|
||||
linop.Op(phaV,MphaV);
|
||||
tmat+=usecond();
|
||||
|
||||
tproj-=usecond();
|
||||
blockProject(coarseInner,MphaV,Subspace.subspace);
|
||||
coarseInner = conjugate(pha) * coarseInner;
|
||||
|
||||
ComputeProj[p] = coarseInner;
|
||||
tproj+=usecond();
|
||||
|
||||
}
|
||||
|
||||
tinv-=usecond();
|
||||
for(int k=0;k<npoint;k++){
|
||||
FT[k] = Zero();
|
||||
for(int l=0;l<npoint;l++){
|
||||
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
|
||||
}
|
||||
|
||||
int osites=CoarseGrid()->oSites();
|
||||
autoView( A_v , _A[k], AcceleratorWrite);
|
||||
autoView( FT_v , FT[k], AcceleratorRead);
|
||||
accelerator_for(sss, osites, 1, {
|
||||
for(int j=0;j<nbasis;j++){
|
||||
A_v[sss](i,j) = FT_v[sss](j);
|
||||
}
|
||||
});
|
||||
}
|
||||
tinv+=usecond();
|
||||
}
|
||||
|
||||
// Only needed if nonhermitian
|
||||
if ( ! hermitian ) {
|
||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
||||
// PopulateAdag();
|
||||
}
|
||||
|
||||
// Need to write something to populate Adag from A
|
||||
ExchangeCoarseLinks();
|
||||
std::cout << GridLogMessage<<"CoarsenOperator eigen "<<teigen<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
||||
}
|
||||
#else
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Galerkin projection of matrix
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
||||
{
|
||||
CoarsenOperator(linop,Subspace,Subspace);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Petrov - Galerkin projection of matrix
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||
Aggregation<Fobj,CComplex,nbasis> & U,
|
||||
Aggregation<Fobj,CComplex,nbasis> & V)
|
||||
{
|
||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrix "<< std::endl;
|
||||
GridBase *grid = FineGrid();
|
||||
|
||||
RealD tproj=0.0;
|
||||
RealD teigen=0.0;
|
||||
RealD tmat=0.0;
|
||||
RealD tphase=0.0;
|
||||
RealD tphaseBZ=0.0;
|
||||
RealD tinv=0.0;
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Orthogonalise the subblocks over the basis
|
||||
/////////////////////////////////////////////////////////////
|
||||
CoarseScalar InnerProd(CoarseGrid());
|
||||
blockOrthogonalise(InnerProd,V.subspace);
|
||||
blockOrthogonalise(InnerProd,U.subspace);
|
||||
|
||||
const int npoint = geom.npoint;
|
||||
|
||||
Coordinate clatt = CoarseGrid()->GlobalDimensions();
|
||||
int Nd = CoarseGrid()->Nd();
|
||||
|
||||
/*
|
||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
||||
* Matrix index i is mapped to this shift via
|
||||
* geom.shifts[i]
|
||||
*
|
||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
||||
* = M_{kl} A_ji^{b.b+l}
|
||||
*
|
||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
||||
*
|
||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||
*
|
||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
||||
*/
|
||||
teigen-=usecond();
|
||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
ComplexD ci(0.0,1.0);
|
||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
||||
|
||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
||||
ComplexD phase(0.0,0.0);
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
phase=phase+TwoPiL*geom.shifts[k][mu]*geom.shifts[l][mu];
|
||||
}
|
||||
phase=exp(phase*ci);
|
||||
Mkl(k,l) = phase;
|
||||
}
|
||||
}
|
||||
invMkl = Mkl.inverse();
|
||||
teigen+=usecond();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Now compute the matrix elements of linop between the orthonormal
|
||||
// set of vectors.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
FineField phaV(grid); // Phased block basis vector
|
||||
FineField MphaV(grid);// Matrix applied
|
||||
std::vector<FineComplexField> phaF(npoint,grid);
|
||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid());
|
||||
|
||||
CoarseVector coarseInner(CoarseGrid());
|
||||
|
||||
typedef typename CComplex::scalar_type SComplex;
|
||||
FineComplexField one(grid); one=SComplex(1.0);
|
||||
FineComplexField zz(grid); zz = Zero();
|
||||
tphase=-usecond();
|
||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||
/////////////////////////////////////////////////////
|
||||
// Stick a phase on every block
|
||||
/////////////////////////////////////////////////////
|
||||
CoarseComplexField coor(CoarseGrid());
|
||||
pha[p]=Zero();
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
LatticeCoordinate(coor,mu);
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
pha[p] = pha[p] + (TwoPiL * geom.shifts[p][mu]) * coor;
|
||||
}
|
||||
pha[p] =exp(pha[p]*ci);
|
||||
|
||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
||||
|
||||
}
|
||||
tphase+=usecond();
|
||||
|
||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid());
|
||||
std::vector<CoarseVector> FT(npoint,CoarseGrid());
|
||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||
tphaseBZ-=usecond();
|
||||
phaV = phaF[p]*V.subspace[i];
|
||||
tphaseBZ+=usecond();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Multiple phased subspace vector by matrix and project to subspace
|
||||
// Remove local bulk phase to leave relative phases
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
tmat-=usecond();
|
||||
linop.Op(phaV,MphaV);
|
||||
tmat+=usecond();
|
||||
// std::cout << i << " " <<p << " MphaV "<<norm2(MphaV)<<" "<<norm2(phaV)<<std::endl;
|
||||
|
||||
tproj-=usecond();
|
||||
blockProject(coarseInner,MphaV,U.subspace);
|
||||
coarseInner = conjugate(pha[p]) * coarseInner;
|
||||
|
||||
ComputeProj[p] = coarseInner;
|
||||
tproj+=usecond();
|
||||
// std::cout << i << " " <<p << " ComputeProj "<<norm2(ComputeProj[p])<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
tinv-=usecond();
|
||||
for(int k=0;k<npoint;k++){
|
||||
FT[k] = Zero();
|
||||
for(int l=0;l<npoint;l++){
|
||||
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
|
||||
}
|
||||
|
||||
int osites=CoarseGrid()->oSites();
|
||||
autoView( A_v , _A[k], AcceleratorWrite);
|
||||
autoView( FT_v , FT[k], AcceleratorRead);
|
||||
accelerator_for(sss, osites, 1, {
|
||||
for(int j=0;j<nbasis;j++){
|
||||
A_v[sss](i,j) = FT_v[sss](j);
|
||||
}
|
||||
});
|
||||
}
|
||||
tinv+=usecond();
|
||||
}
|
||||
|
||||
// Only needed if nonhermitian
|
||||
if ( ! hermitian ) {
|
||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
||||
// PopulateAdag();
|
||||
}
|
||||
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl;
|
||||
}
|
||||
|
||||
// Need to write something to populate Adag from A
|
||||
ExchangeCoarseLinks();
|
||||
std::cout << GridLogMessage<<"CoarsenOperator eigen "<<teigen<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
||||
}
|
||||
#endif
|
||||
void ExchangeCoarseLinks(void){
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
_A[p] = Cell.ExchangePeriodic(_A[p]);
|
||||
// _Adag[p]= Cell.ExchangePeriodic(_Adag[p]);
|
||||
}
|
||||
}
|
||||
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
||||
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
|
||||
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
|
||||
};
|
||||
|
||||
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -0,0 +1,729 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrixMultiRHS.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
|
||||
// Fine Object == (per site) type of fine field
|
||||
// nbasis == number of deflation vectors
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class MultiGeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
||||
public:
|
||||
typedef typename CComplex::scalar_object SComplex;
|
||||
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
||||
typedef MultiGeneralCoarsenedMatrix<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
|
||||
|
||||
typedef iVector<CComplex,nbasis > siteVector;
|
||||
typedef iMatrix<CComplex,nbasis > siteMatrix;
|
||||
typedef iVector<SComplex,nbasis > calcVector;
|
||||
typedef iMatrix<SComplex,nbasis > calcMatrix;
|
||||
typedef Lattice<iScalar<CComplex> > CoarseComplexField;
|
||||
typedef Lattice<siteVector> CoarseVector;
|
||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
||||
typedef iVector<CComplex,nbasis > Cvec;
|
||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||
typedef Lattice<Fobj > FineField;
|
||||
typedef Lattice<CComplex > FineComplexField;
|
||||
typedef CoarseVector Field;
|
||||
|
||||
////////////////////
|
||||
// Data members
|
||||
////////////////////
|
||||
GridCartesian * _CoarseGridMulti;
|
||||
NonLocalStencilGeometry geom;
|
||||
NonLocalStencilGeometry geom_srhs;
|
||||
PaddedCell Cell;
|
||||
GeneralLocalStencil Stencil;
|
||||
|
||||
deviceVector<calcVector> BLAS_B;
|
||||
deviceVector<calcVector> BLAS_C;
|
||||
std::vector<deviceVector<calcMatrix> > BLAS_A;
|
||||
|
||||
std::vector<deviceVector<ComplexD *> > BLAS_AP;
|
||||
std::vector<deviceVector<ComplexD *> > BLAS_BP;
|
||||
deviceVector<ComplexD *> BLAS_CP;
|
||||
|
||||
///////////////////////
|
||||
// Interface
|
||||
///////////////////////
|
||||
GridBase * Grid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
|
||||
GridCartesian * CoarseGrid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
|
||||
|
||||
// Can be used to do I/O on the operator matrices externally
|
||||
void SetMatrix (int p,CoarseMatrix & A)
|
||||
{
|
||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
||||
GridtoBLAS(A[p],BLAS_A[p]);
|
||||
}
|
||||
void GetMatrix (int p,CoarseMatrix & A)
|
||||
{
|
||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
||||
BLAStoGrid(A[p],BLAS_A[p]);
|
||||
}
|
||||
void CopyMatrix (GeneralCoarseOp &_Op)
|
||||
{
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
||||
//Unpadded
|
||||
GridtoBLAS(Aup,BLAS_A[p]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
void CheckMatrix (GeneralCoarseOp &_Op)
|
||||
{
|
||||
std::cout <<"************* Checking the little direc operator mRHS"<<std::endl;
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
//Unpadded
|
||||
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
||||
auto Ack = Aup;
|
||||
BLAStoGrid(Ack,BLAS_A[p]);
|
||||
std::cout << p<<" Ack "<<norm2(Ack)<<std::endl;
|
||||
std::cout << p<<" Aup "<<norm2(Aup)<<std::endl;
|
||||
}
|
||||
std::cout <<"************* "<<std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
MultiGeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGridMulti) :
|
||||
_CoarseGridMulti(CoarseGridMulti),
|
||||
geom_srhs(_geom),
|
||||
geom(_CoarseGridMulti,_geom.hops,_geom.skip+1),
|
||||
Cell(geom.Depth(),_CoarseGridMulti),
|
||||
Stencil(Cell.grids.back(),geom.shifts) // padded cell stencil
|
||||
{
|
||||
int32_t padded_sites = Cell.grids.back()->lSites();
|
||||
int32_t unpadded_sites = CoarseGridMulti->lSites();
|
||||
|
||||
int32_t nrhs = CoarseGridMulti->FullDimensions()[0]; // # RHS
|
||||
int32_t orhs = nrhs/CComplex::Nsimd();
|
||||
|
||||
padded_sites = padded_sites/nrhs;
|
||||
unpadded_sites = unpadded_sites/nrhs;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Device data vector storage
|
||||
/////////////////////////////////////////////////
|
||||
BLAS_A.resize(geom.npoint);
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
BLAS_A[p].resize (unpadded_sites); // no ghost zone, npoint elements
|
||||
}
|
||||
|
||||
BLAS_B.resize(nrhs *padded_sites); // includes ghost zone
|
||||
BLAS_C.resize(nrhs *unpadded_sites); // no ghost zone
|
||||
BLAS_AP.resize(geom.npoint);
|
||||
BLAS_BP.resize(geom.npoint);
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
BLAS_AP[p].resize(unpadded_sites);
|
||||
BLAS_BP[p].resize(unpadded_sites);
|
||||
}
|
||||
BLAS_CP.resize(unpadded_sites);
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Pointers to data
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
// Site identity mapping for A
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
for(int ss=0;ss<unpadded_sites;ss++){
|
||||
ComplexD *ptr = (ComplexD *)&BLAS_A[p][ss];
|
||||
acceleratorPut(BLAS_AP[p][ss],ptr);
|
||||
}
|
||||
}
|
||||
// Site identity mapping for C
|
||||
for(int ss=0;ss<unpadded_sites;ss++){
|
||||
ComplexD *ptr = (ComplexD *)&BLAS_C[ss*nrhs];
|
||||
acceleratorPut(BLAS_CP[ss],ptr);
|
||||
}
|
||||
|
||||
// Neighbour table is more complicated
|
||||
int32_t j=0; // Interior point counter (unpadded)
|
||||
for(int32_t s=0;s<padded_sites;s++){ // 4 volume, padded
|
||||
int ghost_zone=0;
|
||||
for(int32_t point = 0 ; point < geom.npoint; point++){
|
||||
int i=s*orhs*geom.npoint+point;
|
||||
if( Stencil._entries[i]._wrap ) { // stencil is indexed by the oSite of the CoarseGridMulti, hence orhs factor
|
||||
ghost_zone=1; // If general stencil wrapped in any direction, wrap=1
|
||||
}
|
||||
}
|
||||
|
||||
if( ghost_zone==0) {
|
||||
for(int32_t point = 0 ; point < geom.npoint; point++){
|
||||
int i=s*orhs*geom.npoint+point;
|
||||
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite
|
||||
GRID_ASSERT(nbr<BLAS_B.size());
|
||||
ComplexD * ptr = (ComplexD *)&BLAS_B[nbr];
|
||||
acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
GRID_ASSERT(j==unpadded_sites);
|
||||
}
|
||||
template<class vobj> void GridtoBLAS(const Lattice<vobj> &from,deviceVector<typename vobj::scalar_object> &to)
|
||||
{
|
||||
typedef typename vobj::scalar_object sobj;
|
||||
typedef typename vobj::scalar_type scalar_type;
|
||||
typedef typename vobj::vector_type vector_type;
|
||||
|
||||
GridBase *Fg = from.Grid();
|
||||
GRID_ASSERT(!Fg->_isCheckerBoarded);
|
||||
int nd = Fg->_ndimension;
|
||||
|
||||
to.resize(Fg->lSites());
|
||||
|
||||
Coordinate LocalLatt = Fg->LocalDimensions();
|
||||
size_t nsite = 1;
|
||||
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do the index calc on the GPU
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Coordinate f_ostride = Fg->_ostride;
|
||||
Coordinate f_istride = Fg->_istride;
|
||||
Coordinate f_rdimensions = Fg->_rdimensions;
|
||||
|
||||
autoView(from_v,from,AcceleratorRead);
|
||||
auto to_v = &to[0];
|
||||
|
||||
const int words=sizeof(vobj)/sizeof(vector_type);
|
||||
accelerator_for(idx,nsite,1,{
|
||||
|
||||
Coordinate from_coor, base;
|
||||
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
|
||||
for(int i=0;i<nd;i++){
|
||||
from_coor[i] = base[i];
|
||||
}
|
||||
int from_oidx = 0; for(int d=0;d<nd;d++) from_oidx+=f_ostride[d]*(from_coor[d]%f_rdimensions[d]);
|
||||
int from_lane = 0; for(int d=0;d<nd;d++) from_lane+=f_istride[d]*(from_coor[d]/f_rdimensions[d]);
|
||||
|
||||
const vector_type* from = (const vector_type *)&from_v[from_oidx];
|
||||
scalar_type* to = (scalar_type *)&to_v[idx];
|
||||
|
||||
scalar_type stmp;
|
||||
for(int w=0;w<words;w++){
|
||||
stmp = getlane(from[w], from_lane);
|
||||
to[w] = stmp;
|
||||
}
|
||||
});
|
||||
}
|
||||
template<class vobj> void BLAStoGrid(Lattice<vobj> &grid,deviceVector<typename vobj::scalar_object> &in)
|
||||
{
|
||||
typedef typename vobj::scalar_object sobj;
|
||||
typedef typename vobj::scalar_type scalar_type;
|
||||
typedef typename vobj::vector_type vector_type;
|
||||
|
||||
GridBase *Tg = grid.Grid();
|
||||
GRID_ASSERT(!Tg->_isCheckerBoarded);
|
||||
int nd = Tg->_ndimension;
|
||||
|
||||
GRID_ASSERT(in.size()==Tg->lSites());
|
||||
|
||||
Coordinate LocalLatt = Tg->LocalDimensions();
|
||||
size_t nsite = 1;
|
||||
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do the index calc on the GPU
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Coordinate t_ostride = Tg->_ostride;
|
||||
Coordinate t_istride = Tg->_istride;
|
||||
Coordinate t_rdimensions = Tg->_rdimensions;
|
||||
|
||||
autoView(to_v,grid,AcceleratorWrite);
|
||||
auto from_v = &in[0];
|
||||
|
||||
const int words=sizeof(vobj)/sizeof(vector_type);
|
||||
accelerator_for(idx,nsite,1,{
|
||||
|
||||
Coordinate to_coor, base;
|
||||
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
|
||||
for(int i=0;i<nd;i++){
|
||||
to_coor[i] = base[i];
|
||||
}
|
||||
int to_oidx = 0; for(int d=0;d<nd;d++) to_oidx+=t_ostride[d]*(to_coor[d]%t_rdimensions[d]);
|
||||
int to_lane = 0; for(int d=0;d<nd;d++) to_lane+=t_istride[d]*(to_coor[d]/t_rdimensions[d]);
|
||||
|
||||
vector_type* to = (vector_type *)&to_v[to_oidx];
|
||||
scalar_type* from = (scalar_type *)&from_v[idx];
|
||||
|
||||
scalar_type stmp;
|
||||
for(int w=0;w<words;w++){
|
||||
stmp=from[w];
|
||||
putlane(to[w], stmp, to_lane);
|
||||
}
|
||||
});
|
||||
}
|
||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||
Aggregation<Fobj,CComplex,nbasis> & Subspace,
|
||||
GridBase *CoarseGrid)
|
||||
{
|
||||
#if 0
|
||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs "<< std::endl;
|
||||
|
||||
GridBase *grid = Subspace.FineGrid;
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Orthogonalise the subblocks over the basis
|
||||
/////////////////////////////////////////////////////////////
|
||||
CoarseScalar InnerProd(CoarseGrid);
|
||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
||||
|
||||
const int npoint = geom_srhs.npoint;
|
||||
|
||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||
int Nd = CoarseGrid->Nd();
|
||||
/*
|
||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
||||
* Matrix index i is mapped to this shift via
|
||||
* geom.shifts[i]
|
||||
*
|
||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
||||
* = M_{kl} A_ji^{b.b+l}
|
||||
*
|
||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
||||
*
|
||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||
*
|
||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
||||
*/
|
||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
ComplexD ci(0.0,1.0);
|
||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
||||
|
||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
||||
ComplexD phase(0.0,0.0);
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
||||
}
|
||||
phase=exp(phase*ci);
|
||||
Mkl(k,l) = phase;
|
||||
}
|
||||
}
|
||||
invMkl = Mkl.inverse();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Now compute the matrix elements of linop between the orthonormal
|
||||
// set of vectors.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
FineField phaV(grid); // Phased block basis vector
|
||||
FineField MphaV(grid);// Matrix applied
|
||||
std::vector<FineComplexField> phaF(npoint,grid);
|
||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid);
|
||||
|
||||
CoarseVector coarseInner(CoarseGrid);
|
||||
|
||||
typedef typename CComplex::scalar_type SComplex;
|
||||
FineComplexField one(grid); one=SComplex(1.0);
|
||||
FineComplexField zz(grid); zz = Zero();
|
||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||
/////////////////////////////////////////////////////
|
||||
// Stick a phase on every block
|
||||
/////////////////////////////////////////////////////
|
||||
CoarseComplexField coor(CoarseGrid);
|
||||
pha[p]=Zero();
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
LatticeCoordinate(coor,mu);
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
||||
}
|
||||
pha[p] =exp(pha[p]*ci);
|
||||
|
||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
||||
}
|
||||
|
||||
// Could save on temporary storage here
|
||||
std::vector<CoarseMatrix> _A;
|
||||
_A.resize(geom_srhs.npoint,CoarseGrid);
|
||||
|
||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid);
|
||||
CoarseVector FT(CoarseGrid);
|
||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||
|
||||
phaV = phaF[p]*Subspace.subspace[i];
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Multiple phased subspace vector by matrix and project to subspace
|
||||
// Remove local bulk phase to leave relative phases
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
linop.Op(phaV,MphaV);
|
||||
|
||||
// Fixme, could use batched block projector here
|
||||
blockProject(coarseInner,MphaV,Subspace.subspace);
|
||||
|
||||
coarseInner = conjugate(pha[p]) * coarseInner;
|
||||
|
||||
ComputeProj[p] = coarseInner;
|
||||
}
|
||||
|
||||
// Could do this with a block promote or similar BLAS call via the MultiRHSBlockProjector with a const matrix.
|
||||
for(int k=0;k<npoint;k++){
|
||||
|
||||
FT = Zero();
|
||||
for(int l=0;l<npoint;l++){
|
||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
||||
}
|
||||
|
||||
int osites=CoarseGrid->oSites();
|
||||
autoView( A_v , _A[k], AcceleratorWrite);
|
||||
autoView( FT_v , FT, AcceleratorRead);
|
||||
accelerator_for(sss, osites, 1, {
|
||||
for(int j=0;j<nbasis;j++){
|
||||
A_v[sss](i,j) = FT_v[sss](j);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Only needed if nonhermitian
|
||||
// if ( ! hermitian ) {
|
||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
||||
// PopulateAdag();
|
||||
// }
|
||||
// Need to write something to populate Adag from A
|
||||
|
||||
for(int p=0;p<geom_srhs.npoint;p++){
|
||||
GridtoBLAS(_A[p],BLAS_A[p]);
|
||||
}
|
||||
/*
|
||||
Grid : Message : 11698.730546 s : CoarsenOperator eigen 1334 us
|
||||
Grid : Message : 11698.730563 s : CoarsenOperator phase 34729 us
|
||||
Grid : Message : 11698.730565 s : CoarsenOperator phaseBZ 2423814 us
|
||||
Grid : Message : 11698.730566 s : CoarsenOperator mat 127890998 us
|
||||
Grid : Message : 11698.730567 s : CoarsenOperator proj 515840840 us
|
||||
Grid : Message : 11698.730568 s : CoarsenOperator inv 103948313 us
|
||||
Takes 600s to compute matrix elements, DOMINATED by the block project.
|
||||
Easy to speed up with the batched block project.
|
||||
Store npoint vectors, get npoint x Nbasis block projection, and 81 fold faster.
|
||||
|
||||
// Block project below taks to 240s
|
||||
Grid : Message : 328.193418 s : CoarsenOperator phase 38338 us
|
||||
Grid : Message : 328.193434 s : CoarsenOperator phaseBZ 1711226 us
|
||||
Grid : Message : 328.193436 s : CoarsenOperator mat 122213270 us
|
||||
//Grid : Message : 328.193438 s : CoarsenOperator proj 1181154 us <-- this is mistimed
|
||||
//Grid : Message : 11698.730568 s : CoarsenOperator inv 103948313 us <-- Cut this ~10x if lucky by loop fusion
|
||||
*/
|
||||
#else
|
||||
RealD tproj=0.0;
|
||||
RealD tmat=0.0;
|
||||
RealD tphase=0.0;
|
||||
RealD tphaseBZ=0.0;
|
||||
RealD tinv=0.0;
|
||||
|
||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs "<< std::endl;
|
||||
|
||||
GridBase *grid = Subspace.FineGrid;
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Orthogonalise the subblocks over the basis
|
||||
/////////////////////////////////////////////////////////////
|
||||
CoarseScalar InnerProd(CoarseGrid);
|
||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
||||
|
||||
|
||||
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
||||
Projector.Allocate(nbasis,grid,CoarseGrid);
|
||||
Projector.ImportBasis(Subspace.subspace);
|
||||
|
||||
const int npoint = geom_srhs.npoint;
|
||||
|
||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||
int Nd = CoarseGrid->Nd();
|
||||
/*
|
||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
||||
* Matrix index i is mapped to this shift via
|
||||
* geom.shifts[i]
|
||||
*
|
||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
||||
* = M_{kl} A_ji^{b.b+l}
|
||||
*
|
||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
||||
*
|
||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||
*
|
||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
||||
*/
|
||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||
ComplexD ci(0.0,1.0);
|
||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
||||
|
||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
||||
ComplexD phase(0.0,0.0);
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
||||
}
|
||||
phase=exp(phase*ci);
|
||||
Mkl(k,l) = phase;
|
||||
}
|
||||
}
|
||||
invMkl = Mkl.inverse();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Now compute the matrix elements of linop between the orthonormal
|
||||
// set of vectors.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
FineField phaV(grid); // Phased block basis vector
|
||||
FineField MphaV(grid);// Matrix applied
|
||||
std::vector<FineComplexField> phaF(npoint,grid);
|
||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid);
|
||||
|
||||
CoarseVector coarseInner(CoarseGrid);
|
||||
|
||||
tphase=-usecond();
|
||||
typedef typename CComplex::scalar_type SComplex;
|
||||
FineComplexField one(grid); one=SComplex(1.0);
|
||||
FineComplexField zz(grid); zz = Zero();
|
||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||
/////////////////////////////////////////////////////
|
||||
// Stick a phase on every block
|
||||
/////////////////////////////////////////////////////
|
||||
CoarseComplexField coor(CoarseGrid);
|
||||
pha[p]=Zero();
|
||||
for(int mu=0;mu<Nd;mu++){
|
||||
LatticeCoordinate(coor,mu);
|
||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
||||
}
|
||||
pha[p] =exp(pha[p]*ci);
|
||||
|
||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
||||
}
|
||||
tphase+=usecond();
|
||||
|
||||
// Could save on temporary storage here
|
||||
std::vector<CoarseMatrix> _A;
|
||||
_A.resize(geom_srhs.npoint,CoarseGrid);
|
||||
|
||||
// Count use small chunks than npoint == 81 and save memory
|
||||
int batch = 9;
|
||||
std::vector<FineField> _MphaV(batch,grid);
|
||||
std::vector<CoarseVector> TmpProj(batch,CoarseGrid);
|
||||
|
||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid);
|
||||
CoarseVector FT(CoarseGrid);
|
||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
||||
|
||||
// std::cout << GridLogMessage << " phasing the fine vector "<<std::endl;
|
||||
// Fixme : do this in batches
|
||||
for(int p=0;p<npoint;p+=batch){ // Loop over momenta in npoint
|
||||
|
||||
for(int b=0;b<MIN(batch,npoint-p);b++){
|
||||
tphaseBZ-=usecond();
|
||||
phaV = phaF[p+b]*Subspace.subspace[i];
|
||||
tphaseBZ+=usecond();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Multiple phased subspace vector by matrix and project to subspace
|
||||
// Remove local bulk phase to leave relative phases
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Memory footprint was an issue
|
||||
tmat-=usecond();
|
||||
linop.Op(phaV,MphaV);
|
||||
_MphaV[b] = MphaV;
|
||||
tmat+=usecond();
|
||||
}
|
||||
|
||||
// std::cout << GridLogMessage << " Calling block project "<<std::endl;
|
||||
tproj-=usecond();
|
||||
Projector.blockProject(_MphaV,TmpProj);
|
||||
tproj+=usecond();
|
||||
|
||||
// std::cout << GridLogMessage << " conj phasing the coarse vectors "<<std::endl;
|
||||
for(int b=0;b<MIN(batch,npoint-p);b++){
|
||||
ComputeProj[p+b] = conjugate(pha[p+b])*TmpProj[b];
|
||||
}
|
||||
}
|
||||
|
||||
// Could do this with a block promote or similar BLAS call via the MultiRHSBlockProjector with a const matrix.
|
||||
|
||||
// std::cout << GridLogMessage << " Starting FT inv "<<std::endl;
|
||||
tinv-=usecond();
|
||||
for(int k=0;k<npoint;k++){
|
||||
FT = Zero();
|
||||
// 81 kernel calls as many ComputeProj vectors
|
||||
// Could fuse with a vector of views, but ugly
|
||||
// Could unroll the expression and run fewer kernels -- much more attractive
|
||||
// Could also do non blocking.
|
||||
#if 0
|
||||
for(int l=0;l<npoint;l++){
|
||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
||||
}
|
||||
#else
|
||||
const int radix = 9;
|
||||
int ll;
|
||||
for(ll=0;ll+radix-1<npoint;ll+=radix){
|
||||
// When ll = npoint-radix, ll+radix-1 = npoint-1, and we do it all.
|
||||
FT = FT
|
||||
+ invMkl(ll+0,k)*ComputeProj[ll+0]
|
||||
+ invMkl(ll+1,k)*ComputeProj[ll+1]
|
||||
+ invMkl(ll+2,k)*ComputeProj[ll+2]
|
||||
+ invMkl(ll+3,k)*ComputeProj[ll+3]
|
||||
+ invMkl(ll+4,k)*ComputeProj[ll+4]
|
||||
+ invMkl(ll+5,k)*ComputeProj[ll+5]
|
||||
+ invMkl(ll+6,k)*ComputeProj[ll+6]
|
||||
+ invMkl(ll+7,k)*ComputeProj[ll+7]
|
||||
+ invMkl(ll+8,k)*ComputeProj[ll+8];
|
||||
}
|
||||
for(int l=ll;l<npoint;l++){
|
||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
||||
}
|
||||
#endif
|
||||
|
||||
// 1 kernel call -- must be cheaper
|
||||
int osites=CoarseGrid->oSites();
|
||||
autoView( A_v , _A[k], AcceleratorWrite);
|
||||
autoView( FT_v , FT, AcceleratorRead);
|
||||
accelerator_for(sss, osites, 1, {
|
||||
for(int j=0;j<nbasis;j++){
|
||||
A_v[sss](i,j) = FT_v[sss](j);
|
||||
}
|
||||
});
|
||||
}
|
||||
tinv+=usecond();
|
||||
}
|
||||
|
||||
// Only needed if nonhermitian
|
||||
// if ( ! hermitian ) {
|
||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
||||
// PopulateAdag();
|
||||
// }
|
||||
// Need to write something to populate Adag from A
|
||||
// std::cout << GridLogMessage << " Calling GridtoBLAS "<<std::endl;
|
||||
for(int p=0;p<geom_srhs.npoint;p++){
|
||||
GridtoBLAS(_A[p],BLAS_A[p]);
|
||||
}
|
||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
void Mdag(const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
this->M(in,out);
|
||||
}
|
||||
void M (const CoarseVector &in, CoarseVector &out)
|
||||
{
|
||||
// std::cout << GridLogMessage << "New Mrhs coarse"<<std::endl;
|
||||
conformable(CoarseGrid(),in.Grid());
|
||||
conformable(in.Grid(),out.Grid());
|
||||
out.Checkerboard() = in.Checkerboard();
|
||||
|
||||
RealD t_tot;
|
||||
RealD t_exch;
|
||||
RealD t_GtoB;
|
||||
RealD t_BtoG;
|
||||
RealD t_mult;
|
||||
|
||||
t_tot=-usecond();
|
||||
CoarseVector tin=in;
|
||||
t_exch=-usecond();
|
||||
CoarseVector pin = Cell.ExchangePeriodic(tin); //padded input
|
||||
t_exch+=usecond();
|
||||
|
||||
CoarseVector pout(pin.Grid());
|
||||
|
||||
int npoint = geom.npoint;
|
||||
typedef calcMatrix* Aview;
|
||||
typedef LatticeView<Cvec> Vview;
|
||||
|
||||
const int Nsimd = CComplex::Nsimd();
|
||||
|
||||
int64_t nrhs =pin.Grid()->GlobalDimensions()[0];
|
||||
GRID_ASSERT(nrhs>=1);
|
||||
|
||||
RealD flops,bytes;
|
||||
int64_t osites=in.Grid()->oSites(); // unpadded
|
||||
int64_t unpadded_vol = CoarseGrid()->lSites()/nrhs;
|
||||
|
||||
flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
|
||||
bytes = 1.0*osites*sizeof(siteMatrix)*npoint/pin.Grid()->GlobalDimensions()[0]
|
||||
+ 2.0*osites*sizeof(siteVector)*npoint;
|
||||
|
||||
|
||||
t_GtoB=-usecond();
|
||||
GridtoBLAS(pin,BLAS_B);
|
||||
t_GtoB+=usecond();
|
||||
|
||||
GridBLAS BLAS;
|
||||
|
||||
t_mult=-usecond();
|
||||
for(int p=0;p<geom.npoint;p++){
|
||||
RealD c = 1.0;
|
||||
if (p==0) c = 0.0;
|
||||
ComplexD beta(c);
|
||||
|
||||
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
||||
ComplexD(1.0),
|
||||
BLAS_AP[p],
|
||||
BLAS_BP[p],
|
||||
ComplexD(c),
|
||||
BLAS_CP);
|
||||
}
|
||||
BLAS.synchronise();
|
||||
t_mult+=usecond();
|
||||
|
||||
t_BtoG=-usecond();
|
||||
BLAStoGrid(out,BLAS_C);
|
||||
t_BtoG+=usecond();
|
||||
t_tot+=usecond();
|
||||
/*
|
||||
std::cout << GridLogMessage << "New Mrhs coarse DONE "<<std::endl;
|
||||
std::cout << GridLogMessage<<"Coarse Mult exch "<<t_exch<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"Coarse Mult mult "<<t_mult<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"Coarse Mult GtoB "<<t_GtoB<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"Coarse Mult BtoG "<<t_BtoG<<" us"<<std::endl;
|
||||
std::cout << GridLogMessage<<"Coarse Mult tot "<<t_tot<<" us"<<std::endl;
|
||||
*/
|
||||
// std::cout << GridLogMessage<<std::endl;
|
||||
// std::cout << GridLogMessage<<"Coarse Kernel flops "<< flops<<std::endl;
|
||||
// std::cout << GridLogMessage<<"Coarse Kernel flop/s "<< flops/t_mult<<" mflop/s"<<std::endl;
|
||||
// std::cout << GridLogMessage<<"Coarse Kernel bytes/s "<< bytes/t_mult/1000<<" GB/s"<<std::endl;
|
||||
// std::cout << GridLogMessage<<"Coarse overall flops/s "<< flops/t_tot<<" mflop/s"<<std::endl;
|
||||
// std::cout << GridLogMessage<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
|
||||
};
|
||||
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
||||
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
|
||||
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -0,0 +1,238 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrix.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Geometry class in cartesian case
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
class Geometry {
|
||||
public:
|
||||
int npoint;
|
||||
int base;
|
||||
std::vector<int> directions ;
|
||||
std::vector<int> displacements;
|
||||
std::vector<int> points_dagger;
|
||||
|
||||
Geometry(int _d) {
|
||||
|
||||
base = (_d==5) ? 1:0;
|
||||
|
||||
// make coarse grid stencil for 4d , not 5d
|
||||
if ( _d==5 ) _d=4;
|
||||
|
||||
npoint = 2*_d+1;
|
||||
directions.resize(npoint);
|
||||
displacements.resize(npoint);
|
||||
points_dagger.resize(npoint);
|
||||
for(int d=0;d<_d;d++){
|
||||
directions[d ] = d+base;
|
||||
directions[d+_d] = d+base;
|
||||
displacements[d ] = +1;
|
||||
displacements[d+_d]= -1;
|
||||
points_dagger[d ] = d+_d;
|
||||
points_dagger[d+_d] = d;
|
||||
}
|
||||
directions [2*_d]=0;
|
||||
displacements[2*_d]=0;
|
||||
points_dagger[2*_d]=2*_d;
|
||||
}
|
||||
|
||||
int point(int dir, int disp) {
|
||||
GRID_ASSERT(disp == -1 || disp == 0 || disp == 1);
|
||||
GRID_ASSERT(base+0 <= dir && dir < base+4);
|
||||
|
||||
// directions faster index = new indexing
|
||||
// 4d (base = 0):
|
||||
// point 0 1 2 3 4 5 6 7 8
|
||||
// dir 0 1 2 3 0 1 2 3 0
|
||||
// disp +1 +1 +1 +1 -1 -1 -1 -1 0
|
||||
// 5d (base = 1):
|
||||
// point 0 1 2 3 4 5 6 7 8
|
||||
// dir 1 2 3 4 1 2 3 4 0
|
||||
// disp +1 +1 +1 +1 -1 -1 -1 -1 0
|
||||
|
||||
// displacements faster index = old indexing
|
||||
// 4d (base = 0):
|
||||
// point 0 1 2 3 4 5 6 7 8
|
||||
// dir 0 0 1 1 2 2 3 3 0
|
||||
// disp +1 -1 +1 -1 +1 -1 +1 -1 0
|
||||
// 5d (base = 1):
|
||||
// point 0 1 2 3 4 5 6 7 8
|
||||
// dir 1 1 2 2 3 3 4 4 0
|
||||
// disp +1 -1 +1 -1 +1 -1 +1 -1 0
|
||||
|
||||
if(dir == 0 and disp == 0)
|
||||
return 8;
|
||||
else // New indexing
|
||||
return (1 - disp) / 2 * 4 + dir - base;
|
||||
// else // Old indexing
|
||||
// return (4 * (dir - base) + 1 - disp) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Less local equivalent of Geometry class in cartesian case
|
||||
/////////////////////////////////////////////////////////////////
|
||||
class NonLocalStencilGeometry {
|
||||
public:
|
||||
// int depth;
|
||||
int skip;
|
||||
int hops;
|
||||
int npoint;
|
||||
std::vector<Coordinate> shifts;
|
||||
Coordinate stencil_size;
|
||||
Coordinate stencil_lo;
|
||||
Coordinate stencil_hi;
|
||||
GridCartesian *grid;
|
||||
GridCartesian *Grid() {return grid;};
|
||||
int Depth(void){return 1;}; // Ghost zone depth
|
||||
int Hops(void){return hops;}; // # of hops=> level of corner fill in in stencil
|
||||
int DimSkip(void){return skip;};
|
||||
|
||||
virtual ~NonLocalStencilGeometry() {};
|
||||
|
||||
int Reverse(int point)
|
||||
{
|
||||
int Nd = Grid()->Nd();
|
||||
Coordinate shft = shifts[point];
|
||||
Coordinate rev(Nd);
|
||||
for(int mu=0;mu<Nd;mu++) rev[mu]= -shft[mu];
|
||||
for(int p=0;p<npoint;p++){
|
||||
if(rev==shifts[p]){
|
||||
return p;
|
||||
}
|
||||
}
|
||||
GRID_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
void BuildShifts(void)
|
||||
{
|
||||
this->shifts.resize(0);
|
||||
int Nd = this->grid->Nd();
|
||||
|
||||
int dd = this->DimSkip();
|
||||
for(int s0=this->stencil_lo[dd+0];s0<=this->stencil_hi[dd+0];s0++){
|
||||
for(int s1=this->stencil_lo[dd+1];s1<=this->stencil_hi[dd+1];s1++){
|
||||
for(int s2=this->stencil_lo[dd+2];s2<=this->stencil_hi[dd+2];s2++){
|
||||
for(int s3=this->stencil_lo[dd+3];s3<=this->stencil_hi[dd+3];s3++){
|
||||
Coordinate sft(Nd,0);
|
||||
sft[dd+0] = s0;
|
||||
sft[dd+1] = s1;
|
||||
sft[dd+2] = s2;
|
||||
sft[dd+3] = s3;
|
||||
int nhops = abs(s0)+abs(s1)+abs(s2)+abs(s3);
|
||||
if(nhops<=this->hops) this->shifts.push_back(sft);
|
||||
}}}}
|
||||
this->npoint = this->shifts.size();
|
||||
std::cout << GridLogMessage << "NonLocalStencilGeometry has "<< this->npoint << " terms in stencil "<<std::endl;
|
||||
}
|
||||
|
||||
NonLocalStencilGeometry(GridCartesian *_coarse_grid,int _hops,int _skip) : grid(_coarse_grid), hops(_hops), skip(_skip)
|
||||
{
|
||||
Coordinate latt = grid->GlobalDimensions();
|
||||
stencil_size.resize(grid->Nd());
|
||||
stencil_lo.resize(grid->Nd());
|
||||
stencil_hi.resize(grid->Nd());
|
||||
for(int d=0;d<grid->Nd();d++){
|
||||
if ( latt[d] == 1 ) {
|
||||
stencil_lo[d] = 0;
|
||||
stencil_hi[d] = 0;
|
||||
stencil_size[d]= 1;
|
||||
} else if ( latt[d] == 2 ) {
|
||||
stencil_lo[d] = -1;
|
||||
stencil_hi[d] = 0;
|
||||
stencil_size[d]= 2;
|
||||
} else if ( latt[d] > 2 ) {
|
||||
stencil_lo[d] = -1;
|
||||
stencil_hi[d] = 1;
|
||||
stencil_size[d]= 3;
|
||||
}
|
||||
}
|
||||
this->BuildShifts();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// Need to worry about red-black now
|
||||
class NonLocalStencilGeometry4D : public NonLocalStencilGeometry {
|
||||
public:
|
||||
virtual int DerivedDimSkip(void) { return 0;};
|
||||
NonLocalStencilGeometry4D(GridCartesian *Coarse,int _hops) : NonLocalStencilGeometry(Coarse,_hops,0) { };
|
||||
virtual ~NonLocalStencilGeometry4D() {};
|
||||
};
|
||||
class NonLocalStencilGeometry5D : public NonLocalStencilGeometry {
|
||||
public:
|
||||
virtual int DerivedDimSkip(void) { return 1; };
|
||||
NonLocalStencilGeometry5D(GridCartesian *Coarse,int _hops) : NonLocalStencilGeometry(Coarse,_hops,1) { };
|
||||
virtual ~NonLocalStencilGeometry5D() {};
|
||||
};
|
||||
/*
|
||||
* Bunch of different options classes
|
||||
*/
|
||||
class NextToNextToNextToNearestStencilGeometry4D : public NonLocalStencilGeometry4D {
|
||||
public:
|
||||
NextToNextToNextToNearestStencilGeometry4D(GridCartesian *Coarse) : NonLocalStencilGeometry4D(Coarse,4)
|
||||
{
|
||||
};
|
||||
};
|
||||
class NextToNextToNextToNearestStencilGeometry5D : public NonLocalStencilGeometry5D {
|
||||
public:
|
||||
NextToNextToNextToNearestStencilGeometry5D(GridCartesian *Coarse) : NonLocalStencilGeometry5D(Coarse,4)
|
||||
{
|
||||
};
|
||||
};
|
||||
class NextToNearestStencilGeometry4D : public NonLocalStencilGeometry4D {
|
||||
public:
|
||||
NextToNearestStencilGeometry4D(GridCartesian *Coarse) : NonLocalStencilGeometry4D(Coarse,2)
|
||||
{
|
||||
};
|
||||
};
|
||||
class NextToNearestStencilGeometry5D : public NonLocalStencilGeometry5D {
|
||||
public:
|
||||
NextToNearestStencilGeometry5D(GridCartesian *Coarse) : NonLocalStencilGeometry5D(Coarse,2)
|
||||
{
|
||||
};
|
||||
};
|
||||
class NearestStencilGeometry4D : public NonLocalStencilGeometry4D {
|
||||
public:
|
||||
NearestStencilGeometry4D(GridCartesian *Coarse) : NonLocalStencilGeometry4D(Coarse,1)
|
||||
{
|
||||
};
|
||||
};
|
||||
class NearestStencilGeometry5D : public NonLocalStencilGeometry5D {
|
||||
public:
|
||||
NearestStencilGeometry5D(GridCartesian *Coarse) : NonLocalStencilGeometry5D(Coarse,1)
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: Grid/algorithms/multigrid/MultiGrid.h
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <pboyle@bnl.gov>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
#include <Grid/algorithms/multigrid/Aggregates.h>
|
||||
#include <Grid/algorithms/multigrid/Geometry.h>
|
||||
#include <Grid/algorithms/multigrid/CoarsenedMatrix.h>
|
||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrix.h>
|
||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
|
||||
@@ -1,154 +0,0 @@
|
||||
#include <Grid/GridCore.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
MemoryStats *MemoryProfiler::stats = nullptr;
|
||||
bool MemoryProfiler::debug = false;
|
||||
|
||||
int PointerCache::NcacheSmall = PointerCache::NcacheSmallMax;
|
||||
#ifdef GRID_CUDA
|
||||
int PointerCache::Ncache = 32;
|
||||
#else
|
||||
int PointerCache::Ncache = 8;
|
||||
#endif
|
||||
int PointerCache::Victim;
|
||||
int PointerCache::VictimSmall;
|
||||
PointerCache::PointerCacheEntry PointerCache::Entries[PointerCache::NcacheMax];
|
||||
PointerCache::PointerCacheEntry PointerCache::EntriesSmall[PointerCache::NcacheSmallMax];
|
||||
|
||||
void PointerCache::Init(void)
|
||||
{
|
||||
char * str;
|
||||
|
||||
str= getenv("GRID_ALLOC_NCACHE_LARGE");
|
||||
if ( str ) Ncache = atoi(str);
|
||||
if ( (Ncache<0) || (Ncache > NcacheMax)) Ncache = NcacheMax;
|
||||
|
||||
str= getenv("GRID_ALLOC_NCACHE_SMALL");
|
||||
if ( str ) NcacheSmall = atoi(str);
|
||||
if ( (NcacheSmall<0) || (NcacheSmall > NcacheSmallMax)) NcacheSmall = NcacheSmallMax;
|
||||
|
||||
// printf("Aligned alloocator cache: large %d/%d small %d/%d\n",Ncache,NcacheMax,NcacheSmall,NcacheSmallMax);
|
||||
}
|
||||
void *PointerCache::Insert(void *ptr,size_t bytes)
|
||||
{
|
||||
if (bytes < GRID_ALLOC_SMALL_LIMIT )
|
||||
return Insert(ptr,bytes,EntriesSmall,NcacheSmall,VictimSmall);
|
||||
return Insert(ptr,bytes,Entries,Ncache,Victim);
|
||||
}
|
||||
void *PointerCache::Insert(void *ptr,size_t bytes,PointerCacheEntry *entries,int ncache,int &victim)
|
||||
{
|
||||
#ifdef GRID_OMP
|
||||
assert(omp_in_parallel()==0);
|
||||
#endif
|
||||
|
||||
void * ret = NULL;
|
||||
int v = -1;
|
||||
|
||||
for(int e=0;e<ncache;e++) {
|
||||
if ( entries[e].valid==0 ) {
|
||||
v=e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( v==-1 ) {
|
||||
v=victim;
|
||||
victim = (victim+1)%ncache;
|
||||
}
|
||||
|
||||
if ( entries[v].valid ) {
|
||||
ret = entries[v].address;
|
||||
entries[v].valid = 0;
|
||||
entries[v].address = NULL;
|
||||
entries[v].bytes = 0;
|
||||
}
|
||||
|
||||
entries[v].address=ptr;
|
||||
entries[v].bytes =bytes;
|
||||
entries[v].valid =1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *PointerCache::Lookup(size_t bytes)
|
||||
{
|
||||
if (bytes < GRID_ALLOC_SMALL_LIMIT )
|
||||
return Lookup(bytes,EntriesSmall,NcacheSmall);
|
||||
return Lookup(bytes,Entries,Ncache);
|
||||
}
|
||||
void *PointerCache::Lookup(size_t bytes,PointerCacheEntry *entries,int ncache)
|
||||
{
|
||||
#ifdef GRID_OMP
|
||||
assert(omp_in_parallel()==0);
|
||||
#endif
|
||||
for(int e=0;e<ncache;e++){
|
||||
if ( entries[e].valid && ( entries[e].bytes == bytes ) ) {
|
||||
entries[e].valid = 0;
|
||||
return entries[e].address;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void check_huge_pages(void *Buf,uint64_t BYTES)
|
||||
{
|
||||
#ifdef __linux__
|
||||
int fd = open("/proc/self/pagemap", O_RDONLY);
|
||||
assert(fd >= 0);
|
||||
const int page_size = 4096;
|
||||
uint64_t virt_pfn = (uint64_t)Buf / page_size;
|
||||
off_t offset = sizeof(uint64_t) * virt_pfn;
|
||||
uint64_t npages = (BYTES + page_size-1) / page_size;
|
||||
uint64_t pagedata[npages];
|
||||
uint64_t ret = lseek(fd, offset, SEEK_SET);
|
||||
assert(ret == offset);
|
||||
ret = ::read(fd, pagedata, sizeof(uint64_t)*npages);
|
||||
assert(ret == sizeof(uint64_t) * npages);
|
||||
int nhugepages = npages / 512;
|
||||
int n4ktotal, nnothuge;
|
||||
n4ktotal = 0;
|
||||
nnothuge = 0;
|
||||
for (int i = 0; i < nhugepages; ++i) {
|
||||
uint64_t baseaddr = (pagedata[i*512] & 0x7fffffffffffffULL) * page_size;
|
||||
for (int j = 0; j < 512; ++j) {
|
||||
uint64_t pageaddr = (pagedata[i*512+j] & 0x7fffffffffffffULL) * page_size;
|
||||
++n4ktotal;
|
||||
if (pageaddr != baseaddr + j * page_size)
|
||||
++nnothuge;
|
||||
}
|
||||
}
|
||||
int rank = CartesianCommunicator::RankWorld();
|
||||
printf("rank %d Allocated %d 4k pages, %d not in huge pages\n", rank, n4ktotal, nnothuge);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string sizeString(const size_t bytes)
|
||||
{
|
||||
constexpr unsigned int bufSize = 256;
|
||||
const char *suffixes[7] = {"", "K", "M", "G", "T", "P", "E"};
|
||||
char buf[256];
|
||||
size_t s = 0;
|
||||
double count = bytes;
|
||||
|
||||
while (count >= 1024 && s < 7)
|
||||
{
|
||||
s++;
|
||||
count /= 1024;
|
||||
}
|
||||
if (count - floor(count) == 0.0)
|
||||
{
|
||||
snprintf(buf, bufSize, "%d %sB", (int)count, suffixes[s]);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, bufSize, "%.1f %sB", count, suffixes[s]);
|
||||
}
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
+149
-187
@@ -26,129 +26,10 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#ifndef GRID_ALIGNED_ALLOCATOR_H
|
||||
#define GRID_ALIGNED_ALLOCATOR_H
|
||||
|
||||
#ifdef HAVE_MALLOC_MALLOC_H
|
||||
#include <malloc/malloc.h>
|
||||
#endif
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MM_MALLOC_H
|
||||
#include <mm_malloc.h>
|
||||
#endif
|
||||
|
||||
#define POINTER_CACHE
|
||||
#define GRID_ALLOC_ALIGN (2*1024*1024)
|
||||
#define GRID_ALLOC_SMALL_LIMIT (4096)
|
||||
#pragma once
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
// Move control to configure.ac and Config.h?
|
||||
|
||||
class PointerCache {
|
||||
private:
|
||||
/*Pinning pages is costly*/
|
||||
/*Could maintain separate large and small allocation caches*/
|
||||
/* Could make these configurable, perhaps up to a max size*/
|
||||
static const int NcacheSmallMax=128;
|
||||
static const int NcacheMax=16;
|
||||
static int NcacheSmall;
|
||||
static int Ncache;
|
||||
|
||||
typedef struct {
|
||||
void *address;
|
||||
size_t bytes;
|
||||
int valid;
|
||||
} PointerCacheEntry;
|
||||
|
||||
static PointerCacheEntry Entries[NcacheMax];
|
||||
static int Victim;
|
||||
static PointerCacheEntry EntriesSmall[NcacheSmallMax];
|
||||
static int VictimSmall;
|
||||
|
||||
public:
|
||||
static void Init(void);
|
||||
static void *Insert(void *ptr,size_t bytes) ;
|
||||
static void *Insert(void *ptr,size_t bytes,PointerCacheEntry *entries,int ncache,int &victim) ;
|
||||
static void *Lookup(size_t bytes) ;
|
||||
static void *Lookup(size_t bytes,PointerCacheEntry *entries,int ncache) ;
|
||||
};
|
||||
|
||||
std::string sizeString(size_t bytes);
|
||||
|
||||
struct MemoryStats
|
||||
{
|
||||
size_t totalAllocated{0}, maxAllocated{0},
|
||||
currentlyAllocated{0}, totalFreed{0};
|
||||
};
|
||||
|
||||
class MemoryProfiler
|
||||
{
|
||||
public:
|
||||
static MemoryStats *stats;
|
||||
static bool debug;
|
||||
};
|
||||
|
||||
#ifdef GRID_NVCC
|
||||
#define profilerCudaMeminfo \
|
||||
{ size_t f, t ; cudaMemGetInfo ( &f,&t); std::cout << GridLogDebug << "[Memory debug] Cuda free "<<f<<"/"<<t << std::endl;}
|
||||
#else
|
||||
#define profilerCudaMeminfo
|
||||
#endif
|
||||
|
||||
#define memString(bytes) std::to_string(bytes) + " (" + sizeString(bytes) + ")"
|
||||
#define profilerDebugPrint \
|
||||
if (MemoryProfiler::stats) \
|
||||
{ \
|
||||
auto s = MemoryProfiler::stats; \
|
||||
std::cout << GridLogDebug << "[Memory debug] Stats " << MemoryProfiler::stats << std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] total : " << memString(s->totalAllocated) \
|
||||
<< std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] max : " << memString(s->maxAllocated) \
|
||||
<< std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] current: " << memString(s->currentlyAllocated) \
|
||||
<< std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] freed : " << memString(s->totalFreed) \
|
||||
<< std::endl; \
|
||||
} \
|
||||
profilerCudaMeminfo;
|
||||
|
||||
#define profilerAllocate(bytes) \
|
||||
if (MemoryProfiler::stats) \
|
||||
{ \
|
||||
auto s = MemoryProfiler::stats; \
|
||||
s->totalAllocated += (bytes); \
|
||||
s->currentlyAllocated += (bytes); \
|
||||
s->maxAllocated = std::max(s->maxAllocated, s->currentlyAllocated); \
|
||||
} \
|
||||
if (MemoryProfiler::debug) \
|
||||
{ \
|
||||
std::cout << GridLogDebug << "[Memory debug] allocating " << memString(bytes) << std::endl; \
|
||||
profilerDebugPrint; \
|
||||
}
|
||||
|
||||
#define profilerFree(bytes) \
|
||||
if (MemoryProfiler::stats) \
|
||||
{ \
|
||||
auto s = MemoryProfiler::stats; \
|
||||
s->totalFreed += (bytes); \
|
||||
s->currentlyAllocated -= (bytes); \
|
||||
} \
|
||||
if (MemoryProfiler::debug) \
|
||||
{ \
|
||||
std::cout << GridLogDebug << "[Memory debug] freeing " << memString(bytes) << std::endl; \
|
||||
profilerDebugPrint; \
|
||||
}
|
||||
|
||||
void check_huge_pages(void *Buf,uint64_t BYTES);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// A lattice of something, but assume the something is SIMDized.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename _Tp>
|
||||
class alignedAllocator {
|
||||
public:
|
||||
@@ -172,89 +53,170 @@ public:
|
||||
{
|
||||
size_type bytes = __n*sizeof(_Tp);
|
||||
profilerAllocate(bytes);
|
||||
|
||||
|
||||
#ifdef POINTER_CACHE
|
||||
_Tp *ptr = (_Tp *) PointerCache::Lookup(bytes);
|
||||
#else
|
||||
pointer ptr = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef GRID_NVCC
|
||||
////////////////////////////////////
|
||||
// Unified (managed) memory
|
||||
////////////////////////////////////
|
||||
if ( ptr == (_Tp *) NULL ) {
|
||||
// printf(" alignedAllocater cache miss %ld bytes ",bytes); BACKTRACEFP(stdout);
|
||||
auto err = cudaMallocManaged((void **)&ptr,bytes);
|
||||
if( err != cudaSuccess ) {
|
||||
ptr = (_Tp *) NULL;
|
||||
std::cerr << " cudaMallocManaged failed for " << bytes<<" bytes " <<cudaGetErrorString(err)<< std::endl;
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
assert( ptr != (_Tp *)NULL);
|
||||
#else
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 2MB align; could make option probably doesn't need configurability
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef HAVE_MM_MALLOC_H
|
||||
if ( ptr == (_Tp *) NULL ) ptr = (_Tp *) _mm_malloc(bytes,GRID_ALLOC_ALIGN);
|
||||
#else
|
||||
if ( ptr == (_Tp *) NULL ) ptr = (_Tp *) memalign(GRID_ALLOC_ALIGN,bytes);
|
||||
#endif
|
||||
assert( ptr != (_Tp *)NULL);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// First touch optimise in threaded loop
|
||||
//////////////////////////////////////////////////
|
||||
uint64_t *cp = (uint64_t *)ptr;
|
||||
thread_for(n,bytes/sizeof(uint64_t), { // need only one touch per page
|
||||
cp[n]=0;
|
||||
});
|
||||
#endif
|
||||
_Tp *ptr = (_Tp*) MemoryManager::CpuAllocate(bytes);
|
||||
if ( (_Tp*)ptr == (_Tp *) NULL ) {
|
||||
printf("Grid CPU Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
|
||||
}
|
||||
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer __p, size_type __n) {
|
||||
void deallocate(pointer __p, size_type __n)
|
||||
{
|
||||
size_type bytes = __n * sizeof(_Tp);
|
||||
|
||||
profilerFree(bytes);
|
||||
|
||||
#ifdef POINTER_CACHE
|
||||
pointer __freeme = (pointer)PointerCache::Insert((void *)__p,bytes);
|
||||
#else
|
||||
pointer __freeme = __p;
|
||||
#endif
|
||||
|
||||
#ifdef GRID_NVCC
|
||||
if ( __freeme ) cudaFree((void *)__freeme);
|
||||
#else
|
||||
#ifdef HAVE_MM_MALLOC_H
|
||||
if ( __freeme ) _mm_free((void *)__freeme);
|
||||
#else
|
||||
if ( __freeme ) free((void *)__freeme);
|
||||
#endif
|
||||
#endif
|
||||
MemoryManager::CpuFree((void *)__p,bytes);
|
||||
}
|
||||
|
||||
// FIXME: hack for the copy constructor, eventually it must be avoided
|
||||
void construct(pointer __p, const _Tp& __val) { new((void *)__p) _Tp(__val); };
|
||||
//void construct(pointer __p, const _Tp& __val) { };
|
||||
// FIXME: hack for the copy constructor: it must be avoided to avoid single thread loop
|
||||
void construct(pointer __p, const _Tp& __val) { };
|
||||
void construct(pointer __p) { };
|
||||
void destroy(pointer __p) { };
|
||||
};
|
||||
template<typename _Tp> inline bool operator==(const alignedAllocator<_Tp>&, const alignedAllocator<_Tp>&){ return true; }
|
||||
template<typename _Tp> inline bool operator!=(const alignedAllocator<_Tp>&, const alignedAllocator<_Tp>&){ return false; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Unified virtual memory
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
template<typename _Tp>
|
||||
class uvmAllocator {
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef _Tp* pointer;
|
||||
typedef const _Tp* const_pointer;
|
||||
typedef _Tp& reference;
|
||||
typedef const _Tp& const_reference;
|
||||
typedef _Tp value_type;
|
||||
|
||||
template<typename _Tp1> struct rebind { typedef uvmAllocator<_Tp1> other; };
|
||||
uvmAllocator() throw() { }
|
||||
uvmAllocator(const uvmAllocator&) throw() { }
|
||||
template<typename _Tp1> uvmAllocator(const uvmAllocator<_Tp1>&) throw() { }
|
||||
~uvmAllocator() throw() { }
|
||||
pointer address(reference __x) const { return &__x; }
|
||||
size_type max_size() const throw() { return size_t(-1) / sizeof(_Tp); }
|
||||
|
||||
pointer allocate(size_type __n, const void* _p= 0)
|
||||
{
|
||||
size_type bytes = __n*sizeof(_Tp);
|
||||
profilerAllocate(bytes);
|
||||
_Tp *ptr = (_Tp*) MemoryManager::SharedAllocate(bytes);
|
||||
if ( (_Tp*)ptr == (_Tp *) NULL ) {
|
||||
printf("Grid Shared Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
|
||||
}
|
||||
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer __p, size_type __n)
|
||||
{
|
||||
size_type bytes = __n * sizeof(_Tp);
|
||||
profilerFree(bytes);
|
||||
MemoryManager::SharedFree((void *)__p,bytes);
|
||||
}
|
||||
|
||||
void construct(pointer __p, const _Tp& __val) { new((void *)__p) _Tp(__val); };
|
||||
void construct(pointer __p) { };
|
||||
void destroy(pointer __p) { };
|
||||
};
|
||||
template<typename _Tp> inline bool operator==(const uvmAllocator<_Tp>&, const uvmAllocator<_Tp>&){ return true; }
|
||||
template<typename _Tp> inline bool operator!=(const uvmAllocator<_Tp>&, const uvmAllocator<_Tp>&){ return false; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Device memory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template<typename _Tp>
|
||||
class devAllocator {
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef _Tp* pointer;
|
||||
typedef const _Tp* const_pointer;
|
||||
typedef _Tp& reference;
|
||||
typedef const _Tp& const_reference;
|
||||
typedef _Tp value_type;
|
||||
|
||||
template<typename _Tp1> struct rebind { typedef devAllocator<_Tp1> other; };
|
||||
devAllocator() throw() { }
|
||||
devAllocator(const devAllocator&) throw() { }
|
||||
template<typename _Tp1> devAllocator(const devAllocator<_Tp1>&) throw() { }
|
||||
~devAllocator() throw() { }
|
||||
pointer address(reference __x) const { return &__x; }
|
||||
size_type max_size() const throw() { return size_t(-1) / sizeof(_Tp); }
|
||||
|
||||
pointer allocate(size_type __n, const void* _p= 0)
|
||||
{
|
||||
size_type bytes = __n*sizeof(_Tp);
|
||||
profilerAllocate(bytes);
|
||||
_Tp *ptr = (_Tp*) MemoryManager::AcceleratorAllocate(bytes);
|
||||
if ( (_Tp*)ptr == (_Tp *) NULL ) {
|
||||
printf("Grid Device Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
|
||||
}
|
||||
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer __p, size_type __n)
|
||||
{
|
||||
size_type bytes = __n * sizeof(_Tp);
|
||||
profilerFree(bytes);
|
||||
MemoryManager::AcceleratorFree((void *)__p,bytes);
|
||||
}
|
||||
void construct(pointer __p, const _Tp& __val) { };
|
||||
void construct(pointer __p) { };
|
||||
void destroy(pointer __p) { };
|
||||
};
|
||||
template<typename _Tp> inline bool operator==(const devAllocator<_Tp>&, const devAllocator<_Tp>&){ return true; }
|
||||
template<typename _Tp> inline bool operator!=(const devAllocator<_Tp>&, const devAllocator<_Tp>&){ return false; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Template typedefs
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template<class T> using commAllocator = alignedAllocator<T>;
|
||||
template<class T> using Vector = std::vector<T,alignedAllocator<T> >;
|
||||
template<class T> using commVector = std::vector<T,alignedAllocator<T> >;
|
||||
template<class T> using Matrix = std::vector<std::vector<T,alignedAllocator<T> > >;
|
||||
template<class T> using hostVector = std::vector<T,alignedAllocator<T> >; // Needs autoview
|
||||
template<class T> using Vector = std::vector<T,uvmAllocator<T> >; // Really want to deprecate
|
||||
template<class T> using uvmVector = std::vector<T,uvmAllocator<T> >; // auto migrating page
|
||||
template<class T> using deviceVector = std::vector<T,devAllocator<T> >; // device vector
|
||||
|
||||
/*
|
||||
template<class T> class vecView
|
||||
{
|
||||
protected:
|
||||
T * data;
|
||||
uint64_t size;
|
||||
ViewMode mode;
|
||||
void * cpu_ptr;
|
||||
public:
|
||||
// Rvalue accessor
|
||||
accelerator_inline T & operator[](size_t i) const { return this->data[i]; };
|
||||
vecView(Vector<T> &refer_to_me,ViewMode _mode)
|
||||
{
|
||||
cpu_ptr = &refer_to_me[0];
|
||||
size = refer_to_me.size();
|
||||
mode = _mode;
|
||||
data =(T *) MemoryManager::ViewOpen(cpu_ptr,
|
||||
size*sizeof(T),
|
||||
mode,
|
||||
AdviseDefault);
|
||||
}
|
||||
void ViewClose(void)
|
||||
{ // Inform the manager
|
||||
MemoryManager::ViewClose(this->cpu_ptr,this->mode);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> vecView<T> VectorView(Vector<T> &vec,ViewMode _mode)
|
||||
{
|
||||
vecView<T> ret(vec,_mode); // does the open
|
||||
return ret; // must be closed
|
||||
}
|
||||
|
||||
#define autoVecView(v_v,v,mode) \
|
||||
auto v_v = VectorView(v,mode); \
|
||||
ViewCloser<decltype(v_v)> _autoView##v_v(v_v);
|
||||
*/
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <Grid/allocator/MemoryStats.h>
|
||||
#include <Grid/allocator/MemoryManager.h>
|
||||
#include <Grid/allocator/AlignedAllocator.h>
|
||||
@@ -0,0 +1,362 @@
|
||||
#include <Grid/GridCore.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/*Allocation types, saying which pointer cache should be used*/
|
||||
#define Cpu (0)
|
||||
#define CpuHuge (1)
|
||||
#define CpuSmall (2)
|
||||
#define Acc (3)
|
||||
#define AccHuge (4)
|
||||
#define AccSmall (5)
|
||||
#define Shared (6)
|
||||
#define SharedHuge (7)
|
||||
#define SharedSmall (8)
|
||||
#undef GRID_MM_VERBOSE
|
||||
uint64_t total_shared;
|
||||
uint64_t total_device;
|
||||
uint64_t total_host;;
|
||||
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(leak_sanitizer)
|
||||
#define ASAN_LEAK_CHECK
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ASAN_LEAK_CHECK
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#include <sanitizer/common_interface_defs.h>
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#define LEAK_CHECK(A) { __lsan_do_recoverable_leak_check(); }
|
||||
#else
|
||||
#define LEAK_CHECK(A) { }
|
||||
#endif
|
||||
|
||||
void MemoryManager::DisplayMallinfo(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
struct mallinfo mi; // really want mallinfo2, but glibc version isn't uniform
|
||||
|
||||
mi = mallinfo();
|
||||
|
||||
std::cout << "MemoryManager: Total non-mmapped bytes (arena): "<< (size_t)mi.arena<<std::endl;
|
||||
std::cout << "MemoryManager: # of free chunks (ordblks): "<< (size_t)mi.ordblks<<std::endl;
|
||||
std::cout << "MemoryManager: # of free fastbin blocks (smblks): "<< (size_t)mi.smblks<<std::endl;
|
||||
std::cout << "MemoryManager: # of mapped regions (hblks): "<< (size_t)mi.hblks<<std::endl;
|
||||
std::cout << "MemoryManager: Bytes in mapped regions (hblkhd): "<< (size_t)mi.hblkhd<<std::endl;
|
||||
std::cout << "MemoryManager: Max. total allocated space (usmblks): "<< (size_t)mi.usmblks<<std::endl;
|
||||
std::cout << "MemoryManager: Free bytes held in fastbins (fsmblks): "<< (size_t)mi.fsmblks<<std::endl;
|
||||
std::cout << "MemoryManager: Total allocated space (uordblks): "<< (size_t)mi.uordblks<<std::endl;
|
||||
std::cout << "MemoryManager: Total free space (fordblks): "<< (size_t)mi.fordblks<<std::endl;
|
||||
std::cout << "MemoryManager: Topmost releasable block (keepcost): "<< (size_t)mi.keepcost<<std::endl;
|
||||
#endif
|
||||
LEAK_CHECK();
|
||||
|
||||
}
|
||||
|
||||
void MemoryManager::PrintBytes(void)
|
||||
{
|
||||
std::cout << " MemoryManager : ------------------------------------ "<<std::endl;
|
||||
std::cout << " MemoryManager : PrintBytes "<<std::endl;
|
||||
std::cout << " MemoryManager : ------------------------------------ "<<std::endl;
|
||||
std::cout << " MemoryManager : "<<(total_shared>>20)<<" shared Mbytes "<<std::endl;
|
||||
std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl;
|
||||
std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl;
|
||||
uint64_t cacheBytes;
|
||||
cacheBytes = CacheBytes[Cpu];
|
||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu cache Mbytes "<<std::endl;
|
||||
cacheBytes = CacheBytes[Acc];
|
||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc cache Mbytes "<<std::endl;
|
||||
cacheBytes = CacheBytes[Shared];
|
||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" shared cache Mbytes "<<std::endl;
|
||||
|
||||
#ifdef GRID_CUDA
|
||||
cuda_mem();
|
||||
#endif
|
||||
DisplayMallinfo();
|
||||
}
|
||||
|
||||
uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccHuge] + CacheBytes[AccSmall]; }
|
||||
uint64_t MemoryManager::HostCacheBytes() { return CacheBytes[Cpu] + CacheBytes[CpuHuge] + CacheBytes[CpuSmall]; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Data tables for recently freed pooiniter caches
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
MemoryManager::AllocationCacheEntry MemoryManager::Entries[MemoryManager::NallocType][MemoryManager::NallocCacheMax];
|
||||
int MemoryManager::Victim[MemoryManager::NallocType];
|
||||
int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 0, 8, 8, 0, 16, 8, 0, 16 };
|
||||
uint64_t MemoryManager::CacheBytes[MemoryManager::NallocType];
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Actual allocation and deallocation utils
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void *MemoryManager::AcceleratorAllocate(size_t bytes)
|
||||
{
|
||||
total_device+=bytes;
|
||||
void *ptr = (void *) Lookup(bytes,Acc);
|
||||
if ( ptr == (void *) NULL ) {
|
||||
ptr = (void *) acceleratorAllocDevice(bytes);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"AcceleratorAllocate "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
return ptr;
|
||||
}
|
||||
void MemoryManager::AcceleratorFree (void *ptr,size_t bytes)
|
||||
{
|
||||
total_device-=bytes;
|
||||
void *__freeme = Insert(ptr,bytes,Acc);
|
||||
if ( __freeme ) {
|
||||
acceleratorFreeDevice(__freeme);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"AcceleratorFree "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
}
|
||||
void *MemoryManager::SharedAllocate(size_t bytes)
|
||||
{
|
||||
total_shared+=bytes;
|
||||
void *ptr = (void *) Lookup(bytes,Shared);
|
||||
if ( ptr == (void *) NULL ) {
|
||||
ptr = (void *) acceleratorAllocShared(bytes);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"SharedAllocate "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
return ptr;
|
||||
}
|
||||
void MemoryManager::SharedFree (void *ptr,size_t bytes)
|
||||
{
|
||||
total_shared-=bytes;
|
||||
void *__freeme = Insert(ptr,bytes,Shared);
|
||||
if ( __freeme ) {
|
||||
acceleratorFreeShared(__freeme);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"SharedFree "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
}
|
||||
#ifdef GRID_UVM
|
||||
void *MemoryManager::CpuAllocate(size_t bytes)
|
||||
{
|
||||
total_host+=bytes;
|
||||
void *ptr = (void *) Lookup(bytes,Cpu);
|
||||
if ( ptr == (void *) NULL ) {
|
||||
ptr = (void *) acceleratorAllocShared(bytes);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"CpuAllocate "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
return ptr;
|
||||
}
|
||||
void MemoryManager::CpuFree (void *_ptr,size_t bytes)
|
||||
{
|
||||
total_host-=bytes;
|
||||
NotifyDeletion(_ptr);
|
||||
void *__freeme = Insert(_ptr,bytes,Cpu);
|
||||
if ( __freeme ) {
|
||||
acceleratorFreeShared(__freeme);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"CpuFree "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void *MemoryManager::CpuAllocate(size_t bytes)
|
||||
{
|
||||
total_host+=bytes;
|
||||
void *ptr = (void *) Lookup(bytes,Cpu);
|
||||
if ( ptr == (void *) NULL ) {
|
||||
ptr = (void *) acceleratorAllocCpu(bytes);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"CpuAllocate "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
return ptr;
|
||||
}
|
||||
void MemoryManager::CpuFree (void *_ptr,size_t bytes)
|
||||
{
|
||||
total_host-=bytes;
|
||||
NotifyDeletion(_ptr);
|
||||
void *__freeme = Insert(_ptr,bytes,Cpu);
|
||||
if ( __freeme ) {
|
||||
acceleratorFreeCpu(__freeme);
|
||||
}
|
||||
#ifdef GRID_MM_VERBOSE
|
||||
std::cout <<"CpuFree "<<std::endl;
|
||||
PrintBytes();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////
|
||||
// call only once
|
||||
//////////////////////////////////////////
|
||||
void MemoryManager::Init(void)
|
||||
{
|
||||
|
||||
char * str;
|
||||
int Nc;
|
||||
|
||||
str= getenv("GRID_ALLOC_NCACHE_LARGE");
|
||||
if ( str ) {
|
||||
Nc = atoi(str);
|
||||
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
||||
Ncache[Cpu]=Nc;
|
||||
Ncache[Acc]=Nc;
|
||||
Ncache[Shared]=Nc;
|
||||
}
|
||||
}
|
||||
|
||||
str= getenv("GRID_ALLOC_NCACHE_HUGE");
|
||||
if ( str ) {
|
||||
Nc = atoi(str);
|
||||
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
||||
Ncache[CpuHuge]=Nc;
|
||||
Ncache[AccHuge]=Nc;
|
||||
Ncache[SharedHuge]=Nc;
|
||||
}
|
||||
}
|
||||
|
||||
str= getenv("GRID_ALLOC_NCACHE_SMALL");
|
||||
if ( str ) {
|
||||
Nc = atoi(str);
|
||||
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
||||
Ncache[CpuSmall]=Nc;
|
||||
Ncache[AccSmall]=Nc;
|
||||
Ncache[SharedSmall]=Nc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MemoryManager::InitMessage(void) {
|
||||
|
||||
#ifndef GRID_UVM
|
||||
std::cout << GridLogMessage << "MemoryManager Cache "<< MemoryManager::DeviceMaxBytes <<" bytes "<<std::endl;
|
||||
#endif
|
||||
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() setting up"<<std::endl;
|
||||
#ifdef ALLOCATION_CACHE
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent host allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<" HUGE "<<Ncache[CpuHuge]<<std::endl;
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent device allocations: SMALL "<<Ncache[AccSmall]<<" LARGE "<<Ncache[Acc]<<" Huge "<<Ncache[AccHuge]<<std::endl;
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent shared allocations: SMALL "<<Ncache[SharedSmall]<<" LARGE "<<Ncache[Shared]<<" Huge "<<Ncache[SharedHuge]<<std::endl;
|
||||
#endif
|
||||
|
||||
#ifdef GRID_UVM
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Unified memory space"<<std::endl;
|
||||
#ifdef GRID_CUDA
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using cudaMallocManaged"<<std::endl;
|
||||
#endif
|
||||
#ifdef GRID_HIP
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using hipMallocManaged"<<std::endl;
|
||||
#endif
|
||||
#ifdef GRID_SYCL
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using SYCL malloc_shared"<<std::endl;
|
||||
#endif
|
||||
#else
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory"<<std::endl;
|
||||
#ifdef GRID_CUDA
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using cudaMalloc"<<std::endl;
|
||||
#endif
|
||||
#ifdef GRID_HIP
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using hipMalloc"<<std::endl;
|
||||
#endif
|
||||
#ifdef GRID_SYCL
|
||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using SYCL malloc_device"<<std::endl;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void *MemoryManager::Insert(void *ptr,size_t bytes,int type)
|
||||
{
|
||||
#ifdef ALLOCATION_CACHE
|
||||
int cache;
|
||||
if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2;
|
||||
else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1;
|
||||
else cache = type;
|
||||
|
||||
return Insert(ptr,bytes,Entries[cache],Ncache[cache],Victim[cache],CacheBytes[cache]);
|
||||
#else
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes)
|
||||
{
|
||||
#ifdef GRID_OMP
|
||||
GRID_ASSERT(omp_in_parallel()==0);
|
||||
#endif
|
||||
|
||||
if (ncache == 0) return ptr;
|
||||
|
||||
void * ret = NULL;
|
||||
int v = -1;
|
||||
|
||||
for(int e=0;e<ncache;e++) {
|
||||
if ( entries[e].valid==0 ) {
|
||||
v=e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( v==-1 ) {
|
||||
v=victim;
|
||||
victim = (victim+1)%ncache;
|
||||
}
|
||||
|
||||
if ( entries[v].valid ) {
|
||||
ret = entries[v].address;
|
||||
cacheBytes -= entries[v].bytes;
|
||||
entries[v].valid = 0;
|
||||
entries[v].address = NULL;
|
||||
entries[v].bytes = 0;
|
||||
}
|
||||
|
||||
entries[v].address=ptr;
|
||||
entries[v].bytes =bytes;
|
||||
entries[v].valid =1;
|
||||
cacheBytes += bytes;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *MemoryManager::Lookup(size_t bytes,int type)
|
||||
{
|
||||
#ifdef ALLOCATION_CACHE
|
||||
int cache;
|
||||
if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2;
|
||||
else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1;
|
||||
else cache = type;
|
||||
|
||||
return Lookup(bytes,Entries[cache],Ncache[cache],CacheBytes[cache]);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes)
|
||||
{
|
||||
#ifdef GRID_OMP
|
||||
GRID_ASSERT(omp_in_parallel()==0);
|
||||
#endif
|
||||
for(int e=0;e<ncache;e++){
|
||||
if ( entries[e].valid && ( entries[e].bytes == bytes ) ) {
|
||||
entries[e].valid = 0;
|
||||
cacheBytes -= entries[e].bytes;
|
||||
return entries[e].address;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/MemoryManager.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
// Move control to configure.ac and Config.h?
|
||||
|
||||
#define GRID_ALLOC_SMALL_LIMIT (4096)
|
||||
#define GRID_ALLOC_HUGE_LIMIT (2147483648)
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
#define FILE_LINE __FILE__ ":" TOSTRING(__LINE__)
|
||||
#define AUDIT(a) MemoryManager::Audit(FILE_LINE)
|
||||
|
||||
/*Pinning pages is costly*/
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Advise the LatticeAccelerator class
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
enum ViewAdvise {
|
||||
AdviseDefault = 0x0, // Regular data
|
||||
AdviseInfrequentUse = 0x1 // Advise that the data is used infrequently. This can
|
||||
// significantly influence performance of bulk storage.
|
||||
|
||||
// AdviseTransient = 0x2, // Data will mostly be read. On some architectures
|
||||
// enables read-only copies of memory to be kept on
|
||||
// host and device.
|
||||
|
||||
// AdviseAcceleratorWriteDiscard = 0x4 // Field will be written in entirety on device
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// View Access Mode
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
enum ViewMode {
|
||||
AcceleratorRead = 0x01,
|
||||
AcceleratorWrite = 0x02,
|
||||
AcceleratorWriteDiscard = 0x04,
|
||||
CpuRead = 0x08,
|
||||
CpuWrite = 0x10,
|
||||
CpuWriteDiscard = 0x10 // same for now
|
||||
};
|
||||
|
||||
struct MemoryStatus {
|
||||
uint64_t DeviceBytes;
|
||||
uint64_t DeviceLRUBytes;
|
||||
uint64_t DeviceMaxBytes;
|
||||
uint64_t HostToDeviceBytes;
|
||||
uint64_t DeviceToHostBytes;
|
||||
uint64_t HostToDeviceXfer;
|
||||
uint64_t DeviceToHostXfer;
|
||||
uint64_t DeviceEvictions;
|
||||
uint64_t DeviceDestroy;
|
||||
uint64_t DeviceAllocCacheBytes;
|
||||
uint64_t HostAllocCacheBytes;
|
||||
};
|
||||
|
||||
|
||||
class MemoryManager {
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// For caching recently freed allocations
|
||||
////////////////////////////////////////////////////////////
|
||||
typedef struct {
|
||||
void *address;
|
||||
size_t bytes;
|
||||
int valid;
|
||||
} AllocationCacheEntry;
|
||||
|
||||
static const int NallocCacheMax=128;
|
||||
static const int NallocType=9;
|
||||
static AllocationCacheEntry Entries[NallocType][NallocCacheMax];
|
||||
static int Victim[NallocType];
|
||||
static int Ncache[NallocType];
|
||||
static uint64_t CacheBytes[NallocType];
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Free pool
|
||||
/////////////////////////////////////////////////
|
||||
static void *Insert(void *ptr,size_t bytes,int type) ;
|
||||
static void *Lookup(size_t bytes,int type) ;
|
||||
static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim,uint64_t &cbytes) ;
|
||||
static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t &cbytes) ;
|
||||
|
||||
public:
|
||||
static void PrintBytes(void);
|
||||
static void Audit(std::string s);
|
||||
static void Init(void);
|
||||
static void InitMessage(void);
|
||||
static void *AcceleratorAllocate(size_t bytes);
|
||||
static void AcceleratorFree (void *ptr,size_t bytes);
|
||||
static void *SharedAllocate(size_t bytes);
|
||||
static void SharedFree (void *ptr,size_t bytes);
|
||||
static void *CpuAllocate(size_t bytes);
|
||||
static void CpuFree (void *ptr,size_t bytes);
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Footprint tracking
|
||||
////////////////////////////////////////////////////////
|
||||
static uint64_t DeviceBytes;
|
||||
static uint64_t DeviceLRUBytes;
|
||||
static uint64_t DeviceMaxBytes;
|
||||
static uint64_t HostToDeviceBytes;
|
||||
static uint64_t DeviceToHostBytes;
|
||||
static uint64_t HostToDeviceXfer;
|
||||
static uint64_t DeviceToHostXfer;
|
||||
static uint64_t DeviceEvictions;
|
||||
static uint64_t DeviceDestroy;
|
||||
|
||||
static uint64_t DeviceCacheBytes();
|
||||
static uint64_t HostCacheBytes();
|
||||
|
||||
static MemoryStatus GetFootprint(void) {
|
||||
MemoryStatus stat;
|
||||
stat.DeviceBytes = DeviceBytes;
|
||||
stat.DeviceLRUBytes = DeviceLRUBytes;
|
||||
stat.DeviceMaxBytes = DeviceMaxBytes;
|
||||
stat.HostToDeviceBytes = HostToDeviceBytes;
|
||||
stat.DeviceToHostBytes = DeviceToHostBytes;
|
||||
stat.HostToDeviceXfer = HostToDeviceXfer;
|
||||
stat.DeviceToHostXfer = DeviceToHostXfer;
|
||||
stat.DeviceEvictions = DeviceEvictions;
|
||||
stat.DeviceDestroy = DeviceDestroy;
|
||||
stat.DeviceAllocCacheBytes = DeviceCacheBytes();
|
||||
stat.HostAllocCacheBytes = HostCacheBytes();
|
||||
return stat;
|
||||
};
|
||||
|
||||
private:
|
||||
#ifndef GRID_UVM
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Data tables for ViewCache
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
typedef std::list<uint64_t> LRU_t;
|
||||
typedef typename LRU_t::iterator LRUiterator;
|
||||
typedef struct {
|
||||
int LRU_valid;
|
||||
LRUiterator LRU_entry;
|
||||
uint64_t CpuPtr;
|
||||
uint64_t AccPtr;
|
||||
size_t bytes;
|
||||
uint32_t transient;
|
||||
uint32_t state;
|
||||
uint32_t accLock;
|
||||
uint32_t cpuLock;
|
||||
} AcceleratorViewEntry;
|
||||
|
||||
typedef std::unordered_map<uint64_t,AcceleratorViewEntry> AccViewTable_t;
|
||||
typedef typename AccViewTable_t::iterator AccViewTableIterator ;
|
||||
|
||||
static AccViewTable_t AccViewTable;
|
||||
static LRU_t LRU;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Device motion
|
||||
/////////////////////////////////////////////////
|
||||
static void Create(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
||||
static void EvictVictims(uint64_t bytes); // Frees up <bytes>
|
||||
static void Evict(AcceleratorViewEntry &AccCache);
|
||||
static void Flush(AcceleratorViewEntry &AccCache);
|
||||
static void Clone(AcceleratorViewEntry &AccCache);
|
||||
static void AccDiscard(AcceleratorViewEntry &AccCache);
|
||||
static void CpuDiscard(AcceleratorViewEntry &AccCache);
|
||||
|
||||
// static void LRUupdate(AcceleratorViewEntry &AccCache);
|
||||
static void LRUinsert(AcceleratorViewEntry &AccCache);
|
||||
static void LRUremove(AcceleratorViewEntry &AccCache);
|
||||
|
||||
// manage entries in the table
|
||||
static int EntryPresent(uint64_t CpuPtr);
|
||||
static void EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
||||
static void EntryErase (uint64_t CpuPtr);
|
||||
static AccViewTableIterator EntryLookup(uint64_t CpuPtr);
|
||||
static void EntrySet (uint64_t CpuPtr,AcceleratorViewEntry &entry);
|
||||
|
||||
static void AcceleratorViewClose(uint64_t AccPtr);
|
||||
static uint64_t AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
||||
static void CpuViewClose(uint64_t Ptr);
|
||||
static uint64_t CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
||||
#endif
|
||||
|
||||
public:
|
||||
static void DisplayMallinfo(void);
|
||||
static void NotifyDeletion(void * CpuPtr);
|
||||
static void Print(void);
|
||||
static void PrintAll(void);
|
||||
static void PrintState( void* CpuPtr);
|
||||
static int isOpen (void* CpuPtr);
|
||||
static void ViewClose(void* CpuPtr,ViewMode mode);
|
||||
static void *ViewOpen (void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
|
||||
@@ -0,0 +1,606 @@
|
||||
#include <Grid/GridCore.h>
|
||||
#ifndef GRID_UVM
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
#define MAXLINE 512
|
||||
static char print_buffer [ MAXLINE ];
|
||||
|
||||
#define mprintf(...) snprintf (print_buffer,MAXLINE, __VA_ARGS__ ); std::cout << GridLogMemory << print_buffer << std::endl;
|
||||
#define dprintf(...) snprintf (print_buffer,MAXLINE, __VA_ARGS__ ); std::cout << GridLogDebug << print_buffer << std::endl;
|
||||
//#define dprintf(...)
|
||||
//#define mprintf(...)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// For caching copies of data on device
|
||||
////////////////////////////////////////////////////////////
|
||||
MemoryManager::AccViewTable_t MemoryManager::AccViewTable;
|
||||
MemoryManager::LRU_t MemoryManager::LRU;
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Footprint tracking
|
||||
////////////////////////////////////////////////////////
|
||||
uint64_t MemoryManager::DeviceBytes;
|
||||
uint64_t MemoryManager::DeviceLRUBytes;
|
||||
uint64_t MemoryManager::DeviceMaxBytes = 1024*1024*128;
|
||||
uint64_t MemoryManager::HostToDeviceBytes;
|
||||
uint64_t MemoryManager::DeviceToHostBytes;
|
||||
uint64_t MemoryManager::HostToDeviceXfer;
|
||||
uint64_t MemoryManager::DeviceToHostXfer;
|
||||
uint64_t MemoryManager::DeviceEvictions;
|
||||
uint64_t MemoryManager::DeviceDestroy;
|
||||
|
||||
////////////////////////////////////
|
||||
// Priority ordering for unlocked entries
|
||||
// Empty
|
||||
// CpuDirty
|
||||
// Consistent
|
||||
// AccDirty
|
||||
////////////////////////////////////
|
||||
#define Empty (0x0) /*Entry unoccupied */
|
||||
#define CpuDirty (0x1) /*CPU copy is golden, Acc buffer MAY not be allocated*/
|
||||
#define Consistent (0x2) /*ACC copy AND CPU copy are valid */
|
||||
#define AccDirty (0x4) /*ACC copy is golden */
|
||||
#define EvictNext (0x8) /*Priority for eviction*/
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Mechanics of data table maintenance
|
||||
/////////////////////////////////////////////////
|
||||
int MemoryManager::EntryPresent(uint64_t CpuPtr)
|
||||
{
|
||||
if(AccViewTable.empty()) return 0;
|
||||
|
||||
auto count = AccViewTable.count(CpuPtr); GRID_ASSERT((count==0)||(count==1));
|
||||
return count;
|
||||
}
|
||||
void MemoryManager::EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
||||
{
|
||||
GRID_ASSERT(!EntryPresent(CpuPtr));
|
||||
AcceleratorViewEntry AccCache;
|
||||
AccCache.CpuPtr = CpuPtr;
|
||||
AccCache.AccPtr = (uint64_t)NULL;
|
||||
AccCache.bytes = bytes;
|
||||
AccCache.state = CpuDirty;
|
||||
AccCache.LRU_valid=0;
|
||||
AccCache.transient=0;
|
||||
AccCache.accLock=0;
|
||||
AccCache.cpuLock=0;
|
||||
AccViewTable[CpuPtr] = AccCache;
|
||||
}
|
||||
MemoryManager::AccViewTableIterator MemoryManager::EntryLookup(uint64_t CpuPtr)
|
||||
{
|
||||
GRID_ASSERT(EntryPresent(CpuPtr));
|
||||
auto AccCacheIterator = AccViewTable.find(CpuPtr);
|
||||
GRID_ASSERT(AccCacheIterator!=AccViewTable.end());
|
||||
return AccCacheIterator;
|
||||
}
|
||||
void MemoryManager::EntryErase(uint64_t CpuPtr)
|
||||
{
|
||||
auto AccCache = EntryLookup(CpuPtr);
|
||||
AccViewTable.erase(CpuPtr);
|
||||
}
|
||||
void MemoryManager::LRUinsert(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
||||
if (AccCache.transient) {
|
||||
LRU.push_back(AccCache.CpuPtr);
|
||||
AccCache.LRU_entry = --LRU.end();
|
||||
} else {
|
||||
LRU.push_front(AccCache.CpuPtr);
|
||||
AccCache.LRU_entry = LRU.begin();
|
||||
}
|
||||
AccCache.LRU_valid = 1;
|
||||
DeviceLRUBytes+=AccCache.bytes;
|
||||
}
|
||||
void MemoryManager::LRUremove(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
GRID_ASSERT(AccCache.LRU_valid==1);
|
||||
LRU.erase(AccCache.LRU_entry);
|
||||
AccCache.LRU_valid = 0;
|
||||
DeviceLRUBytes-=AccCache.bytes;
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
// Accelerator cache motion & consistency logic
|
||||
/////////////////////////////////////////////////
|
||||
void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
///////////////////////////////////////////////////////////
|
||||
// Remove from Accelerator, remove entry, without flush
|
||||
// Cannot be locked. If allocated Must be in LRU pool.
|
||||
///////////////////////////////////////////////////////////
|
||||
GRID_ASSERT(AccCache.state!=Empty);
|
||||
|
||||
dprintf("MemoryManager: Discard(%lx) %lx",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr);
|
||||
GRID_ASSERT(AccCache.accLock==0);
|
||||
GRID_ASSERT(AccCache.cpuLock==0);
|
||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
||||
if(AccCache.AccPtr) {
|
||||
AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes);
|
||||
DeviceDestroy++;
|
||||
DeviceBytes -=AccCache.bytes;
|
||||
LRUremove(AccCache);
|
||||
AccCache.AccPtr=(uint64_t) NULL;
|
||||
dprintf("MemoryManager: Free(%lx) LRU %ld Total %ld",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes);
|
||||
}
|
||||
uint64_t CpuPtr = AccCache.CpuPtr;
|
||||
EntryErase(CpuPtr);
|
||||
}
|
||||
|
||||
void MemoryManager::Evict(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry
|
||||
// Cannot be acclocked. If allocated must be in LRU pool.
|
||||
//
|
||||
// Nov 2022... Felix issue: Allocating two CpuPtrs, can have an entry in LRU-q with CPUlock.
|
||||
// and require to evict the AccPtr copy. Eviction was a mistake in CpuViewOpen
|
||||
// but there is a weakness where CpuLock entries are attempted for erase
|
||||
// Take these OUT LRU queue when CPU locked?
|
||||
// Cannot take out the table as cpuLock data is important.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
GRID_ASSERT(AccCache.state!=Empty);
|
||||
|
||||
mprintf("MemoryManager: Evict CpuPtr %lx AccPtr %lx cpuLock %ld accLock %ld",
|
||||
(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr,
|
||||
(uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock);
|
||||
if (AccCache.accLock!=0) return;
|
||||
if (AccCache.cpuLock!=0) return;
|
||||
if(AccCache.state==AccDirty) {
|
||||
Flush(AccCache);
|
||||
}
|
||||
if(AccCache.AccPtr) {
|
||||
AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes);
|
||||
LRUremove(AccCache);
|
||||
AccCache.AccPtr=(uint64_t)NULL;
|
||||
AccCache.state=CpuDirty; // CPU primary now
|
||||
DeviceBytes -=AccCache.bytes;
|
||||
dprintf("MemoryManager: Free(AccPtr %lx) footprint now %ld ",(uint64_t)AccCache.AccPtr,DeviceBytes);
|
||||
}
|
||||
// uint64_t CpuPtr = AccCache.CpuPtr;
|
||||
DeviceEvictions++;
|
||||
// EntryErase(CpuPtr);
|
||||
}
|
||||
void MemoryManager::Flush(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
GRID_ASSERT(AccCache.state==AccDirty);
|
||||
GRID_ASSERT(AccCache.cpuLock==0);
|
||||
GRID_ASSERT(AccCache.accLock==0);
|
||||
GRID_ASSERT(AccCache.AccPtr!=(uint64_t)NULL);
|
||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
||||
acceleratorCopyFromDevice((void *)AccCache.AccPtr,(void *)AccCache.CpuPtr,AccCache.bytes);
|
||||
mprintf("MemoryManager: acceleratorCopyFromDevice Flush size %ld AccPtr %lx -> CpuPtr %lx",(uint64_t)AccCache.bytes,(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout);
|
||||
DeviceToHostBytes+=AccCache.bytes;
|
||||
DeviceToHostXfer++;
|
||||
AccCache.state=Consistent;
|
||||
}
|
||||
void MemoryManager::Clone(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
GRID_ASSERT(AccCache.state==CpuDirty);
|
||||
GRID_ASSERT(AccCache.cpuLock==0);
|
||||
GRID_ASSERT(AccCache.accLock==0);
|
||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
||||
if(AccCache.AccPtr==(uint64_t)NULL){
|
||||
AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes);
|
||||
DeviceBytes+=AccCache.bytes;
|
||||
}
|
||||
mprintf("MemoryManager: acceleratorCopyToDevice Clone size %ld AccPtr %lx <- CpuPtr %lx",
|
||||
(uint64_t)AccCache.bytes,
|
||||
(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout);
|
||||
acceleratorCopyToDevice((void *)AccCache.CpuPtr,(void *)AccCache.AccPtr,AccCache.bytes);
|
||||
HostToDeviceBytes+=AccCache.bytes;
|
||||
HostToDeviceXfer++;
|
||||
AccCache.state=Consistent;
|
||||
}
|
||||
|
||||
void MemoryManager::CpuDiscard(AcceleratorViewEntry &AccCache)
|
||||
{
|
||||
GRID_ASSERT(AccCache.state!=Empty);
|
||||
GRID_ASSERT(AccCache.cpuLock==0);
|
||||
GRID_ASSERT(AccCache.accLock==0);
|
||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
||||
if(AccCache.AccPtr==(uint64_t)NULL){
|
||||
AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes);
|
||||
DeviceBytes+=AccCache.bytes;
|
||||
}
|
||||
AccCache.state=AccDirty;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// View management
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
void MemoryManager::ViewClose(void* Ptr,ViewMode mode)
|
||||
{
|
||||
if( (mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard) ){
|
||||
dprintf("AcceleratorViewClose %lx",(uint64_t)Ptr);
|
||||
AcceleratorViewClose((uint64_t)Ptr);
|
||||
} else if( (mode==CpuRead)||(mode==CpuWrite)){
|
||||
CpuViewClose((uint64_t)Ptr);
|
||||
} else {
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
}
|
||||
void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
||||
{
|
||||
uint64_t CpuPtr = (uint64_t)_CpuPtr;
|
||||
if( (mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard) ){
|
||||
dprintf("AcceleratorViewOpen %lx",(uint64_t)CpuPtr);
|
||||
return (void *) AcceleratorViewOpen(CpuPtr,bytes,mode,hint);
|
||||
} else if( (mode==CpuRead)||(mode==CpuWrite)){
|
||||
return (void *)CpuViewOpen(CpuPtr,bytes,mode,hint);
|
||||
} else {
|
||||
GRID_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
void MemoryManager::EvictVictims(uint64_t bytes)
|
||||
{
|
||||
if(bytes>=DeviceMaxBytes) {
|
||||
printf("EvictVictims bytes %ld DeviceMaxBytes %ld\n",bytes,DeviceMaxBytes);
|
||||
}
|
||||
GRID_ASSERT(bytes<DeviceMaxBytes);
|
||||
while(bytes+DeviceLRUBytes > DeviceMaxBytes){
|
||||
if ( DeviceLRUBytes > 0){
|
||||
GRID_ASSERT(LRU.size()>0);
|
||||
uint64_t victim = LRU.back(); // From the LRU
|
||||
auto AccCacheIterator = EntryLookup(victim);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
Evict(AccCache);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Find if present, otherwise get or force an empty
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
if ( EntryPresent(CpuPtr)==0 ){
|
||||
EntryCreate(CpuPtr,bytes,mode,hint);
|
||||
}
|
||||
|
||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
if (!AccCache.AccPtr) {
|
||||
EvictVictims(bytes);
|
||||
}
|
||||
GRID_ASSERT((mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard));
|
||||
|
||||
GRID_ASSERT(AccCache.cpuLock==0); // Programming error
|
||||
|
||||
if(AccCache.state!=Empty) {
|
||||
dprintf("ViewOpen found entry %lx %lx : sizes %ld %ld accLock %ld",
|
||||
(uint64_t)AccCache.CpuPtr,
|
||||
(uint64_t)CpuPtr,
|
||||
(uint64_t)AccCache.bytes,
|
||||
(uint64_t)bytes,
|
||||
(uint64_t)AccCache.accLock);
|
||||
GRID_ASSERT(AccCache.CpuPtr == CpuPtr);
|
||||
GRID_ASSERT(AccCache.bytes ==bytes);
|
||||
}
|
||||
/*
|
||||
* State transitions and actions
|
||||
*
|
||||
* Action State StateNext Flush Clone
|
||||
*
|
||||
* AccRead Empty Consistent - Y
|
||||
* AccWrite Empty AccDirty - Y
|
||||
* AccRead CpuDirty Consistent - Y
|
||||
* AccWrite CpuDirty AccDirty - Y
|
||||
* AccRead Consistent Consistent - -
|
||||
* AccWrite Consistent AccDirty - -
|
||||
* AccRead AccDirty AccDirty - -
|
||||
* AccWrite AccDirty AccDirty - -
|
||||
*/
|
||||
if(AccCache.state==Empty) {
|
||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
||||
AccCache.CpuPtr = CpuPtr;
|
||||
AccCache.AccPtr = (uint64_t)NULL;
|
||||
AccCache.bytes = bytes;
|
||||
AccCache.state = CpuDirty; // Cpu starts primary
|
||||
if(mode==AcceleratorWriteDiscard){
|
||||
CpuDiscard(AccCache);
|
||||
AccCache.state = AccDirty; // Empty + AcceleratorWrite=> AccDirty
|
||||
} else if(mode==AcceleratorWrite){
|
||||
Clone(AccCache);
|
||||
AccCache.state = AccDirty; // Empty + AcceleratorWrite=> AccDirty
|
||||
} else {
|
||||
Clone(AccCache);
|
||||
AccCache.state = Consistent; // Empty + AccRead => Consistent
|
||||
}
|
||||
AccCache.accLock= 1;
|
||||
dprintf("Copied Empty entry into device accLock= %d",AccCache.accLock);
|
||||
} else if(AccCache.state==CpuDirty ){
|
||||
if(mode==AcceleratorWriteDiscard) {
|
||||
CpuDiscard(AccCache);
|
||||
AccCache.state = AccDirty; // CpuDirty + AcceleratorWrite=> AccDirty
|
||||
} else if(mode==AcceleratorWrite) {
|
||||
Clone(AccCache);
|
||||
AccCache.state = AccDirty; // CpuDirty + AcceleratorWrite=> AccDirty
|
||||
} else {
|
||||
Clone(AccCache);
|
||||
AccCache.state = Consistent; // CpuDirty + AccRead => Consistent
|
||||
}
|
||||
AccCache.accLock++;
|
||||
dprintf("CpuDirty entry into device ++accLock= %d",AccCache.accLock);
|
||||
} else if(AccCache.state==Consistent) {
|
||||
if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard))
|
||||
AccCache.state = AccDirty; // Consistent + AcceleratorWrite=> AccDirty
|
||||
else
|
||||
AccCache.state = Consistent; // Consistent + AccRead => Consistent
|
||||
AccCache.accLock++;
|
||||
dprintf("Consistent entry into device ++accLock= %d",AccCache.accLock);
|
||||
} else if(AccCache.state==AccDirty) {
|
||||
if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard))
|
||||
AccCache.state = AccDirty; // AccDirty + AcceleratorWrite=> AccDirty
|
||||
else
|
||||
AccCache.state = AccDirty; // AccDirty + AccRead => AccDirty
|
||||
AccCache.accLock++;
|
||||
dprintf("AccDirty entry ++accLock= %d",AccCache.accLock);
|
||||
} else {
|
||||
GRID_ASSERT(0);
|
||||
}
|
||||
|
||||
GRID_ASSERT(AccCache.accLock>0);
|
||||
// If view is opened on device must remove from LRU
|
||||
if(AccCache.LRU_valid==1){
|
||||
// must possibly remove from LRU as now locked on GPU
|
||||
dprintf("AccCache entry removed from LRU ");
|
||||
LRUremove(AccCache);
|
||||
}
|
||||
|
||||
int transient =hint;
|
||||
AccCache.transient= transient? EvictNext : 0;
|
||||
|
||||
return AccCache.AccPtr;
|
||||
}
|
||||
////////////////////////////////////
|
||||
// look up & decrement lock count
|
||||
////////////////////////////////////
|
||||
void MemoryManager::AcceleratorViewClose(uint64_t CpuPtr)
|
||||
{
|
||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
|
||||
GRID_ASSERT(AccCache.cpuLock==0);
|
||||
GRID_ASSERT(AccCache.accLock>0);
|
||||
|
||||
AccCache.accLock--;
|
||||
// Move to LRU queue if not locked and close on device
|
||||
if(AccCache.accLock==0) {
|
||||
dprintf("AccleratorViewClose %lx AccLock decremented to %ld move to LRU queue",(uint64_t)CpuPtr,(uint64_t)AccCache.accLock);
|
||||
LRUinsert(AccCache);
|
||||
} else {
|
||||
dprintf("AccleratorViewClose %lx AccLock decremented to %ld",(uint64_t)CpuPtr,(uint64_t)AccCache.accLock);
|
||||
}
|
||||
}
|
||||
void MemoryManager::CpuViewClose(uint64_t CpuPtr)
|
||||
{
|
||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
|
||||
GRID_ASSERT(AccCache.cpuLock>0);
|
||||
GRID_ASSERT(AccCache.accLock==0);
|
||||
|
||||
AccCache.cpuLock--;
|
||||
}
|
||||
/*
|
||||
* Action State StateNext Flush Clone
|
||||
*
|
||||
* CpuRead Empty CpuDirty - -
|
||||
* CpuWrite Empty CpuDirty - -
|
||||
* CpuRead CpuDirty CpuDirty - -
|
||||
* CpuWrite CpuDirty CpuDirty - -
|
||||
* CpuRead Consistent Consistent - -
|
||||
* CpuWrite Consistent CpuDirty - -
|
||||
* CpuRead AccDirty Consistent Y -
|
||||
* CpuWrite AccDirty CpuDirty Y -
|
||||
*/
|
||||
uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise transient)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Find if present, otherwise get or force an empty
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
if ( EntryPresent(CpuPtr)==0 ){
|
||||
EntryCreate(CpuPtr,bytes,mode,transient);
|
||||
}
|
||||
|
||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
|
||||
// CPU doesn't need to free space
|
||||
// if (!AccCache.AccPtr) {
|
||||
// EvictVictims(bytes);
|
||||
// }
|
||||
|
||||
GRID_ASSERT((mode==CpuRead)||(mode==CpuWrite));
|
||||
GRID_ASSERT(AccCache.accLock==0); // Programming error
|
||||
|
||||
if(AccCache.state!=Empty) {
|
||||
GRID_ASSERT(AccCache.CpuPtr == CpuPtr);
|
||||
GRID_ASSERT(AccCache.bytes==bytes);
|
||||
}
|
||||
|
||||
if(AccCache.state==Empty) {
|
||||
AccCache.CpuPtr = CpuPtr;
|
||||
AccCache.AccPtr = (uint64_t)NULL;
|
||||
AccCache.bytes = bytes;
|
||||
AccCache.state = CpuDirty; // Empty + CpuRead/CpuWrite => CpuDirty
|
||||
AccCache.accLock= 0;
|
||||
AccCache.cpuLock= 1;
|
||||
} else if(AccCache.state==CpuDirty ){
|
||||
// AccPtr dont care, deferred allocate
|
||||
AccCache.state = CpuDirty; // CpuDirty +CpuRead/CpuWrite => CpuDirty
|
||||
AccCache.cpuLock++;
|
||||
} else if(AccCache.state==Consistent) {
|
||||
GRID_ASSERT(AccCache.AccPtr != (uint64_t)NULL);
|
||||
if(mode==CpuWrite)
|
||||
AccCache.state = CpuDirty; // Consistent +CpuWrite => CpuDirty
|
||||
else
|
||||
AccCache.state = Consistent; // Consistent +CpuRead => Consistent
|
||||
AccCache.cpuLock++;
|
||||
} else if(AccCache.state==AccDirty) {
|
||||
GRID_ASSERT(AccCache.AccPtr != (uint64_t)NULL);
|
||||
Flush(AccCache);
|
||||
if(mode==CpuWrite) AccCache.state = CpuDirty; // AccDirty +CpuWrite => CpuDirty, Flush
|
||||
else AccCache.state = Consistent; // AccDirty +CpuRead => Consistent, Flush
|
||||
AccCache.cpuLock++;
|
||||
} else {
|
||||
GRID_ASSERT(0); // should be unreachable
|
||||
}
|
||||
|
||||
AccCache.transient= transient? EvictNext : 0;
|
||||
|
||||
return AccCache.CpuPtr;
|
||||
}
|
||||
void MemoryManager::NotifyDeletion(void *_ptr)
|
||||
{
|
||||
// Look up in ViewCache
|
||||
uint64_t ptr = (uint64_t)_ptr;
|
||||
if(EntryPresent(ptr)) {
|
||||
auto e = EntryLookup(ptr);
|
||||
AccDiscard(e->second);
|
||||
}
|
||||
}
|
||||
void MemoryManager::Print(void)
|
||||
{
|
||||
PrintBytes();
|
||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
||||
std::cout << GridLogMessage << "Memory Manager " << std::endl;
|
||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
||||
std::cout << GridLogMessage << DeviceBytes << " bytes allocated on device " << std::endl;
|
||||
std::cout << GridLogMessage << DeviceLRUBytes<< " bytes evictable on device " << std::endl;
|
||||
std::cout << GridLogMessage << DeviceMaxBytes<< " bytes max on device " << std::endl;
|
||||
std::cout << GridLogMessage << HostToDeviceXfer << " transfers to device " << std::endl;
|
||||
std::cout << GridLogMessage << DeviceToHostXfer << " transfers from device " << std::endl;
|
||||
std::cout << GridLogMessage << HostToDeviceBytes<< " bytes transfered to device " << std::endl;
|
||||
std::cout << GridLogMessage << DeviceToHostBytes<< " bytes transfered from device " << std::endl;
|
||||
std::cout << GridLogMessage << DeviceEvictions << " Evictions from device " << std::endl;
|
||||
std::cout << GridLogMessage << DeviceDestroy << " Destroyed vectors on device " << std::endl;
|
||||
std::cout << GridLogMessage << AccViewTable.size()<< " vectors " << LRU.size()<<" evictable"<< std::endl;
|
||||
acceleratorMem();
|
||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
||||
}
|
||||
void MemoryManager::PrintAll(void)
|
||||
{
|
||||
Print();
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
||||
std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl;
|
||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
||||
for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){
|
||||
auto &AccCache = it->second;
|
||||
|
||||
std::string str;
|
||||
if ( AccCache.state==Empty ) str = std::string("Empty");
|
||||
if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty");
|
||||
if ( AccCache.state==AccDirty ) str = std::string("AccDirty");
|
||||
if ( AccCache.state==Consistent)str = std::string("Consistent");
|
||||
|
||||
std::cout << GridLogMessage << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec
|
||||
<< "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
|
||||
<< "\t" << AccCache.cpuLock
|
||||
<< "\t" << AccCache.accLock
|
||||
<< "\t" << AccCache.LRU_valid<<std::endl;
|
||||
}
|
||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
||||
|
||||
};
|
||||
int MemoryManager::isOpen (void* _CpuPtr)
|
||||
{
|
||||
uint64_t CpuPtr = (uint64_t)_CpuPtr;
|
||||
if ( EntryPresent(CpuPtr) ){
|
||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
return AccCache.cpuLock+AccCache.accLock;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
void MemoryManager::Audit(std::string s)
|
||||
{
|
||||
uint64_t CpuBytes=0;
|
||||
uint64_t AccBytes=0;
|
||||
uint64_t LruBytes1=0;
|
||||
uint64_t LruBytes2=0;
|
||||
uint64_t LruCnt=0;
|
||||
|
||||
std::cout << " Memory Manager::Audit() from "<<s<<std::endl;
|
||||
for(auto it=LRU.begin();it!=LRU.end();it++){
|
||||
uint64_t cpuPtr = *it;
|
||||
GRID_ASSERT(EntryPresent(cpuPtr));
|
||||
auto AccCacheIterator = EntryLookup(cpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
LruBytes2+=AccCache.bytes;
|
||||
GRID_ASSERT(AccCache.LRU_valid==1);
|
||||
GRID_ASSERT(AccCache.LRU_entry==it);
|
||||
}
|
||||
std::cout << " Memory Manager::Audit() LRU queue matches table entries "<<std::endl;
|
||||
|
||||
for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){
|
||||
auto &AccCache = it->second;
|
||||
|
||||
std::string str;
|
||||
if ( AccCache.state==Empty ) str = std::string("Empty");
|
||||
if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty");
|
||||
if ( AccCache.state==AccDirty ) str = std::string("AccDirty");
|
||||
if ( AccCache.state==Consistent)str = std::string("Consistent");
|
||||
|
||||
CpuBytes+=AccCache.bytes;
|
||||
if( AccCache.AccPtr ) AccBytes+=AccCache.bytes;
|
||||
if( AccCache.LRU_valid ) LruBytes1+=AccCache.bytes;
|
||||
if( AccCache.LRU_valid ) LruCnt++;
|
||||
|
||||
if ( AccCache.cpuLock || AccCache.accLock ) {
|
||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
||||
|
||||
std::cout << GridLogError << s<< "\n\t 0x"<<std::hex<<AccCache.CpuPtr<<std::dec
|
||||
<< "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
|
||||
<< "\t cpuLock " << AccCache.cpuLock
|
||||
<< "\t accLock " << AccCache.accLock
|
||||
<< "\t LRUvalid " << AccCache.LRU_valid<<std::endl;
|
||||
}
|
||||
|
||||
GRID_ASSERT( AccCache.cpuLock== 0 ) ;
|
||||
GRID_ASSERT( AccCache.accLock== 0 ) ;
|
||||
}
|
||||
std::cout << " Memory Manager::Audit() no locked table entries "<<std::endl;
|
||||
GRID_ASSERT(LruBytes1==LruBytes2);
|
||||
GRID_ASSERT(LruBytes1==DeviceLRUBytes);
|
||||
std::cout << " Memory Manager::Audit() evictable bytes matches sum over table "<<std::endl;
|
||||
GRID_ASSERT(AccBytes==DeviceBytes);
|
||||
std::cout << " Memory Manager::Audit() device bytes matches sum over table "<<std::endl;
|
||||
GRID_ASSERT(LruCnt == LRU.size());
|
||||
std::cout << " Memory Manager::Audit() LRU entry count matches "<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
void MemoryManager::PrintState(void* _CpuPtr)
|
||||
{
|
||||
uint64_t CpuPtr = (uint64_t)_CpuPtr;
|
||||
|
||||
if ( EntryPresent(CpuPtr) ){
|
||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
||||
auto & AccCache = AccCacheIterator->second;
|
||||
std::string str;
|
||||
if ( AccCache.state==Empty ) str = std::string("Empty");
|
||||
if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty");
|
||||
if ( AccCache.state==AccDirty ) str = std::string("AccDirty");
|
||||
if ( AccCache.state==Consistent)str = std::string("Consistent");
|
||||
if ( AccCache.state==EvictNext) str = std::string("EvictNext");
|
||||
|
||||
std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl;
|
||||
std::cout << GridLogMessage << "\tx"<<std::hex<<AccCache.CpuPtr<<std::dec
|
||||
<< "\tx"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
|
||||
<< "\t" << AccCache.cpuLock
|
||||
<< "\t" << AccCache.accLock
|
||||
<< "\t" << AccCache.LRU_valid<<std::endl;
|
||||
|
||||
} else {
|
||||
std::cout << GridLogMessage << "No Entry in AccCache table." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <Grid/GridCore.h>
|
||||
#ifdef GRID_UVM
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// View management is 1:1 address space mapping
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
uint64_t MemoryManager::DeviceBytes;
|
||||
uint64_t MemoryManager::DeviceLRUBytes;
|
||||
uint64_t MemoryManager::DeviceMaxBytes = 1024*1024*128;
|
||||
uint64_t MemoryManager::HostToDeviceBytes;
|
||||
uint64_t MemoryManager::DeviceToHostBytes;
|
||||
uint64_t MemoryManager::HostToDeviceXfer;
|
||||
uint64_t MemoryManager::DeviceToHostXfer;
|
||||
uint64_t MemoryManager::DeviceEvictions;
|
||||
uint64_t MemoryManager::DeviceDestroy;
|
||||
|
||||
void MemoryManager::Audit(std::string s){};
|
||||
void MemoryManager::ViewClose(void* AccPtr,ViewMode mode){};
|
||||
void *MemoryManager::ViewOpen(void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint){ return CpuPtr; };
|
||||
int MemoryManager::isOpen (void* CpuPtr) { return 0;}
|
||||
void MemoryManager::PrintState(void* CpuPtr)
|
||||
{
|
||||
std::cout << GridLogMessage << "Host<->Device memory movement not currently managed by Grid." << std::endl;
|
||||
};
|
||||
void MemoryManager::Print(void){};
|
||||
void MemoryManager::PrintAll(void){};
|
||||
void MemoryManager::NotifyDeletion(void *ptr){};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <Grid/GridCore.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
MemoryStats *MemoryProfiler::stats = nullptr;
|
||||
bool MemoryProfiler::debug = false;
|
||||
|
||||
void check_huge_pages(void *Buf,uint64_t BYTES)
|
||||
{
|
||||
#ifdef __linux__
|
||||
int fd = open("/proc/self/pagemap", O_RDONLY);
|
||||
GRID_ASSERT(fd >= 0);
|
||||
const int page_size = 4096;
|
||||
uint64_t virt_pfn = (uint64_t)Buf / page_size;
|
||||
off_t offset = sizeof(uint64_t) * virt_pfn;
|
||||
uint64_t npages = (BYTES + page_size-1) / page_size;
|
||||
std::vector<uint64_t> pagedata(npages);
|
||||
uint64_t ret = lseek(fd, offset, SEEK_SET);
|
||||
GRID_ASSERT(ret == offset);
|
||||
ret = ::read(fd, &pagedata[0], sizeof(uint64_t)*npages);
|
||||
GRID_ASSERT(ret == sizeof(uint64_t) * npages);
|
||||
int nhugepages = npages / 512;
|
||||
int n4ktotal, nnothuge;
|
||||
n4ktotal = 0;
|
||||
nnothuge = 0;
|
||||
for (int i = 0; i < nhugepages; ++i) {
|
||||
uint64_t baseaddr = (pagedata[i*512] & 0x7fffffffffffffULL) * page_size;
|
||||
for (int j = 0; j < 512; ++j) {
|
||||
uint64_t pageaddr = (pagedata[i*512+j] & 0x7fffffffffffffULL) * page_size;
|
||||
++n4ktotal;
|
||||
if (pageaddr != baseaddr + j * page_size)
|
||||
++nnothuge;
|
||||
}
|
||||
}
|
||||
int rank = CartesianCommunicator::RankWorld();
|
||||
printf("rank %d Allocated %d 4k pages, %d not in huge pages\n", rank, n4ktotal, nnothuge);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string sizeString(const size_t bytes)
|
||||
{
|
||||
constexpr unsigned int bufSize = 256;
|
||||
const char *suffixes[7] = {"", "K", "M", "G", "T", "P", "E"};
|
||||
char buf[256];
|
||||
size_t s = 0;
|
||||
double count = bytes;
|
||||
|
||||
while (count >= 1024 && s < 7)
|
||||
{
|
||||
s++;
|
||||
count /= 1024;
|
||||
}
|
||||
if (count - floor(count) == 0.0)
|
||||
{
|
||||
snprintf(buf, bufSize, "%d %sB", (int)count, suffixes[s]);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, bufSize, "%.1f %sB", count, suffixes[s]);
|
||||
}
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/MemoryStats.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See the full license in the file "LICENSE" in the top level distribution directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
std::string sizeString(size_t bytes);
|
||||
|
||||
struct MemoryStats
|
||||
{
|
||||
size_t totalAllocated{0}, maxAllocated{0},
|
||||
currentlyAllocated{0}, totalFreed{0};
|
||||
};
|
||||
|
||||
class MemoryProfiler
|
||||
{
|
||||
public:
|
||||
static MemoryStats *stats;
|
||||
static bool debug;
|
||||
};
|
||||
|
||||
#define memString(bytes) std::to_string(bytes) + " (" + sizeString(bytes) + ")"
|
||||
#define profilerDebugPrint \
|
||||
if (MemoryProfiler::stats) \
|
||||
{ \
|
||||
auto s = MemoryProfiler::stats; \
|
||||
std::cout << GridLogDebug << "[Memory debug] Stats " << MemoryProfiler::stats << std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] total : " << memString(s->totalAllocated) \
|
||||
<< std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] max : " << memString(s->maxAllocated) \
|
||||
<< std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] current: " << memString(s->currentlyAllocated) \
|
||||
<< std::endl; \
|
||||
std::cout << GridLogDebug << "[Memory debug] freed : " << memString(s->totalFreed) \
|
||||
<< std::endl; \
|
||||
}
|
||||
|
||||
#define profilerAllocate(bytes) \
|
||||
if (MemoryProfiler::stats) \
|
||||
{ \
|
||||
auto s = MemoryProfiler::stats; \
|
||||
s->totalAllocated += (bytes); \
|
||||
s->currentlyAllocated += (bytes); \
|
||||
s->maxAllocated = std::max(s->maxAllocated, s->currentlyAllocated); \
|
||||
} \
|
||||
if (MemoryProfiler::debug) \
|
||||
{ \
|
||||
std::cout << GridLogDebug << "[Memory debug] allocating " << memString(bytes) << std::endl; \
|
||||
profilerDebugPrint; \
|
||||
}
|
||||
|
||||
#define profilerFree(bytes) \
|
||||
if (MemoryProfiler::stats) \
|
||||
{ \
|
||||
auto s = MemoryProfiler::stats; \
|
||||
s->totalFreed += (bytes); \
|
||||
s->currentlyAllocated -= (bytes); \
|
||||
} \
|
||||
if (MemoryProfiler::debug) \
|
||||
{ \
|
||||
std::cout << GridLogDebug << "[Memory debug] freeing " << memString(bytes) << std::endl; \
|
||||
profilerDebugPrint; \
|
||||
}
|
||||
|
||||
void check_huge_pages(void *Buf,uint64_t BYTES);
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
@@ -70,8 +70,8 @@ public:
|
||||
Coordinate _istride; // Inner stride i.e. within simd lane
|
||||
int _osites; // _isites*_osites = product(dimensions).
|
||||
int _isites;
|
||||
int _fsites; // _isites*_osites = product(dimensions).
|
||||
int _gsites;
|
||||
int64_t _fsites; // _isites*_osites = product(dimensions).
|
||||
int64_t _gsites;
|
||||
Coordinate _slice_block;// subslice information
|
||||
Coordinate _slice_stride;
|
||||
Coordinate _slice_nblock;
|
||||
@@ -81,6 +81,8 @@ public:
|
||||
|
||||
bool _isCheckerBoarded;
|
||||
int LocallyPeriodic;
|
||||
Coordinate _checker_dim_mask;
|
||||
int _checker_dim;
|
||||
|
||||
public:
|
||||
|
||||
@@ -88,7 +90,7 @@ public:
|
||||
// Checkerboarding interface is virtual and overridden by
|
||||
// GridCartesian / GridRedBlackCartesian
|
||||
////////////////////////////////////////////////////////////////
|
||||
virtual int CheckerBoarded(int dim)=0;
|
||||
virtual int CheckerBoarded(int dim) =0;
|
||||
virtual int CheckerBoard(const Coordinate &site)=0;
|
||||
virtual int CheckerBoardDestination(int source_cb,int shift,int dim)=0;
|
||||
virtual int CheckerBoardShift(int source_cb,int dim,int shift,int osite)=0;
|
||||
@@ -163,7 +165,7 @@ public:
|
||||
//
|
||||
if ( _simd_layout[dimension] > 2 ) {
|
||||
for(int d=0;d<_ndimension;d++){
|
||||
if ( d != dimension ) assert ( (_simd_layout[d]==1) );
|
||||
if ( d != dimension ) GRID_ASSERT ( (_simd_layout[d]==1) );
|
||||
}
|
||||
permute_type = RotateBit; // How to specify distance; this is not just direction.
|
||||
return permute_type;
|
||||
@@ -182,10 +184,10 @@ public:
|
||||
inline int Nsimd(void) const { return _isites; };// Synonymous with iSites
|
||||
inline int oSites(void) const { return _osites; };
|
||||
inline int lSites(void) const { return _isites*_osites; };
|
||||
inline int gSites(void) const { return _isites*_osites*_Nprocessors; };
|
||||
inline int64_t gSites(void) const { return (int64_t)_isites*(int64_t)_osites*(int64_t)_Nprocessors; };
|
||||
inline int Nd (void) const { return _ndimension;};
|
||||
|
||||
inline const Coordinate LocalStarts(void) { return _lstart; };
|
||||
inline const Coordinate &LocalStarts(void) { return _lstart; };
|
||||
inline const Coordinate &FullDimensions(void) { return _fdimensions;};
|
||||
inline const Coordinate &GlobalDimensions(void) { return _gdimensions;};
|
||||
inline const Coordinate &LocalDimensions(void) { return _ldimensions;};
|
||||
@@ -213,15 +215,15 @@ public:
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Global addressing
|
||||
////////////////////////////////////////////////////////////////
|
||||
void GlobalIndexToGlobalCoor(int gidx,Coordinate &gcoor){
|
||||
assert(gidx< gSites());
|
||||
void GlobalIndexToGlobalCoor(int64_t gidx,Coordinate &gcoor){
|
||||
GRID_ASSERT(gidx< gSites());
|
||||
Lexicographic::CoorFromIndex(gcoor,gidx,_gdimensions);
|
||||
}
|
||||
void LocalIndexToLocalCoor(int lidx,Coordinate &lcoor){
|
||||
assert(lidx<lSites());
|
||||
GRID_ASSERT(lidx<lSites());
|
||||
Lexicographic::CoorFromIndex(lcoor,lidx,_ldimensions);
|
||||
}
|
||||
void GlobalCoorToGlobalIndex(const Coordinate & gcoor,int & gidx){
|
||||
void GlobalCoorToGlobalIndex(const Coordinate & gcoor,int64_t & gidx){
|
||||
gidx=0;
|
||||
int mult=1;
|
||||
for(int mu=0;mu<_ndimension;mu++) {
|
||||
|
||||
@@ -38,6 +38,7 @@ class GridCartesian: public GridBase {
|
||||
|
||||
public:
|
||||
int dummy;
|
||||
// Coordinate _checker_dim_mask;
|
||||
virtual int CheckerBoardFromOindexTable (int Oindex) {
|
||||
return 0;
|
||||
}
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int CheckerBoarded(int dim){
|
||||
virtual int CheckerBoarded(int dim) {
|
||||
return 0;
|
||||
}
|
||||
virtual int CheckerBoard(const Coordinate &site){
|
||||
@@ -104,6 +105,8 @@ public:
|
||||
_ldimensions.resize(_ndimension);
|
||||
_rdimensions.resize(_ndimension);
|
||||
_simd_layout.resize(_ndimension);
|
||||
_checker_dim_mask.resize(_ndimension);;
|
||||
_checker_dim = -1;
|
||||
_lstart.resize(_ndimension);
|
||||
_lend.resize(_ndimension);
|
||||
|
||||
@@ -114,6 +117,8 @@ public:
|
||||
|
||||
for (int d = 0; d < _ndimension; d++)
|
||||
{
|
||||
_checker_dim_mask[d]=0;
|
||||
|
||||
_fdimensions[d] = dimensions[d]; // Global dimensions
|
||||
_gdimensions[d] = _fdimensions[d]; // Global dimensions
|
||||
_simd_layout[d] = simd_layout[d];
|
||||
@@ -123,10 +128,10 @@ public:
|
||||
// Use a reduced simd grid
|
||||
_ldimensions[d] = _gdimensions[d] / _processors[d]; //local dimensions
|
||||
//std::cout << _ldimensions[d] << " " << _gdimensions[d] << " " << _processors[d] << std::endl;
|
||||
assert(_ldimensions[d] * _processors[d] == _gdimensions[d]);
|
||||
GRID_ASSERT(_ldimensions[d] * _processors[d] == _gdimensions[d]);
|
||||
|
||||
_rdimensions[d] = _ldimensions[d] / _simd_layout[d]; //overdecomposition
|
||||
assert(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]);
|
||||
GRID_ASSERT(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]);
|
||||
|
||||
_lstart[d] = _processor_coor[d] * _ldimensions[d];
|
||||
_lend[d] = _processor_coor[d] * _ldimensions[d] + _ldimensions[d] - 1;
|
||||
|
||||
@@ -35,22 +35,39 @@ static const int CbRed =0;
|
||||
static const int CbBlack=1;
|
||||
static const int Even =CbRed;
|
||||
static const int Odd =CbBlack;
|
||||
|
||||
accelerator_inline int RedBlackCheckerBoardFromOindex (int oindex,const Coordinate &rdim,const Coordinate &chk_dim_msk)
|
||||
{
|
||||
int nd=rdim.size();
|
||||
Coordinate coor(nd);
|
||||
|
||||
Lexicographic::CoorFromIndex(coor,oindex,rdim);
|
||||
|
||||
int linear=0;
|
||||
for(int d=0;d<nd;d++){
|
||||
if(chk_dim_msk[d])
|
||||
linear=linear+coor[d];
|
||||
}
|
||||
return (linear&0x1);
|
||||
}
|
||||
|
||||
|
||||
// Specialise this for red black grids storing half the data like a chess board.
|
||||
class GridRedBlackCartesian : public GridBase
|
||||
{
|
||||
public:
|
||||
Coordinate _checker_dim_mask;
|
||||
int _checker_dim;
|
||||
// Coordinate _checker_dim_mask;
|
||||
// int _checker_dim;
|
||||
std::vector<int> _checker_board;
|
||||
|
||||
virtual int isCheckerBoarded(void) const { return 1; };
|
||||
virtual int CheckerBoarded(int dim){
|
||||
if( dim==_checker_dim) return 1;
|
||||
else return 0;
|
||||
}
|
||||
virtual int CheckerBoard(const Coordinate &site){
|
||||
int linear=0;
|
||||
assert(site.size()==_ndimension);
|
||||
GRID_ASSERT(site.size()==_ndimension);
|
||||
for(int d=0;d<_ndimension;d++){
|
||||
if(_checker_dim_mask[d])
|
||||
linear=linear+site[d];
|
||||
@@ -131,7 +148,7 @@ public:
|
||||
{
|
||||
Init(base->_fdimensions,base->_simd_layout,base->_processors,checker_dim_mask,checker_dim) ;
|
||||
}
|
||||
|
||||
|
||||
virtual ~GridRedBlackCartesian() = default;
|
||||
|
||||
void Init(const Coordinate &dimensions,
|
||||
@@ -143,11 +160,11 @@ public:
|
||||
|
||||
_isCheckerBoarded = true;
|
||||
_checker_dim = checker_dim;
|
||||
assert(checker_dim_mask[checker_dim] == 1);
|
||||
GRID_ASSERT(checker_dim_mask[checker_dim] == 1);
|
||||
_ndimension = dimensions.size();
|
||||
assert(checker_dim_mask.size() == _ndimension);
|
||||
assert(processor_grid.size() == _ndimension);
|
||||
assert(simd_layout.size() == _ndimension);
|
||||
GRID_ASSERT(checker_dim_mask.size() == _ndimension);
|
||||
GRID_ASSERT(processor_grid.size() == _ndimension);
|
||||
GRID_ASSERT(simd_layout.size() == _ndimension);
|
||||
|
||||
_fdimensions.resize(_ndimension);
|
||||
_gdimensions.resize(_ndimension);
|
||||
@@ -173,20 +190,20 @@ public:
|
||||
|
||||
if (d == _checker_dim)
|
||||
{
|
||||
assert((_gdimensions[d] & 0x1) == 0);
|
||||
GRID_ASSERT((_gdimensions[d] & 0x1) == 0);
|
||||
_gdimensions[d] = _gdimensions[d] / 2; // Remove a checkerboard
|
||||
_gsites /= 2;
|
||||
}
|
||||
_ldimensions[d] = _gdimensions[d] / _processors[d];
|
||||
assert(_ldimensions[d] * _processors[d] == _gdimensions[d]);
|
||||
GRID_ASSERT(_ldimensions[d] * _processors[d] == _gdimensions[d]);
|
||||
_lstart[d] = _processor_coor[d] * _ldimensions[d];
|
||||
_lend[d] = _processor_coor[d] * _ldimensions[d] + _ldimensions[d] - 1;
|
||||
|
||||
// Use a reduced simd grid
|
||||
_simd_layout[d] = simd_layout[d];
|
||||
_rdimensions[d] = _ldimensions[d] / _simd_layout[d]; // this is not checking if this is integer
|
||||
assert(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]);
|
||||
assert(_rdimensions[d] > 0);
|
||||
GRID_ASSERT(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]);
|
||||
GRID_ASSERT(_rdimensions[d] > 0);
|
||||
|
||||
// all elements of a simd vector must have same checkerboard.
|
||||
// If Ls vectorised, this must still be the case; e.g. dwf rb5d
|
||||
|
||||
@@ -33,6 +33,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
bool Stencil_force_mpi = true;
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Info that is setup once and indept of cartesian layout
|
||||
///////////////////////////////////////////////////////////////
|
||||
@@ -55,18 +57,29 @@ int CartesianCommunicator::ProcessorCount(void) { return
|
||||
// very VERY rarely (Log, serial RNG) we need world without a grid
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef USE_GRID_REDUCTION
|
||||
void CartesianCommunicator::GlobalSum(ComplexF &c)
|
||||
{
|
||||
GlobalSumP2P(c);
|
||||
}
|
||||
void CartesianCommunicator::GlobalSum(ComplexD &c)
|
||||
{
|
||||
GlobalSumP2P(c);
|
||||
}
|
||||
#else
|
||||
void CartesianCommunicator::GlobalSum(ComplexF &c)
|
||||
{
|
||||
GlobalSumVector((float *)&c,2);
|
||||
}
|
||||
void CartesianCommunicator::GlobalSumVector(ComplexF *c,int N)
|
||||
{
|
||||
GlobalSumVector((float *)c,2*N);
|
||||
}
|
||||
void CartesianCommunicator::GlobalSum(ComplexD &c)
|
||||
{
|
||||
GlobalSumVector((double *)&c,2);
|
||||
}
|
||||
#endif
|
||||
void CartesianCommunicator::GlobalSumVector(ComplexF *c,int N)
|
||||
{
|
||||
GlobalSumVector((float *)c,2*N);
|
||||
}
|
||||
void CartesianCommunicator::GlobalSumVector(ComplexD *c,int N)
|
||||
{
|
||||
GlobalSumVector((double *)c,2*N);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
@@ -34,8 +33,12 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
///////////////////////////////////
|
||||
#include <Grid/communicator/SharedMemory.h>
|
||||
|
||||
#define NVLINK_GET
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
extern bool Stencil_force_mpi ;
|
||||
|
||||
class CartesianCommunicator : public SharedMemory {
|
||||
|
||||
public:
|
||||
@@ -52,10 +55,11 @@ public:
|
||||
// Communicator should know nothing of the physics grid, only processor grid.
|
||||
////////////////////////////////////////////
|
||||
int _Nprocessors; // How many in all
|
||||
Coordinate _processors; // Which dimensions get relayed out over processors lanes.
|
||||
int _processor; // linear processor rank
|
||||
Coordinate _processor_coor; // linear processor coordinate
|
||||
unsigned long _ndimension;
|
||||
Coordinate _shm_processors; // Which dimensions get relayed out over processors lanes.
|
||||
Coordinate _processors; // Which dimensions get relayed out over processors lanes.
|
||||
Coordinate _processor_coor; // linear processor coordinate
|
||||
static Grid_MPI_Comm communicator_world;
|
||||
Grid_MPI_Comm communicator;
|
||||
std::vector<Grid_MPI_Comm> communicator_halo;
|
||||
@@ -96,18 +100,22 @@ public:
|
||||
int BossRank(void) ;
|
||||
int ThisRank(void) ;
|
||||
const Coordinate & ThisProcessorCoor(void) ;
|
||||
const Coordinate & ShmGrid(void) { return _shm_processors; } ;
|
||||
const Coordinate & ProcessorGrid(void) ;
|
||||
int ProcessorCount(void) ;
|
||||
int ProcessorCount(void) ;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// very VERY rarely (Log, serial RNG) we need world without a grid
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static int RankWorld(void) ;
|
||||
static void BroadcastWorld(int root,void* data, int bytes);
|
||||
static void BroadcastWorld(int root,void* data, uint64_t bytes);
|
||||
static void BarrierWorld(void);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Reduction
|
||||
////////////////////////////////////////////////////////////
|
||||
void GlobalMax(RealD &);
|
||||
void GlobalMax(RealF &);
|
||||
void GlobalSum(RealF &);
|
||||
void GlobalSumVector(RealF *,int N);
|
||||
void GlobalSum(RealD &);
|
||||
@@ -121,50 +129,84 @@ public:
|
||||
void GlobalSumVector(ComplexD *c,int N);
|
||||
void GlobalXOR(uint32_t &);
|
||||
void GlobalXOR(uint64_t &);
|
||||
|
||||
|
||||
template<class obj> void GlobalSumP2P(obj &o)
|
||||
{
|
||||
std::vector<obj> column;
|
||||
obj accum = o;
|
||||
int source,dest;
|
||||
for(int d=0;d<_ndimension;d++){
|
||||
column.resize(_processors[d]);
|
||||
column[0] = accum;
|
||||
std::vector<MpiCommsRequest_t> list;
|
||||
for(int p=1;p<_processors[d];p++){
|
||||
ShiftedRanks(d,p,source,dest);
|
||||
SendToRecvFromBegin(list,
|
||||
&column[0],
|
||||
dest,
|
||||
&column[p],
|
||||
source,
|
||||
sizeof(obj),d*100+p);
|
||||
|
||||
}
|
||||
if (!list.empty()) // avoid triggering GRID_ASSERT in comms == none
|
||||
CommsComplete(list);
|
||||
for(int p=1;p<_processors[d];p++){
|
||||
accum = accum + column[p];
|
||||
}
|
||||
}
|
||||
Broadcast(0,accum);
|
||||
o=accum;
|
||||
}
|
||||
|
||||
template<class obj> void GlobalSum(obj &o){
|
||||
typedef typename obj::scalar_type scalar_type;
|
||||
int words = sizeof(obj)/sizeof(scalar_type);
|
||||
scalar_type * ptr = (scalar_type *)& o;
|
||||
scalar_type * ptr = (scalar_type *)& o; // Safe alias
|
||||
GlobalSumVector(ptr,words);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Face exchange, buffer swap in translational invariant way
|
||||
////////////////////////////////////////////////////////////
|
||||
void CommsComplete(std::vector<MpiCommsRequest_t> &list);
|
||||
void SendToRecvFromBegin(std::vector<MpiCommsRequest_t> &list,
|
||||
void *xmit,
|
||||
int dest,
|
||||
void *recv,
|
||||
int from,
|
||||
uint64_t bytes,int dir);
|
||||
|
||||
void SendToRecvFrom(void *xmit,
|
||||
int xmit_to_rank,
|
||||
void *recv,
|
||||
int recv_from_rank,
|
||||
int bytes);
|
||||
uint64_t bytes);
|
||||
|
||||
void SendRecvPacket(void *xmit,
|
||||
void *recv,
|
||||
int xmit_to_rank,
|
||||
int recv_from_rank,
|
||||
int bytes);
|
||||
|
||||
void SendToRecvFromBegin(std::vector<CommsRequest_t> &list,
|
||||
void *xmit,
|
||||
int xmit_to_rank,
|
||||
void *recv,
|
||||
int recv_from_rank,
|
||||
int bytes);
|
||||
|
||||
void SendToRecvFromComplete(std::vector<CommsRequest_t> &waitall);
|
||||
|
||||
int IsOffNode(int rank);
|
||||
double StencilSendToRecvFrom(void *xmit,
|
||||
int xmit_to_rank,
|
||||
int xmit_to_rank,int do_xmit,
|
||||
void *recv,
|
||||
int recv_from_rank,
|
||||
int bytes,int dir);
|
||||
int recv_from_rank,int do_recv,
|
||||
uint64_t bytes,int dir);
|
||||
|
||||
double StencilSendToRecvFromPrepare(std::vector<CommsRequest_t> &list,
|
||||
void *xmit,
|
||||
int xmit_to_rank,int do_xmit,
|
||||
void *recv,
|
||||
int recv_from_rank,int do_recv,
|
||||
uint64_t xbytes,uint64_t rbytes,int dir);
|
||||
|
||||
// Could do a PollHtoD and have a CommsMerge dependence
|
||||
void StencilSendToRecvFromPollDtoH (std::vector<CommsRequest_t> &list);
|
||||
void StencilSendToRecvFromPollIRecv(std::vector<CommsRequest_t> &list);
|
||||
|
||||
double StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list,
|
||||
void *xmit,
|
||||
int xmit_to_rank,
|
||||
void *recv,
|
||||
int recv_from_rank,
|
||||
int bytes,int dir);
|
||||
void *xmit,void *xmit_comp,
|
||||
int xmit_to_rank,int do_xmit,
|
||||
void *recv,void *recv_comp,
|
||||
int recv_from_rank,int do_recv,
|
||||
uint64_t xbytes,uint64_t rbytes,int dir);
|
||||
|
||||
|
||||
void StencilSendToRecvFromComplete(std::vector<CommsRequest_t> &waitall,int i);
|
||||
@@ -178,20 +220,20 @@ public:
|
||||
////////////////////////////////////////////////////////////
|
||||
// Broadcast a buffer and composite larger
|
||||
////////////////////////////////////////////////////////////
|
||||
void Broadcast(int root,void* data, int bytes);
|
||||
void Broadcast(int root,void* data, uint64_t bytes);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// All2All down one dimension
|
||||
////////////////////////////////////////////////////////////
|
||||
template<class T> void AllToAll(int dim,std::vector<T> &in, std::vector<T> &out){
|
||||
assert(dim>=0);
|
||||
assert(dim<_ndimension);
|
||||
assert(in.size()==out.size());
|
||||
GRID_ASSERT(dim>=0);
|
||||
GRID_ASSERT(dim<_ndimension);
|
||||
GRID_ASSERT(in.size()==out.size());
|
||||
int numnode = _processors[dim];
|
||||
uint64_t bytes=sizeof(T);
|
||||
uint64_t words=in.size()/numnode;
|
||||
assert(numnode * words == in.size());
|
||||
assert(words < (1ULL<<31));
|
||||
GRID_ASSERT(numnode * words == in.size());
|
||||
GRID_ASSERT(words < (1ULL<<31));
|
||||
AllToAll(dim,(void *)&in[0],(void *)&out[0],words,bytes);
|
||||
}
|
||||
void AllToAll(int dim ,void *in,void *out,uint64_t words,uint64_t bytes);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user