From 561d245890e482f527ddee143b836ed614e8714c Mon Sep 17 00:00:00 2001 From: AntonNI8 Date: Fri, 31 Oct 2025 17:44:38 -0400 Subject: [PATCH 01/31] init --- .../demo.py | 21 +++++++ .../metadata.json | 55 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py create mode 100644 demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/metadata.json diff --git a/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py b/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py new file mode 100644 index 0000000000..da69b8e9e5 --- /dev/null +++ b/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py @@ -0,0 +1,21 @@ +r"""How to use PennyLane for Resource Estimation +================================== + + +""" + + +###################################################################### +# +# +# +# +# Conclusion +# ---------- +# +# +# +# References +# ----------- +# +# diff --git a/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/metadata.json b/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/metadata.json new file mode 100644 index 0000000000..4bac768dcb --- /dev/null +++ b/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/metadata.json @@ -0,0 +1,55 @@ +{ + "title": "How to use PennyLane for Resource Estimation", + "authors": [ + { + "username": "Jay" + }, + { + "username": "ANT0N" + } + ], + "executable_stable": true, + "executable_latest": true, + "dateOfPublication": "2025-11-01T10:00:00+00:00", + "dateOfLastModification": "2025-11-01T15:48:14+00:00", + "categories": [ + "Algorithms" + ], + "tags": [], + "previewImages": [ + { + "type": "thumbnail", + "uri": "IMAGENAME.png" + }, + { + "type": "large_thumbnail", + "uri": "IMAGENAME.png" + } + ], + "seoDescription": "WRITE AN SEO DESCRIPTION HERE", + "doi": "", + "references": [ + { + + }, + { + + } + ], + "basedOnPapers": [ + "" + ], + "referencedByPapers": [], + "relatedContent": [ + { + "type": "demonstration", + "id": "...", + "weight": 1.0 + }, + { + "type": "demonstration", + "id": "...", + "weight": 1.0 + } + ] +} \ No newline at end of file From 818b01e426e5c28da5806ef6e1dc7e8b9ba6f1bc Mon Sep 17 00:00:00 2001 From: AntonNI8 Date: Wed, 12 Nov 2025 16:24:31 -0500 Subject: [PATCH 02/31] v0 --- ...28ac98fd-aa4c-478c-9142-5340c86c4fdf_1.png | Bin 0 -> 25938 bytes .../demo.py | 787 +++++++++++++++++- 2 files changed, 779 insertions(+), 8 deletions(-) create mode 100644 _static/demonstration_assets/re_how_to_use_pennylane_for_resource_estimation/re_how_to_use_pennylane_for_resource_estimation_28ac98fd-aa4c-478c-9142-5340c86c4fdf_1.png diff --git a/_static/demonstration_assets/re_how_to_use_pennylane_for_resource_estimation/re_how_to_use_pennylane_for_resource_estimation_28ac98fd-aa4c-478c-9142-5340c86c4fdf_1.png b/_static/demonstration_assets/re_how_to_use_pennylane_for_resource_estimation/re_how_to_use_pennylane_for_resource_estimation_28ac98fd-aa4c-478c-9142-5340c86c4fdf_1.png new file mode 100644 index 0000000000000000000000000000000000000000..87feea0294e783dfb8e609d8b4fabad151cd7569 GIT binary patch literal 25938 zcmZsDbySpJ_@@deC`t)P3PU3x4BcG<(%mw&bhjWebazR2HwcJGjC3PNNq2Yc<-7aa z{bxPL(o$kdj~+b{dGzRU9qLo?Ke*GWeBd7* zM{x~DWg8Pm7ejmFM{i_-* zla0OUd)X=q6>t?aTPaP4M~^TJAO1cr6f7`*^a$1_EhenunzlRl%r$)d`q_bvGaOe0 z4j;D;)2Ol>&mUL&w9g??qLs~LOfbbNe# z29~MLgF(Iugp{cCDuoK}gWTYP$w{f4Hb2M4SOf)h?d~F|`QaVIs#M^RM6d+>SQRwZXf8r*c+24}^uF zKYR8p!wT}Q`{U8;USbge0fB?#W0})m=9YexIAUUJ;I$>#|iJpxSzw0#zP_pgI}$M?hi(-*MbETQli{ot?PMn*i%V;e|KH6^$I$v zf-yfop9D1yQJhbO(=wv_mnuY(@{fz#{rK{jZc-BAb@Zm1nB8Jrp|i_)mqb*n(%`ZE z>hJiS#_`$H?Wv(}`*LC@Ja15w;Vnx;RqOUiSIGo0)p^`^YKxc0_w)N*q4*m0pOaIx zyT-*OB|GDiv^ao4|UbC7q|6_Ve zsb&6$czRnqJ4J%8DeRX2u1>eN1P}ss78CR%%X!-$nk#Ep&VKkJP1tdAaeopBe&-9&~-usU&CduHk^ zC-%#0YFI;F)A_L&br(8Yn7SvP$1WIhHypJ}T?fNQe+u>XQX;*6 zMWBQGesR1$B=6{(@Iq4w8<|n77PZD?;OFL8!6AYs^P5Z}({!8HbstAhPv_VePKjEX z(%7poLiXnrk`cdt$G@Anwa3HygRysToq?U47DQK1S_SC1AhO`))yosdw~F@cu@*b#Ej`@^D1* zEtPwJ0;9I=-HqGL@sOZKgLV4zW=f%0A+ct^70=J}-?igvj-d!Md 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�hXmk;xpw;Roy*Y4Pq-9fVy|&9=RX0h;f=L^lVN;IaDtc z8F%@y-|rcIsWK~Dk7r0Uo$2N@0oP)=WcS|cyS2fgtjh1dPcXhSr#dqt8~#ZQUT+Jn z6d9e&#(wubX$&V?kiU;Dx!#`{*)Fla89vkWGNp)>F0McSij%;zs@8mAFC=IdjX2l_1A|ZFtTuC-uN!wS9*(=E#Xutr6mf=%1x{ z5(s$c1@yeYTMrP#6GRhX4P!4;CMY^#*UH3<6^%H|gL-=898)+2u@*zNtg{b?d(};0 zwuC>}vsES>Azrck#Csmig-Re%32tl*t2P@~j4wC8J2xXL;#f)MZT@Sv2<-Z(_P61Y zK{i+(2A-!Ge96Z!!=V_~_f)58EW;c$n znT>La`erc{ekpBP@}aP4O7PFKRg@N=cX@`WZgA2wXAzN=QVdyQH_UtsGGB)6)0B^v zTyl9E(v%lJ-LlUb3_J0|>0ip)wVe8b#=QNXG+@$F zu}j&e^K7ZQ-Fxlft9eyi^KZzbZgcu(i5`@zOha!I6Yxi`BY9FCEph0uA6+5zR*y1YRWDiPJWQe z^mF{p@MKENc6k#_coGKlY!}kWX{ZO4_!zkSs&}sX)U2zwI(*ZH)MKG@HGRXjFZC}! zWHto`MB?~lH1J22G5j(<$(S@l;ad|ul>Yg?2j%-n`mFq_%|!yl&FB&s-iss?dFHsK z6zF%!7O)|~ouou`=+eXb$08dh%T7;km%UlHLb^q$gNU}il9N)N6K{__?h+A|QkY3z z7S9BG)!lQy&QPGwr9p`9_fMfGKg9BfH<38Yrj-M!BIXJ%n@f_5ald*0&EA^yi4ECM zNHK(ib&M-RqfN)!&=E;_Bu_nsAl+BxEo%1qM*~QC!zJNRNLPNSk4YLcYAGx|XPe|j1cClSh6pSP?`u^c89M_`- z;t?1dk^Dj)XRdC5nm}q}UD-a`*Wo{@B3^-nQaJgg&n=m+Y_(ZW-hUsoz0`A~K)2}6sefF8mt|rVu0g>dS@*7p>8kvdvg)?tRHJ@G#f_xRyrA49d8H>0m8m5{&)9qKE z0K+ccdMO^0ifmT60C8H0!~bUTe-|NU-It;Y;Wzluz;gHn&K!*VrwzTDzy|0jUuwwN zK6J_nIXBRm9D~>5_3q^THPc}l$P5u)N}Xe_wg_);V>?xWtZ{~LRX7p32IFpe(OJgg z$2KnUc_VPz3l&%`RVJvQy~FP>PKoDl2skfRCH%P>I&topPWn^jZEbC=^R0Gp?jrF$ zfykrK#rQ^lim#!S6uy{s@pm`CuwW(rH})?4v*1tYDYWqa2lM`KKip_{{YS9hUJ`HM z43|rgu`e0@H23fppfx7I9x-bNQuf;V7(726K8p?nTKU7;S#pLvzEI!CM+O`Uk3qv| z0mJ%I$rAKU5HiW$18QilV!AJSpAK8&y;Kh#L*S~ew7Z;bxv;i`7rap2*D1Qr9DkQ13s0fuqrXq(pq{V|6Ov;j=$fkd%U{F*Hh2Y&9*wo zKe~vaww*Aq!e+>|2PD(z-{Z%@42ZG%;UAp}+HNIOrL*X+&R!(dev2f!e%4R_#6P^>{N?HC24wn8K!RbKw{ z=g-T_6W61E!&u=|N6M^^-+FDIZb(&CR@MwA{U}-6_Hb4Zi-Z4m&!3PSKXLL^5fsF( zA!iQwAc}nw+PIFdFnVvVg=(k)4+;oVgYt#{A~iW)+5GpD?7Oup=&s;ZWcM`VM6@B( zA`Z#q7i>dhIAYX+@9zH2l0BgG7GLbTweeA5!kCh8$FBk!@=;7_<6V)fCSF~trdIAZF%I-2ol#{J+9jiZ<)TSu zZyNZ09s(*)Q-X{hst#vtP*C2!&EIq9ZDnqIqWoLv!+!^mRbWmI|6^E$x%V`IUr{|P z7yXG#Ffncc{DOSzz8_E39!CAHf)IX+K6HMx3k*dE&22mX#F|^g7Ut&%k{HQ}4OH*o zH|%S^7l6`U8tM0<&A_VJdpi2S;Xh+pPT%d%`bUpQDf7W5b|hZRyl>%lJ%$N{K*XjF zaPC*Z4xUS2PJ^th3};AjguPP-*1Z&Emy2{b-Y2DjjYJ^R@F@k?5C1u{ZHH8Wh5LbN zumeQvHuFzvQ7rvAvSDE*hS~m?aR=OU$lk!R^j`zd|HT^rFKOt=hc-_DZ_*4et!9s# z5Y#0hVD@Yt88PYBbtSO&PgThK?Ve0fwYBbv6^?g301vtJ&uJcs8xZs?L_uS4plxf^ ztx?m%2fCKCZ=~2{F_I9A1U&LKit@&+DQL!b0kb;^p|`d}L80Lx0kk+KTvrDqs1Wmn z@spoFe-8UC0xenx9zCO&*-7tTlL2RqW-kc2`}iA(o*rUJ$PZ;K)G50Z*WU@H#KA-Z?3qvc61a-t6oJU3_Ha7 zvFJyx1{}J=Gu+iv?l~yAYM*`VWF@_vMGEpzT;`244!tS7u z7FavfS8qZ31iHyN@8f+eylIL+QldH~O$Fc#{RB}HA7cyo`pL3#t2Z)64K4ox7@zTz z!|_^jqR>i!F4kX`fP~v;(2pSY1GO^3uLrMh0HB`>T(iL?Km!aMAg0QbQl0z+WigBZ7QF#6Qcy-o02E6BX-P4l1^olA#=It8NlC`jS3FeK*eF@V^iATR z@URBuU$DvzkZJ%Zr}x{MIF2v}k7*#9F<1%)_xcPBfN?g+TqAn8?Ik2Q!yR+#i~!I5 zb!jOggocCfQPR->dgCbq?s~o3t)+S>S(&y7bHwea#jE6IuX#8DgniDi&^Y}|HI%mY zeOY0P7qXQC4asfi2J$jfgiHFt;A>k%_-!9b5b{|1V!jGXO2H_Qk^0wW2i3=5(Hxf| zKVXFP*{$z$b1#3^AlPIC)DdQ(j$nN}I@IDs^1P>$y*zecMj-H>Zt!Rh?jwZN9mKxG=jL~QDCOUv~OkSRlT08dns z!*>nmdRs2X=gVt===P*)_f4zrAaTSoIFn+pAo0t}Mwe0P$^)3;mGVH*(*%Ce?QPb4 zqmts4%}uS9hPmOAzdUNU@tYoXKmt!}YHDH}=b!M}L1g7@U^{KbE^wBkH`-6M!nr{; zITCI0g@=-Bgwb^LAgZ(P8LzoHd4-m)mpc^gsGew%$hi7?Ss|n1a0I@#2k(S6h`RZv zBCjwh^t@`hqp;wpRk$D(+=$T3tg6b!lq^*`7$ESYeSkMNt1Sp02UU+u@!?=u|SNFmd5F`Jr%zNTIxW|5!6tilp@MdEx1?xZ1F%a4?@F% zC^ip%YtDT!6{Znyz8;Qp8`h$t4PnE=P}p?}j!Xeti}#ZR{jQ31I}G6+%d)gb0j27X(Y-dN+YC;K@!kJOdqIOC3vov zaGQsMj@J$gvVoUAbdREKEG5*A$P$05L~?U~LzL@p-?I++f@^Lz$AhDD$Em^wu+rnV zVK=5_XDHYZX=$ES~x&*A#6<9!e~w{j41 zPGvKqE}qJzhm4#VjgAcubpp2$n}Y$+uIk8HFY{&20{kRb;wHUEHY%_WoLG6D?;?H4 zJ12KkK34JuMEbOY@k&A0YAY|cA=80SfjfIn|?NLA@3s#wu7@c#gC|Af5& literal 0 HcmV?d00001 diff --git a/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py b/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py index da69b8e9e5..00868c89a6 100644 --- a/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py +++ b/demonstrations_v2/re_how_to_use_pennylane_for_resource_estimation/demo.py @@ -1,21 +1,792 @@ -r"""How to use PennyLane for Resource Estimation -================================== +r"""How To: Resource Estimation +--------------------------- +""" +import time +import math +import numpy as np -""" +import pennylane as qml +import pennylane.estimator as qre + +###################################################################### +# 1. Introduction: +# ~~~~~~~~~~~~~~~~ +# +# < Insert Elevator Pitch for RE > +# +# We will be using the Kitaev model as an example to explore resource estimation. See `these +# docs `__ for more +# information about the Kitaev hamiltonian. The hamiltonian is defined through nearest neighbor +# interactions on a honeycomb shaped lattice as follows: +# +# :raw-latex:`\begin{align*} +# \hat{H} = K_X \sum_{\langle i,j \rangle \in X}\sigma_i^x\sigma_j^x + +# \:\: K_Y \sum_{\langle i,j \rangle \in Y}\sigma_i^y\sigma_j^y + +# \:\: K_Z \sum_{\langle i,j \rangle \in Z}\sigma_i^z\sigma_j^z +# \end{align*}` +# +# In this demo we will estimate the quantum resources required to evolve the quantum state of a 100 x +# 100 unit honeycomb lattice of spins (thats 20,000 spins!) under the Kitaev hamiltonian. +# + +# Construct the hamiltonian on a 30 units x 30 units lattice +n_cells = [30, 30] +kx, ky, kz = (0.5, 0.6, 0.7) + +t1 = time.time() +spin_ham = qml.spin.kitaev(n_cells, coupling=np.array([kx, ky, kz])) +t2 = time.time() +print(f"Processing time: ~ {round(t2 - t1)} sec") +print("Total number of terms:", len(spin_ham.operands)) +print("Total number of qubits:", len(spin_ham.wires)) ###################################################################### +# .. rst-class:: sphx-glr-script-out # +# .. code-block:: none # +# Processing time: ~ 11 sec +# Total number of terms: 2640 +# Total number of qubits: 1800 + +###################################################################### +# Notice that it took some time to generate this hamiltonian. Resource estimation is important because +# even generating a full description of the hamiltonian can be quite computationally expensive. In +# this case it would take about 1 hour just to generate the dense description of the hamiltonian! # + +###################################################################### +# [Optional] Show how long it would take: +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# Conclusion -# ---------- +# Show how long it would take to generate the full description of the hamiltonian # + +# [Optional] +import matplotlib.pyplot as plt + +n_lst = [i for i in range(6,32)] +time_lst = [] +for n in n_lst: + n_cells = [n, n] + kx, ky, kz = (0.5, 0.6, 0.7) + + t1 = time.time() + spin_ham = qml.spin.kitaev(n_cells, coupling=np.array([kx, ky, kz])) + t2 = time.time() + time_lst.append(t2-t1) + print(f"Finished n = {n} in ~ {time_lst[-1]} sec") + +###################################################################### +# .. rst-class:: sphx-glr-script-out # +# .. code-block:: none # -# References -# ----------- -# +# Finished n = 6 in ~ 0.04488110542297363 sec +# Finished n = 7 in ~ 0.06234097480773926 sec +# Finished n = 8 in ~ 0.09322404861450195 sec +# Finished n = 9 in ~ 0.21576905250549316 sec +# Finished n = 10 in ~ 0.21580290794372559 sec +# Finished n = 11 in ~ 0.30110788345336914 sec +# Finished n = 12 in ~ 0.35100388526916504 sec +# Finished n = 13 in ~ 0.4110829830169678 sec +# Finished n = 14 in ~ 0.6779649257659912 sec +# Finished n = 15 in ~ 0.7971811294555664 sec +# Finished n = 16 in ~ 1.0414037704467773 sec +# Finished n = 17 in ~ 1.6982488632202148 sec +# Finished n = 18 in ~ 1.8217010498046875 sec +# Finished n = 19 in ~ 1.890824794769287 sec +# Finished n = 20 in ~ 2.2494540214538574 sec +# Finished n = 21 in ~ 2.927581787109375 sec +# Finished n = 22 in ~ 3.3197081089019775 sec +# Finished n = 23 in ~ 3.985456705093384 sec +# Finished n = 24 in ~ 4.6444091796875 sec +# Finished n = 25 in ~ 5.6554481983184814 sec +# Finished n = 26 in ~ 6.637783050537109 sec +# Finished n = 27 in ~ 7.614826202392578 sec +# Finished n = 28 in ~ 9.7729651927948 sec +# Finished n = 29 in ~ 11.51502013206482 sec +# Finished n = 30 in ~ 11.752906799316406 sec +# Finished n = 31 in ~ 13.435622215270996 sec + +plt.plot(n_lst, time_lst, ".", label="Measured processing times") + +a, b = (0.000016, 4) +fit_lst = [ + a * n**b for n in (n_lst + [110]) +] +projected_time = a * (100**b) + +plt.plot(n_lst + [110], fit_lst, "--g", label="Best-fit") +plt.plot([100], [projected_time], "*r", label=f"n={100}, time ~ {round(projected_time / 60)} mins") + +plt.xscale("log") +plt.xlabel("Number of unit cells") +plt.yscale("log") +plt.ylabel("Processing time (sec)") + +plt.legend() +plt.show() + +###################################################################### # +# .. figure:: ../_static/demonstration_assets/re_how_to_use_pennylane_for_resource_estimation/re_how_to_use_pennylane_for_resource_estimation_28ac98fd-aa4c-478c-9142-5340c86c4fdf_1.png +# :align: center +# :width: 80% + +###################################################################### +# 2. Compact Hamiltonian Representation +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Thankfully we don’t need a detailed description of our hamiltonian to estimate its resources! The +# geometry of the honeycomb lattice and the structure of the hamiltonian allows us to calculate some +# important quantities directly: +# +# :raw-latex:`\begin{align} +# n_{q} = 2 n^{2}, \\ +# n_{YY} = n_{ZZ} = n * (n - 1), \\ +# n_{XX} = n^{2}, \\ +# \end{align}` +# +# We can capture the key information of our hamiltonian in a compact representation using the +# ``qre.PauliHamiltonian`` class! +# + +n_cell = 100 +n_q = 2 * n_cell**2 +n_xx = n_cell**2 +n_yy = n_cell*(n_cell-1) +n_zz = n_yy + +distribution_of_pauli_words = { + "XX": n_xx, + "YY": n_yy, + "ZZ": n_zz, +} + +kitaev_H = qre.PauliHamiltonian( + num_qubits = n_q, + pauli_dist = distribution_of_pauli_words, +) + +###################################################################### +# 3. Estimate: +# ~~~~~~~~~~~~ +# + +order = 2 +num_steps = 10 + +def circuit(hamiltonian): + qre.UniformStatePrep(num_states = 2**n_q) # Prepare a uniform superposition over all 2^num_qubit basis states + qre.TrotterPauli(hamiltonian, num_steps, order) + return + +print(qre.estimate(circuit)(kitaev_H)) + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# --- Resources: --- +# Total wires: 2.000E+4 +# algorithmic wires: 20000 +# allocated wires: 0 +# zero state: 0 +# any state: 0 +# Total gates : 2.862E+7 +# 'T': 2.622E+7, +# 'CNOT': 1.192E+6, +# 'Z': 3.960E+5, +# 'S': 7.920E+5, +# 'Hadamard': 2.000E+4 + +# We can estimate the resources of individual operators as well! +resources_without_grouping = qre.estimate(qre.TrotterPauli(kitaev_H, num_steps, order)) + + +# Commuting groups: +commuting_groups = [ # Alternatively we can split our terms into groups + {"XX": n_xx}, # of commuting terms, this will help reduce the + {"YY": n_yy}, # cost of Trotterization as we will see: + {"ZZ": n_zz}, +] + +kitaev_H_with_grouping = qre.PauliHamiltonian( + num_qubits = n_q, + commuting_groups = commuting_groups, +) + +resources_with_grouping = qre.estimate(qre.TrotterPauli(kitaev_H_with_grouping, num_steps, order)) + + +# print("Without grouping:", f"\n{resources_without_grouping}\n") # [Optionally show this?] +# print("With grouping:", f"\n{resources_with_grouping}") + +# Just compare T gates: +print("With grouping:", f"\n T counts: {resources_with_grouping.gate_counts["T"]:.3E}\n") +print("Without grouping:", f"\n T counts: {resources_without_grouping.gate_counts["T"]:.3E}") + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# With grouping: +# T counts: 1.791E+07 +# +# Without grouping: +# T counts: 2.622E+07 + +###################################################################### +# 4. Gatesets & Config +# ~~~~~~~~~~~~~~~~~~~~ +# +# The cost of an algorithm is quantified by the number of logical qubits required and the number of +# gates used. Different hardware will natively support different gatesets. The default gateset used by +# ``estimate`` is ``'Toffoli', 'Hadamard', 'X', 'T', 'S', 'Y', 'Z', 'CNOT'``. We can configure the +# gateset to obtain resource estimates at various levels of abstraction +# + +from pennylane.estimator.resources_base import DefaultGateSet +print("Default gateset:\n", DefaultGateSet) + +res = qre.estimate(circuit)(kitaev_H_with_grouping) +print(f"\n{res}") + + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# Default gateset: +# frozenset({'Hadamard', 'CNOT', 'Y', 'Toffoli', 'T', 'X', 'Z', 'S'}) +# +# --- Resources: --- +# Total wires: 2.000E+4 +# algorithmic wires: 20000 +# allocated wires: 0 +# zero state: 0 +# any state: 0 +# Total gates : 1.993E+7 +# 'T': 1.791E+7, +# 'CNOT': 8.140E+5, +# 'Z': 3.960E+5, +# 'S': 7.920E+5, +# 'Hadamard': 2.000E+4 + +# Customize gateset: + +highlevel_gateset = { + 'RX', + 'RY', + 'RZ', + 'Toffoli', + 'Hadamard', + 'X', + 'T', + 'S', + 'Y', + 'Z', + 'CNOT', +} + +high_res = qre.estimate(circuit, gate_set=highlevel_gateset)(kitaev_H_with_grouping) +print(f"High-level resources:\n{high_res}\n") + +lowlevel_gateset = { + 'T', + 'CNOT', + 'Hadamard', +} + +low_res = qre.estimate(circuit, gate_set=lowlevel_gateset)(kitaev_H_with_grouping) +print(f"Low-level resources:\n{low_res}") + + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# High-level resources: +# --- Resources: --- +# Total wires: 2.000E+4 +# algorithmic wires: 20000 +# allocated wires: 0 +# zero state: 0 +# any state: 0 +# Total gates : 2.429E+6 +# 'RX': 1.100E+5, +# 'RY': 1.980E+5, +# 'RZ': 9.900E+4, +# 'CNOT': 8.140E+5, +# 'Z': 3.960E+5, +# 'S': 7.920E+5, +# 'Hadamard': 2.000E+4 +# +# Low-level resources: +# --- Resources: --- +# Total wires: 2.000E+4 +# algorithmic wires: 20000 +# allocated wires: 0 +# zero state: 0 +# any state: 0 +# Total gates : 2.191E+7 +# 'T': 2.108E+7, +# 'CNOT': 8.140E+5, +# 'Hadamard': 2.000E+4 + +###################################################################### +# When decomposing our algorithms to a gateset, it is often the case that we only have some +# approximate decomposition of our building-block into the target gateset (e.g approximate state +# loading to some precision, or rotation synthesis within some precision of the rotation angle. +# +# These approximate decompositions are accurate to within some error threshold; tuning this error +# threshold determines the resource cost of the algorithm. We can set and tune these errors using +# ``ResourceConfig``. +# + +custom_rc = qre.ResourceConfig() +# print(custom_rc) # This print looks pretty ugly :( + +rz_precisions = custom_rc.resource_op_precisions[qre.RZ] +print(rz_precisions) # Notice that the default precision for RZ is 1e-9 + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# {'precision': 1e-09} + +custom_rc.set_precision(qre.RZ, 1e-15) # setting the required precision from 1e-9 --> 1e-15 + +res = qre.estimate( + circuit, + gate_set=lowlevel_gateset, + config=custom_rc, +)(kitaev_H_with_grouping) + +# print(f"New low-level resources:\n{res}") # [Optionally show this?] + +# Just compare T gates: +print("Low precision (1e-9):", f"\n T counts: {low_res.gate_counts["T"]:.3E}\n") +print("High precision (1e-15):", f"\n T counts: {res.gate_counts["T"]:.3E}") # Notice that a more precise estimate requires more T-gates! + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# Low precision (1e-9): +# T counts: 2.108E+07 +# +# High precision (1e-15): +# T counts: 2.325E+07 + +###################################################################### +# 5. Swapping Decompositions +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# There are many ways to decompose a quantum gate into our target gateset. Selecting an alternate +# decomposition is a great way to optimize the cost of your quantum workflow. This can be done easily +# with the ``ResourceConfig`` class. +# +# Let’s explore decompositions for the ``RZ`` gate: +# +# Current decomposition for RZ (single qubit rotation synthesis in general) is: `Efficient Synthesis +# of Universal Repeat-Until-Success Circuits (Bocharov, et al) `__ +# +# Other state of the art methods we could use instead: - `Optimal ancilla-free Clifford+T approximation +# of z-rotations (Ross, Selinger) `__ - `Shorter quantum circuits via +# single-qubit gate approximation (Kliuchnikov et al) `__ +# + +default_cost_RZ = qre.estimate(qre.RZ(precision=1e-9)) # We can also manually set the precision +print(default_cost_RZ) + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# --- Resources: --- +# Total wires: 1 +# algorithmic wires: 1 +# allocated wires: 0 +# zero state: 0 +# any state: 0 +# Total gates : 44 +# 'T': 44 + +# According to paper by Ross & Selinger, we can decompose RZ rotations into T-gates according to: + +def gridsynth_t_cost(error): + return round(3 * math.log2(1/error)) + +# According to paper by Kliuchnikov et al, we can decompose RZ rotations into T-gates according to: + +def mixed_fallback_t_cost(error): + return round(0.56 * math.log2(1/error) + 5.3) + + +# In order to define a resource decomposition we first need to know what the resource_keys are for +# the operator whose decomposition we want to add + +print(qre.RZ.resource_keys) # this tells us all of the REQUIRED arguments our function must take: + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# {'precision'} + +# Now we define our resource decomp: + +def gridsynth_decomp(precision): + t_resource_rep = qre.resource_rep(qre.T) + t_counts = gridsynth_t_cost(precision) + + t_gate_counts = qre.GateCount(t_resource_rep, t_counts) # The GateCounts tell us how many of this type of gate is used in the decomposition + + return [t_gate_counts] # We return a list because there could have been other gates that appear in the decomposition + + +def mixed_fallback_decomp(precision): + t_resource_rep = qre.resource_rep(qre.T) + t_counts = mixed_fallback_t_cost(precision) + + t_gate_counts = qre.GateCount(t_resource_rep, t_counts) # The GateCounts tell us how many of this type of gate is used in the decomposition + + return [t_gate_counts] # We return a list because there could have been other gates that appear in the decomposition + +# Finally we set the new decomposition in our Resource Config + +grisynth_rc = qre.ResourceConfig() +grisynth_rc.set_decomp(qre.RZ, gridsynth_decomp) +grisynth_cost_RZ = qre.estimate(qre.RZ(precision=1e-9), config=grisynth_rc) + +mixed_fallback_rc = qre.ResourceConfig() +mixed_fallback_rc.set_decomp(qre.RZ, mixed_fallback_decomp) +mixed_fallback_cost_RZ = qre.estimate(qre.RZ(precision=1e-9), config=mixed_fallback_rc) + +print("GridSynth decomposition", f"T counts: {grisynth_cost_RZ.gate_counts["T"]}") +print("Default (RUS) decomposition", f"T counts: {default_cost_RZ.gate_counts["T"]}") +print("Mixed Fallback decomposition", f"T counts: {mixed_fallback_cost_RZ.gate_counts["T"]}") + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# GridSynth decomposition T counts: 90 +# Default (RUS) decomposition T counts: 44 +# Mixed Fallback decomposition T counts: 22 + +###################################################################### +# 6. Wrapping it all up +# ~~~~~~~~~~~~~~~~~~~~~ +# +# We can combine all of the features we have seen so far to optimize the cost of Trotterized time +# evolution of the Kitaev hamiltonian: +# + +kitaev_hamiltonian = kitaev_H_with_grouping # use compact hamiltonian with grouping + +custom_gateset = { # Use the low level gateset + 'T', + 'CNOT', + 'Hadamard', +} + +custom_config = qre.ResourceConfig() +custom_config.set_precision(qre.RZ, precision=1e-12) # set higher precision 1e-9 --> 1e-12 +custom_config.set_decomp(qre.RZ, mixed_fallback_decomp) # set alternate decomposition + +resources = qre.estimate(circuit, gate_set = custom_gateset, config = custom_config)(kitaev_hamiltonian) +print(resources) + +###################################################################### +# .. rst-class:: sphx-glr-script-out +# +# .. code-block:: none +# +# --- Resources: --- +# Total wires: 2.000E+4 +# algorithmic wires: 20000 +# allocated wires: 0 +# zero state: 0 +# any state: 0 +# Total gates : 2.033E+7 +# 'T': 1.949E+7, +# 'CNOT': 8.140E+5, +# 'Hadamard': 2.000E+4 + +###################################################################### +# 7. [Optional] Mapping +# ~~~~~~~~~~~~~~~~~~~~~ +# +# If we already had executable circuits / workflows, we could just call estimate on them directly! +# + +###################################################################### +#