diff --git a/.gitea/workflows/julia-package-ci.yml b/.gitea/workflows/julia-package-ci.yml index 10cf859..b7887e6 100644 --- a/.gitea/workflows/julia-package-ci.yml +++ b/.gitea/workflows/julia-package-ci.yml @@ -22,7 +22,9 @@ jobs: version: '1.9.2' - name: Instantiate - run: julia --project=./ -e 'using Pkg; Pkg.instantiate()' + run: | + julia --project=./ -e 'using Pkg; Pkg.instantiate()' + julia --project=./ -e 'using Pkg; Pkg.add(url="https://github.com/QEDjl-project/QEDprocesses.jl/")' - name: Format check run: | @@ -32,7 +34,7 @@ jobs: if out == "" exit(0) else - @error "Some files have not been formatted !!!" + @error "Some files have not been formatted!!!" write(stdout, out) exit(1) end' diff --git a/Project.toml b/Project.toml index f1957b2..722043e 100644 --- a/Project.toml +++ b/Project.toml @@ -15,6 +15,7 @@ QEDbase = "10e22c08-3ccb-4172-bfcf-7d7aa3d04d93" QEDprocesses = "46de9c38-1bb3-4547-a1ec-da24d767fdad" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [extras] diff --git a/data/qed_ke-kke_reduction_optimizer.csv b/data/qed_ke-kke_reduction_optimizer.csv new file mode 100644 index 0000000..794ca46 --- /dev/null +++ b/data/qed_ke-kke_reduction_optimizer.csv @@ -0,0 +1,16 @@ +operations,graph_nodes,graph_edges,graph_ce,graph_dt,graph_ci,gen_func_t,cpu_compile_t,cpu_st_t,cpu_mt_t,gpu_compile_t,gpu_t +0,77,101,252.0,6240.0,0.04038461538461539,0.02087051,8.691e-6,3.405098066,0.244763721,1.565749515,0.936213163 +1,76,99,246.0,6240.0,0.03942307692307692,0.020658734,9.36e-6,3.244313848,0.230460257,1.548012602,0.887605389 +2,75,97,240.0,6240.0,0.038461538461538464,0.045333482,8.74e-6,3.163679857,0.217614064,1.52780456,0.816496837 +3,74,95,234.0,6240.0,0.0375,0.020314034,9.081e-6,2.956421016,0.183415997,1.524262179,0.793770075 +4,73,93,228.0,6240.0,0.03653846153846154,0.033579409,8.52e-6,2.845414866,0.19168374,1.50907807,0.742734411 +5,72,92,228.0,6144.0,0.037109375,0.019736718,8.87e-6,2.827109937,0.207452606,1.497203204,0.719774022 +6,71,90,222.0,6144.0,0.0361328125,0.043612693,1.01e-5,2.62776692,0.166492497,1.602060948,0.668929854 +7,70,89,222.0,6048.0,0.03670634920634921,0.042731148,1.053e-5,2.631288029,0.185812224,1.514154792,0.694503947 +8,69,87,216.0,6048.0,0.03571428571428571,0.042148711,8.19e-6,2.493343257,0.183595081,1.506478504,0.652420896 +9,68,86,216.0,5952.0,0.036290322580645164,0.041568955,8.571e-6,2.487317627,0.147773078,1.472141844,0.653143947 +10,67,85,216.0,5856.0,0.036885245901639344,0.041307868,9.13e-6,2.491634709,0.175728138,1.482162906,0.63058774 +11,66,84,216.0,5760.0,0.0375,0.041265756,8.43e-6,2.516916643,0.180420842,1.463053866,0.650627815 +12,65,83,205.0,5760.0,0.035590277777777776,0.039711293,9.22e-6,2.479664249,0.178013433,1.459566956,0.652477867 +13,64,82,205.0,5664.0,0.03619350282485876,0.030866093,8.87e-6,2.485424881,0.179983608,1.564961227,0.647932468 +14,63,81,205.0,5568.0,0.03681752873563218,0.029946916,8.93e-6,2.469922022,0.179443854,1.485935831,0.651804318 diff --git a/data/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator().csv b/data/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator().csv new file mode 100644 index 0000000..eb191bd --- /dev/null +++ b/data/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator().csv @@ -0,0 +1,176 @@ +operations,graph_nodes,graph_edges,graph_ce,graph_dt,graph_ci,gen_func_t,cpu_compile_t,cpu_st_t,cpu_mt_t,gpu_compile_t,gpu_t +0,356,493,1399.0,30528.0,0.0458267819706499,0.077070556,2.6761e-5,17.804336617,0.960385595,10.618577031,4.95440474 +1,354,491,1399.0,30432.0,0.04597134595162986,1.030851104,2.37e-5,17.726472964,0.933074463,2.174912444,4.959474851 +2,352,489,1399.0,30336.0,0.04611682489451477,0.376282553,2.3861e-5,17.935912907,0.968087391,2.238665483,4.912705328 +3,350,487,1399.0,30240.0,0.04626322751322751,0.076651194,4.2451e-5,17.976779783,0.977130996,2.246167674,4.954520005 +4,348,485,1399.0,30144.0,0.04641056263269639,0.223709216,2.8031e-5,17.67129111,0.97799748,2.175788856,4.923999491 +5,346,483,1399.0,30048.0,0.04655883919062833,0.076034997,4.3191e-5,17.766336956,0.967055891,2.187609178,4.922574669 +6,344,481,1399.0,29952.0,0.04670806623931624,0.398917781,4.3422e-5,17.709032771,0.971142926,2.170963978,4.917191185 +7,342,479,1399.0,29856.0,0.04685825294748124,0.352569343,4.3801e-5,17.690255833,0.952966242,2.159295978,4.945842152 +8,340,477,1399.0,29760.0,0.04700940860215054,0.117620751,4.2992e-5,17.905787431,0.749896479,2.19940915,4.922882222 +9,338,475,1399.0,29664.0,0.04716154261057174,0.318053898,2.3481e-5,17.522775542,0.745113955,2.202366151,4.928734427 +10,336,473,1399.0,29568.0,0.047314664502164504,0.184069985,2.3381e-5,17.529935879,0.74637911,2.238397648,4.919919125 +11,334,471,1399.0,29472.0,0.047468783930510315,0.086029218,2.365e-5,17.560859257,0.75559668,2.249242933,4.956561058 +12,332,469,1399.0,29376.0,0.04762391067538126,0.077326472,2.4361e-5,17.559317648,0.746726769,2.1818156,4.938490196 +13,330,467,1399.0,29280.0,0.047780054644808743,0.169738661,2.342e-5,17.517109121,0.751453942,2.187781478,4.923659727 +14,328,465,1399.0,29184.0,0.047937225877192985,0.077817676,2.315e-5,17.533304215,0.745481303,2.209343496,4.960503415 +15,326,463,1399.0,29088.0,0.04809543454345434,0.171584444,2.352e-5,17.579912576,0.754778436,2.210370024,4.934281254 +16,324,461,1399.0,28992.0,0.04825469094922737,0.084223667,2.305e-5,17.570464754,0.751290178,2.22797709,4.939806799 +17,322,459,1399.0,28896.0,0.04841500553709856,0.123005102,2.3661e-5,17.605650973,0.756929676,2.269940175,4.937928844 +18,320,457,1399.0,28800.0,0.04857638888888889,0.086677986,2.37e-5,17.5539199,0.746367967,2.264938904,4.959258096 +19,318,455,1399.0,28704.0,0.04873885172798216,0.12293158,2.3711e-5,17.609395222,0.755783994,2.264754078,4.92827168 +20,316,453,1399.0,28608.0,0.04890240492170023,0.124475123,2.4281e-5,17.597716228,0.75106304,2.20218749,4.933120236 +21,314,451,1399.0,28512.0,0.04906705948372615,0.112172177,2.6391e-5,17.623178954,0.755694751,2.186417905,4.921509117 +22,312,449,1399.0,28416.0,0.04923282657657658,0.219362642,2.321e-5,17.593459902,0.747914841,2.168628993,4.952994795 +23,310,447,1399.0,28320.0,0.049399717514124294,0.080729209,2.358e-5,17.571675834,0.755489634,2.209531477,4.951190234 +24,308,445,1399.0,28224.0,0.049567743764172334,0.080235835,2.3271e-5,17.615791747,0.750314688,2.21464245,4.949496195 +25,306,443,1399.0,28128.0,0.049736916951080776,0.124106403,2.374e-5,17.60716179,0.753826187,2.186184237,4.920128786 +26,304,441,1399.0,28032.0,0.04990724885844749,0.080715608,2.3781e-5,17.581988477,0.750266997,2.209826064,4.937813884 +27,302,439,1399.0,27936.0,0.05007875143184422,0.080606465,2.4071e-5,17.633096607,0.749125265,2.198599437,4.935320693 +28,300,437,1399.0,27840.0,0.0502514367816092,0.081056137,2.3781e-5,17.564695624,0.746230293,2.225110355,4.939656214 +29,298,435,1399.0,27744.0,0.05042531718569781,0.096545225,2.379e-5,17.58144781,0.747458632,2.263551336,4.924245431 +30,296,433,1399.0,27648.0,0.050600405092592594,0.120638697,2.383e-5,17.574370836,0.748933285,2.234417803,4.915183371 +31,294,431,1399.0,27552.0,0.0507767131242741,0.125073582,2.393e-5,17.627352699,0.754384428,2.214199106,4.938130459 +32,292,429,1399.0,27456.0,0.05095425407925408,0.12314953,2.468e-5,17.697160429,0.796488763,2.261473826,4.956976138 +33,290,427,1399.0,27360.0,0.051133040935672516,0.125481487,2.354e-5,17.636971006,0.748416796,2.222200724,4.948970096 +34,288,425,1399.0,27264.0,0.051313086854460094,0.094052012,2.4301e-5,17.62971842,0.805139938,2.205015347,4.959455536 +35,286,423,1399.0,27168.0,0.051494405182567725,0.08136377,2.4041e-5,17.621304482,0.747718686,2.244362062,4.941432169 +36,284,421,1399.0,27072.0,0.05167700945626478,0.080217839,2.3921e-5,17.61427713,0.747754586,2.212103901,4.933185029 +37,282,417,1399.0,26976.0,0.051860913404507714,0.126372199,2.376e-5,17.601417663,0.750036789,2.163344775,4.926698186 +38,280,414,1399.0,26880.0,0.052046130952380955,0.125444544,2.476e-5,17.612452443,0.748155225,2.195259021,4.91594575 +39,278,412,1399.0,26784.0,0.05223267622461171,0.083158944,2.4551e-5,17.599589645,0.741671021,2.208064301,4.9351555 +40,276,410,1399.0,26688.0,0.05242056354916067,0.083321959,2.4101e-5,17.567124159,0.748238012,2.197233222,4.954754226 +41,274,408,1399.0,26592.0,0.052609807460890494,0.084803792,2.3901e-5,17.549365204,0.754817994,2.229499405,4.94957165 +42,272,405,1399.0,26496.0,0.05280042270531401,0.127648261,2.3851e-5,17.582852416,0.750759497,2.230398721,4.937220319 +43,270,401,1399.0,26400.0,0.052992424242424244,0.128445184,2.428e-5,17.596647819,0.75777713,2.160922996,4.937371146 +44,268,399,1399.0,26304.0,0.053185827250608275,0.129526096,2.5081e-5,17.594476326,0.746906342,2.219401891,4.93357998 +45,266,397,1399.0,26208.0,0.05338064713064713,0.129819495,2.4731e-5,17.568331366,0.750368555,2.18948505,4.922275732 +46,264,394,1399.0,26112.0,0.05357689950980392,0.087649075,2.462e-5,17.585414218,0.751605626,2.198684054,4.941424565 +47,262,391,1399.0,26016.0,0.05377460024600246,0.089110637,2.4551e-5,17.614139291,0.750622403,2.168793662,4.953321773 +48,260,389,1399.0,25920.0,0.053973765432098766,0.090307061,2.45e-5,17.633806293,0.749096576,2.224521298,4.930813246 +49,258,387,1399.0,25824.0,0.054174411400247834,0.133480181,2.461e-5,17.634768586,0.756613261,2.201452177,4.972809945 +50,256,385,1399.0,25728.0,0.05437655472636816,0.134254424,2.425e-5,17.606323938,0.748779206,2.216818872,4.939295094 +51,254,382,1399.0,25632.0,0.05458021223470662,0.134016868,2.4531e-5,17.5926305,0.75625873,2.227679889,4.968213894 +52,252,379,1399.0,25536.0,0.054785401002506263,0.135650945,2.4601e-5,17.642803637,0.751975585,2.226011125,4.9285844 +53,250,375,1399.0,25440.0,0.054992138364779876,0.136647933,2.4161e-5,17.799738254,0.76667472,2.165144989,4.930427128 +54,248,373,1399.0,25344.0,0.05520044191919192,0.123103164,2.4461e-5,17.745879754,0.760526742,2.161495227,4.940492285 +55,246,370,1399.0,25248.0,0.05541032953105197,0.09476826,2.3511e-5,17.596131758,0.756924114,2.180021837,4.954121771 +56,244,365,1399.0,25152.0,0.05562181933842239,0.095345787,2.4171e-5,17.612023424,0.747989147,2.215139082,4.945396527 +57,242,362,1399.0,25056.0,0.05583492975734355,0.139570128,2.3801e-5,17.630922372,0.750668446,2.186529739,4.961981394 +58,240,359,1399.0,24960.0,0.05604967948717949,0.097466916,2.4451e-5,17.61078772,0.7485922,2.217673752,4.95291513 +59,238,357,1399.0,24864.0,0.05626608751608752,0.138599302,2.3601e-5,17.586404505,0.756929027,2.233374301,4.935342135 +60,236,352,1399.0,24768.0,0.05648417312661499,0.147210964,2.4911e-5,17.650436019,0.74908103,2.157077946,4.937714591 +61,234,350,1399.0,24672.0,0.05670395590142672,0.099491094,2.3601e-5,17.608002511,0.756924473,2.165309665,4.932434479 +62,232,348,1399.0,24576.0,0.056925455729166664,0.141929827,2.454e-5,17.605756917,0.749178717,2.234082435,4.957629943 +63,230,344,1399.0,24480.0,0.057148692810457515,0.142483983,2.4211e-5,17.623883273,0.758216784,2.210078838,4.930940098 +64,228,341,1399.0,24384.0,0.057373687664041995,0.101524943,2.4371e-5,17.662312587,0.751128917,2.22449657,4.96708528 +65,226,339,1399.0,24288.0,0.05760046113306983,0.102619253,2.3831e-5,17.610112922,0.758167777,2.187456785,4.957519684 +66,224,337,1399.0,24192.0,0.05782903439153439,0.10351088,2.3401e-5,17.611932402,0.749178457,2.236980212,4.933450322 +67,222,335,1399.0,24096.0,0.05805942895086321,0.148780402,2.3711e-5,17.636035095,0.75707833,2.252138664,4.951632995 +68,220,333,1399.0,24000.0,0.058291666666666665,0.148311059,2.4851e-5,17.617252052,0.750104986,2.22330739,4.9243139 +69,218,329,1399.0,23904.0,0.05852576974564926,0.151678794,2.4181e-5,17.627742278,0.755299894,2.248062201,4.951401482 +70,216,326,1399.0,23808.0,0.05876176075268817,0.15082361,2.3851e-5,17.647410652,0.752445605,2.240948426,4.949599133 +71,214,323,1399.0,23712.0,0.05899966261808367,0.153382492,2.4011e-5,17.654743596,0.752802907,2.253819342,4.966250371 +72,212,320,1399.0,23616.0,0.05923949864498645,0.151516131,2.3931e-5,17.672908543,0.750257716,2.220003155,4.944782327 +73,210,317,1399.0,23520.0,0.059481292517006804,0.154244628,2.386e-5,17.60330678,0.750422813,2.211295295,4.943727837 +74,208,313,1399.0,23424.0,0.05972506830601093,0.153767234,2.4291e-5,17.640950842,0.74988433,2.24794966,4.952712228 +75,206,311,1399.0,23328.0,0.05997085048010974,0.155927375,2.406e-5,17.589128666,0.749120129,2.253801308,4.953014816 +76,204,306,1399.0,23232.0,0.06021866391184573,0.15464184,2.4521e-5,17.662616581,0.750484429,2.227511412,4.924026259 +77,202,304,1399.0,23136.0,0.06046853388658368,0.157807248,2.4041e-5,17.611953814,0.755679546,2.178734374,4.943974526 +78,200,301,1399.0,23040.0,0.06072048611111111,0.155978707,2.4051e-5,17.624250437,0.794935481,2.247188963,4.940403894 +79,198,298,1399.0,22944.0,0.06097454672245467,0.158377905,2.5091e-5,17.634938402,0.754743461,2.245248812,4.919902064 +80,196,296,1399.0,22848.0,0.061230742296918765,0.158750786,2.4511e-5,17.6360904,0.750867213,2.200032233,4.942215648 +81,194,293,1399.0,22752.0,0.061489099859353025,0.161152794,2.4831e-5,17.780761042,0.765338482,2.204873372,4.939655562 +82,192,290,1399.0,22656.0,0.061749646892655365,0.160175486,2.318e-5,17.798147683,0.76168194,2.230891056,4.955801153 +83,190,287,1399.0,22560.0,0.06201241134751773,0.159868767,2.4791e-5,17.764165058,0.796377137,2.239618185,4.928054627 +84,188,283,1399.0,22464.0,0.06227742165242165,0.160933577,2.4221e-5,17.798426962,0.848255338,2.218112612,4.932433146 +85,186,280,1399.0,22368.0,0.06254470672389127,0.163393917,2.4371e-5,17.808464853,0.765692696,2.213490844,4.943298137 +86,184,277,1399.0,22272.0,0.06281429597701149,0.163792118,2.4261e-5,17.805783627,0.761027705,2.232891092,4.919454211 +87,182,275,1399.0,22176.0,0.06308621933621934,0.162177953,2.43e-5,17.797665375,0.761040026,2.236586089,4.951072155 +88,180,271,1399.0,22080.0,0.06336050724637682,0.165377424,2.557e-5,17.805099359,0.763146286,2.212611436,4.921150887 +89,178,268,1399.0,21984.0,0.06363719068413391,0.166754373,2.5141e-5,17.770997205,0.764361801,2.199943181,4.934748884 +90,176,266,1399.0,21888.0,0.06391630116959064,0.167241957,2.4571e-5,17.770223198,0.759580227,2.247867501,4.935730147 +91,174,264,1399.0,21792.0,0.06419787077826726,0.169623073,2.5e-5,17.771153368,0.750276145,2.243455929,4.939933808 +92,172,261,1399.0,21696.0,0.06448193215339233,0.168358288,2.5181e-5,17.799224982,0.760906435,2.210000929,4.943923374 +93,170,259,1399.0,21600.0,0.06476851851851852,0.170287483,2.529e-5,17.79271252,0.763151029,2.205444892,4.924953813 +94,168,254,1399.0,21504.0,0.06505766369047619,0.168986856,2.5021e-5,17.775583682,0.760237647,2.222811993,4.951301097 +95,166,250,1399.0,21408.0,0.06534940209267563,0.171662521,2.4401e-5,17.636022254,0.749599438,2.234944605,4.958431762 +96,164,246,1399.0,21312.0,0.06564376876876876,0.170911431,2.4481e-5,17.633556045,0.788097892,2.198060879,4.922871993 +97,162,244,1399.0,21216.0,0.06594079939668175,0.172387252,2.4781e-5,17.620254381,0.799269067,2.202436673,4.936411908 +98,160,241,1399.0,21120.0,0.0662405303030303,0.171830017,2.581e-5,17.656653806,0.750275098,2.200933622,4.94776375 +99,158,238,1399.0,21024.0,0.06654299847792998,0.174560093,2.447e-5,17.625724723,0.756745741,2.249721096,4.958786002 +100,156,235,1399.0,20928.0,0.06684824159021406,0.178996759,2.453e-5,17.669194606,0.749422535,2.218089817,4.960858653 +101,154,231,1399.0,20832.0,0.0671562980030722,0.175032127,2.3871e-5,17.642586975,0.754643863,2.194675279,4.944134534 +102,152,229,1399.0,20736.0,0.06746720679012345,0.176393906,2.4731e-5,17.592973556,0.749943551,2.229565622,4.927935661 +103,150,225,1399.0,20640.0,0.06778100775193799,0.178017631,2.412e-5,17.630568322,0.755272802,2.221125776,4.952348991 +104,148,223,1399.0,20544.0,0.0680977414330218,0.175897841,2.36e-5,17.661766307,0.749293633,2.2201698,4.963634779 +105,146,221,1399.0,20448.0,0.06841744913928012,0.178367362,2.5001e-5,17.654508999,0.755361234,2.185187066,4.938710949 +106,144,218,1399.0,20352.0,0.06874017295597484,0.178791594,2.502e-5,17.649520916,0.749748217,2.238645461,4.955141284 +107,142,216,1399.0,20256.0,0.06906595576619273,0.175900502,2.3291e-5,17.648252045,0.755157659,2.250102545,4.948078116 +108,140,212,1399.0,20160.0,0.06939484126984127,0.180050739,2.3901e-5,17.642556024,0.751139061,2.195233955,4.92102672 +109,138,210,1399.0,20064.0,0.06972687400318979,0.182587052,2.492e-5,17.631301401,0.754040144,2.177296385,4.948297571 +110,136,207,1399.0,19968.0,0.07006209935897435,0.181449712,2.4401e-5,17.618787463,0.748940439,2.251932822,4.950366155 +111,134,203,1399.0,19872.0,0.07040056360708534,0.183466877,2.407e-5,17.658532693,0.756589176,2.240568188,4.97337861 +112,132,201,1399.0,19776.0,0.0707423139158576,0.181545084,2.485e-5,17.63441504,0.751343023,2.183033772,4.975534251 +113,130,199,1399.0,19680.0,0.07108739837398374,0.177809314,2.417e-5,17.627163359,0.754577307,2.211080446,4.977438563 +114,128,195,1399.0,19584.0,0.07143586601307189,0.183038393,2.5541e-5,17.63366534,0.751510139,2.237832092,4.969644912 +115,126,191,1399.0,19488.0,0.07178776683087028,0.186344151,2.4971e-5,17.711808739,0.759177,2.236586017,4.951292022 +116,124,187,1399.0,19392.0,0.07214315181518152,0.184833587,2.475e-5,17.648467279,0.749564641,2.179772409,4.97017709 +117,122,183,1399.0,19296.0,0.07250207296849089,0.193249355,2.3811e-5,17.639230223,0.755564354,2.195109482,4.982434629 +118,120,180,1399.0,19200.0,0.07286458333333333,0.186818046,2.372e-5,17.635977046,0.750626058,2.243877912,4.972608068 +119,118,177,1399.0,19104.0,0.07323073701842546,0.189204719,2.4961e-5,17.791522288,0.766082656,2.242948358,4.980365418 +120,116,173,1399.0,19008.0,0.07360058922558922,0.186391669,2.4181e-5,17.645956891,0.750893368,2.197914806,4.98745469 +121,114,171,1399.0,18912.0,0.07397419627749577,0.19060573,2.4701e-5,17.771140583,0.765197694,2.20643796,4.959618561 +122,112,169,1399.0,18816.0,0.0743516156462585,0.188466188,2.381e-5,17.795228145,0.759434429,2.26208531,4.965068853 +123,110,165,1399.0,18720.0,0.07473290598290598,0.191524927,2.3841e-5,17.779734215,0.767242896,2.242967333,4.950554681 +124,108,161,1399.0,18624.0,0.07511812714776632,0.189450326,2.3601e-5,17.807849571,0.762371273,2.196711688,4.966122065 +125,106,157,1399.0,18528.0,0.0755073402417962,0.191473057,2.357e-5,17.632877767,0.755845465,2.188474891,4.977562868 +126,104,153,1399.0,18432.0,0.0759006076388889,0.191382079,2.3851e-5,17.775729988,0.758861116,2.278116886,4.979965119 +127,102,151,1399.0,18336.0,0.07629799301919721,0.192296369,2.394e-5,17.777918793,0.764981303,2.224818047,4.949944943 +128,100,149,1399.0,18240.0,0.07669956140350877,0.191424719,2.4331e-5,17.856475915,0.76057459,2.201588049,4.941974925 +129,98,146,1399.0,18144.0,0.07710537918871252,0.194280932,2.3951e-5,17.779963845,0.766401736,2.223182601,4.961465017 +130,96,142,1399.0,18048.0,0.07751551418439716,0.192850597,2.3861e-5,17.765033828,0.760509569,2.250897799,4.967399083 +131,94,138,1399.0,17952.0,0.07793003565062388,0.194741823,2.38e-5,17.778261696,0.764271609,2.248898068,4.975998565 +132,92,136,1399.0,17856.0,0.07834901433691756,0.193567295,2.5281e-5,17.791322862,0.759809249,2.216694812,4.962092553 +133,90,132,1399.0,17760.0,0.07877252252252252,0.196949912,2.4641e-5,17.775924767,0.766636532,2.192664527,4.943809886 +134,88,129,1399.0,17664.0,0.07920063405797101,0.19423328,2.4491e-5,17.775940481,0.759698903,2.241454301,4.965419114 +135,86,125,1399.0,17568.0,0.07963342440801457,0.196021362,2.4541e-5,17.749824568,0.77002309,2.244133161,4.973507276 +136,84,123,1399.0,17472.0,0.08007097069597069,0.195945063,2.4791e-5,17.793381264,0.758984676,2.223761942,4.967845004 +137,82,120,1399.0,17376.0,0.0805133517495396,0.196404909,2.5491e-5,17.781126567,0.76777764,2.208548873,4.942758101 +138,80,116,1399.0,17280.0,0.08096064814814814,0.197313346,2.469e-5,17.785944557,0.814271788,2.200296465,4.939179018 +139,78,114,1399.0,17184.0,0.08141294227188083,0.155633427,2.5181e-5,17.79491891,0.767423131,2.233213884,4.963944358 +140,76,111,1399.0,17088.0,0.08187031835205992,0.194686919,2.4311e-5,17.835512877,0.761171578,2.216772786,4.968370761 +141,74,108,1399.0,16992.0,0.0823328625235405,0.19895497,2.4301e-5,17.80769545,0.768202031,2.212642548,4.971369432 +142,72,106,1399.0,16896.0,0.08280066287878787,0.197589165,2.4241e-5,17.817799582,0.760097766,2.219367009,4.967751237 +143,70,102,1399.0,16800.0,0.08327380952380953,0.200103786,2.425e-5,17.804210307,0.767108387,2.264925155,4.965506236 +144,68,99,1399.0,16704.0,0.08375239463601533,0.196633322,2.5371e-5,17.822197608,0.762852947,2.20877412,4.971541033 +145,66,97,1399.0,16608.0,0.08423651252408478,0.200144552,2.4801e-5,17.823667792,0.766965999,2.209992675,4.969252216 +146,64,93,1399.0,16512.0,0.08472625968992248,0.199816644,2.4901e-5,17.838429006,0.764432365,2.241092809,4.961995819 +147,62,89,1399.0,16416.0,0.08522173489278752,0.187325579,2.5321e-5,17.811923957,0.767393244,2.227406228,4.960056608 +148,60,85,1399.0,16320.0,0.08572303921568628,0.198893612,2.4451e-5,17.82940565,0.760747136,2.209815727,4.971563658 +149,58,83,1399.0,16224.0,0.08623027613412229,0.201039293,2.4651e-5,17.817639935,0.767607352,2.210546374,4.97066195 +150,56,81,1399.0,16128.0,0.08674355158730158,0.199841932,2.414e-5,17.82203287,0.760048809,2.243550629,4.954439346 +151,54,79,1399.0,16032.0,0.0872629740518962,0.2011596,2.4741e-5,17.804574042,0.767800679,2.250206119,4.955980994 +152,52,75,1399.0,15936.0,0.08778865461847389,0.19971389,2.4331e-5,17.829821975,0.762018993,2.205143141,4.970086548 +153,50,73,1399.0,15840.0,0.08832070707070708,0.201368798,2.4881e-5,17.836101646,0.767371477,2.218711432,4.96364023 +154,48,71,1399.0,15744.0,0.08885924796747967,0.200798594,2.4491e-5,17.830384655,0.765407907,2.286796949,4.939295093 +155,46,67,1399.0,15648.0,0.08940439672801637,0.202551163,2.5121e-5,17.827221721,0.768466657,2.262575248,4.943430916 +156,44,65,1399.0,15552.0,0.08995627572016461,0.198816901,2.578e-5,17.840506569,0.760760306,2.220630133,4.952844324 +157,42,63,1399.0,15456.0,0.09051501035196688,0.201424744,2.5021e-5,17.814439397,0.767553139,2.196934945,4.958506547 +158,40,59,1399.0,15360.0,0.09108072916666667,0.202145126,2.565e-5,17.808712307,0.76137146,2.235801178,4.949559042 +159,38,55,1399.0,15264.0,0.0916535639412998,0.201663393,2.4591e-5,17.784477195,0.766209648,2.249329555,4.964028527 +160,36,53,1399.0,15168.0,0.09223364978902954,0.199579456,2.5461e-5,17.900752023,0.761934363,2.209582978,4.950507063 +161,34,48,1399.0,15072.0,0.09282112526539278,0.159541692,2.5211e-5,17.769415534,0.935609132,2.216664395,4.962977201 +162,32,44,1399.0,14976.0,0.09341613247863248,0.201979445,2.5581e-5,17.802148727,0.758630938,2.257162782,4.954367291 +163,30,40,1399.0,14880.0,0.09401881720430108,0.203381244,2.5411e-5,17.808584074,0.768160516,2.239967841,4.949515694 +164,28,35,1399.0,14784.0,0.09462932900432901,0.200707381,2.5071e-5,17.811958674,0.765546396,2.222827481,4.962523474 +165,26,31,1399.0,14688.0,0.09524782135076253,0.203476579,2.4431e-5,17.791537057,0.759747517,2.210172596,4.96717851 +166,24,29,1399.0,14592.0,0.09587445175438597,0.38619058,2.5161e-5,17.784565893,0.765981903,2.205094732,4.970469758 +167,22,25,1399.0,14496.0,0.09650938189845475,0.209174268,2.6071e-5,17.886396985,0.762283972,2.251379768,4.9348063 +168,20,21,1399.0,14400.0,0.09715277777777778,0.184182012,2.5331e-5,17.791795342,0.760972528,2.229551257,4.941190792 +169,18,17,1399.0,14304.0,0.09780480984340045,0.203935864,2.572e-5,17.823665061,0.762353868,2.199132836,4.965200905 +170,16,15,1399.0,14208.0,0.09846565315315316,0.200164969,2.4631e-5,17.792385586,0.76804392,2.174965407,4.972074439 +171,14,13,1399.0,14112.0,0.09913548752834467,0.204567903,2.5071e-5,17.806154396,0.759505453,2.2340466,4.972671228 +172,12,11,1399.0,14016.0,0.09981449771689498,0.201861418,2.5971e-5,18.529840195,0.789347616,2.23167521,4.947890089 +173,10,9,1399.0,13920.0,0.1005028735632184,0.202902727,2.4951e-5,17.865867105,0.761004999,2.194876208,4.93177029 +174,8,7,1399.0,13824.0,0.10120081018518519,0.198079003,2.4651e-5,17.791197743,0.767399089,2.226370372,4.951979965 diff --git a/data/qed_ke-kkke_reduction_optimizer.csv b/data/qed_ke-kkke_reduction_optimizer.csv new file mode 100644 index 0000000..bd41fbf --- /dev/null +++ b/data/qed_ke-kkke_reduction_optimizer.csv @@ -0,0 +1,82 @@ +operations,graph_nodes,graph_edges,graph_ce,graph_dt,graph_ci,gen_func_t,cpu_compile_t,cpu_st_t,cpu_mt_t,gpu_compile_t,gpu_t +0,356,493,1399.0,30528.0,0.0458267819706499,0.084389903,2.4971e-5,17.802549835,0.960409581,2.406448706,4.927079076 +1,351,483,1369.0,30528.0,0.044844077568134175,0.126855933,2.9211e-5,16.868735557,0.927387188,2.257632484,4.697683068 +2,346,478,1369.0,30048.0,0.04556043663471779,0.08319682,3.5431e-5,16.871399152,0.834869326,2.264361993,4.701280771 +3,341,473,1314.0,30048.0,0.04373003194888179,0.124422234,2.392e-5,16.454231193,0.856669072,2.271991539,4.68580348 +4,336,463,1284.0,30048.0,0.042731629392971246,0.121696991,2.2921e-5,15.881542683,0.816430136,2.213686135,4.449106524 +5,331,458,1284.0,29568.0,0.04342532467532467,0.124024888,2.314e-5,15.879200155,0.799333453,2.194093083,4.435654931 +6,326,448,1254.0,29568.0,0.04241071428571429,0.121610951,2.2e-5,15.325702423,0.833341953,2.203843882,4.199677306 +7,321,438,1224.0,29568.0,0.041396103896103896,0.118972208,2.1631e-5,14.367273685,0.711553932,2.16189756,3.948872646 +8,316,433,1224.0,29088.0,0.04207920792079208,0.074826839,2.2031e-5,14.367107152,0.792981221,2.169096496,3.961630969 +9,311,428,1169.0,29088.0,0.04018839383938394,0.116237162,2.15e-5,14.416973472,0.788583102,2.092186151,3.946339564 +10,306,418,1139.0,29088.0,0.03915704070407041,0.114647398,2.031e-5,13.671420757,0.745657392,2.037551329,3.657411205 +11,301,408,1109.0,29088.0,0.03812568756875687,0.11434652,1.951e-5,13.093103664,0.686554396,2.065489584,3.441139671 +12,296,403,1109.0,28608.0,0.03876538031319911,0.112282663,1.8991e-5,13.11525848,0.705183633,2.0639299,3.422598036 +13,291,398,1109.0,28128.0,0.039426905574516495,0.111549203,1.9661e-5,13.08100601,0.700772882,2.065935946,3.41679234 +14,286,388,1079.0,28128.0,0.0383603526734926,0.109881396,1.907e-5,11.871746271,0.665244638,2.063828106,3.187580585 +15,281,378,1049.0,28128.0,0.037293799772468716,0.108444747,1.7961e-5,10.963517612,0.62180291,2.037926216,2.935137574 +16,276,373,1049.0,27648.0,0.03794126157407408,0.107959773,1.874e-5,11.021594456,0.541779823,2.003876106,2.931304737 +17,271,368,1049.0,27168.0,0.03861160188457008,0.105629068,1.8241e-5,11.017450178,0.581974375,2.017201027,2.952118903 +18,266,363,1049.0,26688.0,0.0393060551558753,0.107303406,1.8301e-5,11.028597789,0.556078309,2.037535226,2.911405619 +19,261,358,994.0,26688.0,0.03724520383693045,0.106584986,1.7111e-5,10.789192026,0.525275525,2.011931363,2.931360979 +20,256,353,939.0,26688.0,0.035184352517985615,0.105743463,1.7521e-5,10.50283261,0.535253087,1.962456949,2.941274646 +21,255,351,933.0,26688.0,0.03495953237410072,0.105189187,1.7471e-5,10.739591259,0.555102576,2.013201521,2.896175037 +22,254,350,933.0,26592.0,0.035085740072202165,0.105895137,1.6631e-5,10.68514711,0.571809578,1.974934611,2.890503396 +23,253,348,927.0,26592.0,0.0348601083032491,0.104181459,1.817e-5,10.344271645,0.572483889,2.002875753,2.842241926 +24,252,347,927.0,26496.0,0.034986413043478264,0.103568232,1.7471e-5,10.363216025,0.602207417,1.943794016,2.811132729 +25,247,342,927.0,26016.0,0.035631918819188195,0.102006829,1.669e-5,10.360319761,0.588967585,1.942523675,2.838431844 +26,246,340,921.0,26016.0,0.03540129151291513,0.103244544,1.672e-5,10.140255758,0.565172778,1.980058606,2.776594151 +27,245,339,921.0,25920.0,0.03553240740740741,0.102991317,1.723e-5,10.166352736,0.588556746,2.025713505,2.754827976 +28,244,337,915.0,25920.0,0.03530092592592592,0.102527335,1.6261e-5,9.965044496,0.527648944,1.966870364,2.708992883 +29,243,335,909.0,25920.0,0.035069444444444445,0.101020632,1.6541e-5,9.899918186,0.530837495,1.99964346,2.686936268 +30,242,334,909.0,25824.0,0.03519981412639405,0.099846559,1.614e-5,9.924451078,0.532149983,1.992832633,2.667590089 +31,241,333,909.0,25728.0,0.035331156716417914,0.103293156,1.634e-5,9.893503718,0.500188044,1.971455575,2.661440862 +32,236,328,909.0,25248.0,0.036002851711026615,0.110948742,1.5851e-5,9.916889596,0.515528547,2.014256204,2.691654688 +33,235,326,903.0,25248.0,0.03576520912547528,0.099799239,1.658e-5,9.667648582,0.561210643,1.981308261,2.647665444 +34,234,324,897.0,25248.0,0.035527566539923956,0.099455409,1.6561e-5,9.588166052,0.544847505,1.932560182,2.56349283 +35,233,323,897.0,25152.0,0.035663167938931296,0.103335368,1.6271e-5,9.590387462,0.542413718,1.965145602,2.559435691 +36,232,321,891.0,25152.0,0.03542461832061069,0.097770562,1.6571e-5,9.362808632,0.543288523,2.017894491,2.498672404 +37,231,320,891.0,25056.0,0.03556034482758621,0.100428616,1.5941e-5,9.340302395,0.548822639,1.994799194,2.525394 +38,230,319,891.0,24960.0,0.03569711538461538,0.056667955,1.5341e-5,9.356871677,0.537041949,1.921246656,2.507595034 +39,225,314,891.0,24480.0,0.036397058823529414,0.099323026,1.636e-5,9.383625024,0.506403697,1.972101141,2.529248938 +40,220,309,836.0,24480.0,0.03415032679738562,0.096789665,1.645e-5,9.524601658,0.473707387,1.980933173,2.524768525 +41,215,304,836.0,24000.0,0.034833333333333334,0.053463925,1.671e-5,9.520567128,0.487585179,1.942542795,2.535491481 +42,214,302,830.0,24000.0,0.034583333333333334,0.096303802,1.6011e-5,9.137262758,0.4297148,1.950560163,2.478408276 +43,213,301,830.0,23904.0,0.034722222222222224,0.070596338,1.6901e-5,9.143790565,0.492842898,1.949332161,2.476752284 +44,212,299,824.0,23904.0,0.034471218206157964,0.09696925,1.612e-5,9.089211511,0.456930617,2.022026121,2.419473874 +45,211,297,818.0,23904.0,0.03422021419009371,0.052526649,1.536e-5,8.807671694,0.471203239,1.970488502,2.372441242 +46,210,296,818.0,23808.0,0.03435819892473118,0.096716114,1.5701e-5,8.806210783,0.451452844,1.960073481,2.387451098 +47,209,295,818.0,23712.0,0.034497300944669365,0.05145174,1.6061e-5,8.867215342,0.450895098,1.968012818,2.394204111 +48,204,290,818.0,23232.0,0.03521005509641873,0.093248236,1.9521e-5,8.844517253,0.476030278,1.963827031,2.389413849 +49,203,288,812.0,23232.0,0.034951790633608815,0.093881584,1.527e-5,8.849095772,0.446415074,1.974782212,2.332439097 +50,202,287,812.0,23136.0,0.03509681881051176,0.050473481,1.5851e-5,8.784636116,0.469233287,1.953068913,2.321316886 +51,201,285,806.0,23136.0,0.034837482710926695,0.092750242,1.5541e-5,8.632088328,0.491467054,1.945455141,2.29300329 +52,200,284,806.0,23040.0,0.03498263888888889,0.092540087,1.7161e-5,8.637677414,0.471865872,1.975464118,2.259260411 +53,199,282,800.0,23040.0,0.034722222222222224,0.092944049,1.5261e-5,8.624992966,0.478249573,1.931707577,2.232058939 +54,198,281,800.0,22944.0,0.03486750348675035,0.091660013,1.575e-5,8.680034605,0.429976994,2.022314921,2.224544849 +55,197,279,794.0,22944.0,0.03460599721059972,0.092591389,1.582e-5,8.266084761,0.442472956,1.949268775,2.165130527 +56,196,278,794.0,22848.0,0.03475140056022409,0.090376966,1.529e-5,8.26930839,0.438461132,1.960119483,2.169387658 +57,191,273,739.0,22848.0,0.03234418767507003,0.090398736,1.589e-5,8.061516101,0.468233752,1.825342557,2.144808638 +58,186,268,739.0,22368.0,0.03303826895565093,0.090566151,1.5781e-5,8.051685873,0.472555774,1.827021946,2.175475243 +59,185,266,733.0,22368.0,0.03277002861230329,0.046301524,1.4931e-5,7.809555195,0.466519375,1.819191936,2.095906173 +60,184,264,727.0,22368.0,0.03250178826895565,0.087977349,1.4771e-5,7.825535183,0.452072238,1.820734702,2.06485156 +61,183,263,727.0,22272.0,0.032641882183908046,0.08908488,1.4591e-5,7.77560322,0.445728609,1.804235078,2.06763398 +62,182,262,727.0,22176.0,0.03278318903318903,0.076517376,1.461e-5,7.754359737,0.421063625,1.812681957,2.076417548 +63,181,260,721.0,22176.0,0.032512626262626264,0.088983767,1.4091e-5,7.616158878,0.422402602,1.868182992,2.016601005 +64,180,259,721.0,22080.0,0.03265398550724638,0.089172453,1.467e-5,7.63910266,0.402654247,1.844390793,2.031385412 +65,175,254,666.0,22080.0,0.03016304347826087,0.091971222,1.3851e-5,7.35822511,0.443635961,1.719023302,2.007792679 +66,170,249,666.0,21600.0,0.030833333333333334,0.073480651,1.3871e-5,7.291999508,0.434965958,1.750073777,1.999358953 +67,169,247,660.0,21600.0,0.030555555555555555,0.085309774,1.7211e-5,7.245192983,0.412650069,1.744681817,1.962798523 +68,168,245,654.0,21600.0,0.03027777777777778,0.089043539,1.367e-5,7.024436477,0.421292773,1.722710908,1.890918459 +69,167,243,648.0,21600.0,0.03,0.084353527,1.428e-5,6.8832018,0.415786727,1.715216258,1.830282141 +70,166,242,648.0,21504.0,0.030133928571428572,0.084367977,1.3441e-5,6.899982477,0.419080281,1.707637056,1.843529005 +71,165,241,648.0,21408.0,0.030269058295964126,0.085701815,1.4031e-5,6.936174291,0.377346024,1.704252961,1.85218872 +72,164,240,648.0,21312.0,0.030405405405405407,0.083910355,1.3601e-5,6.9051589,0.389477478,1.75740328,1.867258596 +73,159,235,593.0,21312.0,0.0278246996996997,0.082135195,1.3351e-5,7.031037571,0.356084586,1.631072,1.797434919 +74,154,230,593.0,20832.0,0.028465821812596007,0.080356395,1.358e-5,7.040766129,0.405151789,1.620631997,1.781269114 +75,153,228,587.0,20832.0,0.02817780337941628,0.066967517,1.3391e-5,6.644186555,0.395240289,1.641155866,1.743666486 +76,152,226,581.0,20832.0,0.02788978494623656,0.080763676,1.298e-5,6.633937959,0.388869331,1.630064054,1.701302723 +77,151,225,581.0,20736.0,0.028018904320987654,0.080671833,1.2781e-5,6.622133299,0.392564435,1.625932508,1.711411428 +78,150,224,581.0,20640.0,0.02814922480620155,0.080368195,1.358e-5,6.599986437,0.397419271,1.657700695,1.694756709 +79,149,222,575.0,20640.0,0.027858527131782947,0.080015475,1.298e-5,6.281191715,0.37819019,1.622522233,1.656839741 +80,148,221,575.0,20544.0,0.027988707165109036,0.065331671,1.334e-5,6.313635402,0.380955078,1.627111603,1.638795233 diff --git a/data/qed_ke-kkkkke_reduction_optimizer.csv b/data/qed_ke-kkkkke_reduction_optimizer.csv new file mode 100644 index 0000000..9432121 --- /dev/null +++ b/data/qed_ke-kkkkke_reduction_optimizer.csv @@ -0,0 +1,79 @@ +operations,graph_nodes,graph_edges,graph_ce,graph_dt,graph_ci,gen_func_t,cpu_compile_t,cpu_st_t,cpu_mt_t,gpu_compile_t,gpu_t +0,15866,21617,66249.0,1.314048e6,0.050415966540035065,6.468999136,0.001398329,8.478099553,0.43958521,0.0,0.0 +10,14676,19713,60656.0,1.279776e6,0.0473957942639962,5.993535435,0.000745961,7.192805963,0.417393835,0.0,0.0 +20,13774,18527,56334.0,1.243296e6,0.04531020770596865,5.489738392,0.000682889,6.652182167,0.336339503,0.0,0.0 +30,13352,17940,53276.0,1.236672e6,0.04308013765978368,5.169906767,0.000675318,6.370526843,0.313517861,0.0,0.0 +40,12714,17168,51163.0,1.199712e6,0.042646068389746876,4.845906388,0.000634457,6.124306725,0.311820244,0.0,0.0 +50,12004,16270,48473.0,1.163232e6,0.04167096503534978,4.433653313,0.000596017,5.760561483,0.320897852,0.0,0.0 +60,11750,15983,48022.0,1.144224e6,0.04196905501020779,4.316924709,0.000596237,5.738809149,0.283214404,0.0,0.0 +70,11538,15697,47325.0,1.133184e6,0.04176285581158929,4.201152631,0.000554855,5.438337093,0.313985744,0.0,0.0 +80,11434,15550,46814.0,1.129536e6,0.04144533684628024,4.216359254,0.000553545,5.429706297,0.268223845,0.0,0.0 +90,11066,15085,46232.0,1.10352e6,0.041895026823256486,3.924567625,0.000560535,5.412444055,0.274917428,0.0,0.0 +100,10848,14847,44297.0,1.100352e6,0.04025711772232885,3.848048388,0.000527955,5.127227854,0.294706757,0.0,0.0 +110,10462,14382,42261.0,1.084512e6,0.038967756926617685,3.674674179,0.000509054,4.922064369,0.276530272,0.0,0.0 +120,10304,14191,41810.0,1.07472e6,0.038903156170909635,3.58233155,0.000516074,5.02371138,0.266906519,0.0,0.0 +130,10200,14067,41437.0,1.068864e6,0.03876732680677804,3.529160319,0.000501634,4.863804478,0.24639169,0.0,0.0 +140,10042,13871,40956.0,1.059552e6,0.03865407266467337,3.346890818,0.000488403,4.753116119,0.254509861,0.0,0.0 +150,9956,13765,40583.0,1.055424e6,0.038451844945727974,3.41847396,0.000500654,4.756966153,0.255966291,0.0,0.0 +160,9906,13690,40433.0,1.053024e6,0.03839703558513386,3.405093274,0.000496774,4.812050085,0.24421971,0.0,0.0 +170,9838,13597,40283.0,1.048896e6,0.038405142168527674,3.348340057,0.000481363,4.669473296,0.234701411,0.0,0.0 +180,9242,12790,37708.0,1.02336e6,0.03684724828017511,3.063089187,0.000449352,4.335668832,0.228471471,0.0,0.0 +190,9120,12648,37082.0,1.017984e6,0.03642689865459575,2.994073054,0.000429002,4.181894908,0.224361729,0.0,0.0 +200,9052,12555,36932.0,1.013856e6,0.03642726383233911,3.046147594,0.000427282,4.151250123,0.212513705,0.0,0.0 +210,8912,12405,36366.0,1.005792e6,0.03615658108237091,2.937579863,0.000433982,4.261727394,0.214012817,0.0,0.0 +220,8808,12281,35993.0,999936.0,0.035995303699436765,2.892146284,0.000432382,4.198423468,0.219749812,0.0,0.0 +230,8626,12061,35765.0,986112.0,0.03626869970145379,2.752333211,0.000414672,4.035044142,0.241721263,0.0,0.0 +240,8426,11841,34336.0,980256.0,0.03502758463095355,2.714773746,0.000414522,4.036870861,0.235365769,0.0,0.0 +250,8118,11464,33416.0,961728.0,0.03474579090969588,2.579966689,0.000402461,3.870568035,0.20937257,0.0,0.0 +260,7942,11242,32634.0,953664.0,0.034219599355747934,2.520293442,0.000391581,3.72881432,0.191238985,0.0,0.0 +270,7838,11100,32153.0,949536.0,0.0338618019748593,2.456319106,0.000383211,3.635092003,0.187908484,0.0,0.0 +280,7716,10940,31672.0,943680.0,0.033562224482875554,2.402192681,0.00037687,3.594882506,0.194062713,0.0,0.0 +290,7576,10772,30745.0,939552.0,0.032723042471305475,2.338714319,0.00037334,3.556085038,0.194369971,0.0,0.0 +300,7376,10529,30487.0,924480.0,0.0329774575977847,2.279512925,0.00036552,3.504723807,0.191079171,0.0,0.0 +310,7218,10310,29868.0,917376.0,0.03255807869401423,2.207692656,0.000355539,3.30937664,0.181261073,0.0,0.0 +320,7078,10137,29417.0,909312.0,0.03235083227759009,2.147511905,0.000352659,3.30461376,0.18005858,0.0,0.0 +330,6860,9848,28991.0,895200.0,0.032384941912421805,2.078259266,0.00033941,3.211808988,0.172834084,0.0,0.0 +340,6702,9611,28264.0,889824.0,0.03176358470888625,2.069880378,0.000318959,3.033092324,0.154811992,0.0,0.0 +350,6616,9505,27891.0,885696.0,0.03149048883589855,2.005510172,0.000326369,3.008426711,0.173417779,0.0,0.0 +360,6512,9391,27325.0,881088.0,0.03101279327377061,1.968347618,0.000315789,2.921325386,0.168873786,0.0,0.0 +370,6426,9280,27175.0,875232.0,0.03104891046031224,1.92734893,0.000315548,2.990437001,0.181187901,0.0,0.0 +380,6358,9187,27025.0,871104.0,0.031023850194695467,1.889258172,0.000308689,2.846738111,0.181651873,0.0,0.0 +390,6272,9081,26652.0,866976.0,0.030741335400287898,1.840892272,0.000329279,2.825270586,0.177422669,0.0,0.0 +400,6204,8993,26532.0,862368.0,0.03076644773460982,1.820608708,0.000296329,2.759355249,0.175583708,0.0,0.0 +410,6118,8864,26274.0,858240.0,0.030613814317673377,1.783961229,0.000290708,2.707626007,0.172954176,0.0,0.0 +420,6014,8740,25901.0,852384.0,0.030386539400082593,1.774576254,0.000288998,2.694176581,0.173939173,0.0,0.0 +430,5928,8629,25498.0,848736.0,0.030042321758473777,1.7065974,0.000284277,2.675798329,0.170062674,0.0,0.0 +440,5842,8523,25125.0,844608.0,0.029747527847238008,1.685087395,0.000287118,2.688215586,0.166480549,0.0,0.0 +450,5738,8399,24752.0,838752.0,0.02951051085422151,1.673553823,0.000274969,2.523253333,0.167824913,0.0,0.0 +460,5670,8316,24662.0,833664.0,0.02958266159987717,1.625105871,0.000272178,2.52817126,0.164730041,0.0,0.0 +470,5548,8161,24211.0,827328.0,0.029264088729016785,1.583826656,0.000262318,2.419247276,0.160768733,0.0,0.0 +480,5426,8006,23760.0,820992.0,0.028940598690364826,1.58433006,0.000264708,2.454129792,0.155746163,0.0,0.0 +490,5358,7918,23640.0,816384.0,0.028956961429915332,1.520887155,0.000253268,2.329551174,0.153813499,0.0,0.0 +500,5272,7807,23237.0,812736.0,0.02859108000629971,1.488167166,0.000248837,2.282665244,0.154234105,0.0,0.0 +510,5150,7647,22756.0,806880.0,0.028202458853856832,1.448681065,0.000247727,2.275316917,0.149501885,0.0,0.0 +520,5028,7487,22022.0,803232.0,0.02741673638500458,1.43939862,0.000236057,2.14942739,0.146771977,0.0,0.0 +530,4906,7350,21679.0,795168.0,0.02726342106322186,1.367826149,0.000242258,2.188588822,0.148076932,0.0,0.0 +540,4838,7257,21529.0,791040.0,0.027216069983818772,1.341798982,0.000230357,2.096237881,0.141709174,0.0,0.0 +550,4752,7151,21156.0,786912.0,0.02688483591557887,1.339939443,0.000227267,2.062687036,0.13782156,0.0,0.0 +560,4684,7068,21066.0,781824.0,0.026944683202357565,1.327848904,0.000222317,2.00294804,0.139508498,0.0,0.0 +570,4634,6993,20916.0,779424.0,0.02683520137948023,1.276183945,0.000224717,2.021180753,0.13573571,0.0,0.0 +580,4548,6882,20766.0,773568.0,0.026844440307768676,1.235522514,0.000212457,1.917354147,0.128401984,0.0,0.0 +590,4498,6807,20616.0,771168.0,0.026733474418025645,1.267249751,0.000212506,1.899792552,0.133449083,0.0,0.0 +600,4376,6657,20195.0,764352.0,0.0264210730134807,1.209891149,0.000205326,1.850663451,0.129490109,0.0,0.0 +610,4326,6582,20045.0,761952.0,0.026307431439250767,1.18887911,0.000203196,1.819359467,0.129183977,0.0,0.0 +620,4204,6422,19564.0,756096.0,0.02587502116133401,1.172245936,0.000212366,1.757557943,0.125887084,0.0,0.0 +630,3836,5980,17558.0,741504.0,0.02367890126014155,1.043747354,0.000175996,1.554965777,0.115650062,0.0,0.0 +640,3732,5856,17438.0,733440.0,0.023775632635253053,1.010298683,0.000174715,1.562411059,0.113877446,0.0,0.0 +650,3628,5714,16957.0,729312.0,0.023250680093019175,0.985957627,0.000170445,1.474744854,0.110990727,0.0,0.0 +660,3506,5549,16446.0,723936.0,0.022717477788091765,0.948042334,0.000161975,1.420057878,0.106426767,0.0,0.0 +670,3420,5448,16103.0,719328.0,0.0223861715378798,0.921840457,0.000156765,1.356400004,0.10491163,0.0,0.0 +680,3316,5319,15700.0,713952.0,0.021990273855945496,0.892707383,0.000162605,1.335548894,0.100909488,0.0,0.0 +690,3212,5200,15357.0,707616.0,0.02170244878578212,0.89578919,0.000149085,1.299462304,0.099173414,0.0,0.0 +700,2916,4871,13850.0,693792.0,0.019962755407960886,0.781393124,0.000134984,1.179737113,0.096642976,0.0,0.0 +710,2722,4598,13123.0,684960.0,0.019158782994627425,0.725161332,0.000122213,1.056813282,0.08619269,0.0,0.0 +720,2636,4492,12750.0,680832.0,0.018727086858432038,0.701632434,0.000128984,1.019551067,0.085388434,0.0,0.0 +730,2532,4373,12407.0,674496.0,0.018394475282284845,0.675037355,0.000119134,0.993660466,0.082709493,0.0,0.0 +740,2428,4231,11926.0,670368.0,0.017790228650532244,0.6435086,0.000109403,0.927737064,0.078423743,0.0,0.0 +750,2342,4125,11553.0,666240.0,0.017340597982708934,0.619218823,0.000106693,0.883708241,0.075467284,0.0,0.0 +760,2274,4032,11403.0,662112.0,0.017222161809482384,0.635081649,0.000103493,0.919860114,0.074058132,0.0,0.0 +770,2234,3977,11313.0,659712.0,0.017148392025611175,0.593953439,0.000110543,0.84404911,0.077019298,0.0,0.0 diff --git a/examples/Project.toml b/examples/Project.toml index ac0e043..0a5d779 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -1,3 +1,9 @@ [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" MetagraphOptimization = "3e869610-d48d-4942-ba70-c1b702a33ca4" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +QEDprocesses = "46de9c38-1bb3-4547-a1ec-da24d767fdad" +StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd" diff --git a/examples/qed_bench.jl b/examples/qed_bench.jl new file mode 100644 index 0000000..67934fa --- /dev/null +++ b/examples/qed_bench.jl @@ -0,0 +1,148 @@ +using MetagraphOptimization +using LIKWID +using CUDA +using UUIDs + +function cpu_bench(compute_function, inputs) + compute_function.(inputs[begin:10]) # make sure it's compiled + + time = @elapsed Threads.@threads for i in eachindex(inputs) + @invokelatest compute_function(inputs[i]) + end + rate = length(inputs) / time + return (time, rate) +end + +function gpu_bench(compute_function, inputs) + CUDA.@sync compute_function.(inputs[begin:10]) # make sure it's compiled + + time = @elapsed CUDA.@sync compute_function.(inputs) + rate = length(inputs) / time + + return (time, rate) +end + +function bench_process( + process::MetagraphOptimization.AbstractProcessDescription, + func, + io::IO = stdout; + use_likwid = true, +) + println(io, "\n--- Benchmarking $(process) ---") + + NFLOPs = GraphProperties(graph).computeEffort + if use_likwid + input = gen_process_input(process) + func(input) # compile first + _, events = @perfmon "FLOPS_DP" func(input) + NFLOPs = first(events["FLOPS_DP"])["RETIRED_SSE_AVX_FLOPS_ALL"] + end + + nInputs = 10000000 # ten million + println(io, "Generating $nInputs inputs with $(Threads.nthreads()) threads...") + + inputs = Vector{typeof(gen_process_input(process))}() + resize!(inputs, nInputs) + processes = Vector{typeof(process)}() + for i in 1:Threads.nthreads() + push!(processes, copy(process)) + end + + Threads.@threads for i in eachindex(inputs) + inputs[i] = gen_process_input(processes[Threads.nthreads()]) + end + + println(io, "Benchmarking CPU with $(Threads.nthreads()) threads...") + (time_cpu, rate_cpu) = cpu_bench(func, inputs) + flops_cpu = (rate_cpu * NFLOPs) / 1024^3 + + println(io, "Benchmarking GPU...") + cuInputs = CuArray(inputs) + (time_gpu, rate_gpu) = gpu_bench(func, cuInputs) + flops_gpu = (rate_gpu * NFLOPs) / 1024^3 + + println(io, "\nBenchmark Summary for $(process):") + + if use_likwid + println(io, "Measured FLOPS by LIKWID: $NFLOPs") + else + println(io, "Total graph compute effort: $NFLOPs") + end + println(io, "Total input size: $(bytes_to_human_readable(Base.summarysize(inputs)))") + println(io, "CPU, $(Threads.nthreads()) threads") + println(io, " Time: $time_cpu") + println(io, " Rate: $rate_cpu") + println(io, " GFLOPS: $flops_cpu") + println(io, "GPU, $(name(first(CUDA.devices())))") + println(io, " Time: $time_gpu") + println(io, " Rate: $rate_gpu") + return println(io, " GFLOPS: $flops_gpu") +end + +# use "mock" machine that only uses cpu +machine = Machine( + [ + MetagraphOptimization.NumaNode( + 0, + 1, + MetagraphOptimization.default_strategy(MetagraphOptimization.NumaNode), + -1.0, + UUIDs.uuid1(), + ), + ], + [-1.0;;], +) +optimizer = ReductionOptimizer() + +# sadly cannot put these in functions because the world age must increase after the function is created which happens only in the global scope + +# compton +process = parse_process("ke->ke", QEDModel()) +graph = gen_graph(process) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) + +# 2-photon compton +process = parse_process("ke->kke", QEDModel()) +graph = gen_graph(process) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) + +# 3-photon compton +process = parse_process("ke->kkke", QEDModel()) +graph = gen_graph(process) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) + +# AB->AB +process = parse_process("AB->AB", ABCModel()) +graph = parse_dag("input/AB->AB.txt", ABCModel()) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) + +# AB->AB^3 +process = parse_process("AB->ABBB", ABCModel()) +graph = parse_dag("input/AB->ABBB.txt", ABCModel()) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) + +exit(0) + +# 4-photon compton +process = parse_process("ke->kkkke", QEDModel()) +graph = gen_graph(process) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) + +# AB->AB^5 +process = parse_process("AB->ABBBBB", ABCModel()) +graph = parse_dag("input/AB->ABBBBB.txt", ABCModel()) +optimize_to_fixpoint!(optimizer, graph) +compute_func = get_compute_function(graph, process, machine) +bench_process(process, compute_func) diff --git a/examples/reduction.ipynb b/examples/reduction.ipynb new file mode 100644 index 0000000..cfa8674 --- /dev/null +++ b/examples/reduction.ipynb @@ -0,0 +1,542 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Threads: 32\n" + ] + } + ], + "source": [ + "#using Pkg\n", + "#Pkg.add(url=\"https://github.com/QEDjl-project/QEDprocesses.jl/\")\n", + "\n", + "using MetagraphOptimization\n", + "using CUDA\n", + "using UUIDs\n", + "using BenchmarkTools\n", + "\n", + "println(\"Threads: $(Threads.nthreads())\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "compute__5aa84716_9ba0_11ee_31df_554757739f76 (generic function with 1 method)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# preparation of graph\n", + "machine = Machine([MetagraphOptimization.NumaNode(0, 1, MetagraphOptimization.default_strategy(MetagraphOptimization.NumaNode), -1.0, UUIDs.uuid1())], [-1.0;;])\n", + "model = QEDModel()\n", + "process = parse_process(\"ke->kkkkke\", model)\n", + "graph = gen_graph(process)\n", + "n_inputs = 10_000\n", + "inputs = [gen_process_input(process) for _ in 1:n_inputs]\n", + "cu_inputs = CuArray(inputs)\n", + "optimizer = ReductionOptimizer()\n", + "\n", + "get_compute_function(graph, process, machine) # run once for compilation" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "bench (generic function with 1 method)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function bench(func, inputs, cu_inputs)\n", + " compile_time = @elapsed func(inputs[1])\n", + "\n", + " single_thread = @elapsed func.(inputs)\n", + " multi_threaded = @elapsed Threads.@threads for i in eachindex(inputs)\n", + " func(inputs[i]) \n", + " end\n", + " \n", + " gpu_compile = 0 #@elapsed CUDA.@sync func.(cu_inputs[1:2])\n", + " gpu = 0 #@elapsed CUDA.@sync func.(cu_inputs)\n", + " return (cpu_compile_time = compile_time, gpu_compile_time = gpu_compile, cpu_single_thread_time = single_thread, cpu_multi_thread_time = multi_threaded, gpu_time = gpu)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# bench and produce data\n", + "using DataFrames\n", + "\n", + "STEPSIZE = 10\n", + "n = 0\n", + "\n", + "df = DataFrame(operations=Int[], graph_nodes=Int[], graph_edges=Int[], graph_ce=Float64[], graph_dt=Float64[], graph_ci=Float64[], gen_func_t=Float64[], cpu_compile_t=Float64[], cpu_st_t=Float64[], cpu_mt_t=Float64[], gpu_compile_t=Float64[], gpu_t=Float64[])\n", + "\n", + "while true\n", + " func_gen_time = @elapsed func = get_compute_function(graph, process, machine)\n", + " res = bench(func, inputs, cu_inputs)\n", + "\n", + " graph_properties = get_properties(graph)\n", + " push!(df, (\n", + " n,\n", + " graph_properties.noNodes,\n", + " graph_properties.noEdges,\n", + " graph_properties.computeEffort,\n", + " graph_properties.data,\n", + " graph_properties.computeIntensity,\n", + " func_gen_time,\n", + " res.cpu_compile_time,\n", + " res.cpu_single_thread_time,\n", + " res.cpu_multi_thread_time,\n", + " res.gpu_compile_time,\n", + " res.gpu_time\n", + " ))\n", + "\n", + " if fixpoint_reached(optimizer, graph)\n", + " break\n", + " end\n", + "\n", + " optimize!(optimizer, graph, STEPSIZE)\n", + " n += STEPSIZE\n", + "end\n", + ";" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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PgkJCbdu3fLy8qqcGDsuLs7d3T01NTUtLS0kJKTaTIKEkLKysk2bNt24cYP7tbS0NDY2tqCgoFGjRsHBwYaGhsHBwXl5efHx8a/pGQilUsntCDx3cD2c7Nnb23vEiBFamez5xVAarGVvb8+ybE6ZvPIMFiHkaWkF9yeNcxPOlL2Y7PkVMNnzKwitxzHZMxVxcXFZT7Jc+jSpWl1xRBJxg+5uKesTDx48OGbMGLrvy8nOzs7IyOCe8zczM6tcrlKpTExMqr04Jyfn6tWrubm5O3fudHZ25gqsZcuWGRsbm5ubT5ky5dKlSx4eHufOnfv000+tra39/PyMjIwqKioGDx7cpUsXkUg0bdq0gwcPent7Hzx4cPny5S1atMjNzX3//fdPnz7t5ubGsmy/fv3y8vLatWu3cuXKyuHIk5OT+/bt2759e3Nz8+nTp+/atatNmzYFBQXt27f38PBwdnbmJhN80bfffrt58+aePXtev3598eLF+/fvt7CweO+99+bOnTts2LCqszVHRkaWlZX5+/svXLhw5MiR3HzMS5YsWbVqVWRkpEKhOHLkyLp169auXWtjY8OduUhJSRk5cmRqauqpU6dUKtVff/1lZmZmb29///59bhofQsiUKVNiY2O7d+++YMGCiIgI7u7yyZMny2SyevXqWVlZTZky5ezZsz4+PlXTPnnypJubm729PSGkqKiodevWISEhTk5O3KBi4eHhhJCuXbvu27fvNRVYdTnZs44VWLrO1dW1RTPvNQn35oV6cktYQtZeuR8U6M994AAA9AZ3ec7M2eKlfzVzsSCEVA7/TdGKFStWrlz5+PHjWbNmVY5CzikoKJg7d+7cuXOrreLu7j5w4MDr169v2LChcmGLFi0WLFhACBGJRNu2beOKj4cPH548eZI76+bv779kyRLubv21a9fOnTt3x44dPXr06NGjBxdhzpw5a9euXbp06f79+1NTU2/cuCGRSK5evVo5FNaHH344ffr0CRMmEEI6dOgwc+bM2NjYNWvWeHp67t69mxDy66+/Hjt2rFq2t27dWrFiRXJyMnd6LCwsbN++ff3799+6dWuvXr1sbW0nTZoUExNjaWm5bt06hmFOnDhBCJk5c6aHh8eYMWPy8vLmz59//fr1GucTnDZt2uzZsxctWsQNjn///n1ueUJCQnR09N27d62trb/44gs3N7dR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", + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# plot data\n", + "using Plots\n", + "using StatsPlots\n", + "\n", + "img = @df df scatter(\n", + " :operations, \n", + " [:gen_func_t, :cpu_st_t, :cpu_mt_t], \n", + " label=[\"Function generation (s)\" \"Single threaded execution (s)\" \"$(Threads.nthreads())-threaded execution (s)\"], \n", + " title=\"$process using $optimizer ($(n_inputs) inputs)\",\n", + " linewidth=2,\n", + " xlabel=\"optimizer steps\",\n", + " ylabel=\"time (s)\",\n", + " yscale=:log10,\n", + " minorgrid=true,\n", + " size=(800, 600),\n", + " fmt=:pdf\n", + ")\n", + "\n", + "savefig(img, \"../images/$(String(process))_exec_$(n_inputs)_inputs.pdf\")\n", + "\n", + "img" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "img = @df df scatter(\n", + " :operations,\n", + " [:graph_nodes, :graph_edges],\n", + " label=[\"Graph Nodes (#)\" \"Graph Edges (#)\"],\n", + " title=\"$process using $optimizer\",\n", + " linewidth=2,\n", + " xlabel=\"optimizer steps\",\n", + " ylims=(0.0, 1.05 * maximum(df.graph_edges)),\n", + " fmt=:pdf,\n", + " size=(800, 600)\n", + ")\n", + "\n", + "savefig(img, \"../images/$(String(process))_graph_properties.pdf\")\n", + "\n", + "img" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.4 (32 Threads)", + "language": "julia", + "name": "julia-1.9" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/images/qed_ke-kke_exec_1000000_inputs.pdf b/images/qed_ke-kke_exec_1000000_inputs.pdf new file mode 100644 index 0000000..c6d5b3b Binary files /dev/null and b/images/qed_ke-kke_exec_1000000_inputs.pdf differ diff --git a/images/qed_ke-kke_graph_properties.pdf b/images/qed_ke-kke_graph_properties.pdf new file mode 100644 index 0000000..83fecf2 Binary files /dev/null and b/images/qed_ke-kke_graph_properties.pdf differ diff --git a/images/qed_ke-kkke_exec_1000000_inputs.pdf b/images/qed_ke-kkke_exec_1000000_inputs.pdf new file mode 100644 index 0000000..3ee95a4 Binary files /dev/null and b/images/qed_ke-kkke_exec_1000000_inputs.pdf differ diff --git a/images/qed_ke-kkke_graph_properties.pdf b/images/qed_ke-kkke_graph_properties.pdf new file mode 100644 index 0000000..fce3bff Binary files /dev/null and b/images/qed_ke-kkke_graph_properties.pdf differ diff --git a/images/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator()_exec_1000000_inputs.pdf b/images/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator()_exec_1000000_inputs.pdf new file mode 100644 index 0000000..a617cb8 Binary files /dev/null and b/images/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator()_exec_1000000_inputs.pdf differ diff --git a/images/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator()_graph_properties.pdf b/images/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator()_graph_properties.pdf new file mode 100644 index 0000000..2602ec9 Binary files /dev/null and b/images/qed_ke-kkke_greedy_optimizer_GlobalMetricEstimator()_graph_properties.pdf differ diff --git a/images/qed_ke-kkkke_exec_100000_inputs.pdf b/images/qed_ke-kkkke_exec_100000_inputs.pdf new file mode 100644 index 0000000..095401c Binary files /dev/null and b/images/qed_ke-kkkke_exec_100000_inputs.pdf differ diff --git a/images/qed_ke-kkkke_graph_properties.pdf b/images/qed_ke-kkkke_graph_properties.pdf new file mode 100644 index 0000000..fabbf22 Binary files /dev/null and b/images/qed_ke-kkkke_graph_properties.pdf differ diff --git a/images/qed_ke-kkkkke_exec_10000_inputs.pdf b/images/qed_ke-kkkkke_exec_10000_inputs.pdf new file mode 100644 index 0000000..4ee404f Binary files /dev/null and b/images/qed_ke-kkkkke_exec_10000_inputs.pdf differ diff --git a/images/qed_ke-kkkkke_graph_properties.pdf b/images/qed_ke-kkkkke_graph_properties.pdf new file mode 100644 index 0000000..0e7639a Binary files /dev/null and b/images/qed_ke-kkkkke_graph_properties.pdf differ diff --git a/notebooks/abc_model_showcase.ipynb b/notebooks/abc_model_showcase.ipynb index 771c26b..865c4a3 100644 --- a/notebooks/abc_model_showcase.ipynb +++ b/notebooks/abc_model_showcase.ipynb @@ -391,7 +391,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Julia 1.9.3", + "display_name": "Julia 1.9.4", "language": "julia", "name": "julia-1.9" }, @@ -399,7 +399,7 @@ "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", - "version": "1.9.3" + "version": "1.9.4" } }, "nbformat": 4, diff --git a/notebooks/large_compton.ipynb b/notebooks/large_compton.ipynb index 7845ece..f5dc873 100644 --- a/notebooks/large_compton.ipynb +++ b/notebooks/large_compton.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -31,13 +31,13 @@ "data": { "text/plain": [ "Graph:\n", - " Nodes: Total: 131069, DataTask: 65539, ComputeTaskQED_Sum: 1, \n", - " ComputeTaskQED_V: 35280, ComputeTaskQED_S2: 5040, ComputeTaskQED_U: 9, \n", - " ComputeTaskQED_S1: 25200\n", - " Edges: 176419\n", - " Total Compute Effort: 549370.0\n", - " Total Data Transfer: 1.0645344e7\n", - " Total Compute Intensity: 0.05160659909158408\n" + " Nodes: Total: 15866, DataTask: 7937, ComputeTaskQED_S2: 720, \n", + " ComputeTaskQED_Sum: 1, ComputeTaskQED_V: 4320, ComputeTaskQED_S1: 2880, \n", + " ComputeTaskQED_U: 8\n", + " Edges: 21617\n", + " Total Compute Effort: 66249.0\n", + " Total Data Transfer: 1.314048e6\n", + " Total Compute Intensity: 0.050415966540035065\n" ] }, "metadata": {}, @@ -47,7 +47,7 @@ "source": [ "machine = get_machine_info()\n", "model = QEDModel()\n", - "process = parse_process(\"ke->kkkkkke\", model)\n", + "process = parse_process(\"ke->kkkkke\", model)\n", "\n", "inputs = [gen_process_input(process) for _ in 1:1e3];\n", "graph = gen_graph(process)" @@ -62,13 +62,13 @@ "data": { "text/plain": [ "Graph:\n", - " Nodes: Total: 14783, DataTask: 7396, ComputeTaskQED_Sum: 1, \n", - " ComputeTaskQED_V: 1819, ComputeTaskQED_S2: 5040, ComputeTaskQED_U: 9, \n", - " ComputeTaskQED_S1: 518\n", - " Edges: 26672\n", - " Total Compute Effort: 77102.0\n", - " Total Data Transfer: 5.063616e6\n", - " Total Compute Intensity: 0.015226668056977465\n" + " Nodes: Total: 2234, DataTask: 1121, ComputeTaskQED_S2: 720, \n", + " ComputeTaskQED_Sum: 1, ComputeTaskQED_V: 312, ComputeTaskQED_S1: 72, \n", + " ComputeTaskQED_U: 8\n", + " Edges: 3977\n", + " Total Compute Effort: 11313.0\n", + " Total Data Transfer: 659712.0\n", + " Total Compute Intensity: 0.017148392025611175\n" ] }, "metadata": {}, @@ -78,6 +78,7 @@ "source": [ "optimizer = ReductionOptimizer()\n", "\n", + "compute_compton = get_compute_function(graph, process, machine)\n", "optimize_to_fixpoint!(optimizer, graph)\n", "graph" ] @@ -91,7 +92,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Calculated 15537.0 results/s, 1295.0 results/s per thread for QED Process: 'ke->kkkkkke' (12 threads)\n" + "Calculated 133942.0 results/s, 11162.0 results/s per thread for QED Process: 'ke->kkkkke' (12 threads)\n" ] } ], @@ -109,6 +110,31 @@ "rate_per_thread = rate / Threads.nthreads()\n", "println(\"Calculated $(round(rate)) results/s, $(round(rate_per_thread)) results/s per thread for $(process) ($(Threads.nthreads()) threads)\")" ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Calculated 17124.0 results/s, 1427.0 results/s per thread for QED Process: 'ke->kkkkke' (12 threads)\n" + ] + } + ], + "source": [ + "bench_result = @benchmark begin\n", + " Threads.@threads :static for i in eachindex(inputs)\n", + " outputs[i] = compute_compton(inputs[i])\n", + " end\n", + "end\n", + "\n", + "rate = length(inputs) / (mean(bench_result.times) / 1.0e9)\n", + "rate_per_thread = rate / Threads.nthreads()\n", + "println(\"Calculated $(round(rate)) results/s, $(round(rate_per_thread)) results/s per thread for $(process) ($(Threads.nthreads()) threads)\")" + ] } ], "metadata": { diff --git a/results/FWK8999_QED.txt b/results/FWK8999_QED.txt new file mode 100644 index 0000000..deaebe9 --- /dev/null +++ b/results/FWK8999_QED.txt @@ -0,0 +1,62 @@ +CPU: AMD EPYC 7452 32 Cores, 64 Threads | 122.8 GFLOPS (?, source: https://www.cpubenchmark.net/cpu.php?cpu=AMD+EPYC+7452) +GPU: A30 24GB | 5.161 TFLOPS (source: https://www.techpowerup.com/gpu-specs/a30-pcie.c3792) + +Benchmark Summary for QED Process: 'ke->ke': +Measured FLOPS by LIKWID: 5657.0 +Total input size: 1.394 GiB +CPU, 64 threads + Time: 0.594810558 + Rate: 1.681207548437632e7 + GFLOPS: 88.57428190774921 +GPU, NVIDIA A30 + Time: 1.547648257 + Rate: 6.461416510353748e6 + GFLOPS: 34.041919930904314 + +Benchmark Summary for QED Process: 'ke->kke': +Measured FLOPS by LIKWID: 16256.0 +Total input size: 1.768 GiB +CPU, 64 threads + Time: 1.294064702 + Rate: 7.7275888790914565e6 + GFLOPS: 116.99244828756034 +GPU, NVIDIA A30 + Time: 4.973188906 + Rate: 2.0107822544072892e6 + GFLOPS: 30.442398346629826 + +Benchmark Summary for QED Process: 'ke->kkke': +Measured FLOPS by LIKWID: 43433.0 +Total input size: 2.632 GiB +CPU, 64 threads + Time: 3.232029091 + Rate: 3.094031556784648e6 + GFLOPS: 125.15398916399816 +GPU, NVIDIA A30 + Time: 14.597070187 + Rate: 685068.9810963502 + GFLOPS: 27.711131662091034 + +Benchmark Summary for ABC Process: 'AB->AB': +Measured FLOPS by LIKWID: 41.0 +Total input size: 2.201 GiB +CPU, 64 threads + Time: 0.688079611 + Rate: 1.453320203089116e7 + GFLOPS: 0.5549390644454747 +GPU, NVIDIA A30 + Time: 0.013803574 + Rate: 7.244500590933913e8 + GFLOPS: 27.662564462822903 + +Benchmark Summary for ABC Process: 'AB->ABBB': +Measured FLOPS by LIKWID: 899.0 +Total input size: 3.079 GiB +CPU, 64 threads + Time: 0.855687624 + Rate: 1.1686507692204276e7 + GFLOPS: 9.784633680518386 +GPU, NVIDIA A30 + Time: 0.014804518 + Rate: 6.754694749265056e8 + GFLOPS: 565.542893445984 diff --git a/src/code_gen/main.jl b/src/code_gen/main.jl index 3b8b24b..94ba03a 100644 --- a/src/code_gen/main.jl +++ b/src/code_gen/main.jl @@ -62,21 +62,12 @@ function gen_input_assignment_code( assignInputs = Vector{Expr}() for (name, symbols) in inputSymbols (type, index) = type_index_from_name(model(processDescription), name) - p = nothing - - if (index > get(in_particles(processDescription), type, 0)) - index -= get(in_particles(processDescription), type, 0) - @assert index <= out_particles(processDescription)[type] "Too few particles of type $type in input particles for this process" - - p = "filter(x -> typeof(x) <: $type, out_particles($(processInputSymbol)))[$(index)]" - else - p = "filter(x -> typeof(x) <: $type, in_particles($(processInputSymbol)))[$(index)]" - end + p = "get_particle($(processInputSymbol), $(type), $(index))" for symbol in symbols device = entry_device(machine) evalExpr = eval(gen_access_expr(device, symbol)) - push!(assignInputs, Meta.parse("$(evalExpr)::ParticleValue{$type} = ParticleValue($p, one(ComplexF64))")) + push!(assignInputs, Meta.parse("$(evalExpr) = ParticleValue{$type, ComplexF64}($p, one(ComplexF64))")) end end @@ -111,10 +102,12 @@ end Execute the code of the given `graph` on the given input particles. This is essentially shorthand for - ```julia - compute_graph = get_compute_function(graph, process) - result = compute_graph(particles) - ``` +```julia +compute_graph = get_compute_function(graph, process) +result = compute_graph(particles) +``` +If an exception occurs during the execution of the generated code, it will be printed for investigation. + See also: [`parse_dag`](@ref), [`parse_process`](@ref), [`gen_process_input`](@ref) """ @@ -135,6 +128,8 @@ function execute(graph::DAG, process::AbstractProcessDescription, machine::Machi result = 0 try result = @eval $func($input) + #functionStr = string(expr) + #println("Function:\n$functionStr") catch e println("Error while evaluating: $e") diff --git a/src/estimator/global_metric.jl b/src/estimator/global_metric.jl index d83a450..3e0a55e 100644 --- a/src/estimator/global_metric.jl +++ b/src/estimator/global_metric.jl @@ -75,3 +75,7 @@ function operation_effect(estimator::GlobalMetricEstimator, graph::DAG, operatio ce::Float64 = s * compute_effort(task(operation.input)) return (data = d, computeEffort = ce, computeIntensity = ce / d)::CDCost end + +function String(::GlobalMetricEstimator) + return "global_metric" +end diff --git a/src/models/abc/compute.jl b/src/models/abc/compute.jl index 6f1857d..8315c60 100644 --- a/src/models/abc/compute.jl +++ b/src/models/abc/compute.jl @@ -1,4 +1,5 @@ using AccurateArithmetic +using StaticArrays """ compute(::ComputeTaskABC_P, data::ABCParticleValue) @@ -75,14 +76,14 @@ function compute(::ComputeTaskABC_S1, data::ABCParticleValue{P})::ABCParticleVal end """ - compute(::ComputeTaskABC_Sum, data::Vector{Float64}) + compute(::ComputeTaskABC_Sum, data::StaticVector) Compute a sum over the vector. Use an algorithm that accounts for accumulated errors in long sums with potentially large differences in magnitude of the summands. Linearly many FLOP with growing data. """ -function compute(::ComputeTaskABC_Sum, data::Vector{Float64})::Float64 - return sum_kbn(data) +function compute(::ComputeTaskABC_Sum, data::StaticVector)::Float64 + return sum(data) end """ @@ -159,5 +160,7 @@ function get_expression(::ComputeTaskABC_Sum, device::AbstractDevice, inExprs::V in = eval.(inExprs) out = eval(outExpr) - return Meta.parse("$out = compute(ComputeTaskABC_Sum(), [$(unroll_symbol_vector(in))])") + return Meta.parse( + "$out = compute(ComputeTaskABC_Sum(), SVector{$(length(inExprs)), Float64}($(unroll_symbol_vector(in))))", + ) end diff --git a/src/models/abc/create.jl b/src/models/abc/create.jl index 0d5f844..ecc0126 100644 --- a/src/models/abc/create.jl +++ b/src/models/abc/create.jl @@ -5,6 +5,20 @@ using ForwardDiff ComputeTaskABC_Sum() = ComputeTaskABC_Sum(0) +function _svector_from_type_in(processDescription::ABCProcessDescription, type, particles) + if haskey(in_particles(processDescription), type) + return SVector{in_particles(processDescription)[type], type}(filter(x -> typeof(x) <: type, particles)) + end + return SVector{0, type}() +end + +function _svector_from_type_out(processDescription::ABCProcessDescription, type, particles) + if haskey(out_particles(processDescription), type) + return SVector{out_particles(processDescription)[type], type}(filter(x -> typeof(x) <: type, particles)) + end + return SVector{0, type}() +end + """ gen_process_input(processDescription::ABCProcessDescription) @@ -58,7 +72,15 @@ function gen_process_input(processDescription::ABCProcessDescription) end end - processInput = ABCProcessInput(processDescription, inputParticles, outputParticles) + inA = _svector_from_type_in(processDescription, ParticleA, inputParticles) + inB = _svector_from_type_in(processDescription, ParticleB, inputParticles) + inC = _svector_from_type_in(processDescription, ParticleC, inputParticles) + + outA = _svector_from_type_out(processDescription, ParticleA, outputParticles) + outB = _svector_from_type_out(processDescription, ParticleB, outputParticles) + outC = _svector_from_type_out(processDescription, ParticleC, outputParticles) + + processInput = ABCProcessInput(processDescription, inA, inB, inC, outA, outB, outC) return return processInput end diff --git a/src/models/abc/particle.jl b/src/models/abc/particle.jl index 8769eaf..d91bc0b 100644 --- a/src/models/abc/particle.jl +++ b/src/models/abc/particle.jl @@ -1,3 +1,5 @@ +using StaticArrays + import QEDbase.mass """ @@ -60,27 +62,30 @@ Input for a ABC Process. Contains the [`ABCProcessDescription`](@ref) of the pro See also: [`gen_process_input`](@ref) """ -struct ABCProcessInput <: AbstractProcessInput +struct ABCProcessInput{N1, N2, N3, N4, N5, N6} <: AbstractProcessInput process::ABCProcessDescription - inParticles::Vector{ABCParticle} - outParticles::Vector{ABCParticle} + inA::SVector{N1, ParticleA} + inB::SVector{N2, ParticleB} + inC::SVector{N3, ParticleC} + outA::SVector{N4, ParticleA} + outB::SVector{N5, ParticleB} + outC::SVector{N6, ParticleC} end ABCParticleValue{ParticleType <: ABCParticle} = ParticleValue{ParticleType, ComplexF64} -""" - PARTICLE_MASSES - -A constant dictionary containing the masses of the different [`ABCParticle`](@ref)s. -""" -const PARTICLE_MASSES = Dict{Type, Float64}(ParticleA => 1.0, ParticleB => 1.0, ParticleC => 0.0) - """ mass(t::Type{T}) where {T <: ABCParticle} Return the mass (at rest) of the given particle type. """ -mass(t::Type{T}) where {T <: ABCParticle} = PARTICLE_MASSES[t] +mass(::ParticleA) = 1.0 +mass(::ParticleB) = 1.0 +mass(::ParticleC) = 0.0 + +mass(::Type{ParticleA}) = 1.0 +mass(::Type{ParticleB}) = 1.0 +mass(::Type{ParticleC}) = 0.0 """ interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: ABCParticle, T2 <: ABCParticle} @@ -126,7 +131,7 @@ Return the factor of the inner edge with the given (virtual) particle. Takes 10 effective FLOP. (3 here + 7 in square(p)) """ function ABC_inner_edge(p::ABCParticle) - return 1.0 / (square(p) - mass(typeof(p)) * mass(typeof(p))) + return 1.0 / (square(p) - mass(p)^2) end """ @@ -166,6 +171,10 @@ function ABC_conserve_momentum(p1::ABCParticle, p2::ABCParticle) return p3 end +function copy(process::ABCProcessDescription) + return ABCProcessDescription(copy(process.inParticles), copy(process.outParticles)) +end + model(::ABCProcessDescription) = ABCModel() model(::ABCProcessInput) = ABCModel() @@ -195,14 +204,29 @@ function in_particles(process::ABCProcessDescription) return process.inParticles end -function in_particles(input::ABCProcessInput) - return input.inParticles -end - function out_particles(process::ABCProcessDescription) return process.outParticles end -function out_particles(input::ABCProcessInput) - return input.outParticles +function get_particle(input::ABCProcessInput, t::Type{Particle}, n::Int)::Particle where {Particle} + if (t <: ParticleA) + if (n > length(input.inA)) + return input.outA[n - length(input.inA)] + else + return input.inA[n] + end + elseif (t <: ParticleB) + if (n > length(input.inB)) + return input.outB[n - length(input.inB)] + else + return input.inB[n] + end + elseif (t <: ParticleC) + if (n > length(input.inC)) + return input.outC[n - length(input.inC)] + else + return input.inC[n] + end + end + @assert false "Invalid type given" end diff --git a/src/models/abc/print.jl b/src/models/abc/print.jl index 26bfb39..9ae692a 100644 --- a/src/models/abc/print.jl +++ b/src/models/abc/print.jl @@ -36,15 +36,26 @@ Pretty print an [`ABCProcessInput`](@ref) (with newlines). """ function show(io::IO, processInput::ABCProcessInput) println(io, "Input for $(processInput.process):") - println(io, " $(length(processInput.inParticles)) Incoming particles:") - for particle in processInput.inParticles - println(io, " $particle") + println(io, "Incoming particles:") + if !isempty(processInput.inA) + println(io, " $(processInput.inA)") end - println(io, " $(length(processInput.outParticles)) Outgoing Particles:") - for particle in processInput.outParticles - println(io, " $particle") + if !isempty(processInput.inB) + println(io, " $(processInput.inB)") + end + if !isempty(processInput.inC) + println(io, " $(processInput.inC)") + end + println(io, "Outgoing particles:") + if !isempty(processInput.outA) + println(io, " $(processInput.outA)") + end + if !isempty(processInput.outB) + println(io, " $(processInput.outB)") + end + if !isempty(processInput.outC) + println(io, " $(processInput.outC)") end - return nothing end """ diff --git a/src/models/interface.jl b/src/models/interface.jl index ca30152..60e6d5e 100644 --- a/src/models/interface.jl +++ b/src/models/interface.jl @@ -80,6 +80,14 @@ Returns a `<: Vector{AbstractParticle}` object with the values of all outgoing p """ function out_particles end +""" + get_particle(::AbstractProcessInput, t::Type, n::Int) + +Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref). +Returns the `n`th particle of type `t`. +""" +function get_particle end + """ parse_process(::AbstractString, ::AbstractPhysicsModel) diff --git a/src/models/qed/compute.jl b/src/models/qed/compute.jl index 1225160..166529a 100644 --- a/src/models/qed/compute.jl +++ b/src/models/qed/compute.jl @@ -1,12 +1,13 @@ +using StaticArrays """ compute(::ComputeTaskQED_P, data::QEDParticleValue) Return the particle as is and initialize the Value. """ -function compute(::ComputeTaskQED_P, data::QEDParticleValue{P})::QEDParticleValue{P} where {P <: QEDParticle} +function compute(::ComputeTaskQED_P, data::QEDParticleValue{P}) where {P <: QEDParticle} # TODO do we actually need this for anything? - return QEDParticleValue{P}(data.p, one(DiracMatrix)) + return ParticleValue{P, DiracMatrix}(data.p, one(DiracMatrix)) end """ @@ -15,7 +16,8 @@ end Compute an outer edge. Return the particle value with the same particle and the value multiplied by an outer_edge factor. """ function compute(::ComputeTaskQED_U, data::PV) where {P <: QEDParticle, PV <: QEDParticleValue{P}} - state = base_state(particle(data.p), direction(data.p), momentum(data.p), spin_or_pol(data.p)) + part::P = data.p + state = base_state(particle(part), direction(part), momentum(part), spin_or_pol(part)) return ParticleValue{P, typeof(state)}( data.p, state, # will return a SLorentzVector{ComplexF64}, BiSpinor or AdjointBiSpinor @@ -34,7 +36,6 @@ function compute( ) where {P1 <: QEDParticle, P2 <: QEDParticle, PV1 <: QEDParticleValue{P1}, PV2 <: QEDParticleValue{P2}} p3 = QED_conserve_momentum(data1.p, data2.p) P3 = interaction_result(P1, P2) - state = QED_vertex() if (typeof(data1.v) <: AdjointBiSpinor) state = data1.v * state @@ -47,7 +48,7 @@ function compute( state = state * data2.v end - dataOut = ParticleValue{P3, typeof(state)}(P3(p3), state) + dataOut = ParticleValue{P3, typeof(state)}(P3(momentum(p3)), state) return dataOut end @@ -64,13 +65,10 @@ function compute( ::ComputeTaskQED_S2, data1::ParticleValue{P1}, data2::ParticleValue{P2}, -)::ComplexF64 where { - P1 <: Union{AntiFermionStateful, FermionStateful}, - P2 <: Union{AntiFermionStateful, FermionStateful}, -} - @assert isapprox(data1.p.momentum, data2.p.momentum, rtol = sqrt(eps()), atol = sqrt(eps())) "$(data1.p.momentum) vs. $(data2.p.momentum)" +) where {P1 <: Union{AntiFermionStateful, FermionStateful}, P2 <: Union{AntiFermionStateful, FermionStateful}} + #@assert isapprox(data1.p.momentum, data2.p.momentum, rtol = sqrt(eps()), atol = sqrt(eps())) "$(data1.p.momentum) vs. $(data2.p.momentum)" - inner = QED_inner_edge(propagation_result(P1)(data1.p)) + inner = QED_inner_edge(propagation_result(P1)(momentum(data1.p))) # inner edge is just a "scalar", data1 and data2 are bispinor/adjointbispinnor, need to keep correct order if typeof(data1.v) <: BiSpinor @@ -80,12 +78,11 @@ function compute( end end -# TODO: S2 when the particles are photons? function compute( ::ComputeTaskQED_S2, data1::ParticleValue{P1}, data2::ParticleValue{P2}, -)::ComplexF64 where {P1 <: PhotonStateful, P2 <: PhotonStateful} +) where {P1 <: PhotonStateful, P2 <: PhotonStateful} # TODO: assert that data1 and data2 are opposites inner = QED_inner_edge(data1.p) # inner edge is just a scalar, data1 and data2 are photon states that are just Complex numbers here @@ -97,9 +94,9 @@ end Compute inner edge (1 input particle, 1 output particle). """ -function compute(::ComputeTaskQED_S1, data::QEDParticleValue{P})::QEDParticleValue where {P <: QEDParticle} +function compute(::ComputeTaskQED_S1, data::QEDParticleValue{P}) where {P <: QEDParticle} newP = propagation_result(P) - new_p = newP(data.p) + new_p = newP(momentum(data.p)) # inner edge is just a scalar, can multiply from either side if typeof(data.v) <: BiSpinor return ParticleValue(new_p, QED_inner_edge(new_p) * data.v) @@ -109,13 +106,13 @@ function compute(::ComputeTaskQED_S1, data::QEDParticleValue{P})::QEDParticleVal end """ - compute(::ComputeTaskQED_Sum, data::Vector{ComplexF64}) + compute(::ComputeTaskQED_Sum, data::StaticVector) Compute a sum over the vector. Use an algorithm that accounts for accumulated errors in long sums with potentially large differences in magnitude of the summands. Linearly many FLOP with growing data. """ -function compute(::ComputeTaskQED_Sum, data::Vector{ComplexF64})::ComplexF64 +function compute(::ComputeTaskQED_Sum, data::StaticVector)::ComplexF64 # TODO: want to use sum_kbn here but it doesn't seem to support ComplexF64, do it element-wise? return sum(data) end @@ -194,5 +191,7 @@ function get_expression(::ComputeTaskQED_Sum, device::AbstractDevice, inExprs::V in = eval.(inExprs) out = eval(outExpr) - return Meta.parse("$out = compute(ComputeTaskQED_Sum(), [$(unroll_symbol_vector(in))])") + return Meta.parse( + "$out = compute(ComputeTaskQED_Sum(), SVector{$(length(inExprs)), ComplexF64}($(unroll_symbol_vector(in))))", + ) end diff --git a/src/models/qed/create.jl b/src/models/qed/create.jl index baf73f2..899e76d 100644 --- a/src/models/qed/create.jl +++ b/src/models/qed/create.jl @@ -1,6 +1,16 @@ ComputeTaskQED_Sum() = ComputeTaskQED_Sum(0) +function _svector_from_type(processDescription::QEDProcessDescription, type, particles) + if haskey(in_particles(processDescription), type) + return SVector{in_particles(processDescription)[type], type}(filter(x -> typeof(x) <: type, particles)) + end + if haskey(out_particles(processDescription), type) + return SVector{out_particles(processDescription)[type], type}(filter(x -> typeof(x) <: type, particles)) + end + return SVector{0, type}() +end + """ gen_process_input(processDescription::QEDProcessDescription) @@ -29,30 +39,37 @@ function gen_process_input(processDescription::QEDProcessDescription) massSum += rand(rng[threadid()]) * (length(inputMasses) + length(outputMasses)) - inputParticles = Vector{QEDParticle}() + particles = Vector{QEDParticle}() initialMomenta = generate_initial_moms(massSum, inputMasses) index = 1 for (particle, n) in processDescription.inParticles for _ in 1:n mom = initialMomenta[index] - push!(inputParticles, particle(mom)) + push!(particles, particle(mom)) index += 1 end end - outputParticles = Vector{QEDParticle}() final_momenta = generate_physical_massive_moms(rng[threadid()], massSum, outputMasses) index = 1 for (particle, n) in processDescription.outParticles for _ in 1:n - push!(outputParticles, particle(final_momenta[index])) + push!(particles, particle(final_momenta[index])) index += 1 end end - processInput = QEDProcessInput(processDescription, inputParticles, outputParticles) + inFerms = _svector_from_type(processDescription, FermionStateful{Incoming, SpinUp}, particles) + outFerms = _svector_from_type(processDescription, FermionStateful{Outgoing, SpinUp}, particles) + inAntiferms = _svector_from_type(processDescription, AntiFermionStateful{Incoming, SpinUp}, particles) + outAntiferms = _svector_from_type(processDescription, AntiFermionStateful{Outgoing, SpinUp}, particles) + inPhotons = _svector_from_type(processDescription, PhotonStateful{Incoming, PolX}, particles) + outPhotons = _svector_from_type(processDescription, PhotonStateful{Outgoing, PolX}, particles) - return return processInput + processInput = + QEDProcessInput(processDescription, inFerms, outFerms, inAntiferms, outAntiferms, inPhotons, outPhotons) + + return processInput end """ diff --git a/src/models/qed/diagrams.jl b/src/models/qed/diagrams.jl index eb207a4..073fe4e 100644 --- a/src/models/qed/diagrams.jl +++ b/src/models/qed/diagrams.jl @@ -82,7 +82,7 @@ end function particle_after_tie(p::FeynmanParticle, t::FeynmanTie) if p == t.in1 || p == t.in2 - return FeynmanParticle(FermionStateful{Incoming}, -1) # placeholder particle and id for tied particles + return FeynmanParticle(FermionStateful{Incoming, SpinUp}, -1) # placeholder particle and id for tied particles end return p end diff --git a/src/models/qed/particle.jl b/src/models/qed/particle.jl index 208a5ae..dfdbb99 100644 --- a/src/models/qed/particle.jl +++ b/src/models/qed/particle.jl @@ -1,4 +1,5 @@ using QEDprocesses +using StaticArrays import QEDbase.mass # TODO check @@ -34,19 +35,6 @@ struct QEDProcessDescription <: AbstractProcessDescription outParticles::Dict{Type{<:QEDParticle{Outgoing}}, Int} end -""" - QEDProcessInput <: AbstractProcessInput - -Input for a QED Process. Contains the [`QEDProcessDescription`](@ref) of the process it is an input for, and the values of the in and out particles. - -See also: [`gen_process_input`](@ref) -""" -struct QEDProcessInput <: AbstractProcessInput - process::QEDProcessDescription - inParticles::Vector{QEDParticle} - outParticles::Vector{QEDParticle} -end - QEDParticleValue{ParticleType <: QEDParticle} = Union{ ParticleValue{ParticleType, BiSpinor}, ParticleValue{ParticleType, AdjointBiSpinor}, @@ -60,51 +48,44 @@ QEDParticleValue{ParticleType <: QEDParticle} = Union{ A photon of the [`QEDModel`](@ref) with its state. """ -struct PhotonStateful{Direction <: ParticleDirection} <: QEDParticle{Direction} +struct PhotonStateful{Direction <: ParticleDirection, Pol <: AbstractDefinitePolarization} <: QEDParticle{Direction} momentum::SFourMomentum - # this will maybe change to the full polarization vector? or do i need both - polarization::AbstractDefinitePolarization end PhotonStateful{Direction}(mom::SFourMomentum) where {Direction <: ParticleDirection} = - PhotonStateful{Direction}(mom, PolX()) # TODO: make allpol possible + PhotonStateful{Direction, PolX}(mom) -PhotonStateful{Dir1}(ph::PhotonStateful{Dir2}) where {Dir1 <: ParticleDirection, Dir2 <: ParticleDirection} = - PhotonStateful{Dir1}(ph.momentum, ph.polarization) +PhotonStateful{Dir, Pol}(ph::PhotonStateful) where {Dir, Pol} = PhotonStateful{Dir, Pol}(ph.momentum) """ FermionStateful <: QEDParticle A fermion of the [`QEDModel`](@ref) with its state. """ -struct FermionStateful{Direction <: ParticleDirection} <: QEDParticle{Direction} +struct FermionStateful{Direction <: ParticleDirection, Spin <: AbstractDefiniteSpin} <: QEDParticle{Direction} momentum::SFourMomentum - spin::AbstractDefiniteSpin # TODO: mass for electron/muon/tauon representation? end FermionStateful{Direction}(mom::SFourMomentum) where {Direction <: ParticleDirection} = - FermionStateful{Direction}(mom, SpinUp()) # TODO: make allspin possible + FermionStateful{Direction, SpinUp}(mom) -FermionStateful{Dir1}(f::FermionStateful{Dir2}) where {Dir1 <: ParticleDirection, Dir2 <: ParticleDirection} = - FermionStateful{Dir1}(f.momentum, f.spin) +FermionStateful{Dir, Spin}(f::FermionStateful) where {Dir, Spin} = FermionStateful{Dir, Spin}(f.momentum) """ AntiFermionStateful <: QEDParticle An anti-fermion of the [`QEDModel`](@ref) with its state. """ -struct AntiFermionStateful{Direction <: ParticleDirection} <: QEDParticle{Direction} +struct AntiFermionStateful{Direction <: ParticleDirection, Spin <: AbstractDefiniteSpin} <: QEDParticle{Direction} momentum::SFourMomentum - spin::AbstractDefiniteSpin # TODO: mass for electron/muon/tauon representation? end AntiFermionStateful{Direction}(mom::SFourMomentum) where {Direction <: ParticleDirection} = - AntiFermionStateful{Direction}(mom, SpinUp()) # TODO: make allspin possible + AntiFermionStateful{Direction, SpinUp}(mom) -AntiFermionStateful{Dir1}(f::AntiFermionStateful{Dir2}) where {Dir1 <: ParticleDirection, Dir2 <: ParticleDirection} = - AntiFermionStateful{Dir1}(f.momentum, f.spin) +AntiFermionStateful{Dir, Spin}(f::AntiFermionStateful) where {Dir, Spin} = AntiFermionStateful{Dir, Spin}(f.momentum) """ interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: QEDParticle, T2 <: QEDParticle} @@ -115,19 +96,33 @@ function interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: QEDParticle @assert false "Invalid interaction between particles of types $t1 and $t2" end -interaction_result(::Type{FermionStateful{Incoming}}, ::Type{FermionStateful{Outgoing}}) = PhotonStateful{Incoming} -interaction_result(::Type{FermionStateful{Incoming}}, ::Type{AntiFermionStateful{Incoming}}) = PhotonStateful{Incoming} -interaction_result(::Type{FermionStateful{Incoming}}, ::Type{<:PhotonStateful}) = FermionStateful{Outgoing} +interaction_result( + ::Type{FermionStateful{Incoming, Spin1}}, + ::Type{FermionStateful{Outgoing, Spin2}}, +) where {Spin1, Spin2} = PhotonStateful{Incoming, PolX} +interaction_result( + ::Type{FermionStateful{Incoming, Spin1}}, + ::Type{AntiFermionStateful{Incoming, Spin2}}, +) where {Spin1, Spin2} = PhotonStateful{Incoming, PolX} +interaction_result(::Type{FermionStateful{Incoming, Spin1}}, ::Type{<:PhotonStateful}) where {Spin1} = + FermionStateful{Outgoing, SpinUp} -interaction_result(::Type{FermionStateful{Outgoing}}, ::Type{FermionStateful{Incoming}}) = PhotonStateful{Incoming} -interaction_result(::Type{FermionStateful{Outgoing}}, ::Type{AntiFermionStateful{Outgoing}}) = PhotonStateful{Incoming} -interaction_result(::Type{FermionStateful{Outgoing}}, ::Type{<:PhotonStateful}) = FermionStateful{Incoming} +interaction_result( + ::Type{FermionStateful{Outgoing, Spin1}}, + ::Type{FermionStateful{Incoming, Spin2}}, +) where {Spin1, Spin2} = PhotonStateful{Incoming, PolX} +interaction_result( + ::Type{FermionStateful{Outgoing, Spin1}}, + ::Type{AntiFermionStateful{Outgoing, Spin2}}, +) where {Spin1, Spin2} = PhotonStateful{Incoming, PolX} +interaction_result(::Type{FermionStateful{Outgoing, Spin1}}, ::Type{<:PhotonStateful}) where {Spin1} = + FermionStateful{Incoming, SpinUp} # antifermion mirror -interaction_result(::Type{AntiFermionStateful{Incoming}}, t2::Type{<:QEDParticle}) = - interaction_result(FermionStateful{Outgoing}, t2) -interaction_result(::Type{AntiFermionStateful{Outgoing}}, t2::Type{<:QEDParticle}) = - interaction_result(FermionStateful{Incoming}, t2) +interaction_result(::Type{AntiFermionStateful{Incoming, Spin}}, t2::Type{<:QEDParticle}) where {Spin} = + interaction_result(FermionStateful{Outgoing, Spin}, t2) +interaction_result(::Type{AntiFermionStateful{Outgoing, Spin}}, t2::Type{<:QEDParticle}) where {Spin} = + interaction_result(FermionStateful{Incoming, Spin}, t2) # photon commutativity interaction_result(t1::Type{<:PhotonStateful}, t2::Type{<:QEDParticle}) = interaction_result(t2, t1) @@ -142,12 +137,18 @@ end Return the type of the inverted direction. E.g. """ -propagation_result(::Type{FermionStateful{Incoming}}) = FermionStateful{Outgoing} -propagation_result(::Type{FermionStateful{Outgoing}}) = FermionStateful{Incoming} -propagation_result(::Type{AntiFermionStateful{Incoming}}) = AntiFermionStateful{Outgoing} -propagation_result(::Type{AntiFermionStateful{Outgoing}}) = AntiFermionStateful{Incoming} -propagation_result(::Type{PhotonStateful{Incoming}}) = PhotonStateful{Outgoing} -propagation_result(::Type{PhotonStateful{Outgoing}}) = PhotonStateful{Incoming} +propagation_result(::Type{FermionStateful{Incoming, Spin}}) where {Spin <: AbstractDefiniteSpin} = + FermionStateful{Outgoing, Spin} +propagation_result(::Type{FermionStateful{Outgoing, Spin}}) where {Spin <: AbstractDefiniteSpin} = + FermionStateful{Incoming, Spin} +propagation_result(::Type{AntiFermionStateful{Incoming, Spin}}) where {Spin <: AbstractDefiniteSpin} = + AntiFermionStateful{Outgoing, Spin} +propagation_result(::Type{AntiFermionStateful{Outgoing, Spin}}) where {Spin <: AbstractDefiniteSpin} = + AntiFermionStateful{Incoming, Spin} +propagation_result(::Type{PhotonStateful{Incoming, Pol}}) where {Pol <: AbstractDefinitePolarization} = + PhotonStateful{Outgoing, Pol} +propagation_result(::Type{PhotonStateful{Outgoing, Pol}}) where {Pol <: AbstractDefinitePolarization} = + PhotonStateful{Incoming, Pol} """ types(::QEDModel) @@ -156,12 +157,12 @@ Return a Vector of the possible types of particle in the [`QEDModel`](@ref). """ function types(::QEDModel) return [ - PhotonStateful{Incoming}, - PhotonStateful{Outgoing}, - FermionStateful{Incoming}, - FermionStateful{Outgoing}, - AntiFermionStateful{Incoming}, - AntiFermionStateful{Outgoing}, + PhotonStateful{Incoming, PolX}, + PhotonStateful{Outgoing, PolX}, + FermionStateful{Incoming, SpinUp}, + FermionStateful{Outgoing, SpinUp}, + AntiFermionStateful{Incoming, SpinUp}, + AntiFermionStateful{Outgoing, SpinUp}, ] end @@ -190,17 +191,23 @@ end @inline momentum(p::FermionStateful)::SFourMomentum = p.momentum @inline momentum(p::AntiFermionStateful)::SFourMomentum = p.momentum -@inline spin_or_pol(p::PhotonStateful)::AbstractPolarization = p.polarization -@inline spin_or_pol(p::FermionStateful)::AbstractSpin = p.spin -@inline spin_or_pol(p::AntiFermionStateful)::AbstractSpin = p.spin +@inline spin_or_pol(p::PhotonStateful{Dir, Pol}) where {Dir, Pol <: AbstractDefinitePolarization} = Pol() +@inline spin_or_pol(p::FermionStateful{Dir, Spin}) where {Dir, Spin <: AbstractDefiniteSpin} = Spin() +@inline spin_or_pol(p::AntiFermionStateful{Dir, Spin}) where {Dir, Spin <: AbstractDefiniteSpin} = Spin() -@inline direction(::PhotonStateful{Dir}) where {Dir <: ParticleDirection} = Dir() -@inline direction(::FermionStateful{Dir}) where {Dir <: ParticleDirection} = Dir() -@inline direction(::AntiFermionStateful{Dir}) where {Dir <: ParticleDirection} = Dir() +@inline direction( + ::Type{P}, +) where {P <: Union{FermionStateful{Incoming}, AntiFermionStateful{Incoming}, PhotonStateful{Incoming}}} = Incoming() +@inline direction( + ::Type{P}, +) where {P <: Union{FermionStateful{Outgoing}, AntiFermionStateful{Outgoing}, PhotonStateful{Outgoing}}} = Outgoing() -@inline direction(::Type{PhotonStateful{Dir}}) where {Dir <: ParticleDirection} = Dir() -@inline direction(::Type{FermionStateful{Dir}}) where {Dir <: ParticleDirection} = Dir() -@inline direction(::Type{AntiFermionStateful{Dir}}) where {Dir <: ParticleDirection} = Dir() +@inline direction( + ::P, +) where {P <: Union{FermionStateful{Incoming}, AntiFermionStateful{Incoming}, PhotonStateful{Incoming}}} = Incoming() +@inline direction( + ::P, +) where {P <: Union{FermionStateful{Outgoing}, AntiFermionStateful{Outgoing}, PhotonStateful{Outgoing}}} = Outgoing() @inline isincoming(::QEDParticle{Incoming}) = true @inline isincoming(::QEDParticle{Outgoing}) = false @@ -216,12 +223,12 @@ end @inline mass(::Type{<:AntiFermionStateful}) = 1.0 @inline mass(::Type{<:PhotonStateful}) = 0.0 -@inline invert_momentum(p::FermionStateful{Dir}) where {Dir <: ParticleDirection} = - FermionStateful{Dir}(-p.momentum, p.spin) -@inline invert_momentum(p::AntiFermionStateful{Dir}) where {Dir <: ParticleDirection} = - AntiFermionStateful{Dir}(-p.momentum, p.spin) -@inline invert_momentum(k::PhotonStateful{Dir}) where {Dir <: ParticleDirection} = - PhotonStateful{Dir}(-k.momentum, k.polarization) +@inline invert_momentum(p::FermionStateful{Dir, Spin}) where {Dir, Spin} = + FermionStateful{Dir, Spin}(-p.momentum, p.spin) +@inline invert_momentum(p::AntiFermionStateful{Dir, Spin}) where {Dir, Spin} = + AntiFermionStateful{Dir, Spin}(-p.momentum, p.spin) +@inline invert_momentum(k::PhotonStateful{Dir, Spin}) where {Dir, Spin} = + PhotonStateful{Dir, Spin}(-k.momentum, k.polarization) """ @@ -240,10 +247,10 @@ function caninteract(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle}) end for (P1, P2) in [(T1, T2), (T2, T1)] - if (P1 == FermionStateful{Incoming} && P2 == AntiFermionStateful{Outgoing}) + if (P1 <: FermionStateful{Incoming} && P2 <: AntiFermionStateful{Outgoing}) return false end - if (P1 == FermionStateful{Outgoing} && P2 == AntiFermionStateful{Incoming}) + if (P1 <: FermionStateful{Outgoing} && P2 <: AntiFermionStateful{Incoming}) return false end end @@ -253,17 +260,17 @@ end function type_index_from_name(::QEDModel, name::String) if startswith(name, "ki") - return (PhotonStateful{Incoming}, parse(Int, name[3:end])) + return (PhotonStateful{Incoming, PolX}, parse(Int, name[3:end])) elseif startswith(name, "ko") - return (PhotonStateful{Outgoing}, parse(Int, name[3:end])) + return (PhotonStateful{Outgoing, PolX}, parse(Int, name[3:end])) elseif startswith(name, "ei") - return (FermionStateful{Incoming}, parse(Int, name[3:end])) + return (FermionStateful{Incoming, SpinUp}, parse(Int, name[3:end])) elseif startswith(name, "eo") - return (FermionStateful{Outgoing}, parse(Int, name[3:end])) + return (FermionStateful{Outgoing, SpinUp}, parse(Int, name[3:end])) elseif startswith(name, "pi") - return (AntiFermionStateful{Incoming}, parse(Int, name[3:end])) + return (AntiFermionStateful{Incoming, SpinUp}, parse(Int, name[3:end])) elseif startswith(name, "po") - return (AntiFermionStateful{Outgoing}, parse(Int, name[3:end])) + return (AntiFermionStateful{Outgoing, SpinUp}, parse(Int, name[3:end])) else throw("Invalid name for a particle in the QED model") end @@ -291,8 +298,7 @@ Return the factor of a vertex in a QED feynman diagram. end @inline function QED_inner_edge(p::QEDParticle) - pos_mom = p.momentum - return propagator(particle(p), pos_mom) + return propagator(particle(p), p.momentum) end """ @@ -300,24 +306,49 @@ end Calculate and return a new particle from two given interacting ones at a vertex. """ -function QED_conserve_momentum(p1::QEDParticle, p2::QEDParticle) - #println("Conserving momentum of \n$(direction(p1)) $(p1)\n and \n$(direction(p2)) $(p2)") - T3 = interaction_result(typeof(p1), typeof(p2)) - # TODO: probably also need to do something about the spin/pol +function QED_conserve_momentum( + p1::P1, + p2::P2, +) where { + Dir1 <: ParticleDirection, + Dir2 <: ParticleDirection, + SpinPol1 <: AbstractSpinOrPolarization, + SpinPol2 <: AbstractSpinOrPolarization, + P1 <: Union{FermionStateful{Dir1, SpinPol1}, AntiFermionStateful{Dir1, SpinPol1}, PhotonStateful{Dir1, SpinPol1}}, + P2 <: Union{FermionStateful{Dir2, SpinPol2}, AntiFermionStateful{Dir2, SpinPol2}, PhotonStateful{Dir2, SpinPol2}}, +} + P3 = interaction_result(P1, P2) p1_mom = p1.momentum - if (typeof(direction(p1)) <: Outgoing) + if (Dir1 <: Outgoing) p1_mom *= -1 end p2_mom = p2.momentum - if (typeof(direction(p2)) <: Outgoing) + if (Dir2 <: Outgoing) p2_mom *= -1 end p3_mom = p1_mom + p2_mom - if (typeof(direction(T3)) <: Incoming) - return T3(-p3_mom) + if (typeof(direction(P3)) <: Incoming) + return P3(-p3_mom) end - return T3(p3_mom) + return P3(p3_mom) +end + +""" + QEDProcessInput <: AbstractProcessInput + +Input for a QED Process. Contains the [`QEDProcessDescription`](@ref) of the process it is an input for, and the values of the in and out particles. + +See also: [`gen_process_input`](@ref) +""" +struct QEDProcessInput{N1, N2, N3, N4, N5, N6} <: AbstractProcessInput + process::QEDProcessDescription + inFerms::SVector{N1, FermionStateful{Incoming, SpinUp}} + outFerms::SVector{N2, FermionStateful{Outgoing, SpinUp}} + inAntiferms::SVector{N3, AntiFermionStateful{Incoming, SpinUp}} + outAntiferms::SVector{N4, AntiFermionStateful{Outgoing, SpinUp}} + inPhotons::SVector{N5, PhotonStateful{Incoming, PolX}} + outPhotons::SVector{N6, PhotonStateful{Outgoing, PolX}} end """ @@ -328,6 +359,10 @@ Return the model of this process description. model(::QEDProcessDescription) = QEDModel() model(::QEDProcessInput) = QEDModel() +function copy(process::QEDProcessDescription) + return QEDProcessDescription(copy(process.inParticles), copy(process.outParticles)) +end + ==(p1::QEDProcessDescription, p2::QEDProcessDescription) = p1.inParticles == p2.inParticles && p1.outParticles == p2.outParticles @@ -335,14 +370,23 @@ function in_particles(process::QEDProcessDescription) return process.inParticles end -function in_particles(input::QEDProcessInput) - return input.inParticles -end - function out_particles(process::QEDProcessDescription) return process.outParticles end -function out_particles(input::QEDProcessInput) - return input.outParticles +function get_particle(input::QEDProcessInput, t::Type{Particle}, n::Int)::Particle where {Particle} + if (t <: FermionStateful{Incoming}) + return input.inFerms[n] + elseif (t <: FermionStateful{Outgoing}) + return input.outFerms[n] + elseif (t <: AntiFermionStateful{Incoming}) + return input.inAntiferms[n] + elseif (t <: AntiFermionStateful{Outgoing}) + return input.outAntiferms[n] + elseif (t <: PhotonStateful{Incoming}) + return input.inPhotons[n] + elseif (t <: PhotonStateful{Outgoing}) + return input.outPhotons[n] + end + @assert false "Invalid type given" end diff --git a/src/models/qed/print.jl b/src/models/qed/print.jl index 76f2166..7ba4160 100644 --- a/src/models/qed/print.jl +++ b/src/models/qed/print.jl @@ -32,20 +32,63 @@ function show(io::IO, process::QEDProcessDescription) return nothing end + +""" + String(process::QEDProcessDescription) + +Create a short string suitable as a filename or similar, describing the given process. + +```jldoctest +julia> using MetagraphOptimization + +julia> String(parse_process("ke->ke", QEDModel())) +qed_ke-ke + +julia> print(parse_process("kk->ep", QEDModel())) +qed_kk-ep +``` +""" +function String(process::QEDProcessDescription) + # types() gives the types in order (QED) instead of random like keys() would + str = "qed_" + for type in types(QEDModel()) + for _ in 1:get(process.inParticles, type, 0) + str = str * String(type) + end + end + str = str * "-" + for type in types(QEDModel()) + for _ in 1:get(process.outParticles, type, 0) + str = str * String(type) + end + end + return str +end + """ show(io::IO, processInput::QEDProcessInput) -Pretty print an [`QEDProcessInput`](@ref) (with newlines). +Pretty print a [`QEDProcessInput`](@ref) (with newlines). """ function show(io::IO, processInput::QEDProcessInput) println(io, "Input for $(processInput.process):") - println(io, " $(length(processInput.inParticles)) Incoming particles:") - for particle in processInput.inParticles - println(io, " $particle") + if !isempty(processInput.inFerms) + println(io, " $(processInput.inFerms)") end - println(io, " $(length(processInput.outParticles)) Outgoing Particles:") - for particle in processInput.outParticles - println(io, " $particle") + if !isempty(processInput.outFerms) + println(io, " $(processInput.outFerms)") + end + if !isempty(processInput.inAntiferms) + println(io, " $(processInput.inAntiferms)") + end + if !isempty(processInput.outAntiferms) + println(io, " $(processInput.outAntiferms)") + end + if !isempty(processInput.inPhotons) + println(io, " $(processInput.inPhotons)") + end + if !isempty(processInput.outPhotons) + println(io, " $(processInput.outPhotons)") end return nothing end @@ -53,7 +96,7 @@ end """ show(io::IO, particle::T) where {T <: QEDParticle} -Pretty print an [`QEDParticle`](@ref) (no newlines). +Pretty print a [`QEDParticle`](@ref) (no newlines). """ function show(io::IO, particle::T) where {T <: QEDParticle} print(io, "$(String(typeof(particle))): $(particle.momentum)") diff --git a/src/node/type.jl b/src/node/type.jl index 9105424..39283d0 100644 --- a/src/node/type.jl +++ b/src/node/type.jl @@ -3,7 +3,7 @@ using UUIDs using Base.Threads # TODO: reliably find out how many threads we're running with (nthreads() returns 1 when precompiling :/) -rng = [Random.MersenneTwister(0) for _ in 1:32] +rng = [Random.MersenneTwister(0) for _ in 1:64] """ Node diff --git a/src/optimization/greedy.jl b/src/optimization/greedy.jl index 20ef336..a777587 100644 --- a/src/optimization/greedy.jl +++ b/src/optimization/greedy.jl @@ -71,3 +71,7 @@ function optimize_to_fixpoint!(optimizer::GreedyOptimizer, graph::DAG) end return nothing end + +function String(optimizer::GreedyOptimizer) + return "greedy_optimizer_$(optimizer.estimator)" +end diff --git a/src/optimization/random_walk.jl b/src/optimization/random_walk.jl index e43507e..41e4d21 100644 --- a/src/optimization/random_walk.jl +++ b/src/optimization/random_walk.jl @@ -47,3 +47,7 @@ function optimize_step!(optimizer::RandomWalkOptimizer, graph::DAG) end end end + +function String(::RandomWalkOptimizer) + return "random_walker" +end diff --git a/src/optimization/reduce.jl b/src/optimization/reduce.jl index 625874a..c6414f5 100644 --- a/src/optimization/reduce.jl +++ b/src/optimization/reduce.jl @@ -28,3 +28,7 @@ function optimize_to_fixpoint!(optimizer::ReductionOptimizer, graph::DAG) end return nothing end + +function String(::ReductionOptimizer) + return "reduction_optimizer" +end diff --git a/test/Project.toml b/test/Project.toml index 5c459da..41c02e3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -4,5 +4,7 @@ QEDbase = "10e22c08-3ccb-4172-bfcf-7d7aa3d04d93" QEDprocesses = "46de9c38-1bb3-4547-a1ec-da24d767fdad" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" diff --git a/test/runtests.jl b/test/runtests.jl index 39a7cca..fa37b10 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -18,12 +18,12 @@ end @safetestset "ABC-Model Unit Tests " begin include("unit_tests_abcmodel.jl") end -@safetestset "QED Feynman Diagram Generation Tests" begin - include("unit_tests_qed_diagrams.jl") -end @safetestset "QED-Model Unit Tests " begin include("unit_tests_qedmodel.jl") end +@safetestset "QED Feynman Diagram Generation Tests" begin + include("unit_tests_qed_diagrams.jl") +end @safetestset "Node Reduction Unit Tests " begin include("node_reduction.jl") end diff --git a/test/unit_tests_execution.jl b/test/unit_tests_execution.jl index dea4072..6c8b43f 100644 --- a/test/unit_tests_execution.jl +++ b/test/unit_tests_execution.jl @@ -2,6 +2,8 @@ using MetagraphOptimization using QEDbase using AccurateArithmetic using Random +using UUIDs +using StaticArrays import MetagraphOptimization.ABCParticle import MetagraphOptimization.interaction_result @@ -27,11 +29,11 @@ function ground_truth_graph_result(input::ABCProcessInput) constant = (1 / 137.0)^2 # calculate particle C in diagram 1 - diagram1_C = ParticleC(input.inParticles[1].momentum + input.inParticles[2].momentum) - diagram2_C = ParticleC(input.inParticles[1].momentum + input.outParticles[2].momentum) + diagram1_C = ParticleC(input.inA[1].momentum + input.inB[1].momentum) + diagram2_C = ParticleC(input.inA[1].momentum + input.outB[1].momentum) - diagram1_Cp = ParticleC(input.outParticles[1].momentum + input.outParticles[2].momentum) - diagram2_Cp = ParticleC(input.outParticles[1].momentum + input.inParticles[2].momentum) + diagram1_Cp = ParticleC(input.outA[1].momentum + input.outB[1].momentum) + diagram2_Cp = ParticleC(input.outA[1].momentum + input.inB[1].momentum) check_particle_reverse_moment(diagram1_Cp.momentum, diagram1_C.momentum) check_particle_reverse_moment(diagram2_Cp.momentum, diagram2_C.momentum) @@ -47,7 +49,18 @@ function ground_truth_graph_result(input::ABCProcessInput) return sum_kbn([diagram1_result, diagram2_result]) end -machine = get_machine_info() +machine = Machine( + [ + MetagraphOptimization.NumaNode( + 0, + 1, + MetagraphOptimization.default_strategy(MetagraphOptimization.NumaNode), + -1.0, + UUIDs.uuid1(), + ), + ], + [-1.0;;], +) process_2_2 = ABCProcessDescription( Dict{Type, Int64}(ParticleA => 1, ParticleB => 1), @@ -56,14 +69,12 @@ process_2_2 = ABCProcessDescription( particles_2_2 = ABCProcessInput( process_2_2, - ABCParticle[ - ParticleA(SFourMomentum(0.823648, 0.0, 0.0, 0.823648)), - ParticleB(SFourMomentum(0.823648, 0.0, 0.0, -0.823648)), - ], - ABCParticle[ - ParticleA(SFourMomentum(0.823648, -0.835061, -0.474802, 0.277915)), - ParticleB(SFourMomentum(0.823648, 0.835061, 0.474802, -0.277915)), - ], + SVector{1}(ParticleA(SFourMomentum(0.823648, 0.0, 0.0, 0.823648))), + SVector{1}(ParticleB(SFourMomentum(0.823648, 0.0, 0.0, -0.823648))), + SVector{0, ParticleC}(), + SVector{1}(ParticleA(SFourMomentum(0.823648, -0.835061, -0.474802, 0.277915))), + SVector{1}(ParticleB(SFourMomentum(0.823648, 0.835061, 0.474802, -0.277915))), + SVector{0, ParticleC}(), ) expected_result = ground_truth_graph_result(particles_2_2) diff --git a/test/unit_tests_qedmodel.jl b/test/unit_tests_qedmodel.jl index af63fdf..14a03ac 100644 --- a/test/unit_tests_qedmodel.jl +++ b/test/unit_tests_qedmodel.jl @@ -3,6 +3,7 @@ using QEDbase using QEDprocesses using StatsBase # for countmap using Random +using UUIDs import MetagraphOptimization.caninteract import MetagraphOptimization.issame @@ -17,32 +18,32 @@ def_momentum = SFourMomentum(1.0, 0.0, 0.0, 0.0) RNG = Random.default_rng() testparticleTypes = [ - PhotonStateful{Incoming}, - PhotonStateful{Outgoing}, - FermionStateful{Incoming}, - FermionStateful{Outgoing}, - AntiFermionStateful{Incoming}, - AntiFermionStateful{Outgoing}, + PhotonStateful{Incoming, PolX}, + PhotonStateful{Outgoing, PolX}, + FermionStateful{Incoming, SpinUp}, + FermionStateful{Outgoing, SpinUp}, + AntiFermionStateful{Incoming, SpinUp}, + AntiFermionStateful{Outgoing, SpinUp}, ] testparticleTypesPropagated = [ - PhotonStateful{Outgoing}, - PhotonStateful{Incoming}, - FermionStateful{Outgoing}, - FermionStateful{Incoming}, - AntiFermionStateful{Outgoing}, - AntiFermionStateful{Incoming}, + PhotonStateful{Outgoing, PolX}, + PhotonStateful{Incoming, PolX}, + FermionStateful{Outgoing, SpinUp}, + FermionStateful{Incoming, SpinUp}, + AntiFermionStateful{Outgoing, SpinUp}, + AntiFermionStateful{Incoming, SpinUp}, ] function compton_groundtruth(input::QEDProcessInput) # p1k1 -> p2k2 # formula: −(ie)^2 (u(p2) slashed(ε1) S(p2 − k1) slashed(ε2) u(p1) + u(p2) slashed(ε2) S(p1 + k1) slashed(ε1) u(p1)) - p1 = input.inParticles[findfirst(x -> typeof(x) <: FermionStateful, input.inParticles)] - p2 = input.outParticles[findfirst(x -> typeof(x) <: FermionStateful, input.outParticles)] + p1 = input.inFerms[1] + p2 = input.outFerms[1] - k1 = input.inParticles[findfirst(x -> typeof(x) <: PhotonStateful, input.inParticles)] - k2 = input.outParticles[findfirst(x -> typeof(x) <: PhotonStateful, input.outParticles)] + k1 = input.inPhotons[1] + k2 = input.outPhotons[1] u_p1 = base_state(Electron(), Incoming(), p1.momentum, spin_or_pol(p1)) u_p2 = base_state(Electron(), Outgoing(), p2.momentum, spin_or_pol(p2)) @@ -117,36 +118,36 @@ end @testset "Known processes" begin compton_process = QEDProcessDescription( - Dict{Type, Int}(PhotonStateful{Incoming} => 1, FermionStateful{Incoming} => 1), - Dict{Type, Int}(PhotonStateful{Outgoing} => 1, FermionStateful{Outgoing} => 1), + Dict{Type, Int}(PhotonStateful{Incoming, PolX} => 1, FermionStateful{Incoming, SpinUp} => 1), + Dict{Type, Int}(PhotonStateful{Outgoing, PolX} => 1, FermionStateful{Outgoing, SpinUp} => 1), ) @test parse_process("ke->ke", QEDModel()) == compton_process positron_compton_process = QEDProcessDescription( - Dict{Type, Int}(PhotonStateful{Incoming} => 1, AntiFermionStateful{Incoming} => 1), - Dict{Type, Int}(PhotonStateful{Outgoing} => 1, AntiFermionStateful{Outgoing} => 1), + Dict{Type, Int}(PhotonStateful{Incoming, PolX} => 1, AntiFermionStateful{Incoming, SpinUp} => 1), + Dict{Type, Int}(PhotonStateful{Outgoing, PolX} => 1, AntiFermionStateful{Outgoing, SpinUp} => 1), ) @test parse_process("kp->kp", QEDModel()) == positron_compton_process trident_process = QEDProcessDescription( - Dict{Type, Int}(PhotonStateful{Incoming} => 1, FermionStateful{Incoming} => 1), - Dict{Type, Int}(FermionStateful{Outgoing} => 2, AntiFermionStateful{Outgoing} => 1), + Dict{Type, Int}(PhotonStateful{Incoming, PolX} => 1, FermionStateful{Incoming, SpinUp} => 1), + Dict{Type, Int}(FermionStateful{Outgoing, SpinUp} => 2, AntiFermionStateful{Outgoing, SpinUp} => 1), ) @test parse_process("ke->eep", QEDModel()) == trident_process pair_production_process = QEDProcessDescription( - Dict{Type, Int}(PhotonStateful{Incoming} => 2), - Dict{Type, Int}(FermionStateful{Outgoing} => 1, AntiFermionStateful{Outgoing} => 1), + Dict{Type, Int}(PhotonStateful{Incoming, PolX} => 2), + Dict{Type, Int}(FermionStateful{Outgoing, SpinUp} => 1, AntiFermionStateful{Outgoing, SpinUp} => 1), ) @test parse_process("kk->pe", QEDModel()) == pair_production_process pair_annihilation_process = QEDProcessDescription( - Dict{Type, Int}(FermionStateful{Incoming} => 1, AntiFermionStateful{Incoming} => 1), - Dict{Type, Int}(PhotonStateful{Outgoing} => 2), + Dict{Type, Int}(FermionStateful{Incoming, SpinUp} => 1, AntiFermionStateful{Incoming, SpinUp} => 1), + Dict{Type, Int}(PhotonStateful{Outgoing, PolX} => 2), ) @test parse_process("pe->kk", QEDModel()) == pair_annihilation_process @@ -160,12 +161,24 @@ end for i in 1:100 input = gen_process_input(process) - @test countmap(typeof.(input.inParticles)) == process.inParticles - @test countmap(typeof.(input.outParticles)) == process.outParticles + @test length(input.inFerms) == get(process.inParticles, FermionStateful{Incoming, SpinUp}, 0) + @test length(input.inAntiferms) == get(process.inParticles, AntiFermionStateful{Incoming, SpinUp}, 0) + @test length(input.inPhotons) == get(process.inParticles, PhotonStateful{Incoming, PolX}, 0) + @test length(input.outFerms) == get(process.outParticles, FermionStateful{Outgoing, SpinUp}, 0) + @test length(input.outAntiferms) == get(process.outParticles, AntiFermionStateful{Outgoing, SpinUp}, 0) + @test length(input.outPhotons) == get(process.outParticles, PhotonStateful{Outgoing, PolX}, 0) @test isapprox( - sum(getfield.(input.inParticles, :momentum)), - sum(getfield.(input.outParticles, :momentum)); + sum([ + getfield.(input.inFerms, :momentum)..., + getfield.(input.inAntiferms, :momentum)..., + getfield.(input.inPhotons, :momentum)..., + ]), + sum([ + getfield.(input.outFerms, :momentum)..., + getfield.(input.outAntiferms, :momentum)..., + getfield.(input.outPhotons, :momentum)..., + ]); atol = sqrt(eps()), ) end @@ -179,7 +192,18 @@ end model = QEDModel() process = parse_process("ke->ke", model) - machine = get_machine_info() + machine = Machine( + [ + MetagraphOptimization.NumaNode( + 0, + 1, + MetagraphOptimization.default_strategy(MetagraphOptimization.NumaNode), + -1.0, + UUIDs.uuid1(), + ), + ], + [-1.0;;], + ) graph = MetagraphOptimization.DAG() @@ -289,3 +313,37 @@ end compton_function = get_compute_function(graph_generated, process, machine) @test isapprox(compton_function.(input), compton_groundtruth.(input)) end + +@testset "Equal results after optimization" for optimizer in + [ReductionOptimizer(), RandomWalkOptimizer(MersenneTwister(0))] + @testset "Process $proc_str" for proc_str in ["ke->ke", "kp->kp", "kk->ep", "ep->kk", "ke->kke", "ke->kkke"] + model = QEDModel() + process = parse_process(proc_str, model) + machine = Machine( + [ + MetagraphOptimization.NumaNode( + 0, + 1, + MetagraphOptimization.default_strategy(MetagraphOptimization.NumaNode), + -1.0, + UUIDs.uuid1(), + ), + ], + [-1.0;;], + ) + graph = gen_graph(process) + + compute_function = get_compute_function(graph, process, machine) + + if (typeof(optimizer) <: RandomWalkOptimizer) + optimize!(optimizer, graph, 100) + elseif (typeof(optimizer) <: ReductionOptimizer) + optimize_to_fixpoint!(optimizer, graph) + end + reduced_compute_function = get_compute_function(graph, process, machine) + + input = [gen_process_input(process) for _ in 1:100] + + @test isapprox(compute_function.(input), reduced_compute_function.(input)) + end +end