From 477f4b941a72dd2f23c11c8b29daacfe3078b04c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 May 2024 15:16:08 +0000 Subject: [PATCH] Deployed 03179771 to dev with MkDocs 1.6.0 and mike 2.0.0 --- dev/objects.inv | Bin 7698 -> 7769 bytes .../emulate/ir/state_vector/index.html | 490 ++++++----- .../bloqade/ir/routine/bloqade/index.html | 830 ++++++++++-------- dev/search/search_index.json | 2 +- dev/sitemap.xml | 254 +++--- dev/sitemap.xml.gz | Bin 1160 -> 1160 bytes 6 files changed, 856 insertions(+), 720 deletions(-) diff --git a/dev/objects.inv b/dev/objects.inv index c72d06fad407fe4889f6f725579474096f53ff68..3a26df6eca0096d74823fbca6f437798515cb64e 100644 GIT binary patch delta 7206 zcmV+>9NFWNJlQ;usDI|kHj_Mf1w3cFe*N$v9s=}nuaLx@O{VP!=Nv#f9Py;fb5Z@l zPW*?<$A6ux)3x9~{PaW3UwF;SjQ{vNe){9T-~I3(|0aG(SX-S_R@bb28(Yt*&fodA^$dSbejfgv0TC+7wBMw;X$WwjWqQg( zGj#2b2c~#u8Gjo)Rw#}Os~ZmXlVR&0+nVJHR~bG`h7Xgk{m+lpwe6aQ;J}JXv%Jo( zc^gRF=MjCyg%(-!*Su+Y9qJSc_%$0^XT$%v_Id*8d=l_$HiXXl6x|JTldLplYgeh3zgUN)yeDRzRL$Ee!8#O zJIk9jkd;^)u>Jx%G`ApUS9Merlj!vU%=f9;vf&h}0$WIp6Q5OOR#o*WFPXTR8^xKM zwTxt?RRliIr$aDUl^hcaNA6Wl!=G~jU?n1vb0jI$(v2*QUZ#=N(MvL;y7>8J!K+Oz zo?S&nXMfeWjXC(9K4KWCzEmau$g1sQfdt6HPx` zfdsS9AToOL8CWUgo$ka-3k5{YM4LM`t|%IKvXNgTbbGl+|ybYxnhIcJa< zC4c2Sc3$88axGZxHS9>ZM`RwEo?7%-XgewaZA1_aLRy^!^N_4HdMuL3DWoG=&oLoM zRuCa8$#f(l(?$@r+)$x5SytEWG%toHj=+6Z|>q!P^3 z($pmDs30Zt6U|U6i3H=5kQqHW30?}JNq>_i`}dpbL6UoxedFb+_X+8#mZ>p3k9%yJgZN+xsiEx}Fe?gPlkum;so|IWJ0Yb9M>(%Vi7(5A^>OX$@YHr8B^;j<&I$VA+wK;`c4m}xqx0_lYi&AAe+^_C-hsHHXXzC_xthxCtknj*~|r%R(L?2m3shPTl@oF_inX*|xN%2<)|^R?*wsE(5%1{xXFr|+zuT9Wl~ zc|C5o85rkvB0e(X17$t*lT-#Nf7@eKL^c%7xwcU%Io~#_rRm(7uU`CjOcF6lp@&$9q#U8NGAVb& z{}1s=^(8W9Ok?PYS#`Y>dHD)VXw&9qu;gdnUQ2;$|E~5W=Y?W^*6`;Uf674jMV=CE zHz?pi+)%q9G#`%DlxB1-@&kY8#pmg_6Xpny@kfP@BYjaPhln3)<&p70twJ*Xr%+h4 z=R8ag9FPqf7F&Du7>yG^%-xCMSoc_JA>kkjNz`0KC6S(!s8rH(6GgxG*w-Ef-pKV=UP;Fqu3ksicXs`fIe%Fq>iSK`d}jgGoz+kb31=<%u@#hP zuzJ+#5qAy?WbmdfSeErSaXhPi%}ie>TY+9=;CWhxH^|cf2lyispGFOl%%@XFCiUqA zlF5C#Qy;3YsIM%}7cqQ~QcDPNJi4Y3@QBuT;2`h{)A7smB=vw0$D!*2L5?VHDjGSr zCw~xd*L*TK!B`3xnkJ@-x_5f0L{%i%BdAnT8Fon_@Y?FezLC3YJ-lrsz%l4bBzp#4 z4{H4#fbZJd^)xO9wCrJ9wY=e@1IF5ud8N1k?Rq!$L5XJz??5RDuv z_Yj>tI=2y>LNa%f_TvICRyA&{?n2{d$y3G_FX@JSnK16Mg`AuVsBX5vI|Eqq$g`?G z!JlZOVUv3J5$sDqf~O+SxOdq%)TAjewMl{SrrTK%c;r37oktn8!ce`G5l9q~EDX^h zH&$aJJMa!`)kWl1e{0uC=ALVpO6KN^9ASm;o5m4ZAj)dDU1!z#k{8zRGpEheJ14AU z#YdAjP?TwV3taz~b5){I$K7DyVg^G$fiZ0|=1jJN0g`8LJo}ao@u6{LmN6V?lgqVO z%=gyB&aNx9byfU0Y`*+Tcl<;EVd-T@fhm~wP>N7k226({e=%EhY@ZuQI&N?P9eJ}b zDwjqeK>*u8x5uKYkt6F314!FQFZT>=}X_RGQ6Y*owc z7e=*$0_iBme+K4$@&y3+z{U2yo42bBdGV|H#7|o;PPt;8tASLVV%^UB77Kjr+g&#m z=PdWh)fz$RCOHDR%7_?TOfrrLFeW`m1Q~^%c@(s_$&N@44R+k|;N~hs9NSohi01Z$ z*{ovRZb9FypERR*X><{x=7%U*Qan<~L-R*FDmaw4_8%)ECHH%&tc2FRW+7gvl z;A7+XlP`u^#^*$P`G(^MFDKi{){VldGVd%Y|C2}K>^`VUUbc{AnbgCham<%-v-o;a$bc#Q*{KjL8M-qBy<4DH+tr|vnEpzwV^RP3J$Y-lj zGm3l(J(wx48o`iXsI4>1ZSbAmx}1+Nv8$-VV=&c#Y#o z@8@iLQxkDut=5A6dPjXGY~Sy!$mP`LNGwC(Nh72-6MOjZU}6vQOv^|ZFS3|q9M{=i z`maN{>s>_bZE5AW=VcRx@p0J_8vV3v#i4sxHa>*68<5yevwQM*pML(&9;6Ey3SdF+FncvDuM z#?S^SFXdZATV+ihoi)zkDor6I%P>cxNZXo4JhY?~?i`Pefhsn6qip&&%6R5$#yQ&6 zrGLZ&Htf}2OXwHY(|uT=#8%2(lh4vz8Su^a^L8Qbg!gcp#sEfpSTC8KutL|Uf17xG z1&#?1Jd56VWnOukX6(YA^CE9^-t0pIPO>**III%@;`vY~UJshSxxrXd{5rk&6@82R zwdAL5`*GVpdC6a|HOH0+TkpQ4P?tr93Y7OwWC+3QR|BS}Zr7~qxYZTVe>+mq z%`sLWwjSyrT?J{9;M|)(zp=bTcj93$UEc*9uGs_>RB?K?;ELX%6}-{<@yM;~zW8e? zBPoc&_3ROU)uHkF1%Lwjh;BYI!`^XaKCbm^t>p1My8F*dT)dB50PihVC=##kavWoP zsY?ElRp)0k?`_XGW;oNue=$UfFS;pexT6{4%pbuVXR>exIdcRt$r=6#qnz0TndRTu zYjx&mgBW*<;|_u=jx|hV(S1Qh9L*F2c?3rgOyTT65Cri8fqjG#2j&f``uso~tj90EO4$VWd9kS4`v3_v ziCL*=oPPYyiYv!H@%k!iXrUPUokE;}uhUFgoSJ?w=ON1B(`u4A&~@e2?_rQr;z9axyAP7#Xzrcp80Bjn(_*S(3al$308W? z2#1_Ey8|0Mt9B0uG)y8nbTe{YihOR+7R;MhCj%RaSgvbt9n+#;4Zvo}_)nlE<0dfR z1P3%C_+n#_!Am4ii>FnKC^qZc!RK5ol)p28C7&)AUcyg01QPZT5{-zyj9}zn{&lIn z>K$I}^IQK`3UKp(7~WygC>zK4kqoM&R+JU^30hX*&I}13y}LuBFE4`Ph|4i<2EP~+ zB@v|t7al#%@Ad5us|lCPVn0}3KcO;P<0DC$}#L#336?r!?A{6 zeYoYs3s=v;utvoX5VrtxTw*s?YL403lxn@}d{Q0&H=Q3C7W*j>`8GLCT zLwO+oHh<_;Imo*S?kFl0$O3oO8wzHCE~SV(+aIz@kw^1G6fp8b(Rq%72}uV1jw1Ta_5k_J}SZ6ET? zaepeFSyezBObM(3G& z+NsXp`L?GGe@=cL{+$6K^y~Fko=?OC50hdYBkfXR7%r`aESGqBS2DuGq^uT&dDkNF zA!079ZaDa+Fl;?8F5WiS;lpuxaq_kQ`LVjTU7HmgU^Q|b(*UZbLZC}7xH`E=X@7{5 zCg9g>aDj3;zyJboW&2ia5P>3*`m)6d4nd8|*E>2PK6_(k`R@=0c$DHY@gE$}BflNi zz)vu!uV&sK6xMewONbfRcP>R1jGGWA%Dq$zZOX{#zN2`Fm*6h* zlgjT7wNygKcv;e6X?|Jag+QoB)|?5Q?U!;0MEs>75=mc{L?!TVG!e}GTa6^+|5hf+ zQGlXgRNTS9-ilRG#{~EqR)L2G*s@mOQGrvLD_;Wwm*Q4&#sYF_seglp0qzyIVvYdz z)ZmI6{@to_dCC2PnpEV+)dvfm_Jx|mg8r>y8G(!subvh<-rtlENcJ}^BtpGVLnY74 zatKEGg*=ieexZWkpi?p%%0S8{i)!5AmKud5#( zl;HOjhm4ux_m+!{n}6ip3rKoN^AbI&M2|0L89LQViVjQmiz1r@(mk%|XXu2#P(vW) zFZ7T|`jR3lX@4z=VB+6MB$@g*DoG~)ZLQw8>3?Uf;HV=2boJmsqk!oLL?vN+4R&p< zU+@6KokJhyTl83}Re!P$rvVQdS%r8P(=t;A zrCPA?^%cw5K(V?h-B#aGUEuc?n<$u;KkIi{#w3N zKEu}#ReHb^YZ>}oJ@x1u_Q)wV)Bz;VtU;jkTN|O0$61*1Ora5z=B@e;dG5DFwxB+B zpd+$Xz?0zjdw*E706;s2p^tw;TvYKV3t)0T{{PFl8*)wE9Ck7s2@?KZ74IB%W?ZRW zs_a|Z^~p94-5WdSMO&5K2-X&2c5|J1%LbDPP4p+PtKjGG>qq-mm1vvUL6X$hGS&h0 zTxoY(E1p^QEd=R9cSkAi`!D~@ZzXD3^R4rlZoAn-`+vVB!{1~WBi(P^E>F`C@s($o9z3Rm=C&$v8^BZgj=Y4cscm?X@SX8>H0?jy7y=221{a z{iSBEu7Bf7;MOl8Y{9Q=Kj0hZ*qE=uxZOtW_~+yy??XuRGDNV(Xp;oUha@;_~ZrQ3Fz-ILSxe6hdJ z&I(gh?BoZ+?Bp8vB=G^M*PA$bSm&n2H*)PXE79E`xw zQhy>5Cj4KQs&2bHmtN}XdC@@3#_g`9x$rE1$w8AI6f}$7wPgKGEX}I&C4YreNIPkP z&(-)~1%}vfZG@s|7LbvOBt#MI*I;Chvfs{fWW(TMaWrCRVT}=DSh5Zfx81veN#EJb zog4^~Jm+oG)_M7A@2gPAVWNsZxFT=bbblBWdxM<5EF34cKzs^{4EhSi3?_!H_69PH zTh#!`uX!Q9hJ_WbNDtD|JwlOC|anW#E7OJ zOOJpy*81iO{;o|23VGBAz*Ihz*^kx5(QfC$v%=(s3!Pp;k87N=j91>zB|KjW-hc8_ z+HchOKGL4zh2=$1{JhMYw~!1$fFpW?OnbQ&-aAR%{rt{@UT8s)m+#&2hUEx^?aPyw zs6%QEfZHWO4tU&##2rp;?@)D~dB?6=R-|Q@yIS*{PXMmmuXf+0ww-j}k%Okwj1a2t ou165a)2b0e@sLV_NSj=IBM1kZ_YYCPLdJ>%mNZKIKV;H(Ts&+RAOHXW delta 7133 zcmV<38zSV{Jd!++sDFM>wwdI?E8scX_3MWZ0Rr@KuaLx@O{VP!=Nv#f9Py;fb6)oE zH-jag+CO|+f}e6$*8#8-c0Q-qvd)^UEIOi>TXE`t4#9Q3=6@dxlD{ZOo^M%x=GBjN z!>X2HL>GTf`adUMtMavCm#6-h0T(MIJ-5`SCOwt6;T#7)^pWw*i}r+ngy!VM?ZPY8 z@X$mr+3T$e&hqwg<<(18^hB&_Dr4(8SJ^w?wx0ga$y?(=}Y;zEn8_-j@-yb5&+1^k)~t+VET+&VphbUq3AH5)=_^>KdY)vGUO8sYE? z(yVAtF+0V$o!2Lpdw2cMZ!9akCY~NKNz)5!PH*ieuYYx#8ZT5<%~vO@lKVCvocZ~_ zV(%=g*FaWcZNT~q=+NAPoL$vXQB0!O2Qc5KX3K_Cs0wT$HI95%m8Y_-&RN04&Dfh1)|#h{f0qESerl3*N~ zl}3+5Gc|=+G^;r#8qNF>;?Yb-A|kEiP>V@xdGw;vS|Po-G%JiBnKs-+W7DiNN_6@g zdo3^AYi=4Kj|feInOb5RWgQilWPYMqNhOhBP=69Kqo*XnOCcf&DaYg^K^`F-2__QB zNY+9v7A>^V%R~!#^a7EP7e5UxiHSxbAu>u1ddRrnr$-&&5gDlGrIvsOQAg#UnVo3* z*$O0>eR`46lTXh|A@}q`j!8W|b%e~*^N>h9vl41~M^Z*F?MUM2Wu0DJ{G=n(63sci z#D6F$=dtzr_Lp1EDz9Nj!aXAM$n?~r&qCW#31}mNXb{rsB$$U}t*lC@9^NDFQBve7~w zy;vmV#ZN>_VxnP4h>Vhheyg$&&zNS|Bl3{MPAwD3(vHeSYYjxRkxnMTd?ZPYo{=PO z3OPxVb4*r}1VqS75*vxkwAMo{H*NIM%T61K^zxG=F@A>Hk`v8QlF$%YN}N7RL4Tz2 z+%NLdc;>q(1KhvcR1cEev(q8`IQjeSmx_(Oa^NrsT6hcY{^n?4CN;97AjLR%mO+Vp5^+M%*OQ$E3-pp%PnvHsEU&y1{xVv=kKfPuwIn8wf(Pvz~JXT>Wpp^clH!IGbOb1MX@{kz(ijOU8^S^b}9e<%ap7kNsw z-JpO6F`>3WXg(aPDb46wEVlOQF&Za;n7b3hvF@?dLc&25lBl_eN+LZcQK_WoCW?OVv9CP}yq%Hf z(PBrB^Wqe5MkFu^M4eH|P_b4usvu!f3o+E}X(^7LF)j7cv!qd9^mx-RPy>$p#2U9d z9n7%K4dzHNEX6uk51{LHeU6a5PXkY#uzD?wVzBH5uAfo@T0023DgE02JCn)>B_Hu!5Bya-Oi(N6;MB5(&D*EHU!%Xh|MgX`-hh8Iu)3Le$gQ9p*BZpg9D0z z$?PXqH`wBL{XR5cz z*iwgMsrx;2J~4c{Lbqu_ucTu&SFfY%I=gcuWsSnjx)K?bgix|E~sU?Is9$ixictqGT6Vv!T3-Lr0b}jayi(kNcDH4i!Qa1yNNF4k-LXZ@<`u8w<@XK zK_`WW-$1u}z@7~_iBak3-pvXf#1HR!z0<)g-uDWO1QT zdx%aRo!f{`A(=Z#^Kpe2s~R>|ccJ042 zZmh;ccHkY>s*A|2f7Y&(%stmGmCVf-Il>CxH;p5-K$PWfyFQhdYnEHT&m1>X@0_rL z;Nyh{S(2>;( zqjG5k5(Kafw0kV78ac8~7(m*tbEK_*6$Q_ooBRR9^=wsA)& zL2}RP?DWuv!nQ5{&hqRGnEl~_R#5TJ(gh7l=-hH9CTO$SQ2IMqcL}VY+AjxNvsEp( zUl`R23Zx?+e;Szk$rk|N0~g!-Zr-jkW%@G}BegikEHw?Skn*OjAfApEZmc{&pAi1_TT6l0Cwl+c~ z@*8FmnB#6RaIsq9xAGxtcs0zj)_=b;aaH_#)$TAoOW4qe4aVWcnnf=RJ17uGZHdY& z@S$=1$rnQ{<8z|DeEso*my_*eYe!*ak+qhT{mG+owjY!QFPhr*%FPHs$}`7|&ewFq z-YBZLe*+AI20u;L=?r*k$S&M|o3)FA@L5Si{K|{gHyfU^x9(Bana4fuiGFyUX_{3B z@%OLCicTLsooa|9CPR&ackxsw9pg_dzwy}Ok%S)FIFfOHtA-KYip>4?-0ut|^4V(C zj3Qq`4`$4(Mlj?TYU>OPfYzsN?@a7`POt4hfA%?8IvNQ9NI7JKrfi3j)8V-guW=md z{hV!YY9bD-)mqSB@2Jm&?fab-xt!V@iDd{pX@t~fVh8B~=X;U%ZhgJ`>ddq9~Pyp;TtG$-cFRY{cut15el)EOMrP&o=d;Yv# zx;f%KOkaG1(H_=|EJv)+Rn0~oU$>|c4?OSMcx7IBlb+a>J!kC*GvoC>G~l#se&lIfcpj5YVI^Ilg2Hg5;a{Jd>HZu_?b(AQhVu|=iUyDur!g><0;<-HSm zrG~c7tQDnxE5BPWkqSjutk-S-;R#w1W zaCvdN^iQXKfGOIoBNC*NF$m_IfB8UZ&o=h}^UhAfEcf7y2A37x1ILrEb_;u1C)#S-sY2ial2=Fm2z)7xrFa8A;6s;6lAc1Pf4k#a;;` zrb)!9EUR-?um+G=>=)AQj;Qe`6Oo%^tUzo%)Iqum(j>vTH-CO(S%L1vfBjy%z6&^9 zvk54u;`D666}>|%c%${>fm_#o@z+vDQV@mf*#rKnL*w-e00s0B-F#q%y%ouPTVu+_n#LUY^ms)1Gn6aHfl6h!S6PQ`B%rGsc-e zf;rA);S6%-2x5{m{1HYuf3pWN%fGSL^1{&uG3*w{9Ryb#YZ%F*`+|x%nkfkK2#z3_ z!r6f!2;v0-`v@Zt)PY>!A>;Uv(Crk*0eYr5{ePf}-ut(-(fWQ*9iivx#oCFA9f32Wb3lT zP(h%#OB!}`zCS(vaTCkyalHL*ar{3W6YxDzJ|* zQ$ZcbM}O-k=eotOdDl2@Ao0a<1eq|pE9l6hIfEo1f;&iT;T%FD3E~nG{t-?g(Fbyi z?dxuVUg13R)qgbQ6$qg%*76gqSc?%3Id66cHh5O;9u8=jL~`h6>(r?5q%lK$iMvST6xvmzY6BJ?mY(J=6}(@^`KTZj^QI2R7tH^i2f6F zA^M#e5cB=%rw$R~N!>>Nva^mtA;$V7UBG(Q)pBjIhA!l4~$o|@LRkxG< zZ39k+BB2wxZonxX_(Kgh31ma{Km4^FPrvg3M=?T^qZ2Jd8Xpe(K7eQDfdL9)KgF2YFJ;-hg=lC!Fp-uPaB$)91&@_P@dB!o z6BNpSv?;shIi@JDs%U$(e0vY9h z&QdVJJkqU7jAwg97m)GjjHuc&{!DRS*_lv&h^N@N zz`d%A`QTu6UN&l6r%!?p*UxlHSGqWV70;|HqL)EKDT-Db&8EtA0;Ox~2v;)A#*Cas z>3V=ilvA^5!cV3IA3bt%B@Q=EwMGp$%E!8e5-s?2&1Nm@Pxg46G{e!V==qlAXWqZX zy^`dJKMyPBlCM?yTCvMh|I2_2R)90tJtqijygMd>`jQFr;%M(D)G({pCV4)8FEa^5 z67c@8Ll5ZQF^GBlxbo^HE4a~l#-4VrvUk4iY5kv*pZkAjKnVSM{gvkvF~Y;7SjR}a zlo*CfYaz=eUfz`q@GvQ>MPc5x2z-c`E30Y_zA5xu4~vVp4YvPqSYDib?S6hNZ%x}~ z1qWD-T*owks<9C0k_)a*E>aqQ;-m@qH5*)@Tn;dRz^QECiVY%ABvN0tIKm;QQTcjD zC&XuO%q;&M!T^s_Tqgd51A64Q!y5Pr2KCj<`-8&zu4M@^1N+W}3UNbw)OY6^*hz(V z^g4>r0e%*AScsPwcMu5jsI?}c!@MkpK%kf9kO=j&AS%IrrHEj-Un?Yk8SvL4NrwE* z;;^_uf8W})s9_(wS}kzk$1Y$4r~R0(de@N?%=z*nvV%gt``Wsg34hOOySVY*ZT+2> zY%i!u1$*2|z|g5)s5vasPiqqi|{6 zlJK}G-DQ!umyF&MlS=5A#p(nm6l%at|aW^w159#NAxqRL0$9PU*3102WQ3xW85O6AVwKjMV*P2G)y zGWyQKBr&7HN%ug!6JJeDM9phz5ho$*ti5CK)9$9lHbhcm0ArSGG zf=DENSrV1NztKc6_ir_ljQ?AiBu4>?f>Ch?1A8l0MI961Ygh#y8eq#>fky>SWv+Y; z2waL=#Tg68rKJvk8V0ym+=@8@*i(ZmZuobr%H<{Z3u;o4A6FkNblMkc4h#BI#WDgJ zA6`8zbiALG5J>iu780ResG*YQWjO?+{6Zed6u(eNGQcm38^+DhqL@@DhB$7=18oHG*DUt`|Rwvwmgo@}I zkS%&F)hbzkhtq%ujjTevi)ooDgHo;HVnD8=gP>U$p*Uy}N-7juw33R3w)ZT=35ck- zJw!5ee3@|sS$m~ep7VTcSbr^EN}u7Yk18GDnKcakuAXXe4twMj8|na(XI3Lnx~+{+ z$>S`{c&5;ZN&QxShdlRNB3n?OTF?R6D&R@*`#r3GSpc9N!_bF6Aug);lLaujAO8RP z(Y%!f`a16*Nvd0cIz2nN($-h8f4SW9cDPi9yl5(-;8#vb8szfD@`QoykzdM& z@28V-lzeS`W|tan6~p%0lhvm1iowzP&COuR-*3NE%++;V3EcW6ge~}$?GpVkiNbsh zhV3?Lhd(C|SvM5PttT3UlDxD0#%)g;%ur8%(_!t<^E<0D_MG#bFeg@n9%h{j#koaNb{wlq9#r`bI@-!51C3(Kr9dCAUxP&|mK**kYexjkR9y0(Ao zF>j~?fDAz;lYN*}5c#wjs*fY{}HRAfZ-& zK1juFeFI1FK(h*dkeklBAjh7v7R_?jCQzCuu65=sd(QH#$#{)AZh1%3(O<i3_`j}Y)wFpoy;kM(qJfx=+g(e2 z<)`c=1AVKlpjqs$CF^ct>8UJUvR62Ng|w3v_)-oJZeWPr)9O&8%RHHRRbiyX1Vwl)7nJrFv&~*UwYwx4Lggc zsE`9RA*&wt?1nN5b$9}xIeeRn|E&R&&@9wXVnox8r3XM8Yc;uoziZQiLLT)2FqIEw z_G7hiwB0%PtT1`uLZ?^I;~J+dJS10J^_ar;x-IaHOM zyknOQ%hRIGU5$BC9{^YGSG#Xg+fKUg$U*ZZ1_;%6*8>RTY1M$Cct|Bdq;)2~5rl)y T`zT-`W5pgz8YTW8>pTPr#=YZ# diff --git a/dev/reference/bloqade/emulate/ir/state_vector/index.html b/dev/reference/bloqade/emulate/ir/state_vector/index.html index 6c49ef77b..a07f77447 100644 --- a/dev/reference/bloqade/emulate/ir/state_vector/index.html +++ b/dev/reference/bloqade/emulate/ir/state_vector/index.html @@ -1,4 +1,4 @@ - State vector - The Neutral Atom SDK

State vector

AnalogGate dataclass

run

run(
+ State vector - The Neutral Atom SDK      

State vector

AnalogGate dataclass

run

run(
     shots=1,
     solver_name="dop853",
     atol=1e-14,
@@ -7,151 +7,61 @@
     interaction_picture=False,
     project_hyperfine=True,
 )
-

Run the emulation with all atoms in the ground state, sampling the final state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype
-def run(
-    self,
-    shots: int = 1,
-    solver_name: str = "dop853",
-    atol: float = 1e-14,
-    rtol: float = 1e-7,
-    nsteps: int = 2_147_483_647,
-    interaction_picture: bool = False,
-    project_hyperfine: bool = True,
-):
-    """Run the emulation with all atoms in the ground state,
-    sampling the final state vector."""
-
-    options = dict(
-        solver_name=solver_name,
-        atol=atol,
-        rtol=rtol,
-        nsteps=nsteps,
-        interaction_picture=interaction_picture,
-    )
-
-    state = self.hamiltonian.space.zero_state()
-    (result,) = self.apply(state, **options)
-    result /= np.linalg.norm(result)
-
-    return self.hamiltonian.space.sample_state_vector(
-        result, shots, project_hyperfine=project_hyperfine
-    )
+

Run the emulation with all atoms in the ground state, sampling the final state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype
+def run(
+    self,
+    shots: int = 1,
+    solver_name: str = "dop853",
+    atol: float = 1e-14,
+    rtol: float = 1e-7,
+    nsteps: int = 2_147_483_647,
+    interaction_picture: bool = False,
+    project_hyperfine: bool = True,
+) -> NDArray[np.uint8]:
+    """Run the emulation with all atoms in the ground state,
+    sampling the final state vector."""
+
+    options = dict(
+        solver_name=solver_name,
+        atol=atol,
+        rtol=rtol,
+        nsteps=nsteps,
+        interaction_picture=interaction_picture,
+    )
+
+    state = self.hamiltonian.space.zero_state()
+    (result,) = self.apply(state, **options)
+    result.normalize()
+
+    return result.sample(shots, project_hyperfine=project_hyperfine)
 

RydbergHamiltonian dataclass

average

average(register, time=None)
-

Get energy average from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take average with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description
float float

average energy at time time

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype
-def average(
-    self,
-    register: StateVector,
-    time: Optional[float] = None,
-) -> float:
-    """Get energy average from RydbergHamiltonian object at time `time` with
-    register `register`
-
-    Args:
-        register (StateVector): The state vector to take average with
-        time (Optional[float], optional): Time value to evaluate average at.
-        Defaults to duration of RydbergHamiltonian.
-
-    Returns:
-        float: average energy at time `time`
-    """
-    return np.vdot(register.data, self._apply(register.data, time)).real
-

average_and_variance

average_and_variance(register, time=None)
-

Get energy average and variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take average and variance with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Type Description
float

Tuple[float, float]: average and variance of energy at time time

float

respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype
-def average_and_variance(
-    self,
-    register: StateVector,
-    time: Optional[float] = None,
-) -> Tuple[float, float]:
-    """Get energy average and variance from RydbergHamiltonian object at time `time`
-    with register `register`
-
-    Args:
-        register (StateVector): The state vector to take average and variance with
-        time (Optional[float], optional): Time value to evaluate average at.
-        Defaults to duration of RydbergHamiltonian.
-
-    Returns:
-        Tuple[float, float]: average and variance of energy at time `time`
-        respectively.
-    """
-    H_register_data = self._apply(register.data, time)
-
-    average = np.vdot(register.data, H_register_data).real
-    square_average = np.vdot(H_register_data, H_register_data).real
-
-    return average, square_average - average**2
-

expectation_value

expectation_value(register, operator, site_indices)
-

Calculate expectation values of one and two body operators.

Parameters:

Name Type Description Default
register ndarray

Register to evaluate expectation value with

required
operator ndarray

Operator to take expectation value of.

required
site_indices (int, Tuple[int, int])

site/sites to evaluate operator at. It can either a single integer or a tuple of two integers for one and two body operator respectively.

required

Raises:

Type Description
ValueError

Error is raised when the dimension of operator is not

Returns:

Name Type Description
complex complex

The expectation value.

Source code in src/bloqade/emulate/ir/state_vector.py
382
-383
-384
+

Get energy average from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take average with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description
float float

average energy at time time

Source code in src/bloqade/emulate/ir/state_vector.py
384
 385
 386
 387
@@ -168,77 +78,154 @@
 398
 399
 400
-401
-402
-403
+401
@beartype
+def average(
+    self,
+    register: StateVector,
+    time: Optional[float] = None,
+) -> float:
+    """Get energy average from RydbergHamiltonian object at time `time` with
+    register `register`
+
+    Args:
+        register (StateVector): The state vector to take average with
+        time (Optional[float], optional): Time value to evaluate average at.
+        Defaults to duration of RydbergHamiltonian.
+
+    Returns:
+        float: average energy at time `time`
+    """
+    return np.vdot(register.data, self._apply(register.data, time)).real
+

average_and_variance

average_and_variance(register, time=None)
+

Get energy average and variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take average and variance with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Type Description
float

Tuple[float, float]: average and variance of energy at time time

float

respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@plum.dispatch
-def expectation_value(  # noqa: F811
-    self, register: np.ndarray, operator: np.ndarray, site_indices: int
-) -> complex:
-    """Calculate expectation values of one and two body operators.
-
-    Args:
-        register (np.ndarray): Register to evaluate expectation value with
-        operator (np.ndarray): Operator to take expectation value of.
-        site_indices (int, Tuple[int, int]): site/sites to evaluate `operator` at.
-            It can either a single integer or a tuple of two integers for one and
-            two body operator respectively.
-
-    Raises:
-        ValueError: Error is raised when the dimension of `operator` is not
-        consistent with `site` argument. The size of the operator must fit the
-        size of the local hilbert space of `site` depending on the number of sites
-        and the number of levels inside each atom, e.g. for two site expectation v
-        alue with a three level atom the operator must be a 9 by 9 array.
-
-    Returns:
-        complex: The expectation value.
-    """
-    self._check_register(register)
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
@beartype
+def average_and_variance(
+    self,
+    register: StateVector,
+    time: Optional[float] = None,
+) -> Tuple[float, float]:
+    """Get energy average and variance from RydbergHamiltonian object at time `time`
+    with register `register`
+
+    Args:
+        register (StateVector): The state vector to take average and variance with
+        time (Optional[float], optional): Time value to evaluate average at.
+        Defaults to duration of RydbergHamiltonian.
+
+    Returns:
+        Tuple[float, float]: average and variance of energy at time `time`
+        respectively.
+    """
+    H_register_data = self._apply(register.data, time)
+
+    average = np.vdot(register.data, H_register_data).real
+    square_average = np.vdot(H_register_data, H_register_data).real
+
+    return average, square_average - average**2
+

tocsr

tocsr(time)
+

Return the Hamiltonian as a csr matrix at time time.

Parameters:

Name Type Description Default
time float

time to evaluate the Hamiltonian at.

required

Returns:

Name Type Description
csr_matrix csr_matrix

The Hamiltonian as a csr matrix.

Source code in src/bloqade/emulate/ir/state_vector.py
def tocsr(self, time: float) -> csr_matrix:
+    """Return the Hamiltonian as a csr matrix at time `time`.
+
+    Args:
+        time (float): time to evaluate the Hamiltonian at.
+
+    Returns:
+        csr_matrix: The Hamiltonian as a csr matrix.
+
+    """
+    diagonal = sum(
+        (detuning.get_diagonal(time) for detuning in self.detuning_ops),
+        start=self.rydberg,
+    )
+
+    hamiltonian = diags(diagonal).tocsr()
+    for rabi_op in self.rabi_ops:
+        hamiltonian = hamiltonian + rabi_op.tocsr(time)
+
+    return hamiltonian
 

variance

variance(register, time=None)
-

Get the energy variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take variance with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description
complex float

variance of energy at time time respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype
-def variance(
-    self,
-    register: StateVector,
-    time: Optional[float] = None,
-) -> float:
-    """Get the energy variance from RydbergHamiltonian object at
-    time `time` with register `register`
-
-    Args:
-        register (StateVector): The state vector to take variance with
-        time (Optional[float], optional): Time value to evaluate average at.
-        Defaults to duration of RydbergHamiltonian.
-
-    Returns:
-        complex: variance of energy at time `time` respectively.
-    """
-
-    _, var = self.average_and_variance(register, time)
-    return var
+

Get the energy variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take variance with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description
complex float

variance of energy at time time respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype
+def variance(
+    self,
+    register: StateVector,
+    time: Optional[float] = None,
+) -> float:
+    """Get the energy variance from RydbergHamiltonian object at
+    time `time` with register `register`
+
+    Args:
+        register (StateVector): The state vector to take variance with
+        time (Optional[float], optional): Time value to evaluate average at.
+        Defaults to duration of RydbergHamiltonian.
+
+    Returns:
+        complex: variance of energy at time `time` respectively.
+    """
+
+    _, var = self.average_and_variance(register, time)
+    return var
 

StateVector dataclass

local_trace

local_trace(matrix, site_index)
-

return trace of an operator over the StateVector.

Parameters:

Name Type Description Default
matrix ndarray

Square matrix representing operator in the local hilbert space.

required
site_index int | Tuple[int, int]

sites to apply one body operator to.

required

Returns:

Name Type Description
complex complex

the trace of the operator over the state-vector.

Raises:

Type Description
ValueError

Error is raised when the dimension of operator is not

ValueError

Error is raised when the site argument is out of bounds.

Source code in src/bloqade/emulate/ir/state_vector.py
174
-175
+

return trace of an operator over the StateVector.

Parameters:

Name Type Description Default
matrix ndarray

Square matrix representing operator in the local hilbert space.

required
site_index int | Tuple[int, int]

sites to apply one body operator to.

required

Returns:

Name Type Description
complex complex

the trace of the operator over the state-vector.

Raises:

Type Description
ValueError

Error is raised when the dimension of operator is not

ValueError

Error is raised when the site argument is out of bounds.

Source code in src/bloqade/emulate/ir/state_vector.py
175
 176
 177
 178
@@ -262,30 +249,55 @@
 196
 197
 198
-199
@plum.dispatch
-def local_trace(  # noqa: F811
-    self, matrix: np.ndarray, site_index: Union[int, Tuple[int, int]]
-) -> complex:  # noqa: F811
-    """return trace of an operator over the StateVector.
-
-    Args:
-        matrix (np.ndarray): Square matrix representing operator in the local
-            hilbert space.
-        site_index (int | Tuple[int, int]): sites to apply one body operator to.
-
-    Returns:
-        complex: the trace of the operator over the state-vector.
-
-    Raises:
-        ValueError: Error is raised when the dimension of `operator` is not
-        consistent with `site` argument. The size of the operator must fit
-        the size of the local hilbert space of `site` depending on the number
-        of sites and the number of levels inside each atom, e.g. for two site
-        expectation value with a three level atom the operator must be a 9 by
-        9 array.
-
-        ValueError: Error is raised when the `site` argument is out of bounds.
-
-    """
-    ...
+199
+200
@plum.dispatch
+def local_trace(  # noqa: F811
+    self, matrix: np.ndarray, site_index: Union[int, Tuple[int, int]]
+) -> complex:  # noqa: F811
+    """return trace of an operator over the StateVector.
+
+    Args:
+        matrix (np.ndarray): Square matrix representing operator in the local
+            hilbert space.
+        site_index (int | Tuple[int, int]): sites to apply one body operator to.
+
+    Returns:
+        complex: the trace of the operator over the state-vector.
+
+    Raises:
+        ValueError: Error is raised when the dimension of `operator` is not
+        consistent with `site` argument. The size of the operator must fit
+        the size of the local hilbert space of `site` depending on the number
+        of sites and the number of levels inside each atom, e.g. for two site
+        expectation value with a three level atom the operator must be a 9 by
+        9 array.
+
+        ValueError: Error is raised when the `site` argument is out of bounds.
+
+    """
+    ...
+

norm

norm()
+

Return the norm of the state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
def norm(self) -> float:
+    """Return the norm of the state vector."""
+    return np.linalg.norm(self.data)
+

normalize

normalize()
+

Normalize the state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
def normalize(self) -> None:
+    """Normalize the state vector."""
+    data = self.data
+    data /= np.linalg.norm(data)
+

sample

sample(shots, project_hyperfine=True)
+

Sample the state vector and return bitstrings.

Source code in src/bloqade/emulate/ir/state_vector.py
def sample(self, shots: int, project_hyperfine: bool = True) -> NDArray:
+    """Sample the state vector and return bitstrings."""
+    return self.space.sample_state_vector(
+        self.data, shots, project_hyperfine=project_hyperfine
+    )
 
\ No newline at end of file diff --git a/dev/reference/bloqade/ir/routine/bloqade/index.html b/dev/reference/bloqade/ir/routine/bloqade/index.html index 0913bfbb5..99713c334 100644 --- a/dev/reference/bloqade/ir/routine/bloqade/index.html +++ b/dev/reference/bloqade/ir/routine/bloqade/index.html @@ -1,4 +1,128 @@ - Bloqade - The Neutral Atom SDK

Bloqade

BloqadePythonRoutine

Bases: RoutineBase

run

run(
+ Bloqade - The Neutral Atom SDK      

Bloqade

BloqadeEmulation dataclass

Data class to hold the Hamiltonian and metadata for a given set of parameters

hamiltonian property

hamiltonian
+

Return the Hamiltonian object for the given task data.

metadata property

metadata
+

The metadata for the given task data.

evolve

evolve(
+    state=None,
+    solver_name="dop853",
+    atol=1e-07,
+    rtol=1e-14,
+    nsteps=2147483647,
+    times=(),
+    interaction_picture=False,
+)
+

Evolve an initial state vector using the Hamiltonian

Parameters:

Name Type Description Default
state Optional[StateVector]

The initial state vector to

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults

1e-07
rtol float

Relative tolerance for adaptive step in

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647
times Sequence[float]

The times to evaluate the state vector

()
interaction_picture bool

Use the interaction picture when

False

Returns:

Type Description
Iterator[StateVector]

Iterator[StateVector]: An iterator of the state vectors at each time step.

Source code in src/bloqade/ir/routine/bloqade.py
def evolve(
+    self,
+    state: Optional[StateVector] = None,
+    solver_name: str = "dop853",
+    atol: float = 1e-7,
+    rtol: float = 1e-14,
+    nsteps: int = 2147483647,
+    times: Sequence[float] = (),
+    interaction_picture: bool = False,
+) -> Iterator[StateVector]:
+    """Evolve an initial state vector using the Hamiltonian
+
+    Args:
+        state (Optional[StateVector], optional): The initial state vector to
+        evolve. if not provided, the zero state will be used. Defaults to None.
+        solver_name (str, optional): Which SciPy Solver to use. Defaults to
+        "dop853".
+        atol (float, optional): Absolute tolerance for ODE solver. Defaults
+        to 1e-14.
+        rtol (float, optional): Relative tolerance for adaptive step in
+        ODE solver. Defaults to 1e-7.
+        nsteps (int, optional): Maximum number of steps allowed per integration
+        step. Defaults to 2147483647.
+        times (Sequence[float], optional): The times to evaluate the state vector
+        at. Defaults to (). If not provided the state will be evaluated at
+        the end of the bloqade program.
+        interaction_picture (bool, optional): Use the interaction picture when
+        solving schrodinger equation. Defaults to False.
+
+    Returns:
+        Iterator[StateVector]: An iterator of the state vectors at each time step.
+
+    """
+    state = self.zero_state(np.complex128) if state is None else state
+
+    U = AnalogGate(self.hamiltonian)
+
+    return U.apply(
+        state,
+        times=times,
+        solver_name=solver_name,
+        atol=atol,
+        rtol=rtol,
+        nsteps=nsteps,
+        interaction_picture=interaction_picture,
+    )
+

fock_state

fock_state(fock_state_str, dtype=np.float64)
+

Return the fock state for the given Hamiltonian.

Source code in src/bloqade/ir/routine/bloqade.py
84
+85
+86
+87
+88
+89
+90
+91
def fock_state(
+    self, fock_state_str: str, dtype: np.dtype = np.float64
+) -> StateVector:
+    """Return the fock state for the given Hamiltonian."""
+    index = self.hamiltonian.space.fock_state_to_index(fock_state_str)
+    data = np.zeros(self.hamiltonian.space.size, dtype=dtype)
+    data[index] = 1
+    return StateVector(data, self.hamiltonian.space)
+

zero_state

zero_state(dtype=np.float64)
+

Return the zero state for the given Hamiltonian.

Source code in src/bloqade/ir/routine/bloqade.py
80
+81
+82
def zero_state(self, dtype: np.dtype = np.float64) -> StateVector:
+    """Return the zero state for the given Hamiltonian."""
+    return self.hamiltonian.space.zero_state(dtype)
+

BloqadePythonRoutine

Bases: RoutineBase

run

run(
     shots,
     args=(),
     name=None,
@@ -13,67 +137,7 @@
     rtol=1e-14,
     nsteps=2147483647,
 )
-

Run the current program using bloqade python backend

Parameters:

Name Type Description Default
shots int

number of shots after running state vector simulation

required
args Tuple[LiteralType, ...]

The values for parameters defined

()
name Optional[str]

Name to give this run. Defaults to None.

None
blockade_radius float

Use the Blockade subspace given a

0.0
waveform_runtime str

(bool, optional): Use Numba to compile the waveforms,

'interpret'
interaction_picture bool

Use the interaction picture when

False
cache_matrices bool

Reuse previously evaluated matrcies when

False
multiprocessing bool

Use multiple processes to process the

False
num_workers Optional[int]

Number of processes to run with

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults to

1e-07
rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647

Raises:

Type Description
ValueError

Cannot use multiprocessing and cache_matrices at the same time.

Returns:

Name Type Description
LocalBatch LocalBatch

Batch of local tasks that have been executed.

Source code in src/bloqade/ir/routine/bloqade.py
166
-167
-168
-169
-170
-171
-172
-173
-174
-175
-176
-177
-178
-179
-180
-181
-182
-183
-184
-185
-186
-187
-188
-189
-190
-191
-192
-193
-194
-195
-196
-197
-198
-199
-200
-201
-202
-203
-204
-205
-206
-207
-208
-209
-210
-211
-212
-213
-214
-215
-216
-217
-218
-219
-220
-221
-222
-223
-224
-225
-226
+

Run the current program using bloqade python backend

Parameters:

Name Type Description Default
shots int

number of shots after running state vector simulation

required
args Tuple[LiteralType, ...]

The values for parameters defined

()
name Optional[str]

Name to give this run. Defaults to None.

None
blockade_radius float

Use the Blockade subspace given a

0.0
waveform_runtime str

(bool, optional): Use Numba to compile the waveforms,

'interpret'
interaction_picture bool

Use the interaction picture when

False
cache_matrices bool

Reuse previously evaluated matrcies when

False
multiprocessing bool

Use multiple processes to process the

False
num_workers Optional[int]

Number of processes to run with

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults to

1e-07
rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647

Raises:

Type Description
ValueError

Cannot use multiprocessing and cache_matrices at the same time.

Returns:

Name Type Description
LocalBatch LocalBatch

Batch of local tasks that have been executed.

Source code in src/bloqade/ir/routine/bloqade.py
226
 227
 228
 229
@@ -91,103 +155,43 @@
 241
 242
 243
-244
@beartype
-def run(
-    self,
-    shots: int,
-    args: Tuple[LiteralType, ...] = (),
-    name: Optional[str] = None,
-    blockade_radius: float = 0.0,
-    waveform_runtime: str = "interpret",
-    interaction_picture: bool = False,
-    cache_matrices: bool = False,
-    multiprocessing: bool = False,
-    num_workers: Optional[int] = None,
-    solver_name: str = "dop853",
-    atol: float = 1e-7,
-    rtol: float = 1e-14,
-    nsteps: int = 2_147_483_647,
-) -> LocalBatch:
-    """Run the current program using bloqade python backend
-
-    Args:
-        shots (int): number of shots after running state vector simulation
-        args (Tuple[LiteralType, ...], optional): The values for parameters defined
-        in `args`. Defaults to ().
-        name (Optional[str], optional): Name to give this run. Defaults to None.
-        blockade_radius (float, optional): Use the Blockade subspace given a
-        particular radius. Defaults to 0.0.
-        waveform_runtime: (bool, optional): Use Numba to compile the waveforms,
-        Defaults to False.
-        interaction_picture (bool, optional): Use the interaction picture when
-        solving schrodinger equation. Defaults to False.
-        cache_matrices (bool, optional): Reuse previously evaluated matrcies when
-        possible. Defaults to False.
-        multiprocessing (bool, optional): Use multiple processes to process the
-        batches. Defaults to False.
-        num_workers (Optional[int], optional): Number of processes to run with
-        multiprocessing. Defaults to None.
-        solver_name (str, optional): Which SciPy Solver to use. Defaults to
-        "dop853".
-        atol (float, optional): Absolute tolerance for ODE solver. Defaults to
-        1e-14.
-        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.
-        Defaults to 1e-7.
-        nsteps (int, optional): Maximum number of steps allowed per integration
-        step. Defaults to 2_147_483_647, the maximum value.
-
-    Raises:
-        ValueError: Cannot use multiprocessing and cache_matrices at the same time.
-
-    Returns:
-        LocalBatch: Batch of local tasks that have been executed.
-    """
-    if multiprocessing and cache_matrices:
-        raise ValueError(
-            "Cannot use multiprocessing and cache_matrices at the same time."
-        )
-
-    compile_options = dict(
-        shots=shots,
-        args=args,
-        name=name,
-        blockade_radius=blockade_radius,
-        cache_matrices=cache_matrices,
-        waveform_runtime=waveform_runtime,
-    )
-
-    solver_options = dict(
-        multiprocessing=multiprocessing,
-        num_workers=num_workers,
-        solver_name=solver_name,
-        atol=atol,
-        rtol=rtol,
-        nsteps=nsteps,
-        interaction_picture=interaction_picture,
-    )
-
-    batch = self._compile(**compile_options)
-    batch._run(**solver_options)
-
-    return batch
-

run_callback

run_callback(
-    callback,
-    program_args=(),
-    callback_args=(),
-    ignore_exceptions=False,
-    blockade_radius=0.0,
-    waveform_runtime="interpret",
-    interaction_picture=False,
-    cache_matrices=False,
-    multiprocessing=False,
-    num_workers=None,
-    solver_name="dop853",
-    atol=1e-07,
-    rtol=1e-14,
-    nsteps=2147483647,
-    use_hyperfine=False,
-)
-

Run state-vector simulation with a callback to access full state-vector from emulator

Parameters:

Name Type Description Default
callback Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]
required
program_args Tuple[LiteralType, ...]

The values for parameters

()
callback_args Tuple[Any, ...]

Extra arguments to pass into

()
ignore_exceptions bool

(bool, optional) If True any exception raised during

False
blockade_radius float

Use the Blockade subspace given a

0.0
waveform_runtime str

(str, optional): Specify which runtime to use for

'interpret'
interaction_picture bool

Use the interaction picture when

False
cache_matrices bool

Reuse previously evaluated matrcies when

False
multiprocessing bool

Use multiple processes to process the

False
num_workers Optional[int]

Number of processes to run with

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults to

1e-07
rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647

Returns:

Name Type Description
List List

List of resulting outputs from the callbacks

Raises:

Type Description
RuntimeError

Raises the first error that occurs, only if

Note

For the callback function, first argument is the many-body wavefunction as a 1D complex numpy array, the second argument is of type Metadata which is a Named Tuple where the fields correspond to the parameters of that given task, RydbergHamiltonian is the object that contains the Hamiltonian used to generate the evolution for that task, Finally any optional positional arguments are allowed after that. The return value can be anything, the results will be collected in a list for each task in the batch.

Source code in src/bloqade/ir/routine/bloqade.py
280
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
 281
 282
 283
@@ -211,43 +215,103 @@
 301
 302
 303
-304
-305
-306
-307
-308
-309
-310
-311
-312
-313
-314
-315
-316
-317
-318
-319
-320
-321
-322
-323
-324
-325
-326
-327
-328
-329
-330
-331
-332
-333
-334
-335
-336
-337
-338
-339
-340
+304
@beartype
+def run(
+    self,
+    shots: int,
+    args: Tuple[LiteralType, ...] = (),
+    name: Optional[str] = None,
+    blockade_radius: float = 0.0,
+    waveform_runtime: str = "interpret",
+    interaction_picture: bool = False,
+    cache_matrices: bool = False,
+    multiprocessing: bool = False,
+    num_workers: Optional[int] = None,
+    solver_name: str = "dop853",
+    atol: float = 1e-7,
+    rtol: float = 1e-14,
+    nsteps: int = 2_147_483_647,
+) -> LocalBatch:
+    """Run the current program using bloqade python backend
+
+    Args:
+        shots (int): number of shots after running state vector simulation
+        args (Tuple[LiteralType, ...], optional): The values for parameters defined
+        in `args`. Defaults to ().
+        name (Optional[str], optional): Name to give this run. Defaults to None.
+        blockade_radius (float, optional): Use the Blockade subspace given a
+        particular radius. Defaults to 0.0.
+        waveform_runtime: (bool, optional): Use Numba to compile the waveforms,
+        Defaults to False.
+        interaction_picture (bool, optional): Use the interaction picture when
+        solving schrodinger equation. Defaults to False.
+        cache_matrices (bool, optional): Reuse previously evaluated matrcies when
+        possible. Defaults to False.
+        multiprocessing (bool, optional): Use multiple processes to process the
+        batches. Defaults to False.
+        num_workers (Optional[int], optional): Number of processes to run with
+        multiprocessing. Defaults to None.
+        solver_name (str, optional): Which SciPy Solver to use. Defaults to
+        "dop853".
+        atol (float, optional): Absolute tolerance for ODE solver. Defaults to
+        1e-14.
+        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.
+        Defaults to 1e-7.
+        nsteps (int, optional): Maximum number of steps allowed per integration
+        step. Defaults to 2_147_483_647, the maximum value.
+
+    Raises:
+        ValueError: Cannot use multiprocessing and cache_matrices at the same time.
+
+    Returns:
+        LocalBatch: Batch of local tasks that have been executed.
+    """
+    if multiprocessing and cache_matrices:
+        raise ValueError(
+            "Cannot use multiprocessing and cache_matrices at the same time."
+        )
+
+    compile_options = dict(
+        shots=shots,
+        args=args,
+        name=name,
+        blockade_radius=blockade_radius,
+        cache_matrices=cache_matrices,
+        waveform_runtime=waveform_runtime,
+    )
+
+    solver_options = dict(
+        multiprocessing=multiprocessing,
+        num_workers=num_workers,
+        solver_name=solver_name,
+        atol=atol,
+        rtol=rtol,
+        nsteps=nsteps,
+        interaction_picture=interaction_picture,
+    )
+
+    batch = self._compile(**compile_options)
+    batch._run(**solver_options)
+
+    return batch
+

run_callback

run_callback(
+    callback,
+    program_args=(),
+    callback_args=(),
+    ignore_exceptions=False,
+    blockade_radius=0.0,
+    waveform_runtime="interpret",
+    interaction_picture=False,
+    cache_matrices=False,
+    multiprocessing=False,
+    num_workers=None,
+    solver_name="dop853",
+    atol=1e-07,
+    rtol=1e-14,
+    nsteps=2147483647,
+    use_hyperfine=False,
+)
+

Run state-vector simulation with a callback to access full state-vector from emulator

Parameters:

Name Type Description Default
callback Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]
required
program_args Tuple[LiteralType, ...]

The values for parameters

()
callback_args Tuple[Any, ...]

Extra arguments to pass into

()
ignore_exceptions bool

(bool, optional) If True any exception raised during

False
blockade_radius float

Use the Blockade subspace given a

0.0
waveform_runtime str

(str, optional): Specify which runtime to use for

'interpret'
interaction_picture bool

Use the interaction picture when

False
cache_matrices bool

Reuse previously evaluated matrcies when

False
multiprocessing bool

Use multiple processes to process the

False
num_workers Optional[int]

Number of processes to run with

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults to

1e-07
rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647

Returns:

Name Type Description
List List

List of resulting outputs from the callbacks

Raises:

Type Description
RuntimeError

Raises the first error that occurs, only if

Note

For the callback function, first argument is the many-body wavefunction as a 1D complex numpy array, the second argument is of type Metadata which is a Named Tuple where the fields correspond to the parameters of that given task, RydbergHamiltonian is the object that contains the Hamiltonian used to generate the evolution for that task, Finally any optional positional arguments are allowed after that. The return value can be anything, the results will be collected in a list for each task in the batch.

Source code in src/bloqade/ir/routine/bloqade.py
340
 341
 342
 343
@@ -343,161 +407,221 @@
 433
 434
 435
-436
@beartype
-def run_callback(
-    self,
-    callback: Callable[[StateVector, NamedTuple, RydbergHamiltonian, Any], Any],
-    program_args: Tuple[LiteralType, ...] = (),
-    callback_args: Tuple = (),
-    ignore_exceptions: bool = False,
-    blockade_radius: float = 0.0,
-    waveform_runtime: str = "interpret",
-    interaction_picture: bool = False,
-    cache_matrices: bool = False,
-    multiprocessing: bool = False,
-    num_workers: Optional[int] = None,
-    solver_name: str = "dop853",
-    atol: float = 1e-7,
-    rtol: float = 1e-14,
-    nsteps: int = 2_147_483_647,
-    use_hyperfine: bool = False,
-) -> List:
-    """Run state-vector simulation with a callback to access full state-vector from
-    emulator
-
-    Args:
-        callback (Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]):
-        The callback function to run for each task in batch. See note below for more
-        details about the signature of the function.
-        program_args (Tuple[LiteralType, ...], optional): The values for parameters
-        defined in `args`. Defaults to ().
-        callback_args (Tuple[Any,...], optional): Extra arguments to pass into
-        ignore_exceptions: (bool, optional) If `True` any exception raised during
-        a task will be saved instead of the resulting output of the callback,
-        otherwise the first exception by task number will be raised after *all*
-        tasks have executed. Defaults to False.
-        blockade_radius (float, optional): Use the Blockade subspace given a
-        particular radius. Defaults to 0.0.
-        waveform_runtime: (str, optional): Specify which runtime to use for
-        waveforms. Defaults to "interpret".
-        interaction_picture (bool, optional): Use the interaction picture when
-        solving schrodinger equation. Defaults to False.
-        cache_matrices (bool, optional): Reuse previously evaluated matrcies when
-        possible. Defaults to False.
-        multiprocessing (bool, optional): Use multiple processes to process the
-        batches. Defaults to False.
-        num_workers (Optional[int], optional): Number of processes to run with
-        multiprocessing. Defaults to None.
-        solver_name (str, optional): Which SciPy Solver to use. Defaults to
-        "dop853".
-        atol (float, optional): Absolute tolerance for ODE solver. Defaults to
-        1e-14.
-        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.
-        Defaults to 1e-7.
-        nsteps (int, optional): Maximum number of steps allowed per integration
-        step. Defaults to 2_147_483_647, the maximum value.
-
-    Returns:
-        List: List of resulting outputs from the callbacks
-
-    Raises:
-        RuntimeError: Raises the first error that occurs, only if
-        `ignore_exceptions=False`.
-
-    Note:
-        For the `callback` function, first argument is the many-body wavefunction
-        as a 1D complex numpy array, the second argument is of type `Metadata` which
-        is a Named Tuple where the fields correspond to the parameters of that given
-        task, RydbergHamiltonian is the object that contains the Hamiltonian used to
-        generate the evolution for that task, Finally any optional positional
-        arguments are allowed after that. The return value can be anything, the
-        results will be collected in a list for each task in the batch.
-
-
-    """
-    if multiprocessing:
-        from multiprocessing import Process, Queue, cpu_count
-    else:
-        from queue import Queue
-
-    if cache_matrices:
-        compile_cache = CompileCache()
-    else:
-        compile_cache = None
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
@beartype
+def run_callback(
+    self,
+    callback: Callable[[StateVector, NamedTuple, RydbergHamiltonian, Any], Any],
+    program_args: Tuple[LiteralType, ...] = (),
+    callback_args: Tuple = (),
+    ignore_exceptions: bool = False,
+    blockade_radius: float = 0.0,
+    waveform_runtime: str = "interpret",
+    interaction_picture: bool = False,
+    cache_matrices: bool = False,
+    multiprocessing: bool = False,
+    num_workers: Optional[int] = None,
+    solver_name: str = "dop853",
+    atol: float = 1e-7,
+    rtol: float = 1e-14,
+    nsteps: int = 2_147_483_647,
+    use_hyperfine: bool = False,
+) -> List:
+    """Run state-vector simulation with a callback to access full state-vector from
+    emulator
 
-    solver_args = dict(
-        solver_name=solver_name,
-        atol=atol,
-        rtol=rtol,
-        nsteps=nsteps,
-        interaction_picture=interaction_picture,
-    )
-
-    runner = self.EmuRunner(
-        compile_cache=compile_cache,
-        solver_args=solver_args,
-        callback=callback,
-        callback_args=callback_args,
-    )
-
-    tasks = Queue()
-    results = Queue()
-
-    total_tasks = 0
-    ir_iter = self._generate_ir(
-        program_args, blockade_radius, waveform_runtime, use_hyperfine
-    )
-    for task_data in ir_iter:
-        task_number = task_data.task_id
-        emulator_ir = task_data.emulator_ir
-        metadata = task_data.metadata_dict
-        total_tasks += 1
-        tasks.put((task_number, (emulator_ir, metadata)))
-
-    workers = []
-    if multiprocessing:
-        num_workers = max(int(num_workers or cpu_count()), 1)
-        num_workers = min(total_tasks, num_workers)
-
-        for _ in range(num_workers):
-            worker = Process(
-                target=BloqadePythonRoutine.process_tasks,
-                args=(runner, tasks, results),
-            )
-            worker.start()
-
-            workers.append(worker)
-    else:
-        self.process_tasks(runner, tasks, results)
-
-    # blocks until all
-    # results have been fetched
-    # from the id_results Queue
-    id_results = []
-    for i in range(total_tasks):
-        id_results.append(results.get())
-
-    if workers:
-        for worker in workers:
-            worker.join()
-
-        tasks.close()
-        results.close()
-
-    id_results.sort(key=lambda x: x[0])
-    results = []
-
-    for task_id, result in id_results:
-        if not ignore_exceptions and isinstance(result, BaseException):
-            try:
-                raise result
-            except BaseException:
-                raise RuntimeError(
-                    f"{result.__class__.__name__} occured during child process "
-                    f"running for task number {task_id}:\n{traceback.format_exc()}"
-                )
-
-        results.append(result)
-
-    return results
-

HamiltonianData dataclass

Data class to hold the Hamiltonian and metadata for a given set of parameters

TaskData dataclass

Data class to hold the program ir and metadata for a given set of parameters

\ No newline at end of file + Args: + callback (Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]): + The callback function to run for each task in batch. See note below for more + details about the signature of the function. + program_args (Tuple[LiteralType, ...], optional): The values for parameters + defined in `args`. Defaults to (). + callback_args (Tuple[Any,...], optional): Extra arguments to pass into + ignore_exceptions: (bool, optional) If `True` any exception raised during + a task will be saved instead of the resulting output of the callback, + otherwise the first exception by task number will be raised after *all* + tasks have executed. Defaults to False. + blockade_radius (float, optional): Use the Blockade subspace given a + particular radius. Defaults to 0.0. + waveform_runtime: (str, optional): Specify which runtime to use for + waveforms. Defaults to "interpret". + interaction_picture (bool, optional): Use the interaction picture when + solving schrodinger equation. Defaults to False. + cache_matrices (bool, optional): Reuse previously evaluated matrcies when + possible. Defaults to False. + multiprocessing (bool, optional): Use multiple processes to process the + batches. Defaults to False. + num_workers (Optional[int], optional): Number of processes to run with + multiprocessing. Defaults to None. + solver_name (str, optional): Which SciPy Solver to use. Defaults to + "dop853". + atol (float, optional): Absolute tolerance for ODE solver. Defaults to + 1e-14. + rtol (float, optional): Relative tolerance for adaptive step in ODE solver. + Defaults to 1e-7. + nsteps (int, optional): Maximum number of steps allowed per integration + step. Defaults to 2_147_483_647, the maximum value. + + Returns: + List: List of resulting outputs from the callbacks + + Raises: + RuntimeError: Raises the first error that occurs, only if + `ignore_exceptions=False`. + + Note: + For the `callback` function, first argument is the many-body wavefunction + as a 1D complex numpy array, the second argument is of type `Metadata` which + is a Named Tuple where the fields correspond to the parameters of that given + task, RydbergHamiltonian is the object that contains the Hamiltonian used to + generate the evolution for that task, Finally any optional positional + arguments are allowed after that. The return value can be anything, the + results will be collected in a list for each task in the batch. + + + """ + if multiprocessing: + from multiprocessing import Process, Queue, cpu_count + else: + from queue import Queue + + if cache_matrices: + compile_cache = CompileCache() + else: + compile_cache = None + + solver_args = dict( + solver_name=solver_name, + atol=atol, + rtol=rtol, + nsteps=nsteps, + interaction_picture=interaction_picture, + ) + + runner = self.EmuRunner( + compile_cache=compile_cache, + solver_args=solver_args, + callback=callback, + callback_args=callback_args, + ) + + tasks = Queue() + results = Queue() + + total_tasks = 0 + ir_iter = self._generate_ir( + program_args, blockade_radius, waveform_runtime, use_hyperfine + ) + for task_data in ir_iter: + task_number = task_data.task_id + emulator_ir = task_data.emulator_ir + metadata = task_data.metadata_dict + total_tasks += 1 + tasks.put((task_number, (emulator_ir, metadata))) + + workers = [] + if multiprocessing: + num_workers = max(int(num_workers or cpu_count()), 1) + num_workers = min(total_tasks, num_workers) + + for _ in range(num_workers): + worker = Process( + target=BloqadePythonRoutine.process_tasks, + args=(runner, tasks, results), + ) + worker.start() + + workers.append(worker) + else: + self.process_tasks(runner, tasks, results) + + # blocks until all + # results have been fetched + # from the id_results Queue + id_results = [] + for i in range(total_tasks): + id_results.append(results.get()) + + if workers: + for worker in workers: + worker.join() + + tasks.close() + results.close() + + id_results.sort(key=lambda x: x[0]) + results = [] + + for task_id, result in id_results: + if not ignore_exceptions and isinstance(result, BaseException): + try: + raise result + except BaseException: + raise RuntimeError( + f"{result.__class__.__name__} occured during child process " + f"running for task number {task_id}:\n{traceback.format_exc()}" + ) + + results.append(result) + + return results +

TaskData dataclass

Data class to hold the program ir and metadata for a given set of parameters

\ No newline at end of file diff --git a/dev/search/search_index.json b/dev/search/search_index.json index 19fcf9593..f0cad6405 100644 --- a/dev/search/search_index.json +++ b/dev/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-,:!=\\[\\: )\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Index","text":""},{"location":"#welcome-to-bloqade-queras-neutral-atom-sdk","title":"Welcome to Bloqade: QuEra's Neutral Atom SDK","text":""},{"location":"#what-is-bloqade","title":"What is Bloqade?","text":"

Bloqade is a Python SDK for QuEra's neutral atom quantum computer Aquila (check out our paper!). It's designed to make writing and analyzing the results of analog quantum programs on Aquila as easy as possible. It features custom atom geometries and flexible waveform definitions in both emulation and real hardware. Bloqade interfaces with the AWS Braket cloud service where Aquila is hosted, enabling you to submit programs as well as retrieve and analyze real hardware results all-in-one.

"},{"location":"#installation","title":"Installation","text":"

You can install the package with pip in your Python environment of choice via:

pip install bloqade\n
"},{"location":"#a-glimpse-of-bloqade","title":"A Glimpse of Bloqade","text":"

Let's try a simple example where we drive a Rabi oscillation on a single neutral atom. Don't worry if you're unfamiliar with neutral atom physics, (you can check out our Background for more information!) the goal here is to just give you a taste of what Bloqade can do.

We start by defining where our atoms go, otherwise known as the atom geometry. In this particular example we will use a small Honeycomb lattice:

from bloqade.atom_arrangement import Honeycomb\n\ngeometry = Honeycomb(2, lattice_spacing = 10.0)\n

We can verify what the atom geometry looks like by .show()'ing it:

geometry.show()\n

We now define what the time evolution looks like using a pulse sequence. The pulse sequence here is the time profile of the Rabi Drive targeting the ground-Rydberg two level transition, which causes the Rabi oscillations. We choose a constant waveform with a value of \\(\\frac{\\pi}{2} \\text{rad}/\\text{us}\\) and a duration of \\(1.0 \\,\\text{us}\\). This produces a \\(\\frac{\\pi}{2}\\) rotation on the Bloch sphere meaning our final measurements should be split 50/50 between the ground and Rydberg state.

from math import pi\nrabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .constant(value=pi/2, duration=1.0)\n)\n

Here rabi.amplitude means exactly what it is, the Rabi amplitude term of the Hamiltonian. uniform refers to applying the waveform uniformly across all the atom locations.

We can visualize what our program looks like again with .show():

We can now run the program through Bloqade's built-in emulator to get some results. We designate that we want the program to be run and measurements performed 100 times:

emulation_results = rabi_program.bloqade.python().run(100)\n

With the results we can generate a report object that contains a number of methods for analyzing our data, including the number of counts per unique bitstring:

bitstring_counts = emulation_results.report().counts()\n

Which gives us:

[OrderedDict([('0', 55), ('1', 45)])]\n

If we want to submit our program to hardware we'll need to adjust the waveform as there is a constraint the Rabi amplitude waveform must start and end at zero. This is easy to do as we can build off the atom geometry we saved previously but apply a piecewise linear waveform:

hardware_rabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06])\n)\n\nhardware_rabi_program.show()\n

Now instead of using the built-in Bloqade emulator we submit the program to Aquila. You will need to use the AWS CLI to obtain credentials from your AWS account or set the proper environment variables before hand.

hardware_results = hardware_rabi_program.braket.aquila.run_async(100)\n

.run_async is a non-blocking version of the standard .run method, allowing you to continue work while waiting for results from Aquila. .run_async immediately returns an object you can query for the status of your tasks in the queue as well.

You can do the exact same analysis you do on emulation results with hardware results too:

hardware_bitstring_counts = hardware_results.report().counts()\n

If you want to try the above at once, we collected the above steps into the snippet below:

from math import pi\nfrom bloqade.atom_arrangement import Honeycomb\n\ngeometry = Honeycomb(2, lattice_spacing = 10.0)\nrabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .constant(value=pi/2, duration=1.0)\n)\nemulation_results = rabi_program.bloqade.python().run(100) \nbitstring_counts = emulation_results.report().counts()\n\nhardware_rabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06])\n)\nhardware_results = hardware_rabi_program.braket.aquila.run_async(100)\nhardware_bitstring_counts = hardware_results.report().counts()\n

"},{"location":"#features","title":"Features","text":""},{"location":"#customizable-atom-geometries","title":"Customizable Atom Geometries","text":"

You can easily explore a number of common geometric lattices with Bloqade's atom_arrangement's:

from bloqade.atom_arrangement import Lieb, Square, Chain, Kagome\n\ngeometry_1 = Lieb(3)\ngeometry_2 = Square(2)\ngeometry_3 = Chain(5)\ngeometry_4 = Kagome(3)\n

If you're not satisfied with the Bravais lattices we also allow you to modify existing Bravais lattices as follows:

geometry_5 = Kagome(3).add_position((10,11))\n

You can also build your geometry completely from scratch:

from bloqade import start\n\ngeometry = start.add_positions([(0,0), (6,0), (12,0)])\n
"},{"location":"#flexible-pulse-sequence-construction","title":"Flexible Pulse Sequence Construction","text":"

Define waveforms for pulse sequences any way you like by either building (and chaining!) them immediately as part of your program:

from bloqade.atom_arrangement import Square\n\ngeometry = Square(2)\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\ncustom_rabi_amp_waveform = (\n  target_rabi_amplitude\n  .piecewise_linear(values=[0, 10, 10, 0], durations=[0.1, 3.5, 0.1])\n  .piecewise_linear(values=[0, 5, 3, 0], durations=[0.2, 2.0, 0.2])\n)\n

Or building them separately and applying them later:

from bloqade.atom_arrangement import Square, Chain\n\ngeometry_1 = Square(3)\ngeometry_2 = Chain(5)\n\ntarget_rabi_amplitude = start.rydberg.rabi.amplitude.uniform\npulse_sequence = target_rabi_amplitude.uniform.constant(value=2.0, duration=1.5).parse_sequence()\n\nprogram_1 = geometry_1.apply(pulse_sequence)\nprogram_2 = geometry_2.apply(pulse_sequence)\n
"},{"location":"#hardware-and-emulation-backends","title":"Hardware and Emulation Backends","text":"

Go from a fast and powerful emulator:

from bloqade.atom_arrangement import Square\nfrom math import pi\n\ngeometry = Square(3, lattice_spacing = 6.5)\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nprogram = (\n  target_rabi_amplitude\n  .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06])\n)\nemulation_results = program.bloqade.python().run(100)\n

To real quantum hardware in a snap:

hardware_results = program.braket.aquila().run_async(100)\n
"},{"location":"#simple-parameter-sweeps","title":"Simple Parameter Sweeps","text":"

Use variables to make parameter sweeps easy on both emulation and hardware:

from bloqade import start\nimport numpy as np\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nrabi_oscillation_program = (\n  target_rabi_amplitude\n  .piecewise_linear(durations = [0.06, \"run_time\", 0.06], values = [0, 15, 15, 0])\n)\nrabi_oscillation_job = rabi_oscillation_program.batch_assign(run_time=np.linspace(0, 3, 101))\n\nemulation_results = rabi_oscillation_job.bloqade.python().run(100)\nhardware_results = rabi_oscillation_job.braket.aquila().run(100)\n
emulation_results.report().rydberg_densities()\n                0\ntask_number      \n0            0.16\n1            0.35\n2            0.59\n3            0.78\n4            0.96\n...           ...\n96           0.01\n97           0.09\n98           0.24\n99           0.49\n100          0.68\n\n[101 rows x 1 columns]\n
"},{"location":"#quick-results-analysis","title":"Quick Results Analysis","text":"

Want to just see some plots of your results? .show() will show you the way!

from bloqade.atom_arrangement import Square\n\nrabi_amplitude_values = [0.0, 15.8, 15.8, 0.0]\nrabi_detuning_values = [-16.33, -16.33, 42.66, 42.66]\ndurations = [0.8, 2.4, 0.8]\n\ngeometry = Square(3, lattice_spacing=5.9)\nrabi_amplitude_waveform = (\n  geometry\n  .rydberg.rabi.amplitude.uniform.piecewise_linear(durations, rabi_amplitude_values)\n)\nprogram = (\n  rabi_amplitude_waveform\n  .detuning.uniform.piecewise_linear(durations, rabi_detuning_values)\n)\nemulation_results = program.bloqade.python().run(100)\nemulation_results.report().show()\n

"},{"location":"#contributing-to-bloqade","title":"Contributing to Bloqade","text":"

Bloqade is released under the Apache License, Version 2.0. If you'd like the chance to shape the future of neutral atom quantum computation, see our Contributing Guide for more info!

"},{"location":"blog/2023/","title":"Bloqade Blog Posts 2023","text":"

Sept. 21, 2023 - Introducing Bloqade SDK for Python

"},{"location":"blog/2023/posts/bloqade-release/","title":"Introducing Bloqade SDK for Python","text":"

Greetings Neutral Atom QC experts, enthusiasts, and newcomers!

We are excited to the Rydberg state thrilled to announce the Python version of our cutting-edge SDK, Bloqade. Originally developed in Julia, Bloqade has been a game-changer in the realm of Neutral Atom quantum computing. With the introduction of the Python version, we aim to make this revolutionary technology more accessible and user-friendly than ever before.

"},{"location":"blog/2023/posts/bloqade-release/#why-python","title":"Why Python?","text":"

Python is one of the most widely used programming languages, especially in the quantum computing community and broader scientific communities. By extending Bloqade to Python, we are opening doors to a broader audience, enabling more developers, researchers, and organizations to harness the power of Neutral Atom quantum computing.

"},{"location":"blog/2023/posts/bloqade-release/#neutral-atom-quantum-computing","title":"Neutral Atom Quantum Computing","text":"

Recently, the Neutral Atom platform has come on the QC scene in the form of Analog Hamiltonian Simulators that have a broad set of use cases beyond quantum circuits. Ranging from simulating unique quantum phases of matter, solving combinatorical optimization problems, and machine learning applications, the analog mode provides strong values in solving practical, interesting problems in the near term.

These advances are crucial milestones on the way towards scalable digital gate-based architecture using atom shuttling. This new technology and its novel applications demand a paradigm shift in the way we not only think about quantum computing, but translate those ideas to real hardware. Enter Bloqade, a next-generation SDK designed to put the power of neutral atoms at your fingertips.

"},{"location":"blog/2023/posts/bloqade-release/#why-bloqade","title":"Why Bloqade?","text":"

Bloqade is designed with the primary goal of making it easier to compose programs for QuEra\u2019s hardware and analyze results.

We've gained valuable insights into how users have used our neutral-atom hardware and with it, their struggles with existing tools. We took advantage of this knowledge to produce a tool that could take the \"hard\" out of \"hardware\". Bloqade is precision-balanced in both flexibility to empower novices to experiment with ease and power to let experts perform cutting-edge work without breaking a sweat.

"},{"location":"blog/2023/posts/bloqade-release/#highlights","title":"Highlights","text":""},{"location":"blog/2023/posts/bloqade-release/#smart-documentation","title":"Smart Documentation","text":"

With our commitment to enabling more seamless program development, we've put the relevant documentation you need right where and when you need it.

No more obnoxious switching between your favorite coding environment and documentation in a separate window. Let Bloqade guide you where you'd like to go:

"},{"location":"blog/2023/posts/bloqade-release/#fully-parameterized-analog-programs","title":"Fully Parameterized Analog Programs","text":"

Parameter sweeps are a common theme of programs for analog quantum computers, where a user would like to observe differences in output results by varying a value or values in their program.

You used to have to manually crank out variations of your program with different values and then keep track of all the individual submissions to the emulator and hardware, a mess to keep track of and process the results of afterwards.

Bloqade eliminates this with its own support for variables that can later be assigned single values or a whole sequence of values for trivial parameter sweeping. This isn't some feature that's constrained to a certain backend, you can take your program with all its variables and submit it to your choice of emulator or our hardware directly.

from bloqade import var\nfrom bloqade.atom_arrangement import Square\n\nimport numpy as np\n\nadiabatic_durations = [0.4, 3.2, 0.4]\n\n# create variables explicitly...\nmax_detuning = var(\"max_detuning\")\n# ...or implicitly inside the program definition.\nadiabatic_program = (\n    Square(3, \"lattice_spacing\")\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=adiabatic_durations, values=[0.0, \"max_rabi\", \"max_rabi\", 0.0]\n    )\n    .detuning.uniform.piecewise_linear(\n        durations=adiabatic_durations,\n        values=[\n            -max_detuning, # scalar variables support direct arithmetic operations\n            -max_detuning,\n            max_detuning,\n            max_detuning,\n        ],\n    )\n    .assign(max_rabi=15.8, max_detuning=16.33)\n    .batch_assign(lattice_spacing=np.arange(4.0, 7.0, 0.5))\n)\n\n# Launch your program on your choice of Braket or in-house emulator...\nemu_results = adiabatic_program.braket.local_emulator().run(10000)\nfaster_emu_results = adiabatic_program.bloqade.python().run(10000)\n# ...as well as hardware without stress\nhw_results = adiabatic_program.parallelize(24).braket.aquila().run_async(100)\n
"},{"location":"blog/2023/posts/bloqade-release/#integrated-visualization-tools","title":"Integrated Visualization Tools","text":"

Instantly understand what your programs are doing faster than you can say \"neutral atoms rock!\" with Bloqade's built-in visualization tools:

For your results, no more obnoxious manual compilation of results across different parameters or wrangling them into more useful forms. Get insights of experiment outcomes in the blink of an eye:

Now that's what we call having your cake AND eating it.

"},{"location":"blog/2023/posts/bloqade-release/#bloqade-roadmap","title":"Bloqade Roadmap","text":""},{"location":"blog/2023/posts/bloqade-release/#bloqade-alpha-phase","title":"Bloqade Alpha Phase","text":"

During the next year, we plan on continuing development of Bloqade's python interface. If you are as excited about Neutral Atom quantum computing as us, or heck, even just quantum physics in general, give Bloqade a try! This is your opportunity to influence the direction of Bloqade and get in on the ground floor of the next Quantum Computing revolution.

"},{"location":"blog/2023/posts/bloqade-release/#but-what-about-julia","title":"But what about Julia?","text":"

Don't you guys already HAVE an SDK in Julia? Why do you need two SDKs?

That's right! However, there's a key motivating factor for the reason we created Bloqade Python that's distinct for Bloqade.jl's existence.

Bloqade.jl is primarily geared as a high-performance emulator. It allows you to design complex neutral-atom algorithms that may not necessarily run on our hardware BUT are excellent if you're exploring novel physical phenonema/algorithms or as a tool for pedagogical purposes.

Bloqade.jl does have the ability to submit to Aquila, our flagship quantum computer, but for more complex tasks such as sweeping parameters (e.g. running the same program on hardware with slightly different parameters each time) or advanced post-processing, it becomes cumbersome quite quickly.

There are no plans to drop support any time soon though. On the contrary, we plan on fully integrating Bloqade.jl into the Python package, which will enable you to program Neutral Atom quantum hardware without having to choose.

We very much look forward to you trying out Bloqade!

"},{"location":"contributing/","title":"Contributing","text":"

Thank you for your interest in contributing to the project! We welcome all contributions. There are many different ways to contribute to Bloqade, and we are always looking for more help. We accept contributions in the form of bug reports, feature requests, documentation improvements, and code contributions. For more information about how to contribute, please read the following sections.

"},{"location":"contributing/#table-of-contents","title":"Table of Contents","text":"
  • Reporting a Bug
  • Reporting Documentation Issues
  • Feature Requests
  • Developing Bloqade
  • Design Philosophy and Architecture
  • Community Slack
  • Ask a Question
  • Providing Feedback
"},{"location":"contributing/asking-a-question/","title":"Ask a Question","text":"

If you're interested in contributing to Bloqade, or just want to discuss the project, join the discussion on GitHub Discussions at https://github.com/QuEraComputing/bloqade-python/discussions

"},{"location":"contributing/code-of-conduct/","title":"Design Philosophy and Architecture","text":"

Given the heterogeneous nature of the hardware we target, We have decided to use a compiler-based approach to our software stack, allowing us to target different hardware backends with the same high-level language. Below is a diagram of the software stack in Bloqade.

graph TD\n    Builder[\"Builder Representation\"]\n    PythonAST[\"Bloqade AST Python\"]\n    JuliaAST[\"Bloqade AST Julia\"]\n\n    EmulatorPy[\"Emulator IR Python\"]\n    EmulatorJL[\"Emulator IR Julia\"]\n\n    QuEra[\"QuEra IR\"]\n    Braket[\"Braket IR\"]\n    JuliaEmulator[\"Bloqade.jl\"]\n    PythonEmulator[\"Python Emulator\"]\n\n    Aquila[\"Aquila\"]\n\n    Builder -->|parse| PythonAST\n    PythonAST -->|lower| EmulatorPy\n    PythonAST -->|lower| QuEra\n    PythonAST -->|lower| Braket\n    PythonAST -->|transpile| JuliaAST\n\n    QuEra -->|execute| Aquila\n    Braket -->|execute| Aquila\n\n    JuliaAST -->|lower| EmulatorJL\n    EmulatorPy -->|execute| PythonEmulator\n    EmulatorJL -->|execute| JuliaEmulator\n
"},{"location":"contributing/code-of-conduct/#high-level-builder-representation","title":"High-Level Builder Representation","text":"

When programming Bloqade using the Python API, the user constructs a representation of an analog quantum circuit. This representation is a flattened version of the actual analog circuit. Flattened means that the user input is a linear sequence of operations where the context of neighboring nodes in the sequence of instructions can determine the program tree structure. The Bloqade AST describes the actual analog circuit.

"},{"location":"contributing/code-of-conduct/#bloqade-ast","title":"Bloqade AST","text":"

The Bloqade AST is a representation of a quantum analog circuit for neutral atom computing. It is a directed acyclic graph (DAG) with nodes for different hierarchical levels of the circuit. The base node is the AnalogCircuit which contains the geometry of the atoms stored as a AtomArragment or ParallelRegister objects. The other part of the circuit is the Sequence, which contains the waveforms that describe the drives for the Ryberg/Hyperfine transitions of each Rydberg atom. Each transition is represented by a Pulse including a Field for the drive's detuning, Rabi amplitude, and Rabi phase . A Field relates the spatial and temporal dependence of a drive. The spatial modulates the temporal dependence of the waveform. A DAG also describes the Waveform object. Finally, we have basic Scalar expressions as well for describing the syntax of real-valued continuous numbers.

"},{"location":"contributing/code-of-conduct/#bloqade-compilers-and-transpilers","title":"Bloqade Compilers and Transpilers","text":"

Given a user program expressed as the Bloqade AST, we can target various backends by transforming from the Bloqade AST to other kinds of IR. For example, when submitting a task to QuEra's hardware, we transform the Bloqade AST to the IR that describes a valid program for the hardware.

This process is referred to as lowering, which in a general sense is a transformation that takes you from one IR to another where the target IR is specialized or has a smaller syntactical structure. Transpiling corresponds to a transformation that takes you from one language to equivalent expressions in another. For example, we can transpile from the Bloqade AST in Python to the Bloqade AST in Julia. The generic term for both of these types of transformation in Bloqade is Code Generation. You will find various code generation implementations in various codegen modules.

"},{"location":"contributing/community-slack/","title":"Community Slack","text":"

You can join QuEra's Slack workspace with this link. Join the #bloqade channel to discuss anything related to Bloqade.

"},{"location":"contributing/design-philosophy-and-architecture/","title":"Design Philosophy and Architecture","text":"

Given the heterogeneous nature of the hardware we target, We have decided to use a compiler-based approach to our software stack, allowing us to target different hardware backends with the same high-level language. Below is a diagram of the software stack in Bloqade.

graph TD\n    Builder[\"Builder Representation\"]\n    PythonAST[\"Bloqade AST Python\"]\n    JuliaAST[\"Bloqade AST Julia\"]\n\n    EmulatorPy[\"Emulator IR Python\"]\n    EmulatorJL[\"Emulator IR Julia\"]\n\n    QuEra[\"QuEra IR\"]\n    Braket[\"Braket IR\"]\n    JuliaEmulator[\"Bloqade.jl\"]\n    PythonEmulator[\"Python Emulator\"]\n\n    Aquila[\"Aquila\"]\n\n    Builder -->|parse| PythonAST\n    PythonAST -->|lower| EmulatorPy\n    PythonAST -->|lower| QuEra\n    PythonAST -->|lower| Braket\n    PythonAST -->|transpile| JuliaAST\n\n    QuEra -->|execute| Aquila\n    Braket -->|execute| Aquila\n\n    JuliaAST -->|lower| EmulatorJL\n    EmulatorPy -->|execute| PythonEmulator\n    EmulatorJL -->|execute| JuliaEmulator\n
"},{"location":"contributing/design-philosophy-and-architecture/#high-level-builder-representation","title":"High-Level Builder Representation","text":"

When programming Bloqade using the Python API, the user constructs a representation of an analog quantum circuit. This representation is a flattened version of the actual analog circuit. Flattened means that the user input is a linear sequence of operations where the context of neighboring nodes in the sequence of instructions can determine the program tree structure. The Bloqade AST describes the actual analog circuit.

"},{"location":"contributing/design-philosophy-and-architecture/#bloqade-ast","title":"Bloqade AST","text":"

The Bloqade AST is a representation of a quantum analog circuit for neutral atom computing. It is a directed acyclic graph (DAG) with nodes for different hierarchical levels of the circuit. The base node is the AnalogCircuit which contains the geometry of the atoms stored as a AtomArragment or ParallelRegister objects. The other part of the circuit is the Sequence, which contains the waveforms that describe the drives for the Ryberg/Hyperfine transitions of each Rydberg atom. Each transition is represented by a Pulse including a Field for the drive's detuning, Rabi amplitude, and Rabi phase . A Field relates the spatial and temporal dependence of a drive. The spatial modulates the temporal dependence of the waveform. A DAG also describes the Waveform object. Finally, we have basic Scalar expressions as well for describing the syntax of real-valued continuous numbers.

"},{"location":"contributing/design-philosophy-and-architecture/#bloqade-compilers-and-transpilers","title":"Bloqade Compilers and Transpilers","text":"

Given a user program expressed as the Bloqade AST, we can target various backends by transforming from the Bloqade AST to other kinds of IR. For example, when submitting a task to QuEra's hardware, we transform the Bloqade AST to the IR that describes a valid program for the hardware.

This process is referred to as lowering, which in a general sense is a transformation that takes you from one IR to another where the target IR is specialized or has a smaller syntactical structure. Transpiling corresponds to a transformation that takes you from one language to equivalent expressions in another. For example, we can transpile from the Bloqade AST in Python to the Bloqade AST in Julia. The generic term for both of these types of transformation in Bloqade is Code Generation. You will find various code generation implementations in various codegen modules.

"},{"location":"contributing/developing-bloqade/","title":"Setting up your Development Environment","text":"

Before You Get Started

Depending on the complexity of the contribution you'd like to make to Bloqade, it may be worth reading the Design Philosophy and Architecture section to get an idea of why Bloqade is structured the way that it is and how to make your contribution adhere to this philosophy.

Our development environment contains a set of tools we use for development, testing, and documentation. This section describes how to set up the development environment. We primarily use pdm to manage python environments and dependencies.

"},{"location":"contributing/developing-bloqade/#setting-up-python","title":"Setting up Python","text":"

We use pdm to manage dependencies and virtual environment. After cloning the repository, run the following command to install dependencies:

pdm install\n

You can also install different dependency groups:

  • dev: dependencies for development
pdm install --dev\n# or\npdm install -d\n
  • doc: dependencies for building documentation
pdm install -G doc\n
"},{"location":"contributing/developing-bloqade/#useful-pdm-scripts","title":"Useful PDM scripts","text":""},{"location":"contributing/developing-bloqade/#tests","title":"Tests","text":"

You can run tests via

pdm run test\n

Or run tests and generate coverage via

pdm run coverage\n

will print out the coverage file level report in terminal.

pdm run coverage-html\n

This command generates an interactive html report in htmlcov folder. This will show which specific lines are not covered by tests.

"},{"location":"contributing/developing-bloqade/#documentation","title":"Documentation","text":"

You can build documentation via

pdm run doc_build\n

Or run a local server to preview documentation via

pdm run doc\n
"},{"location":"contributing/developing-bloqade/#jupytext","title":"Jupytext","text":"

You can sync jupyter notebooks and python scripts via

pdm run jupytext\n

this will help you development examples in jupyter notebook and python scripts simultaneously.

"},{"location":"contributing/developing-bloqade/#lint","title":"Lint","text":"

We primarily use ruff - an extremely fast linter for Python, and black as formatter. These have been configured into pre-commit hooks. After installing pre-commit on your own system, you can install pre-commit hooks to git via

pre-commit install\n
"},{"location":"contributing/documentation-issues/","title":"Reporting a Documentation Issue","text":"

We are always looking to improve our documentation. If you find a typo or think something is unclear, please open an issue on our GitHub page: https://github.com/QuEraComputing/bloqade-python/issues

For typos or other minor problems, create an issue that contains a link to the specific page that includes the problem, along with a description of the problem and possibly a solution.

For a request for new documentation content, please open up an issue and describe what you think is missing from the documentation.

"},{"location":"contributing/feature-requests/","title":"Requesting new Features","text":"

Given that we are currently at the beginning of the development of the Bloqade python interface, we are open to suggestions about what features would be helpful to include in future package iterations. If you have a request for a new feature, please open an issue on our GitHub page: https://github.com/QuEraComputing/bloqade-python/issues

We ask that the feature requests be as specific as possible. Please include the following information in your feature request:

  1. A short, descriptive title.

  2. A detailed description of the feature, including your attempt to solve the problem with the current version of Bloqade.

  3. A minimal code example that demonstrates the need for the feature.

  4. The version of Bloqade you are using.

  5. The version of Python you are using.

  6. The version of your operating system.

"},{"location":"contributing/providing-feedback/","title":"Providing Feedback","text":"

While Github Issues are a great way for us to better understand any issues your having with Bloqade as well as provide us with feature requests, we're always looking for ways to collect more general feedback about what the user experience with Bloqade is like.

To do that we have this form where you can provide your thoughts after using/experimenting/tinkering/hacking with Bloqade.

Your feedback will help guide the future of Bloqade's design so be honest and know that you're contributing to the future of Quantum Computing with Neutral Atoms!

"},{"location":"contributing/reporting-a-bug/","title":"Reporting a Bug","text":"

Bloqade is currently in the alpha phase of development, meaning bugs most likely exist in the current implementation. We are continuously striving to improve the stability of Bloqade. As such, we encourage our users to report all bugs they find. To do this, we ask you to submit an issue to our GitHub page: https://github.com/QuEraComputing/bloqade-python/issues

Please include the following information in your bug report:

  1. A short, descriptive title.

  2. A detailed description of the bug, including the expected behavior and what happened.

  3. A minimal code example that reproduces the bug.

  4. The version of Bloqade you are using.

  5. The version of Python you are using.

  6. The version of your operating system.

"},{"location":"drafts/aws_hybrid_execution/","title":"Hybrid Execution with AWS Hybrid Jobs","text":""},{"location":"drafts/aws_hybrid_execution/#introduction","title":"Introduction","text":"

Analog Hamiltonian Simulation (AHS) has proven itself to be a powerful method of performing quantum computation that is well-suited for solving optimization problems and performing computationally difficult simulations of other quantum systems. Unlike its counterpart digital/gate-based quantum computing where you think of your programs in terms of unitary operations that are akin to classicla gates, you think of programs in AHS in terms of the geometry of your qubits (individual atoms!) and the waveforms of the lasers that are applied to them.

The team at QuEra Computing believes this is a useful step in the path to fault tolerant quantum computation but also realizes that such novel power and capabilities require a novel tool.

That's why we're proud to announce the release of the Bloqade SDK for Python! We've had the opportunity to obtain valuable feedback from the community and leveraged our unique position as the only provider of publicly cloud-accessible Nuetral Atom hardware to produce a tool that puts the power of AHS hardware at your fingertips.

"},{"location":"drafts/aws_hybrid_execution/#installation","title":"Installation","text":"

Bloqade is a pure Python library so installation is as easy as pip install bloqade! Once installed you have a variety of options for building your Nuetral atom Analog program. We have worked very hard to provide a seamless user experience when venturing into unfamiliar territory of AHS with Nuetral Atoms! Let\u2019s dig into some of the major features Bloqade has to offer!

"},{"location":"drafts/aws_hybrid_execution/#features-of-bloqade","title":"Features of Bloqade","text":""},{"location":"drafts/aws_hybrid_execution/#just-in-time-documentation","title":"Just-in-time Documentation","text":"

I know you have probably spent a pretty penny on your multi-monitor setup so that you do not have to switch between windows when writing code and looking at documentation. What if I told you there was a better way? What if your code contained all the documentation you needed to continue writing your program? That\u2019s exactly what we have designed in the user experinence (UX) of Bloqade.

Typically when building an AHS program one needs to construct many different objects and combine them in very particular ways which are not obvious without understanding the concept as a whole. Bloqade provides a unique experience programming AHS. Our interface eases the user into AHS programming by using Python\u2019s rich type-hinting system. Most IDE's, as well as IPython and Jupyter, use the type hints to access to the methods and attributes that are availible to whatever object you currently have. We use this hinting to our advantage in Bloqade by building your AHS program with a chain of methods and attributes separated by .. In doing so you will be given hints as to what to do next at every step of the way when building your program.

While it is conventional thinking that it is bad to chain . statements together, there are some well known libraries like Pandas that can and do make heavy use of this pattern of programming. The dot-chaining syntax also integrate python linters like black to make your code highly readable without having to do anything other can call the linter on your python file.

Example, before linter some unstrctured code After running black we get consistent formatting. In this case properties are always chained which makes reading the code a lot like reading a sentence.

On top of these nice features, If you\u2019re in an IDE like VS code or PyCharm you can access the documentation of each method and attribute in which we provide even more hints for the next step after your current selection

Here is a blog post that goes into the advantages and disadvantages of chaining method calls/attributes like we have shown. It worth a read if you are still a bit skeptical! It's worth noting that you do not neccesarily have to chain method/attribute calls you can safely store a intermediate parts of program with intermediate objects because calling a method does not act in the object in-place. This means you can

"},{"location":"drafts/aws_hybrid_execution/#parameterized-programs","title":"Parameterized Programs","text":"

keep title capitalization consistent [name=jzlong]

Many near-term applications for QC as well as AHS require some notion of parameterized programs. We\u2019ve made this a first-class feature in Bloqade enabling you to write a single AHS program precisely to define your experiment or algorithm symbolically enabling more readable and sharable code!

You can also create stand-alone variables which have a basic symbolic representation that support some arithmetic operations that are useful for more advanced AHS applications. For example, say I have a piecewise linear pulse that has variable segments but I have another constant waveform running for the total time of the other waveform. I can sum the durations of each segment of the piecewise linear waveform to get the total duration and use that to construct the constant waveform.

from bloqade import var, start\n\nrabi_durations = [0.1, var(\"run_time\"), 0.1]\ntotal_time = sum(rabi_durations)\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(total_time, \"detuning\")\n    .amplitude.uniform.piecewise_linear(\n        rabi_durations, [0, \"amplitude\",  \"amplitude\",  0]\n    )\n)\n

Once you have defined your parameterized program there are three different methods of specifying the run time values of the parameters:

program.assign(var1=value1, var2=value2)\n

which assigns the variables in a static way. The basic logic here is for segments of program in which you want to share without limiting other users to use the concrete values you decided works for you.

program.batch_assign(var1=[value1_1, value1_2, \u2026],\u2026)\n
or

program.batch_assign([dict(var1=value1_1,\u2026), dict(var1=value1_2,\u2026),\u2026])\n

specify a batch of tasks via parameters assigned to lists or a list of dictionaries with the assigned parameters. Next,

args([\u201cvar1\u201d, \u201cvar2\u201d])\n

will delay the assignment of the variable until the program is being executed. We will discuss this below in more detail.

Note that none of these methods are required for non-parameterized programs. For parameterized programs you can mix and match all of these assignments together. However, they will always come in the order of assign, batch_assign then args. let's take the program above to give a concrete example.

After specifying the program we can do the following assignment:

assigned_program = (\n    program.assign(amplitude=15.7)\n    .batch_assign(run_time=np.linspace(0.05, 3.0, 101))\n    .args([\"detuning\"])\n)\n

First we assign the amplitude to a value of 15.7 which is going to be non-changing regardless of the other changing parameters in execution. Because we assign \u2018run_time\u2019 using batch_assign that means every time we execute this program we will run 101 tasks with the parameter sweep defined by the list of values. Finally args([\u201cdetuning\u201d]) implies that you must provide the value of the detuning as an argument when calling the execution method of the device. This is primarily useful for hybrid work flows or if you are simply just wanting to split your experiments into chunks defined by a different set of parameters. In conclusion, whether it is a hybrid algorithm or a parameter scan for your next fancy experiment Bloqade has you covered!

"},{"location":"drafts/aws_hybrid_execution/#visualization-tools","title":"Visualization Tools","text":"

While having a clean, readable syntax is great, there is always going to be the need for more visual representations. This is especially true for AHS programming where you think about things in terms of waveforms and atom configurations. As such, we provide visualization of your programs using Bokeh. Bokeh allows for very clean responsive interactive plots which we have implemented not only for AHS programs but AHS results as well!

We also provide some visualization of your AHS program in the terminal:

In [1]: from bloqade import start\n   ...:\n   ...: program = (\n   ...:     start.add_position((0, 0))\n   ...:     .add_position((0, \"r\"))\n   ...:     .rydberg.detuning.uniform.piecewise_linear([0.1, 1.2, 0.1], [-20, -20, 20, 20])\n   ...:     .amplitude.uniform.piecewise_linear([0.1, 1.2, 0.1], [0, 15, 15, 0])\n   ...:     .args([\"r\"])\n   ...: )\n\nIn [2]: program.parse_circuit()\nOut[2]:\nAnalogCircuit\n\u251c\u2500 register\n\u2502  \u21d2 AtomArrangement\n\u2502    \u251c\u2500 Location: filled\n\u2502    \u2502  \u251c\u2500 x\n\u2502    \u2502  \u2502  \u21d2 Literal: 0\n\u2502    \u2502  \u2514\u2500 y\n\u2502    \u2502     \u21d2 Literal: 0\n\u2502    \u2514\u2500 Location: filled\n\u2502       \u251c\u2500 x\n\u2502       \u2502  \u21d2 Literal: 0\n\u2502       \u2514\u2500 y\n\u2502          \u21d2 Variable: r\n\u2514\u2500 sequence\n   \u21d2 Sequence\n     \u2514\u2500 RydbergLevelCoupling\n        \u21d2 Pulse\n          \u251c\u2500 Detuning\n          \u2502  \u21d2 Field\n          \u2502    \u2514\u2500 Drive\n          \u2502       \u251c\u2500 modulation\n          \u2502       \u2502  \u21d2 UniformModulation\n          \u2502       \u2514\u2500 waveform\n          \u2502          \u21d2 Append\n          \u2502            \u251c\u2500 Linear\n          \u2502            \u2502  \u251c\u2500 start\n          \u2502            \u2502  \u2502  \u21d2 Literal: -20\n          \u2502            \u2502  \u251c\u2500 stop\n          \u2502            \u2502  \u2502  \u21d2 Literal: -20\n          \u2502            \u2502  \u2514\u2500 duration\n          \u2502            \u2502     \u21d2 Literal: 0.1\n          \u2502            \u251c\u2500 Linear\n          \u2502            \u2502  \u251c\u2500 start\n          \u2502            \u2502  \u2502  \u21d2 Literal: -20\n          \u2502            \u2502  \u251c\u2500 stop\n          \u2502            \u2502  \u2502  \u21d2 Literal: 20\n          \u2502            \u2502  \u2514\u2500 duration\n          \u2502            \u2502     \u21d2 Literal: 1.2\n          \u2502            \u2514\u2500 Linear\n          \u2502               \u251c\u2500 start\n          \u2502               \u2502  \u21d2 Literal: 20\n          \u2502               \u251c\u2500 stop\n          \u2502               \u2502  \u21d2 Literal: 20\n          \u2502               \u2514\u2500 duration\n          \u2502                  \u21d2 Literal: 0.1\n          \u2514\u2500 RabiFrequencyAmplitude\n             \u21d2 Field\n               \u2514\u2500 Drive\n                  \u251c\u2500 modulation\n                  \u2502  \u21d2 UniformModulation\n                  \u2514\u2500 waveform\n                     \u21d2 Append\n                       \u251c\u2500 Linear\n                       \u2502  \u251c\u2500 start\n                       \u2502  \u2502  \u21d2 Literal: 0\n                       \u2502  \u251c\u2500 stop\n                       \u2502  \u2502  \u21d2 Literal: 15\n                       \u2502  \u2514\u2500 duration\n                       \u2502     \u21d2 Literal: 0.1\n                       \u251c\u2500 Linear\n                       \u2502  \u251c\u2500 start\n                       \u2502  \u2502  \u21d2 Literal: 15\n                       \u2502  \u251c\u2500 stop\n                       \u2502  \u2502  \u21d2 Literal: 15\n                       \u2502  \u2514\u2500 duration\n                       \u2502     \u21d2 Literal: 1.2\n                       \u2514\u2500 Linear\n                          \u251c\u2500 start\n                          \u2502  \u21d2 Literal: 15\n                          \u251c\u2500 stop\n                          \u2502  \u21d2 Literal: 0\n                          \u2514\u2500 duration\n                             \u21d2 Literal: 0.1\n
"},{"location":"drafts/aws_hybrid_execution/#bloqade-emulator","title":"Bloqade Emulator","text":"

Anyone familiar with QuEra\u2019s Julia SDK Bloqade.jl knows that we\u2019re pretty obsessed with performance. We would also be remiss to advertise Bloqade\u2019s pure python emulator. While not as fast as Bloqade.jl, we have have worked to optimize our the state-vector simulator to get the most out of python as possible. The emulator supports both two and three level atom configurations, along with global and local driving and support for the blockade subspace (for those who are more familiar with Rydberg atoms). The blockade subspace and matrix calculations are nearly optimal for both memory and time and are written in pure NumPy and SciPy. We also have basic Numba JIT compiled sparse operations that further optimize the memory when solving the time-dependent Schr\u00f6dinger equation. We hope our python emulator will allow you to explore a wide variety of applications for neutral atoms and prototype some neat new algorithms with AHS.

Capitalize Bloqade across the entire article [name=jzlong]

"},{"location":"drafts/aws_hybrid_execution/#target-multiple-backends","title":"Target Multiple Backends","text":"

All Bloqade programs can be targeted to multiple emulation and hardware backends very easily, again using the chaining of .\u2019s. Also note that the chaining syntax allows Bloqade to let you know exactly when a program should be able to be run. To select braket as your service simply select the braket attribute of your program. At this stage there will be two methods availible for you, aquila and local_emulator.

I personally prefer to distinguish between methods and attributes by putting the () after a method name and omitting them for attributes [name=jzlong]

Each backend has different restrictions in terms of the types of AHS programs that can be run. During the alpha phase of Bloqade we will continue to improve the error messages that are given when targeting specific backends making it easy for you to diagnose any issues with your program execution.

Depending on the backend there are two or three methods for executing your program. For Cloud devices Bloqade has an API for both asynchronous (run_async) and synchronous (run) method for executing the job. Local emulator backends only support the run API.

Now let us revisit the meaning of args assignment. Every execution method has a args argument, this is where you can specify the values of the parameters defined in args when defining your program. The order of the arguments in the args tuple is the order of the variables specified in the args method.

a vs an? [name=jzlong]

Finally the backend object that gets created is also callable where the such that object(*args, shots=shots,...) is equivalent to object.run(shots, args=args, ...). While this is primarily a stylistic choice but this is an available interface for you if need be.

\"where the such that\" -> \"such that\" \"While this is primarily a stylistic choice but this is an available\" -> \"While this is primarily a stylicstic choice, this is also an available interface for you if need be\" [name=jzlong]

"},{"location":"drafts/aws_hybrid_execution/#job-management-features","title":"Job management Features","text":"

If you use the batch_assign API combined with your parameterized program it is possible to submit entire batches of AWS tasks. It's not enough to make programming AHS easy though; you also need to manage all the data that gets generated. Fear not, we have you covered. We know it can be quite cumbersome to have to manage hundreds or thousands of tasks so we have provided some useful functionality inside Bloqade to make your life easier as well as making experiments more reproducible.

Fear not, we have you covered! (worth adding an exclamation mark just to go the full nine yards with the chippy/enthusiastic tone). [name=jzlong]

One of the most common issues when running AHS (or just QC in general) is saving experimental results. When using a cloud device one also has the added difficultly of associating a task id with the parameter in the parameter scan as well as checking and fetching the task results as the tasks are completed. Bloqade provides a uniform format of saving this data which is useful for users to do sharable and reproducable work. To save your results simply invoke the save and load functions. We also provide dumps and loads if you want to work directly with strings instead of JSON files! It's as simple as:

capitalize ID [name=jzlong]

from bloqade import save, load\n\n# define program\n...\n\nbatch_task = my_program.braket.aquila().run_async(100)\n\nsave(batch_task, \u201cmy_aquila_results.json\u201d)\n\n# in some other file:\n\nloaded_batch_task = load(\u201cmy_aquila_results.json\u201d)\n\n# continue with analysis\n...\n

These objects will contain all the necessary information to fetch results and obtain the values of parameters used in your program.

Saving files isn\u2019t all that Bloqade offers. When dealing with a Cloud device like Aquila it is important to be able to manage those asynchronous tasks. Bloqade offers different ways to do this:

\"...isn't all that Bloqade offers.\" -> \"..isn't all that Bloqade offers to make your life easier.\"

  • batch_task.fetch() queries the current task statuses and fetches the results of completed tasks without blocking the python interpreter. The results are stored inside the current object.

  • batch_task.pull() Like fetch but waits until all tasks have been completed before unblocking the interpreter. The tasks the results are stored inside the current object.

  • batch_task.get_tasks(*status_codes) returns a new Batch object that contains the tasks with the status given by the inputs to the function

  • batch_task.remove_tasks(*status_codes) return a new Batch object that contains tasks that did not match the status code that have been given.

See the documentation for more details on what the various status codes are and what they mean.

"},{"location":"drafts/aws_hybrid_execution/#adaptive-workloads","title":"Adaptive workloads","text":"

As mentioned above, the ability to parameterize and assign values to your analog program means that there is a lot one can do in terms of running various kinds of experiments. Here we will discuss how to combine the parameterized pulses with braket\u2019s hybrid jobs!

In this case we can pass our parameterized pulse program into classical optimizer in order to provide classical feedback for the next quantum program to run. The use case we will cover here is a pretty standard problem that maps very neatly onto the AHS architecture implemented with Neutral atoms. Because of the Rydberg blockade effect the ground state of a collection of atoms maps exactly to what is called the Maximum Independent Set (MIS) problem on geometric graphs. The MIS problem is a graph coloring problem where the goal is to find the largest set of nodes in a graph such that no two nodes are connected by an edge. This problem is NP-hard and has many applications in scheduling, resource allocation, and even quantum error correction.

I also think it better to use the term \"Combinatorial Optimization\" over \"Graph Coloring\" considering CO might open a broader window in people's minds for what the machine is capable of than just \"Graph Coloring\". It would also be nice to have links to examples for each application (MIS for scheduling, resource allocation, QEC.)

We refer the reader to this Notebook example for a more detailed explanation of the problem and how it maps onto AHS. For this blog post we will focus on the implementation of the hybrid algorithm using Bloqade and Braket Hybrid Jobs.

Like most of the Bloqade programs we begin by importing the necessary components to build the program:

import numpy as np\nfrom bloqade import RB_C6, save, start, var\nfrom bloqade.atom_arrangement import Square\nfrom braket.devices import Devices\nfrom braket.aws import AwsDevice\n

using the AwsDevice we can get some information about the capabilities of the device. Note that Bloqade uses rad/us and us for energy and time units respectively while braket uses SI unites, e.g. rad/s and s, hence the conversion of units below.

# define the parameters of the experiment\ndevice = AwsDevice(Devices.QuEra.Aquila)\nrydberg_properties = device.properties.paradigm.rydberg.rydbergGlobal\n\n# Convert energy units to rad/us and time to us\ndetuning_max = float(rydberg_properties.detuningRange[1]) / 1e6\nrabi_max = float(rydberg_properties.rabiFrequencyRange[1]) / 1e6\ntotal_time = float(rydberg_properties.timeMax) * 1e6\n

For the particular problem we are studying we need to map the problem graph onto the atom arrangement. For more information we refer the reader to the notebook example.

# make sure next nearest neighbors are blockaded\nRmin = np.sqrt(2) # minimum unit disk radius\nRmax = 2  # maximum unit disk radius\nRudg = np.sqrt(Rmin * Rmax) # geometric mean of Rmin and Rmax\n\ndetuning_end = detuning_max / 4 # detuning at the end of the pulse\nblockade_radius = (RB_C6 / detuning_end) ** (1 / 6)\nlattice_spacing = blockade_radius / Rudg\n

Now we can define the program. We will use the var function to define the parameters for the program. Also the cost function will involve calculating the final energy of tha atoms which can be obtained from the geometry of the program via the rydberg_interaction method.

# define the time step for the detuning waveform\ndt = 0.4\nn_steps = int(total_time / dt)\n# create variables for the detuning time steps\ndetuning_vars = [var(f\"d{i}\") for i in range(n_steps - 1)]\n\n# define the lattice size before defect insertion\nL = 4\n# set seed for defect generation\nrng = np.random.default_rng(1337)\n# define the geometry of the program\ngeometry = (\n    Square(L, lattice_spacing)\n    .apply_defect_count(L**2 // 2, rng=rng)\n    .remove_vacant_sites()\n)\n# define the program\nprogram = (\n    geometry.rydberg.detuning.uniform.piecewise_linear(\n        n_steps * [dt], [-detuning_end] + detuning_vars + [detuning_end]\n    )\n    .amplitude.uniform.piecewise_linear(\n        [0.1, total_time - 0.2, 0.1], [0, rabi_max, rabi_max, 0]\n    )\n)\n# get the interaction matrix\nV_ij = geometry.interaction_matrix()\n

We need to build the infrastructure to do the hybrid job. There are many different tools availible in braket.jobs that allow you to log the progress of your hybrid algorithm. Here we set up a simple class that wraps the cost function and log the progress of the algorithm.

from braket.jobs import (\n    InstanceConfig,\n    hybrid_job,\n    save_job_checkpoint,\n)\nfrom braket.jobs.metrics import log_metric\nfrom braket.jobs_data import PersistedJobDataFormat\nfrom braket.tracking import Tracker\n\n# define a wrapper for the cost function for reporting\nclass CostFuncWrapper:\n    def __init__(self, backend, shots=10, **options):\n        self.backend = backend\n        self.options = options\n        self.iterations = 0\n        self.shots = shots\n        self.prev_calls = {}\n        self.task_tracker = Tracker().start()\n\n    @staticmethod\n    def cost_func(report):\n        bitstrings = 1 - np.asarray(report.bitstrings(False))\n        detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n        interaction_energies = np.einsum(\n            \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n        )\n\n        total_energy = detuning_energies + interaction_energies\n        # minimize the energy mean and standard deviation\n        return total_energy.mean() + total_energy.std()\n\n    def __call__(self, x):\n        args = tuple(x)\n\n        batch_task = self.backend.run(self.shots, args=args, **self.options)\n        report = batch_task.report()\n\n        save(batch_task, f\"my-aquila_results-{self.iterations}.json\")\n\n        self.prev_calls[args] = report\n        return self.cost_func(report)\n\n    def callback(self, state):\n        args = tuple(state.x)\n        self.iterations += 1\n        bitstrings = 1 - np.asarray(self.prev_calls[args].bitstrings(False))\n        detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n\n        interaction_energies = np.einsum(\n            \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n        )\n\n        total_energy = detuning_energies + interaction_energies\n        mean_energy = total_energy.mean()\n        std_energy = total_energy.std()\n\n        # Log metrics to display in Braket Console\n        log_metric(\n            iteration_number=self.iterations, value=state.fun, metric_name=\"loss\"\n        )\n        log_metric(\n            iteration_number=self.iterations,\n            value=mean_energy,\n            metric_name=\"mean energy\",\n        )\n        log_metric(\n            iteration_number=self.iterations,\n            value=std_energy,\n            metric_name=\"std energy\",\n        )\n\n        # Also track the quantum task cost for Braket devices\n        braket_task_cost = float(\n            self.task_tracker.qpu_tasks_cost()\n            + self.task_tracker.simulator_tasks_cost()\n        )\n        log_metric(\n            metric_name=\"braket_cost\",\n            value=braket_task_cost,\n            iteration_number=self.iterations,\n        )\n\n        # Save a checkpoint to resume the hybrid job from where you left off\n        checkpoint_data = {\"i\": self.iterations, \"args\": args}\n        save_job_checkpoint(\n            checkpoint_data, data_format=PersistedJobDataFormat.PICKLED_V4\n        )\n        # end the job if the std energy is less than 5% of the mean energy\n        # this indicates that the system is close to the ground state\n        return abs(std_energy / mean_energy) < 0.05\n

While this is a lot to take in let us go through some important things. Firstly, you can generate a Report object from a Bloqade batch of tasks. This object provides some basic analysis methods common to Neutral atom AHS computing. The one we make the most use of here is the bitstrings method which return a list of arrays that contains the shot results after executing the program. It takes a boolean argument that specifies whether or not to return the bitstrings of shots where there were atoms that did not get put into a trap before the computation was executed. By default this filter is applied, but for this problem we want to include those shots in our analysis hence the False argument.

No need to capitalize \"Batch\" unless you want to refer to the object type You can just say AHS, AHS Computing seems redundant Do we assume users know what a trap is? Might be worth having a sentence or two, just say something like \"It takes a boolean argument that specifies whether or not to return the bitstrings of shots where there were atoms that did not successfully get put into position...\"

Another thing to note is that our cost function not only contains the mean energy but also the standard deviation. The reason for this is because we are targeting an eigenstate of the final Hamiltonian which has no energy variance. This is a good way to check if the system is in the ground state. We use the ratio of the standard deviation to the mean energy as a stopping condition for the algorithm.

Finally, we have a callback function that is called after each iteration of the optimizer. This is where we can log the progress of the algorithm. We use the Tracker object to track the cost of the quantum tasks that are being executed. We also use the log_metric function to log the mean and standard deviation of the energy as well as the cost function of the quantum tasks.

Now we can define the function that will run the hybrid job.

def run_algo(assigned_program, device_arn=None, n_calls=10, shots=10):\n    @hybrid_job(\n        device=device_arn,  # Which device to get priority access to\n        dependencies=\"requirements.txt\",  # install bloqade\n        instance_config=InstanceConfig(\"ml.m5.large\"),\n    )\n    def _runner(backend, shots, n_calls, **options):\n        from skopt import gp_minimize\n\n        # Braket task cost\n        wrapped_cost_func = CostFuncWrapper(backend, shots=shots, **options)\n\n        n_params = len(backend.params.args_list)\n        bounds = n_params * [(-detuning_max, detuning_max)]\n\n        result = gp_minimize(\n            wrapped_cost_func,\n            bounds,\n            callback=wrapped_cost_func.callback,\n            n_calls=n_calls,\n        )\n\n        detuning_values = {var.name: val for var, val in zip(detuning_vars, result.x)}\n\n        return detuning_values\n\n    if device_arn == Devices.QuEra.Aquila:  # use Aquila\n        backend = assigned_program.braket.aquila()\n        options = dict()\n    else:  # use  bloqade emulator\n        backend = assigned_program.bloqade.python()\n        options = dict(atol=1e-8, rtol=1e-4, solver_name=\"dopri5\")\n\n    # Run the hybrid job\n    return _runner(backend, n_calls, shots, **options)\n

We use the hybrid_job decorator to define the hybrid job. This decorator takes a device_arn argument which is the Amazon Resource Name (ARN) of the device you want to run the hybrid job on. There are some other options associated with the EC2 instance that is used to run the hybrid job. We will use the InstanceConfig object to specify the instance type and number of instances to use. dependencies points to a text file that contains the python dependencies needed to run this hybrid job. In this case we need bloqade and scikit-optimize in this text file.

The function _runner that is being decorated must take the backend as the first argument. This object is generated by the Bloqade API and must match the device arn that is specified in the decorator. n_calls is the total number of iterations of the optimizer and shots is the number of shots to use for each iteration. options is a dictionary of options that are passed to the run method of the backend. The return value of the function is the final value of the parameters that were optimized. We wrap the _runner inside run_algo that takes the program and the device_arn as arguments to make sure that the device requested by hybrid_jobs matches the device called by bloqade as well as setting up specialized options for the different bloqade backends.

To run the hybrid job we simply call the run_algo function with the assigned program and the device arn.

optimal_parameters = run_algo(program.args(detuning_vars), device_arn=Devices.QuEra.Aquila)\n

Finally we can plot the results of the hybrid job.

assigned_program = program.assign(**optimal_params)\nassigned_program.show()\n

To run the algorithm with Bloqade emulator we simply change the device arn to None.

Full source code:

```python= import numpy as np from bloqade import RB_C6, save, start, var from bloqade.atom_arrangement import Square from braket.devices import Devices from braket.aws import AwsDevice

"},{"location":"drafts/aws_hybrid_execution/#define-the-parameters-of-the-experiment","title":"define the parameters of the experiment","text":"

device = AwsDevice(Devices.QuEra.Aquila) rydberg_properties = device.properties.paradigm.rydberg.rydbergGlobal

"},{"location":"drafts/aws_hybrid_execution/#convert-energy-units-to-radus-and-time-to-us","title":"Convert energy units to rad/us and time to us","text":"

detuning_max = float(rydberg_properties.detuningRange[1]) / 1e6 rabi_max = float(rydberg_properties.rabiFrequencyRange[1]) / 1e6 total_time = float(rydberg_properties.timeMax) * 1e6

"},{"location":"drafts/aws_hybrid_execution/#make-sure-next-nearest-neighbors-are-blockaded","title":"make sure next nearest neighbors are blockaded","text":""},{"location":"drafts/aws_hybrid_execution/#unit-disk-minimum-and-maximum-radii","title":"unit disk minimum and maximum radii","text":"

Rmin = np.sqrt(2) # minimum unit disk radius Rmax = 2 # maximum unit disk radius

"},{"location":"drafts/aws_hybrid_execution/#choose-geometric-mean-of-rmin-and-rmax","title":"choose geometric mean of Rmin and Rmax","text":"

Rudg = np.sqrt(Rmin * Rmax)

detuning_end = detuning_max / 4 blockade_radius = (RB_C6 / detuning_end) ** (1 / 6) lattice_spacing = blockade_radius / Rudg

"},{"location":"drafts/aws_hybrid_execution/#define-the-time-step-for-the-detuning-waveform","title":"define the time step for the detuning waveform","text":"

dt = 0.4 n_steps = int(total_time / dt)

"},{"location":"drafts/aws_hybrid_execution/#create-variables-for-the-detuning-time-steps","title":"create variables for the detuning time steps","text":"

detuning_vars = [var(f\"d{i}\") for i in range(n_steps - 1)]

"},{"location":"drafts/aws_hybrid_execution/#define-the-lattice-size-before-defect-insertion","title":"define the lattice size before defect insertion","text":"

L = 4

"},{"location":"drafts/aws_hybrid_execution/#set-seed-for-geometry-generation","title":"set seed for geometry generation","text":"

rng = np.random.default_rng(1337) program = ( Square(L, lattice_spacing) .apply_defect_count(L**2 // 2, rng=rng) .remove_vacant_sites() .rydberg.detuning.uniform.piecewise_linear( n_steps * [dt], [-detuning_end] + detuning_vars + [detuning_end] ) .amplitude.uniform.piecewise_linear( [0.1, total_time - 0.2, 0.1], [0, rabi_max, rabi_max, 0] ) )

"},{"location":"drafts/aws_hybrid_execution/#get-atom-register-and-interaction-matrix","title":"get atom register and interaction matrix","text":"

V_ij = program.parse_register().rydberg_interaction()

from braket.jobs import ( InstanceConfig, hybrid_job, save_job_checkpoint, ) from braket.jobs.metrics import log_metric from braket.jobs_data import PersistedJobDataFormat from braket.tracking import Tracker

"},{"location":"drafts/aws_hybrid_execution/#define-a-wrapper-for-the-cost-function-for-reporting","title":"define a wrapper for the cost function for reporting","text":"

class CostFuncWrapper: def init(self, cost_func, backend, shots=10, **options): self.backend = backend self.options = options self.iterations = 0 self.shots = shots self.prev_calls = {} self.task_tracker = Tracker().start()

@staticmethod\ndef cost_func(report):\n    bitstrings = 1 - np.asarray(report.bitstrings(False))\n    detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n    interaction_energies = np.einsum(\n        \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n    )\n\n    total_energy = detuning_energies + interaction_energies\n    # minimize the energy mean and standard deviation\n    return total_energy.mean() + total_energy.std()\n\ndef __call__(self, x):\n    args = tuple(x)\n\n    batch_task = self.backend.run(self.shots, args=args, **self.options)\n    report = batch_task.report()\n\n    save(batch_task, f\"my-aquila_results-{self.iterations}.json\")\n\n    self.prev_calls[args] = report\n    return self.cost_func(report)\n\ndef callback(self, state):\n    args = tuple(state.x)\n    self.iterations += 1\n    bitstrings = 1 - np.asarray(self.prev_calls[args].bitstrings(False))\n    detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n\n    interaction_energies = np.einsum(\n        \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n    )\n\n    total_energy = detuning_energies + interaction_energies\n    mean_energy = total_energy.mean()\n    std_energy = total_energy.std()\n\n    # Log metrics to display in Braket Console\n    log_metric(\n        iteration_number=self.iterations, value=state.fun, metric_name=\"loss\"\n    )\n    log_metric(\n        iteration_number=self.iterations,\n        value=mean_energy,\n        metric_name=\"mean energy\",\n    )\n    log_metric(\n        iteration_number=self.iterations,\n        value=std_energy,\n        metric_name=\"std energy\",\n    )\n\n    # Also track the quantum task cost for Braket devices\n    braket_task_cost = float(\n        self.task_tracker.qpu_tasks_cost()\n        + self.task_tracker.simulator_tasks_cost()\n    )\n    log_metric(\n        metric_name=\"braket_cost\",\n        value=braket_task_cost,\n        iteration_number=self.iterations,\n    )\n\n    # Save a checkpoint to resume the hybrid job from where you left off\n    checkpoint_data = {\"i\": self.iterations, \"args\": args}\n    save_job_checkpoint(\n        checkpoint_data, data_format=PersistedJobDataFormat.PICKLED_V4\n    )\n    # end the job if the std energy is less than 5% of the mean energy\n    # this indicates that the system is close to the ground state\n    return abs(std_energy / mean_energy) < 0.05\n

def run_algo(assigned_program, device_arn=None, n_calls=10, shots=10): @hybrid_job( device=device_arn, # Which device to get priority access to dependencies=\"requirements.txt\", # install bloqade instance_config=InstanceConfig(\"ml.m5.large\"), ) def _runner(backend, shots, n_calls, **options): from skopt import gp_minimize

    # Braket task cost\n    wrapped_cost_func = CostFuncWrapper(backend, shots=shots, **options)\n\n    n_params = len(backend.params.args_list)\n    bounds = n_params * [(-detuning_max, detuning_max)]\n\n    result = gp_minimize(\n        wrapped_cost_func,\n        bounds,\n        callback=wrapped_cost_func.callback,\n        n_calls=n_calls,\n    )\n\n    detuning_values = {var.name: val for var, val in zip(detuning_vars, result.x)}\n\n    return detuning_values\n\nif device_arn == Devices.QuEra.Aquila:  # use Aquila\n    backend = assigned_program.braket.aquila()\n    options = dict()\nelse:  # use  bloqade emulator\n    backend = assigned_program.bloqade.python()\n    options = dict(atol=1e-8, rtol=1e-4, solver_name=\"dopri5\")\n\n# Run the hybrid job\nreturn _runner(backend, n_calls, shots, **options)\n
"},{"location":"drafts/aws_hybrid_execution/#optimal_params-run_algoprogramargsdetuning_vars-devicesqueraaquila","title":"optimal_params = run_algo(program.args(detuning_vars), Devices.QuEra.Aquila)","text":"

optimal_params = run_algo(program.args(detuning_vars), None, n_calls=10, shots=10)

assigned_program = program.assign(**optimal_params) assigned_program.show()

```

"},{"location":"home/advanced_usage/","title":"Advanced Usage","text":""},{"location":"home/advanced_usage/#geometry-concepts","title":"Geometry Concepts","text":"

In Getting Started, we discussed the parameterization of waveforms inside a Rydberg/hyperfine drive. We have also provided infrastructure that allows you to parameterize the position of the atoms/sites in your atom array. There are a few methods to do this:

"},{"location":"home/advanced_usage/#scaling-your-lattice","title":"Scaling Your Lattice","text":"

Every AtomArrangement object has an option to scale, which applies a multiplicative factor to all site coordinates. To parameterize this scaling, you can either pass a literal value, a string, or a scalar expression:

new_geometry_1 = my_geometry.scale(1.0)\nnew_geometry_2 = my_geometry.scale('scale_factor')\n\nscale_expr = var(\"param\") / 2 + var(\"starting_scale\")\nnew_geometry_3 = my_geometry.scale(scalar_expr)\n
"},{"location":"home/advanced_usage/#parameterize-the-position-of-individual-sites","title":"Parameterize the Position of Individual Sites","text":"

Suppose you are constructing a set of sites using the start.add_position(...) method. In that case, you can pass a literal value, a string, or a scalar expression to the position argument:

x_div_2 = var(\"x\") / 2\ny_dic_2 = var(\"y\") / 2\nstart.add_position([(0, 1), (\"x\", \"y\"), (x_div_2, y_div_2)])\n
"},{"location":"home/advanced_usage/#parallelize-over-space","title":"Parallelize Over Space","text":"

For AHS programs with a few sites/atoms, you can (and should) make full use of the user area allowed by QuEra's AHS devices. While the Rydberg interaction between the atoms decays as a power-law, it decays fast enough for you to separate small clusters of atom sites cleanly and be assured they will not interact and entangle. This fact allows you to parallelize your program over space. We have added this functionality into Bloqade by simply calling the .parallelize(spacing) method, where spacing is a real value, the spacing between the bounding boxes of the clusters. For example, calling .parallelize(20.0) would ensure that the atoms in other clusters are at least 20 um away from each other.

When fetching the results and generating the report, the shot results will be organized by the cluster to which they belong. The methods of the Report object will always merge all the cluster shots so you can analyze them as if you're analyzing a single cluster.

"},{"location":"home/advanced_usage/#programming-a-local-drive","title":"Programming A Local Drive","text":"

In the Getting Started section, you might have noticed that we used ...uniform... when defining the various drives applied to the atoms. This syntax refers to one of three ways of expressing the spatial modulation of the waveform. What do we mean by spatial modulation? In this case, you can think of a drive having a temporal component, e.g., the waveform, and a spatial part, which we call spatial modulation. The spatial modulation is a scale factor applied to the waveform when acting on a particular site. For example, if we have two atoms and we have a spatial modulation for site-0 which is 0.1 and for site-1 which is 0.2, then the waveform will be scaled by 0.1 when acting on-site-0 and 0.2 when applied to site-1.

Intuitively, uniform spatial modulation simply means that regardless of the atom, the waveform value is always the same. The other two options for defining spatial modulations are: scale and location. They have two distinct use cases:

"},{"location":"home/advanced_usage/#scale-method","title":"scale Method","text":"

When calling the scale method, you can pass either a list of real values or a string. The list of values defines the scaling for every atom in the system, so the number of elements must equal the number of sites in your geometry. The string is a placeholder that allows you to define that mask as a list of values by passing them in through assign, batch_assign, or args. For assign and batch_assign, you can pass a list or a list of lists, respectively. On the other hand, args, the list of values, must be inserted into the args tuple when calling run or run_async, for example, let's take a two-atom program:

from bloqade import start\n\nprogram = (\n    start.add_position([(0, 0), (0, 5)])\n    .rydberg.detuning.scale(\"local_detuning\")\n    .piecewise_linear([0.1, 1.0, 0.1], [0, \"detuning\", \"detuning\", 0])\n    .amplitude.uniform.piecewise_linear([0.1, 1.0, 0.1], [0, 15, 15, 0])\n    .args([\"local_detuning\", \"detuning\"])\n    .bloqade.python()\n)\n

To call the run method properly, you must unpack the spatial modulation list into the tuple, e.g.

local_detuning = [0.4, 0.1]\n\nprogram.run(100, args=(*local_detuning, 30))\n
"},{"location":"home/advanced_usage/#location-method","title":"location Method","text":"

The location method takes two arguments; the second is optional. The first argument is an integer or a list of integers corresponding to the site(s) to which you want to apply the spatial modulation. The second argument is the value you wish to apply to the site(s). If you pass a list of integers, you must pass a list of values for the second argument, while if you pass a single integer, you must pass a single value. The interpretation is that for any sites not specified in the first argument, the spatial modulation is 0.0. Note that the values you pass in can be a real value, a string (for a variable), or a scalar expression. Below is an example using location:

program = (\n    start.add_position([(0, 0), (0, 5)])\n    .rydberg.detuning.location([0, 1], [\"detuning_0\", \"detuning_1\"])\n    .piecewise_linear([0.1, 1.0, 0.1], [0, \"detuning\", \"detuning\", 0])\n    .amplitude.location(0)\n    .piecewise_linear([0.1, 1.0, 0.1], [0, 15, 15, 0])\n    .location(0, \"rabi_scale\")\n    .piecewise_linear([0.1, 1.0], [0, 15, 0])\n)\n
"},{"location":"home/advanced_usage/#slicing-waveforms","title":"Slicing Waveforms","text":"

Sometimes, you want to run a program at intermediate time points as a parameter scan. To facilitate this, we have added the slice method that can be called during the waveform construction. The slice method takes two arguments: start and stop. The start argument determines the new starting point for the sliced waveform. At the same time, stop specifies the new stopping time for the waveform. A valid slice must have start <= stop, start >= 0, and stop <= duration, where duration is the duration of the waveform. Both arguments can be Scalar expressions as well as concrete values.

record is another useful feature we have implemented to be used with slice. For example, if you slice a waveform, you may or may not know the value of the waveform at the end of the resulting waveform from the slice. On the other hand, you need to be able to use that slice as a base for a new waveform that is continuous, which requires knowledge of the value of the sliced waveform at the end of the slice. A common use case for this is to take a Rabi drive and ensure it is 0 at the end of the protocol to make it compatible with hardware. A great example of this exact use case is Quantum Scar Dynamics Tutorial, which goes through exactly how to use slice and record together.

"},{"location":"home/advanced_usage/#python-functions-as-waveforms","title":"Python Functions as Waveforms","text":"

In Bloqade, we provide a set of commonly used waveform shapes for hardware applications. However, we also allow users to pass various Python functions as Waveforms! There are two ways to go about using Python functions as waveforms. The first involves passing the function into the fn() method when building your program. The second involves the bloqade.waveform decorator on your function, turning it into a waveform object.

These are super convenient for emulation. However, these waveforms can't be executed on quantum hardware. As such, we provide the sample method that allows you to easily discretize your waveform into a form compatible with the hardware. This method replaces your waveform with a piecewise linear or piecewise constant form. The Rabi amplitude and detuning both expect piecewise linear while the phase expects piecewise constant for the waveforms. If you are using the builder method to build your program, you do not need to specify the kind of interpolation explicitly; Bloqade can deduce from the build what type of interpolation to do, but if you are using the waveform decoration, you need to specify the kind of interpolation, either 'linear' or 'constant'.

An example on Floquet Dynamics is an excellent place to start with these particular features.

"},{"location":"home/getting_started/","title":"Getting Started","text":"

This page is an excellent place to start for those familiar with the Neutral Atoms AHS model. For those who are not, we recommend you read our Bloqade 101 tutorial. Here, we will go through how to define your AHS program for two and three-level schemes. The beginning of your program starts with the atom geometry. You can build it as a list of coordinates or by using some pre-defined Bravais Lattices found in bloqade.atom_arrangements. For example, to define a simple 2x2 square lattice, you can do the following:

from bloqade.atom_arrangement import Square\n\nprogram = (\n    Square(2, 2, lattice_spacing=6.0)\n)\n
The analog pulse sequence is defined through the . syntax starting with which level coupling to drive rydberg or hyperfine. Next, specify the detuning, rabi.amplitude, and rabi.phase. After this, you specify the spatial modulation of that waveform, e.g. the relative scale factor that each atom feels from a given waveform. Finally, you select the temporal modulation of the waveform. You can build the pulses in various ways. Because we use the . to split up the different parts of the pulse program, Bloqade will only give you valid options for the next part of the pulse program. For example, to define a simple two-level Rabi drive with a detuning:

from bloqade import start\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(10, 1.1)\n    .amplitude.uniform.constant(15, 1.1)\n)\n

There are some helpful shortcuts for generating piecewise linear and piecewise constant waveforms. For example, to define a piecewise linear Rabi amplitude that starts at 0 ramps up to 15 rad/us, stays at 15 rad/us for 1.1 us, and then ramps back down to 0 we just need two lists. The first list is the durations of each linear segment in us, and the second is the Rabi amplitude in rad/us. The i-th element in durations is the duration between values[i] and values[i+1].

from bloqade import start\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(10, 1.1)\n    .amplitude.uniform.piecewise_linear([0.05, 1.0, 0.05], [0, 15, 15, 0])\n)\n

Note that because rydberg.detuning precedes amplitude, we do not need to specify rabi.amplitude. If we flip the order, we need to put rabi in the chain of dots. To run your program, you can select the backend you want to target:

emulation_result = program.bloqade.python().run(100)\nhardware_result = program.braket.aquila().run_async(100)\n
here, run_async denotes that the function call is asynchronous, meaning that the function will return immediately, and the result will be a future-like object that will handle retrieving the results from the cloud. You can also call run, but this will block Python until the results from the QPU have been completed. For more on this, see our Tutorials.

It is easy to add hyperfine drives to your program. Select your program's .hyperfine property to start building the hyperfine pulse sequence. By selecting the rydberg and hyperfine properties, you can also switch back and forth between the different kinds of drives. To tell what kind of drive is being built, follow the string of options back to the first instance you find of either rydberg or hyperfine. Looking back also determines if the drive acts as the detuning, rabi.amplitude, and rabi.phase.

"},{"location":"home/getting_started/#parameterized-programs","title":"Parameterized Programs","text":"

This is all very nice, but tracking individual tasks when doing parameter scans is annoying. Bloqade takes care of this by allowing you to parameterize the pulse sequences. For example, we want to sweep over the Rabi drive's drive time. In that case, you can insert strings into the fields to turn those into variables or make an explicit variable object:

from bloqade import start, var\n\nrun_time = var(\"run_time\")\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(10, run_time + 0.1)\n    .amplitude.uniform.piecewise_linear([0.05, run_time, 0.05], [0, 15, 15, 0])\n)\n

here we use a variable run_time, which denotes the length of the rabi drive \"plateau.\" These variables support simple arithmetic such as +, -, *, and/, as shown in the previous code example, which we used to define the duration of the detuning waveform in the last example code. To define a parameter scan, simply use the batch_assign method before calling the execution backend:

result = (\n    program.batch_assign(run_time=[0.1, 0.2, 0.3, 0.4, 0.5])\n    .bloqade.python()\n    .run(100)\n)\n

There are also other methods available to assign the parameter; for example, if we do not know the values of the parameters we would like to run in a particular task, we can use the args method to specify that \"run_time\" will be assigned when calling the run or run_async methods. This function takes a list as an input, and the order of the names in the list corresponds to the order in the variables that need to be specified during the call of run. For example, let's say our program has two parameters, \"a\" and \"b\". We can specify both of these parameters as runtime assigned:

assigned_program = program.args([\"a\", \"b\"])\n
Now when you execute this program you need to specify the values of \"a\" and \"b\" in the run method:

result = assigned_program.bloqade.python().run(100, args=(1, 2))\n
where args argument is a tuple of the values of \"a\" and \"b\" respectively.

There is also an assign(var1=value1, var2=value2, ...) method which is useful if you are given a program that is imported from another package or comes from a source which you should not edit directly. In this case you can use the assign method to assign the value of the parameters for every task execution that happens.

"},{"location":"home/getting_started/#analyzing-results","title":"Analyzing Results","text":""},{"location":"home/getting_started/#batch-objects","title":"Batch Objects","text":"

Now that you have your program, we need to analyze the results. The results come in either a RemoteBatch and LocalBatch. RemoteBatch objects are returned from any execution that calls a remote backend, e.g. braket.aquila(). In contrast, LocalBatch is returned by local emulation backends, e.g., bloqade.python(), or braket.local_emulator(). The only difference between RemoteBatch and LocalBatch is that RemoteBatch has extra methods that you can use to fetch remote results, check the status of remote tasks, and filter based on the task status. Some things to note about RemoteBatch objects:

  • Filtering is applied based on the tasks' current known status. If you filter based on the current status, you can precede the filter method with a result.fetch() call, e.g., completed_results = results.fetch().get_completed_tasks().
  • The pull() method will wait until all tasks have stopped running, e.g., tasks that are completed, failed, or canceled, before continuing execution of your Python code. This functionality is helpful for hybrid tasks where your classical step can only happen once the quantum task(s) have finished.

You must have active credentials for fetch() and pull() to run without an exception. Finally, batch objects can be saved/loaded as JSON via bloqade.save, bloqade.dumps, bloqade.load, and bloqade.loads.

"},{"location":"home/getting_started/#report-objects","title":"Report Objects","text":"

Both RemoteBatch and LocalBatch objects support a report() method that will take any task data and package it up into a new object that is useful for various kinds of analysis. The three main modes of analysis are:

report.bitstrings(filter_perfect_filling=True)\nreport.rydberg_densities(filter_perfect_filling=True)\nreport.counts(filter_perfect_filling=True)\n

During the program execution on the hardware atoms may sometimes not end up in every specified site. Thus each shot has a pre and post-sequence measurement of the atoms. In specific applications, having a missing atom can mean your computation will not give the correct results, so it is helpful to filter out shots that are not perfectly filled using the boolean option in all three methods. Below, we summarize the different methods and what they return:

  1. bitstrings is a method that returns a list of numpy arrays where each array is a (shots, num_sites) array of 0 or 1. Note that 0 corresponds to the Rydberg state while one corresponds to the ground state
  2. rydberg_densities is a method that returns a Pandas Series object that is an average over the shots and gives the probability of each atom being in the Rydberg state over every single task in the report.
  3. counts is a method that returns a list of ordered dictionaries where the keys are the bitstrings as a string, and the values are the number of times that bitstring was observed in the shots.

Another helpful method is report.list_param(param_string), which returns a list of values for the particular parameter given as a string in the function's input. This data is useful for plotting parameter scans. For example, if we want to plot the Rydberg density as a function of the Rabi drive time we can do the following:

from bloqade import start\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nrun_times = np.linspace(0,1,51)\n\nreport = (\n    start.add_position((0, 0))\n    .add_position((0, 5.0))\n    .rydberg.detuning.uniform.constant(10, \"run_time\")\n    .amplitude.uniform.constant(15, \"run_time\")\n    .batch_assign(run_time=run_times)\n    .bloqade.python().run(1000).report()\n)\n\ntimes = report.list_param(\"run_time\")\ndensities = report.rydberg_densities(filter_perfect_filling=True)\n\nplt.plot(times, densities)\nplt.xlabel(\"Rabi Drive Time (us)\")\nplt.ylabel(\"Rydberg Density\")\nplt.show()\n

This concludes the intermediate tutorial, for more advanced usage see our Advanced Usage tutorial.

"},{"location":"home/gotchas/","title":"Bloqade Gotchas: Common Mistakes in Using Bloqade","text":"

It is tempting when coming from different quantum SDKs and frameworks to apply the same pattern of thought to Bloqade. However, a lot of practices from those prior tools end up being anti-patterns in Bloqade. While you can use those patterns and they can still work, it ends up causing you the developer to write unnecessarily verbose, complex, and hard-to-read code as well as preventing you from reaping the full benefits of what Bloqade has to offer.

This page is dedicated to cataloguing those anti-patterns and what you can do instead to maximize the benefit Bloqade can offer you.

"},{"location":"home/gotchas/#redefining-lattices-and-common-atom-arrangements","title":"Redefining Lattices and Common Atom Arrangements","text":"

You might be tempted to define lattice-based geometries through the following means:

from bloqade import start\n\nspacing = 4.0\ngeometry = start.add_positions(\n    [(i * spacing, j * spacing) for i in range(4) for j in range(4)]\n)\n

This is quite redundant and verbose, especially considering Bloqade offers a large number of pre-defined lattices you can customize the spacing of in bloqade.atom_arrangement. In the code above, we're just defining a 4x4 square lattice of atoms with 4.0 micrometers of spacing between them. This can be expressed as follows

from bloqade.atom_arrangement import Square\n\nspacing = 4.0\ngeometry = Square(4, lattice_spacing = spacing)\n
"},{"location":"home/gotchas/#copying-a-program-to-create-new-ones","title":"Copying a Program to create New Ones","text":"

Many gate-based SDKs rely on having a mutable object representing your circuit. This means if you want to build on top of some base circuit without mutating it, you have to copy it:

import copy\n\nbase_circuit = qubits.x(0)....\n# make copy of base circuit\ncustom_circuit_1 = copy(base_circuit)\n# build on top of copy of base circuit\ncustom_circuit_1.x(0).z(5)...\n# create a new circuit by copying the base again\ncustom_circuit_2 = copy(base_circuit)\n# build on top of that copy again\ncustom_circuit_2.y(5).cz(0,2)...\n

In Bloqade Python this is unnecessary because at every step of your program an immutable object is returned which means you can save it and not have to worry about mutating any internal state.

from bloqade import start\nbase_program = start.add_position((0,0)).rydberg.rabi.amplitude.uniform\n# Just recycle your base program! No `copy` needed!\nnew_program_1 = base_program.constant(duration=5.0, value=5.0)\nnew_program_2 = base_program.piecewise_linear(\n    durations=[5.0], values = [0.0, 5.0]\n)\n
"},{"location":"home/gotchas/#creating-new-programs-instead-of-using-batch_assign","title":"Creating New Programs Instead of Using .batch_assign","text":"

If you have a set of parameters you'd like to test your program on versus a single parameter, don't generate a new program for each value:

rabi_values = [2.0, 4.7, 6.1]\nprograms_with_different_rabi_values = []\n\nfor rabi_value in rabi_values:\n    program = start.add_position((0, 0)).rydberg.rabi.amplitude.uniform.constant(\n        duration=5.0, value=rabi_value\n    )\n    programs_with_different_rabi_values.append(program)\n\nresults = []\n\nfor program in programs_with_different_rabi_values:\n    result = program.bloqade.python().run(100)\n    results.append(result)\n

Instead take advantage of the fact Bloqade has facilities specifically designed to make trying out multiple values in your program without needing to make individual copies via .batch_assign. The results are also automatically handled for you so each value you test has its own set of results, but all collected in a singular dataframe versus the above where you'd have to keep track of individual results.

rabi_values = [2.0, 4.7, 6.1]\n# place a variable for the Rabi Value and then batch assign values to it\nprogram_with_rabi_values = start.add_position(\n    0, 0\n).rydberg.rabi.amplitude.uniform.constant(duration=5.0, value=\"rabi_value\")\nprogram_with_assignments = program_with_rabi_values.batch_assign(\n    rabi_value=rabi_values\n)\n\n# get your results in one dataframe versus having to keep track of a\n# bunch of individual programs and their individual results\nbatch = program_with_assignments.bloqade.python().run(100)\nresults_dataframe = batch.report().dataframe\n
"},{"location":"home/quick_start/","title":"Quickstart","text":""},{"location":"home/quick_start/#quick-start","title":"Quick Start","text":"

All the sections below are self-contained, you can click on the links in the Table of Contents to read the relevant parts.

"},{"location":"home/quick_start/#navigating-the-bloqade-api","title":"Navigating the Bloqade API","text":"

As you develop your Bloqade program, you are expected to rely on pop-up \"hints\" provided in your development environment to help you determine what the next part of your program should be.

"},{"location":"home/quick_start/#vs-code","title":"VS Code","text":"

In VS Code this is automatic, just type the . and see what options pop up:

"},{"location":"home/quick_start/#jetbrains-pycharm","title":"JetBrains PyCharm","text":"

The same goes for JetBrains PyCharm:

"},{"location":"home/quick_start/#jupyter-notebook","title":"Jupyter Notebook","text":"

In a Jupyter Notebook you'll need to type . and then hit tab for the hints to appear:

"},{"location":"home/quick_start/#ipython","title":"IPython","text":"

The same goes for IPython:

"},{"location":"home/quick_start/#defining-atom-geometry","title":"Defining Atom Geometry","text":"

You can import pre-defined geometries based on Bravais lattices from bloqade.atom_arrangement. You may also specify a lattice spacing which dictates the spacing between the atoms as well as the number of atom sites in a certain direction.

from bloqade.atom_arrangement import Square, Kagome\n\nsimple_geometry = Square(2, 4, lattice_spacing = 4.0)\nmore_complex_geometry = Kagome(2, 2, lattice_spacing = 2.0)\n

You can easily visualize your geometries as well with .show():

more_complex_geometry.show()\n

You can also add positions to a pre-defined geometry:

from bloqade.atom_arrangement import Square\n\nbase_geometry = Square(2)\ngeometry_with_my_positions = base_geometry.add_position([(10,10), (20,20)])\n

As well as apply defects via .apply_defect_density. In the example below we apply a defect with a probability of 0.2:

from bloqade.atom_arrangement import Square, Kagome\n\nmore_complex_geometry = Kagome(2, 2, lattice_spacing = 2.0)\ndefective_geometry = more_complex_geometry.apply_defect_density(0.2)\n

Or if you want to completely roll out your own atom geometry from scratch just use add_position by itself:

from bloqade import start\n\nmy_geometry = start.add_position([(1,2), (3,4), (5,6)])\n
"},{"location":"home/quick_start/#building-waveforms","title":"Building Waveforms","text":"

After you've defined a geometry you:

  • Specify which level coupling to drive: rydberg or hyperfine
  • Specify detuning, rabi.amplitude or rabi.phase
  • Specify the spatial modulation

Which then leads you to the ability to specify a waveform of interest and begin constructing your pulse sequence. In the example below, we target the ground-Rydberg level coupling to drive with uniform spatial modulation for the Rabi amplitude. Our waveform is a piecewise linear one which ramps from \\(0\\) to \\(5 \\,\\text{rad/us}\\), holds that value for \\(1 \\,\\text{us}\\) and then ramps back down to \\(0 \\,\\text{rad/us}\\).

from bloqade import start\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nwaveform_applied = (\n    target_rabi_amplitude\n    .piecewise_linear(durations = [0.06, 1, 0.06], values = [0, 5, 5, 0])\n)\n

You aren't restricted to just piecewise linear waveforms however, you can also specify:

  • linear - Define a transition from one value to another over a duration
  • constant - Define a fixed value over a duration
  • piecewise_constant - Define a step-wise function with specific durations for each step
  • poly - Define a polynomial waveform using coefficients over a duration
"},{"location":"home/quick_start/#arbitrary-functions-as-waveforms","title":"Arbitrary Functions as Waveforms","text":"

For more complex waveforms it may provde to be tedious trying to chain together a large number of piecewise_constant or piecewise_linear methods and instead easier to just define the waveform as a function of time.

Bloqade lets you easily plug in an arbitrary function with .fn:

from bloqade import start\nfrom math import sin\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\n\ndef custom_waveform(t): \n    return 2.0 * sin(t)\n\ncustom_waveform_applied = (\n    target_rabi_amplitude\n    .fn(custom_waveform, duration = 3.0)\n)\n

In this form you can immediately emulate it if you'd like but to run this on hardware you need to discretize it. The waveform on hardware has to either be:

  • Piecewise linear for Rabi amplitude and detuning terms of the Hamiltonian
  • Piecewise constant for the Phase term of the Hamiltonian

Bloqade can automatically perform this conversion with sample(), all you need to do is specify the kind of interpolation and the size of the discretization step in time. Below we set the step duration to be \\(0.05 \\,\\text{us}\\) with \"linear\" interpolation to give us a resulting piecewise linear waveform.

custom_discretized_waveform_applied = (\n    target_rabi_amplitude\n    .fn(custom_waveform, duration = 3.0)\n    .sample(0.05, \"linear\")\n)\n

Note

Programs that have custom functions as waveforms are not fully serializable. This means that when you are saving and reloading results, the original embedded program will be missing that custom waveform. You will still be able to analyze the saved results!

"},{"location":"home/quick_start/#slicing-and-recording-waveforms","title":"Slicing and Recording Waveforms","text":"

When you conduct parameter sweeps with your program, you may want to sweep over your program across time. This will require \"slicing\" your waveforms, where you define the waveform of interest and then, using a variable with .slice, indicate the times at which the waveform duration should be cut short.

In the example below we define a simple piecewise linear waveform but slice it starting from a time duration of \\(0 \\,\\text{us}\\) to values between \\(1\\) to \\(2 \\,\\text{us}\\).

from bloqade import start\nimport numpy as np\n\nsliced_program = (\n    start.add_position((0, 0))\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=[0.5, 2.5, 0.5], values=[0, 3.0, 3.0, 0]\n    ).slice(start=0, stop=\"run_time\")\n)\n\nrun_times = np.linspace(1.0, 2.0, 10)\nvars_assigned_program = sliced_program.batch_assign(run_time=run_times)\n

This program will run fine in emulation but due to hardware constraints certain waveforms (such as those targeting the Rabi Amplitude), the waveform needs to start and end at \\(0 \\,\\text{rad}/\\text{us}\\). Thus, there needs to be a way to slice our waveform but also add an end component to that waveform. .record in Bloqade lets you literally \"record\" the value at the end of a .slice and then use it to construct further parts of the waveform.

In the program below the waveform is still sliced but with the help of .record a linear segment that pulls the waveform down to \\(0.0 \\,\\text{rad}/\\text{us}\\) from whatever its current value at the slice is in \\(0.7 \\,\\text{us}\\) is added.

from bloqade import start\nimport numpy as np\n\nsliced_program = (\n    start.add_position((0, 0))\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=[0.5, 2.5, 0.5], values=[0, 3.0, 3.0, 0]\n    ).slice(start=0, stop=\"run_time\")\n    .record(\"waveform_value\")\n    .linear(\"rabi_value\", 0.0, 0.7)\n)\n\nrun_times = np.linspace(1.0, 2.0, 10)\nvars_assigned_program = sliced_program.batch_assign(run_time=run_times)\n
"},{"location":"home/quick_start/#waveforms-with-no-geometry","title":"Waveforms with No Geometry","text":"

If you have multiple atom geometries you'd like to apply a pulse sequence to or you simply don't want to worry about what atom geometry to start with, you can just build straight off of start:

from bloqade import start\n\npulse_sequence = (\n    start\n    .rydberg.rabi.amplitude.uniform\n    .constant(value=1.0, duration=1.0)\n    .parse_sequence()\n)\n

You can visualize your sequence as well with .show():

pulse_sequence.show()\n

And when you're content with it you just .apply() it on the geometries of your choice:

from bloqade.atom_arrangement import Honeycomb, Kagome \n\ngeometry_1 = Honeycomb(2, lattice_spacing = 6.0)\ngeometry_2 = Kagome(2, lattice_spacing = 6.0)\n\nprogram_1  = geometry_1.apply(pulse_sequence)\nprogram_2  = geometry_2.apply(pulse_sequence)\n
"},{"location":"home/quick_start/#emulation","title":"Emulation","text":"

When you've completed the definition of your program you can use Bloqade's own emulator to get results. The emulation performs the time evolution of the analog Rydberg Hamiltonian. Here we say we want to the program to be run and measurements obtained 1000 times.

results = your_program.bloqade.python().run(1000)\n

Note

If your atoms are particularly close together or the ODE solver gives you the following message:

RuntimeError: DOP853/DOPRI5: Problem is probably stiff (interrupted).\n

In which case you will need to specify the interaction_picture=True argument:

results = your_program.bloqade.python().run(1000, interaction_picture=True)\n
"},{"location":"home/quick_start/#submitting-to-hardware","title":"Submitting to Hardware","text":"

To submit your program to hardware ensure you have your AWS Braket credentials loaded. You will need to use the AWS CLI to do this.

Then it's just a matter of selecting the Aquila on Braket backend. Before going any further Bloqade provides two options for running your program on actul hardware:

  • Using .run is blocking, meaning you will not be able to execute anything else while Bloqade waits for results
  • Using .run_async lets you submit to hardware and continue any further execution, while also letting you query the status of your program in the queue.

In the example below we use .run_async to specify the program should be run and measurements obtained 1000 times.

async_results = your_program.braket.aquila().run_async(1000)\n

We can see the status of our program via:

async_results.fetch()\n
Which gives us the Task ID, a unique identifier for the task as well as the status of the task. In the example below the task is Enqueued meaning it has been successfully created and is awaiting execution on the cloud. When the task is actually running on hardware, the status will change to Running.
                                             task ID    status  shots\n0  arn:aws:braket:us-east-1:XXXXXXXXXXXX:quantum-...  Enqueued    100\n

"},{"location":"home/quick_start/#analyzing-results","title":"Analyzing Results","text":"

When you've retrieved your results from either emulation or hardware you can generate a .report():

report = results.report()\n

For the examples below we analyze the results of a two atom program.

The report contains useful information such as:

  • The raw bitstrings measured per each execution of the program

    report.bitstrings()\n
    [array([[1, 1],\n        [1, 1],\n        [1, 1],\n        ...,\n        [1, 1],\n        [1, 1],\n        [1, 0]], dtype=int8)]\n

  • The number of times each unique bitstring occurred:

    report.counts()\n
    [OrderedDict([('11', 892), ('10', 59), ('01', 49)])]\n

  • The Rydberg Density for each atom

    report.rydberg_densities()\n
                     0      1\ntask_number              \n0            0.053  0.054\n

And can also provide useful visual information such as the state of your atoms and the bitstring distribution via:

report.show()\n

"},{"location":"home/quick_start/#parameter-sweeps","title":"Parameter Sweeps","text":"

You can easily do parameter sweeps in emulation and on Aquila with variables. Bloqade automatically detects strings in your program as variables that you can later assign singular or multiple values to.

In the example below, we define a program with a singular variable that controls the amplitude of the waveform.

from bloqade import start\n\nrabi_oscillations_program = (\n    start.add_position((0, 0))\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=[0.06, 3, 0.06], \n        values=[0, \"rabi_amplitude\", \"rabi_amplitude\", 0]\n    )\n)\n

We can assign a single fixed value to the variable:

single_value_assignment = rabi_oscillations_program.assign(rabi_amplitude=3.5)\n

Or, to perform a sweep, we use .batch_assign:

import numpy as np\nrabi_amplitudes = np.linspace(1.0, 2.0, 20)\n\nmultiple_value_assignment = rabi_oscillations_program.batch_assign(rabi_amplitude=rabi_amplitudes)\n

This will actually create multiple versions of the program internally, with each program assigned a fixed value from the sweep. Bloqade will automatically handle the compilation of results from these multiple programs in order, meaning there is no major departure from what you saw in analyzing the results of your program.

You can also delay assignment of a value to a variable by first declaring it in .args() and then passing a value when you call run:

delayed_assignment_program = rabi_oscillations_program.args([\"rabi_amplitude\"])\nresults = delayed_assignment_program.bloqade.python().run(100, args=(1.0,))\n

You can alternatively treat the program as a callable after using .args() (note the inverted order of arguments in the call!):

delayed_assignment_program = rabi_oscillations_program.args([\"rabi_amplitude\"])\ncallable_program = delayed_assignment_program.bloqade.python()\nresults = callable_program(1.0, shots=100)\n

Variables aren't just restricted to having values assigned to them, you can also symbolically manipulate them!

"},{"location":"home/quick_start/#symbolic-parameters","title":"Symbolic Parameters","text":"

Variables in Bloqade can also be symbolically manipulated, giving you even more flexibility when you construct your program.

In the example below, we externally declare a variable my_var that then has some arithmetic done on it to allow it to have a different value in a later part of the program:

from bloqade import start, var\n\nmy_var = var(\"my_variable\")\nwaveform_durations = [0.6, 1.0, 0.6]\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nrabi_waveform = (\n    target_rabi_amplitude\n    .piecewise_linear(durations=waveform_durations, \n                      values=[0.0, my_var, my_var, 0.0])\n)\ntarget_detuning = rabi_waveform.detuning.uniform\ndetuning_waveform = (\n    target_detuning\n    .piecewise_linear(durations=waveform_durations, \n                      values=[my_var-1.0, my_var*0.5, my_var/2, my_var+1.0 ])\n)\n

You still perform variable assignment just like you normally would:

program = detuning_waveform.assign(my_variable=1.0)\n

You can also use Python's built-in sum if you want the sum of multiple variables as a value in your program. This is quite useful when it comes to needing to indicate a full duration for a waveform that doesn't need to be split up:

from bloqade import start, var\n\nvariable_durations = var([\"a\", \"b\", \"c\"])\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nrabi_waveform = (\n    target_rabi_amplitude\n    .piecewise_linear(durations=variable_durations, \n                      values=[0.0, 1.5, 1.5, 0.0])\n)\ntarget_detuning = rabi_waveform.detuning.uniform\ndetuning_waveform = (\n    target_detuning\n    .constant(duration=sum(variable_durations),\n              value=16.2)\n)\n
We later assign values and Bloqade will automatically handle the summation:

program = detuning_waveform.assign(a=0.5, b=1.2, c=0.5)\n
"},{"location":"home/quick_start/#saving-and-loading-results","title":"Saving and Loading Results","text":"

You can save your results in JSON format using Bloqade's save function:

from bloqade import start, save\n\nyour_program = ...\nemulation_results = your_program.bloqade.python().run(100)\nhardware_results = your_program.braket.aquila.run_async(100)\n\nsave(emulation_results, \"emulation_results.json\") \nsave(hardware_results, \"hardware_results.json\") \n

And later reload them into Python using the load function:

from bloqade import load\nemulation_results = load(\"emulation_results.json\")\nhardware_results = load(\"hardware_results.json\")\n
"},{"location":"reference/hardware-capabilities/","title":"Hardware Capabilities","text":"

During program development, it can be quite handy to know what true hardware capabilities are and incorporate that information programmaticaly. Bloqade offers the ability to do this via get_capabilities().

"},{"location":"reference/hardware-capabilities/#programmatic-access","title":"Programmatic Access","text":"

get_capabilities() (importable directly from bloqade) returns a QuEraCapabilities object. This object contains all the hardware constraints in Decimal format for the Aquila machine, our publically-accessible QPU on AWS Braket.

An example of using get_capabilities() is presented below:

from bloqade import get_capabilities, piecewise_linear\n\n# get capabilities for Aquila\naquila_capabilities = get_capabilities()\n\n# obtain maximum Rabi frequency as Decimal\nmax_rabi = aquila_capabilities.capabilities.rydberg.global_.rabi_frequency_max\n\n# use that value in constructing a neat Rabi waveform\nrabi_wf = piecewise_linear(durations = [0.5, 1.0, 0.5], values = [0, max_rabi, max_rabi, 0])\n

The attribute names for each value have been provided below but will require you to provide the proper prefix like in the example above (e.g. the maximum number of qubits lives under the number_qubits_max attribute which can be navigated to via *your_QuEra_Capabilities_Object*.lattice.number_qubits_max).

"},{"location":"reference/hardware-capabilities/#aquila-capabilities","title":"Aquila Capabilities","text":""},{"location":"reference/hardware-capabilities/#task","title":"Task","text":"
  • Use prefix your_capabilities_object.capabilities.task for:
    • minimum number of shots
    • maximum number of shots
Capability Attribute Value Minimum Number of Shots number_shots_min 1 Maximum Number of Shots number_shots_max 1000"},{"location":"reference/hardware-capabilities/#lattice-geometry","title":"Lattice Geometry","text":"
  • Use prefix your_capabilities_object.capabilities.lattice for:
    • maximum number of qubits
  • Use prefix your_capabilities_object.capabilities.lattice.area for:
    • maximum lattice area width
    • maximum lattice area height
  • Use prefix your_capabilities_object.capabilities.lattice.geometry for:
    • maximum number of sites
    • position resolution
    • minimum radial spacing
    • minimum vertical spacing
Capability Attribute Value Maximum Number of Qubits number_qubits_max 256 Maximum Lattice Area Width width 75.0 \u00b5m Maximum Lattice Area Height height 76.0 \u00b5m Minimum Radial Spacing between Qubits spacing_radial_min 4.0 \u00b5m Minimum Vertical Spacing between Qubits spacing_vertical_min 4.0 \u00b5m Position Resolution position_resolution 0.1 \u00b5m Maximum Number of Sites number_sites_max 256"},{"location":"reference/hardware-capabilities/#global-rydberg-values","title":"Global Rydberg Values","text":"
  • Use prefix your_capabilities_object.capabilities.rydberg for:
    • C6 Coefficient
  • Use prefix your_capabilities_object.capabilities.rydberg.global_ for:
    • Everything else related to global (applied to all atom) capabilities
Capability Attribute Value Rydberg Interaction Constant c6_coefficient 5.42\u00d710\u2076 rad/\u03bcs \u00d7 \u00b5m\u2076 Minimum Rabi Frequency rabi_frequency_min 0.00 rad/\u03bcs Maximum Rabi Frequency rabi_frequency_max 15.8 rad/\u03bcs Rabi Frequency Resolution rabi_frequency_resolution 0.0004 rad/\u03bcs Maximum Rabi Frequency Slew Rate rabi_frequency_slew_rate_max 250.0 rad/\u00b5s\u00b2 Minimum Detuning detuning_min -125.0 rad/\u03bcs Maximum Detuning detuning_max 125.0 rad/\u03bcs Detuning Resolution detuning_resolution 2.0\u00d710\u207b\u2077 rad/\u03bcs Maximum Detuning Slew Rate detuning_slew_rate_max 2500.0 rad/\u00b5s\u00b2 Minimum Phase phase_min -99.0 rad Maximum Phase phase_max 99.0 rad Phase Resolution phase_resolution 5.0\u00d710\u207b\u2077 rad Minimum Time time_min 0.0 \u00b5s Maximum Time time_max 4.0 \u00b5s Time Resolution time_resolution 0.001 \u00b5s Minimum \u0394t time_delta_min 0.05 \u00b5s"},{"location":"reference/hardware-capabilities/#local-detuning-values","title":"Local Detuning Values","text":"
  • Use prefix your_capabilities_object.capabilities.rydberg.local for the following values:
Capability Attribute Value Maximum Detuning detuning_max 125.0 rad/\u03bcs Minimum Detuning detuning_min 0 rad/\u03bcs Maximum Detuning Slew Rate detuning_slew_rate_max 1256.0 rad/\u00b5s\u00b2 Maximum Number of Local Detuning Sites number_local_detuning_sites 200 Maximum Site Coefficient site_coefficient_max 1.0 Minimum Site Coefficient site_ceofficient_min 0.0 Minimum Radial Spacing spacing_radial_min 5 \u00b5m Minimum \u0394t time_delta_min 0.05 \u03bcs Time Resolution time_resolution 0.001 \u00b5s"},{"location":"reference/overview/","title":"Builder Overview","text":"

You may have noticed from the Getting Started and Tutorials that Bloqade uses this interesting, dot-intensive syntax.

from bloqade import start\n\nprog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform.constant(1,1)\n
Exhibit A: Lots of Dots

In fact, it might look remniscent of what you see in some gate-based Quantum Computing SDKs:

# this is strictly pseudocode\ncircuit = init_qubits(n_qubits)\n# note the dots!\ncircuit.x(0).z(1).cnot(0, 1)...\n

What's the deal with that?

"},{"location":"reference/overview/#syntax-motivations","title":"Syntax Motivations","text":"

We call this syntax the builder or builder syntax and as its name implies, it is designed to let you build programs for Analog Hamiltonian Simulation hardware as easily and as straightforward as possible.

The linear structure implies a natural hierarchy in how you think about targeting the various degrees of freedom (detuning, atom positions, Rabi amplitude, etc.) your program will have. In the beginning you have unrestricted access to all these degrees of freedom but in order to do something useful you need to:

  1. Narrow down and explicitly identify what you want to control
  2. Provide the instructions on how you want to control what your focused on

Context is a strong component of the builder syntax, as you are both actively restricted from doing certain things that can introduce ambiguity based on where you are in your program and repeating the same action in different parts of the program yields different results.

"},{"location":"reference/overview/#visual-guides","title":"Visual Guides","text":"

While we hope the Smart Documentation (the ability to instantly see all your next possible steps and their capabilities in your favorite IDE/IPython) is sufficient to get you where you need to go, we undestand it's particularly beneficial to get a high-level overview of things before diving in.

The Standard Representation is a nice flow chart that gives a high-level overview of the different steps and components in the builder syntax.

"},{"location":"reference/standard/","title":"Build Workflow","text":"
\nflowchart TD\n  ProgramStart([\"start\"])\n\n  Geometry(\"Geometry or Lattice\")\n\n  Coupling[\"Coupling\n  -----------\n  rydberg\n  hyperfine\"]\n\n  Detuning[\"detuning\"]\n  Rabi[\"rabi\"]\n\n  Amplitude[\"amplitude\"]\n  Phase[\"phase\"]\n\n  SpaceModulation(\"SpatialModulation\n  ----------------------\n  uniform\n  scale\n  location\n  \")\n  Waveform{\"Waveform\n  ------------\n  piecewise_linear\n  piecewise_constant\n  constant\n  linear\n  poly\n  fn\n  \"}\n\n  Options([\"Options\n  ---------\n  assign\n  batch_assign\n  args\n  parallelize\n  \"])\n\n  Services([\"Services\n  ----------\n  bloqade\n  quera\n  braket\"])\n\n  QuEraBackends([\"Backends\n  ------------\n  mock\n  cloud_mock\n  aquila\n  device\"])\n\n  BraketBackends([\"Backends\n  ------------\n  aquila\n  local_emulator\"])\n\n  BloqadeBackends([\"Backends\n  ------------\n  python\n  julia\"])\n\n  Execution(\"\n  Execution hardware only\n  -------------------------------\n  run_async()\n\n  Hardware and simulation\n  -------------------------------\n  run()\n  __call__\")\n\n  ProgramStart -->|add_position| Geometry;\n  Geometry --> Coupling;\n  ProgramStart --> Coupling;\n\n  Coupling --> Detuning;\n  Coupling --> Rabi;\n\n  Rabi --> Amplitude;\n  Rabi --> Phase;\n\n  Detuning --> SpaceModulation;\n  Amplitude --> SpaceModulation;\n  Phase --> SpaceModulation;\n\n  SpaceModulation --> Waveform;\n\n  Waveform --> Coupling;\n  Waveform --> Services;\n  Waveform --> Options;\n  Options --> Services;\n\n  Services -->|quera| QuEraBackends;\n  Services -->|braket| BraketBackends;\n  Services -->|bloqade| BloqadeBackends;\n  QuEraBackends --> Execution;\n  BraketBackends --> Execution;\n  BloqadeBackends --> Execution;\n\n  click ProgramStart \"../bloqade/#bloqade.start\";\n  click Geometry \"../bloqade/atom_arrangement/\";\n  click Coupling \"../bloqade/builder/drive/\";\n  click Detuning \"../bloqade/builder/field/#bloqade.builder.field.Detuning\";\n  click Rabi \"../bloqade/builder/field/#bloqade.builder.field.Rabi\";\n  click Amplitude \"../bloqade/builder/field/#bloqade.builder.field.Amplitude\";\n  click Phase \"../bloqade/builder/field/#bloqade.builder.field.Phase\";\n  click SpaceModulation \"../bloqade/builder/spatial/\";\n  click Waveform \"../bloqade/builder/waveform/\";\n  click Options \"../bloqade/builder/pragmas/\";\n  click Services \"../bloqade/builder/backend/\";\n  click QuEraBackends \"../bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute\";\n  click BraketBackends \"../bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute\";\n  click BloqadeBackends \"../bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeBackend\";\n  click Execution \"../bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketRoutine\";\n
"},{"location":"reference/bloqade/","title":"Index","text":""},{"location":"reference/bloqade/atom_arrangement/","title":"Atom arrangement","text":""},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/constants/","title":"Constants","text":""},{"location":"reference/bloqade/constants/#bloqade.constants.RB_C6","title":"RB_C6 module-attribute","text":"
RB_C6 = 2 * pi * 862690\n

The C6 constant for the Rydberg Interaction of two Rubidium atoms in units of: rad \u03bcm^6/\u03bcs

"},{"location":"reference/bloqade/factory/","title":"Factory","text":""},{"location":"reference/bloqade/factory/#bloqade.factory.constant","title":"constant","text":"
constant(duration, value)\n

Create a Constant waveform.

Parameters:

Name Type Description Default duration ScalarType

Duration of the Constant waveform.

required value ScalarType

Value of the Constant waveform.s

required

Returns:

Name Type Description Constant Constant

A Constant waveform.

Source code in src/bloqade/factory.py
@beartype\ndef constant(duration: ScalarType, value: ScalarType) -> Constant:\n    \"\"\"Create a Constant waveform.\n\n    Args:\n        duration (ScalarType): Duration of the Constant waveform.\n        value (ScalarType): Value of the Constant waveform.s\n\n    Returns:\n        Constant: A Constant waveform.\n    \"\"\"\n    return Constant(value, duration)\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.get_capabilities","title":"get_capabilities","text":"
get_capabilities()\n

Get the device capabilities for Aquila

Returns:

Name Type Description QuEraCapabilities QuEraCapabilities

capabilities object for Aquila device.

Note

Units of time, distance, and energy are microseconds (us), micrometers (um), and rad / us, respectively.

For a comprehensive list of capabilities, see the Hardware Reference page

Source code in src/bloqade/factory.py
def get_capabilities() -> \"QuEraCapabilities\":\n    \"\"\"Get the device capabilities for Aquila\n\n    Returns:\n        QuEraCapabilities: capabilities object for Aquila device.\n\n\n    Note:\n        Units of time, distance, and energy are microseconds (us),\n        micrometers (um), and rad / us, respectively.\n\n        For a comprehensive list of capabilities,\n        see the [Hardware Reference](../../reference/hardware-capabilities.md)\n        page\n    \"\"\"\n\n    from bloqade.submission.capabilities import get_capabilities\n\n    # manually convert to units\n    return get_capabilities().scale_units(Decimal(\"1e6\"), Decimal(\"1e-6\"))\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.linear","title":"linear","text":"
linear(duration, start, stop)\n

Create a Linear waveform.

Parameters:

Name Type Description Default duration ScalarType

Duration of linear waveform

required start ScalarType

Starting value of linear waveform

required stop ScalarType

Ending value of linear waveform

required

Returns:

Name Type Description Linear Linear

Linear waveform

Source code in src/bloqade/factory.py
@beartype\ndef linear(duration: ScalarType, start: ScalarType, stop: ScalarType) -> Linear:\n    \"\"\"Create a Linear waveform.\n\n    Args:\n        duration (ScalarType): Duration of linear waveform\n        start (ScalarType): Starting value of linear waveform\n        stop (ScalarType): Ending value of linear waveform\n\n    Returns:\n        Linear: Linear waveform\n    \"\"\"\n    return Linear(start, stop, duration)\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.piecewise_constant","title":"piecewise_constant","text":"
piecewise_constant(durations, values)\n

Create a piecewise linear waveform.

Create a piecewise constant waveform from a list of durations and values. The value duration[i] corresponds to the length of time for the i'th segment with a value of values[i].

Parameters:

Name Type Description Default durations List[ScalarType]

The duration of each segment

required values List[ScalarType]

The values for each segment

required

Raises:

Type Description ValueError

If the length of values is not the same as the length of

Returns:

Name Type Description Waveform Waveform

The piecewise linear waveform.

Source code in src/bloqade/factory.py
@beartype\ndef piecewise_constant(\n    durations: List[ScalarType], values: List[ScalarType]\n) -> Waveform:\n    \"\"\"Create a piecewise linear waveform.\n\n    Create a piecewise constant waveform from a list of durations and values. The\n    value `duration[i]` corresponds to the length of time for the i'th segment\n    with a value of `values[i]`.\n\n    Args:\n        durations (List[ScalarType]): The duration of each segment\n        values (List[ScalarType]): The values for each segment\n\n    Raises:\n        ValueError: If the length of `values` is not the same as the length of\n        `durations`.\n\n    Returns:\n        Waveform: The piecewise linear waveform.\n    \"\"\"\n    if len(durations) != len(values):\n        raise ValueError(\n            \"The length of values must be the same as the length of durations\"\n        )\n\n    pwc_wf = None\n    for duration, value in zip(durations, values):\n        if pwc_wf is None:\n            pwc_wf = Constant(value, duration)\n        else:\n            pwc_wf = pwc_wf.append(Constant(value, duration))\n\n    return pwc_wf\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.piecewise_linear","title":"piecewise_linear","text":"
piecewise_linear(durations, values)\n

Create a piecewise linear waveform.

Create a piecewise linear waveform from a list of durations and values. The value duration[i] is of the linear segment between values[i] and values[i+1].

Parameters:

Name Type Description Default durations List[ScalarType]

The duration of each segment

required values List[ScalarType]

The values for each segment

required

Raises:

Type Description ValueError

If the length of values is not one greater than the length of

Returns:

Name Type Description Waveform Waveform

The piecewise linear waveform.

Source code in src/bloqade/factory.py
@beartype\ndef piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> Waveform:\n    \"\"\"Create a piecewise linear waveform.\n\n    Create a piecewise linear waveform from a list of durations and values. The\n    value `duration[i]` is of the linear segment between `values[i]` and `values[i+1]`.\n\n    Args:\n        durations (List[ScalarType]): The duration of each segment\n        values (List[ScalarType]): The values for each segment\n\n    Raises:\n        ValueError: If the length of `values` is not one greater than the length of\n        `durations`.\n\n    Returns:\n        Waveform: The piecewise linear waveform.\n    \"\"\"\n\n    if len(durations) + 1 != len(values):\n        raise ValueError(\n            \"The length of values must be one greater than the length of durations\"\n        )\n\n    pwl_wf = None\n    for duration, start, stop in zip(durations, values[:-1], values[1:]):\n        if pwl_wf is None:\n            pwl_wf = Linear(start, stop, duration)\n        else:\n            pwl_wf = pwl_wf.append(Linear(start, stop, duration))\n\n    return pwl_wf\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.rydberg_h","title":"rydberg_h","text":"
rydberg_h(\n    atoms_positions,\n    detuning=None,\n    amplitude=None,\n    phase=None,\n    static_params={},\n    batch_params=[],\n    args=[],\n)\n

Create a rydberg program with uniform detuning, amplitude, and phase.

Parameters:

Name Type Description Default atoms_positions Any

Description of geometry of atoms in system.

required detuning Optional[Waveform]

Waveform for detuning. Defaults to None.

None amplitude Optional[Waveform]

Waveform describing the amplitude of the rabi term. Defaults to None.

None phase Optional[Waveform]

Waveform describing the phase of rabi term. Defaults to None.

None static_params Dict[str, Any]

Define static parameters of your program. Defaults to {}.

{} batch_params Union[List[Dict[str, Any]], Dict[str, Any]]

Parmaters for a batch of tasks. Defaults to [].

[] args List[str]

List of arguments to leave till runtime. Defaults to [].

[]

Returns:

Name Type Description Routine Routine

An object that can be used to dispatch a rydberg program to multiple backends.

Source code in src/bloqade/factory.py
@beartype\ndef rydberg_h(\n    atoms_positions: Any,\n    detuning: Optional[Waveform] = None,\n    amplitude: Optional[Waveform] = None,\n    phase: Optional[Waveform] = None,\n    static_params: Dict[str, Any] = {},\n    batch_params: Union[List[Dict[str, Any]], Dict[str, Any]] = [],\n    args: List[str] = [],\n) -> Routine:\n    \"\"\"Create a rydberg program with uniform detuning, amplitude, and phase.\n\n    Args:\n        atoms_positions (Any): Description of geometry of atoms in system.\n        detuning (Optional[Waveform], optional): Waveform for detuning.\n            Defaults to None.\n        amplitude (Optional[Waveform], optional): Waveform describing the amplitude of\n            the rabi term. Defaults to None.\n        phase (Optional[Waveform], optional): Waveform describing the phase of rabi\n            term. Defaults to None.\n        static_params (Dict[str, Any], optional): Define static parameters of your\n            program. Defaults to {}.\n        batch_params (Union[List[Dict[str, Any]], Dict[str, Any]], optional):\n            Parmaters for a batch of tasks. Defaults to [].\n        args (List[str], optional): List of arguments to leave till runtime.\n            Defaults to [].\n\n    Returns:\n        Routine: An object that can be used to dispatch a rydberg program to\n            multiple backends.\n    \"\"\"\n    from bloqade import start\n    from bloqade.atom_arrangement import AtomArrangement\n\n    if isinstance(atoms_positions, AtomArrangement):\n        prog = atoms_positions\n    else:\n        prog = start.add_position(atoms_positions)\n\n    if detuning is not None:\n        prog = prog.rydberg.detuning.uniform.apply(detuning)\n\n    if amplitude is not None:\n        prog = prog.amplitude.uniform.apply(amplitude)\n\n    if phase is not None:\n        prog = prog.phase.uniform.apply(phase)\n\n    prog = prog.assign(**static_params)\n\n    if isinstance(batch_params, dict):\n        prog = prog.batch_assign(**batch_params)\n    else:\n        prog = prog.batch_assign(batch_params)\n\n    prog = prog.args(args)\n\n    return prog.parse()\n
"},{"location":"reference/bloqade/serialize/","title":"Serialize","text":""},{"location":"reference/bloqade/serialize/#bloqade.serialize.dumps","title":"dumps","text":"
dumps(o, use_decimal=True, **json_kwargs)\n

Serialize object to string

Parameters:

Name Type Description Default o Any

the object to serialize

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.dumps

{}

Returns:

Name Type Description str str

the serialized object as a string

Source code in src/bloqade/serialize.py
@beartype\ndef dumps(\n    o: Any,\n    use_decimal: bool = True,\n    **json_kwargs,\n) -> str:\n    \"\"\"Serialize object to string\n\n    Args:\n        o (Any): the object to serialize\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.dumps\n\n    Returns:\n        str: the serialized object as a string\n    \"\"\"\n    if not isinstance(o, Serializer.types):\n        raise TypeError(\n            f\"Object of type {type(o)} is not JSON serializable. \"\n            f\"Only {Serializer.types} are supported.\"\n        )\n    return json.dumps(o, cls=Serializer, use_decimal=use_decimal, **json_kwargs)\n
"},{"location":"reference/bloqade/serialize/#bloqade.serialize.load","title":"load","text":"
load(fp, use_decimal=True, **json_kwargs)\n

Load object from file

Parameters:

Name Type Description Default fp Union[TextIO, str]

the file path or file object

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.load

{}

Returns:

Name Type Description Any

the deserialized object

Source code in src/bloqade/serialize.py
@beartype\ndef load(fp: Union[TextIO, str], use_decimal: bool = True, **json_kwargs):\n    \"\"\"Load object from file\n\n    Args:\n        fp (Union[TextIO, str]): the file path or file object\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.load\n\n    Returns:\n        Any: the deserialized object\n    \"\"\"\n    load_bloqade()\n    if isinstance(fp, str):\n        with open(fp, \"r\") as f:\n            return json.load(\n                f,\n                object_hook=Serializer.object_hook,\n                use_decimal=use_decimal,\n                **json_kwargs,\n            )\n    else:\n        return json.load(\n            fp,\n            object_hook=Serializer.object_hook,\n            use_decimal=use_decimal,\n            **json_kwargs,\n        )\n
"},{"location":"reference/bloqade/serialize/#bloqade.serialize.loads","title":"loads","text":"
loads(s, use_decimal=True, **json_kwargs)\n

Load object from string

Parameters:

Name Type Description Default s str

the string to load

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.loads

{}

Returns:

Name Type Description Any

the deserialized object

Source code in src/bloqade/serialize.py
@beartype\ndef loads(s: str, use_decimal: bool = True, **json_kwargs):\n    \"\"\"Load object from string\n\n    Args:\n        s (str): the string to load\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.loads\n\n    Returns:\n        Any: the deserialized object\n    \"\"\"\n    load_bloqade()\n    return json.loads(\n        s, object_hook=Serializer.object_hook, use_decimal=use_decimal, **json_kwargs\n    )\n
"},{"location":"reference/bloqade/serialize/#bloqade.serialize.save","title":"save","text":"
save(o, fp, use_decimal=True, **json_kwargs)\n

Serialize object to file

Parameters:

Name Type Description Default o Any

the object to serialize

required fp Union[TextIO, str]

the file path or file object

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.dump

{}

Returns:

Type Description None

None

Source code in src/bloqade/serialize.py
@beartype\ndef save(\n    o: Any,\n    fp: Union[TextIO, str],\n    use_decimal=True,\n    **json_kwargs,\n) -> None:\n    \"\"\"Serialize object to file\n\n    Args:\n        o (Any): the object to serialize\n        fp (Union[TextIO, str]): the file path or file object\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.dump\n\n    Returns:\n        None\n    \"\"\"\n    if not isinstance(o, Serializer.types):\n        raise TypeError(\n            f\"Object of type {type(o)} is not JSON serializable. \"\n            f\"Only {Serializer.types} are supported.\"\n        )\n    if isinstance(fp, str):\n        with open(fp, \"w\") as f:\n            json.dump(o, f, cls=Serializer, use_decimal=use_decimal, **json_kwargs)\n    else:\n        json.dump(o, fp, cls=Serializer, use_decimal=use_decimal, **json_kwargs)\n
"},{"location":"reference/bloqade/builder/","title":"Index","text":""},{"location":"reference/bloqade/builder/args/","title":"Args","text":""},{"location":"reference/bloqade/builder/assign/","title":"Assign","text":""},{"location":"reference/bloqade/builder/coupling/","title":"Coupling","text":""},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.Hyperfine","title":"Hyperfine","text":"
Hyperfine(parent=None)\n

Bases: LevelCoupling

This node represent level coupling between hyperfine state.

Examples:

- To reach the node from the start node:\n\n>>> node = bloqade.start.hyperfine\n>>> type(node)\n<class 'bloqade.builder.coupling.Hyperfine'>\n\n- Hyperfine level coupling have two reachable field nodes:\n\n    - detuning term (See also [`Detuning`][bloqade.builder.field.Detuning])\n    - rabi term (See also [`Rabi`][bloqade.builder.field.Rabi])\n\n>>> hyp_detune = bloqade.start.hyperfine.detuning\n>>> hyp_rabi = bloqade.start.hyperfine.rabi\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling","title":"LevelCoupling","text":"
LevelCoupling(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling.detuning","title":"detuning property","text":"
detuning\n

Specify the Detuning Field of your program. You will be able to specify the spatial modulation afterwards.

Returns:

Type Description Detuning

Detuning: A program node representing the detuning field.

Background and Context

In the Many-Body Rydberg Hamiltonian:

\\[ \\frac{\\mathcal{H}(t)}{\\hbar} = \\sum_j \\frac{\\Omega_j(t)}{2} \\left( e^{i \\phi_j(t) } | g_j \\rangle \\langle r_j | + e^{-i \\phi_j(t) } | r_j \\rangle \\langle g_j | \\right) - \\sum_j \\Delta_j(t) \\hat{n}_j + \\sum_{j < k} V_{jk} \\hat{n}_j \\hat{n}_k. \\]

The detuning is specified by the term \\(\\Delta_j(t)\\) and specifies how off-resonant the laser being applied to the atoms is from the atomic energy transition, which is driven by the Rabi frequency \\(\\Omega_j(t)\\).

The detuning is described by a field, which is the summation of one or more drives, with the drive being the sum of a waveform and spatial modulation:

\\[ \\sum_j \\Delta_j(t) = \\sum_j \\sum_a C^{a}_{j} f_{a}(t) \\]

Note that the spatial modulation \\(C_{j}\\) scales how much of the detuning waveform is experienced by the atom at site \\(j\\). You can specify the scaling that all atoms feel to be identical (global detuning) or you can specify different scaling for different atoms (local detuning).

Examples

from bloqade import start\n\n# specify geometry, in this case just one atom\ngeometry = start.add_position((0,0))\n# specify your coupling (either `rydberg` or `hyperfine`)\ncoupling = geometry.rydberg\n# Begin specifying your detuning\ncoupling.detuning\n
Alternatively you may start with building your Rabi field and then reach the ability to build your detuning like so:

from bloqade import start\ngeometry = start.add_position((0,0))\ncoupling = geometry.rydberg\nrabi_field = coupling.rabi.amplitude.uniform.constant(duration = 1.0, value = 1.0)\ndetuning = rabi_field.detuning\n
Applications
  • Single Qubit Floquet Dynamics
  • Two Qubit Adiabatic Sweep
  • 1D Z2 State Preparation
  • 2D State Preparation
  • Quantum Scar Dynamics
  • Solving the Maximal Independent Set Problem on defective King Graph
Potential Pitfalls

Bloqade allows you to build a field for the Detuning in the form of:

\\[ \\sum_j \\Delta_j(t) = \\sum_j \\sum_a C^{a}_{j} f_{a}(t) \\]

Where your field can contain multiple drives.

In reality the hardware only supports the following configuration:

\\[ \\Delta_{i}(t) = \\Delta_{1}(t) + c_{i} \\Delta_{2}(t) \\] \\[ c_i \\in [0, 1] \\] \\[ \\Delta_{2}(t) \\leq 0 \\]

Where \\(\\Delta_{1}(t)\\) is your global detuning (establishable via uniform) and \\(\\Delta_{2}(t)\\) is your local detuning waveform with the spatial modulation \\(c_{i}\\) establishable via location or scale.

"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling.detuning--next-possible-steps","title":"Next Possible Steps","text":"

You may continue building your program via:

  • uniform: To address all atoms in the field
  • location(locations, scales): To address atoms at specific locations via indices
  • scale(coeffs): To address all atoms with an individual scale factor
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling.rabi","title":"rabi property","text":"
rabi\n

Specify the complex-valued Rabi field of your program.

The Rabi field is composed of a real-valued Amplitude and Phase field.

  • Next possible steps to build your program are creating the RabiAmplitude field and RabiPhase field of the field:
    • ...rabi.amplitude: To create the Rabi amplitude field
    • ...rabi.phase: To create the Rabi phase field
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.Rydberg","title":"Rydberg","text":"
Rydberg(parent=None)\n

Bases: LevelCoupling

This node represent level coupling of rydberg state.

Examples:

- To reach the node from the start node:\n\n>>> node = bloqade.start.rydberg\n>>> type(node)\n<class 'bloqade.builder.coupling.Rydberg'>\n\n- Rydberg level coupling have two reachable field nodes:\n\n    - detuning term (See also [`Detuning`][bloqade.builder.field.Detuning])\n    - rabi term (See also [`Rabi`][bloqade.builder.field.Rabi])\n\n>>> ryd_detune = bloqade.start.rydberg.detuning\n>>> ryd_rabi = bloqade.start.rydberg.rabi\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/drive/","title":"Drive","text":""},{"location":"reference/bloqade/builder/drive/#bloqade.builder.drive.Drive","title":"Drive","text":""},{"location":"reference/bloqade/builder/drive/#bloqade.builder.drive.Drive.hyperfine","title":"hyperfine property","text":"
hyperfine\n

Address the Hyperfine level coupling in your program.

  • Next possible steps to build your program are specifying the Rabi field or Detuning field.
    • ...hyperfine.rabi: for Rabi field
    • ...hyperfine.detuning: for Detuning field
  • In the absence of a field you the value is set to zero by default.
"},{"location":"reference/bloqade/builder/drive/#bloqade.builder.drive.Drive.rydberg","title":"rydberg property","text":"
rydberg\n

Address the Rydberg level coupling in your program.

  • Next possible steps to build your program are specifying the Rabi field or Detuning field.
    • ...rydberg.rabi: for Rabi field
    • ...rydberg.detuning: for Detuning field
  • In the absence of a field you the value is set to zero by default.
"},{"location":"reference/bloqade/builder/field/","title":"Field","text":""},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Detuning","title":"Detuning","text":"
Detuning(parent=None)\n

Bases: Field

This node represent detuning field of a specified level coupling (rydberg or hyperfine) type.

Examples:

- To specify detuning of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.detuning\n>>> type(node)\n<class 'bloqade.builder.field.Detuning'>\n\n- To specify detuning of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.detuning\n>>> type(node)\n<class 'bloqade.builder.field.Detuning'>\n
Note

This node is a SpatialModulation node. See SpatialModulation for additional options.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field","title":"Field","text":"
Field(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.uniform","title":"uniform property","text":"
uniform\n

Address all atoms as part of defining the spatial modulation component of a drive.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.).

  • You can now do:
    • ...uniform.linear(start, stop, duration) : to apply a linear waveform
    • ...uniform.constant(value, duration) : to apply a constant waveform
    • ...uniform.poly([coefficients], duration) : to apply a polynomial waveform
    • ...uniform.apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...uniform.piecewise_linear([durations], [values]): to apply a piecewise linear waveform
    • ...uniform.piecewise_constant([durations], [values]): to apply a piecewise constant waveform
    • ...uniform.fn(f(t,...)): to apply a function as a waveform
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.location","title":"location","text":"
location(labels, scales=None)\n

Address a single atom (or multiple) atoms.

Address a single atom (or multiple) as part of defining the spatial modulation component of a drive. You can specify the atoms to target as a list of labels and a list of scales. The scales are used to multiply the waveform that is applied to the atom. You can also specify a single label and scale to target a single atom.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field. (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.location--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n# to target a single atom with a waveform\n>>> one_location_prog = prog.location(0)\n# to target a single atom with a scale\n>>> one_location_prog = prog.location(0, 0.5)\n# to target multiple atoms with same waveform\n>>> multi_location_prog = prog.location([0, 2])\n# to target multiple atoms with different scales\n>>> multi_location_prog = prog.location([0, 2], [0.5, \"scale\"])\n
  • You can now do:
    • ...location(labels, scales).linear(start, stop, duration) : to apply a linear waveform
    • ...location(labels, scales).constant(value, duration) : to apply a constant waveform
    • ...location(labels, scales).poly([coefficients], duration) : to apply a polynomial waveform
    • ...location(labels, scales).apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...location(labels, scales).piecewise_linear([durations], [values]): to apply a piecewise linear waveform
    • ...location(labels, scales).piecewise_constant([durations], [values]): to apply a piecewise constant waveform
    • ...location(labels, scales).fn(f(t,..)): to apply a function as a waveform
Source code in src/bloqade/builder/field.py
def location(\n    self,\n    labels: Union[List[int], int],\n    scales: Union[List[ScalarType], ScalarType, None] = None,\n) -> \"Location\":\n    \"\"\"Address a single atom (or multiple) atoms.\n\n    Address a single atom (or multiple) as part of defining the spatial\n    modulation component of a drive. You can specify the atoms to target\n    as a list of labels and a list of scales. The scales are used to\n    multiply the waveform that is applied to the atom. You can also specify\n    a single label and scale to target a single atom.\n\n    Next steps to build your program include choosing the waveform that\n    will be summed with the spatial modulation to create a drive.\n\n    The drive by itself, or the sum of subsequent drives (created by just\n    chaining the construction of drives) will become the field.\n    (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n    # to target a single atom with a waveform\n    >>> one_location_prog = prog.location(0)\n    # to target a single atom with a scale\n    >>> one_location_prog = prog.location(0, 0.5)\n    # to target multiple atoms with same waveform\n    >>> multi_location_prog = prog.location([0, 2])\n    # to target multiple atoms with different scales\n    >>> multi_location_prog = prog.location([0, 2], [0.5, \"scale\"])\n    ```\n\n    - You can now do:\n        - `...location(labels, scales).linear(start, stop, duration)` : to apply\n            a linear waveform\n        - `...location(labels, scales).constant(value, duration)` : to apply\n            a constant waveform\n        - `...location(labels, scales).poly([coefficients], duration)` : to apply\n            a polynomial waveform\n        - `...location(labels, scales).apply(wf:bloqade.ir.Waveform)`: to apply\n            a pre-defined waveform\n        - `...location(labels, scales).piecewise_linear([durations], [values])`:\n            to apply\n            a piecewise linear waveform\n        - `...location(labels, scales).piecewise_constant([durations], [values])`:\n            to apply\n            a piecewise constant waveform\n        - `...location(labels, scales).fn(f(t,..))`: to apply a function as a\n            waveform\n\n    \"\"\"\n    return self._location(labels, scales)\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.scale","title":"scale","text":"
scale(coeffs)\n

Address all the atoms scaling each atom with an element of the list or define a variable name for the scale list to be assigned later by defining a name and using assign or batch_assign later.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.scale--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n\n# assign a literal list of values to scale each atom\n>>> one_location_prog = prog.scale([0.1, 0.2, 0.3])\n# assign a variable name to be assigned later\n>>> one_location_prog = prog.scale(\"a\")\n# \"a\" can be assigned in the END of the program during variable assignment\n# using a list of values, indicating the scaling for each atom\n>>> single_assignment = ...assign(a = [0.1, 0.2, 0.3])\n# a list of lists, indicating a set of atoms should be targeted\n# for each task in a batch.\n>>> batch_assignment = ...batch_assign(a = [list_1, list_2, list_3,...])\n
  • You can now do:
    • ...scale(coeffs).linear(start, stop, duration) : to apply a linear waveform
    • ...scale(coeffs).constant(value, duration) : to apply a constant waveform
    • ...scale(coeffs).poly([coefficients], duration) : to apply a polynomial waveform
    • ...scale(coeffs).apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...scale(coeffs).piecewise_linear(durations, values): to apply a piecewise linear waveform
    • ...scale(coeffs).piecewise_constant(durations, values): to apply a piecewise constant waveform
    • ...scale(coeffs).fn(f(t,..)): to apply a function as a waveform
Source code in src/bloqade/builder/field.py
def scale(self, coeffs: Union[str, List[ScalarType]]) -> \"Scale\":\n    \"\"\"\n    Address all the atoms scaling each atom with an element of the list\n    or define a variable name for the scale list to be assigned later by\n    defining a `name` and using `assign` or `batch_assign` later.\n\n    Next steps to build your program include choosing the waveform that\n    will be summed with the spatial modulation to create a drive.\n\n    The drive by itself, or the sum of subsequent drives (created by just\n    chaining the construction of drives) will become the field\n    (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n\n    # assign a literal list of values to scale each atom\n    >>> one_location_prog = prog.scale([0.1, 0.2, 0.3])\n    # assign a variable name to be assigned later\n    >>> one_location_prog = prog.scale(\"a\")\n    # \"a\" can be assigned in the END of the program during variable assignment\n    # using a list of values, indicating the scaling for each atom\n    >>> single_assignment = ...assign(a = [0.1, 0.2, 0.3])\n    # a list of lists, indicating a set of atoms should be targeted\n    # for each task in a batch.\n    >>> batch_assignment = ...batch_assign(a = [list_1, list_2, list_3,...])\n\n    ```\n\n    - You can now do:\n        - `...scale(coeffs).linear(start, stop, duration)` : to apply\n            a linear waveform\n        - `...scale(coeffs).constant(value, duration)` : to apply\n            a constant waveform\n        - `...scale(coeffs).poly([coefficients], duration)` : to apply\n            a polynomial waveform\n        - `...scale(coeffs).apply(wf:bloqade.ir.Waveform)`: to apply\n            a pre-defined waveform\n        - `...scale(coeffs).piecewise_linear(durations, values)`:  to\n            apply a piecewise linear waveform\n        - `...scale(coeffs).piecewise_constant(durations, values)`: to\n            apply a piecewise constant waveform\n        - `...scale(coeffs).fn(f(t,..))`: to apply a function as a waveform\n\n    \"\"\"\n    from bloqade.builder.spatial import Scale\n\n    return Scale(coeffs, self)\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Rabi","title":"Rabi","text":"
Rabi(parent=None)\n

Bases: Builder

This node represent rabi field of a specified level coupling (rydberg or hyperfine) type.

Examples:

- To specify rabi of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.rabi\n<class 'bloqade.builder.field.Rabi'>\n\n- To specify rabi of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.rabi\n>>> type(node)\n<class 'bloqade.builder.field.Rabi'>\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Rabi.amplitude","title":"amplitude property","text":"
amplitude\n

Specify the real-valued Rabi Amplitude field.

Next steps to build your program focus on specifying a spatial modulation.

The spatial modulation, when coupled with a waveform, completes the specification of a \"Drive\". One or more drives can be summed together automatically to create a field such as the Rabi Amplitude here.

  • You can now
    • ...amplitude.uniform: Address all atoms in the field
    • ...amplitude.location(...): Scale atoms by their indices
    • ...amplitude.scale(...): Scale each atom with a value from a list or assign a variable name to be assigned later
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Rabi.phase","title":"phase property","text":"
phase\n

Specify the real-valued Rabi Phase field.

Next steps to build your program focus on specifying a spatial modulation.

The spatial modulation, when coupled with a waveform, completes the specification of a \"Drive\". One or more drives can be summed together automatically to create a field such as the Rabi Phase here.

  • You can now
    • ...amplitude.uniform: Address all atoms in the field
    • ...amplitude.location(...): Scale atoms by their indices
    • ...amplitude.scale(...): Scale each atom with a value from a list or assign a variable name to be assigned later
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.RabiAmplitude","title":"RabiAmplitude","text":"
RabiAmplitude(parent=None)\n

Bases: Field

This node represent amplitude of a rabi field.

Examples:

- To specify rabi amplitude of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.rabi.amplitude\n>>> type(node)\n<class 'bloqade.builder.field.Amplitude'>\n\n- To specify rabi amplitude of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.rabi.amplitude\n>>> type(node)\n<class 'bloqade.builder.field.Amplitude'>\n
Note

This node is a SpatialModulation node. See SpatialModulation for additional options.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.RabiPhase","title":"RabiPhase","text":"
RabiPhase(parent=None)\n

Bases: Field

This node represent phase of a rabi field.

Examples:

- To specify rabi phase of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.rabi.phase\n>>> type(node)\n<class 'bloqade.builder.field.Phase'>\n\n- To specify rabi phase of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.rabi.phase\n>>> type(node)\n<class 'bloqade.builder.field.Phase'>\n
Note

This node is a SpatialModulation node. See SpatialModulation for additional options.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/parallelize/","title":"Parallelize","text":""},{"location":"reference/bloqade/builder/pragmas/","title":"Pragmas","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Assignable","title":"Assignable","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Assignable.assign","title":"assign","text":"
assign(**assignments)\n

Assign values to variables declared previously in the program.

This is reserved for variables that should only take single values OR for spatial modulations that were created with .scale(str) in which case you can pass in a list. This is the ONLY circumstance in which multiple values are allowed.

"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Assignable.assign--usage-examples","title":"Usage Examples:","text":"
# define geometry\n>>> reg = bloqade.start\n...       .add_position([(0,0),(1,1),(2,2),(3,3)])\n# define variables in program\n>>> seq = reg.rydberg.detuning.uniform\n...       .linear(start=\"ival\",stop=1,duration=\"span_time\")\n# assign values to variables\n>>> seq = seq.assign(span_time = 0.5, ival = 0.0)\n
  • You can now:
    • ...assign(assignments).bloqade: select the bloqade local emulator backend
    • ...assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...assign(assignments).device(specifier_string): select backend by specifying a string
  • Assign multiple values to a single variable for a parameter sweep:
    • ...assign(assignments).batch_assign(assignments):
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...assign(assignments).args([previously_defined_vars])
Source code in src/bloqade/builder/pragmas.py
def assign(self, **assignments) -> \"Assign\":\n    \"\"\"\n    Assign values to variables declared previously in the program.\n\n    This is reserved for variables that should only take single values OR\n    for spatial modulations that were created with `.scale(str)` in which case\n    you can pass in a list. This is the ONLY circumstance in which multiple\n    values are allowed.\n\n    ### Usage Examples:\n    ```\n    # define geometry\n    >>> reg = bloqade.start\n    ...       .add_position([(0,0),(1,1),(2,2),(3,3)])\n    # define variables in program\n    >>> seq = reg.rydberg.detuning.uniform\n    ...       .linear(start=\"ival\",stop=1,duration=\"span_time\")\n    # assign values to variables\n    >>> seq = seq.assign(span_time = 0.5, ival = 0.0)\n    ```\n\n    - You can now:\n        - `...assign(assignments).bloqade`: select the bloqade local\n            emulator backend\n        - `...assign(assignments).braket`: select braket local emulator or\n            QuEra hardware\n        - `...assign(assignments).device(specifier_string)`: select backend\n            by specifying a string\n    - Assign multiple values to a single variable for a parameter sweep:\n        - `...assign(assignments).batch_assign(assignments)`:\n    - Parallelize the program register, duplicating the geometry and waveform\n        sequence to take advantage of all available\n      space/qubits on the QPU:\n        - `...assign(assignments).parallelize(cluster_spacing)`\n    - Defer value assignment of certain variables to runtime:\n        - `...assign(assignments).args([previously_defined_vars])`\n\n    \"\"\"\n    from bloqade.builder.assign import Assign\n\n    return Assign(assignments, parent=self)\n
"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.BatchAssignable","title":"BatchAssignable","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.BatchAssignable.batch_assign","title":"batch_assign","text":"
batch_assign(__batch_params=[], **assignments)\n

Assign multiple values to a single variable to create a parameter sweep.

Bloqade automatically handles the multiple programs this would generate and treats it as object with unified results for easy post-processing.

NOTE: if you assign multiple values to multiple variables in your program, the values must be of the same length. Bloqade will NOT do a Cartesian product (e.g. if \"var1\" is assigned [1,2,3] and \"var2\" is assigned [4,5,6] then the resulting programs will have assignments [1,4], [2,5], [3,6]).

"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.BatchAssignable.batch_assign--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (0, \"atom_distance\")])\n>>> prog = reg.rydberg.rabi.amplitude.uniform.constant(\"value\", 5.0)\n>>> var_assigned_prog = prog.batch_assign(value = [1.0, 2.0, 3.0],\natom_distance = [1.0, 2.0, 3.0])\n
  • Next steps are:
    • ...batch_assign(assignments).bloqade: select the bloqade local emulator backend
    • ...batch_assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...batch_assign(assignments).device(specifier_string): select backend by specifying a string
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...batch_assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...batch_assign(assignments).args([previously_defined_vars])
Source code in src/bloqade/builder/pragmas.py
def batch_assign(\n    self,\n    __batch_params: List[Dict[str, ParamType]] = [],\n    **assignments: List[ParamType],\n) -> Union[\"BatchAssign\", \"ListAssign\"]:\n    \"\"\"\n\n    Assign multiple values to a single variable to create a parameter sweep.\n\n    Bloqade automatically handles the multiple programs this would generate\n    and treats it as object with unified results for easy post-processing.\n\n    NOTE: if you assign multiple values to multiple variables in your program,\n    the values must be of the same length. Bloqade will NOT do a Cartesian product\n    (e.g. if \"var1\" is assigned [1,2,3] and \"var2\" is assigned [4,5,6] then the\n    resulting programs will have assignments [1,4], [2,5], [3,6]).\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (0, \"atom_distance\")])\n    >>> prog = reg.rydberg.rabi.amplitude.uniform.constant(\"value\", 5.0)\n    >>> var_assigned_prog = prog.batch_assign(value = [1.0, 2.0, 3.0],\n    atom_distance = [1.0, 2.0, 3.0])\n    ```\n\n    - Next steps are:\n        - `...batch_assign(assignments).bloqade`: select the bloqade\n            local emulator backend\n        - `...batch_assign(assignments).braket`: select braket local\n        emulator or QuEra hardware\n        - `...batch_assign(assignments).device(specifier_string)`: select\n        backend by specifying a string\n    - Parallelize the program register, duplicating the geometry and waveform\n      sequence to take advantage of all available\n      space/qubits on the QPU:\n        - `...batch_assign(assignments).parallelize(cluster_spacing)`\n    - Defer value assignment of certain variables to runtime:\n        - `...batch_assign(assignments).args([previously_defined_vars])`\n\n    \"\"\"\n    from bloqade.builder.assign import BatchAssign, ListAssign\n\n    if len(__batch_params) > 0 and assignments:\n        raise ValueError(\"batch_params and assignments cannot be used together.\")\n\n    if len(__batch_params) > 0:\n        return ListAssign(__batch_params, parent=self)\n    else:\n        return BatchAssign(assignments, parent=self)\n
"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Parallelizable","title":"Parallelizable","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Parallelizable.parallelize","title":"parallelize","text":"
parallelize(cluster_spacing)\n

Parallelize the current problem (register and sequence) by duplicating the geometry to take advantage of all available space/qubits on hardware.

The singular argument lets you specify how far apart the clusters should be in micrometers.

"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Parallelizable.parallelize--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position((0,0)).rydberg.rabi.uniform.amplitude\n.constant(1.0, 1.0)\n# copy-paste the geometry and waveforms\n>>> parallelized_prog = reg.parallelize(24)\n
  • Your next steps are: ...parallelize(cluster_spacing).bloqade: select the bloqade local emulator backend ...parallelize(cluster_spacing).braket: select braket local emulator or QuEra hardware on the cloud ...parallelize(cluster_spacing).device(specifier_string): select backend by specifying a string
Source code in src/bloqade/builder/pragmas.py
def parallelize(self, cluster_spacing: LiteralType) -> \"Parallelize\":\n    \"\"\"\n\n    Parallelize the current problem (register and sequence) by duplicating\n    the geometry to take advantage of all available space/qubits on hardware.\n\n    The singular argument lets you specify how far apart the clusters\n    should be in micrometers.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position((0,0)).rydberg.rabi.uniform.amplitude\n    .constant(1.0, 1.0)\n    # copy-paste the geometry and waveforms\n    >>> parallelized_prog = reg.parallelize(24)\n    ```\n\n    - Your next steps are:\n         `...parallelize(cluster_spacing).bloqade`: select the bloqade\n            local emulator backend\n         `...parallelize(cluster_spacing).braket`: select braket\n            local emulator or QuEra hardware on the cloud\n         `...parallelize(cluster_spacing).device(specifier_string)`: select\n            backend by specifying a string\n\n    \"\"\"\n    from bloqade.builder.parallelize import Parallelize\n\n    return Parallelize(cluster_spacing, self)\n
"},{"location":"reference/bloqade/builder/route/","title":"Route","text":""},{"location":"reference/bloqade/builder/sequence_builder/","title":"Sequence builder","text":""},{"location":"reference/bloqade/builder/spatial/","title":"Spatial","text":""},{"location":"reference/bloqade/builder/spatial/#bloqade.builder.spatial.Location","title":"Location","text":"
Location(labels, scales, parent=None)\n

Bases: SpatialModulation

Source code in src/bloqade/builder/spatial.py
@beartype\ndef __init__(\n    self,\n    labels: List[int],\n    scales: List[ScalarType],\n    parent: Optional[Builder] = None,\n) -> None:\n    from bloqade.ir.scalar import cast\n    from bloqade.ir.control.field import Location\n\n    super().__init__(parent)\n    self._scaled_locations = {\n        Location(label): cast(scale) for label, scale in zip(labels, scales)\n    }\n
"},{"location":"reference/bloqade/builder/spatial/#bloqade.builder.spatial.Uniform","title":"Uniform","text":"
Uniform(parent=None)\n

Bases: SpatialModulation

The node specify a uniform spacial modulation. Which is ready to apply waveform (See Waveform for available waveform options)

Examples:

- To hit this node from the start node:\n\n>>> reg = bloqade.start.add_position([(0,0),(1,1),(2,2),(3,3)])\n>>> loc = reg.rydberg.detuning.uniform\n\n- Apply Linear waveform:\n\n>>> wv = bloqade.ir.Linear(start=0,stop=1,duration=0.5)\n>>> reg = bloqade.start.add_position([(0,0),(1,1),(2,2),(3,3)])\n>>> loc = reg.rydberg.detuning.uniform.apply(wv)\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/start/","title":"Start","text":""},{"location":"reference/bloqade/builder/start/#bloqade.builder.start.ProgramStart","title":"ProgramStart","text":"
ProgramStart(parent=None)\n

Bases: Drive, Builder

ProgramStart is the base class for a starting/entry node for building a program.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/start/#bloqade.builder.start.ProgramStart.apply","title":"apply","text":"
apply(sequence)\n

Apply a pre-built sequence to a program.

This allows you to build a program independent of any geometry and then apply the program to said geometry. Or, if you have a program you would like to try on multiple geometries you can trivially do so with this.

Example Usage:

>>> from numpy import pi\n>>> seq = start.rydberg.rabi.amplitude.constant(2.0 * pi, 4.5)\n# choose a geometry of interest to apply the program on\n>>> from bloqade.atom_arrangement import Chain, Kagome\n>>> complete_program = Chain(10).apply(seq)\n# you can .apply to as many geometries as you like\n>>> another_complete_program = Kagome(3).apply(seq)\n

  • From here you can now do:
    • ...assign(assignments).bloqade: select the bloqade local emulator backend
    • ...assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...assign(assignments).device(specifier_string): select backend by specifying a string
  • Assign multiple values to a single variable for a parameter sweep:
    • ...assign(assignments).batch_assign(assignments):
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...assign(assignments).args([previously_defined_vars])
Source code in src/bloqade/builder/start.py
@beartype\ndef apply(self, sequence: SequenceExpr) -> SequenceBuilder:\n    \"\"\"\n    Apply a pre-built sequence to a program.\n\n    This allows you to build a program independent of any geometry\n    and then `apply` the program to said geometry. Or, if you have a\n    program you would like to try on multiple geometries you can\n    trivially do so with this.\n\n    Example Usage:\n    ```\n    >>> from numpy import pi\n    >>> seq = start.rydberg.rabi.amplitude.constant(2.0 * pi, 4.5)\n    # choose a geometry of interest to apply the program on\n    >>> from bloqade.atom_arrangement import Chain, Kagome\n    >>> complete_program = Chain(10).apply(seq)\n    # you can .apply to as many geometries as you like\n    >>> another_complete_program = Kagome(3).apply(seq)\n    ```\n\n    - From here you can now do:\n        - `...assign(assignments).bloqade`: select the bloqade\n            local emulator backend\n        - `...assign(assignments).braket`: select braket\n            local emulator or QuEra hardware\n        - `...assign(assignments).device(specifier_string)`: select\n            backend by specifying a string\n    - Assign multiple values to a single variable for a parameter sweep:\n        - `...assign(assignments).batch_assign(assignments)`:\n    - Parallelize the program register, duplicating the geometry and waveform\n        sequence to take advantage of all available\n      space/qubits on the QPU:\n        - `...assign(assignments).parallelize(cluster_spacing)`\n    - Defer value assignment of certain variables to runtime:\n        - `...assign(assignments).args([previously_defined_vars])`\n\n    \"\"\"\n    return SequenceBuilder(sequence, self)\n
"},{"location":"reference/bloqade/builder/typing/","title":"Typing","text":""},{"location":"reference/bloqade/builder/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Recordable","title":"Recordable","text":""},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Recordable.record","title":"record","text":"
record(name)\n

Copy or \"record\" the value at the end of the waveform into a variable so that it can be used in another place.

A common design pattern is to couple this with .slice() considering you may not know exactly what the end value of a .slice() is, especially in parameter sweeps where it becomes cumbersome to handle.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Recordable.record--usage-example","title":"Usage Example:","text":"
# define program of interest\n>>> from bloqade import start\n>>> prog = start.rydberg.rabi.amplitude.uniform\n>>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n# We now slice the piecewise_linear from above and record the\n# value at the end of that slice. We then use that value\n# to construct a new waveform that can be appended to the previous\n# one without introducing discontinuity (refer to the\n# \"Quantum Scar Dynamics\" tutorial for how this could be handy)\n>>> prog_with_record = prog_with_wf.slice(0.0, 1.0).record(\"end_of_wf\")\n>>> record_applied_prog = prog_with_record.linear(start=\"end_of_wf\"\n, stop=0.0, duration=0.3)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...slice(start, stop).linear(start, stop, duration): to append another linear waveform
    • ...slice(start, stop).constant(value, duration): to append a constant waveform
    • ...slice(start, stop).piecewise_linear(): to append a piecewise linear waveform
    • ...slice(start, stop).piecewise_constant(): to append a piecewise constant waveform
    • ...slice(start, stop).poly([coefficients], duration): to append a polynomial waveform
    • ...slice(start, stop).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...slilce(start, stop).fn(f(t,...)): to append a waveform defined by a python function
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...slice(start, stop).uniform: To address all atoms in the field
    • ...slice(start, stop).location(int): To address an atom at a specific location via index
    • ...slice(start, stop).scale(str)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...slice(start, stop).assign(variable_name = value): to assign a single value to a variable
    • ...slice(start, stop) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...slice(start, stop).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...slice(start, stop).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...slice(start, stop).bloqade: to run on the Bloqade local emulator
    • ...slice(start, stop).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...slice(start, stop).parallelize(spacing)
  • Start targeting another level coupling
    • ...slice(start, stop).rydberg: to target the Rydberg level coupling
    • ...slice(start, stop).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...slice(start, stop).amplitude: to target the real-valued Rabi Amplitude field
    • ...slice(start, stop).phase: to target the real-valued Rabi Phase field
    • ...slice(start, stop).detuning: to target the Detuning field
    • ...slice(start, stop).rabi: to target the complex-valued Rabi field ```
Source code in src/bloqade/builder/waveform.py
@beartype\ndef record(self, name: str) -> \"Record\":\n    \"\"\"\n    Copy or \"record\" the value at the end of the waveform into a variable\n    so that it can be used in another place.\n\n    A common design pattern is to couple this with `.slice()` considering\n    you may not know exactly what the end value of a `.slice()` is,\n    especially in parameter sweeps where it becomes cumbersome to handle.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    # define program of interest\n    >>> from bloqade import start\n    >>> prog = start.rydberg.rabi.amplitude.uniform\n    >>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    # We now slice the piecewise_linear from above and record the\n    # value at the end of that slice. We then use that value\n    # to construct a new waveform that can be appended to the previous\n    # one without introducing discontinuity (refer to the\n    # \"Quantum Scar Dynamics\" tutorial for how this could be handy)\n    >>> prog_with_record = prog_with_wf.slice(0.0, 1.0).record(\"end_of_wf\")\n    >>> record_applied_prog = prog_with_record.linear(start=\"end_of_wf\"\n    , stop=0.0, duration=0.3)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...slice(start, stop).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...slice(start, stop).constant(value, duration)`:\n            to append a constant waveform\n        - `...slice(start, stop).piecewise_linear()`:\n            to append a piecewise linear waveform\n        - `...slice(start, stop).piecewise_constant()`:\n            to append a piecewise constant waveform\n        - `...slice(start, stop).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...slice(start, stop).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...slilce(start, stop).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...slice(start, stop).uniform`:\n            To address all atoms in the field\n        - `...slice(start, stop).location(int)`:\n            To address an atom at a specific location via index\n        - `...slice(start, stop).scale(str)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...slice(start, stop).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...slice(start, stop)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...slice(start, stop).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...slice(start, stop).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...slice(start, stop).bloqade`:\n            to run on the Bloqade local emulator\n        - `...slice(start, stop).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...slice(start, stop).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...slice(start, stop).rydberg`:\n            to target the Rydberg level coupling\n        - `...slice(start, stop).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...slice(start, stop).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...slice(start, stop).phase`:\n            to target the real-valued Rabi Phase field\n        - `...slice(start, stop).detuning`:\n            to target the Detuning field\n        - `...slice(start, stop).rabi`:\n            to target the complex-valued Rabi field\n    ```\n    \"\"\"\n    return Record(name, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Sliceable","title":"Sliceable","text":""},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Sliceable.slice","title":"slice","text":"
slice(start=None, stop=None)\n

Indicate that you only want a portion of your waveform to be used in the program.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Sliceable.slice--usage-example","title":"Usage Example:","text":"
# define a program with a waveform of interest\n>>> from bloqade import start\n>>> prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform\n>>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n# instead of using the full waveform we opt to only take the first 1 us\n>>> prog_with_slice = prog_with_wf.slice(0.0, 1.0)\n# you may use variables as well\n>>> prog_with_slice = prog_with_wf.slice(\"start\", \"end\")\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...slice(start, stop).linear(start, stop, duration): to append another linear waveform
    • ...slice(start, stop).constant(value, duration): to append a constant waveform
    • ...slice(start, stop).piecewise_linear(): to append a piecewise linear waveform
    • ...slice(start, stop).piecewise_constant(): to append a piecewise constant waveform
    • ...slice(start, stop).poly([coefficients], duration): to append a polynomial waveform
    • ...slice(start, stop).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...slilce(start, stop).fn(f(t,...)): to append a waveform defined by a python function
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...slice(start, stop).uniform: To address all atoms in the field
    • ...slice(start, stop).location(int): To address an atom at a specific location via index
    • ...slice(start, stop).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...slice(start, stop).assign(variable_name = value): to assign a single value to a variable
    • ...slice(start, stop) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...slice(start, stop).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...slice(start, stop).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...slice(start, stop).bloqade: to run on the Bloqade local emulator
    • ...slice(start, stop).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...slice(start, stop).parallelize(spacing)
  • Start targeting another level coupling
    • ...slice(start, stop).rydberg: to target the Rydberg level coupling
    • ...slice(start, stop).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...slice(start, stop).amplitude: to target the real-valued Rabi Amplitude field
    • ...slice(start, stop).phase: to target the real-valued Rabi Phase field
    • ...slice(start, stop).detuning: to target the Detuning field
    • ...slice(start, stop).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef slice(\n    self,\n    start: Optional[ScalarType] = None,\n    stop: Optional[ScalarType] = None,\n) -> \"Slice\":\n    \"\"\"\n    Indicate that you only want a portion of your waveform to be used in\n    the program.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n\n    ### Usage Example:\n    ```\n    # define a program with a waveform of interest\n    >>> from bloqade import start\n    >>> prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform\n    >>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    # instead of using the full waveform we opt to only take the first 1 us\n    >>> prog_with_slice = prog_with_wf.slice(0.0, 1.0)\n    # you may use variables as well\n    >>> prog_with_slice = prog_with_wf.slice(\"start\", \"end\")\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...slice(start, stop).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...slice(start, stop).constant(value, duration)`:\n            to append a constant waveform\n        - `...slice(start, stop).piecewise_linear()`:\n            to append a piecewise linear waveform\n        - `...slice(start, stop).piecewise_constant()`:\n            to append a piecewise constant waveform\n        - `...slice(start, stop).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...slice(start, stop).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...slilce(start, stop).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...slice(start, stop).uniform`:\n            To address all atoms in the field\n        - `...slice(start, stop).location(int)`:\n            To address an atom at a specific location via index\n        - `...slice(start, stop).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...slice(start, stop).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...slice(start, stop)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...slice(start, stop).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...slice(start, stop).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...slice(start, stop).bloqade`:\n            to run on the Bloqade local emulator\n        - `...slice(start, stop).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...slice(start, stop).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...slice(start, stop).rydberg`:\n            to target the Rydberg level coupling\n        - `...slice(start, stop).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...slice(start, stop).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...slice(start, stop).phase`:\n            to target the real-valued Rabi Phase field\n        - `...slice(start, stop).detuning`:\n            to target the Detuning field\n        - `...slice(start, stop).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return Slice(start, stop, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable","title":"WaveformAttachable","text":"
WaveformAttachable(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.apply","title":"apply","text":"
apply(wf)\n

Apply a Waveform built previously to current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.apply--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# build our waveform independently of the main program\n>>> from bloqade import piecewise_linear\n>>> wf = piecewise_linear(durations=[0.3, 2.5, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n>>> prog.apply(wf)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...apply(waveform).linear(start, stop, duration): to append another linear waveform
    • ...apply(waveform).constant(value, duration): to append a constant waveform
    • ...apply(waveform).piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...apply(waveform).piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...apply(waveform).poly([coefficients], duration): to append a polynomial waveform
    • ...apply(waveform).apply(waveform): to append a pre-defined waveform
    • ...apply(waveform).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...apply(waveform).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...apply(waveform).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...apply(waveform).uniform: To address all atoms in the field
    • ...apply(waveform).location(int): To address an atom at a specific location via index
    • ...apply(waveform).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...apply(waveform).assign(variable_name = value): to assign a single value to a variable
    • ...apply(waveform).batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...apply(waveform).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...apply(waveform).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...apply(waveform).bloqade: to run on the Bloqade local emulator
    • ...apply(waveform).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...apply(waveform).parallelize(spacing)
  • Start targeting another level coupling
    • ...apply(waveform).rydberg: to target the Rydberg level coupling
    • ...apply(waveform).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...apply(waveform).amplitude: to target the real-valued Rabi Amplitude field
    • ...apply(waveform).phase: to target the real-valued Rabi Phase field
    • ...apply(waveform).detuning: to target the Detuning field
    • ...apply(waveform).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef apply(self, wf: ir.Waveform) -> \"Apply\":\n    \"\"\"\n    Apply a [`Waveform`][bloqade.ir.control.Waveform] built previously to\n    current location(s).\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # build our waveform independently of the main program\n    >>> from bloqade import piecewise_linear\n    >>> wf = piecewise_linear(durations=[0.3, 2.5, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    >>> prog.apply(wf)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...apply(waveform).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...apply(waveform).constant(value, duration)`:\n            to append a constant waveform\n        - `...apply(waveform).piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...apply(waveform).piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...apply(waveform).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...apply(waveform).apply(waveform)`:\n            to append a pre-defined waveform\n        - `...apply(waveform).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...apply(waveform).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...apply(waveform).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...apply(waveform).uniform`: To address all atoms in the field\n        - `...apply(waveform).location(int)`:\n            To address an atom at a specific location via index\n        - `...apply(waveform).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying a\n                single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...apply(waveform).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...apply(waveform).batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...apply(waveform).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...apply(waveform).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...apply(waveform).bloqade`:\n            to run on the Bloqade local emulator\n        - `...apply(waveform).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...apply(waveform).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...apply(waveform).rydberg`: to target the Rydberg level coupling\n        - `...apply(waveform).hyperfine`: to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...apply(waveform).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...apply(waveform).phase`:\n            to target the real-valued Rabi Phase field\n        - `...apply(waveform).detuning`:\n            to target the Detuning field\n        - `...apply(waveform).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return Apply(wf, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.constant","title":"constant","text":"
constant(value, duration)\n

Append or assign a constant waveform to the current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.constant--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# apply a constant waveform of 1.9 radians/us for 0.5 us\n>>> prog.constant(value=1.9,duration=0.5)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...constant(value, duration).linear(start, stop, duration): to append another linear waveform
    • ...constant(value, duration).constant(value, duration): to append a constant waveform
    • ...constant(value, duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...constant(value, duration) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...constant(value, duration).poly([coefficients], duration): to append a polynomial waveform
    • ...constant(value, duration).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...constant(value, duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...constant(value, duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...constant(value, duration).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...constant(value, duration).uniform: To address all atoms in the field
    • ...constant(value, duration).scale(...): To address an atom at a specific location via index
    • ...constant(value, duration).location(int)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...constant(value, duration).assign(variable_name = value): to assign a single value to a variable
    • ...constant(value, duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...constant(value, duration).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...constant(value, duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...constant(value, duration).bloqade: to run on the Bloqade local emulator
    • ...constant(value, duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...constant(start, stop, duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...constant(value, duration).rydberg: to target the Rydberg level coupling
    • ...constant(value, duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...constant(value, duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...constant(value, duration).phase: to target the real-valued Rabi Phase field
    • ...constant(value, duration).detuning: to target the Detuning field
    • ...constant(value, duration).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef constant(self, value: ScalarType, duration: ScalarType) -> \"Constant\":\n    \"\"\"\n    Append or assign a constant waveform to the current location(s).\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # apply a constant waveform of 1.9 radians/us for 0.5 us\n    >>> prog.constant(value=1.9,duration=0.5)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...constant(value, duration).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...constant(value, duration).constant(value, duration)`:\n            to append a constant waveform\n        - `...constant(value, duration)\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...constant(value, duration)\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...constant(value, duration).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...constant(value, duration).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...constant(value, duration).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...constant(value, duration).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...constant(value, duration).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...constant(value, duration).uniform`:\n            To address all atoms in the field\n        - `...constant(value, duration).scale(...)`:\n            To address an atom at a specific location via index\n        - `...constant(value, duration).location(int)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...constant(value, duration).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...constant(value, duration)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...constant(value, duration).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...constant(value, duration).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...constant(value, duration).bloqade`:\n            to run on the Bloqade local emulator\n        - `...constant(value, duration).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...constant(start, stop, duration).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...constant(value, duration).rydberg`:\n            to target the Rydberg level coupling\n        - `...constant(value, duration).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current\n      level coupling (previously selected as `rydberg` or `hyperfine`):\n        - `...constant(value, duration).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...constant(value, duration).phase`:\n            to target the real-valued Rabi Phase field\n        - `...constant(value, duration).detuning`:\n            to target the Detuning field\n        - `...constant(value, duration).rabi`:\n            to target the complex-valued Rabi field\n\n    \"\"\"\n    return Constant(value, duration, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.fn","title":"fn","text":"
fn(fn, duration)\n

Append or assign a custom function as a waveform.

The function must have its first argument be that of time but can also have other arguments which are treated as variables. You can assign values to later in the program via .assign or .batch_assign.

The function must also return a singular float value.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.fn--usage-examples","title":"### Usage Examples:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# define our custom waveform. It must have one argument\n# be time followed by any other number of arguments that can\n# be assigned a value later in the program via `.assign` or `.batch_assign`\n>>> def custom_waveform_function(t, arg1, arg2):\n        return arg1*t + arg2\n>>> prog = prog.fn(custom_waveform_function, duration = 0.5)\n# assign values\n>>> assigned_vars_prog = prog.assign(arg1 = 1.0, arg2 = 2.0)\n# or go for batching!\n>>> assigned_vars_batch_prog = prog.assign(arg1 = 1.0, arg2 = [1.0, 2.0, 3.0])\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...fn(f(t,...)) .linear(start, stop, duration): to append another linear waveform
    • ...fn(f(t,...)) .constant(value, duration): to append a constant waveform
    • ...fn(f(t,...)) .piecewise_linear(durations, values): to append a piecewise linear waveform
    • ...fn(f(t,...)) .piecewise_constant(durations, values): to append a piecewise constant waveform
    • ...fn(f(t,...)) .poly([coefficients], duration): to append a polynomial waveform
    • ...fn(f(t,...)) .apply(waveform): to append a pre-defined waveform
    • ...fn(f(t,...)) .fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...fn(f(t,...)).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...fn(f(t,...)).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...fn(f(t,...)).uniform: To address all atoms in the field
    • ...fn(f(t,...)).scale(...): To address an atom at a specific location via index
    • ...fn(f(t,...)).location(int)`
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...fn(f(t,...)) .assign(variable_name = value): to assign a single value to a variable
    • ...fn(f(t,...)) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...fn(f(t,...)) .args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...fn(f(t,...)).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...fn(f(t,...)).bloqade: to run on the Bloqade local emulator
    • ...fn(f(t,...)).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...fn(f(t,...)).parallelize(spacing)
  • Start targeting another level coupling
    • ...fn(f(t,...)).rydberg: to target the Rydberg level coupling
    • ...fn(f(t,...)).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...fn(f(t,...)).amplitude: to target the real-valued Rabi Amplitude field
    • ...fn(f(t,...)).phase: to target the real-valued Rabi Phase field
    • ...fn(f(t,...)).detuning: to target the Detuning field
    • ...fn(f(t,...)).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef fn(self, fn: Callable, duration: ScalarType) -> \"Fn\":\n    \"\"\"\n    Append or assign a custom function as a waveform.\n\n    The function must have its first argument be that of time but\n    can also have other arguments which are treated as variables.\n    You can assign values to later in the program via `.assign` or `.batch_assign`.\n\n    The function must also return a singular float value.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### ### Usage Examples:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # define our custom waveform. It must have one argument\n    # be time followed by any other number of arguments that can\n    # be assigned a value later in the program via `.assign` or `.batch_assign`\n    >>> def custom_waveform_function(t, arg1, arg2):\n            return arg1*t + arg2\n    >>> prog = prog.fn(custom_waveform_function, duration = 0.5)\n    # assign values\n    >>> assigned_vars_prog = prog.assign(arg1 = 1.0, arg2 = 2.0)\n    # or go for batching!\n    >>> assigned_vars_batch_prog = prog.assign(arg1 = 1.0, arg2 = [1.0, 2.0, 3.0])\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...fn(f(t,...))\n            .linear(start, stop, duration)`: to append another linear waveform\n        - `...fn(f(t,...))\n            .constant(value, duration)`: to append a constant waveform\n        - `...fn(f(t,...))\n            .piecewise_linear(durations, values)`:\n            to append a piecewise linear waveform\n        - `...fn(f(t,...))\n            .piecewise_constant(durations, values)`:\n            to append a piecewise constant waveform\n        - `...fn(f(t,...))\n            .poly([coefficients], duration)`: to append a polynomial waveform\n        - `...fn(f(t,...))\n            .apply(waveform)`: to append a pre-defined waveform\n        - `...fn(f(t,...))\n            .fn(f(t,...))`: to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...fn(f(t,...)).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...fn(f(t,...)).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...fn(f(t,...)).uniform`:\n            To address all atoms in the field\n        - `...fn(f(t,...)).scale(...)`:\n            To address an atom at a specific location via index\n        - ...fn(f(t,...)).location(int)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning it a\n                list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...fn(f(t,...))\n            .assign(variable_name = value)`: to assign a single value to a variable\n        - `...fn(f(t,...))\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...fn(f(t,...))\n            .args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...fn(f(t,...)).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...fn(f(t,...)).bloqade`:\n            to run on the Bloqade local emulator\n        - `...fn(f(t,...)).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...fn(f(t,...)).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...fn(f(t,...)).rydberg`:\n            to target the Rydberg level coupling\n        - `...fn(f(t,...)).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...fn(f(t,...)).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...fn(f(t,...)).phase`:\n            to target the real-valued Rabi Phase field\n        - `...fn(f(t,...)).detuning`:\n            to target the Detuning field\n        - `...fn(f(t,...)).rabi`:\n            to target the complex-valued Rabi field\n\n    \"\"\"\n    return Fn(fn, duration, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.linear","title":"linear","text":"
linear(start, stop, duration)\n

Append or assign a linear waveform to the current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.linear--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# apply a linear waveform that goes from 0 to 1 radians/us in 0.5 us\n>>> prog.linear(start=0,stop=1,duration=0.5)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...linear(start, stop, duration).linear(start, stop, duration): to append another linear waveform
    • ...linear(start, stop, duration).constant(value, duration): to append a constant waveform
    • ...linear(start, stop, duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...linear(start, stop, duration) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...linear(start, stop, duration).poly([coefficients], duration): to append a polynomial waveform
    • ...linear(start, stop, duration).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...linear(start, stop, duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...linear(start, stop, duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...linear(start, stop, duration).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...linear(start, stop, duration).uniform: To address all atoms in the field
    • ...linear(start, stop, duration).location(int): To address atoms at specific location with scaling
    • ...linear(start, stop, duration).scale(...)
      • To address atoms at specific location with scaling
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...linear(start, stop, duration).assign(variable_name = value): to assign a single value to a variable
    • ...linear(start, stop, duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...linear(start, stop, duration).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...linear(start, stop, duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...linear(start, stop, duration).bloqade: to run on the Bloqade local emulator
    • ...linear(start, stop, duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...linear(start, stop, duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...linear(start, stop, duration).rydberg: to target the Rydberg level coupling
    • ...linear(start, stop, duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...linear(start, stop, duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...linear(start, stop, duration).phase: to target the real-valued Rabi Phase field
    • ...linear(start, stop, duration).detuning: to target the Detuning field
    • ...linear(start, stop, duration).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef linear(\n    self, start: ScalarType, stop: ScalarType, duration: ScalarType\n) -> \"Linear\":\n    \"\"\"\n\n    Append or assign a linear waveform to the current location(s).\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # apply a linear waveform that goes from 0 to 1 radians/us in 0.5 us\n    >>> prog.linear(start=0,stop=1,duration=0.5)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...linear(start, stop, duration).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...linear(start, stop, duration).constant(value, duration)`:\n            to append a constant waveform\n        - `...linear(start, stop, duration)\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...linear(start, stop, duration)\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...linear(start, stop, duration).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...linear(start, stop, duration).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...linear(start, stop, duration).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...linear(start, stop, duration).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...linear(start, stop, duration).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...linear(start, stop, duration).uniform`:\n            To address all atoms in the field\n        - `...linear(start, stop, duration).location(int)`:\n            To address atoms at specific location with scaling\n        - `...linear(start, stop, duration).scale(...)`\n            - To address atoms at specific location with scaling\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...linear(start, stop, duration).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...linear(start, stop, duration)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...linear(start, stop, duration).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...linear(start, stop, duration).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...linear(start, stop, duration).bloqade`:\n            to run on the Bloqade local emulator\n        - `...linear(start, stop, duration).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...linear(start, stop, duration).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...linear(start, stop, duration).rydberg`:\n            to target the Rydberg level coupling\n        - `...linear(start, stop, duration).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...linear(start, stop, duration).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...linear(start, stop, duration).phase`:\n            to target the real-valued Rabi Phase field\n        - `...linear(start, stop, duration).detuning`:\n            to target the Detuning field\n        - `...linear(start, stop, duration).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n\n    return Linear(start, stop, duration, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_constant","title":"piecewise_constant","text":"
piecewise_constant(durations, values)\n

Append or assign a piecewise constant waveform to current location(s).

The durations argument should have number of elements = len(values). durations should be the duration PER section of the waveform, NON-CUMULATIVE.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_constant--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.rabi.phase.uniform\n# create a staircase, we hold 0.0 rad/us for 1.0 us, then\n# to 1.0 rad/us for 0.5 us before stopping at 0.8 rad/us for 0.9 us.\n>>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3], values=[1.0, 0.5, 0.9])\n
  • Your next steps including:
  • Continue building your waveform via:
    • ...piecewise_constant([durations], [values]) .linear(start, stop, duration): to append another linear waveform
    • ...piecewise_constant([durations], [values]) .constant(value, duration): to append a constant waveform
    • ...piecewise_constant([durations], [values]) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...piecewise_constant([durations], [values]) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...piecewise_constant([durations], [values]) .poly([coefficients], duration): to append a polynomial waveform
    • ...piecewise_constant([durations], [values]) .apply(waveform): to append a pre-defined waveform
    • ...piecewise_constant([durations], [values]).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...piecewise_constant([durations], [values]) .slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...piecewise_constant([durations], [values]) .record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...piecewise_constant([durations], [values]).uniform: To address all atoms in the field
    • ...piecewise_constant([durations], [values]).location(int): To address an atom at a specific location via index
    • ...piecewise_constant([durations], [values]).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...piecewise_constant([durations], [values]) .assign(variable_name = value): to assign a single value to a variable
    • ...piecewise_constant([durations], [values]) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...piecewise_constant([durations], [values]) .args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...piecewise_constant([durations], [values]).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...piecewise_constant([durations], [values]).bloqade: to run on the Bloqade local emulator
    • ...piecewise_constant([durations], [values]).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...piecewise_constat([durations], [values]).parallelize(spacing)
  • Start targeting another level coupling
    • ...piecewise_constant([durations], [values]).rydberg: to target the Rydberg level coupling
    • ...piecewise_constant([durations], [values]).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...piecewise_constant(durations, values).amplitude: to target the real-valued Rabi Amplitude field
    • ...piecewise_constant([durations], [values]).phase: to target the real-valued Rabi Phase field
    • ...piecewise_constant([durations], [values]).detuning: to target the Detuning field
    • ...piecewise_constant([durations], [values]).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef piecewise_constant(\n    self, durations: List[ScalarType], values: List[ScalarType]\n) -> \"PiecewiseConstant\":\n    \"\"\"\n    Append or assign a piecewise constant waveform to current location(s).\n\n    The `durations` argument should have number of elements = len(values).\n    `durations` should be the duration PER section of the waveform,\n    NON-CUMULATIVE.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.rabi.phase.uniform\n    # create a staircase, we hold 0.0 rad/us for 1.0 us, then\n    # to 1.0 rad/us for 0.5 us before stopping at 0.8 rad/us for 0.9 us.\n    >>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3], values=[1.0, 0.5, 0.9])\n    ```\n\n    - Your next steps including:\n    - Continue building your waveform via:\n        - `...piecewise_constant([durations], [values])\n            .linear(start, stop, duration)`: to append another linear waveform\n        - `...piecewise_constant([durations], [values])\n            .constant(value, duration)`: to append a constant waveform\n        - `...piecewise_constant([durations], [values])\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...piecewise_constant([durations], [values])\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...piecewise_constant([durations], [values])\n            .poly([coefficients], duration)`: to append a polynomial waveform\n        - `...piecewise_constant([durations], [values])\n            .apply(waveform)`: to append a pre-defined waveform\n        - `...piecewise_constant([durations], [values]).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...piecewise_constant([durations], [values])\n            .slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...piecewise_constant([durations], [values])\n            .record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...piecewise_constant([durations], [values]).uniform`:\n            To address all atoms in the field\n        - `...piecewise_constant([durations], [values]).location(int)`:\n            To address an atom at a specific location via index\n        - `...piecewise_constant([durations], [values]).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning it a\n                list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...piecewise_constant([durations], [values])\n            .assign(variable_name = value)`: to assign a single value to a variable\n        - `...piecewise_constant([durations], [values])\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...piecewise_constant([durations], [values])\n            .args([\"previously_defined_var\"])`: to defer assignment\n            of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...piecewise_constant([durations], [values]).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...piecewise_constant([durations], [values]).bloqade`:\n            to run on the Bloqade local emulator\n        - `...piecewise_constant([durations], [values]).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...piecewise_constat([durations], [values]).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...piecewise_constant([durations], [values]).rydberg`:\n            to target the Rydberg level coupling\n        - `...piecewise_constant([durations], [values]).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...piecewise_constant(durations, values).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...piecewise_constant([durations], [values]).phase`:\n            to target the real-valued Rabi Phase field\n        - `...piecewise_constant([durations], [values]).detuning`:\n            to target the Detuning field\n        - `...piecewise_constant([durations], [values]).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return PiecewiseConstant(durations, values, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_linear","title":"piecewise_linear","text":"
piecewise_linear(durations, values)\n

Append or assign a piecewise linear waveform to current location(s), where the waveform is formed by connecting values[i], values[i+1] with linear segments.

The durations argument should have # of elements = len(values) - 1. durations should be the duration PER section of the waveform, NON-CUMULATIVE.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_linear--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# ramp our waveform up to a certain value, hold it\n# then ramp down. In this case, we ramp up to 2.0 rad/us in 0.3 us,\n# then hold it for 1.5 us before ramping down in 0.3 us back to 0.0 rad/us.\n>>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...piecewise_linear([durations], [values]) .linear(start, stop, duration): to append another linear waveform
    • ...piecewise_linear([durations], [values]).constant(value, duration): to append a constant waveform
    • ...piecewise_linear([durations], [values]) .piecewise_linear(durations, values): to append a piecewise linear waveform
    • ...piecewise_linear([durations], [values]) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...piecewise_linear([durations], [values]) .poly([coefficients], duration): to append a polynomial waveform
    • ...piecewise_linear([durations], [values]).apply(waveform): to append a pre-defined waveform
    • ...piecewise_linear([durations], [values]).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...piecewise_linear([durations], [values]) .slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...piecewise_linear([durations], [values]) .record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...piecewise_linear([durations], [values]).uniform: To address all atoms in the field
    • ...piecewise_linear([durations], [values]).scale(...): To address an atom at a specific location via index
    • ...piecewise_linear([durations], [values]).location(int)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...piecewise_linear([durations], [values]) .assign(variable_name = value): to assign a single value to a variable
    • ...piecewise_linear([durations], [values]) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...piecewise_linear([durations], [values]) .args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...piecewise_linear([durations], [values]).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...piecewise_linear([durations], [values]).bloqade: to run on the Bloqade local emulator
    • ...piecewise_linear([durations], [values]).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...piecewise_linear([durations], [values]).parallelize(spacing)
  • Start targeting another level coupling
    • ...piecewise_linear([durations], [values]).rydberg: to target the Rydberg level coupling
    • ...piecewise_linear([durations], [values]).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...piecewise_linear([durations], [values]).amplitude: to target the real-valued Rabi Amplitude field
    • ...piecewise_linear([durations], [values]).phase: to target the real-valued Rabi Phase field
    • ...piecewise_linear([durations], [values]).detuning: to target the Detuning field
    • ....rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef piecewise_linear(\n    self, durations: List[ScalarType], values: List[ScalarType]\n) -> \"PiecewiseLinear\":\n    \"\"\"\n    Append or assign a piecewise linear waveform to current location(s),\n    where the waveform is formed by connecting `values[i], values[i+1]`\n    with linear segments.\n\n    The `durations` argument should have # of elements = len(values) - 1.\n    `durations` should be the duration PER section of the waveform, NON-CUMULATIVE.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # ramp our waveform up to a certain value, hold it\n    # then ramp down. In this case, we ramp up to 2.0 rad/us in 0.3 us,\n    # then hold it for 1.5 us before ramping down in 0.3 us back to 0.0 rad/us.\n    >>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...piecewise_linear([durations], [values])\n            .linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...piecewise_linear([durations], [values]).constant(value, duration)`:\n            to append a constant waveform\n        - `...piecewise_linear([durations], [values])\n            .piecewise_linear(durations, values)`:\n            to append a piecewise linear waveform\n        - `...piecewise_linear([durations], [values])\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...piecewise_linear([durations], [values])\n            .poly([coefficients], duration)`: to append a polynomial waveform\n        - `...piecewise_linear([durations], [values]).apply(waveform)`:\n            to append a pre-defined waveform\n        - `...piecewise_linear([durations], [values]).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...piecewise_linear([durations], [values])\n            .slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...piecewise_linear([durations], [values])\n            .record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...piecewise_linear([durations], [values]).uniform`:\n            To address all atoms in the field\n        - `...piecewise_linear([durations], [values]).scale(...)`:\n            To address an atom at a specific location via index\n        - `...piecewise_linear([durations], [values]).location(int)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning it a\n                list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...piecewise_linear([durations], [values])\n            .assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...piecewise_linear([durations], [values])\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...piecewise_linear([durations], [values])\n            .args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...piecewise_linear([durations], [values]).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...piecewise_linear([durations], [values]).bloqade`:\n            to run on the Bloqade local emulator\n        - `...piecewise_linear([durations], [values]).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...piecewise_linear([durations], [values]).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...piecewise_linear([durations], [values]).rydberg`:\n            to target the Rydberg level coupling\n        - `...piecewise_linear([durations], [values]).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...piecewise_linear([durations], [values]).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...piecewise_linear([durations], [values]).phase`:\n            to target the real-valued Rabi Phase field\n        - `...piecewise_linear([durations], [values]).detuning`:\n            to target the Detuning field\n        - `....rabi`: to target the complex-valued Rabi field\n    \"\"\"\n    return PiecewiseLinear(durations, values, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.poly","title":"poly","text":"
poly(coeffs, duration)\n

Append or assign a waveform with a polynomial profile to current location(s).

You pass in a list of coefficients and a duration to this method which obeys the following expression:

wv(t) = coeffs[0] + coeffs[1]*t + coeffs[2]*t^2 + ... + coeffs[n]*t^n

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.poly--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n>>> coeffs = [-1, 0.5, 1.2]\n# resulting polynomial is:\n# f(t) = -1 + 0.5*t + 1.2*t^2 with duration of\n# 0.5 us\n>>> prog.poly(coeffs, duration=0.5)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...poly([coeffs], duration).linear(start, stop, duration): to append another linear waveform
    • ...poly([coeffs], duration).constant(value, duration): to append a constant waveform
    • ...poly([coeffs], duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...poly([coeffs], duration) .piecewise_constant([durations],[values]): to append a piecewise constant waveform
    • ...poly([coeffs], duration).poly([coefficients], duration): to append a polynomial waveform
    • ...poly([coeffs], duration).apply(waveform): to append a pre-defined waveform
    • ...poly([coeffs], duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...poly([coeffs], duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...poly([coeffs], duration).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...poly([coeffs], duration).uniform: To address all atoms in the field
    • ...poly([coeffs], duration).location(int): To address an atom at a specific location via index
    • ...poly([coeffs], duration).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...poly([coeffs], duration).assign(variable_name = value): to assign a single value to a variable
    • ...poly([coeffs], duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...poly([coeffs], duration).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...poly([coeffs], duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...poly([coeffs], duration).bloqade: to run on the Bloqade local emulator
    • ...poly([coeffs], duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...poly([coeffs], duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...poly([coeffs], duration).rydberg: to target the Rydberg level coupling
    • ...poly([coeffs], duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...poly([coeffs], duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...poly([coeffs], duration).phase: to target the real-valued Rabi Phase field
    • ...poly([coeffs], duration).detuning: to target the Detuning field
    • ...poly([coeffs], duration).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef poly(self, coeffs: List[ScalarType], duration: ScalarType) -> \"Poly\":\n    \"\"\"\n    Append or assign a waveform with a polynomial profile to current location(s).\n\n    You pass in a list of coefficients and a duration to this method which obeys\n    the following expression:\n\n    `\n    wv(t) = coeffs[0] + coeffs[1]*t + coeffs[2]*t^2 + ... + coeffs[n]*t^n\n    `\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    >>> coeffs = [-1, 0.5, 1.2]\n    # resulting polynomial is:\n    # f(t) = -1 + 0.5*t + 1.2*t^2 with duration of\n    # 0.5 us\n    >>> prog.poly(coeffs, duration=0.5)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...poly([coeffs], duration).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...poly([coeffs], duration).constant(value, duration)`:\n            to append a constant waveform\n        - `...poly([coeffs], duration)\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...poly([coeffs], duration)\n            .piecewise_constant([durations],[values])`:\n            to append a piecewise constant waveform\n        - `...poly([coeffs], duration).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...poly([coeffs], duration).apply(waveform)`:\n            to append a pre-defined waveform\n        - `...poly([coeffs], duration).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...poly([coeffs], duration).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...poly([coeffs], duration).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...poly([coeffs], duration).uniform`:\n            To address all atoms in the field\n        - `...poly([coeffs], duration).location(int)`:\n            To address an atom at a specific location via index\n        - `...poly([coeffs], duration).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning\n                it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...poly([coeffs], duration).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...poly([coeffs], duration)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...poly([coeffs], duration).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...poly([coeffs], duration).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...poly([coeffs], duration).bloqade`:\n            to run on the Bloqade local emulator\n        - `...poly([coeffs], duration).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...poly([coeffs], duration).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...poly([coeffs], duration).rydberg`:\n            to target the Rydberg level coupling\n        - `...poly([coeffs], duration).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level\n      coupling (previously selected as `rydberg` or `hyperfine`):\n        - `...poly([coeffs], duration).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...poly([coeffs], duration).phase`:\n            to target the real-valued Rabi Phase field\n        - `...poly([coeffs], duration).detuning`:\n            to target the Detuning field\n        - `...poly([coeffs], duration).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return Poly(coeffs, duration, self)\n
"},{"location":"reference/bloqade/builder/backend/","title":"Index","text":""},{"location":"reference/bloqade/builder/backend/#bloqade.builder.backend.BackendRoute","title":"BackendRoute","text":"
BackendRoute(parent=None)\n

Bases: QuEraService, BraketService, BloqadeService

Specify the backend to run your program on via a string (versus more formal builder syntax) of specifying the vendor/product first (Bloqade/Braket) and narrowing it down (e.g: ...device(\"quera.aquila\") versus ...quera.aquila()) - You can pass the following arguments: - \"braket.aquila\" - \"braket.local_emulator\" - \"bloqade.python\" - \"bloqade.julia\"

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/bloqade/","title":"Bloqade","text":""},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeDeviceRoute","title":"BloqadeDeviceRoute","text":"
BloqadeDeviceRoute(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeDeviceRoute.python","title":"python","text":"
python()\n

Specify the Bloqade Python backend.

  • Possible Next Steps:
    • ...python().run(shots): to submit to the python emulator and await results
Source code in src/bloqade/builder/backend/bloqade.py
def python(self):\n    \"\"\"\n    Specify the Bloqade Python backend.\n\n    - Possible Next Steps:\n        - `...python().run(shots)`:\n            to submit to the python emulator and await results\n    \"\"\"\n    return self.parse().bloqade.python()\n
"},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeService","title":"BloqadeService","text":"
BloqadeService(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeService.bloqade","title":"bloqade property","text":"
bloqade\n

Specify the Bloqade backend.

  • Possible Next Steps:
    • ...bloqade.python(): target submission to the Bloqade python backend
    • ...bloqade.julia(): (CURRENTLY NOT IMPLEMENTED!)target submission to the Bloqade.jl backend
"},{"location":"reference/bloqade/builder/backend/braket/","title":"Braket","text":""},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute","title":"BraketDeviceRoute","text":"
BraketDeviceRoute(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute.aquila","title":"aquila","text":"
aquila()\n

Specify QuEra's Aquila QPU on Braket to submit your program to.

The number of shots you specify in the subsequent .run method will either: - dictate the number of times your program is run - dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep

  • Possible next steps are:
    • ...aquila().run(shots): To submit to hardware and WAIT for results (blocking)
    • ...aquila().run_async(shots): To submit to hardware and immediately allow for other operations to occur
Source code in src/bloqade/builder/backend/braket.py
def aquila(self) -> \"BraketHardwareRoutine\":\n    \"\"\"\n    Specify QuEra's Aquila QPU on Braket to submit your program to.\n\n    The number of shots you specify in the subsequent `.run` method will either:\n        - dictate the number of times your program is run\n        - dictate the number of times per parameter your program is run if\n          you have a variable with batch assignments/intend to conduct\n          a parameter sweep\n\n\n    - Possible next steps are:\n        - `...aquila().run(shots)`: To submit to hardware and WAIT for\n            results (blocking)\n        - `...aquila().run_async(shots)`: To submit to hardware and immediately\n            allow for other operations to occur\n    \"\"\"\n    return self.parse().braket.aquila()\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute.device","title":"device","text":"
device(device_arn)\n

Specify QPU based on the device ARN on Braket to submit your program to.

The number of shots you specify in the subsequent .run method will either: - dictate the number of times your program is run - dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep

  • Possible next steps are:
    • ...device(arn).run(shots): To submit to hardware and WAIT for results (blocking)
    • ...device(arn).run_async(shots): To submit to hardware and immediately allow for other operations to occur
Source code in src/bloqade/builder/backend/braket.py
def device(self, device_arn) -> \"BraketHardwareRoutine\":\n    \"\"\"\n    Specify QPU based on the device ARN on Braket to submit your program to.\n\n    The number of shots you specify in the subsequent `.run` method will either:\n        - dictate the number of times your program is run\n        - dictate the number of times per parameter your program is run if\n            you have a variable with batch assignments/intend to conduct\n            a parameter sweep\n\n\n    - Possible next steps are:\n        - `...device(arn).run(shots)`: To submit to hardware and WAIT for\n            results (blocking)\n        - `...device(arn).run_async(shots)`: To submit to hardware and immediately\n            allow for other operations to occur\n    \"\"\"\n    return self.parse().braket.device(device_arn)\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute.local_emulator","title":"local_emulator","text":"
local_emulator()\n

Specify the Braket local emulator to submit your program to.

  • The number of shots you specify in the subsequent .run method will either:
    • dictate the number of times your program is run
    • dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep
  • Possible next steps are:
    • ...local_emulator().run(shots): to submit to the emulator and await results
Source code in src/bloqade/builder/backend/braket.py
def local_emulator(self) -> \"BraketLocalEmulatorRoutine\":\n    \"\"\"\n    Specify the Braket local emulator to submit your program to.\n\n    - The number of shots you specify in the subsequent `.run` method will either:\n        - dictate the number of times your program is run\n        - dictate the number of times per parameter your program is run if\n          you have a variable with batch assignments/intend to\n          conduct a parameter sweep\n    - Possible next steps are:\n        - `...local_emulator().run(shots)`: to submit to the emulator\n            and await results\n\n    \"\"\"\n    return self.parse().braket.local_emulator()\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketService","title":"BraketService","text":"
BraketService(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketService.braket","title":"braket property","text":"
braket\n

Specify the Braket backend. This allows you to access the AWS Braket local emulator OR go submit things to QuEra hardware on AWS Braket service.

  • Possible Next Steps are:
    • ...braket.aquila(): target submission to the QuEra Aquila QPU
    • ...braket.local_emulator(): target submission to the Braket local emulator
"},{"location":"reference/bloqade/builder/backend/quera/","title":"Quera","text":""},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute","title":"QuEraDeviceRoute","text":"
QuEraDeviceRoute(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.aquila","title":"aquila","text":"
aquila()\n

Specify QuEra's Aquila QPU

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def aquila(self):\n    \"\"\"\n    Specify QuEra's Aquila QPU\n\n    Return:\n        QuEraHardwareRoutine\n\n\n    - Possible Next:\n\n        -> `...aquila().submit`\n            :: submit aync remote job\n\n        -> `...aquila().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...aquila().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n    \"\"\"\n    return self.parse().quera.aquila()\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.cloud_mock","title":"cloud_mock","text":"
cloud_mock()\n

Specify QuEra's Remote Mock QPU

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def cloud_mock(self):\n    \"\"\"\n    Specify QuEra's Remote Mock QPU\n\n    Return:\n        QuEraHardwareRoutine\n\n    - Possible Next:\n\n        -> `...aquila().submit`\n            :: submit aync remote job\n\n        -> `...aquila().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...aquila().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n\n    \"\"\"\n    return self.parse().quera.cloud_mock()\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.device","title":"device","text":"
device(config_file=None, **api_config)\n

Specify QuEra's QPU device,

Parameters:

Name Type Description Default config_file str

file that speficy the target hardware

None Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...device().submit :: submit aync remote job

    -> ...device().run :: submit job and wait until job finished and results returned

    -> ...device().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def device(self, config_file: Optional[str] = None, **api_config):\n    \"\"\"\n    Specify QuEra's QPU device,\n\n    Args:\n        config_file (str): file that speficy the target hardware\n\n    Return:\n        QuEraHardwareRoutine\n\n    - Possible Next:\n\n        -> `...device().submit`\n            :: submit aync remote job\n\n        -> `...device().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...device().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n    \"\"\"\n    return self.parse().quera.device(config_file, **api_config)\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.mock","title":"mock","text":"
mock(state_file='.mock_state.txt', submission_error=False)\n

Specify mock, testing locally.

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def mock(self, state_file: str = \".mock_state.txt\", submission_error: bool = False):\n    \"\"\"\n    Specify mock, testing locally.\n\n    Return:\n        QuEraHardwareRoutine\n\n    - Possible Next:\n\n        -> `...aquila().submit`\n            :: submit aync remote job\n\n        -> `...aquila().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...aquila().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n\n    \"\"\"\n    return self.parse().quera.mock(\n        state_file=state_file, submission_error=submission_error\n    )\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraService","title":"QuEraService","text":"
QuEraService(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraService.quera","title":"quera property","text":"
quera\n
  • Specify Quera backend
  • Possible Next:

    -> ...quera.aquila :: Aquila QPU

    -> ...quera.mock :: mock backend, meant for testings

    -> ...quera.device :: QuEra QPU, specifiy by config_file

"},{"location":"reference/bloqade/builder/parse/","title":"Index","text":""},{"location":"reference/bloqade/builder/parse/builder/","title":"Builder","text":""},{"location":"reference/bloqade/builder/parse/stream/","title":"Stream","text":""},{"location":"reference/bloqade/builder/parse/stream/#bloqade.builder.parse.stream.BuilderStream","title":"BuilderStream dataclass","text":"

Represents a stream of builder nodes.

"},{"location":"reference/bloqade/builder/parse/stream/#bloqade.builder.parse.stream.BuilderStream.eat","title":"eat","text":"
eat(types, skips=None)\n

Scan the stream until a node of type in types or skips is found.

Parameters:

Name Type Description Default types List[Type[Builder]]

List of types to move the stream pointer to

required skips List[Type[Builder]] | None

List of types to end the

None

Returns:

Name Type Description BuilderNode BuilderNode

The beginning of the stream which matches a type in types.

Source code in src/bloqade/builder/parse/stream.py
def eat(\n    self, types: List[Type[Builder]], skips: Optional[List[Type[Builder]]] = None\n) -> BuilderNode:\n    \"\"\"Scan the stream until a node of type in `types` or `skips` is found.\n\n    Args:\n        types (List[Type[Builder]]): List of types to move the stream pointer to\n        skips (List[Type[Builder]] | None, optional): List of types to end the\n        stream scan\n\n    Returns:\n        BuilderNode: The beginning of the stream which matches a type in `types`.\n    \"\"\"\n    head = self.read_next(types)\n    curr = head\n    while curr is not None:\n        if type(curr.node) not in types:\n            if skips and type(curr.node) not in skips:\n                break\n        curr = curr.next\n    self.curr = curr\n    return head\n
"},{"location":"reference/bloqade/builder/parse/trait/","title":"Trait","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.Parse","title":"Parse","text":"

Bases: ParseRegister, ParseSequence, ParseCircuit, ParseRoutine

"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.Parse.n_atoms","title":"n_atoms property","text":"
n_atoms\n

Return the number of atoms in the program.

"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseCircuit","title":"ParseCircuit","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseCircuit.parse_circuit","title":"parse_circuit","text":"
parse_circuit()\n

Parse the analog circuit from the program.

Source code in src/bloqade/builder/parse/trait.py
def parse_circuit(self: \"Builder\") -> \"AnalogCircuit\":\n    \"\"\"Parse the analog circuit from the program.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse_circuit(self)\n
"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRegister","title":"ParseRegister","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRegister.parse_register","title":"parse_register","text":"
parse_register()\n

Parse the arrangement of atoms of the program.

Source code in src/bloqade/builder/parse/trait.py
def parse_register(self: \"Builder\") -> Union[\"AtomArrangement\", \"ParallelRegister\"]:\n    \"\"\"Parse the arrangement of atoms of the program.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse_register(self)\n
"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRoutine","title":"ParseRoutine","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRoutine.parse","title":"parse","text":"
parse()\n

Parse the program to return a Routine object.

Source code in src/bloqade/builder/parse/trait.py
def parse(self: \"Builder\") -> \"Routine\":\n    \"\"\"Parse the program to return a Routine object.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse(self)\n
"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseSequence","title":"ParseSequence","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseSequence.parse_sequence","title":"parse_sequence","text":"
parse_sequence()\n

Parse the pulse sequence part of the program.

Source code in src/bloqade/builder/parse/trait.py
def parse_sequence(self: \"Builder\") -> \"Sequence\":\n    \"\"\"Parse the pulse sequence part of the program.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse_sequence(self)\n
"},{"location":"reference/bloqade/compiler/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/common/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/common/assignment_scan/","title":"Assignment scan","text":""},{"location":"reference/bloqade/compiler/analysis/common/check_slices/","title":"Check slices","text":""},{"location":"reference/bloqade/compiler/analysis/common/is_constant/","title":"Is constant","text":""},{"location":"reference/bloqade/compiler/analysis/common/is_hyperfine/","title":"Is hyperfine","text":""},{"location":"reference/bloqade/compiler/analysis/common/scan_channels/","title":"Scan channels","text":""},{"location":"reference/bloqade/compiler/analysis/common/scan_variables/","title":"Scan variables","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/#bloqade.compiler.analysis.hardware.BasicLatticeValidation","title":"BasicLatticeValidation","text":"
BasicLatticeValidation(capabilities)\n

Bases: BloqadeIRVisitor

This visitor checks that the AtomArrangement is within the bounds of the lattice and that the number of sites is within the maximum number of sites.

Source code in src/bloqade/compiler/analysis/hardware/lattice.py
def __init__(self, capabilities: QuEraCapabilities):\n    self.capabilities = capabilities\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/#bloqade.compiler.analysis.hardware.ValidateChannels","title":"ValidateChannels","text":"
ValidateChannels()\n

Bases: BloqadeIRVisitor

Checks to make sure the given sequence can be compiled to hardware.

This check looks at the spatial modulations and the level coupling to determine if the sequence can be compiled to hardware.

Source code in src/bloqade/compiler/analysis/hardware/channels.py
def __init__(self):\n    self.field_name = None\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/channels/","title":"Channels","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/channels/#bloqade.compiler.analysis.hardware.channels.ValidateChannels","title":"ValidateChannels","text":"
ValidateChannels()\n

Bases: BloqadeIRVisitor

Checks to make sure the given sequence can be compiled to hardware.

This check looks at the spatial modulations and the level coupling to determine if the sequence can be compiled to hardware.

Source code in src/bloqade/compiler/analysis/hardware/channels.py
def __init__(self):\n    self.field_name = None\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/lattice/","title":"Lattice","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/lattice/#bloqade.compiler.analysis.hardware.lattice.BasicLatticeValidation","title":"BasicLatticeValidation","text":"
BasicLatticeValidation(capabilities)\n

Bases: BloqadeIRVisitor

This visitor checks that the AtomArrangement is within the bounds of the lattice and that the number of sites is within the maximum number of sites.

Source code in src/bloqade/compiler/analysis/hardware/lattice.py
def __init__(self, capabilities: QuEraCapabilities):\n    self.capabilities = capabilities\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/piecewise_constant/","title":"Piecewise constant","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/piecewise_linear/","title":"Piecewise linear","text":""},{"location":"reference/bloqade/compiler/analysis/python/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/python/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/compiler/passes/","title":"Index","text":""},{"location":"reference/bloqade/compiler/passes/emulator/","title":"Emulator","text":""},{"location":"reference/bloqade/compiler/passes/hardware/","title":"Index","text":""},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.analyze_channels","title":"analyze_channels","text":"
analyze_channels(circuit)\n
  1. Scan channels

This pass checks to make sure that: * There is no hyperfine coupling in the sequence * There are no non-uniform spatial modulation for rabi phase and amplitude * there is no more than one non-uniform spatial modulation for detuning

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to analyze

required

Returns:

Name Type Description level_couplings Dict

Dictionary containing the required channels for the sequence. Note that this will insert a uniform field for any missing channels.

Raises:

Type Description ValueError

If there is hyperfine coupling in the sequence.

ValueError

If there is more than one non-uniform spatial modulation for detuning.

ValueError

If there are non-uniform spatial modulations for rabi phase and amplitude.

Source code in src/bloqade/compiler/passes/hardware/define.py
def analyze_channels(circuit: analog_circuit.AnalogCircuit) -> Dict:\n    \"\"\"1. Scan channels\n\n    This pass checks to make sure that:\n    * There is no hyperfine coupling in the sequence\n    * There are no non-uniform spatial modulation for rabi phase and amplitude\n    * there is no more than one non-uniform spatial modulation for detuning\n\n    Args:\n        circuit: AnalogCircuit to analyze\n\n    Returns:\n        level_couplings: Dictionary containing the required channels for the\n            sequence. Note that this will insert a uniform field for any missing\n            channels.\n\n    Raises:\n        ValueError: If there is hyperfine coupling in the sequence.\n        ValueError: If there is more than one non-uniform spatial modulation for\n            detuning.\n        ValueError: If there are non-uniform spatial modulations for rabi phase\n            and amplitude.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import ValidateChannels\n    from bloqade.compiler.analysis.common import ScanChannels\n\n    ValidateChannels().scan(circuit)\n    level_couplings = ScanChannels().scan(circuit)\n\n    # add missing channels\n    fields = level_couplings[sequence.rydberg]\n    # detuning, phase and amplitude are required\n    # to have at least a uniform field\n    updated_fields = {\n        field_name: fields.get(field_name, {field.Uniform}).union({field.Uniform})\n        for field_name in [pulse.detuning, pulse.rabi.amplitude, pulse.rabi.phase]\n    }\n\n    return {sequence.rydberg: updated_fields}\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.assign_circuit","title":"assign_circuit","text":"
assign_circuit(circuit, assignments)\n
  1. Assign variables and validate assignment

This pass assigns variables to the circuit and validates that all variables have been assigned.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to assign variables to

required assignments Dict[str, ParamType]

Dictionary containing the assignments for the variables in the circuit.

required

Returns:

Name Type Description assigned_circuit Tuple[AnalogCircuit, Dict]

AnalogCircuit with variables assigned.

Raises:

Type Description ValueError

If there are any variables that have not been assigned.

Source code in src/bloqade/compiler/passes/hardware/define.py
def assign_circuit(\n    circuit: analog_circuit.AnalogCircuit, assignments: Dict[str, ParamType]\n) -> Tuple[analog_circuit.AnalogCircuit, Dict]:\n    \"\"\"3. Assign variables and validate assignment\n\n    This pass assigns variables to the circuit and validates that all variables\n    have been assigned.\n\n    Args:\n        circuit: AnalogCircuit to assign variables to\n        assignments: Dictionary containing the assignments for the variables in\n            the circuit.\n\n    Returns:\n        assigned_circuit: AnalogCircuit with variables assigned.\n\n    Raises:\n        ValueError: If there are any variables that have not been assigned.\n\n    \"\"\"\n    from bloqade.compiler.analysis.common import AssignmentScan, ScanVariables\n    from bloqade.compiler.rewrite.common import AssignBloqadeIR\n\n    final_assignments = AssignmentScan(assignments).scan(circuit)\n\n    assigned_circuit = AssignBloqadeIR(final_assignments).visit(circuit)\n\n    assignment_analysis = ScanVariables().scan(assigned_circuit)\n\n    if not assignment_analysis.is_assigned:\n        missing_vars = assignment_analysis.scalar_vars.union(\n            assignment_analysis.vector_vars\n        )\n        raise ValueError(\n            \"Missing assignments for variables:\\n\"\n            + (\"\\n\".join(f\"{var}\" for var in missing_vars))\n            + \"\\n\"\n        )\n\n    return assigned_circuit, final_assignments\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.canonicalize_circuit","title":"canonicalize_circuit","text":"
canonicalize_circuit(circuit, level_couplings)\n
  1. Insert zero waveform in the explicit time intervals missing a waveform

This pass inserts a zero waveform in the explicit time intervals missing a waveform. This is required for later analysis passes to check that the waveforms are compatible with the hardware.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to add padding to

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Return circuit: AnalogCircuit with zero waveforms inserted in the explicit time intervals missing a waveform.

Source code in src/bloqade/compiler/passes/hardware/define.py
def canonicalize_circuit(\n    circuit: analog_circuit.AnalogCircuit, level_couplings: Dict\n) -> analog_circuit.AnalogCircuit:\n    \"\"\"2. Insert zero waveform in the explicit time intervals missing a waveform\n\n    This pass inserts a zero waveform in the explicit time intervals missing a\n    waveform. This is required for later analysis passes to check that the\n    waveforms are compatible with the hardware.\n\n    Args:\n        circuit: AnalogCircuit to add padding to\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Return\n        circuit: AnalogCircuit with zero waveforms inserted in the explicit time\n            intervals missing a waveform.\n\n    \"\"\"\n    from bloqade.compiler.rewrite.common import (\n        AddPadding,\n        AssignToLiteral,\n        Canonicalizer,\n    )\n\n    circuit = AddPadding(level_couplings).visit(circuit)\n    # these two passes are equivalent to a constant propagation pass\n    circuit = AssignToLiteral().visit(circuit)\n    circuit = Canonicalizer().visit(circuit)\n\n    return circuit\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.generate_ahs_code","title":"generate_ahs_code","text":"
generate_ahs_code(capabilities, level_couplings, circuit)\n
  1. generate ahs code

Generates the AHS code for the given circuit. This includes generating the lattice data, global detuning, global amplitude, global phase, local detuning and lattice site coefficients (if applicable).

Parameters:

Name Type Description Default capabilities QuEraCapabilities | None

Capabilities of the hardware.

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required circuit AnalogCircuit

AnalogCircuit to generate AHS code for.

required

Returns:

Name Type Description ahs_components AHSComponents

A collection of the AHS components generated for the given circuit. Can be used to generate the QuEra and Braket IR.

Raises:

Type Description ValueError

If the capabilities are not provided but the circuit has a ParallelRegister. This is because the ParallelRegister requires the capabilities to generate the lattice data.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_ahs_code(\n    capabilities: Optional[QuEraCapabilities],\n    level_couplings: Dict,\n    circuit: analog_circuit.AnalogCircuit,\n) -> AHSComponents:\n    \"\"\"5. generate ahs code\n\n    Generates the AHS code for the given circuit. This includes generating the\n    lattice data, global detuning, global amplitude, global phase, local\n    detuning and lattice site coefficients (if applicable).\n\n    Args:\n        capabilities (QuEraCapabilities | None): Capabilities of the hardware.\n        level_couplings (Dict): Dictionary containing the given channels for the\n            sequence.\n        circuit (AnalogCircuit): AnalogCircuit to generate AHS code for.\n\n    Returns:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit. Can be used to generate the QuEra\n            and Braket IR.\n\n    Raises:\n        ValueError: If the capabilities are not provided but the circuit has\n            a ParallelRegister. This is because the ParallelRegister requires\n            the capabilities to generate the lattice data.\n\n    \"\"\"\n    from bloqade.compiler.codegen.hardware import (\n        GenerateLattice,\n        GenerateLatticeSiteCoefficients,\n        GeneratePiecewiseLinearChannel,\n        GeneratePiecewiseConstantChannel,\n    )\n    from bloqade.compiler.analysis.hardware import BasicLatticeValidation\n\n    if capabilities is not None:\n        # only validate the lattice if capabilities are provided\n        BasicLatticeValidation(capabilities).visit(circuit)\n\n    ahs_lattice_data = GenerateLattice(capabilities).emit(circuit)\n\n    global_detuning = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.detuning, field.Uniform\n    ).visit(circuit)\n\n    global_amplitude = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.rabi.amplitude, field.Uniform\n    ).visit(circuit)\n\n    global_phase = GeneratePiecewiseConstantChannel(\n        sequence.rydberg, pulse.rabi.phase, field.Uniform\n    ).visit(circuit)\n\n    local_detuning = None\n    lattice_site_coefficients = None\n\n    extra_sm = set(level_couplings[sequence.rydberg][pulse.detuning]) - {field.Uniform}\n\n    if extra_sm:\n        if capabilities is not None and capabilities.capabilities.rydberg.local is None:\n            raise ValueError(\n                \"Device does not support local detuning, but the program has a \"\n                \"non-uniform spatial modulation for detuning.\"\n            )\n\n        sm = extra_sm.pop()\n\n        lattice_site_coefficients = GenerateLatticeSiteCoefficients(\n            parallel_decoder=ahs_lattice_data.parallel_decoder\n        ).emit(circuit)\n\n        local_detuning = GeneratePiecewiseLinearChannel(\n            sequence.rydberg, pulse.detuning, sm\n        ).visit(circuit)\n\n    return AHSComponents(\n        lattice_data=ahs_lattice_data,\n        global_detuning=global_detuning,\n        global_amplitude=global_amplitude,\n        global_phase=global_phase,\n        local_detuning=local_detuning,\n        lattice_site_coefficients=lattice_site_coefficients,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.generate_braket_ir","title":"generate_braket_ir","text":"
generate_braket_ir(ahs_components, shots)\n
  1. generate braket ir

This pass takes the AHS components and generates the Braket IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification BraketTaskSpecification

Braket IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_braket_ir(\n    ahs_components: AHSComponents, shots: int\n) -> BraketTaskSpecification:\n    \"\"\"7. generate braket ir\n\n    This pass takes the AHS components and generates the Braket IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (BraketTaskSpecification): Braket IR for the given\n            circuit.\n\n    \"\"\"\n    import braket.ir.ahs as ahs\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    ahs_register = ahs.AtomArrangement(\n        sites=list(map(convert_coordinate_units, ahs_components.lattice_data.sites)),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning_time_series = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning_time_series = ahs.TimeSeries(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n        )\n\n    amplitude_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    phase_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = ahs.PhysicalField(\n        time_series=global_detuning_time_series,\n        pattern=\"uniform\",\n    )\n\n    amplitude = ahs.PhysicalField(\n        time_series=amplitude_time_series,\n        pattern=\"uniform\",\n    )\n\n    phase = ahs.PhysicalField(\n        time_series=phase_time_series,\n        pattern=\"uniform\",\n    )\n\n    local_detuning = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = ahs.PhysicalField(\n            time_series=local_detuning_time_series,\n            pattern=ahs_components.lattice_site_coefficients,\n        )\n\n    driving_field = ahs.DrivingField(\n        detuning=detuning,\n        amplitude=amplitude,\n        phase=phase,\n    )\n\n    shiftingFields = []\n    if ahs_components.lattice_site_coefficients is not None:\n        shiftingFields = [ahs.ShiftingField(magnitude=local_detuning)]\n\n    program = ahs.Program(\n        setup=ahs.Setup(ahs_register=ahs_register),\n        hamiltonian=ahs.Hamiltonian(\n            drivingFields=[driving_field],\n            shiftingFields=shiftingFields,\n        ),\n    )\n\n    return BraketTaskSpecification(nshots=shots, program=program)\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.generate_quera_ir","title":"generate_quera_ir","text":"
generate_quera_ir(ahs_components, shots)\n
  1. generate quera ir

This pass takes the AHS components and generates the QuEra IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification QuEraTaskSpecification

QuEra IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_quera_ir(\n    ahs_components: AHSComponents, shots: int\n) -> QuEraTaskSpecification:\n    \"\"\"7. generate quera ir\n\n    This pass takes the AHS components and generates the QuEra IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (QuEraTaskSpecification): QuEra IR for the given\n            circuit.\n\n    \"\"\"\n    import bloqade.submission.ir.task_specification as task_spec\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    lattice = task_spec.Lattice(\n        sites=list(\n            map(\n                convert_coordinate_units,\n                ahs_components.lattice_data.sites,\n            )\n        ),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning = None\n\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = task_spec.LocalField(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n            lattice_site_coefficients=ahs_components.lattice_site_coefficients,\n        )\n\n    rabi_frequency_amplitude_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    rabi_frequency_phase_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = task_spec.Detuning(\n        global_=global_detuning,\n        local=local_detuning,\n    )\n\n    rabi_frequency_amplitude = task_spec.RabiFrequencyAmplitude(\n        global_=rabi_frequency_amplitude_field,\n    )\n\n    rabi_frequency_phase = task_spec.RabiFrequencyPhase(\n        global_=rabi_frequency_phase_field,\n    )\n\n    rydberg = task_spec.RydbergHamiltonian(\n        rabi_frequency_amplitude=rabi_frequency_amplitude,\n        rabi_frequency_phase=rabi_frequency_phase,\n        detuning=detuning,\n    )\n\n    effective_hamiltonian = task_spec.EffectiveHamiltonian(\n        rydberg=rydberg,\n    )\n\n    return task_spec.QuEraTaskSpecification(\n        nshots=shots,\n        lattice=lattice,\n        effective_hamiltonian=effective_hamiltonian,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.validate_waveforms","title":"validate_waveforms","text":"
validate_waveforms(level_couplings, circuit)\n
  1. validate piecewise linear and piecewise constant pieces of pulses

This pass check to make sure that the waveforms are compatible with the hardware. This includes checking that the waveforms are piecewise linear or piecewise constant. It also checks that the waveforms are compatible with the given channels.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to validate waveforms for

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Raises:

Type Description ValueError

If the waveforms are not piecewise linear or piecewise constant, e.g. the waveform is not continuous.

ValueError

If a waveform segment is not compatible with the given channels.

Source code in src/bloqade/compiler/passes/hardware/define.py
def validate_waveforms(\n    level_couplings: Dict, circuit: analog_circuit.AnalogCircuit\n) -> None:\n    \"\"\"4. validate piecewise linear and piecewise constant pieces of pulses\n\n    This pass check to make sure that the waveforms are compatible with the\n    hardware. This includes checking that the waveforms are piecewise linear or\n    piecewise constant. It also checks that the waveforms are compatible with\n    the given channels.\n\n    Args:\n        circuit: AnalogCircuit to validate waveforms for\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Raises:\n        ValueError: If the waveforms are not piecewise linear or piecewise\n            constant, e.g. the waveform is not continuous.\n        ValueError: If a waveform segment is not compatible with the given\n            channels.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import (\n        ValidatePiecewiseConstantChannel,\n        ValidatePiecewiseLinearChannel,\n    )\n    from bloqade.compiler.analysis.common import CheckSlices\n\n    channel_iter = (\n        (level_coupling, field_name, sm)\n        for level_coupling, fields in level_couplings.items()\n        for field_name, spatial_modulations in fields.items()\n        for sm in spatial_modulations\n    )\n    for channel in channel_iter:\n        if channel[1] in [pulse.detuning, pulse.rabi.amplitude]:\n            ValidatePiecewiseLinearChannel(*channel).visit(circuit)\n        else:\n            ValidatePiecewiseConstantChannel(*channel).visit(circuit)\n\n    CheckSlices().visit(circuit)\n\n    if circuit.sequence.duration() == 0:\n        raise ValueError(\"Circuit Duration must be be non-zero\")\n
"},{"location":"reference/bloqade/compiler/passes/hardware/components/","title":"Components","text":""},{"location":"reference/bloqade/compiler/passes/hardware/define/","title":"Define","text":""},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.analyze_channels","title":"analyze_channels","text":"
analyze_channels(circuit)\n
  1. Scan channels

This pass checks to make sure that: * There is no hyperfine coupling in the sequence * There are no non-uniform spatial modulation for rabi phase and amplitude * there is no more than one non-uniform spatial modulation for detuning

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to analyze

required

Returns:

Name Type Description level_couplings Dict

Dictionary containing the required channels for the sequence. Note that this will insert a uniform field for any missing channels.

Raises:

Type Description ValueError

If there is hyperfine coupling in the sequence.

ValueError

If there is more than one non-uniform spatial modulation for detuning.

ValueError

If there are non-uniform spatial modulations for rabi phase and amplitude.

Source code in src/bloqade/compiler/passes/hardware/define.py
def analyze_channels(circuit: analog_circuit.AnalogCircuit) -> Dict:\n    \"\"\"1. Scan channels\n\n    This pass checks to make sure that:\n    * There is no hyperfine coupling in the sequence\n    * There are no non-uniform spatial modulation for rabi phase and amplitude\n    * there is no more than one non-uniform spatial modulation for detuning\n\n    Args:\n        circuit: AnalogCircuit to analyze\n\n    Returns:\n        level_couplings: Dictionary containing the required channels for the\n            sequence. Note that this will insert a uniform field for any missing\n            channels.\n\n    Raises:\n        ValueError: If there is hyperfine coupling in the sequence.\n        ValueError: If there is more than one non-uniform spatial modulation for\n            detuning.\n        ValueError: If there are non-uniform spatial modulations for rabi phase\n            and amplitude.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import ValidateChannels\n    from bloqade.compiler.analysis.common import ScanChannels\n\n    ValidateChannels().scan(circuit)\n    level_couplings = ScanChannels().scan(circuit)\n\n    # add missing channels\n    fields = level_couplings[sequence.rydberg]\n    # detuning, phase and amplitude are required\n    # to have at least a uniform field\n    updated_fields = {\n        field_name: fields.get(field_name, {field.Uniform}).union({field.Uniform})\n        for field_name in [pulse.detuning, pulse.rabi.amplitude, pulse.rabi.phase]\n    }\n\n    return {sequence.rydberg: updated_fields}\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.assign_circuit","title":"assign_circuit","text":"
assign_circuit(circuit, assignments)\n
  1. Assign variables and validate assignment

This pass assigns variables to the circuit and validates that all variables have been assigned.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to assign variables to

required assignments Dict[str, ParamType]

Dictionary containing the assignments for the variables in the circuit.

required

Returns:

Name Type Description assigned_circuit Tuple[AnalogCircuit, Dict]

AnalogCircuit with variables assigned.

Raises:

Type Description ValueError

If there are any variables that have not been assigned.

Source code in src/bloqade/compiler/passes/hardware/define.py
def assign_circuit(\n    circuit: analog_circuit.AnalogCircuit, assignments: Dict[str, ParamType]\n) -> Tuple[analog_circuit.AnalogCircuit, Dict]:\n    \"\"\"3. Assign variables and validate assignment\n\n    This pass assigns variables to the circuit and validates that all variables\n    have been assigned.\n\n    Args:\n        circuit: AnalogCircuit to assign variables to\n        assignments: Dictionary containing the assignments for the variables in\n            the circuit.\n\n    Returns:\n        assigned_circuit: AnalogCircuit with variables assigned.\n\n    Raises:\n        ValueError: If there are any variables that have not been assigned.\n\n    \"\"\"\n    from bloqade.compiler.analysis.common import AssignmentScan, ScanVariables\n    from bloqade.compiler.rewrite.common import AssignBloqadeIR\n\n    final_assignments = AssignmentScan(assignments).scan(circuit)\n\n    assigned_circuit = AssignBloqadeIR(final_assignments).visit(circuit)\n\n    assignment_analysis = ScanVariables().scan(assigned_circuit)\n\n    if not assignment_analysis.is_assigned:\n        missing_vars = assignment_analysis.scalar_vars.union(\n            assignment_analysis.vector_vars\n        )\n        raise ValueError(\n            \"Missing assignments for variables:\\n\"\n            + (\"\\n\".join(f\"{var}\" for var in missing_vars))\n            + \"\\n\"\n        )\n\n    return assigned_circuit, final_assignments\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.canonicalize_circuit","title":"canonicalize_circuit","text":"
canonicalize_circuit(circuit, level_couplings)\n
  1. Insert zero waveform in the explicit time intervals missing a waveform

This pass inserts a zero waveform in the explicit time intervals missing a waveform. This is required for later analysis passes to check that the waveforms are compatible with the hardware.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to add padding to

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Return circuit: AnalogCircuit with zero waveforms inserted in the explicit time intervals missing a waveform.

Source code in src/bloqade/compiler/passes/hardware/define.py
def canonicalize_circuit(\n    circuit: analog_circuit.AnalogCircuit, level_couplings: Dict\n) -> analog_circuit.AnalogCircuit:\n    \"\"\"2. Insert zero waveform in the explicit time intervals missing a waveform\n\n    This pass inserts a zero waveform in the explicit time intervals missing a\n    waveform. This is required for later analysis passes to check that the\n    waveforms are compatible with the hardware.\n\n    Args:\n        circuit: AnalogCircuit to add padding to\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Return\n        circuit: AnalogCircuit with zero waveforms inserted in the explicit time\n            intervals missing a waveform.\n\n    \"\"\"\n    from bloqade.compiler.rewrite.common import (\n        AddPadding,\n        AssignToLiteral,\n        Canonicalizer,\n    )\n\n    circuit = AddPadding(level_couplings).visit(circuit)\n    # these two passes are equivalent to a constant propagation pass\n    circuit = AssignToLiteral().visit(circuit)\n    circuit = Canonicalizer().visit(circuit)\n\n    return circuit\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.generate_ahs_code","title":"generate_ahs_code","text":"
generate_ahs_code(capabilities, level_couplings, circuit)\n
  1. generate ahs code

Generates the AHS code for the given circuit. This includes generating the lattice data, global detuning, global amplitude, global phase, local detuning and lattice site coefficients (if applicable).

Parameters:

Name Type Description Default capabilities QuEraCapabilities | None

Capabilities of the hardware.

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required circuit AnalogCircuit

AnalogCircuit to generate AHS code for.

required

Returns:

Name Type Description ahs_components AHSComponents

A collection of the AHS components generated for the given circuit. Can be used to generate the QuEra and Braket IR.

Raises:

Type Description ValueError

If the capabilities are not provided but the circuit has a ParallelRegister. This is because the ParallelRegister requires the capabilities to generate the lattice data.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_ahs_code(\n    capabilities: Optional[QuEraCapabilities],\n    level_couplings: Dict,\n    circuit: analog_circuit.AnalogCircuit,\n) -> AHSComponents:\n    \"\"\"5. generate ahs code\n\n    Generates the AHS code for the given circuit. This includes generating the\n    lattice data, global detuning, global amplitude, global phase, local\n    detuning and lattice site coefficients (if applicable).\n\n    Args:\n        capabilities (QuEraCapabilities | None): Capabilities of the hardware.\n        level_couplings (Dict): Dictionary containing the given channels for the\n            sequence.\n        circuit (AnalogCircuit): AnalogCircuit to generate AHS code for.\n\n    Returns:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit. Can be used to generate the QuEra\n            and Braket IR.\n\n    Raises:\n        ValueError: If the capabilities are not provided but the circuit has\n            a ParallelRegister. This is because the ParallelRegister requires\n            the capabilities to generate the lattice data.\n\n    \"\"\"\n    from bloqade.compiler.codegen.hardware import (\n        GenerateLattice,\n        GenerateLatticeSiteCoefficients,\n        GeneratePiecewiseLinearChannel,\n        GeneratePiecewiseConstantChannel,\n    )\n    from bloqade.compiler.analysis.hardware import BasicLatticeValidation\n\n    if capabilities is not None:\n        # only validate the lattice if capabilities are provided\n        BasicLatticeValidation(capabilities).visit(circuit)\n\n    ahs_lattice_data = GenerateLattice(capabilities).emit(circuit)\n\n    global_detuning = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.detuning, field.Uniform\n    ).visit(circuit)\n\n    global_amplitude = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.rabi.amplitude, field.Uniform\n    ).visit(circuit)\n\n    global_phase = GeneratePiecewiseConstantChannel(\n        sequence.rydberg, pulse.rabi.phase, field.Uniform\n    ).visit(circuit)\n\n    local_detuning = None\n    lattice_site_coefficients = None\n\n    extra_sm = set(level_couplings[sequence.rydberg][pulse.detuning]) - {field.Uniform}\n\n    if extra_sm:\n        if capabilities is not None and capabilities.capabilities.rydberg.local is None:\n            raise ValueError(\n                \"Device does not support local detuning, but the program has a \"\n                \"non-uniform spatial modulation for detuning.\"\n            )\n\n        sm = extra_sm.pop()\n\n        lattice_site_coefficients = GenerateLatticeSiteCoefficients(\n            parallel_decoder=ahs_lattice_data.parallel_decoder\n        ).emit(circuit)\n\n        local_detuning = GeneratePiecewiseLinearChannel(\n            sequence.rydberg, pulse.detuning, sm\n        ).visit(circuit)\n\n    return AHSComponents(\n        lattice_data=ahs_lattice_data,\n        global_detuning=global_detuning,\n        global_amplitude=global_amplitude,\n        global_phase=global_phase,\n        local_detuning=local_detuning,\n        lattice_site_coefficients=lattice_site_coefficients,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.generate_braket_ir","title":"generate_braket_ir","text":"
generate_braket_ir(ahs_components, shots)\n
  1. generate braket ir

This pass takes the AHS components and generates the Braket IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification BraketTaskSpecification

Braket IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_braket_ir(\n    ahs_components: AHSComponents, shots: int\n) -> BraketTaskSpecification:\n    \"\"\"7. generate braket ir\n\n    This pass takes the AHS components and generates the Braket IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (BraketTaskSpecification): Braket IR for the given\n            circuit.\n\n    \"\"\"\n    import braket.ir.ahs as ahs\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    ahs_register = ahs.AtomArrangement(\n        sites=list(map(convert_coordinate_units, ahs_components.lattice_data.sites)),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning_time_series = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning_time_series = ahs.TimeSeries(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n        )\n\n    amplitude_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    phase_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = ahs.PhysicalField(\n        time_series=global_detuning_time_series,\n        pattern=\"uniform\",\n    )\n\n    amplitude = ahs.PhysicalField(\n        time_series=amplitude_time_series,\n        pattern=\"uniform\",\n    )\n\n    phase = ahs.PhysicalField(\n        time_series=phase_time_series,\n        pattern=\"uniform\",\n    )\n\n    local_detuning = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = ahs.PhysicalField(\n            time_series=local_detuning_time_series,\n            pattern=ahs_components.lattice_site_coefficients,\n        )\n\n    driving_field = ahs.DrivingField(\n        detuning=detuning,\n        amplitude=amplitude,\n        phase=phase,\n    )\n\n    shiftingFields = []\n    if ahs_components.lattice_site_coefficients is not None:\n        shiftingFields = [ahs.ShiftingField(magnitude=local_detuning)]\n\n    program = ahs.Program(\n        setup=ahs.Setup(ahs_register=ahs_register),\n        hamiltonian=ahs.Hamiltonian(\n            drivingFields=[driving_field],\n            shiftingFields=shiftingFields,\n        ),\n    )\n\n    return BraketTaskSpecification(nshots=shots, program=program)\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.generate_quera_ir","title":"generate_quera_ir","text":"
generate_quera_ir(ahs_components, shots)\n
  1. generate quera ir

This pass takes the AHS components and generates the QuEra IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification QuEraTaskSpecification

QuEra IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_quera_ir(\n    ahs_components: AHSComponents, shots: int\n) -> QuEraTaskSpecification:\n    \"\"\"7. generate quera ir\n\n    This pass takes the AHS components and generates the QuEra IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (QuEraTaskSpecification): QuEra IR for the given\n            circuit.\n\n    \"\"\"\n    import bloqade.submission.ir.task_specification as task_spec\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    lattice = task_spec.Lattice(\n        sites=list(\n            map(\n                convert_coordinate_units,\n                ahs_components.lattice_data.sites,\n            )\n        ),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning = None\n\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = task_spec.LocalField(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n            lattice_site_coefficients=ahs_components.lattice_site_coefficients,\n        )\n\n    rabi_frequency_amplitude_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    rabi_frequency_phase_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = task_spec.Detuning(\n        global_=global_detuning,\n        local=local_detuning,\n    )\n\n    rabi_frequency_amplitude = task_spec.RabiFrequencyAmplitude(\n        global_=rabi_frequency_amplitude_field,\n    )\n\n    rabi_frequency_phase = task_spec.RabiFrequencyPhase(\n        global_=rabi_frequency_phase_field,\n    )\n\n    rydberg = task_spec.RydbergHamiltonian(\n        rabi_frequency_amplitude=rabi_frequency_amplitude,\n        rabi_frequency_phase=rabi_frequency_phase,\n        detuning=detuning,\n    )\n\n    effective_hamiltonian = task_spec.EffectiveHamiltonian(\n        rydberg=rydberg,\n    )\n\n    return task_spec.QuEraTaskSpecification(\n        nshots=shots,\n        lattice=lattice,\n        effective_hamiltonian=effective_hamiltonian,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.validate_waveforms","title":"validate_waveforms","text":"
validate_waveforms(level_couplings, circuit)\n
  1. validate piecewise linear and piecewise constant pieces of pulses

This pass check to make sure that the waveforms are compatible with the hardware. This includes checking that the waveforms are piecewise linear or piecewise constant. It also checks that the waveforms are compatible with the given channels.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to validate waveforms for

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Raises:

Type Description ValueError

If the waveforms are not piecewise linear or piecewise constant, e.g. the waveform is not continuous.

ValueError

If a waveform segment is not compatible with the given channels.

Source code in src/bloqade/compiler/passes/hardware/define.py
def validate_waveforms(\n    level_couplings: Dict, circuit: analog_circuit.AnalogCircuit\n) -> None:\n    \"\"\"4. validate piecewise linear and piecewise constant pieces of pulses\n\n    This pass check to make sure that the waveforms are compatible with the\n    hardware. This includes checking that the waveforms are piecewise linear or\n    piecewise constant. It also checks that the waveforms are compatible with\n    the given channels.\n\n    Args:\n        circuit: AnalogCircuit to validate waveforms for\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Raises:\n        ValueError: If the waveforms are not piecewise linear or piecewise\n            constant, e.g. the waveform is not continuous.\n        ValueError: If a waveform segment is not compatible with the given\n            channels.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import (\n        ValidatePiecewiseConstantChannel,\n        ValidatePiecewiseLinearChannel,\n    )\n    from bloqade.compiler.analysis.common import CheckSlices\n\n    channel_iter = (\n        (level_coupling, field_name, sm)\n        for level_coupling, fields in level_couplings.items()\n        for field_name, spatial_modulations in fields.items()\n        for sm in spatial_modulations\n    )\n    for channel in channel_iter:\n        if channel[1] in [pulse.detuning, pulse.rabi.amplitude]:\n            ValidatePiecewiseLinearChannel(*channel).visit(circuit)\n        else:\n            ValidatePiecewiseConstantChannel(*channel).visit(circuit)\n\n    CheckSlices().visit(circuit)\n\n    if circuit.sequence.duration() == 0:\n        raise ValueError(\"Circuit Duration must be be non-zero\")\n
"},{"location":"reference/bloqade/compiler/passes/hardware/units/","title":"Units","text":""},{"location":"reference/bloqade/compiler/rewrite/","title":"Index","text":""},{"location":"reference/bloqade/compiler/rewrite/common/","title":"Index","text":""},{"location":"reference/bloqade/compiler/rewrite/common/#bloqade.compiler.rewrite.common.AssignToLiteral","title":"AssignToLiteral","text":"

Bases: BloqadeIRTransformer

Transform all assigned variables to literals.

"},{"location":"reference/bloqade/compiler/rewrite/common/add_padding/","title":"Add padding","text":""},{"location":"reference/bloqade/compiler/rewrite/common/assign_to_literal/","title":"Assign to literal","text":""},{"location":"reference/bloqade/compiler/rewrite/common/assign_to_literal/#bloqade.compiler.rewrite.common.assign_to_literal.AssignToLiteral","title":"AssignToLiteral","text":"

Bases: BloqadeIRTransformer

Transform all assigned variables to literals.

"},{"location":"reference/bloqade/compiler/rewrite/common/assign_variables/","title":"Assign variables","text":""},{"location":"reference/bloqade/compiler/rewrite/common/canonicalize/","title":"Canonicalize","text":""},{"location":"reference/bloqade/compiler/rewrite/common/flatten/","title":"Flatten","text":""},{"location":"reference/bloqade/compiler/rewrite/python/","title":"Index","text":""},{"location":"reference/bloqade/compiler/rewrite/python/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/emulate/","title":"Index","text":""},{"location":"reference/bloqade/emulate/sparse_operator/","title":"Sparse operator","text":""},{"location":"reference/bloqade/emulate/ir/","title":"Index","text":""},{"location":"reference/bloqade/emulate/ir/atom_type/","title":"Atom type","text":""},{"location":"reference/bloqade/emulate/ir/emulator/","title":"Emulator","text":""},{"location":"reference/bloqade/emulate/ir/emulator/#bloqade.emulate.ir.emulator.Register","title":"Register dataclass","text":"

This class represents the of the atoms in the system.

"},{"location":"reference/bloqade/emulate/ir/space/","title":"Space","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/","title":"State vector","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.AnalogGate","title":"AnalogGate dataclass","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.AnalogGate.run","title":"run","text":"
run(\n    shots=1,\n    solver_name=\"dop853\",\n    atol=1e-14,\n    rtol=1e-07,\n    nsteps=2147483647,\n    interaction_picture=False,\n    project_hyperfine=True,\n)\n

Run the emulation with all atoms in the ground state, sampling the final state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef run(\n    self,\n    shots: int = 1,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-14,\n    rtol: float = 1e-7,\n    nsteps: int = 2_147_483_647,\n    interaction_picture: bool = False,\n    project_hyperfine: bool = True,\n):\n    \"\"\"Run the emulation with all atoms in the ground state,\n    sampling the final state vector.\"\"\"\n\n    options = dict(\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n\n    state = self.hamiltonian.space.zero_state()\n    (result,) = self.apply(state, **options)\n    result /= np.linalg.norm(result)\n\n    return self.hamiltonian.space.sample_state_vector(\n        result, shots, project_hyperfine=project_hyperfine\n    )\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian","title":"RydbergHamiltonian dataclass","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.average","title":"average","text":"
average(register, time=None)\n

Get energy average from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default register StateVector

The state vector to take average with

required time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description float float

average energy at time time

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef average(\n    self,\n    register: StateVector,\n    time: Optional[float] = None,\n) -> float:\n    \"\"\"Get energy average from RydbergHamiltonian object at time `time` with\n    register `register`\n\n    Args:\n        register (StateVector): The state vector to take average with\n        time (Optional[float], optional): Time value to evaluate average at.\n        Defaults to duration of RydbergHamiltonian.\n\n    Returns:\n        float: average energy at time `time`\n    \"\"\"\n    return np.vdot(register.data, self._apply(register.data, time)).real\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.average_and_variance","title":"average_and_variance","text":"
average_and_variance(register, time=None)\n

Get energy average and variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default register StateVector

The state vector to take average and variance with

required time Optional[float]

Time value to evaluate average at.

None

Returns:

Type Description float

Tuple[float, float]: average and variance of energy at time time

float

respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef average_and_variance(\n    self,\n    register: StateVector,\n    time: Optional[float] = None,\n) -> Tuple[float, float]:\n    \"\"\"Get energy average and variance from RydbergHamiltonian object at time `time`\n    with register `register`\n\n    Args:\n        register (StateVector): The state vector to take average and variance with\n        time (Optional[float], optional): Time value to evaluate average at.\n        Defaults to duration of RydbergHamiltonian.\n\n    Returns:\n        Tuple[float, float]: average and variance of energy at time `time`\n        respectively.\n    \"\"\"\n    H_register_data = self._apply(register.data, time)\n\n    average = np.vdot(register.data, H_register_data).real\n    square_average = np.vdot(H_register_data, H_register_data).real\n\n    return average, square_average - average**2\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.expectation_value","title":"expectation_value","text":"
expectation_value(register, operator, site_indices)\n

Calculate expectation values of one and two body operators.

Parameters:

Name Type Description Default register ndarray

Register to evaluate expectation value with

required operator ndarray

Operator to take expectation value of.

required site_indices (int, Tuple[int, int])

site/sites to evaluate operator at. It can either a single integer or a tuple of two integers for one and two body operator respectively.

required

Raises:

Type Description ValueError

Error is raised when the dimension of operator is not

Returns:

Name Type Description complex complex

The expectation value.

Source code in src/bloqade/emulate/ir/state_vector.py
@plum.dispatch\ndef expectation_value(  # noqa: F811\n    self, register: np.ndarray, operator: np.ndarray, site_indices: int\n) -> complex:\n    \"\"\"Calculate expectation values of one and two body operators.\n\n    Args:\n        register (np.ndarray): Register to evaluate expectation value with\n        operator (np.ndarray): Operator to take expectation value of.\n        site_indices (int, Tuple[int, int]): site/sites to evaluate `operator` at.\n            It can either a single integer or a tuple of two integers for one and\n            two body operator respectively.\n\n    Raises:\n        ValueError: Error is raised when the dimension of `operator` is not\n        consistent with `site` argument. The size of the operator must fit the\n        size of the local hilbert space of `site` depending on the number of sites\n        and the number of levels inside each atom, e.g. for two site expectation v\n        alue with a three level atom the operator must be a 9 by 9 array.\n\n    Returns:\n        complex: The expectation value.\n    \"\"\"\n    self._check_register(register)\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.variance","title":"variance","text":"
variance(register, time=None)\n

Get the energy variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default register StateVector

The state vector to take variance with

required time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description complex float

variance of energy at time time respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef variance(\n    self,\n    register: StateVector,\n    time: Optional[float] = None,\n) -> float:\n    \"\"\"Get the energy variance from RydbergHamiltonian object at\n    time `time` with register `register`\n\n    Args:\n        register (StateVector): The state vector to take variance with\n        time (Optional[float], optional): Time value to evaluate average at.\n        Defaults to duration of RydbergHamiltonian.\n\n    Returns:\n        complex: variance of energy at time `time` respectively.\n    \"\"\"\n\n    _, var = self.average_and_variance(register, time)\n    return var\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector","title":"StateVector dataclass","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector.local_trace","title":"local_trace","text":"
local_trace(matrix, site_index)\n

return trace of an operator over the StateVector.

Parameters:

Name Type Description Default matrix ndarray

Square matrix representing operator in the local hilbert space.

required site_index int | Tuple[int, int]

sites to apply one body operator to.

required

Returns:

Name Type Description complex complex

the trace of the operator over the state-vector.

Raises:

Type Description ValueError

Error is raised when the dimension of operator is not

ValueError

Error is raised when the site argument is out of bounds.

Source code in src/bloqade/emulate/ir/state_vector.py
@plum.dispatch\ndef local_trace(  # noqa: F811\n    self, matrix: np.ndarray, site_index: Union[int, Tuple[int, int]]\n) -> complex:  # noqa: F811\n    \"\"\"return trace of an operator over the StateVector.\n\n    Args:\n        matrix (np.ndarray): Square matrix representing operator in the local\n            hilbert space.\n        site_index (int | Tuple[int, int]): sites to apply one body operator to.\n\n    Returns:\n        complex: the trace of the operator over the state-vector.\n\n    Raises:\n        ValueError: Error is raised when the dimension of `operator` is not\n        consistent with `site` argument. The size of the operator must fit\n        the size of the local hilbert space of `site` depending on the number\n        of sites and the number of levels inside each atom, e.g. for two site\n        expectation value with a three level atom the operator must be a 9 by\n        9 array.\n\n        ValueError: Error is raised when the `site` argument is out of bounds.\n\n    \"\"\"\n    ...\n
"},{"location":"reference/bloqade/ir/","title":"Index","text":""},{"location":"reference/bloqade/ir/#bloqade.ir.start","title":"start module-attribute","text":"
start = ListOfLocations()\n

A Program starting point, alias of empty ListOfLocations.

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for Rydberg Level coupling
    • start.hyperfine: for Hyperfine Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): (\"x\", \"y\")
        • Scalar objects: (2*cast(\"x\"), 5+cast(\"y\"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms
"},{"location":"reference/bloqade/ir/#bloqade.ir.AlignedWaveform","title":"AlignedWaveform","text":"

Bases: Waveform

<padded waveform> ::= <waveform> | <waveform> <alignment> <value>\n\n<alignment> ::= 'left aligned' | 'right aligned'\n<value> ::= 'left value' | 'right value' | <scalar expr>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AnalogCircuit","title":"AnalogCircuit","text":"

AnalogCircuit is a dummy type that bundle register and sequence together.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AnalogCircuit.register","title":"register property","text":"
register\n

Get the register of the program.

Returns:

Type Description

register (Union[\"AtomArrangement\", \"ParallelRegister\"])

Note

If the program is built with parallelize(), The the register will be a ParallelRegister. Otherwise it will be a AtomArrangement.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AnalogCircuit.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the program

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the program

{} Source code in src/bloqade/ir/analog_circuit.py
def show(self, **assignments):\n    \"\"\"Interactive visualization of the program\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the program\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais","title":"BoundedBravais","text":"
BoundedBravais(parent=None)\n

Bases: AtomArrangement

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.__match_args__","title":"__match_args__ class-attribute instance-attribute","text":"
__match_args__ = ('shape', 'lattice_spacing')\n

Base classe for Bravais lattices AtomArrangement.

  • Square
  • Chain
  • Honeycomb
  • Triangular
  • Lieb
  • Kagome
  • Rectangular
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.n_dims","title":"n_dims property","text":"
n_dims\n

dimension of the lattice

Returns:

Name Type Description int

dimension of the lattice

"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.coordinates","title":"coordinates","text":"
coordinates(index)\n

calculate the coordinates of a cell in the lattice given the cell index.

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef coordinates(self, index: List[int]) -> NDArray:\n    \"\"\"calculate the coordinates of a cell in the lattice\n    given the cell index.\n    \"\"\"\n    # damn! this is like stone age broadcasting\n    vectors = np.array(self.cell_vectors())\n    index = np.array(index)\n    pos = np.sum(vectors.T * index, axis=1)\n    return pos + np.array(self.cell_atoms())\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.scale","title":"scale","text":"
scale(factor)\n

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef scale(self, factor: ScalarType) -> \"BoundedBravais\":\n    \"\"\"Scale the current location with a factor.\n\n    (x,y) -> factor*(x,y)\n\n    Args:\n        factor (str | Real | Decimal | Scalar): scale factor\n\n    Returns:\n        BoundedBravais: The lattice with the scaled locations\n    \"\"\"\n    factor = cast(factor)\n    obj = self.__new__(type(self))\n    for f in fields(self):\n        if f.name == \"lattice_spacing\":\n            obj.lattice_spacing = factor * self.lattice_spacing\n        else:\n            setattr(obj, f.name, getattr(self, f.name))\n    return obj\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Constant","title":"Constant","text":"
Constant(value, duration)\n

Bases: Instruction

<constant> ::= 'constant' <scalar expr>\n

f(t=0:duration) = value

Parameters:

Name Type Description Default value Scalar

the constant value

required duration Scalar

the time span of the constant waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, value: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"value\", cast(value))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Field","title":"Field","text":"

Bases: FieldExpr

Field node in the IR. Which contains collection(s) of Waveform

<field> ::= ('field' <spatial modulation>  <padded waveform>)*\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Field.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Field

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Field

{} Source code in src/bloqade/ir/control/field.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Field\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Field\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Linear","title":"Linear","text":"
Linear(start, stop, duration)\n

Bases: Instruction

<linear> ::= 'linear' <scalar expr> <scalar expr>\n

f(t=0:duration) = start + (stop-start)/duration * t

Parameters:

Name Type Description Default start Scalar

start value

required stop Scalar

stop value

required duration Scalar

the time span of the linear waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, start: ScalarType, stop: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"start\", cast(start))\n    object.__setattr__(self, \"stop\", cast(stop))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Literal","title":"Literal","text":"

Bases: Real

"},{"location":"reference/bloqade/ir/#bloqade.ir.Literal.value","title":"value instance-attribute","text":"
value\n

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default value Decimal

decimal value instance

required"},{"location":"reference/bloqade/ir/#bloqade.ir.Poly","title":"Poly","text":"
Poly(coeffs, duration)\n

Bases: Instruction

<poly> ::= <scalar>+\n

f(t=0:duration) = c[0] + c[1]t + c[2]t^2 + ... + c[n-1]t^n-1 + c[n]t^n

Parameters:

Name Type Description Default coeffs Tuple[Scalar]

the coefficients c[] of the polynomial.

required duration Scalar

the time span of the waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, coeffs: Container[ScalarType], duration: ScalarType):\n    object.__setattr__(self, \"coeffs\", tuple(map(cast, coeffs)))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Pulse","title":"Pulse","text":"

Bases: PulseExpr

<pulse> ::= (<field name> <field>)+\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Pulse.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Pulse

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Pulse

{} Source code in src/bloqade/ir/control/pulse.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Pulse\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Pulse\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.PythonFn","title":"PythonFn","text":"

Bases: Instruction

<python-fn> ::= 'python-fn' <python function def> <scalar expr>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Record","title":"Record","text":"

Bases: Waveform

<record> ::= 'record' <waveform> <var> <side>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Sample","title":"Sample","text":"

Bases: Waveform

<sample> ::= 'sample' <waveform> <interpolation> <scalar>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Scalar","title":"Scalar","text":"

Base class for all scalar expressions.

<scalar> ::= <literal>\n| <variable>\n| <default>\n| <negative>\n| <add>\n| <mul>\n| <min>\n| <max>\n| <slice>\n| <inverval>\n\n<mul> ::= <scalar> '*' <scalar>\n<add> ::= <scalar> '+' <scalar>\n<min> ::= 'min' <scalar>+\n<max> ::= 'max' <scalar>+\n<slice> ::= <scalar expr> '[' <interval> ']'\n<interval> ::= <scalar expr> '..' <scalar expr>\n<real> ::= <literal> | <var>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Sequence","title":"Sequence","text":"

Bases: SequenceExpr

Sequence of a program, which includes pulses informations.

"},{"location":"reference/bloqade/ir/#bloqade.ir.Sequence.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Sequence

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Sequence

{} Source code in src/bloqade/ir/control/sequence.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Sequence\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Sequence\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Variable","title":"Variable","text":"

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default name str

variable instance.

required"},{"location":"reference/bloqade/ir/#bloqade.ir.Waveform","title":"Waveform","text":"

Bases: HashTrait, CanonicalizeTrait

Waveform node in the IR.

  • <instruction>
  • <smooth>
  • <slice>
  • <apppend>
  • <negative>
  • <scale>
  • <add>
  • <record>
  • <sample>
<waveform> ::= <instruction>\n    | <smooth>\n    | <slice>\n    | <append>\n    | <negative>\n    | <scale>\n    | <add>\n    | <record>\n    | <sample>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Waveform.figure","title":"figure","text":"
figure(**assignments)\n

get figure of the plotting the waveform.

Returns:

Name Type Description figure

a bokeh figure

Source code in src/bloqade/ir/control/waveform.py
def figure(self, **assignments):\n    \"\"\"get figure of the plotting the waveform.\n\n    Returns:\n        figure: a bokeh figure\n    \"\"\"\n    return get_ir_figure(self, **assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.cast","title":"cast","text":"
cast(py)\n
  1. cast Real number (or list/tuple of Real numbers) to Scalar Literal.

  2. cast str (or list/tuple of Real numbers) to Scalar Variable.

Parameters:

Name Type Description Default py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description Scalar

Scalar

Source code in src/bloqade/ir/scalar.py
def cast(py) -> \"Scalar\":\n    \"\"\"\n    1. cast Real number (or list/tuple of Real numbers)\n    to [`Scalar Literal`][bloqade.ir.scalar.Literal].\n\n    2. cast str (or list/tuple of Real numbers)\n    to [`Scalar Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast\n\n    Returns:\n        Scalar\n    \"\"\"\n    ret = trycast(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Scalar Literal\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.var","title":"var","text":"
var(py)\n

cast string (or list/tuple of strings) to Variable.

Parameters:

Name Type Description Default py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description Variable

Union[Variable]

Source code in src/bloqade/ir/scalar.py
def var(py: str) -> \"Variable\":\n    \"\"\"cast string (or list/tuple of strings)\n    to [`Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str, List[str]]): a string or list/tuple of strings\n\n    Returns:\n       Union[Variable]\n    \"\"\"\n    ret = tryvar(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Variable\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/analog_circuit/","title":"Analog circuit","text":""},{"location":"reference/bloqade/ir/analog_circuit/#bloqade.ir.analog_circuit.AnalogCircuit","title":"AnalogCircuit","text":"

AnalogCircuit is a dummy type that bundle register and sequence together.

"},{"location":"reference/bloqade/ir/analog_circuit/#bloqade.ir.analog_circuit.AnalogCircuit.register","title":"register property","text":"
register\n

Get the register of the program.

Returns:

Type Description

register (Union[\"AtomArrangement\", \"ParallelRegister\"])

Note

If the program is built with parallelize(), The the register will be a ParallelRegister. Otherwise it will be a AtomArrangement.

"},{"location":"reference/bloqade/ir/analog_circuit/#bloqade.ir.analog_circuit.AnalogCircuit.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the program

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the program

{} Source code in src/bloqade/ir/analog_circuit.py
def show(self, **assignments):\n    \"\"\"Interactive visualization of the program\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the program\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/scalar/","title":"Scalar","text":""},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Literal","title":"Literal","text":"

Bases: Real

"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Literal.value","title":"value instance-attribute","text":"
value\n

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default value Decimal

decimal value instance

required"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Scalar","title":"Scalar","text":"

Base class for all scalar expressions.

<scalar> ::= <literal>\n| <variable>\n| <default>\n| <negative>\n| <add>\n| <mul>\n| <min>\n| <max>\n| <slice>\n| <inverval>\n\n<mul> ::= <scalar> '*' <scalar>\n<add> ::= <scalar> '+' <scalar>\n<min> ::= 'min' <scalar>+\n<max> ::= 'max' <scalar>+\n<slice> ::= <scalar expr> '[' <interval> ']'\n<interval> ::= <scalar expr> '..' <scalar expr>\n<real> ::= <literal> | <var>\n
"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Variable","title":"Variable","text":"

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default name str

variable instance.

required"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.cast","title":"cast","text":"
cast(py)\n
  1. cast Real number (or list/tuple of Real numbers) to Scalar Literal.

  2. cast str (or list/tuple of Real numbers) to Scalar Variable.

Parameters:

Name Type Description Default py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description Scalar

Scalar

Source code in src/bloqade/ir/scalar.py
def cast(py) -> \"Scalar\":\n    \"\"\"\n    1. cast Real number (or list/tuple of Real numbers)\n    to [`Scalar Literal`][bloqade.ir.scalar.Literal].\n\n    2. cast str (or list/tuple of Real numbers)\n    to [`Scalar Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast\n\n    Returns:\n        Scalar\n    \"\"\"\n    ret = trycast(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Scalar Literal\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.var","title":"var","text":"
var(py)\n

cast string (or list/tuple of strings) to Variable.

Parameters:

Name Type Description Default py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description Variable

Union[Variable]

Source code in src/bloqade/ir/scalar.py
def var(py: str) -> \"Variable\":\n    \"\"\"cast string (or list/tuple of strings)\n    to [`Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str, List[str]]): a string or list/tuple of strings\n\n    Returns:\n       Union[Variable]\n    \"\"\"\n    ret = tryvar(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Variable\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/control/","title":"Index","text":""},{"location":"reference/bloqade/ir/control/field/","title":"Field","text":""},{"location":"reference/bloqade/ir/control/field/#bloqade.ir.control.field.Field","title":"Field","text":"

Bases: FieldExpr

Field node in the IR. Which contains collection(s) of Waveform

<field> ::= ('field' <spatial modulation>  <padded waveform>)*\n
"},{"location":"reference/bloqade/ir/control/field/#bloqade.ir.control.field.Field.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Field

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Field

{} Source code in src/bloqade/ir/control/field.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Field\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Field\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/control/pulse/","title":"Pulse","text":""},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.Append","title":"Append","text":"

Bases: AppendTrait, PulseExpr

<append> ::= <expr>+\n
"},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.Pulse","title":"Pulse","text":"

Bases: PulseExpr

<pulse> ::= (<field name> <field>)+\n
"},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.Pulse.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Pulse

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Pulse

{} Source code in src/bloqade/ir/control/pulse.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Pulse\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Pulse\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.PulseExpr","title":"PulseExpr","text":"

Bases: HashTrait, CanonicalizeTrait

<expr> ::= <pulse>\n  | <append>\n  | <slice>\n  | <named>\n
"},{"location":"reference/bloqade/ir/control/sequence/","title":"Sequence","text":""},{"location":"reference/bloqade/ir/control/sequence/#bloqade.ir.control.sequence.Sequence","title":"Sequence","text":"

Bases: SequenceExpr

Sequence of a program, which includes pulses informations.

"},{"location":"reference/bloqade/ir/control/sequence/#bloqade.ir.control.sequence.Sequence.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Sequence

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Sequence

{} Source code in src/bloqade/ir/control/sequence.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Sequence\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Sequence\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/control/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Add","title":"Add","text":"

Bases: Waveform

<add> ::= <waveform> '+' <waveform>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.AlignedWaveform","title":"AlignedWaveform","text":"

Bases: Waveform

<padded waveform> ::= <waveform> | <waveform> <alignment> <value>\n\n<alignment> ::= 'left aligned' | 'right aligned'\n<value> ::= 'left value' | 'right value' | <scalar expr>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Append","title":"Append","text":"

Bases: AppendTrait, Waveform

<append> ::= <waveform>+\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Constant","title":"Constant","text":"
Constant(value, duration)\n

Bases: Instruction

<constant> ::= 'constant' <scalar expr>\n

f(t=0:duration) = value

Parameters:

Name Type Description Default value Scalar

the constant value

required duration Scalar

the time span of the constant waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, value: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"value\", cast(value))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Instruction","title":"Instruction","text":"

Bases: Waveform

Instruction node in the IR.

  • <linear>
  • <constant>
  • <poly>
  • <python-fn>
<instruction> ::= <linear>\n    | <constant>\n    | <poly>\n    | <python-fn>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Linear","title":"Linear","text":"
Linear(start, stop, duration)\n

Bases: Instruction

<linear> ::= 'linear' <scalar expr> <scalar expr>\n

f(t=0:duration) = start + (stop-start)/duration * t

Parameters:

Name Type Description Default start Scalar

start value

required stop Scalar

stop value

required duration Scalar

the time span of the linear waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, start: ScalarType, stop: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"start\", cast(start))\n    object.__setattr__(self, \"stop\", cast(stop))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Negative","title":"Negative","text":"

Bases: Waveform

<negative> ::= '-' <waveform>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Poly","title":"Poly","text":"
Poly(coeffs, duration)\n

Bases: Instruction

<poly> ::= <scalar>+\n

f(t=0:duration) = c[0] + c[1]t + c[2]t^2 + ... + c[n-1]t^n-1 + c[n]t^n

Parameters:

Name Type Description Default coeffs Tuple[Scalar]

the coefficients c[] of the polynomial.

required duration Scalar

the time span of the waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, coeffs: Container[ScalarType], duration: ScalarType):\n    object.__setattr__(self, \"coeffs\", tuple(map(cast, coeffs)))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.PythonFn","title":"PythonFn","text":"

Bases: Instruction

<python-fn> ::= 'python-fn' <python function def> <scalar expr>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Record","title":"Record","text":"

Bases: Waveform

<record> ::= 'record' <waveform> <var> <side>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Sample","title":"Sample","text":"

Bases: Waveform

<sample> ::= 'sample' <waveform> <interpolation> <scalar>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Scale","title":"Scale","text":"
Scale(scalar, waveform)\n

Bases: Waveform

<scale> ::= <scalar expr> '*' <waveform>\n
Source code in src/bloqade/ir/control/waveform.py
def __init__(self, scalar, waveform: Waveform):\n    object.__setattr__(self, \"scalar\", cast(scalar))\n    object.__setattr__(self, \"waveform\", waveform)\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Slice","title":"Slice","text":"

Bases: SliceTrait, Waveform

<slice> ::= <waveform> <scalar.interval>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Smooth","title":"Smooth","text":"
Smooth(radius, kernel, waveform)\n

Bases: Waveform

<smooth> ::= 'smooth' <kernel> <waveform>\n
Source code in src/bloqade/ir/control/waveform.py
def __init__(self, radius, kernel, waveform):\n    if isinstance(kernel, str):\n        if kernel == \"Gaussian\":\n            kernel = GaussianKernel\n        elif kernel == \"Logistic\":\n            kernel = LogisticKernel\n        elif kernel == \"Sigmoid\":\n            kernel = SigmoidKernel\n        elif kernel == \"Triangle\":\n            kernel = TriangleKernel\n        elif kernel == \"Uniform\":\n            kernel = UniformKernel\n        elif kernel == \"Parabolic\":\n            kernel = ParabolicKernel\n        elif kernel == \"Biweight\":\n            kernel = BiweightKernel\n        elif kernel == \"Triweight\":\n            kernel = TriweightKernel\n        elif kernel == \"Tricube\":\n            kernel = TricubeKernel\n        elif kernel == \"Cosine\":\n            kernel = CosineKernel\n        else:\n            raise ValueError(f\"Invalid kernel: {kernel}\")\n\n    object.__setattr__(self, \"radius\", cast(radius))\n    object.__setattr__(self, \"kernel\", kernel)\n    object.__setattr__(self, \"waveform\", waveform)\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Waveform","title":"Waveform","text":"

Bases: HashTrait, CanonicalizeTrait

Waveform node in the IR.

  • <instruction>
  • <smooth>
  • <slice>
  • <apppend>
  • <negative>
  • <scale>
  • <add>
  • <record>
  • <sample>
<waveform> ::= <instruction>\n    | <smooth>\n    | <slice>\n    | <append>\n    | <negative>\n    | <scale>\n    | <add>\n    | <record>\n    | <sample>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Waveform.figure","title":"figure","text":"
figure(**assignments)\n

get figure of the plotting the waveform.

Returns:

Name Type Description figure

a bokeh figure

Source code in src/bloqade/ir/control/waveform.py
def figure(self, **assignments):\n    \"\"\"get figure of the plotting the waveform.\n\n    Returns:\n        figure: a bokeh figure\n    \"\"\"\n    return get_ir_figure(self, **assignments)\n
"},{"location":"reference/bloqade/ir/control/traits/","title":"Index","text":""},{"location":"reference/bloqade/ir/control/traits/#bloqade.ir.control.traits.SliceTrait","title":"SliceTrait","text":""},{"location":"reference/bloqade/ir/control/traits/#bloqade.ir.control.traits.SliceTrait.start","title":"start cached property","text":"
start\n

Start time of the sliced object

Returns:

Name Type Description Scalar Scalar

The starting time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/control/traits/#bloqade.ir.control.traits.SliceTrait.stop","title":"stop cached property","text":"
stop\n

Stop time of the sliced object

Returns:

Name Type Description Scalar Scalar

The stopping time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/control/traits/append/","title":"Append","text":""},{"location":"reference/bloqade/ir/control/traits/canonicalize/","title":"Canonicalize","text":""},{"location":"reference/bloqade/ir/control/traits/hash/","title":"Hash","text":""},{"location":"reference/bloqade/ir/control/traits/slice/","title":"Slice","text":""},{"location":"reference/bloqade/ir/control/traits/slice/#bloqade.ir.control.traits.slice.SliceTrait","title":"SliceTrait","text":""},{"location":"reference/bloqade/ir/control/traits/slice/#bloqade.ir.control.traits.slice.SliceTrait.start","title":"start cached property","text":"
start\n

Start time of the sliced object

Returns:

Name Type Description Scalar Scalar

The starting time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/control/traits/slice/#bloqade.ir.control.traits.slice.SliceTrait.stop","title":"stop cached property","text":"
stop\n

Stop time of the sliced object

Returns:

Name Type Description Scalar Scalar

The stopping time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/location/","title":"Index","text":""},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.start","title":"start module-attribute","text":"
start = ListOfLocations()\n

A Program starting point, alias of empty ListOfLocations.

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for Rydberg Level coupling
    • start.hyperfine: for Hyperfine Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): (\"x\", \"y\")
        • Scalar objects: (2*cast(\"x\"), 5+cast(\"y\"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais","title":"BoundedBravais","text":"
BoundedBravais(parent=None)\n

Bases: AtomArrangement

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.__match_args__","title":"__match_args__ class-attribute instance-attribute","text":"
__match_args__ = ('shape', 'lattice_spacing')\n

Base classe for Bravais lattices AtomArrangement.

  • Square
  • Chain
  • Honeycomb
  • Triangular
  • Lieb
  • Kagome
  • Rectangular
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.n_dims","title":"n_dims property","text":"
n_dims\n

dimension of the lattice

Returns:

Name Type Description int

dimension of the lattice

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.coordinates","title":"coordinates","text":"
coordinates(index)\n

calculate the coordinates of a cell in the lattice given the cell index.

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef coordinates(self, index: List[int]) -> NDArray:\n    \"\"\"calculate the coordinates of a cell in the lattice\n    given the cell index.\n    \"\"\"\n    # damn! this is like stone age broadcasting\n    vectors = np.array(self.cell_vectors())\n    index = np.array(index)\n    pos = np.sum(vectors.T * index, axis=1)\n    return pos + np.array(self.cell_atoms())\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.scale","title":"scale","text":"
scale(factor)\n

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef scale(self, factor: ScalarType) -> \"BoundedBravais\":\n    \"\"\"Scale the current location with a factor.\n\n    (x,y) -> factor*(x,y)\n\n    Args:\n        factor (str | Real | Decimal | Scalar): scale factor\n\n    Returns:\n        BoundedBravais: The lattice with the scaled locations\n    \"\"\"\n    factor = cast(factor)\n    obj = self.__new__(type(self))\n    for f in fields(self):\n        if f.name == \"lattice_spacing\":\n            obj.lattice_spacing = factor * self.lattice_spacing\n        else:\n            setattr(obj, f.name, getattr(self, f.name))\n    return obj\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/","title":"Bravais","text":""},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais","title":"BoundedBravais","text":"
BoundedBravais(parent=None)\n

Bases: AtomArrangement

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.__match_args__","title":"__match_args__ class-attribute instance-attribute","text":"
__match_args__ = ('shape', 'lattice_spacing')\n

Base classe for Bravais lattices AtomArrangement.

  • Square
  • Chain
  • Honeycomb
  • Triangular
  • Lieb
  • Kagome
  • Rectangular
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.n_dims","title":"n_dims property","text":"
n_dims\n

dimension of the lattice

Returns:

Name Type Description int

dimension of the lattice

"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.coordinates","title":"coordinates","text":"
coordinates(index)\n

calculate the coordinates of a cell in the lattice given the cell index.

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef coordinates(self, index: List[int]) -> NDArray:\n    \"\"\"calculate the coordinates of a cell in the lattice\n    given the cell index.\n    \"\"\"\n    # damn! this is like stone age broadcasting\n    vectors = np.array(self.cell_vectors())\n    index = np.array(index)\n    pos = np.sum(vectors.T * index, axis=1)\n    return pos + np.array(self.cell_atoms())\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.scale","title":"scale","text":"
scale(factor)\n

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef scale(self, factor: ScalarType) -> \"BoundedBravais\":\n    \"\"\"Scale the current location with a factor.\n\n    (x,y) -> factor*(x,y)\n\n    Args:\n        factor (str | Real | Decimal | Scalar): scale factor\n\n    Returns:\n        BoundedBravais: The lattice with the scaled locations\n    \"\"\"\n    factor = cast(factor)\n    obj = self.__new__(type(self))\n    for f in fields(self):\n        if f.name == \"lattice_spacing\":\n            obj.lattice_spacing = factor * self.lattice_spacing\n        else:\n            setattr(obj, f.name, getattr(self, f.name))\n    return obj\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/location/","title":"Location","text":""},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.ParallelRegisterInfo","title":"ParallelRegisterInfo","text":"
ParallelRegisterInfo(parallel_register)\n

ParallelRegisterInfo

Source code in src/bloqade/ir/location/location.py
def __init__(self, parallel_register: ParallelRegister):\n    atom_arrangement = parallel_register.atom_arrangement\n    cluster_spacing = parallel_register.cluster_spacing\n\n    if atom_arrangement.n_atoms > 0:\n        # calculate bounding box\n        # of this register\n        location_iter = atom_arrangement.enumerate()\n        (x, y) = next(location_iter).position\n        x_min = x\n        x_max = x\n        y_min = y\n        y_max = y\n\n        for location_info in location_iter:\n            (x, y) = location_info.position\n            x_min = x.min(x_min)\n            x_max = x.max(x_max)\n            y_min = y.min(y_min)\n            y_max = y.max(y_max)\n\n        shift_x = (x_max - x_min) + cluster_spacing\n        shift_y = (y_max - y_min) + cluster_spacing\n\n        register_locations = [\n            list(location_info.position)\n            for location_info in atom_arrangement.enumerate()\n        ]\n        register_filling = [\n            location_info.filling.value\n            for location_info in atom_arrangement.enumerate()\n        ]\n        shift_vectors = [[shift_x, cast(0)], [cast(0), shift_y]]\n    else:\n        raise ValueError(\"No locations to parallelize.\")\n\n    self.register_locations = register_locations\n    self.register_filling = register_filling\n    self.shift_vectors = shift_vectors\n
"},{"location":"reference/bloqade/ir/routine/","title":"Index","text":""},{"location":"reference/bloqade/ir/routine/base/","title":"Base","text":""},{"location":"reference/bloqade/ir/routine/base/#bloqade.ir.routine.base.Routine","title":"Routine","text":"

Bases: RoutineBase

Result of parsing a completed Builder string.

"},{"location":"reference/bloqade/ir/routine/base/#bloqade.ir.routine.base.RoutineShow","title":"RoutineShow","text":"

Bases: Show

"},{"location":"reference/bloqade/ir/routine/base/#bloqade.ir.routine.base.RoutineShow.show","title":"show","text":"
show(*args, batch_index=0)\n

Show an interactive plot of the routine.

int

which parameter set out of the batch to use. Default is 0. If there are no batch parameters, use 0.

*args: Any Specify the parameters that are defined in the .args([...]) build step.

Source code in src/bloqade/ir/routine/base.py
def show(self: \"RoutineBase\", *args, batch_index: int = 0):\n    \"\"\"Show an interactive plot of the routine.\n\n    batch_index: int\n        which parameter set out of the batch to use. Default is 0.\n        If there are no batch parameters, use 0.\n\n    *args: Any\n        Specify the parameters that are defined in the `.args([...])` build step.\n\n    \"\"\"\n    if self.source is None:\n        raise ValueError(\"Cannot show a routine without a source Builder.\")\n\n    return self.source.show(*args, batch_id=batch_index)\n
"},{"location":"reference/bloqade/ir/routine/bloqade/","title":"Bloqade","text":""},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadePythonRoutine","title":"BloqadePythonRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadePythonRoutine.run","title":"run","text":"
run(\n    shots,\n    args=(),\n    name=None,\n    blockade_radius=0.0,\n    waveform_runtime=\"interpret\",\n    interaction_picture=False,\n    cache_matrices=False,\n    multiprocessing=False,\n    num_workers=None,\n    solver_name=\"dop853\",\n    atol=1e-07,\n    rtol=1e-14,\n    nsteps=2147483647,\n)\n

Run the current program using bloqade python backend

Parameters:

Name Type Description Default shots int

number of shots after running state vector simulation

required args Tuple[LiteralType, ...]

The values for parameters defined

() name Optional[str]

Name to give this run. Defaults to None.

None blockade_radius float

Use the Blockade subspace given a

0.0 waveform_runtime str

(bool, optional): Use Numba to compile the waveforms,

'interpret' interaction_picture bool

Use the interaction picture when

False cache_matrices bool

Reuse previously evaluated matrcies when

False multiprocessing bool

Use multiple processes to process the

False num_workers Optional[int]

Number of processes to run with

None solver_name str

Which SciPy Solver to use. Defaults to

'dop853' atol float

Absolute tolerance for ODE solver. Defaults to

1e-07 rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14 nsteps int

Maximum number of steps allowed per integration

2147483647

Raises:

Type Description ValueError

Cannot use multiprocessing and cache_matrices at the same time.

Returns:

Name Type Description LocalBatch LocalBatch

Batch of local tasks that have been executed.

Source code in src/bloqade/ir/routine/bloqade.py
@beartype\ndef run(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    blockade_radius: float = 0.0,\n    waveform_runtime: str = \"interpret\",\n    interaction_picture: bool = False,\n    cache_matrices: bool = False,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-7,\n    rtol: float = 1e-14,\n    nsteps: int = 2_147_483_647,\n) -> LocalBatch:\n    \"\"\"Run the current program using bloqade python backend\n\n    Args:\n        shots (int): number of shots after running state vector simulation\n        args (Tuple[LiteralType, ...], optional): The values for parameters defined\n        in `args`. Defaults to ().\n        name (Optional[str], optional): Name to give this run. Defaults to None.\n        blockade_radius (float, optional): Use the Blockade subspace given a\n        particular radius. Defaults to 0.0.\n        waveform_runtime: (bool, optional): Use Numba to compile the waveforms,\n        Defaults to False.\n        interaction_picture (bool, optional): Use the interaction picture when\n        solving schrodinger equation. Defaults to False.\n        cache_matrices (bool, optional): Reuse previously evaluated matrcies when\n        possible. Defaults to False.\n        multiprocessing (bool, optional): Use multiple processes to process the\n        batches. Defaults to False.\n        num_workers (Optional[int], optional): Number of processes to run with\n        multiprocessing. Defaults to None.\n        solver_name (str, optional): Which SciPy Solver to use. Defaults to\n        \"dop853\".\n        atol (float, optional): Absolute tolerance for ODE solver. Defaults to\n        1e-14.\n        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.\n        Defaults to 1e-7.\n        nsteps (int, optional): Maximum number of steps allowed per integration\n        step. Defaults to 2_147_483_647, the maximum value.\n\n    Raises:\n        ValueError: Cannot use multiprocessing and cache_matrices at the same time.\n\n    Returns:\n        LocalBatch: Batch of local tasks that have been executed.\n    \"\"\"\n    if multiprocessing and cache_matrices:\n        raise ValueError(\n            \"Cannot use multiprocessing and cache_matrices at the same time.\"\n        )\n\n    compile_options = dict(\n        shots=shots,\n        args=args,\n        name=name,\n        blockade_radius=blockade_radius,\n        cache_matrices=cache_matrices,\n        waveform_runtime=waveform_runtime,\n    )\n\n    solver_options = dict(\n        multiprocessing=multiprocessing,\n        num_workers=num_workers,\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n\n    batch = self._compile(**compile_options)\n    batch._run(**solver_options)\n\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadePythonRoutine.run_callback","title":"run_callback","text":"
run_callback(\n    callback,\n    program_args=(),\n    callback_args=(),\n    ignore_exceptions=False,\n    blockade_radius=0.0,\n    waveform_runtime=\"interpret\",\n    interaction_picture=False,\n    cache_matrices=False,\n    multiprocessing=False,\n    num_workers=None,\n    solver_name=\"dop853\",\n    atol=1e-07,\n    rtol=1e-14,\n    nsteps=2147483647,\n    use_hyperfine=False,\n)\n

Run state-vector simulation with a callback to access full state-vector from emulator

Parameters:

Name Type Description Default callback Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any] required program_args Tuple[LiteralType, ...]

The values for parameters

() callback_args Tuple[Any, ...]

Extra arguments to pass into

() ignore_exceptions bool

(bool, optional) If True any exception raised during

False blockade_radius float

Use the Blockade subspace given a

0.0 waveform_runtime str

(str, optional): Specify which runtime to use for

'interpret' interaction_picture bool

Use the interaction picture when

False cache_matrices bool

Reuse previously evaluated matrcies when

False multiprocessing bool

Use multiple processes to process the

False num_workers Optional[int]

Number of processes to run with

None solver_name str

Which SciPy Solver to use. Defaults to

'dop853' atol float

Absolute tolerance for ODE solver. Defaults to

1e-07 rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14 nsteps int

Maximum number of steps allowed per integration

2147483647

Returns:

Name Type Description List List

List of resulting outputs from the callbacks

Raises:

Type Description RuntimeError

Raises the first error that occurs, only if

Note

For the callback function, first argument is the many-body wavefunction as a 1D complex numpy array, the second argument is of type Metadata which is a Named Tuple where the fields correspond to the parameters of that given task, RydbergHamiltonian is the object that contains the Hamiltonian used to generate the evolution for that task, Finally any optional positional arguments are allowed after that. The return value can be anything, the results will be collected in a list for each task in the batch.

Source code in src/bloqade/ir/routine/bloqade.py
@beartype\ndef run_callback(\n    self,\n    callback: Callable[[StateVector, NamedTuple, RydbergHamiltonian, Any], Any],\n    program_args: Tuple[LiteralType, ...] = (),\n    callback_args: Tuple = (),\n    ignore_exceptions: bool = False,\n    blockade_radius: float = 0.0,\n    waveform_runtime: str = \"interpret\",\n    interaction_picture: bool = False,\n    cache_matrices: bool = False,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-7,\n    rtol: float = 1e-14,\n    nsteps: int = 2_147_483_647,\n    use_hyperfine: bool = False,\n) -> List:\n    \"\"\"Run state-vector simulation with a callback to access full state-vector from\n    emulator\n\n    Args:\n        callback (Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]):\n        The callback function to run for each task in batch. See note below for more\n        details about the signature of the function.\n        program_args (Tuple[LiteralType, ...], optional): The values for parameters\n        defined in `args`. Defaults to ().\n        callback_args (Tuple[Any,...], optional): Extra arguments to pass into\n        ignore_exceptions: (bool, optional) If `True` any exception raised during\n        a task will be saved instead of the resulting output of the callback,\n        otherwise the first exception by task number will be raised after *all*\n        tasks have executed. Defaults to False.\n        blockade_radius (float, optional): Use the Blockade subspace given a\n        particular radius. Defaults to 0.0.\n        waveform_runtime: (str, optional): Specify which runtime to use for\n        waveforms. Defaults to \"interpret\".\n        interaction_picture (bool, optional): Use the interaction picture when\n        solving schrodinger equation. Defaults to False.\n        cache_matrices (bool, optional): Reuse previously evaluated matrcies when\n        possible. Defaults to False.\n        multiprocessing (bool, optional): Use multiple processes to process the\n        batches. Defaults to False.\n        num_workers (Optional[int], optional): Number of processes to run with\n        multiprocessing. Defaults to None.\n        solver_name (str, optional): Which SciPy Solver to use. Defaults to\n        \"dop853\".\n        atol (float, optional): Absolute tolerance for ODE solver. Defaults to\n        1e-14.\n        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.\n        Defaults to 1e-7.\n        nsteps (int, optional): Maximum number of steps allowed per integration\n        step. Defaults to 2_147_483_647, the maximum value.\n\n    Returns:\n        List: List of resulting outputs from the callbacks\n\n    Raises:\n        RuntimeError: Raises the first error that occurs, only if\n        `ignore_exceptions=False`.\n\n    Note:\n        For the `callback` function, first argument is the many-body wavefunction\n        as a 1D complex numpy array, the second argument is of type `Metadata` which\n        is a Named Tuple where the fields correspond to the parameters of that given\n        task, RydbergHamiltonian is the object that contains the Hamiltonian used to\n        generate the evolution for that task, Finally any optional positional\n        arguments are allowed after that. The return value can be anything, the\n        results will be collected in a list for each task in the batch.\n\n\n    \"\"\"\n    if multiprocessing:\n        from multiprocessing import Process, Queue, cpu_count\n    else:\n        from queue import Queue\n\n    if cache_matrices:\n        compile_cache = CompileCache()\n    else:\n        compile_cache = None\n\n    solver_args = dict(\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n\n    runner = self.EmuRunner(\n        compile_cache=compile_cache,\n        solver_args=solver_args,\n        callback=callback,\n        callback_args=callback_args,\n    )\n\n    tasks = Queue()\n    results = Queue()\n\n    total_tasks = 0\n    ir_iter = self._generate_ir(\n        program_args, blockade_radius, waveform_runtime, use_hyperfine\n    )\n    for task_data in ir_iter:\n        task_number = task_data.task_id\n        emulator_ir = task_data.emulator_ir\n        metadata = task_data.metadata_dict\n        total_tasks += 1\n        tasks.put((task_number, (emulator_ir, metadata)))\n\n    workers = []\n    if multiprocessing:\n        num_workers = max(int(num_workers or cpu_count()), 1)\n        num_workers = min(total_tasks, num_workers)\n\n        for _ in range(num_workers):\n            worker = Process(\n                target=BloqadePythonRoutine.process_tasks,\n                args=(runner, tasks, results),\n            )\n            worker.start()\n\n            workers.append(worker)\n    else:\n        self.process_tasks(runner, tasks, results)\n\n    # blocks until all\n    # results have been fetched\n    # from the id_results Queue\n    id_results = []\n    for i in range(total_tasks):\n        id_results.append(results.get())\n\n    if workers:\n        for worker in workers:\n            worker.join()\n\n        tasks.close()\n        results.close()\n\n    id_results.sort(key=lambda x: x[0])\n    results = []\n\n    for task_id, result in id_results:\n        if not ignore_exceptions and isinstance(result, BaseException):\n            try:\n                raise result\n            except BaseException:\n                raise RuntimeError(\n                    f\"{result.__class__.__name__} occured during child process \"\n                    f\"running for task number {task_id}:\\n{traceback.format_exc()}\"\n                )\n\n        results.append(result)\n\n    return results\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.HamiltonianData","title":"HamiltonianData dataclass","text":"

Data class to hold the Hamiltonian and metadata for a given set of parameters

"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.TaskData","title":"TaskData dataclass","text":"

Data class to hold the program ir and metadata for a given set of parameters

"},{"location":"reference/bloqade/ir/routine/braket/","title":"Braket","text":""},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine","title":"BraketHardwareRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine.__call__","title":"__call__","text":"
__call__(\n    *args, shots=1, name=None, shuffle=False, **kwargs\n)\n

Compile to a RemoteBatch, which contain Braket backend specific tasks, run_async to Braket, and wait until the results are coming back.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

1 args LiteralType

additional arguments for args variables.

() name str

custom name of the batch

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef __call__(\n    self,\n    *args: LiteralType,\n    shots: int = 1,\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n):\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n    Braket backend specific tasks, run_async to Braket,\n    and wait until the results are coming back.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args: additional arguments for args variables.\n        name (str): custom name of the batch\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    return self.run(shots, args, name, shuffle, **kwargs)\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine.run","title":"run","text":"
run(shots, args=(), name=None, shuffle=False, **kwargs)\n

Compile to a RemoteBatch, which contain Braket backend specific tasks, run_async to Braket, and wait until the results are coming back.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple

additional arguments

() name str

custom name of the batch

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef run(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n) -> RemoteBatch:\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n    Braket backend specific tasks, run_async to Braket,\n    and wait until the results are coming back.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args (Tuple): additional arguments\n        name (str): custom name of the batch\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n\n    batch = self.run_async(shots, args, name, shuffle, **kwargs)\n    batch.pull()\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine.run_async","title":"run_async","text":"
run_async(\n    shots, args=(), name=None, shuffle=False, **kwargs\n)\n

Compile to a RemoteBatch, which contain Braket backend specific tasks, and run_async to Braket.

Note

This is async.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple

Values of the parameter defined in args, defaults to ()

() name str | None

custom name of the batch, defaults to None

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef run_async(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n) -> RemoteBatch:\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n    Braket backend specific tasks, and run_async to Braket.\n\n    Note:\n        This is async.\n\n    Args:\n        shots (int): number of shots\n        args (Tuple): Values of the parameter defined in `args`, defaults to ()\n        name (str | None): custom name of the batch, defaults to None\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n\n    batch = self._compile(shots, args, name)\n    batch._submit(shuffle, **kwargs)\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketLocalEmulatorRoutine","title":"BraketLocalEmulatorRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketLocalEmulatorRoutine.__call__","title":"__call__","text":"
__call__(\n    *args,\n    shots=1,\n    name=None,\n    multiprocessing=False,\n    num_workers=None,\n    **kwargs\n)\n

Compile to a LocalBatch, and run. The LocalBatch contain tasks to run on local emulator.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

1 args LiteralType

additional arguments for args variables.

() multiprocessing bool

enable multi-process

False num_workers int

number of workers to run the emulator

None Return

LocalBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef __call__(\n    self,\n    *args: LiteralType,\n    shots: int = 1,\n    name: Optional[str] = None,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    **kwargs,\n):\n    \"\"\"\n    Compile to a LocalBatch, and run.\n    The LocalBatch contain tasks to run on local emulator.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args: additional arguments for args variables.\n        multiprocessing (bool): enable multi-process\n        num_workers (int): number of workers to run the emulator\n\n    Return:\n        LocalBatch\n\n    \"\"\"\n    return self.run(\n        shots,\n        args,\n        name,\n        multiprocessing=multiprocessing,\n        num_workers=num_workers,\n        **kwargs,\n    )\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketLocalEmulatorRoutine.run","title":"run","text":"
run(\n    shots,\n    args=(),\n    name=None,\n    multiprocessing=False,\n    num_workers=None,\n    **kwargs\n)\n

Compile to a LocalBatch, and run. The LocalBatch contain tasks to run on local emulator.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple[LiteralType, ...]

additional arguments for args variables.

() multiprocessing bool

enable multi-process

False num_workers int

number of workers to run the emulator

None Return

LocalBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef run(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    **kwargs,\n) -> LocalBatch:\n    \"\"\"\n    Compile to a LocalBatch, and run.\n    The LocalBatch contain tasks to run on local emulator.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args: additional arguments for args variables.\n        multiprocessing (bool): enable multi-process\n        num_workers (int): number of workers to run the emulator\n\n    Return:\n        LocalBatch\n\n    \"\"\"\n\n    batch = self._compile(shots, args, name)\n    batch._run(multiprocessing=multiprocessing, num_workers=num_workers, **kwargs)\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/params/","title":"Params","text":""},{"location":"reference/bloqade/ir/routine/quera/","title":"Quera","text":""},{"location":"reference/bloqade/ir/routine/quera/#bloqade.ir.routine.quera.QuEraHardwareRoutine","title":"QuEraHardwareRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/quera/#bloqade.ir.routine.quera.QuEraHardwareRoutine.run_async","title":"run_async","text":"
run_async(\n    shots, args=(), name=None, shuffle=False, **kwargs\n)\n

Compile to a RemoteBatch, which contain QuEra backend specific tasks, and run_async through QuEra service.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple

additional arguments

() name str

custom name of the batch

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/quera.py
@beartype\ndef run_async(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n) -> RemoteBatch:\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n        QuEra backend specific tasks,\n        and run_async through QuEra service.\n\n    Args:\n        shots (int): number of shots\n        args (Tuple): additional arguments\n        name (str): custom name of the batch\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    batch = self._compile(shots, args, name)\n    batch._submit(shuffle, **kwargs)\n    return batch\n
"},{"location":"reference/bloqade/submission/","title":"Index","text":""},{"location":"reference/bloqade/submission/base/","title":"Base","text":""},{"location":"reference/bloqade/submission/braket/","title":"Braket","text":""},{"location":"reference/bloqade/submission/load_config/","title":"Load config","text":""},{"location":"reference/bloqade/submission/mock/","title":"Mock","text":""},{"location":"reference/bloqade/submission/quera/","title":"Quera","text":""},{"location":"reference/bloqade/submission/ir/","title":"Index","text":""},{"location":"reference/bloqade/submission/ir/braket/","title":"Braket","text":""},{"location":"reference/bloqade/submission/ir/capabilities/","title":"Capabilities","text":""},{"location":"reference/bloqade/submission/ir/parallel/","title":"Parallel","text":""},{"location":"reference/bloqade/submission/ir/parallel/#bloqade.submission.ir.parallel.ClusterLocationInfo","title":"ClusterLocationInfo","text":"

Bases: BaseModel

Class that stores the mapping of batched jobs.

Parameters:

Name Type Description Default cluster_index int

the index of the cluster a site belongs to

required global_location_index int

the index of the site in the multplexed system

required cluster_location_index int

the index of the site in the original system

required"},{"location":"reference/bloqade/submission/ir/task_results/","title":"Task results","text":""},{"location":"reference/bloqade/submission/ir/task_results/#bloqade.submission.ir.task_results.QuEraTaskResults","title":"QuEraTaskResults","text":"

Bases: BaseModel

"},{"location":"reference/bloqade/submission/ir/task_results/#bloqade.submission.ir.task_results.QuEraTaskResults.export_as_probabilities","title":"export_as_probabilities","text":"
export_as_probabilities()\n

converts from shot results to probabilities

Returns:

Name Type Description TaskProbabilities TaskProbabilities

The task results as probabilties

Source code in src/bloqade/submission/ir/task_results.py
def export_as_probabilities(self) -> TaskProbabilities:\n    \"\"\"converts from shot results to probabilities\n\n    Returns:\n        TaskProbabilities: The task results as probabilties\n    \"\"\"\n    counts = dict()\n    nshots = len(self.shot_outputs)\n    for shot_result in self.shot_outputs:\n        pre_sequence_str = \"\".join(str(bit) for bit in shot_result.pre_sequence)\n\n        post_sequence_str = \"\".join(str(bit) for bit in shot_result.post_sequence)\n\n        configuration = (pre_sequence_str, post_sequence_str)\n        # iterative average\n        current_count = counts.get(configuration, 0)\n        counts[configuration] = current_count + 1\n\n    probabilities = [(config, count / nshots) for config, count in counts.items()]\n    return TaskProbabilities(probabilities=probabilities)\n
"},{"location":"reference/bloqade/submission/ir/task_specification/","title":"Task specification","text":""},{"location":"reference/bloqade/task/","title":"Index","text":""},{"location":"reference/bloqade/task/base/","title":"Base","text":""},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report","title":"Report","text":"
Report(data, metas, geos, name='')\n
Source code in src/bloqade/task/base.py
def __init__(self, data, metas, geos, name=\"\") -> None:\n    self.dataframe = data  # df\n    self._bitstrings = None  # bitstring cache\n    self._counts = None  # counts cache\n    self.metas = metas\n    self.geos = geos\n    self.name = name + \" \" + str(datetime.datetime.now())\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.bitstrings","title":"bitstrings","text":"
bitstrings(filter_perfect_filling=True, clusters=[])\n

Get the bitstrings from the data.

Parameters:

Name Type Description Default filter_perfect_filling bool

whether return will

True clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]):

[]

Returns:

Name Type Description bitstrings list of ndarray

list corresponding to each

List[NDArray]

task in the report. Each element is an ndarray of shape

List[NDArray]

(nshots, nsites) where nshots is the number of shots for

List[NDArray]

the task and nsites is the number of sites in the task.

Note

Note that nshots may vary between tasks if filter_perfect_filling is set to True.

Source code in src/bloqade/task/base.py
@beartype\ndef bitstrings(\n    self,\n    filter_perfect_filling: bool = True,\n    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],\n) -> List[NDArray]:\n    \"\"\"Get the bitstrings from the data.\n\n    Args:\n        filter_perfect_filling (bool): whether return will\n        only contain perfect filling shots. Defaults to True.\n        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):\n        cluster index to filter shots from. If none are provided\n        all clusters are used, defaults to [].\n\n    Returns:\n        bitstrings (list of ndarray): list corresponding to each\n        task in the report. Each element is an ndarray of shape\n        (nshots, nsites) where nshots is the number of shots for\n        the task and nsites is the number of sites in the task.\n\n    Note:\n        Note that nshots may vary between tasks if filter_perfect_filling\n        is set to True.\n\n    \"\"\"\n\n    task_numbers = self.dataframe.index.get_level_values(\"task_number\").unique()\n\n    bitstrings = []\n    for task_number in task_numbers:\n        mask = self._filter(\n            task_number=task_number,\n            filter_perfect_filling=filter_perfect_filling,\n            clusters=clusters,\n        )\n        if np.any(mask):\n            bitstrings.append(self.dataframe.loc[mask].to_numpy())\n        else:\n            bitstrings.append(\n                np.zeros((0, self.dataframe.shape[1]), dtype=np.uint8)\n            )\n\n    return bitstrings\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.counts","title":"counts","text":"
counts(filter_perfect_filling=True, clusters=[])\n

Get the counts of unique bit strings.

Parameters:

Name Type Description Default filter_perfect_filling bool

whether return will

True clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]):

[]

Returns:

Name Type Description bitstrings list of ndarray

list corresponding to each

List[OrderedDict[str, int]]

task in the report. Each element is an ndarray of shape

List[OrderedDict[str, int]]

(nshots, nsites) where nshots is the number of shots for

List[OrderedDict[str, int]]

the task and nsites is the number of sites in the task.

Note

Note that nshots may vary between tasks if filter_perfect_filling is set to True.

Source code in src/bloqade/task/base.py
def counts(\n    self,\n    filter_perfect_filling: bool = True,\n    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],\n) -> List[OrderedDict[str, int]]:\n    \"\"\"Get the counts of unique bit strings.\n\n    Args:\n        filter_perfect_filling (bool): whether return will\n        only contain perfect filling shots. Defaults to True.\n        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):\n        cluster index to filter shots from. If none are provided\n        all clusters are used, defaults to [].\n\n    Returns:\n        bitstrings (list of ndarray): list corresponding to each\n        task in the report. Each element is an ndarray of shape\n        (nshots, nsites) where nshots is the number of shots for\n        the task and nsites is the number of sites in the task.\n\n    Note:\n        Note that nshots may vary between tasks if filter_perfect_filling\n        is set to True.\n\n    \"\"\"\n\n    def generate_counts(bitstring):\n        output = np.unique(bitstring, axis=0, return_counts=True)\n\n        count_list = [\n            (\"\".join(map(str, bitstring)), int(count))\n            for bitstring, count in zip(*output)\n        ]\n        count_list.sort(key=lambda x: x[1], reverse=True)\n        count = OrderedDict(count_list)\n\n        return count\n\n    return list(\n        map(generate_counts, self.bitstrings(filter_perfect_filling, clusters))\n    )\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.list_param","title":"list_param","text":"
list_param(field_name)\n

List the parameters associate with the given variable field_name for each tasks.

Parameters:

Name Type Description Default field_name str

variable name

required Source code in src/bloqade/task/base.py
def list_param(self, field_name: str) -> List[Union[Number, None]]:\n    \"\"\"\n    List the parameters associate with the given variable field_name\n    for each tasks.\n\n    Args:\n        field_name (str): variable name\n\n    \"\"\"\n\n    def cast(x):\n        if x is None:\n            return None\n        elif isinstance(x, (list, tuple, np.ndarray)):\n            return list(map(cast, x))\n        else:\n            return float(x)\n\n    return list(map(cast, (meta.get(field_name) for meta in self.metas)))\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.rydberg_densities","title":"rydberg_densities","text":"
rydberg_densities(filter_perfect_filling=True, clusters=[])\n

Get rydberg density for each task.

Parameters:

Name Type Description Default filter_perfect_filling bool

whether return will

True Return

per-site rydberg density for each task as a pandas DataFrame or Series.

Source code in src/bloqade/task/base.py
@beartype\ndef rydberg_densities(\n    self,\n    filter_perfect_filling: bool = True,\n    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],\n) -> Union[pd.Series, pd.DataFrame]:\n    \"\"\"Get rydberg density for each task.\n\n    Args:\n        filter_perfect_filling (bool, optional): whether return will\n        only contain perfect filling shots. Defaults to True.\n\n    Return:\n        per-site rydberg density for each task as a pandas DataFrame or Series.\n\n    \"\"\"\n    mask = self._filter(\n        filter_perfect_filling=filter_perfect_filling, clusters=clusters\n    )\n    df = self.dataframe[mask]\n    return 1 - (df.groupby(\"task_number\").mean())\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.show","title":"show","text":"
show()\n

Interactive Visualization of the Report

Source code in src/bloqade/task/base.py
def show(self):\n    \"\"\"\n    Interactive Visualization of the Report\n\n    \"\"\"\n    display_report(self)\n
"},{"location":"reference/bloqade/task/batch/","title":"Batch","text":""},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Filter","title":"Filter","text":""},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Filter.filter_metadata","title":"filter_metadata","text":"
filter_metadata(__match_any__=False, **metadata)\n

Create a Batch object that has tasks filtered based on the values of metadata.

Parameters:

Name Type Description Default __match_any__ bool

if True, then a task will be included if it matches any of the metadata filters. If False, then a task will be included only if it matches all of the metadata filters. Defaults to False.

False **metadata MetadataFilterType

the metadata to filter on. The keys are the metadata names and the values (as a set) are the values to filter on. The elements in the set can be Real, Decimal, Tuple[Real], or Tuple[Decimal].

{} Return

type(self): a Batch object with the filtered tasks, either LocalBatch or RemoteBatch depending on the type of self

Source code in src/bloqade/task/batch.py
@beartype\ndef filter_metadata(\n    self, __match_any__: bool = False, **metadata: MetadataFilterType\n) -> Union[\"LocalBatch\", \"RemoteBatch\"]:\n    \"\"\"Create a Batch object that has tasks filtered based on the\n    values of metadata.\n\n    Args:\n        __match_any__: if True, then a task will be included if it\n            matches any of the metadata filters. If False, then a\n            task will be included only if it matches all of the\n            metadata filters. Defaults to False.\n\n        **metadata: the metadata to filter on. The keys are the metadata\n            names and the values (as a set) are the values to filter on.\n            The elements in the set can be Real, Decimal, Tuple[Real], or\n            Tuple[Decimal].\n\n    Return:\n        type(self): a Batch object with the filtered tasks, either\n            LocalBatch or RemoteBatch depending on the type of self\n\n    \"\"\"\n\n    def convert_to_decimal(element):\n        if isinstance(element, list):\n            return list(map(convert_to_decimal, element))\n        elif isinstance(element, (Real, Decimal)):\n            return Decimal(str(element))\n        else:\n            raise ValueError(\n                f\"Invalid value {element} for metadata filter. \"\n                \"Only Real, Decimal, List[Real], and List[Decimal] \"\n                \"are supported.\"\n            )\n\n    def metadata_match_all(task):\n        return all(\n            task.metadata.get(key) in value for key, value in metadata.items()\n        )\n\n    def metadata_match_any(task):\n        return any(\n            task.metadata.get(key) in value for key, value in metadata.items()\n        )\n\n    metadata = {k: list(map(convert_to_decimal, v)) for k, v in metadata.items()}\n\n    metadata_filter = metadata_match_any if __match_any__ else metadata_match_all\n\n    new_tasks = OrderedDict(\n        [(k, v) for k, v in self.tasks.items() if metadata_filter(v)]\n    )\n\n    kw = dict(self.__dict__)\n    kw[\"tasks\"] = new_tasks\n\n    return self.__class__(**kw)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.LocalBatch","title":"LocalBatch dataclass","text":"

Bases: Serializable, Filter

"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.LocalBatch.report","title":"report","text":"
report()\n

Generate analysis report base on currently completed tasks in the LocalBatch.

Return

Report

Source code in src/bloqade/task/batch.py
def report(self) -> Report:\n    \"\"\"\n    Generate analysis report base on currently\n    completed tasks in the LocalBatch.\n\n    Return:\n        Report\n\n    \"\"\"\n\n    ## this potentially can be specialize/disatch\n    ## offline\n    index = []\n    data = []\n    metas = []\n    geos = []\n\n    for task_number, task in self.tasks.items():\n        geometry = task.geometry\n        perfect_sorting = \"\".join(map(str, geometry.filling))\n        parallel_decoder = geometry.parallel_decoder\n\n        if parallel_decoder:\n            cluster_indices = parallel_decoder.get_cluster_indices()\n        else:\n            cluster_indices = {(0, 0): list(range(len(perfect_sorting)))}\n\n        shot_iter = filter(\n            lambda shot: shot.shot_status == QuEraShotStatusCode.Completed,\n            task.result().shot_outputs,\n        )\n\n        for shot, (cluster_coordinate, cluster_index) in product(\n            shot_iter, cluster_indices.items()\n        ):\n            pre_sequence = \"\".join(\n                map(\n                    str,\n                    (shot.pre_sequence[index] for index in cluster_index),\n                )\n            )\n\n            post_sequence = np.asarray(\n                [shot.post_sequence[index] for index in cluster_index],\n                dtype=np.int8,\n            )\n\n            pfc_sorting = \"\".join(\n                [perfect_sorting[index] for index in cluster_index]\n            )\n\n            key = (\n                task_number,\n                cluster_coordinate,\n                pfc_sorting,\n                pre_sequence,\n            )\n\n            index.append(key)\n            data.append(post_sequence)\n\n        metas.append(task.metadata)\n        geos.append(task.geometry)\n\n    index = pd.MultiIndex.from_tuples(\n        index, names=[\"task_number\", \"cluster\", \"perfect_sorting\", \"pre_sequence\"]\n    )\n\n    df = pd.DataFrame(data, index=index)\n    df.sort_index(axis=\"index\")\n\n    rept = None\n    if self.name is None:\n        rept = Report(df, metas, geos, \"Local\")\n    else:\n        rept = Report(df, metas, geos, self.name)\n\n    return rept\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.LocalBatch.rerun","title":"rerun","text":"
rerun(multiprocessing=False, num_workers=None, **kwargs)\n

Rerun all the tasks in the LocalBatch.

Return

Report

Source code in src/bloqade/task/batch.py
@beartype\ndef rerun(\n    self, multiprocessing: bool = False, num_workers: Optional[int] = None, **kwargs\n):\n    \"\"\"\n    Rerun all the tasks in the LocalBatch.\n\n    Return:\n        Report\n\n    \"\"\"\n\n    return self._run(\n        multiprocessing=multiprocessing, num_workers=num_workers, **kwargs\n    )\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch","title":"RemoteBatch dataclass","text":"

Bases: Serializable, Filter

"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.total_nshots","title":"total_nshots property","text":"
total_nshots\n

Total number of shots of all tasks in the RemoteBatch

Return

number of shots

"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.cancel","title":"cancel","text":"
cancel()\n

Cancel all the tasks in the Batch.

Return

self

Source code in src/bloqade/task/batch.py
def cancel(self) -> \"RemoteBatch\":\n    \"\"\"\n    Cancel all the tasks in the Batch.\n\n    Return:\n        self\n\n    \"\"\"\n    # cancel all jobs\n    for task in self.tasks.values():\n        task.cancel()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.fetch","title":"fetch","text":"
fetch()\n

Fetch the tasks in the Batch.

Note

Fetching will update the status of tasks, and only pull the results for those tasks that have completed.

Return

self

Source code in src/bloqade/task/batch.py
def fetch(self) -> \"RemoteBatch\":\n    \"\"\"\n    Fetch the tasks in the Batch.\n\n    Note:\n        Fetching will update the status of tasks,\n        and only pull the results for those tasks\n        that have completed.\n\n    Return:\n        self\n\n    \"\"\"\n    # online, non-blocking\n    # pull the results only when its ready\n    for task in self.tasks.values():\n        task.fetch()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_completed_tasks","title":"get_completed_tasks","text":"
get_completed_tasks()\n

Create a RemoteBatch object that contain completed tasks from current Batch.

Tasks consider completed with following status codes:

  1. Completed
  2. Partial
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def get_completed_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain completed tasks from current Batch.\n\n    Tasks consider completed with following status codes:\n\n    1. Completed\n    2. Partial\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    statuses = [\n        \"Completed\",\n        \"Partial\",\n    ]\n    return self.get_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_failed_tasks","title":"get_failed_tasks","text":"
get_failed_tasks()\n

Create a RemoteBatch object that contain failed tasks from current Batch.

failed tasks with following status codes:

  1. Failed
  2. Unaccepted
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def get_failed_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain failed tasks from current Batch.\n\n    failed tasks with following status codes:\n\n    1. Failed\n    2. Unaccepted\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # statuses that are in a state that are\n    # completed because of an error\n    statuses = [\"Failed\", \"Unaccepted\"]\n    return self.get_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_finished_tasks","title":"get_finished_tasks","text":"
get_finished_tasks()\n

Create a RemoteBatch object that contain finished tasks from current Batch.

Tasks consider finished with following status codes:

  1. Failed
  2. Unaccepted
  3. Completed
  4. Partial
  5. Cancelled
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def get_finished_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain finished tasks from current Batch.\n\n    Tasks consider finished with following status codes:\n\n    1. Failed\n    2. Unaccepted\n    3. Completed\n    4. Partial\n    5. Cancelled\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # statuses that are in a state that will\n    # not run going forward for any reason\n    statuses = [\"Completed\", \"Failed\", \"Unaccepted\", \"Partial\", \"Cancelled\"]\n    return self.get_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_tasks","title":"get_tasks","text":"
get_tasks(*status_codes)\n

Get Tasks with specify status_codes.

Return

RemoteBatch

Source code in src/bloqade/task/batch.py
@beartype\ndef get_tasks(self, *status_codes: str) -> \"RemoteBatch\":\n    \"\"\"\n    Get Tasks with specify status_codes.\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # offline:\n    st_codes = [QuEraTaskStatusCode(x) for x in status_codes]\n\n    new_task_results = OrderedDict()\n    for task_number, task in self.tasks.items():\n        if task.task_result_ir.task_status in st_codes:\n            new_task_results[task_number] = task\n\n    return RemoteBatch(self.source, new_task_results, name=self.name)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.pull","title":"pull","text":"
pull()\n

Pull results of the tasks in the Batch.

Note

Pulling will pull the results for the tasks. If a given task(s) has not been completed, wait until it finished.

Return

self

Source code in src/bloqade/task/batch.py
def pull(self) -> \"RemoteBatch\":\n    \"\"\"\n    Pull results of the tasks in the Batch.\n\n    Note:\n        Pulling will pull the results for the tasks.\n        If a given task(s) has not been completed, wait\n        until it finished.\n\n    Return:\n        self\n    \"\"\"\n    # online, blocking\n    # pull the results. if its not ready, hanging\n    for task in self.tasks.values():\n        task.pull()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.remove_failed_tasks","title":"remove_failed_tasks","text":"
remove_failed_tasks()\n

Create a RemoteBatch object that contain tasks from current Batch, with failed tasks removed.

failed tasks with following status codes:

  1. Failed
  2. Unaccepted
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def remove_failed_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain tasks from current Batch,\n    with failed tasks removed.\n\n    failed tasks with following status codes:\n\n    1. Failed\n    2. Unaccepted\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # statuses that are in a state that will\n    # not run going forward because of an error\n    statuses = [\"Failed\", \"Unaccepted\"]\n    return self.remove_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.remove_invalid_tasks","title":"remove_invalid_tasks","text":"
remove_invalid_tasks()\n

Create a RemoteBatch object that contain tasks from current Batch, with all Unaccepted tasks removed.

Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def remove_invalid_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain tasks from current Batch,\n    with all Unaccepted tasks removed.\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    return self.remove_tasks(\"Unaccepted\")\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.remove_tasks","title":"remove_tasks","text":"
remove_tasks(*status_codes)\n

Remove Tasks with specify status_codes.

Return

RemoteBatch

Source code in src/bloqade/task/batch.py
@beartype\ndef remove_tasks(self, *status_codes: str) -> \"RemoteBatch\":\n    \"\"\"\n    Remove Tasks with specify status_codes.\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # offline:\n\n    st_codes = [QuEraTaskStatusCode(x) for x in status_codes]\n\n    new_results = OrderedDict()\n    for task_number, task in self.tasks.items():\n        if task.task_result_ir.task_status in st_codes:\n            continue\n\n        new_results[task_number] = task\n\n    return RemoteBatch(self.source, new_results, self.name)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.report","title":"report","text":"
report()\n

Generate analysis report base on currently completed tasks in the RemoteBatch.

Return

Report

Source code in src/bloqade/task/batch.py
def report(self) -> \"Report\":\n    \"\"\"\n    Generate analysis report base on currently\n    completed tasks in the RemoteBatch.\n\n    Return:\n        Report\n\n    \"\"\"\n    ## this potentially can be specialize/disatch\n    ## offline\n    index = []\n    data = []\n    metas = []\n    geos = []\n\n    for task_number, task in self.tasks.items():\n        ## fliter not existing results tasks:\n        if (task.task_id is None) or (not task._result_exists()):\n            continue\n\n        ## filter has result but is not correctly completed.\n        if task.task_result_ir.task_status not in [\n            QuEraTaskStatusCode.Completed,\n            QuEraTaskStatusCode.Partial,\n        ]:\n            continue\n\n        geometry = task.geometry\n        perfect_sorting = \"\".join(map(str, geometry.filling))\n        parallel_decoder = geometry.parallel_decoder\n\n        if parallel_decoder:\n            cluster_indices = parallel_decoder.get_cluster_indices()\n        else:\n            cluster_indices = {(0, 0): list(range(len(perfect_sorting)))}\n\n        shot_iter = filter(\n            lambda shot: shot.shot_status == QuEraShotStatusCode.Completed,\n            task.result().shot_outputs,\n        )\n\n        for shot, (cluster_coordinate, cluster_index) in product(\n            shot_iter, cluster_indices.items()\n        ):\n            pre_sequence = \"\".join(\n                map(\n                    str,\n                    (shot.pre_sequence[index] for index in cluster_index),\n                )\n            )\n\n            post_sequence = np.asarray(\n                [shot.post_sequence[index] for index in cluster_index],\n                dtype=np.int8,\n            )\n\n            pfc_sorting = \"\".join(\n                [perfect_sorting[index] for index in cluster_index]\n            )\n\n            key = (\n                task_number,\n                cluster_coordinate,\n                pfc_sorting,\n                pre_sequence,\n            )\n\n            index.append(key)\n            data.append(post_sequence)\n\n        metas.append(task.metadata)\n        geos.append(task.geometry)\n\n    index = pd.MultiIndex.from_tuples(\n        index, names=[\"task_number\", \"cluster\", \"perfect_sorting\", \"pre_sequence\"]\n    )\n\n    df = pd.DataFrame(data, index=index)\n    df.sort_index(axis=\"index\")\n\n    rept = None\n    if self.name is None:\n        rept = Report(df, metas, geos, \"Remote\")\n    else:\n        rept = Report(df, metas, geos, self.name)\n\n    return rept\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.resubmit","title":"resubmit","text":"
resubmit(shuffle_submit_order=True)\n

Resubmit all the tasks in the RemoteBatch

Return

self

Source code in src/bloqade/task/batch.py
@beartype\ndef resubmit(self, shuffle_submit_order: bool = True) -> \"RemoteBatch\":\n    \"\"\"\n    Resubmit all the tasks in the RemoteBatch\n\n    Return:\n        self\n\n    \"\"\"\n    # online, non-blocking\n    self._submit(shuffle_submit_order, force=True)\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.retrieve","title":"retrieve","text":"
retrieve()\n

Retrieve missing task results.

Note

Retrieve will update the status of tasks, and only pull the results for those tasks that have completed.

Return

self

Source code in src/bloqade/task/batch.py
def retrieve(self) -> \"RemoteBatch\":\n    \"\"\"Retrieve missing task results.\n\n    Note:\n        Retrieve will update the status of tasks,\n        and only pull the results for those tasks\n        that have completed.\n\n    Return:\n        self\n\n    \"\"\"\n    # partially online, sometimes blocking\n    # pull the results for tasks that have\n    # not been pulled already.\n    for task in self.tasks.values():\n        if not task._result_exists():\n            task.pull()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.tasks_metric","title":"tasks_metric","text":"
tasks_metric()\n

Get current tasks status metric

Return

dataframe with [\"task id\", \"status\", \"shots\"]

Source code in src/bloqade/task/batch.py
def tasks_metric(self) -> pd.DataFrame:\n    \"\"\"\n    Get current tasks status metric\n\n    Return:\n        dataframe with [\"task id\", \"status\", \"shots\"]\n\n    \"\"\"\n    # [TODO] more info on current status\n    # offline, non-blocking\n    tid = []\n    data = []\n    for int, task in self.tasks.items():\n        tid.append(int)\n\n        dat = [None, None, None]\n        dat[0] = task.task_id\n        if task.task_result_ir is not None:\n            dat[1] = task.task_result_ir.task_status.name\n        dat[2] = task.task_ir.nshots\n        data.append(dat)\n\n    return pd.DataFrame(data, index=tid, columns=[\"task ID\", \"status\", \"shots\"])\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Serializable","title":"Serializable","text":""},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Serializable.json","title":"json","text":"
json(**options)\n

Serialize the object to JSON string.

Return

JSON string

Source code in src/bloqade/task/batch.py
def json(self, **options) -> str:\n    \"\"\"\n    Serialize the object to JSON string.\n\n    Return:\n        JSON string\n\n    \"\"\"\n    from bloqade import dumps\n\n    return dumps(self, **options)\n
"},{"location":"reference/bloqade/task/bloqade/","title":"Bloqade","text":""},{"location":"reference/bloqade/task/braket/","title":"Braket","text":""},{"location":"reference/bloqade/task/braket_simulator/","title":"Braket simulator","text":""},{"location":"reference/bloqade/task/quera/","title":"Quera","text":""}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-,:!=\\[\\: )\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Index","text":""},{"location":"#welcome-to-bloqade-queras-neutral-atom-sdk","title":"Welcome to Bloqade: QuEra's Neutral Atom SDK","text":""},{"location":"#what-is-bloqade","title":"What is Bloqade?","text":"

Bloqade is a Python SDK for QuEra's neutral atom quantum computer Aquila (check out our paper!). It's designed to make writing and analyzing the results of analog quantum programs on Aquila as easy as possible. It features custom atom geometries and flexible waveform definitions in both emulation and real hardware. Bloqade interfaces with the AWS Braket cloud service where Aquila is hosted, enabling you to submit programs as well as retrieve and analyze real hardware results all-in-one.

"},{"location":"#installation","title":"Installation","text":"

You can install the package with pip in your Python environment of choice via:

pip install bloqade\n
"},{"location":"#a-glimpse-of-bloqade","title":"A Glimpse of Bloqade","text":"

Let's try a simple example where we drive a Rabi oscillation on a single neutral atom. Don't worry if you're unfamiliar with neutral atom physics, (you can check out our Background for more information!) the goal here is to just give you a taste of what Bloqade can do.

We start by defining where our atoms go, otherwise known as the atom geometry. In this particular example we will use a small Honeycomb lattice:

from bloqade.atom_arrangement import Honeycomb\n\ngeometry = Honeycomb(2, lattice_spacing = 10.0)\n

We can verify what the atom geometry looks like by .show()'ing it:

geometry.show()\n

We now define what the time evolution looks like using a pulse sequence. The pulse sequence here is the time profile of the Rabi Drive targeting the ground-Rydberg two level transition, which causes the Rabi oscillations. We choose a constant waveform with a value of \\(\\frac{\\pi}{2} \\text{rad}/\\text{us}\\) and a duration of \\(1.0 \\,\\text{us}\\). This produces a \\(\\frac{\\pi}{2}\\) rotation on the Bloch sphere meaning our final measurements should be split 50/50 between the ground and Rydberg state.

from math import pi\nrabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .constant(value=pi/2, duration=1.0)\n)\n

Here rabi.amplitude means exactly what it is, the Rabi amplitude term of the Hamiltonian. uniform refers to applying the waveform uniformly across all the atom locations.

We can visualize what our program looks like again with .show():

We can now run the program through Bloqade's built-in emulator to get some results. We designate that we want the program to be run and measurements performed 100 times:

emulation_results = rabi_program.bloqade.python().run(100)\n

With the results we can generate a report object that contains a number of methods for analyzing our data, including the number of counts per unique bitstring:

bitstring_counts = emulation_results.report().counts()\n

Which gives us:

[OrderedDict([('0', 55), ('1', 45)])]\n

If we want to submit our program to hardware we'll need to adjust the waveform as there is a constraint the Rabi amplitude waveform must start and end at zero. This is easy to do as we can build off the atom geometry we saved previously but apply a piecewise linear waveform:

hardware_rabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06])\n)\n\nhardware_rabi_program.show()\n

Now instead of using the built-in Bloqade emulator we submit the program to Aquila. You will need to use the AWS CLI to obtain credentials from your AWS account or set the proper environment variables before hand.

hardware_results = hardware_rabi_program.braket.aquila.run_async(100)\n

.run_async is a non-blocking version of the standard .run method, allowing you to continue work while waiting for results from Aquila. .run_async immediately returns an object you can query for the status of your tasks in the queue as well.

You can do the exact same analysis you do on emulation results with hardware results too:

hardware_bitstring_counts = hardware_results.report().counts()\n

If you want to try the above at once, we collected the above steps into the snippet below:

from math import pi\nfrom bloqade.atom_arrangement import Honeycomb\n\ngeometry = Honeycomb(2, lattice_spacing = 10.0)\nrabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .constant(value=pi/2, duration=1.0)\n)\nemulation_results = rabi_program.bloqade.python().run(100) \nbitstring_counts = emulation_results.report().counts()\n\nhardware_rabi_program = (\n  geometry\n  .rydberg.rabi.amplitude.uniform\n  .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06])\n)\nhardware_results = hardware_rabi_program.braket.aquila.run_async(100)\nhardware_bitstring_counts = hardware_results.report().counts()\n

"},{"location":"#features","title":"Features","text":""},{"location":"#customizable-atom-geometries","title":"Customizable Atom Geometries","text":"

You can easily explore a number of common geometric lattices with Bloqade's atom_arrangement's:

from bloqade.atom_arrangement import Lieb, Square, Chain, Kagome\n\ngeometry_1 = Lieb(3)\ngeometry_2 = Square(2)\ngeometry_3 = Chain(5)\ngeometry_4 = Kagome(3)\n

If you're not satisfied with the Bravais lattices we also allow you to modify existing Bravais lattices as follows:

geometry_5 = Kagome(3).add_position((10,11))\n

You can also build your geometry completely from scratch:

from bloqade import start\n\ngeometry = start.add_positions([(0,0), (6,0), (12,0)])\n
"},{"location":"#flexible-pulse-sequence-construction","title":"Flexible Pulse Sequence Construction","text":"

Define waveforms for pulse sequences any way you like by either building (and chaining!) them immediately as part of your program:

from bloqade.atom_arrangement import Square\n\ngeometry = Square(2)\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\ncustom_rabi_amp_waveform = (\n  target_rabi_amplitude\n  .piecewise_linear(values=[0, 10, 10, 0], durations=[0.1, 3.5, 0.1])\n  .piecewise_linear(values=[0, 5, 3, 0], durations=[0.2, 2.0, 0.2])\n)\n

Or building them separately and applying them later:

from bloqade.atom_arrangement import Square, Chain\n\ngeometry_1 = Square(3)\ngeometry_2 = Chain(5)\n\ntarget_rabi_amplitude = start.rydberg.rabi.amplitude.uniform\npulse_sequence = target_rabi_amplitude.uniform.constant(value=2.0, duration=1.5).parse_sequence()\n\nprogram_1 = geometry_1.apply(pulse_sequence)\nprogram_2 = geometry_2.apply(pulse_sequence)\n
"},{"location":"#hardware-and-emulation-backends","title":"Hardware and Emulation Backends","text":"

Go from a fast and powerful emulator:

from bloqade.atom_arrangement import Square\nfrom math import pi\n\ngeometry = Square(3, lattice_spacing = 6.5)\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nprogram = (\n  target_rabi_amplitude\n  .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06])\n)\nemulation_results = program.bloqade.python().run(100)\n

To real quantum hardware in a snap:

hardware_results = program.braket.aquila().run_async(100)\n
"},{"location":"#simple-parameter-sweeps","title":"Simple Parameter Sweeps","text":"

Use variables to make parameter sweeps easy on both emulation and hardware:

from bloqade import start\nimport numpy as np\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nrabi_oscillation_program = (\n  target_rabi_amplitude\n  .piecewise_linear(durations = [0.06, \"run_time\", 0.06], values = [0, 15, 15, 0])\n)\nrabi_oscillation_job = rabi_oscillation_program.batch_assign(run_time=np.linspace(0, 3, 101))\n\nemulation_results = rabi_oscillation_job.bloqade.python().run(100)\nhardware_results = rabi_oscillation_job.braket.aquila().run(100)\n
emulation_results.report().rydberg_densities()\n                0\ntask_number      \n0            0.16\n1            0.35\n2            0.59\n3            0.78\n4            0.96\n...           ...\n96           0.01\n97           0.09\n98           0.24\n99           0.49\n100          0.68\n\n[101 rows x 1 columns]\n
"},{"location":"#quick-results-analysis","title":"Quick Results Analysis","text":"

Want to just see some plots of your results? .show() will show you the way!

from bloqade.atom_arrangement import Square\n\nrabi_amplitude_values = [0.0, 15.8, 15.8, 0.0]\nrabi_detuning_values = [-16.33, -16.33, 42.66, 42.66]\ndurations = [0.8, 2.4, 0.8]\n\ngeometry = Square(3, lattice_spacing=5.9)\nrabi_amplitude_waveform = (\n  geometry\n  .rydberg.rabi.amplitude.uniform.piecewise_linear(durations, rabi_amplitude_values)\n)\nprogram = (\n  rabi_amplitude_waveform\n  .detuning.uniform.piecewise_linear(durations, rabi_detuning_values)\n)\nemulation_results = program.bloqade.python().run(100)\nemulation_results.report().show()\n

"},{"location":"#contributing-to-bloqade","title":"Contributing to Bloqade","text":"

Bloqade is released under the Apache License, Version 2.0. If you'd like the chance to shape the future of neutral atom quantum computation, see our Contributing Guide for more info!

"},{"location":"blog/2023/","title":"Bloqade Blog Posts 2023","text":"

Sept. 21, 2023 - Introducing Bloqade SDK for Python

"},{"location":"blog/2023/posts/bloqade-release/","title":"Introducing Bloqade SDK for Python","text":"

Greetings Neutral Atom QC experts, enthusiasts, and newcomers!

We are excited to the Rydberg state thrilled to announce the Python version of our cutting-edge SDK, Bloqade. Originally developed in Julia, Bloqade has been a game-changer in the realm of Neutral Atom quantum computing. With the introduction of the Python version, we aim to make this revolutionary technology more accessible and user-friendly than ever before.

"},{"location":"blog/2023/posts/bloqade-release/#why-python","title":"Why Python?","text":"

Python is one of the most widely used programming languages, especially in the quantum computing community and broader scientific communities. By extending Bloqade to Python, we are opening doors to a broader audience, enabling more developers, researchers, and organizations to harness the power of Neutral Atom quantum computing.

"},{"location":"blog/2023/posts/bloqade-release/#neutral-atom-quantum-computing","title":"Neutral Atom Quantum Computing","text":"

Recently, the Neutral Atom platform has come on the QC scene in the form of Analog Hamiltonian Simulators that have a broad set of use cases beyond quantum circuits. Ranging from simulating unique quantum phases of matter, solving combinatorical optimization problems, and machine learning applications, the analog mode provides strong values in solving practical, interesting problems in the near term.

These advances are crucial milestones on the way towards scalable digital gate-based architecture using atom shuttling. This new technology and its novel applications demand a paradigm shift in the way we not only think about quantum computing, but translate those ideas to real hardware. Enter Bloqade, a next-generation SDK designed to put the power of neutral atoms at your fingertips.

"},{"location":"blog/2023/posts/bloqade-release/#why-bloqade","title":"Why Bloqade?","text":"

Bloqade is designed with the primary goal of making it easier to compose programs for QuEra\u2019s hardware and analyze results.

We've gained valuable insights into how users have used our neutral-atom hardware and with it, their struggles with existing tools. We took advantage of this knowledge to produce a tool that could take the \"hard\" out of \"hardware\". Bloqade is precision-balanced in both flexibility to empower novices to experiment with ease and power to let experts perform cutting-edge work without breaking a sweat.

"},{"location":"blog/2023/posts/bloqade-release/#highlights","title":"Highlights","text":""},{"location":"blog/2023/posts/bloqade-release/#smart-documentation","title":"Smart Documentation","text":"

With our commitment to enabling more seamless program development, we've put the relevant documentation you need right where and when you need it.

No more obnoxious switching between your favorite coding environment and documentation in a separate window. Let Bloqade guide you where you'd like to go:

"},{"location":"blog/2023/posts/bloqade-release/#fully-parameterized-analog-programs","title":"Fully Parameterized Analog Programs","text":"

Parameter sweeps are a common theme of programs for analog quantum computers, where a user would like to observe differences in output results by varying a value or values in their program.

You used to have to manually crank out variations of your program with different values and then keep track of all the individual submissions to the emulator and hardware, a mess to keep track of and process the results of afterwards.

Bloqade eliminates this with its own support for variables that can later be assigned single values or a whole sequence of values for trivial parameter sweeping. This isn't some feature that's constrained to a certain backend, you can take your program with all its variables and submit it to your choice of emulator or our hardware directly.

from bloqade import var\nfrom bloqade.atom_arrangement import Square\n\nimport numpy as np\n\nadiabatic_durations = [0.4, 3.2, 0.4]\n\n# create variables explicitly...\nmax_detuning = var(\"max_detuning\")\n# ...or implicitly inside the program definition.\nadiabatic_program = (\n    Square(3, \"lattice_spacing\")\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=adiabatic_durations, values=[0.0, \"max_rabi\", \"max_rabi\", 0.0]\n    )\n    .detuning.uniform.piecewise_linear(\n        durations=adiabatic_durations,\n        values=[\n            -max_detuning, # scalar variables support direct arithmetic operations\n            -max_detuning,\n            max_detuning,\n            max_detuning,\n        ],\n    )\n    .assign(max_rabi=15.8, max_detuning=16.33)\n    .batch_assign(lattice_spacing=np.arange(4.0, 7.0, 0.5))\n)\n\n# Launch your program on your choice of Braket or in-house emulator...\nemu_results = adiabatic_program.braket.local_emulator().run(10000)\nfaster_emu_results = adiabatic_program.bloqade.python().run(10000)\n# ...as well as hardware without stress\nhw_results = adiabatic_program.parallelize(24).braket.aquila().run_async(100)\n
"},{"location":"blog/2023/posts/bloqade-release/#integrated-visualization-tools","title":"Integrated Visualization Tools","text":"

Instantly understand what your programs are doing faster than you can say \"neutral atoms rock!\" with Bloqade's built-in visualization tools:

For your results, no more obnoxious manual compilation of results across different parameters or wrangling them into more useful forms. Get insights of experiment outcomes in the blink of an eye:

Now that's what we call having your cake AND eating it.

"},{"location":"blog/2023/posts/bloqade-release/#bloqade-roadmap","title":"Bloqade Roadmap","text":""},{"location":"blog/2023/posts/bloqade-release/#bloqade-alpha-phase","title":"Bloqade Alpha Phase","text":"

During the next year, we plan on continuing development of Bloqade's python interface. If you are as excited about Neutral Atom quantum computing as us, or heck, even just quantum physics in general, give Bloqade a try! This is your opportunity to influence the direction of Bloqade and get in on the ground floor of the next Quantum Computing revolution.

"},{"location":"blog/2023/posts/bloqade-release/#but-what-about-julia","title":"But what about Julia?","text":"

Don't you guys already HAVE an SDK in Julia? Why do you need two SDKs?

That's right! However, there's a key motivating factor for the reason we created Bloqade Python that's distinct for Bloqade.jl's existence.

Bloqade.jl is primarily geared as a high-performance emulator. It allows you to design complex neutral-atom algorithms that may not necessarily run on our hardware BUT are excellent if you're exploring novel physical phenonema/algorithms or as a tool for pedagogical purposes.

Bloqade.jl does have the ability to submit to Aquila, our flagship quantum computer, but for more complex tasks such as sweeping parameters (e.g. running the same program on hardware with slightly different parameters each time) or advanced post-processing, it becomes cumbersome quite quickly.

There are no plans to drop support any time soon though. On the contrary, we plan on fully integrating Bloqade.jl into the Python package, which will enable you to program Neutral Atom quantum hardware without having to choose.

We very much look forward to you trying out Bloqade!

"},{"location":"contributing/","title":"Contributing","text":"

Thank you for your interest in contributing to the project! We welcome all contributions. There are many different ways to contribute to Bloqade, and we are always looking for more help. We accept contributions in the form of bug reports, feature requests, documentation improvements, and code contributions. For more information about how to contribute, please read the following sections.

"},{"location":"contributing/#table-of-contents","title":"Table of Contents","text":"
  • Reporting a Bug
  • Reporting Documentation Issues
  • Feature Requests
  • Developing Bloqade
  • Design Philosophy and Architecture
  • Community Slack
  • Ask a Question
  • Providing Feedback
"},{"location":"contributing/asking-a-question/","title":"Ask a Question","text":"

If you're interested in contributing to Bloqade, or just want to discuss the project, join the discussion on GitHub Discussions at https://github.com/QuEraComputing/bloqade-python/discussions

"},{"location":"contributing/code-of-conduct/","title":"Design Philosophy and Architecture","text":"

Given the heterogeneous nature of the hardware we target, We have decided to use a compiler-based approach to our software stack, allowing us to target different hardware backends with the same high-level language. Below is a diagram of the software stack in Bloqade.

graph TD\n    Builder[\"Builder Representation\"]\n    PythonAST[\"Bloqade AST Python\"]\n    JuliaAST[\"Bloqade AST Julia\"]\n\n    EmulatorPy[\"Emulator IR Python\"]\n    EmulatorJL[\"Emulator IR Julia\"]\n\n    QuEra[\"QuEra IR\"]\n    Braket[\"Braket IR\"]\n    JuliaEmulator[\"Bloqade.jl\"]\n    PythonEmulator[\"Python Emulator\"]\n\n    Aquila[\"Aquila\"]\n\n    Builder -->|parse| PythonAST\n    PythonAST -->|lower| EmulatorPy\n    PythonAST -->|lower| QuEra\n    PythonAST -->|lower| Braket\n    PythonAST -->|transpile| JuliaAST\n\n    QuEra -->|execute| Aquila\n    Braket -->|execute| Aquila\n\n    JuliaAST -->|lower| EmulatorJL\n    EmulatorPy -->|execute| PythonEmulator\n    EmulatorJL -->|execute| JuliaEmulator\n
"},{"location":"contributing/code-of-conduct/#high-level-builder-representation","title":"High-Level Builder Representation","text":"

When programming Bloqade using the Python API, the user constructs a representation of an analog quantum circuit. This representation is a flattened version of the actual analog circuit. Flattened means that the user input is a linear sequence of operations where the context of neighboring nodes in the sequence of instructions can determine the program tree structure. The Bloqade AST describes the actual analog circuit.

"},{"location":"contributing/code-of-conduct/#bloqade-ast","title":"Bloqade AST","text":"

The Bloqade AST is a representation of a quantum analog circuit for neutral atom computing. It is a directed acyclic graph (DAG) with nodes for different hierarchical levels of the circuit. The base node is the AnalogCircuit which contains the geometry of the atoms stored as a AtomArragment or ParallelRegister objects. The other part of the circuit is the Sequence, which contains the waveforms that describe the drives for the Ryberg/Hyperfine transitions of each Rydberg atom. Each transition is represented by a Pulse including a Field for the drive's detuning, Rabi amplitude, and Rabi phase . A Field relates the spatial and temporal dependence of a drive. The spatial modulates the temporal dependence of the waveform. A DAG also describes the Waveform object. Finally, we have basic Scalar expressions as well for describing the syntax of real-valued continuous numbers.

"},{"location":"contributing/code-of-conduct/#bloqade-compilers-and-transpilers","title":"Bloqade Compilers and Transpilers","text":"

Given a user program expressed as the Bloqade AST, we can target various backends by transforming from the Bloqade AST to other kinds of IR. For example, when submitting a task to QuEra's hardware, we transform the Bloqade AST to the IR that describes a valid program for the hardware.

This process is referred to as lowering, which in a general sense is a transformation that takes you from one IR to another where the target IR is specialized or has a smaller syntactical structure. Transpiling corresponds to a transformation that takes you from one language to equivalent expressions in another. For example, we can transpile from the Bloqade AST in Python to the Bloqade AST in Julia. The generic term for both of these types of transformation in Bloqade is Code Generation. You will find various code generation implementations in various codegen modules.

"},{"location":"contributing/community-slack/","title":"Community Slack","text":"

You can join QuEra's Slack workspace with this link. Join the #bloqade channel to discuss anything related to Bloqade.

"},{"location":"contributing/design-philosophy-and-architecture/","title":"Design Philosophy and Architecture","text":"

Given the heterogeneous nature of the hardware we target, We have decided to use a compiler-based approach to our software stack, allowing us to target different hardware backends with the same high-level language. Below is a diagram of the software stack in Bloqade.

graph TD\n    Builder[\"Builder Representation\"]\n    PythonAST[\"Bloqade AST Python\"]\n    JuliaAST[\"Bloqade AST Julia\"]\n\n    EmulatorPy[\"Emulator IR Python\"]\n    EmulatorJL[\"Emulator IR Julia\"]\n\n    QuEra[\"QuEra IR\"]\n    Braket[\"Braket IR\"]\n    JuliaEmulator[\"Bloqade.jl\"]\n    PythonEmulator[\"Python Emulator\"]\n\n    Aquila[\"Aquila\"]\n\n    Builder -->|parse| PythonAST\n    PythonAST -->|lower| EmulatorPy\n    PythonAST -->|lower| QuEra\n    PythonAST -->|lower| Braket\n    PythonAST -->|transpile| JuliaAST\n\n    QuEra -->|execute| Aquila\n    Braket -->|execute| Aquila\n\n    JuliaAST -->|lower| EmulatorJL\n    EmulatorPy -->|execute| PythonEmulator\n    EmulatorJL -->|execute| JuliaEmulator\n
"},{"location":"contributing/design-philosophy-and-architecture/#high-level-builder-representation","title":"High-Level Builder Representation","text":"

When programming Bloqade using the Python API, the user constructs a representation of an analog quantum circuit. This representation is a flattened version of the actual analog circuit. Flattened means that the user input is a linear sequence of operations where the context of neighboring nodes in the sequence of instructions can determine the program tree structure. The Bloqade AST describes the actual analog circuit.

"},{"location":"contributing/design-philosophy-and-architecture/#bloqade-ast","title":"Bloqade AST","text":"

The Bloqade AST is a representation of a quantum analog circuit for neutral atom computing. It is a directed acyclic graph (DAG) with nodes for different hierarchical levels of the circuit. The base node is the AnalogCircuit which contains the geometry of the atoms stored as a AtomArragment or ParallelRegister objects. The other part of the circuit is the Sequence, which contains the waveforms that describe the drives for the Ryberg/Hyperfine transitions of each Rydberg atom. Each transition is represented by a Pulse including a Field for the drive's detuning, Rabi amplitude, and Rabi phase . A Field relates the spatial and temporal dependence of a drive. The spatial modulates the temporal dependence of the waveform. A DAG also describes the Waveform object. Finally, we have basic Scalar expressions as well for describing the syntax of real-valued continuous numbers.

"},{"location":"contributing/design-philosophy-and-architecture/#bloqade-compilers-and-transpilers","title":"Bloqade Compilers and Transpilers","text":"

Given a user program expressed as the Bloqade AST, we can target various backends by transforming from the Bloqade AST to other kinds of IR. For example, when submitting a task to QuEra's hardware, we transform the Bloqade AST to the IR that describes a valid program for the hardware.

This process is referred to as lowering, which in a general sense is a transformation that takes you from one IR to another where the target IR is specialized or has a smaller syntactical structure. Transpiling corresponds to a transformation that takes you from one language to equivalent expressions in another. For example, we can transpile from the Bloqade AST in Python to the Bloqade AST in Julia. The generic term for both of these types of transformation in Bloqade is Code Generation. You will find various code generation implementations in various codegen modules.

"},{"location":"contributing/developing-bloqade/","title":"Setting up your Development Environment","text":"

Before You Get Started

Depending on the complexity of the contribution you'd like to make to Bloqade, it may be worth reading the Design Philosophy and Architecture section to get an idea of why Bloqade is structured the way that it is and how to make your contribution adhere to this philosophy.

Our development environment contains a set of tools we use for development, testing, and documentation. This section describes how to set up the development environment. We primarily use pdm to manage python environments and dependencies.

"},{"location":"contributing/developing-bloqade/#setting-up-python","title":"Setting up Python","text":"

We use pdm to manage dependencies and virtual environment. After cloning the repository, run the following command to install dependencies:

pdm install\n

You can also install different dependency groups:

  • dev: dependencies for development
pdm install --dev\n# or\npdm install -d\n
  • doc: dependencies for building documentation
pdm install -G doc\n
"},{"location":"contributing/developing-bloqade/#useful-pdm-scripts","title":"Useful PDM scripts","text":""},{"location":"contributing/developing-bloqade/#tests","title":"Tests","text":"

You can run tests via

pdm run test\n

Or run tests and generate coverage via

pdm run coverage\n

will print out the coverage file level report in terminal.

pdm run coverage-html\n

This command generates an interactive html report in htmlcov folder. This will show which specific lines are not covered by tests.

"},{"location":"contributing/developing-bloqade/#documentation","title":"Documentation","text":"

You can build documentation via

pdm run doc_build\n

Or run a local server to preview documentation via

pdm run doc\n
"},{"location":"contributing/developing-bloqade/#jupytext","title":"Jupytext","text":"

You can sync jupyter notebooks and python scripts via

pdm run jupytext\n

this will help you development examples in jupyter notebook and python scripts simultaneously.

"},{"location":"contributing/developing-bloqade/#lint","title":"Lint","text":"

We primarily use ruff - an extremely fast linter for Python, and black as formatter. These have been configured into pre-commit hooks. After installing pre-commit on your own system, you can install pre-commit hooks to git via

pre-commit install\n
"},{"location":"contributing/documentation-issues/","title":"Reporting a Documentation Issue","text":"

We are always looking to improve our documentation. If you find a typo or think something is unclear, please open an issue on our GitHub page: https://github.com/QuEraComputing/bloqade-python/issues

For typos or other minor problems, create an issue that contains a link to the specific page that includes the problem, along with a description of the problem and possibly a solution.

For a request for new documentation content, please open up an issue and describe what you think is missing from the documentation.

"},{"location":"contributing/feature-requests/","title":"Requesting new Features","text":"

Given that we are currently at the beginning of the development of the Bloqade python interface, we are open to suggestions about what features would be helpful to include in future package iterations. If you have a request for a new feature, please open an issue on our GitHub page: https://github.com/QuEraComputing/bloqade-python/issues

We ask that the feature requests be as specific as possible. Please include the following information in your feature request:

  1. A short, descriptive title.

  2. A detailed description of the feature, including your attempt to solve the problem with the current version of Bloqade.

  3. A minimal code example that demonstrates the need for the feature.

  4. The version of Bloqade you are using.

  5. The version of Python you are using.

  6. The version of your operating system.

"},{"location":"contributing/providing-feedback/","title":"Providing Feedback","text":"

While Github Issues are a great way for us to better understand any issues your having with Bloqade as well as provide us with feature requests, we're always looking for ways to collect more general feedback about what the user experience with Bloqade is like.

To do that we have this form where you can provide your thoughts after using/experimenting/tinkering/hacking with Bloqade.

Your feedback will help guide the future of Bloqade's design so be honest and know that you're contributing to the future of Quantum Computing with Neutral Atoms!

"},{"location":"contributing/reporting-a-bug/","title":"Reporting a Bug","text":"

Bloqade is currently in the alpha phase of development, meaning bugs most likely exist in the current implementation. We are continuously striving to improve the stability of Bloqade. As such, we encourage our users to report all bugs they find. To do this, we ask you to submit an issue to our GitHub page: https://github.com/QuEraComputing/bloqade-python/issues

Please include the following information in your bug report:

  1. A short, descriptive title.

  2. A detailed description of the bug, including the expected behavior and what happened.

  3. A minimal code example that reproduces the bug.

  4. The version of Bloqade you are using.

  5. The version of Python you are using.

  6. The version of your operating system.

"},{"location":"drafts/aws_hybrid_execution/","title":"Hybrid Execution with AWS Hybrid Jobs","text":""},{"location":"drafts/aws_hybrid_execution/#introduction","title":"Introduction","text":"

Analog Hamiltonian Simulation (AHS) has proven itself to be a powerful method of performing quantum computation that is well-suited for solving optimization problems and performing computationally difficult simulations of other quantum systems. Unlike its counterpart digital/gate-based quantum computing where you think of your programs in terms of unitary operations that are akin to classicla gates, you think of programs in AHS in terms of the geometry of your qubits (individual atoms!) and the waveforms of the lasers that are applied to them.

The team at QuEra Computing believes this is a useful step in the path to fault tolerant quantum computation but also realizes that such novel power and capabilities require a novel tool.

That's why we're proud to announce the release of the Bloqade SDK for Python! We've had the opportunity to obtain valuable feedback from the community and leveraged our unique position as the only provider of publicly cloud-accessible Nuetral Atom hardware to produce a tool that puts the power of AHS hardware at your fingertips.

"},{"location":"drafts/aws_hybrid_execution/#installation","title":"Installation","text":"

Bloqade is a pure Python library so installation is as easy as pip install bloqade! Once installed you have a variety of options for building your Nuetral atom Analog program. We have worked very hard to provide a seamless user experience when venturing into unfamiliar territory of AHS with Nuetral Atoms! Let\u2019s dig into some of the major features Bloqade has to offer!

"},{"location":"drafts/aws_hybrid_execution/#features-of-bloqade","title":"Features of Bloqade","text":""},{"location":"drafts/aws_hybrid_execution/#just-in-time-documentation","title":"Just-in-time Documentation","text":"

I know you have probably spent a pretty penny on your multi-monitor setup so that you do not have to switch between windows when writing code and looking at documentation. What if I told you there was a better way? What if your code contained all the documentation you needed to continue writing your program? That\u2019s exactly what we have designed in the user experinence (UX) of Bloqade.

Typically when building an AHS program one needs to construct many different objects and combine them in very particular ways which are not obvious without understanding the concept as a whole. Bloqade provides a unique experience programming AHS. Our interface eases the user into AHS programming by using Python\u2019s rich type-hinting system. Most IDE's, as well as IPython and Jupyter, use the type hints to access to the methods and attributes that are availible to whatever object you currently have. We use this hinting to our advantage in Bloqade by building your AHS program with a chain of methods and attributes separated by .. In doing so you will be given hints as to what to do next at every step of the way when building your program.

While it is conventional thinking that it is bad to chain . statements together, there are some well known libraries like Pandas that can and do make heavy use of this pattern of programming. The dot-chaining syntax also integrate python linters like black to make your code highly readable without having to do anything other can call the linter on your python file.

Example, before linter some unstrctured code After running black we get consistent formatting. In this case properties are always chained which makes reading the code a lot like reading a sentence.

On top of these nice features, If you\u2019re in an IDE like VS code or PyCharm you can access the documentation of each method and attribute in which we provide even more hints for the next step after your current selection

Here is a blog post that goes into the advantages and disadvantages of chaining method calls/attributes like we have shown. It worth a read if you are still a bit skeptical! It's worth noting that you do not neccesarily have to chain method/attribute calls you can safely store a intermediate parts of program with intermediate objects because calling a method does not act in the object in-place. This means you can

"},{"location":"drafts/aws_hybrid_execution/#parameterized-programs","title":"Parameterized Programs","text":"

keep title capitalization consistent [name=jzlong]

Many near-term applications for QC as well as AHS require some notion of parameterized programs. We\u2019ve made this a first-class feature in Bloqade enabling you to write a single AHS program precisely to define your experiment or algorithm symbolically enabling more readable and sharable code!

You can also create stand-alone variables which have a basic symbolic representation that support some arithmetic operations that are useful for more advanced AHS applications. For example, say I have a piecewise linear pulse that has variable segments but I have another constant waveform running for the total time of the other waveform. I can sum the durations of each segment of the piecewise linear waveform to get the total duration and use that to construct the constant waveform.

from bloqade import var, start\n\nrabi_durations = [0.1, var(\"run_time\"), 0.1]\ntotal_time = sum(rabi_durations)\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(total_time, \"detuning\")\n    .amplitude.uniform.piecewise_linear(\n        rabi_durations, [0, \"amplitude\",  \"amplitude\",  0]\n    )\n)\n

Once you have defined your parameterized program there are three different methods of specifying the run time values of the parameters:

program.assign(var1=value1, var2=value2)\n

which assigns the variables in a static way. The basic logic here is for segments of program in which you want to share without limiting other users to use the concrete values you decided works for you.

program.batch_assign(var1=[value1_1, value1_2, \u2026],\u2026)\n
or

program.batch_assign([dict(var1=value1_1,\u2026), dict(var1=value1_2,\u2026),\u2026])\n

specify a batch of tasks via parameters assigned to lists or a list of dictionaries with the assigned parameters. Next,

args([\u201cvar1\u201d, \u201cvar2\u201d])\n

will delay the assignment of the variable until the program is being executed. We will discuss this below in more detail.

Note that none of these methods are required for non-parameterized programs. For parameterized programs you can mix and match all of these assignments together. However, they will always come in the order of assign, batch_assign then args. let's take the program above to give a concrete example.

After specifying the program we can do the following assignment:

assigned_program = (\n    program.assign(amplitude=15.7)\n    .batch_assign(run_time=np.linspace(0.05, 3.0, 101))\n    .args([\"detuning\"])\n)\n

First we assign the amplitude to a value of 15.7 which is going to be non-changing regardless of the other changing parameters in execution. Because we assign \u2018run_time\u2019 using batch_assign that means every time we execute this program we will run 101 tasks with the parameter sweep defined by the list of values. Finally args([\u201cdetuning\u201d]) implies that you must provide the value of the detuning as an argument when calling the execution method of the device. This is primarily useful for hybrid work flows or if you are simply just wanting to split your experiments into chunks defined by a different set of parameters. In conclusion, whether it is a hybrid algorithm or a parameter scan for your next fancy experiment Bloqade has you covered!

"},{"location":"drafts/aws_hybrid_execution/#visualization-tools","title":"Visualization Tools","text":"

While having a clean, readable syntax is great, there is always going to be the need for more visual representations. This is especially true for AHS programming where you think about things in terms of waveforms and atom configurations. As such, we provide visualization of your programs using Bokeh. Bokeh allows for very clean responsive interactive plots which we have implemented not only for AHS programs but AHS results as well!

We also provide some visualization of your AHS program in the terminal:

In [1]: from bloqade import start\n   ...:\n   ...: program = (\n   ...:     start.add_position((0, 0))\n   ...:     .add_position((0, \"r\"))\n   ...:     .rydberg.detuning.uniform.piecewise_linear([0.1, 1.2, 0.1], [-20, -20, 20, 20])\n   ...:     .amplitude.uniform.piecewise_linear([0.1, 1.2, 0.1], [0, 15, 15, 0])\n   ...:     .args([\"r\"])\n   ...: )\n\nIn [2]: program.parse_circuit()\nOut[2]:\nAnalogCircuit\n\u251c\u2500 register\n\u2502  \u21d2 AtomArrangement\n\u2502    \u251c\u2500 Location: filled\n\u2502    \u2502  \u251c\u2500 x\n\u2502    \u2502  \u2502  \u21d2 Literal: 0\n\u2502    \u2502  \u2514\u2500 y\n\u2502    \u2502     \u21d2 Literal: 0\n\u2502    \u2514\u2500 Location: filled\n\u2502       \u251c\u2500 x\n\u2502       \u2502  \u21d2 Literal: 0\n\u2502       \u2514\u2500 y\n\u2502          \u21d2 Variable: r\n\u2514\u2500 sequence\n   \u21d2 Sequence\n     \u2514\u2500 RydbergLevelCoupling\n        \u21d2 Pulse\n          \u251c\u2500 Detuning\n          \u2502  \u21d2 Field\n          \u2502    \u2514\u2500 Drive\n          \u2502       \u251c\u2500 modulation\n          \u2502       \u2502  \u21d2 UniformModulation\n          \u2502       \u2514\u2500 waveform\n          \u2502          \u21d2 Append\n          \u2502            \u251c\u2500 Linear\n          \u2502            \u2502  \u251c\u2500 start\n          \u2502            \u2502  \u2502  \u21d2 Literal: -20\n          \u2502            \u2502  \u251c\u2500 stop\n          \u2502            \u2502  \u2502  \u21d2 Literal: -20\n          \u2502            \u2502  \u2514\u2500 duration\n          \u2502            \u2502     \u21d2 Literal: 0.1\n          \u2502            \u251c\u2500 Linear\n          \u2502            \u2502  \u251c\u2500 start\n          \u2502            \u2502  \u2502  \u21d2 Literal: -20\n          \u2502            \u2502  \u251c\u2500 stop\n          \u2502            \u2502  \u2502  \u21d2 Literal: 20\n          \u2502            \u2502  \u2514\u2500 duration\n          \u2502            \u2502     \u21d2 Literal: 1.2\n          \u2502            \u2514\u2500 Linear\n          \u2502               \u251c\u2500 start\n          \u2502               \u2502  \u21d2 Literal: 20\n          \u2502               \u251c\u2500 stop\n          \u2502               \u2502  \u21d2 Literal: 20\n          \u2502               \u2514\u2500 duration\n          \u2502                  \u21d2 Literal: 0.1\n          \u2514\u2500 RabiFrequencyAmplitude\n             \u21d2 Field\n               \u2514\u2500 Drive\n                  \u251c\u2500 modulation\n                  \u2502  \u21d2 UniformModulation\n                  \u2514\u2500 waveform\n                     \u21d2 Append\n                       \u251c\u2500 Linear\n                       \u2502  \u251c\u2500 start\n                       \u2502  \u2502  \u21d2 Literal: 0\n                       \u2502  \u251c\u2500 stop\n                       \u2502  \u2502  \u21d2 Literal: 15\n                       \u2502  \u2514\u2500 duration\n                       \u2502     \u21d2 Literal: 0.1\n                       \u251c\u2500 Linear\n                       \u2502  \u251c\u2500 start\n                       \u2502  \u2502  \u21d2 Literal: 15\n                       \u2502  \u251c\u2500 stop\n                       \u2502  \u2502  \u21d2 Literal: 15\n                       \u2502  \u2514\u2500 duration\n                       \u2502     \u21d2 Literal: 1.2\n                       \u2514\u2500 Linear\n                          \u251c\u2500 start\n                          \u2502  \u21d2 Literal: 15\n                          \u251c\u2500 stop\n                          \u2502  \u21d2 Literal: 0\n                          \u2514\u2500 duration\n                             \u21d2 Literal: 0.1\n
"},{"location":"drafts/aws_hybrid_execution/#bloqade-emulator","title":"Bloqade Emulator","text":"

Anyone familiar with QuEra\u2019s Julia SDK Bloqade.jl knows that we\u2019re pretty obsessed with performance. We would also be remiss to advertise Bloqade\u2019s pure python emulator. While not as fast as Bloqade.jl, we have have worked to optimize our the state-vector simulator to get the most out of python as possible. The emulator supports both two and three level atom configurations, along with global and local driving and support for the blockade subspace (for those who are more familiar with Rydberg atoms). The blockade subspace and matrix calculations are nearly optimal for both memory and time and are written in pure NumPy and SciPy. We also have basic Numba JIT compiled sparse operations that further optimize the memory when solving the time-dependent Schr\u00f6dinger equation. We hope our python emulator will allow you to explore a wide variety of applications for neutral atoms and prototype some neat new algorithms with AHS.

Capitalize Bloqade across the entire article [name=jzlong]

"},{"location":"drafts/aws_hybrid_execution/#target-multiple-backends","title":"Target Multiple Backends","text":"

All Bloqade programs can be targeted to multiple emulation and hardware backends very easily, again using the chaining of .\u2019s. Also note that the chaining syntax allows Bloqade to let you know exactly when a program should be able to be run. To select braket as your service simply select the braket attribute of your program. At this stage there will be two methods availible for you, aquila and local_emulator.

I personally prefer to distinguish between methods and attributes by putting the () after a method name and omitting them for attributes [name=jzlong]

Each backend has different restrictions in terms of the types of AHS programs that can be run. During the alpha phase of Bloqade we will continue to improve the error messages that are given when targeting specific backends making it easy for you to diagnose any issues with your program execution.

Depending on the backend there are two or three methods for executing your program. For Cloud devices Bloqade has an API for both asynchronous (run_async) and synchronous (run) method for executing the job. Local emulator backends only support the run API.

Now let us revisit the meaning of args assignment. Every execution method has a args argument, this is where you can specify the values of the parameters defined in args when defining your program. The order of the arguments in the args tuple is the order of the variables specified in the args method.

a vs an? [name=jzlong]

Finally the backend object that gets created is also callable where the such that object(*args, shots=shots,...) is equivalent to object.run(shots, args=args, ...). While this is primarily a stylistic choice but this is an available interface for you if need be.

\"where the such that\" -> \"such that\" \"While this is primarily a stylistic choice but this is an available\" -> \"While this is primarily a stylicstic choice, this is also an available interface for you if need be\" [name=jzlong]

"},{"location":"drafts/aws_hybrid_execution/#job-management-features","title":"Job management Features","text":"

If you use the batch_assign API combined with your parameterized program it is possible to submit entire batches of AWS tasks. It's not enough to make programming AHS easy though; you also need to manage all the data that gets generated. Fear not, we have you covered. We know it can be quite cumbersome to have to manage hundreds or thousands of tasks so we have provided some useful functionality inside Bloqade to make your life easier as well as making experiments more reproducible.

Fear not, we have you covered! (worth adding an exclamation mark just to go the full nine yards with the chippy/enthusiastic tone). [name=jzlong]

One of the most common issues when running AHS (or just QC in general) is saving experimental results. When using a cloud device one also has the added difficultly of associating a task id with the parameter in the parameter scan as well as checking and fetching the task results as the tasks are completed. Bloqade provides a uniform format of saving this data which is useful for users to do sharable and reproducable work. To save your results simply invoke the save and load functions. We also provide dumps and loads if you want to work directly with strings instead of JSON files! It's as simple as:

capitalize ID [name=jzlong]

from bloqade import save, load\n\n# define program\n...\n\nbatch_task = my_program.braket.aquila().run_async(100)\n\nsave(batch_task, \u201cmy_aquila_results.json\u201d)\n\n# in some other file:\n\nloaded_batch_task = load(\u201cmy_aquila_results.json\u201d)\n\n# continue with analysis\n...\n

These objects will contain all the necessary information to fetch results and obtain the values of parameters used in your program.

Saving files isn\u2019t all that Bloqade offers. When dealing with a Cloud device like Aquila it is important to be able to manage those asynchronous tasks. Bloqade offers different ways to do this:

\"...isn't all that Bloqade offers.\" -> \"..isn't all that Bloqade offers to make your life easier.\"

  • batch_task.fetch() queries the current task statuses and fetches the results of completed tasks without blocking the python interpreter. The results are stored inside the current object.

  • batch_task.pull() Like fetch but waits until all tasks have been completed before unblocking the interpreter. The tasks the results are stored inside the current object.

  • batch_task.get_tasks(*status_codes) returns a new Batch object that contains the tasks with the status given by the inputs to the function

  • batch_task.remove_tasks(*status_codes) return a new Batch object that contains tasks that did not match the status code that have been given.

See the documentation for more details on what the various status codes are and what they mean.

"},{"location":"drafts/aws_hybrid_execution/#adaptive-workloads","title":"Adaptive workloads","text":"

As mentioned above, the ability to parameterize and assign values to your analog program means that there is a lot one can do in terms of running various kinds of experiments. Here we will discuss how to combine the parameterized pulses with braket\u2019s hybrid jobs!

In this case we can pass our parameterized pulse program into classical optimizer in order to provide classical feedback for the next quantum program to run. The use case we will cover here is a pretty standard problem that maps very neatly onto the AHS architecture implemented with Neutral atoms. Because of the Rydberg blockade effect the ground state of a collection of atoms maps exactly to what is called the Maximum Independent Set (MIS) problem on geometric graphs. The MIS problem is a graph coloring problem where the goal is to find the largest set of nodes in a graph such that no two nodes are connected by an edge. This problem is NP-hard and has many applications in scheduling, resource allocation, and even quantum error correction.

I also think it better to use the term \"Combinatorial Optimization\" over \"Graph Coloring\" considering CO might open a broader window in people's minds for what the machine is capable of than just \"Graph Coloring\". It would also be nice to have links to examples for each application (MIS for scheduling, resource allocation, QEC.)

We refer the reader to this Notebook example for a more detailed explanation of the problem and how it maps onto AHS. For this blog post we will focus on the implementation of the hybrid algorithm using Bloqade and Braket Hybrid Jobs.

Like most of the Bloqade programs we begin by importing the necessary components to build the program:

import numpy as np\nfrom bloqade import RB_C6, save, start, var\nfrom bloqade.atom_arrangement import Square\nfrom braket.devices import Devices\nfrom braket.aws import AwsDevice\n

using the AwsDevice we can get some information about the capabilities of the device. Note that Bloqade uses rad/us and us for energy and time units respectively while braket uses SI unites, e.g. rad/s and s, hence the conversion of units below.

# define the parameters of the experiment\ndevice = AwsDevice(Devices.QuEra.Aquila)\nrydberg_properties = device.properties.paradigm.rydberg.rydbergGlobal\n\n# Convert energy units to rad/us and time to us\ndetuning_max = float(rydberg_properties.detuningRange[1]) / 1e6\nrabi_max = float(rydberg_properties.rabiFrequencyRange[1]) / 1e6\ntotal_time = float(rydberg_properties.timeMax) * 1e6\n

For the particular problem we are studying we need to map the problem graph onto the atom arrangement. For more information we refer the reader to the notebook example.

# make sure next nearest neighbors are blockaded\nRmin = np.sqrt(2) # minimum unit disk radius\nRmax = 2  # maximum unit disk radius\nRudg = np.sqrt(Rmin * Rmax) # geometric mean of Rmin and Rmax\n\ndetuning_end = detuning_max / 4 # detuning at the end of the pulse\nblockade_radius = (RB_C6 / detuning_end) ** (1 / 6)\nlattice_spacing = blockade_radius / Rudg\n

Now we can define the program. We will use the var function to define the parameters for the program. Also the cost function will involve calculating the final energy of tha atoms which can be obtained from the geometry of the program via the rydberg_interaction method.

# define the time step for the detuning waveform\ndt = 0.4\nn_steps = int(total_time / dt)\n# create variables for the detuning time steps\ndetuning_vars = [var(f\"d{i}\") for i in range(n_steps - 1)]\n\n# define the lattice size before defect insertion\nL = 4\n# set seed for defect generation\nrng = np.random.default_rng(1337)\n# define the geometry of the program\ngeometry = (\n    Square(L, lattice_spacing)\n    .apply_defect_count(L**2 // 2, rng=rng)\n    .remove_vacant_sites()\n)\n# define the program\nprogram = (\n    geometry.rydberg.detuning.uniform.piecewise_linear(\n        n_steps * [dt], [-detuning_end] + detuning_vars + [detuning_end]\n    )\n    .amplitude.uniform.piecewise_linear(\n        [0.1, total_time - 0.2, 0.1], [0, rabi_max, rabi_max, 0]\n    )\n)\n# get the interaction matrix\nV_ij = geometry.interaction_matrix()\n

We need to build the infrastructure to do the hybrid job. There are many different tools availible in braket.jobs that allow you to log the progress of your hybrid algorithm. Here we set up a simple class that wraps the cost function and log the progress of the algorithm.

from braket.jobs import (\n    InstanceConfig,\n    hybrid_job,\n    save_job_checkpoint,\n)\nfrom braket.jobs.metrics import log_metric\nfrom braket.jobs_data import PersistedJobDataFormat\nfrom braket.tracking import Tracker\n\n# define a wrapper for the cost function for reporting\nclass CostFuncWrapper:\n    def __init__(self, backend, shots=10, **options):\n        self.backend = backend\n        self.options = options\n        self.iterations = 0\n        self.shots = shots\n        self.prev_calls = {}\n        self.task_tracker = Tracker().start()\n\n    @staticmethod\n    def cost_func(report):\n        bitstrings = 1 - np.asarray(report.bitstrings(False))\n        detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n        interaction_energies = np.einsum(\n            \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n        )\n\n        total_energy = detuning_energies + interaction_energies\n        # minimize the energy mean and standard deviation\n        return total_energy.mean() + total_energy.std()\n\n    def __call__(self, x):\n        args = tuple(x)\n\n        batch_task = self.backend.run(self.shots, args=args, **self.options)\n        report = batch_task.report()\n\n        save(batch_task, f\"my-aquila_results-{self.iterations}.json\")\n\n        self.prev_calls[args] = report\n        return self.cost_func(report)\n\n    def callback(self, state):\n        args = tuple(state.x)\n        self.iterations += 1\n        bitstrings = 1 - np.asarray(self.prev_calls[args].bitstrings(False))\n        detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n\n        interaction_energies = np.einsum(\n            \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n        )\n\n        total_energy = detuning_energies + interaction_energies\n        mean_energy = total_energy.mean()\n        std_energy = total_energy.std()\n\n        # Log metrics to display in Braket Console\n        log_metric(\n            iteration_number=self.iterations, value=state.fun, metric_name=\"loss\"\n        )\n        log_metric(\n            iteration_number=self.iterations,\n            value=mean_energy,\n            metric_name=\"mean energy\",\n        )\n        log_metric(\n            iteration_number=self.iterations,\n            value=std_energy,\n            metric_name=\"std energy\",\n        )\n\n        # Also track the quantum task cost for Braket devices\n        braket_task_cost = float(\n            self.task_tracker.qpu_tasks_cost()\n            + self.task_tracker.simulator_tasks_cost()\n        )\n        log_metric(\n            metric_name=\"braket_cost\",\n            value=braket_task_cost,\n            iteration_number=self.iterations,\n        )\n\n        # Save a checkpoint to resume the hybrid job from where you left off\n        checkpoint_data = {\"i\": self.iterations, \"args\": args}\n        save_job_checkpoint(\n            checkpoint_data, data_format=PersistedJobDataFormat.PICKLED_V4\n        )\n        # end the job if the std energy is less than 5% of the mean energy\n        # this indicates that the system is close to the ground state\n        return abs(std_energy / mean_energy) < 0.05\n

While this is a lot to take in let us go through some important things. Firstly, you can generate a Report object from a Bloqade batch of tasks. This object provides some basic analysis methods common to Neutral atom AHS computing. The one we make the most use of here is the bitstrings method which return a list of arrays that contains the shot results after executing the program. It takes a boolean argument that specifies whether or not to return the bitstrings of shots where there were atoms that did not get put into a trap before the computation was executed. By default this filter is applied, but for this problem we want to include those shots in our analysis hence the False argument.

No need to capitalize \"Batch\" unless you want to refer to the object type You can just say AHS, AHS Computing seems redundant Do we assume users know what a trap is? Might be worth having a sentence or two, just say something like \"It takes a boolean argument that specifies whether or not to return the bitstrings of shots where there were atoms that did not successfully get put into position...\"

Another thing to note is that our cost function not only contains the mean energy but also the standard deviation. The reason for this is because we are targeting an eigenstate of the final Hamiltonian which has no energy variance. This is a good way to check if the system is in the ground state. We use the ratio of the standard deviation to the mean energy as a stopping condition for the algorithm.

Finally, we have a callback function that is called after each iteration of the optimizer. This is where we can log the progress of the algorithm. We use the Tracker object to track the cost of the quantum tasks that are being executed. We also use the log_metric function to log the mean and standard deviation of the energy as well as the cost function of the quantum tasks.

Now we can define the function that will run the hybrid job.

def run_algo(assigned_program, device_arn=None, n_calls=10, shots=10):\n    @hybrid_job(\n        device=device_arn,  # Which device to get priority access to\n        dependencies=\"requirements.txt\",  # install bloqade\n        instance_config=InstanceConfig(\"ml.m5.large\"),\n    )\n    def _runner(backend, shots, n_calls, **options):\n        from skopt import gp_minimize\n\n        # Braket task cost\n        wrapped_cost_func = CostFuncWrapper(backend, shots=shots, **options)\n\n        n_params = len(backend.params.args_list)\n        bounds = n_params * [(-detuning_max, detuning_max)]\n\n        result = gp_minimize(\n            wrapped_cost_func,\n            bounds,\n            callback=wrapped_cost_func.callback,\n            n_calls=n_calls,\n        )\n\n        detuning_values = {var.name: val for var, val in zip(detuning_vars, result.x)}\n\n        return detuning_values\n\n    if device_arn == Devices.QuEra.Aquila:  # use Aquila\n        backend = assigned_program.braket.aquila()\n        options = dict()\n    else:  # use  bloqade emulator\n        backend = assigned_program.bloqade.python()\n        options = dict(atol=1e-8, rtol=1e-4, solver_name=\"dopri5\")\n\n    # Run the hybrid job\n    return _runner(backend, n_calls, shots, **options)\n

We use the hybrid_job decorator to define the hybrid job. This decorator takes a device_arn argument which is the Amazon Resource Name (ARN) of the device you want to run the hybrid job on. There are some other options associated with the EC2 instance that is used to run the hybrid job. We will use the InstanceConfig object to specify the instance type and number of instances to use. dependencies points to a text file that contains the python dependencies needed to run this hybrid job. In this case we need bloqade and scikit-optimize in this text file.

The function _runner that is being decorated must take the backend as the first argument. This object is generated by the Bloqade API and must match the device arn that is specified in the decorator. n_calls is the total number of iterations of the optimizer and shots is the number of shots to use for each iteration. options is a dictionary of options that are passed to the run method of the backend. The return value of the function is the final value of the parameters that were optimized. We wrap the _runner inside run_algo that takes the program and the device_arn as arguments to make sure that the device requested by hybrid_jobs matches the device called by bloqade as well as setting up specialized options for the different bloqade backends.

To run the hybrid job we simply call the run_algo function with the assigned program and the device arn.

optimal_parameters = run_algo(program.args(detuning_vars), device_arn=Devices.QuEra.Aquila)\n

Finally we can plot the results of the hybrid job.

assigned_program = program.assign(**optimal_params)\nassigned_program.show()\n

To run the algorithm with Bloqade emulator we simply change the device arn to None.

Full source code:

```python= import numpy as np from bloqade import RB_C6, save, start, var from bloqade.atom_arrangement import Square from braket.devices import Devices from braket.aws import AwsDevice

"},{"location":"drafts/aws_hybrid_execution/#define-the-parameters-of-the-experiment","title":"define the parameters of the experiment","text":"

device = AwsDevice(Devices.QuEra.Aquila) rydberg_properties = device.properties.paradigm.rydberg.rydbergGlobal

"},{"location":"drafts/aws_hybrid_execution/#convert-energy-units-to-radus-and-time-to-us","title":"Convert energy units to rad/us and time to us","text":"

detuning_max = float(rydberg_properties.detuningRange[1]) / 1e6 rabi_max = float(rydberg_properties.rabiFrequencyRange[1]) / 1e6 total_time = float(rydberg_properties.timeMax) * 1e6

"},{"location":"drafts/aws_hybrid_execution/#make-sure-next-nearest-neighbors-are-blockaded","title":"make sure next nearest neighbors are blockaded","text":""},{"location":"drafts/aws_hybrid_execution/#unit-disk-minimum-and-maximum-radii","title":"unit disk minimum and maximum radii","text":"

Rmin = np.sqrt(2) # minimum unit disk radius Rmax = 2 # maximum unit disk radius

"},{"location":"drafts/aws_hybrid_execution/#choose-geometric-mean-of-rmin-and-rmax","title":"choose geometric mean of Rmin and Rmax","text":"

Rudg = np.sqrt(Rmin * Rmax)

detuning_end = detuning_max / 4 blockade_radius = (RB_C6 / detuning_end) ** (1 / 6) lattice_spacing = blockade_radius / Rudg

"},{"location":"drafts/aws_hybrid_execution/#define-the-time-step-for-the-detuning-waveform","title":"define the time step for the detuning waveform","text":"

dt = 0.4 n_steps = int(total_time / dt)

"},{"location":"drafts/aws_hybrid_execution/#create-variables-for-the-detuning-time-steps","title":"create variables for the detuning time steps","text":"

detuning_vars = [var(f\"d{i}\") for i in range(n_steps - 1)]

"},{"location":"drafts/aws_hybrid_execution/#define-the-lattice-size-before-defect-insertion","title":"define the lattice size before defect insertion","text":"

L = 4

"},{"location":"drafts/aws_hybrid_execution/#set-seed-for-geometry-generation","title":"set seed for geometry generation","text":"

rng = np.random.default_rng(1337) program = ( Square(L, lattice_spacing) .apply_defect_count(L**2 // 2, rng=rng) .remove_vacant_sites() .rydberg.detuning.uniform.piecewise_linear( n_steps * [dt], [-detuning_end] + detuning_vars + [detuning_end] ) .amplitude.uniform.piecewise_linear( [0.1, total_time - 0.2, 0.1], [0, rabi_max, rabi_max, 0] ) )

"},{"location":"drafts/aws_hybrid_execution/#get-atom-register-and-interaction-matrix","title":"get atom register and interaction matrix","text":"

V_ij = program.parse_register().rydberg_interaction()

from braket.jobs import ( InstanceConfig, hybrid_job, save_job_checkpoint, ) from braket.jobs.metrics import log_metric from braket.jobs_data import PersistedJobDataFormat from braket.tracking import Tracker

"},{"location":"drafts/aws_hybrid_execution/#define-a-wrapper-for-the-cost-function-for-reporting","title":"define a wrapper for the cost function for reporting","text":"

class CostFuncWrapper: def init(self, cost_func, backend, shots=10, **options): self.backend = backend self.options = options self.iterations = 0 self.shots = shots self.prev_calls = {} self.task_tracker = Tracker().start()

@staticmethod\ndef cost_func(report):\n    bitstrings = 1 - np.asarray(report.bitstrings(False))\n    detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n    interaction_energies = np.einsum(\n        \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n    )\n\n    total_energy = detuning_energies + interaction_energies\n    # minimize the energy mean and standard deviation\n    return total_energy.mean() + total_energy.std()\n\ndef __call__(self, x):\n    args = tuple(x)\n\n    batch_task = self.backend.run(self.shots, args=args, **self.options)\n    report = batch_task.report()\n\n    save(batch_task, f\"my-aquila_results-{self.iterations}.json\")\n\n    self.prev_calls[args] = report\n    return self.cost_func(report)\n\ndef callback(self, state):\n    args = tuple(state.x)\n    self.iterations += 1\n    bitstrings = 1 - np.asarray(self.prev_calls[args].bitstrings(False))\n    detuning_energies = -detuning_end * bitstrings.sum(axis=-1)\n\n    interaction_energies = np.einsum(\n        \"ij, ...i, ...j-> ...\", V_ij, bitstrings, bitstrings\n    )\n\n    total_energy = detuning_energies + interaction_energies\n    mean_energy = total_energy.mean()\n    std_energy = total_energy.std()\n\n    # Log metrics to display in Braket Console\n    log_metric(\n        iteration_number=self.iterations, value=state.fun, metric_name=\"loss\"\n    )\n    log_metric(\n        iteration_number=self.iterations,\n        value=mean_energy,\n        metric_name=\"mean energy\",\n    )\n    log_metric(\n        iteration_number=self.iterations,\n        value=std_energy,\n        metric_name=\"std energy\",\n    )\n\n    # Also track the quantum task cost for Braket devices\n    braket_task_cost = float(\n        self.task_tracker.qpu_tasks_cost()\n        + self.task_tracker.simulator_tasks_cost()\n    )\n    log_metric(\n        metric_name=\"braket_cost\",\n        value=braket_task_cost,\n        iteration_number=self.iterations,\n    )\n\n    # Save a checkpoint to resume the hybrid job from where you left off\n    checkpoint_data = {\"i\": self.iterations, \"args\": args}\n    save_job_checkpoint(\n        checkpoint_data, data_format=PersistedJobDataFormat.PICKLED_V4\n    )\n    # end the job if the std energy is less than 5% of the mean energy\n    # this indicates that the system is close to the ground state\n    return abs(std_energy / mean_energy) < 0.05\n

def run_algo(assigned_program, device_arn=None, n_calls=10, shots=10): @hybrid_job( device=device_arn, # Which device to get priority access to dependencies=\"requirements.txt\", # install bloqade instance_config=InstanceConfig(\"ml.m5.large\"), ) def _runner(backend, shots, n_calls, **options): from skopt import gp_minimize

    # Braket task cost\n    wrapped_cost_func = CostFuncWrapper(backend, shots=shots, **options)\n\n    n_params = len(backend.params.args_list)\n    bounds = n_params * [(-detuning_max, detuning_max)]\n\n    result = gp_minimize(\n        wrapped_cost_func,\n        bounds,\n        callback=wrapped_cost_func.callback,\n        n_calls=n_calls,\n    )\n\n    detuning_values = {var.name: val for var, val in zip(detuning_vars, result.x)}\n\n    return detuning_values\n\nif device_arn == Devices.QuEra.Aquila:  # use Aquila\n    backend = assigned_program.braket.aquila()\n    options = dict()\nelse:  # use  bloqade emulator\n    backend = assigned_program.bloqade.python()\n    options = dict(atol=1e-8, rtol=1e-4, solver_name=\"dopri5\")\n\n# Run the hybrid job\nreturn _runner(backend, n_calls, shots, **options)\n
"},{"location":"drafts/aws_hybrid_execution/#optimal_params-run_algoprogramargsdetuning_vars-devicesqueraaquila","title":"optimal_params = run_algo(program.args(detuning_vars), Devices.QuEra.Aquila)","text":"

optimal_params = run_algo(program.args(detuning_vars), None, n_calls=10, shots=10)

assigned_program = program.assign(**optimal_params) assigned_program.show()

```

"},{"location":"home/advanced_usage/","title":"Advanced Usage","text":""},{"location":"home/advanced_usage/#geometry-concepts","title":"Geometry Concepts","text":"

In Getting Started, we discussed the parameterization of waveforms inside a Rydberg/hyperfine drive. We have also provided infrastructure that allows you to parameterize the position of the atoms/sites in your atom array. There are a few methods to do this:

"},{"location":"home/advanced_usage/#scaling-your-lattice","title":"Scaling Your Lattice","text":"

Every AtomArrangement object has an option to scale, which applies a multiplicative factor to all site coordinates. To parameterize this scaling, you can either pass a literal value, a string, or a scalar expression:

new_geometry_1 = my_geometry.scale(1.0)\nnew_geometry_2 = my_geometry.scale('scale_factor')\n\nscale_expr = var(\"param\") / 2 + var(\"starting_scale\")\nnew_geometry_3 = my_geometry.scale(scalar_expr)\n
"},{"location":"home/advanced_usage/#parameterize-the-position-of-individual-sites","title":"Parameterize the Position of Individual Sites","text":"

Suppose you are constructing a set of sites using the start.add_position(...) method. In that case, you can pass a literal value, a string, or a scalar expression to the position argument:

x_div_2 = var(\"x\") / 2\ny_dic_2 = var(\"y\") / 2\nstart.add_position([(0, 1), (\"x\", \"y\"), (x_div_2, y_div_2)])\n
"},{"location":"home/advanced_usage/#parallelize-over-space","title":"Parallelize Over Space","text":"

For AHS programs with a few sites/atoms, you can (and should) make full use of the user area allowed by QuEra's AHS devices. While the Rydberg interaction between the atoms decays as a power-law, it decays fast enough for you to separate small clusters of atom sites cleanly and be assured they will not interact and entangle. This fact allows you to parallelize your program over space. We have added this functionality into Bloqade by simply calling the .parallelize(spacing) method, where spacing is a real value, the spacing between the bounding boxes of the clusters. For example, calling .parallelize(20.0) would ensure that the atoms in other clusters are at least 20 um away from each other.

When fetching the results and generating the report, the shot results will be organized by the cluster to which they belong. The methods of the Report object will always merge all the cluster shots so you can analyze them as if you're analyzing a single cluster.

"},{"location":"home/advanced_usage/#programming-a-local-drive","title":"Programming A Local Drive","text":"

In the Getting Started section, you might have noticed that we used ...uniform... when defining the various drives applied to the atoms. This syntax refers to one of three ways of expressing the spatial modulation of the waveform. What do we mean by spatial modulation? In this case, you can think of a drive having a temporal component, e.g., the waveform, and a spatial part, which we call spatial modulation. The spatial modulation is a scale factor applied to the waveform when acting on a particular site. For example, if we have two atoms and we have a spatial modulation for site-0 which is 0.1 and for site-1 which is 0.2, then the waveform will be scaled by 0.1 when acting on-site-0 and 0.2 when applied to site-1.

Intuitively, uniform spatial modulation simply means that regardless of the atom, the waveform value is always the same. The other two options for defining spatial modulations are: scale and location. They have two distinct use cases:

"},{"location":"home/advanced_usage/#scale-method","title":"scale Method","text":"

When calling the scale method, you can pass either a list of real values or a string. The list of values defines the scaling for every atom in the system, so the number of elements must equal the number of sites in your geometry. The string is a placeholder that allows you to define that mask as a list of values by passing them in through assign, batch_assign, or args. For assign and batch_assign, you can pass a list or a list of lists, respectively. On the other hand, args, the list of values, must be inserted into the args tuple when calling run or run_async, for example, let's take a two-atom program:

from bloqade import start\n\nprogram = (\n    start.add_position([(0, 0), (0, 5)])\n    .rydberg.detuning.scale(\"local_detuning\")\n    .piecewise_linear([0.1, 1.0, 0.1], [0, \"detuning\", \"detuning\", 0])\n    .amplitude.uniform.piecewise_linear([0.1, 1.0, 0.1], [0, 15, 15, 0])\n    .args([\"local_detuning\", \"detuning\"])\n    .bloqade.python()\n)\n

To call the run method properly, you must unpack the spatial modulation list into the tuple, e.g.

local_detuning = [0.4, 0.1]\n\nprogram.run(100, args=(*local_detuning, 30))\n
"},{"location":"home/advanced_usage/#location-method","title":"location Method","text":"

The location method takes two arguments; the second is optional. The first argument is an integer or a list of integers corresponding to the site(s) to which you want to apply the spatial modulation. The second argument is the value you wish to apply to the site(s). If you pass a list of integers, you must pass a list of values for the second argument, while if you pass a single integer, you must pass a single value. The interpretation is that for any sites not specified in the first argument, the spatial modulation is 0.0. Note that the values you pass in can be a real value, a string (for a variable), or a scalar expression. Below is an example using location:

program = (\n    start.add_position([(0, 0), (0, 5)])\n    .rydberg.detuning.location([0, 1], [\"detuning_0\", \"detuning_1\"])\n    .piecewise_linear([0.1, 1.0, 0.1], [0, \"detuning\", \"detuning\", 0])\n    .amplitude.location(0)\n    .piecewise_linear([0.1, 1.0, 0.1], [0, 15, 15, 0])\n    .location(0, \"rabi_scale\")\n    .piecewise_linear([0.1, 1.0], [0, 15, 0])\n)\n
"},{"location":"home/advanced_usage/#slicing-waveforms","title":"Slicing Waveforms","text":"

Sometimes, you want to run a program at intermediate time points as a parameter scan. To facilitate this, we have added the slice method that can be called during the waveform construction. The slice method takes two arguments: start and stop. The start argument determines the new starting point for the sliced waveform. At the same time, stop specifies the new stopping time for the waveform. A valid slice must have start <= stop, start >= 0, and stop <= duration, where duration is the duration of the waveform. Both arguments can be Scalar expressions as well as concrete values.

record is another useful feature we have implemented to be used with slice. For example, if you slice a waveform, you may or may not know the value of the waveform at the end of the resulting waveform from the slice. On the other hand, you need to be able to use that slice as a base for a new waveform that is continuous, which requires knowledge of the value of the sliced waveform at the end of the slice. A common use case for this is to take a Rabi drive and ensure it is 0 at the end of the protocol to make it compatible with hardware. A great example of this exact use case is Quantum Scar Dynamics Tutorial, which goes through exactly how to use slice and record together.

"},{"location":"home/advanced_usage/#python-functions-as-waveforms","title":"Python Functions as Waveforms","text":"

In Bloqade, we provide a set of commonly used waveform shapes for hardware applications. However, we also allow users to pass various Python functions as Waveforms! There are two ways to go about using Python functions as waveforms. The first involves passing the function into the fn() method when building your program. The second involves the bloqade.waveform decorator on your function, turning it into a waveform object.

These are super convenient for emulation. However, these waveforms can't be executed on quantum hardware. As such, we provide the sample method that allows you to easily discretize your waveform into a form compatible with the hardware. This method replaces your waveform with a piecewise linear or piecewise constant form. The Rabi amplitude and detuning both expect piecewise linear while the phase expects piecewise constant for the waveforms. If you are using the builder method to build your program, you do not need to specify the kind of interpolation explicitly; Bloqade can deduce from the build what type of interpolation to do, but if you are using the waveform decoration, you need to specify the kind of interpolation, either 'linear' or 'constant'.

An example on Floquet Dynamics is an excellent place to start with these particular features.

"},{"location":"home/getting_started/","title":"Getting Started","text":"

This page is an excellent place to start for those familiar with the Neutral Atoms AHS model. For those who are not, we recommend you read our Bloqade 101 tutorial. Here, we will go through how to define your AHS program for two and three-level schemes. The beginning of your program starts with the atom geometry. You can build it as a list of coordinates or by using some pre-defined Bravais Lattices found in bloqade.atom_arrangements. For example, to define a simple 2x2 square lattice, you can do the following:

from bloqade.atom_arrangement import Square\n\nprogram = (\n    Square(2, 2, lattice_spacing=6.0)\n)\n
The analog pulse sequence is defined through the . syntax starting with which level coupling to drive rydberg or hyperfine. Next, specify the detuning, rabi.amplitude, and rabi.phase. After this, you specify the spatial modulation of that waveform, e.g. the relative scale factor that each atom feels from a given waveform. Finally, you select the temporal modulation of the waveform. You can build the pulses in various ways. Because we use the . to split up the different parts of the pulse program, Bloqade will only give you valid options for the next part of the pulse program. For example, to define a simple two-level Rabi drive with a detuning:

from bloqade import start\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(10, 1.1)\n    .amplitude.uniform.constant(15, 1.1)\n)\n

There are some helpful shortcuts for generating piecewise linear and piecewise constant waveforms. For example, to define a piecewise linear Rabi amplitude that starts at 0 ramps up to 15 rad/us, stays at 15 rad/us for 1.1 us, and then ramps back down to 0 we just need two lists. The first list is the durations of each linear segment in us, and the second is the Rabi amplitude in rad/us. The i-th element in durations is the duration between values[i] and values[i+1].

from bloqade import start\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(10, 1.1)\n    .amplitude.uniform.piecewise_linear([0.05, 1.0, 0.05], [0, 15, 15, 0])\n)\n

Note that because rydberg.detuning precedes amplitude, we do not need to specify rabi.amplitude. If we flip the order, we need to put rabi in the chain of dots. To run your program, you can select the backend you want to target:

emulation_result = program.bloqade.python().run(100)\nhardware_result = program.braket.aquila().run_async(100)\n
here, run_async denotes that the function call is asynchronous, meaning that the function will return immediately, and the result will be a future-like object that will handle retrieving the results from the cloud. You can also call run, but this will block Python until the results from the QPU have been completed. For more on this, see our Tutorials.

It is easy to add hyperfine drives to your program. Select your program's .hyperfine property to start building the hyperfine pulse sequence. By selecting the rydberg and hyperfine properties, you can also switch back and forth between the different kinds of drives. To tell what kind of drive is being built, follow the string of options back to the first instance you find of either rydberg or hyperfine. Looking back also determines if the drive acts as the detuning, rabi.amplitude, and rabi.phase.

"},{"location":"home/getting_started/#parameterized-programs","title":"Parameterized Programs","text":"

This is all very nice, but tracking individual tasks when doing parameter scans is annoying. Bloqade takes care of this by allowing you to parameterize the pulse sequences. For example, we want to sweep over the Rabi drive's drive time. In that case, you can insert strings into the fields to turn those into variables or make an explicit variable object:

from bloqade import start, var\n\nrun_time = var(\"run_time\")\n\nprogram = (\n    start.add_position((0, 0))\n    .rydberg.detuning.uniform.constant(10, run_time + 0.1)\n    .amplitude.uniform.piecewise_linear([0.05, run_time, 0.05], [0, 15, 15, 0])\n)\n

here we use a variable run_time, which denotes the length of the rabi drive \"plateau.\" These variables support simple arithmetic such as +, -, *, and/, as shown in the previous code example, which we used to define the duration of the detuning waveform in the last example code. To define a parameter scan, simply use the batch_assign method before calling the execution backend:

result = (\n    program.batch_assign(run_time=[0.1, 0.2, 0.3, 0.4, 0.5])\n    .bloqade.python()\n    .run(100)\n)\n

There are also other methods available to assign the parameter; for example, if we do not know the values of the parameters we would like to run in a particular task, we can use the args method to specify that \"run_time\" will be assigned when calling the run or run_async methods. This function takes a list as an input, and the order of the names in the list corresponds to the order in the variables that need to be specified during the call of run. For example, let's say our program has two parameters, \"a\" and \"b\". We can specify both of these parameters as runtime assigned:

assigned_program = program.args([\"a\", \"b\"])\n
Now when you execute this program you need to specify the values of \"a\" and \"b\" in the run method:

result = assigned_program.bloqade.python().run(100, args=(1, 2))\n
where args argument is a tuple of the values of \"a\" and \"b\" respectively.

There is also an assign(var1=value1, var2=value2, ...) method which is useful if you are given a program that is imported from another package or comes from a source which you should not edit directly. In this case you can use the assign method to assign the value of the parameters for every task execution that happens.

"},{"location":"home/getting_started/#analyzing-results","title":"Analyzing Results","text":""},{"location":"home/getting_started/#batch-objects","title":"Batch Objects","text":"

Now that you have your program, we need to analyze the results. The results come in either a RemoteBatch and LocalBatch. RemoteBatch objects are returned from any execution that calls a remote backend, e.g. braket.aquila(). In contrast, LocalBatch is returned by local emulation backends, e.g., bloqade.python(), or braket.local_emulator(). The only difference between RemoteBatch and LocalBatch is that RemoteBatch has extra methods that you can use to fetch remote results, check the status of remote tasks, and filter based on the task status. Some things to note about RemoteBatch objects:

  • Filtering is applied based on the tasks' current known status. If you filter based on the current status, you can precede the filter method with a result.fetch() call, e.g., completed_results = results.fetch().get_completed_tasks().
  • The pull() method will wait until all tasks have stopped running, e.g., tasks that are completed, failed, or canceled, before continuing execution of your Python code. This functionality is helpful for hybrid tasks where your classical step can only happen once the quantum task(s) have finished.

You must have active credentials for fetch() and pull() to run without an exception. Finally, batch objects can be saved/loaded as JSON via bloqade.save, bloqade.dumps, bloqade.load, and bloqade.loads.

"},{"location":"home/getting_started/#report-objects","title":"Report Objects","text":"

Both RemoteBatch and LocalBatch objects support a report() method that will take any task data and package it up into a new object that is useful for various kinds of analysis. The three main modes of analysis are:

report.bitstrings(filter_perfect_filling=True)\nreport.rydberg_densities(filter_perfect_filling=True)\nreport.counts(filter_perfect_filling=True)\n

During the program execution on the hardware atoms may sometimes not end up in every specified site. Thus each shot has a pre and post-sequence measurement of the atoms. In specific applications, having a missing atom can mean your computation will not give the correct results, so it is helpful to filter out shots that are not perfectly filled using the boolean option in all three methods. Below, we summarize the different methods and what they return:

  1. bitstrings is a method that returns a list of numpy arrays where each array is a (shots, num_sites) array of 0 or 1. Note that 0 corresponds to the Rydberg state while one corresponds to the ground state
  2. rydberg_densities is a method that returns a Pandas Series object that is an average over the shots and gives the probability of each atom being in the Rydberg state over every single task in the report.
  3. counts is a method that returns a list of ordered dictionaries where the keys are the bitstrings as a string, and the values are the number of times that bitstring was observed in the shots.

Another helpful method is report.list_param(param_string), which returns a list of values for the particular parameter given as a string in the function's input. This data is useful for plotting parameter scans. For example, if we want to plot the Rydberg density as a function of the Rabi drive time we can do the following:

from bloqade import start\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nrun_times = np.linspace(0,1,51)\n\nreport = (\n    start.add_position((0, 0))\n    .add_position((0, 5.0))\n    .rydberg.detuning.uniform.constant(10, \"run_time\")\n    .amplitude.uniform.constant(15, \"run_time\")\n    .batch_assign(run_time=run_times)\n    .bloqade.python().run(1000).report()\n)\n\ntimes = report.list_param(\"run_time\")\ndensities = report.rydberg_densities(filter_perfect_filling=True)\n\nplt.plot(times, densities)\nplt.xlabel(\"Rabi Drive Time (us)\")\nplt.ylabel(\"Rydberg Density\")\nplt.show()\n

This concludes the intermediate tutorial, for more advanced usage see our Advanced Usage tutorial.

"},{"location":"home/gotchas/","title":"Bloqade Gotchas: Common Mistakes in Using Bloqade","text":"

It is tempting when coming from different quantum SDKs and frameworks to apply the same pattern of thought to Bloqade. However, a lot of practices from those prior tools end up being anti-patterns in Bloqade. While you can use those patterns and they can still work, it ends up causing you the developer to write unnecessarily verbose, complex, and hard-to-read code as well as preventing you from reaping the full benefits of what Bloqade has to offer.

This page is dedicated to cataloguing those anti-patterns and what you can do instead to maximize the benefit Bloqade can offer you.

"},{"location":"home/gotchas/#redefining-lattices-and-common-atom-arrangements","title":"Redefining Lattices and Common Atom Arrangements","text":"

You might be tempted to define lattice-based geometries through the following means:

from bloqade import start\n\nspacing = 4.0\ngeometry = start.add_positions(\n    [(i * spacing, j * spacing) for i in range(4) for j in range(4)]\n)\n

This is quite redundant and verbose, especially considering Bloqade offers a large number of pre-defined lattices you can customize the spacing of in bloqade.atom_arrangement. In the code above, we're just defining a 4x4 square lattice of atoms with 4.0 micrometers of spacing between them. This can be expressed as follows

from bloqade.atom_arrangement import Square\n\nspacing = 4.0\ngeometry = Square(4, lattice_spacing = spacing)\n
"},{"location":"home/gotchas/#copying-a-program-to-create-new-ones","title":"Copying a Program to create New Ones","text":"

Many gate-based SDKs rely on having a mutable object representing your circuit. This means if you want to build on top of some base circuit without mutating it, you have to copy it:

import copy\n\nbase_circuit = qubits.x(0)....\n# make copy of base circuit\ncustom_circuit_1 = copy(base_circuit)\n# build on top of copy of base circuit\ncustom_circuit_1.x(0).z(5)...\n# create a new circuit by copying the base again\ncustom_circuit_2 = copy(base_circuit)\n# build on top of that copy again\ncustom_circuit_2.y(5).cz(0,2)...\n

In Bloqade Python this is unnecessary because at every step of your program an immutable object is returned which means you can save it and not have to worry about mutating any internal state.

from bloqade import start\nbase_program = start.add_position((0,0)).rydberg.rabi.amplitude.uniform\n# Just recycle your base program! No `copy` needed!\nnew_program_1 = base_program.constant(duration=5.0, value=5.0)\nnew_program_2 = base_program.piecewise_linear(\n    durations=[5.0], values = [0.0, 5.0]\n)\n
"},{"location":"home/gotchas/#creating-new-programs-instead-of-using-batch_assign","title":"Creating New Programs Instead of Using .batch_assign","text":"

If you have a set of parameters you'd like to test your program on versus a single parameter, don't generate a new program for each value:

rabi_values = [2.0, 4.7, 6.1]\nprograms_with_different_rabi_values = []\n\nfor rabi_value in rabi_values:\n    program = start.add_position((0, 0)).rydberg.rabi.amplitude.uniform.constant(\n        duration=5.0, value=rabi_value\n    )\n    programs_with_different_rabi_values.append(program)\n\nresults = []\n\nfor program in programs_with_different_rabi_values:\n    result = program.bloqade.python().run(100)\n    results.append(result)\n

Instead take advantage of the fact Bloqade has facilities specifically designed to make trying out multiple values in your program without needing to make individual copies via .batch_assign. The results are also automatically handled for you so each value you test has its own set of results, but all collected in a singular dataframe versus the above where you'd have to keep track of individual results.

rabi_values = [2.0, 4.7, 6.1]\n# place a variable for the Rabi Value and then batch assign values to it\nprogram_with_rabi_values = start.add_position(\n    0, 0\n).rydberg.rabi.amplitude.uniform.constant(duration=5.0, value=\"rabi_value\")\nprogram_with_assignments = program_with_rabi_values.batch_assign(\n    rabi_value=rabi_values\n)\n\n# get your results in one dataframe versus having to keep track of a\n# bunch of individual programs and their individual results\nbatch = program_with_assignments.bloqade.python().run(100)\nresults_dataframe = batch.report().dataframe\n
"},{"location":"home/quick_start/","title":"Quickstart","text":""},{"location":"home/quick_start/#quick-start","title":"Quick Start","text":"

All the sections below are self-contained, you can click on the links in the Table of Contents to read the relevant parts.

"},{"location":"home/quick_start/#navigating-the-bloqade-api","title":"Navigating the Bloqade API","text":"

As you develop your Bloqade program, you are expected to rely on pop-up \"hints\" provided in your development environment to help you determine what the next part of your program should be.

"},{"location":"home/quick_start/#vs-code","title":"VS Code","text":"

In VS Code this is automatic, just type the . and see what options pop up:

"},{"location":"home/quick_start/#jetbrains-pycharm","title":"JetBrains PyCharm","text":"

The same goes for JetBrains PyCharm:

"},{"location":"home/quick_start/#jupyter-notebook","title":"Jupyter Notebook","text":"

In a Jupyter Notebook you'll need to type . and then hit tab for the hints to appear:

"},{"location":"home/quick_start/#ipython","title":"IPython","text":"

The same goes for IPython:

"},{"location":"home/quick_start/#defining-atom-geometry","title":"Defining Atom Geometry","text":"

You can import pre-defined geometries based on Bravais lattices from bloqade.atom_arrangement. You may also specify a lattice spacing which dictates the spacing between the atoms as well as the number of atom sites in a certain direction.

from bloqade.atom_arrangement import Square, Kagome\n\nsimple_geometry = Square(2, 4, lattice_spacing = 4.0)\nmore_complex_geometry = Kagome(2, 2, lattice_spacing = 2.0)\n

You can easily visualize your geometries as well with .show():

more_complex_geometry.show()\n

You can also add positions to a pre-defined geometry:

from bloqade.atom_arrangement import Square\n\nbase_geometry = Square(2)\ngeometry_with_my_positions = base_geometry.add_position([(10,10), (20,20)])\n

As well as apply defects via .apply_defect_density. In the example below we apply a defect with a probability of 0.2:

from bloqade.atom_arrangement import Square, Kagome\n\nmore_complex_geometry = Kagome(2, 2, lattice_spacing = 2.0)\ndefective_geometry = more_complex_geometry.apply_defect_density(0.2)\n

Or if you want to completely roll out your own atom geometry from scratch just use add_position by itself:

from bloqade import start\n\nmy_geometry = start.add_position([(1,2), (3,4), (5,6)])\n
"},{"location":"home/quick_start/#building-waveforms","title":"Building Waveforms","text":"

After you've defined a geometry you:

  • Specify which level coupling to drive: rydberg or hyperfine
  • Specify detuning, rabi.amplitude or rabi.phase
  • Specify the spatial modulation

Which then leads you to the ability to specify a waveform of interest and begin constructing your pulse sequence. In the example below, we target the ground-Rydberg level coupling to drive with uniform spatial modulation for the Rabi amplitude. Our waveform is a piecewise linear one which ramps from \\(0\\) to \\(5 \\,\\text{rad/us}\\), holds that value for \\(1 \\,\\text{us}\\) and then ramps back down to \\(0 \\,\\text{rad/us}\\).

from bloqade import start\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nwaveform_applied = (\n    target_rabi_amplitude\n    .piecewise_linear(durations = [0.06, 1, 0.06], values = [0, 5, 5, 0])\n)\n

You aren't restricted to just piecewise linear waveforms however, you can also specify:

  • linear - Define a transition from one value to another over a duration
  • constant - Define a fixed value over a duration
  • piecewise_constant - Define a step-wise function with specific durations for each step
  • poly - Define a polynomial waveform using coefficients over a duration
"},{"location":"home/quick_start/#arbitrary-functions-as-waveforms","title":"Arbitrary Functions as Waveforms","text":"

For more complex waveforms it may provde to be tedious trying to chain together a large number of piecewise_constant or piecewise_linear methods and instead easier to just define the waveform as a function of time.

Bloqade lets you easily plug in an arbitrary function with .fn:

from bloqade import start\nfrom math import sin\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\n\ndef custom_waveform(t): \n    return 2.0 * sin(t)\n\ncustom_waveform_applied = (\n    target_rabi_amplitude\n    .fn(custom_waveform, duration = 3.0)\n)\n

In this form you can immediately emulate it if you'd like but to run this on hardware you need to discretize it. The waveform on hardware has to either be:

  • Piecewise linear for Rabi amplitude and detuning terms of the Hamiltonian
  • Piecewise constant for the Phase term of the Hamiltonian

Bloqade can automatically perform this conversion with sample(), all you need to do is specify the kind of interpolation and the size of the discretization step in time. Below we set the step duration to be \\(0.05 \\,\\text{us}\\) with \"linear\" interpolation to give us a resulting piecewise linear waveform.

custom_discretized_waveform_applied = (\n    target_rabi_amplitude\n    .fn(custom_waveform, duration = 3.0)\n    .sample(0.05, \"linear\")\n)\n

Note

Programs that have custom functions as waveforms are not fully serializable. This means that when you are saving and reloading results, the original embedded program will be missing that custom waveform. You will still be able to analyze the saved results!

"},{"location":"home/quick_start/#slicing-and-recording-waveforms","title":"Slicing and Recording Waveforms","text":"

When you conduct parameter sweeps with your program, you may want to sweep over your program across time. This will require \"slicing\" your waveforms, where you define the waveform of interest and then, using a variable with .slice, indicate the times at which the waveform duration should be cut short.

In the example below we define a simple piecewise linear waveform but slice it starting from a time duration of \\(0 \\,\\text{us}\\) to values between \\(1\\) to \\(2 \\,\\text{us}\\).

from bloqade import start\nimport numpy as np\n\nsliced_program = (\n    start.add_position((0, 0))\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=[0.5, 2.5, 0.5], values=[0, 3.0, 3.0, 0]\n    ).slice(start=0, stop=\"run_time\")\n)\n\nrun_times = np.linspace(1.0, 2.0, 10)\nvars_assigned_program = sliced_program.batch_assign(run_time=run_times)\n

This program will run fine in emulation but due to hardware constraints certain waveforms (such as those targeting the Rabi Amplitude), the waveform needs to start and end at \\(0 \\,\\text{rad}/\\text{us}\\). Thus, there needs to be a way to slice our waveform but also add an end component to that waveform. .record in Bloqade lets you literally \"record\" the value at the end of a .slice and then use it to construct further parts of the waveform.

In the program below the waveform is still sliced but with the help of .record a linear segment that pulls the waveform down to \\(0.0 \\,\\text{rad}/\\text{us}\\) from whatever its current value at the slice is in \\(0.7 \\,\\text{us}\\) is added.

from bloqade import start\nimport numpy as np\n\nsliced_program = (\n    start.add_position((0, 0))\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=[0.5, 2.5, 0.5], values=[0, 3.0, 3.0, 0]\n    ).slice(start=0, stop=\"run_time\")\n    .record(\"waveform_value\")\n    .linear(\"rabi_value\", 0.0, 0.7)\n)\n\nrun_times = np.linspace(1.0, 2.0, 10)\nvars_assigned_program = sliced_program.batch_assign(run_time=run_times)\n
"},{"location":"home/quick_start/#waveforms-with-no-geometry","title":"Waveforms with No Geometry","text":"

If you have multiple atom geometries you'd like to apply a pulse sequence to or you simply don't want to worry about what atom geometry to start with, you can just build straight off of start:

from bloqade import start\n\npulse_sequence = (\n    start\n    .rydberg.rabi.amplitude.uniform\n    .constant(value=1.0, duration=1.0)\n    .parse_sequence()\n)\n

You can visualize your sequence as well with .show():

pulse_sequence.show()\n

And when you're content with it you just .apply() it on the geometries of your choice:

from bloqade.atom_arrangement import Honeycomb, Kagome \n\ngeometry_1 = Honeycomb(2, lattice_spacing = 6.0)\ngeometry_2 = Kagome(2, lattice_spacing = 6.0)\n\nprogram_1  = geometry_1.apply(pulse_sequence)\nprogram_2  = geometry_2.apply(pulse_sequence)\n
"},{"location":"home/quick_start/#emulation","title":"Emulation","text":"

When you've completed the definition of your program you can use Bloqade's own emulator to get results. The emulation performs the time evolution of the analog Rydberg Hamiltonian. Here we say we want to the program to be run and measurements obtained 1000 times.

results = your_program.bloqade.python().run(1000)\n

Note

If your atoms are particularly close together or the ODE solver gives you the following message:

RuntimeError: DOP853/DOPRI5: Problem is probably stiff (interrupted).\n

In which case you will need to specify the interaction_picture=True argument:

results = your_program.bloqade.python().run(1000, interaction_picture=True)\n
"},{"location":"home/quick_start/#submitting-to-hardware","title":"Submitting to Hardware","text":"

To submit your program to hardware ensure you have your AWS Braket credentials loaded. You will need to use the AWS CLI to do this.

Then it's just a matter of selecting the Aquila on Braket backend. Before going any further Bloqade provides two options for running your program on actul hardware:

  • Using .run is blocking, meaning you will not be able to execute anything else while Bloqade waits for results
  • Using .run_async lets you submit to hardware and continue any further execution, while also letting you query the status of your program in the queue.

In the example below we use .run_async to specify the program should be run and measurements obtained 1000 times.

async_results = your_program.braket.aquila().run_async(1000)\n

We can see the status of our program via:

async_results.fetch()\n
Which gives us the Task ID, a unique identifier for the task as well as the status of the task. In the example below the task is Enqueued meaning it has been successfully created and is awaiting execution on the cloud. When the task is actually running on hardware, the status will change to Running.
                                             task ID    status  shots\n0  arn:aws:braket:us-east-1:XXXXXXXXXXXX:quantum-...  Enqueued    100\n

"},{"location":"home/quick_start/#analyzing-results","title":"Analyzing Results","text":"

When you've retrieved your results from either emulation or hardware you can generate a .report():

report = results.report()\n

For the examples below we analyze the results of a two atom program.

The report contains useful information such as:

  • The raw bitstrings measured per each execution of the program

    report.bitstrings()\n
    [array([[1, 1],\n        [1, 1],\n        [1, 1],\n        ...,\n        [1, 1],\n        [1, 1],\n        [1, 0]], dtype=int8)]\n

  • The number of times each unique bitstring occurred:

    report.counts()\n
    [OrderedDict([('11', 892), ('10', 59), ('01', 49)])]\n

  • The Rydberg Density for each atom

    report.rydberg_densities()\n
                     0      1\ntask_number              \n0            0.053  0.054\n

And can also provide useful visual information such as the state of your atoms and the bitstring distribution via:

report.show()\n

"},{"location":"home/quick_start/#parameter-sweeps","title":"Parameter Sweeps","text":"

You can easily do parameter sweeps in emulation and on Aquila with variables. Bloqade automatically detects strings in your program as variables that you can later assign singular or multiple values to.

In the example below, we define a program with a singular variable that controls the amplitude of the waveform.

from bloqade import start\n\nrabi_oscillations_program = (\n    start.add_position((0, 0))\n    .rydberg.rabi.amplitude.uniform.piecewise_linear(\n        durations=[0.06, 3, 0.06], \n        values=[0, \"rabi_amplitude\", \"rabi_amplitude\", 0]\n    )\n)\n

We can assign a single fixed value to the variable:

single_value_assignment = rabi_oscillations_program.assign(rabi_amplitude=3.5)\n

Or, to perform a sweep, we use .batch_assign:

import numpy as np\nrabi_amplitudes = np.linspace(1.0, 2.0, 20)\n\nmultiple_value_assignment = rabi_oscillations_program.batch_assign(rabi_amplitude=rabi_amplitudes)\n

This will actually create multiple versions of the program internally, with each program assigned a fixed value from the sweep. Bloqade will automatically handle the compilation of results from these multiple programs in order, meaning there is no major departure from what you saw in analyzing the results of your program.

You can also delay assignment of a value to a variable by first declaring it in .args() and then passing a value when you call run:

delayed_assignment_program = rabi_oscillations_program.args([\"rabi_amplitude\"])\nresults = delayed_assignment_program.bloqade.python().run(100, args=(1.0,))\n

You can alternatively treat the program as a callable after using .args() (note the inverted order of arguments in the call!):

delayed_assignment_program = rabi_oscillations_program.args([\"rabi_amplitude\"])\ncallable_program = delayed_assignment_program.bloqade.python()\nresults = callable_program(1.0, shots=100)\n

Variables aren't just restricted to having values assigned to them, you can also symbolically manipulate them!

"},{"location":"home/quick_start/#symbolic-parameters","title":"Symbolic Parameters","text":"

Variables in Bloqade can also be symbolically manipulated, giving you even more flexibility when you construct your program.

In the example below, we externally declare a variable my_var that then has some arithmetic done on it to allow it to have a different value in a later part of the program:

from bloqade import start, var\n\nmy_var = var(\"my_variable\")\nwaveform_durations = [0.6, 1.0, 0.6]\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nrabi_waveform = (\n    target_rabi_amplitude\n    .piecewise_linear(durations=waveform_durations, \n                      values=[0.0, my_var, my_var, 0.0])\n)\ntarget_detuning = rabi_waveform.detuning.uniform\ndetuning_waveform = (\n    target_detuning\n    .piecewise_linear(durations=waveform_durations, \n                      values=[my_var-1.0, my_var*0.5, my_var/2, my_var+1.0 ])\n)\n

You still perform variable assignment just like you normally would:

program = detuning_waveform.assign(my_variable=1.0)\n

You can also use Python's built-in sum if you want the sum of multiple variables as a value in your program. This is quite useful when it comes to needing to indicate a full duration for a waveform that doesn't need to be split up:

from bloqade import start, var\n\nvariable_durations = var([\"a\", \"b\", \"c\"])\n\ngeometry = start.add_position((0,0))\ntarget_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform\nrabi_waveform = (\n    target_rabi_amplitude\n    .piecewise_linear(durations=variable_durations, \n                      values=[0.0, 1.5, 1.5, 0.0])\n)\ntarget_detuning = rabi_waveform.detuning.uniform\ndetuning_waveform = (\n    target_detuning\n    .constant(duration=sum(variable_durations),\n              value=16.2)\n)\n
We later assign values and Bloqade will automatically handle the summation:

program = detuning_waveform.assign(a=0.5, b=1.2, c=0.5)\n
"},{"location":"home/quick_start/#saving-and-loading-results","title":"Saving and Loading Results","text":"

You can save your results in JSON format using Bloqade's save function:

from bloqade import start, save\n\nyour_program = ...\nemulation_results = your_program.bloqade.python().run(100)\nhardware_results = your_program.braket.aquila.run_async(100)\n\nsave(emulation_results, \"emulation_results.json\") \nsave(hardware_results, \"hardware_results.json\") \n

And later reload them into Python using the load function:

from bloqade import load\nemulation_results = load(\"emulation_results.json\")\nhardware_results = load(\"hardware_results.json\")\n
"},{"location":"reference/hardware-capabilities/","title":"Hardware Capabilities","text":"

During program development, it can be quite handy to know what true hardware capabilities are and incorporate that information programmaticaly. Bloqade offers the ability to do this via get_capabilities().

"},{"location":"reference/hardware-capabilities/#programmatic-access","title":"Programmatic Access","text":"

get_capabilities() (importable directly from bloqade) returns a QuEraCapabilities object. This object contains all the hardware constraints in Decimal format for the Aquila machine, our publically-accessible QPU on AWS Braket.

An example of using get_capabilities() is presented below:

from bloqade import get_capabilities, piecewise_linear\n\n# get capabilities for Aquila\naquila_capabilities = get_capabilities()\n\n# obtain maximum Rabi frequency as Decimal\nmax_rabi = aquila_capabilities.capabilities.rydberg.global_.rabi_frequency_max\n\n# use that value in constructing a neat Rabi waveform\nrabi_wf = piecewise_linear(durations = [0.5, 1.0, 0.5], values = [0, max_rabi, max_rabi, 0])\n

The attribute names for each value have been provided below but will require you to provide the proper prefix like in the example above (e.g. the maximum number of qubits lives under the number_qubits_max attribute which can be navigated to via *your_QuEra_Capabilities_Object*.lattice.number_qubits_max).

"},{"location":"reference/hardware-capabilities/#aquila-capabilities","title":"Aquila Capabilities","text":""},{"location":"reference/hardware-capabilities/#task","title":"Task","text":"
  • Use prefix your_capabilities_object.capabilities.task for:
    • minimum number of shots
    • maximum number of shots
Capability Attribute Value Minimum Number of Shots number_shots_min 1 Maximum Number of Shots number_shots_max 1000"},{"location":"reference/hardware-capabilities/#lattice-geometry","title":"Lattice Geometry","text":"
  • Use prefix your_capabilities_object.capabilities.lattice for:
    • maximum number of qubits
  • Use prefix your_capabilities_object.capabilities.lattice.area for:
    • maximum lattice area width
    • maximum lattice area height
  • Use prefix your_capabilities_object.capabilities.lattice.geometry for:
    • maximum number of sites
    • position resolution
    • minimum radial spacing
    • minimum vertical spacing
Capability Attribute Value Maximum Number of Qubits number_qubits_max 256 Maximum Lattice Area Width width 75.0 \u00b5m Maximum Lattice Area Height height 76.0 \u00b5m Minimum Radial Spacing between Qubits spacing_radial_min 4.0 \u00b5m Minimum Vertical Spacing between Qubits spacing_vertical_min 4.0 \u00b5m Position Resolution position_resolution 0.1 \u00b5m Maximum Number of Sites number_sites_max 256"},{"location":"reference/hardware-capabilities/#global-rydberg-values","title":"Global Rydberg Values","text":"
  • Use prefix your_capabilities_object.capabilities.rydberg for:
    • C6 Coefficient
  • Use prefix your_capabilities_object.capabilities.rydberg.global_ for:
    • Everything else related to global (applied to all atom) capabilities
Capability Attribute Value Rydberg Interaction Constant c6_coefficient 5.42\u00d710\u2076 rad/\u03bcs \u00d7 \u00b5m\u2076 Minimum Rabi Frequency rabi_frequency_min 0.00 rad/\u03bcs Maximum Rabi Frequency rabi_frequency_max 15.8 rad/\u03bcs Rabi Frequency Resolution rabi_frequency_resolution 0.0004 rad/\u03bcs Maximum Rabi Frequency Slew Rate rabi_frequency_slew_rate_max 250.0 rad/\u00b5s\u00b2 Minimum Detuning detuning_min -125.0 rad/\u03bcs Maximum Detuning detuning_max 125.0 rad/\u03bcs Detuning Resolution detuning_resolution 2.0\u00d710\u207b\u2077 rad/\u03bcs Maximum Detuning Slew Rate detuning_slew_rate_max 2500.0 rad/\u00b5s\u00b2 Minimum Phase phase_min -99.0 rad Maximum Phase phase_max 99.0 rad Phase Resolution phase_resolution 5.0\u00d710\u207b\u2077 rad Minimum Time time_min 0.0 \u00b5s Maximum Time time_max 4.0 \u00b5s Time Resolution time_resolution 0.001 \u00b5s Minimum \u0394t time_delta_min 0.05 \u00b5s"},{"location":"reference/hardware-capabilities/#local-detuning-values","title":"Local Detuning Values","text":"
  • Use prefix your_capabilities_object.capabilities.rydberg.local for the following values:
Capability Attribute Value Maximum Detuning detuning_max 125.0 rad/\u03bcs Minimum Detuning detuning_min 0 rad/\u03bcs Maximum Detuning Slew Rate detuning_slew_rate_max 1256.0 rad/\u00b5s\u00b2 Maximum Number of Local Detuning Sites number_local_detuning_sites 200 Maximum Site Coefficient site_coefficient_max 1.0 Minimum Site Coefficient site_ceofficient_min 0.0 Minimum Radial Spacing spacing_radial_min 5 \u00b5m Minimum \u0394t time_delta_min 0.05 \u03bcs Time Resolution time_resolution 0.001 \u00b5s"},{"location":"reference/overview/","title":"Builder Overview","text":"

You may have noticed from the Getting Started and Tutorials that Bloqade uses this interesting, dot-intensive syntax.

from bloqade import start\n\nprog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform.constant(1,1)\n
Exhibit A: Lots of Dots

In fact, it might look remniscent of what you see in some gate-based Quantum Computing SDKs:

# this is strictly pseudocode\ncircuit = init_qubits(n_qubits)\n# note the dots!\ncircuit.x(0).z(1).cnot(0, 1)...\n

What's the deal with that?

"},{"location":"reference/overview/#syntax-motivations","title":"Syntax Motivations","text":"

We call this syntax the builder or builder syntax and as its name implies, it is designed to let you build programs for Analog Hamiltonian Simulation hardware as easily and as straightforward as possible.

The linear structure implies a natural hierarchy in how you think about targeting the various degrees of freedom (detuning, atom positions, Rabi amplitude, etc.) your program will have. In the beginning you have unrestricted access to all these degrees of freedom but in order to do something useful you need to:

  1. Narrow down and explicitly identify what you want to control
  2. Provide the instructions on how you want to control what your focused on

Context is a strong component of the builder syntax, as you are both actively restricted from doing certain things that can introduce ambiguity based on where you are in your program and repeating the same action in different parts of the program yields different results.

"},{"location":"reference/overview/#visual-guides","title":"Visual Guides","text":"

While we hope the Smart Documentation (the ability to instantly see all your next possible steps and their capabilities in your favorite IDE/IPython) is sufficient to get you where you need to go, we undestand it's particularly beneficial to get a high-level overview of things before diving in.

The Standard Representation is a nice flow chart that gives a high-level overview of the different steps and components in the builder syntax.

"},{"location":"reference/standard/","title":"Build Workflow","text":"
\nflowchart TD\n  ProgramStart([\"start\"])\n\n  Geometry(\"Geometry or Lattice\")\n\n  Coupling[\"Coupling\n  -----------\n  rydberg\n  hyperfine\"]\n\n  Detuning[\"detuning\"]\n  Rabi[\"rabi\"]\n\n  Amplitude[\"amplitude\"]\n  Phase[\"phase\"]\n\n  SpaceModulation(\"SpatialModulation\n  ----------------------\n  uniform\n  scale\n  location\n  \")\n  Waveform{\"Waveform\n  ------------\n  piecewise_linear\n  piecewise_constant\n  constant\n  linear\n  poly\n  fn\n  \"}\n\n  Options([\"Options\n  ---------\n  assign\n  batch_assign\n  args\n  parallelize\n  \"])\n\n  Services([\"Services\n  ----------\n  bloqade\n  quera\n  braket\"])\n\n  QuEraBackends([\"Backends\n  ------------\n  mock\n  cloud_mock\n  aquila\n  device\"])\n\n  BraketBackends([\"Backends\n  ------------\n  aquila\n  local_emulator\"])\n\n  BloqadeBackends([\"Backends\n  ------------\n  python\n  julia\"])\n\n  Execution(\"\n  Execution hardware only\n  -------------------------------\n  run_async()\n\n  Hardware and simulation\n  -------------------------------\n  run()\n  __call__\")\n\n  ProgramStart -->|add_position| Geometry;\n  Geometry --> Coupling;\n  ProgramStart --> Coupling;\n\n  Coupling --> Detuning;\n  Coupling --> Rabi;\n\n  Rabi --> Amplitude;\n  Rabi --> Phase;\n\n  Detuning --> SpaceModulation;\n  Amplitude --> SpaceModulation;\n  Phase --> SpaceModulation;\n\n  SpaceModulation --> Waveform;\n\n  Waveform --> Coupling;\n  Waveform --> Services;\n  Waveform --> Options;\n  Options --> Services;\n\n  Services -->|quera| QuEraBackends;\n  Services -->|braket| BraketBackends;\n  Services -->|bloqade| BloqadeBackends;\n  QuEraBackends --> Execution;\n  BraketBackends --> Execution;\n  BloqadeBackends --> Execution;\n\n  click ProgramStart \"../bloqade/#bloqade.start\";\n  click Geometry \"../bloqade/atom_arrangement/\";\n  click Coupling \"../bloqade/builder/drive/\";\n  click Detuning \"../bloqade/builder/field/#bloqade.builder.field.Detuning\";\n  click Rabi \"../bloqade/builder/field/#bloqade.builder.field.Rabi\";\n  click Amplitude \"../bloqade/builder/field/#bloqade.builder.field.Amplitude\";\n  click Phase \"../bloqade/builder/field/#bloqade.builder.field.Phase\";\n  click SpaceModulation \"../bloqade/builder/spatial/\";\n  click Waveform \"../bloqade/builder/waveform/\";\n  click Options \"../bloqade/builder/pragmas/\";\n  click Services \"../bloqade/builder/backend/\";\n  click QuEraBackends \"../bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute\";\n  click BraketBackends \"../bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute\";\n  click BloqadeBackends \"../bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeBackend\";\n  click Execution \"../bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketRoutine\";\n
"},{"location":"reference/bloqade/","title":"Index","text":""},{"location":"reference/bloqade/atom_arrangement/","title":"Atom arrangement","text":""},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/atom_arrangement/#bloqade.atom_arrangement.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/constants/","title":"Constants","text":""},{"location":"reference/bloqade/constants/#bloqade.constants.RB_C6","title":"RB_C6 module-attribute","text":"
RB_C6 = 2 * pi * 862690\n

The C6 constant for the Rydberg Interaction of two Rubidium atoms in units of: rad \u03bcm^6/\u03bcs

"},{"location":"reference/bloqade/factory/","title":"Factory","text":""},{"location":"reference/bloqade/factory/#bloqade.factory.constant","title":"constant","text":"
constant(duration, value)\n

Create a Constant waveform.

Parameters:

Name Type Description Default duration ScalarType

Duration of the Constant waveform.

required value ScalarType

Value of the Constant waveform.s

required

Returns:

Name Type Description Constant Constant

A Constant waveform.

Source code in src/bloqade/factory.py
@beartype\ndef constant(duration: ScalarType, value: ScalarType) -> Constant:\n    \"\"\"Create a Constant waveform.\n\n    Args:\n        duration (ScalarType): Duration of the Constant waveform.\n        value (ScalarType): Value of the Constant waveform.s\n\n    Returns:\n        Constant: A Constant waveform.\n    \"\"\"\n    return Constant(value, duration)\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.get_capabilities","title":"get_capabilities","text":"
get_capabilities()\n

Get the device capabilities for Aquila

Returns:

Name Type Description QuEraCapabilities QuEraCapabilities

capabilities object for Aquila device.

Note

Units of time, distance, and energy are microseconds (us), micrometers (um), and rad / us, respectively.

For a comprehensive list of capabilities, see the Hardware Reference page

Source code in src/bloqade/factory.py
def get_capabilities() -> \"QuEraCapabilities\":\n    \"\"\"Get the device capabilities for Aquila\n\n    Returns:\n        QuEraCapabilities: capabilities object for Aquila device.\n\n\n    Note:\n        Units of time, distance, and energy are microseconds (us),\n        micrometers (um), and rad / us, respectively.\n\n        For a comprehensive list of capabilities,\n        see the [Hardware Reference](../../reference/hardware-capabilities.md)\n        page\n    \"\"\"\n\n    from bloqade.submission.capabilities import get_capabilities\n\n    # manually convert to units\n    return get_capabilities().scale_units(Decimal(\"1e6\"), Decimal(\"1e-6\"))\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.linear","title":"linear","text":"
linear(duration, start, stop)\n

Create a Linear waveform.

Parameters:

Name Type Description Default duration ScalarType

Duration of linear waveform

required start ScalarType

Starting value of linear waveform

required stop ScalarType

Ending value of linear waveform

required

Returns:

Name Type Description Linear Linear

Linear waveform

Source code in src/bloqade/factory.py
@beartype\ndef linear(duration: ScalarType, start: ScalarType, stop: ScalarType) -> Linear:\n    \"\"\"Create a Linear waveform.\n\n    Args:\n        duration (ScalarType): Duration of linear waveform\n        start (ScalarType): Starting value of linear waveform\n        stop (ScalarType): Ending value of linear waveform\n\n    Returns:\n        Linear: Linear waveform\n    \"\"\"\n    return Linear(start, stop, duration)\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.piecewise_constant","title":"piecewise_constant","text":"
piecewise_constant(durations, values)\n

Create a piecewise linear waveform.

Create a piecewise constant waveform from a list of durations and values. The value duration[i] corresponds to the length of time for the i'th segment with a value of values[i].

Parameters:

Name Type Description Default durations List[ScalarType]

The duration of each segment

required values List[ScalarType]

The values for each segment

required

Raises:

Type Description ValueError

If the length of values is not the same as the length of

Returns:

Name Type Description Waveform Waveform

The piecewise linear waveform.

Source code in src/bloqade/factory.py
@beartype\ndef piecewise_constant(\n    durations: List[ScalarType], values: List[ScalarType]\n) -> Waveform:\n    \"\"\"Create a piecewise linear waveform.\n\n    Create a piecewise constant waveform from a list of durations and values. The\n    value `duration[i]` corresponds to the length of time for the i'th segment\n    with a value of `values[i]`.\n\n    Args:\n        durations (List[ScalarType]): The duration of each segment\n        values (List[ScalarType]): The values for each segment\n\n    Raises:\n        ValueError: If the length of `values` is not the same as the length of\n        `durations`.\n\n    Returns:\n        Waveform: The piecewise linear waveform.\n    \"\"\"\n    if len(durations) != len(values):\n        raise ValueError(\n            \"The length of values must be the same as the length of durations\"\n        )\n\n    pwc_wf = None\n    for duration, value in zip(durations, values):\n        if pwc_wf is None:\n            pwc_wf = Constant(value, duration)\n        else:\n            pwc_wf = pwc_wf.append(Constant(value, duration))\n\n    return pwc_wf\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.piecewise_linear","title":"piecewise_linear","text":"
piecewise_linear(durations, values)\n

Create a piecewise linear waveform.

Create a piecewise linear waveform from a list of durations and values. The value duration[i] is of the linear segment between values[i] and values[i+1].

Parameters:

Name Type Description Default durations List[ScalarType]

The duration of each segment

required values List[ScalarType]

The values for each segment

required

Raises:

Type Description ValueError

If the length of values is not one greater than the length of

Returns:

Name Type Description Waveform Waveform

The piecewise linear waveform.

Source code in src/bloqade/factory.py
@beartype\ndef piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> Waveform:\n    \"\"\"Create a piecewise linear waveform.\n\n    Create a piecewise linear waveform from a list of durations and values. The\n    value `duration[i]` is of the linear segment between `values[i]` and `values[i+1]`.\n\n    Args:\n        durations (List[ScalarType]): The duration of each segment\n        values (List[ScalarType]): The values for each segment\n\n    Raises:\n        ValueError: If the length of `values` is not one greater than the length of\n        `durations`.\n\n    Returns:\n        Waveform: The piecewise linear waveform.\n    \"\"\"\n\n    if len(durations) + 1 != len(values):\n        raise ValueError(\n            \"The length of values must be one greater than the length of durations\"\n        )\n\n    pwl_wf = None\n    for duration, start, stop in zip(durations, values[:-1], values[1:]):\n        if pwl_wf is None:\n            pwl_wf = Linear(start, stop, duration)\n        else:\n            pwl_wf = pwl_wf.append(Linear(start, stop, duration))\n\n    return pwl_wf\n
"},{"location":"reference/bloqade/factory/#bloqade.factory.rydberg_h","title":"rydberg_h","text":"
rydberg_h(\n    atoms_positions,\n    detuning=None,\n    amplitude=None,\n    phase=None,\n    static_params={},\n    batch_params=[],\n    args=[],\n)\n

Create a rydberg program with uniform detuning, amplitude, and phase.

Parameters:

Name Type Description Default atoms_positions Any

Description of geometry of atoms in system.

required detuning Optional[Waveform]

Waveform for detuning. Defaults to None.

None amplitude Optional[Waveform]

Waveform describing the amplitude of the rabi term. Defaults to None.

None phase Optional[Waveform]

Waveform describing the phase of rabi term. Defaults to None.

None static_params Dict[str, Any]

Define static parameters of your program. Defaults to {}.

{} batch_params Union[List[Dict[str, Any]], Dict[str, Any]]

Parmaters for a batch of tasks. Defaults to [].

[] args List[str]

List of arguments to leave till runtime. Defaults to [].

[]

Returns:

Name Type Description Routine Routine

An object that can be used to dispatch a rydberg program to multiple backends.

Source code in src/bloqade/factory.py
@beartype\ndef rydberg_h(\n    atoms_positions: Any,\n    detuning: Optional[Waveform] = None,\n    amplitude: Optional[Waveform] = None,\n    phase: Optional[Waveform] = None,\n    static_params: Dict[str, Any] = {},\n    batch_params: Union[List[Dict[str, Any]], Dict[str, Any]] = [],\n    args: List[str] = [],\n) -> Routine:\n    \"\"\"Create a rydberg program with uniform detuning, amplitude, and phase.\n\n    Args:\n        atoms_positions (Any): Description of geometry of atoms in system.\n        detuning (Optional[Waveform], optional): Waveform for detuning.\n            Defaults to None.\n        amplitude (Optional[Waveform], optional): Waveform describing the amplitude of\n            the rabi term. Defaults to None.\n        phase (Optional[Waveform], optional): Waveform describing the phase of rabi\n            term. Defaults to None.\n        static_params (Dict[str, Any], optional): Define static parameters of your\n            program. Defaults to {}.\n        batch_params (Union[List[Dict[str, Any]], Dict[str, Any]], optional):\n            Parmaters for a batch of tasks. Defaults to [].\n        args (List[str], optional): List of arguments to leave till runtime.\n            Defaults to [].\n\n    Returns:\n        Routine: An object that can be used to dispatch a rydberg program to\n            multiple backends.\n    \"\"\"\n    from bloqade import start\n    from bloqade.atom_arrangement import AtomArrangement\n\n    if isinstance(atoms_positions, AtomArrangement):\n        prog = atoms_positions\n    else:\n        prog = start.add_position(atoms_positions)\n\n    if detuning is not None:\n        prog = prog.rydberg.detuning.uniform.apply(detuning)\n\n    if amplitude is not None:\n        prog = prog.amplitude.uniform.apply(amplitude)\n\n    if phase is not None:\n        prog = prog.phase.uniform.apply(phase)\n\n    prog = prog.assign(**static_params)\n\n    if isinstance(batch_params, dict):\n        prog = prog.batch_assign(**batch_params)\n    else:\n        prog = prog.batch_assign(batch_params)\n\n    prog = prog.args(args)\n\n    return prog.parse()\n
"},{"location":"reference/bloqade/serialize/","title":"Serialize","text":""},{"location":"reference/bloqade/serialize/#bloqade.serialize.dumps","title":"dumps","text":"
dumps(o, use_decimal=True, **json_kwargs)\n

Serialize object to string

Parameters:

Name Type Description Default o Any

the object to serialize

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.dumps

{}

Returns:

Name Type Description str str

the serialized object as a string

Source code in src/bloqade/serialize.py
@beartype\ndef dumps(\n    o: Any,\n    use_decimal: bool = True,\n    **json_kwargs,\n) -> str:\n    \"\"\"Serialize object to string\n\n    Args:\n        o (Any): the object to serialize\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.dumps\n\n    Returns:\n        str: the serialized object as a string\n    \"\"\"\n    if not isinstance(o, Serializer.types):\n        raise TypeError(\n            f\"Object of type {type(o)} is not JSON serializable. \"\n            f\"Only {Serializer.types} are supported.\"\n        )\n    return json.dumps(o, cls=Serializer, use_decimal=use_decimal, **json_kwargs)\n
"},{"location":"reference/bloqade/serialize/#bloqade.serialize.load","title":"load","text":"
load(fp, use_decimal=True, **json_kwargs)\n

Load object from file

Parameters:

Name Type Description Default fp Union[TextIO, str]

the file path or file object

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.load

{}

Returns:

Name Type Description Any

the deserialized object

Source code in src/bloqade/serialize.py
@beartype\ndef load(fp: Union[TextIO, str], use_decimal: bool = True, **json_kwargs):\n    \"\"\"Load object from file\n\n    Args:\n        fp (Union[TextIO, str]): the file path or file object\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.load\n\n    Returns:\n        Any: the deserialized object\n    \"\"\"\n    load_bloqade()\n    if isinstance(fp, str):\n        with open(fp, \"r\") as f:\n            return json.load(\n                f,\n                object_hook=Serializer.object_hook,\n                use_decimal=use_decimal,\n                **json_kwargs,\n            )\n    else:\n        return json.load(\n            fp,\n            object_hook=Serializer.object_hook,\n            use_decimal=use_decimal,\n            **json_kwargs,\n        )\n
"},{"location":"reference/bloqade/serialize/#bloqade.serialize.loads","title":"loads","text":"
loads(s, use_decimal=True, **json_kwargs)\n

Load object from string

Parameters:

Name Type Description Default s str

the string to load

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.loads

{}

Returns:

Name Type Description Any

the deserialized object

Source code in src/bloqade/serialize.py
@beartype\ndef loads(s: str, use_decimal: bool = True, **json_kwargs):\n    \"\"\"Load object from string\n\n    Args:\n        s (str): the string to load\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.loads\n\n    Returns:\n        Any: the deserialized object\n    \"\"\"\n    load_bloqade()\n    return json.loads(\n        s, object_hook=Serializer.object_hook, use_decimal=use_decimal, **json_kwargs\n    )\n
"},{"location":"reference/bloqade/serialize/#bloqade.serialize.save","title":"save","text":"
save(o, fp, use_decimal=True, **json_kwargs)\n

Serialize object to file

Parameters:

Name Type Description Default o Any

the object to serialize

required fp Union[TextIO, str]

the file path or file object

required use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True **json_kwargs

other arguments passed to json.dump

{}

Returns:

Type Description None

None

Source code in src/bloqade/serialize.py
@beartype\ndef save(\n    o: Any,\n    fp: Union[TextIO, str],\n    use_decimal=True,\n    **json_kwargs,\n) -> None:\n    \"\"\"Serialize object to file\n\n    Args:\n        o (Any): the object to serialize\n        fp (Union[TextIO, str]): the file path or file object\n        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.\n        **json_kwargs: other arguments passed to json.dump\n\n    Returns:\n        None\n    \"\"\"\n    if not isinstance(o, Serializer.types):\n        raise TypeError(\n            f\"Object of type {type(o)} is not JSON serializable. \"\n            f\"Only {Serializer.types} are supported.\"\n        )\n    if isinstance(fp, str):\n        with open(fp, \"w\") as f:\n            json.dump(o, f, cls=Serializer, use_decimal=use_decimal, **json_kwargs)\n    else:\n        json.dump(o, fp, cls=Serializer, use_decimal=use_decimal, **json_kwargs)\n
"},{"location":"reference/bloqade/builder/","title":"Index","text":""},{"location":"reference/bloqade/builder/args/","title":"Args","text":""},{"location":"reference/bloqade/builder/assign/","title":"Assign","text":""},{"location":"reference/bloqade/builder/coupling/","title":"Coupling","text":""},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.Hyperfine","title":"Hyperfine","text":"
Hyperfine(parent=None)\n

Bases: LevelCoupling

This node represent level coupling between hyperfine state.

Examples:

- To reach the node from the start node:\n\n>>> node = bloqade.start.hyperfine\n>>> type(node)\n<class 'bloqade.builder.coupling.Hyperfine'>\n\n- Hyperfine level coupling have two reachable field nodes:\n\n    - detuning term (See also [`Detuning`][bloqade.builder.field.Detuning])\n    - rabi term (See also [`Rabi`][bloqade.builder.field.Rabi])\n\n>>> hyp_detune = bloqade.start.hyperfine.detuning\n>>> hyp_rabi = bloqade.start.hyperfine.rabi\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling","title":"LevelCoupling","text":"
LevelCoupling(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling.detuning","title":"detuning property","text":"
detuning\n

Specify the Detuning Field of your program. You will be able to specify the spatial modulation afterwards.

Returns:

Type Description Detuning

Detuning: A program node representing the detuning field.

Background and Context

In the Many-Body Rydberg Hamiltonian:

\\[ \\frac{\\mathcal{H}(t)}{\\hbar} = \\sum_j \\frac{\\Omega_j(t)}{2} \\left( e^{i \\phi_j(t) } | g_j \\rangle \\langle r_j | + e^{-i \\phi_j(t) } | r_j \\rangle \\langle g_j | \\right) - \\sum_j \\Delta_j(t) \\hat{n}_j + \\sum_{j < k} V_{jk} \\hat{n}_j \\hat{n}_k. \\]

The detuning is specified by the term \\(\\Delta_j(t)\\) and specifies how off-resonant the laser being applied to the atoms is from the atomic energy transition, which is driven by the Rabi frequency \\(\\Omega_j(t)\\).

The detuning is described by a field, which is the summation of one or more drives, with the drive being the sum of a waveform and spatial modulation:

\\[ \\sum_j \\Delta_j(t) = \\sum_j \\sum_a C^{a}_{j} f_{a}(t) \\]

Note that the spatial modulation \\(C_{j}\\) scales how much of the detuning waveform is experienced by the atom at site \\(j\\). You can specify the scaling that all atoms feel to be identical (global detuning) or you can specify different scaling for different atoms (local detuning).

Examples

from bloqade import start\n\n# specify geometry, in this case just one atom\ngeometry = start.add_position((0,0))\n# specify your coupling (either `rydberg` or `hyperfine`)\ncoupling = geometry.rydberg\n# Begin specifying your detuning\ncoupling.detuning\n
Alternatively you may start with building your Rabi field and then reach the ability to build your detuning like so:

from bloqade import start\ngeometry = start.add_position((0,0))\ncoupling = geometry.rydberg\nrabi_field = coupling.rabi.amplitude.uniform.constant(duration = 1.0, value = 1.0)\ndetuning = rabi_field.detuning\n
Applications
  • Single Qubit Floquet Dynamics
  • Two Qubit Adiabatic Sweep
  • 1D Z2 State Preparation
  • 2D State Preparation
  • Quantum Scar Dynamics
  • Solving the Maximal Independent Set Problem on defective King Graph
Potential Pitfalls

Bloqade allows you to build a field for the Detuning in the form of:

\\[ \\sum_j \\Delta_j(t) = \\sum_j \\sum_a C^{a}_{j} f_{a}(t) \\]

Where your field can contain multiple drives.

In reality the hardware only supports the following configuration:

\\[ \\Delta_{i}(t) = \\Delta_{1}(t) + c_{i} \\Delta_{2}(t) \\] \\[ c_i \\in [0, 1] \\] \\[ \\Delta_{2}(t) \\leq 0 \\]

Where \\(\\Delta_{1}(t)\\) is your global detuning (establishable via uniform) and \\(\\Delta_{2}(t)\\) is your local detuning waveform with the spatial modulation \\(c_{i}\\) establishable via location or scale.

"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling.detuning--next-possible-steps","title":"Next Possible Steps","text":"

You may continue building your program via:

  • uniform: To address all atoms in the field
  • location(locations, scales): To address atoms at specific locations via indices
  • scale(coeffs): To address all atoms with an individual scale factor
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.LevelCoupling.rabi","title":"rabi property","text":"
rabi\n

Specify the complex-valued Rabi field of your program.

The Rabi field is composed of a real-valued Amplitude and Phase field.

  • Next possible steps to build your program are creating the RabiAmplitude field and RabiPhase field of the field:
    • ...rabi.amplitude: To create the Rabi amplitude field
    • ...rabi.phase: To create the Rabi phase field
"},{"location":"reference/bloqade/builder/coupling/#bloqade.builder.coupling.Rydberg","title":"Rydberg","text":"
Rydberg(parent=None)\n

Bases: LevelCoupling

This node represent level coupling of rydberg state.

Examples:

- To reach the node from the start node:\n\n>>> node = bloqade.start.rydberg\n>>> type(node)\n<class 'bloqade.builder.coupling.Rydberg'>\n\n- Rydberg level coupling have two reachable field nodes:\n\n    - detuning term (See also [`Detuning`][bloqade.builder.field.Detuning])\n    - rabi term (See also [`Rabi`][bloqade.builder.field.Rabi])\n\n>>> ryd_detune = bloqade.start.rydberg.detuning\n>>> ryd_rabi = bloqade.start.rydberg.rabi\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/drive/","title":"Drive","text":""},{"location":"reference/bloqade/builder/drive/#bloqade.builder.drive.Drive","title":"Drive","text":""},{"location":"reference/bloqade/builder/drive/#bloqade.builder.drive.Drive.hyperfine","title":"hyperfine property","text":"
hyperfine\n

Address the Hyperfine level coupling in your program.

  • Next possible steps to build your program are specifying the Rabi field or Detuning field.
    • ...hyperfine.rabi: for Rabi field
    • ...hyperfine.detuning: for Detuning field
  • In the absence of a field you the value is set to zero by default.
"},{"location":"reference/bloqade/builder/drive/#bloqade.builder.drive.Drive.rydberg","title":"rydberg property","text":"
rydberg\n

Address the Rydberg level coupling in your program.

  • Next possible steps to build your program are specifying the Rabi field or Detuning field.
    • ...rydberg.rabi: for Rabi field
    • ...rydberg.detuning: for Detuning field
  • In the absence of a field you the value is set to zero by default.
"},{"location":"reference/bloqade/builder/field/","title":"Field","text":""},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Detuning","title":"Detuning","text":"
Detuning(parent=None)\n

Bases: Field

This node represent detuning field of a specified level coupling (rydberg or hyperfine) type.

Examples:

- To specify detuning of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.detuning\n>>> type(node)\n<class 'bloqade.builder.field.Detuning'>\n\n- To specify detuning of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.detuning\n>>> type(node)\n<class 'bloqade.builder.field.Detuning'>\n
Note

This node is a SpatialModulation node. See SpatialModulation for additional options.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field","title":"Field","text":"
Field(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.uniform","title":"uniform property","text":"
uniform\n

Address all atoms as part of defining the spatial modulation component of a drive.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.).

  • You can now do:
    • ...uniform.linear(start, stop, duration) : to apply a linear waveform
    • ...uniform.constant(value, duration) : to apply a constant waveform
    • ...uniform.poly([coefficients], duration) : to apply a polynomial waveform
    • ...uniform.apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...uniform.piecewise_linear([durations], [values]): to apply a piecewise linear waveform
    • ...uniform.piecewise_constant([durations], [values]): to apply a piecewise constant waveform
    • ...uniform.fn(f(t,...)): to apply a function as a waveform
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.location","title":"location","text":"
location(labels, scales=None)\n

Address a single atom (or multiple) atoms.

Address a single atom (or multiple) as part of defining the spatial modulation component of a drive. You can specify the atoms to target as a list of labels and a list of scales. The scales are used to multiply the waveform that is applied to the atom. You can also specify a single label and scale to target a single atom.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field. (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.location--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n# to target a single atom with a waveform\n>>> one_location_prog = prog.location(0)\n# to target a single atom with a scale\n>>> one_location_prog = prog.location(0, 0.5)\n# to target multiple atoms with same waveform\n>>> multi_location_prog = prog.location([0, 2])\n# to target multiple atoms with different scales\n>>> multi_location_prog = prog.location([0, 2], [0.5, \"scale\"])\n
  • You can now do:
    • ...location(labels, scales).linear(start, stop, duration) : to apply a linear waveform
    • ...location(labels, scales).constant(value, duration) : to apply a constant waveform
    • ...location(labels, scales).poly([coefficients], duration) : to apply a polynomial waveform
    • ...location(labels, scales).apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...location(labels, scales).piecewise_linear([durations], [values]): to apply a piecewise linear waveform
    • ...location(labels, scales).piecewise_constant([durations], [values]): to apply a piecewise constant waveform
    • ...location(labels, scales).fn(f(t,..)): to apply a function as a waveform
Source code in src/bloqade/builder/field.py
def location(\n    self,\n    labels: Union[List[int], int],\n    scales: Union[List[ScalarType], ScalarType, None] = None,\n) -> \"Location\":\n    \"\"\"Address a single atom (or multiple) atoms.\n\n    Address a single atom (or multiple) as part of defining the spatial\n    modulation component of a drive. You can specify the atoms to target\n    as a list of labels and a list of scales. The scales are used to\n    multiply the waveform that is applied to the atom. You can also specify\n    a single label and scale to target a single atom.\n\n    Next steps to build your program include choosing the waveform that\n    will be summed with the spatial modulation to create a drive.\n\n    The drive by itself, or the sum of subsequent drives (created by just\n    chaining the construction of drives) will become the field.\n    (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n    # to target a single atom with a waveform\n    >>> one_location_prog = prog.location(0)\n    # to target a single atom with a scale\n    >>> one_location_prog = prog.location(0, 0.5)\n    # to target multiple atoms with same waveform\n    >>> multi_location_prog = prog.location([0, 2])\n    # to target multiple atoms with different scales\n    >>> multi_location_prog = prog.location([0, 2], [0.5, \"scale\"])\n    ```\n\n    - You can now do:\n        - `...location(labels, scales).linear(start, stop, duration)` : to apply\n            a linear waveform\n        - `...location(labels, scales).constant(value, duration)` : to apply\n            a constant waveform\n        - `...location(labels, scales).poly([coefficients], duration)` : to apply\n            a polynomial waveform\n        - `...location(labels, scales).apply(wf:bloqade.ir.Waveform)`: to apply\n            a pre-defined waveform\n        - `...location(labels, scales).piecewise_linear([durations], [values])`:\n            to apply\n            a piecewise linear waveform\n        - `...location(labels, scales).piecewise_constant([durations], [values])`:\n            to apply\n            a piecewise constant waveform\n        - `...location(labels, scales).fn(f(t,..))`: to apply a function as a\n            waveform\n\n    \"\"\"\n    return self._location(labels, scales)\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.scale","title":"scale","text":"
scale(coeffs)\n

Address all the atoms scaling each atom with an element of the list or define a variable name for the scale list to be assigned later by defining a name and using assign or batch_assign later.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Field.scale--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n\n# assign a literal list of values to scale each atom\n>>> one_location_prog = prog.scale([0.1, 0.2, 0.3])\n# assign a variable name to be assigned later\n>>> one_location_prog = prog.scale(\"a\")\n# \"a\" can be assigned in the END of the program during variable assignment\n# using a list of values, indicating the scaling for each atom\n>>> single_assignment = ...assign(a = [0.1, 0.2, 0.3])\n# a list of lists, indicating a set of atoms should be targeted\n# for each task in a batch.\n>>> batch_assignment = ...batch_assign(a = [list_1, list_2, list_3,...])\n
  • You can now do:
    • ...scale(coeffs).linear(start, stop, duration) : to apply a linear waveform
    • ...scale(coeffs).constant(value, duration) : to apply a constant waveform
    • ...scale(coeffs).poly([coefficients], duration) : to apply a polynomial waveform
    • ...scale(coeffs).apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...scale(coeffs).piecewise_linear(durations, values): to apply a piecewise linear waveform
    • ...scale(coeffs).piecewise_constant(durations, values): to apply a piecewise constant waveform
    • ...scale(coeffs).fn(f(t,..)): to apply a function as a waveform
Source code in src/bloqade/builder/field.py
def scale(self, coeffs: Union[str, List[ScalarType]]) -> \"Scale\":\n    \"\"\"\n    Address all the atoms scaling each atom with an element of the list\n    or define a variable name for the scale list to be assigned later by\n    defining a `name` and using `assign` or `batch_assign` later.\n\n    Next steps to build your program include choosing the waveform that\n    will be summed with the spatial modulation to create a drive.\n\n    The drive by itself, or the sum of subsequent drives (created by just\n    chaining the construction of drives) will become the field\n    (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi\n\n    # assign a literal list of values to scale each atom\n    >>> one_location_prog = prog.scale([0.1, 0.2, 0.3])\n    # assign a variable name to be assigned later\n    >>> one_location_prog = prog.scale(\"a\")\n    # \"a\" can be assigned in the END of the program during variable assignment\n    # using a list of values, indicating the scaling for each atom\n    >>> single_assignment = ...assign(a = [0.1, 0.2, 0.3])\n    # a list of lists, indicating a set of atoms should be targeted\n    # for each task in a batch.\n    >>> batch_assignment = ...batch_assign(a = [list_1, list_2, list_3,...])\n\n    ```\n\n    - You can now do:\n        - `...scale(coeffs).linear(start, stop, duration)` : to apply\n            a linear waveform\n        - `...scale(coeffs).constant(value, duration)` : to apply\n            a constant waveform\n        - `...scale(coeffs).poly([coefficients], duration)` : to apply\n            a polynomial waveform\n        - `...scale(coeffs).apply(wf:bloqade.ir.Waveform)`: to apply\n            a pre-defined waveform\n        - `...scale(coeffs).piecewise_linear(durations, values)`:  to\n            apply a piecewise linear waveform\n        - `...scale(coeffs).piecewise_constant(durations, values)`: to\n            apply a piecewise constant waveform\n        - `...scale(coeffs).fn(f(t,..))`: to apply a function as a waveform\n\n    \"\"\"\n    from bloqade.builder.spatial import Scale\n\n    return Scale(coeffs, self)\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Rabi","title":"Rabi","text":"
Rabi(parent=None)\n

Bases: Builder

This node represent rabi field of a specified level coupling (rydberg or hyperfine) type.

Examples:

- To specify rabi of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.rabi\n<class 'bloqade.builder.field.Rabi'>\n\n- To specify rabi of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.rabi\n>>> type(node)\n<class 'bloqade.builder.field.Rabi'>\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Rabi.amplitude","title":"amplitude property","text":"
amplitude\n

Specify the real-valued Rabi Amplitude field.

Next steps to build your program focus on specifying a spatial modulation.

The spatial modulation, when coupled with a waveform, completes the specification of a \"Drive\". One or more drives can be summed together automatically to create a field such as the Rabi Amplitude here.

  • You can now
    • ...amplitude.uniform: Address all atoms in the field
    • ...amplitude.location(...): Scale atoms by their indices
    • ...amplitude.scale(...): Scale each atom with a value from a list or assign a variable name to be assigned later
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.Rabi.phase","title":"phase property","text":"
phase\n

Specify the real-valued Rabi Phase field.

Next steps to build your program focus on specifying a spatial modulation.

The spatial modulation, when coupled with a waveform, completes the specification of a \"Drive\". One or more drives can be summed together automatically to create a field such as the Rabi Phase here.

  • You can now
    • ...amplitude.uniform: Address all atoms in the field
    • ...amplitude.location(...): Scale atoms by their indices
    • ...amplitude.scale(...): Scale each atom with a value from a list or assign a variable name to be assigned later
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.RabiAmplitude","title":"RabiAmplitude","text":"
RabiAmplitude(parent=None)\n

Bases: Field

This node represent amplitude of a rabi field.

Examples:

- To specify rabi amplitude of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.rabi.amplitude\n>>> type(node)\n<class 'bloqade.builder.field.Amplitude'>\n\n- To specify rabi amplitude of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.rabi.amplitude\n>>> type(node)\n<class 'bloqade.builder.field.Amplitude'>\n
Note

This node is a SpatialModulation node. See SpatialModulation for additional options.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/field/#bloqade.builder.field.RabiPhase","title":"RabiPhase","text":"
RabiPhase(parent=None)\n

Bases: Field

This node represent phase of a rabi field.

Examples:

- To specify rabi phase of rydberg coupling:\n\n>>> node = bloqade.start.rydberg.rabi.phase\n>>> type(node)\n<class 'bloqade.builder.field.Phase'>\n\n- To specify rabi phase of hyperfine coupling:\n\n>>> node = bloqade.start.hyperfine.rabi.phase\n>>> type(node)\n<class 'bloqade.builder.field.Phase'>\n
Note

This node is a SpatialModulation node. See SpatialModulation for additional options.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/parallelize/","title":"Parallelize","text":""},{"location":"reference/bloqade/builder/pragmas/","title":"Pragmas","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Assignable","title":"Assignable","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Assignable.assign","title":"assign","text":"
assign(**assignments)\n

Assign values to variables declared previously in the program.

This is reserved for variables that should only take single values OR for spatial modulations that were created with .scale(str) in which case you can pass in a list. This is the ONLY circumstance in which multiple values are allowed.

"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Assignable.assign--usage-examples","title":"Usage Examples:","text":"
# define geometry\n>>> reg = bloqade.start\n...       .add_position([(0,0),(1,1),(2,2),(3,3)])\n# define variables in program\n>>> seq = reg.rydberg.detuning.uniform\n...       .linear(start=\"ival\",stop=1,duration=\"span_time\")\n# assign values to variables\n>>> seq = seq.assign(span_time = 0.5, ival = 0.0)\n
  • You can now:
    • ...assign(assignments).bloqade: select the bloqade local emulator backend
    • ...assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...assign(assignments).device(specifier_string): select backend by specifying a string
  • Assign multiple values to a single variable for a parameter sweep:
    • ...assign(assignments).batch_assign(assignments):
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...assign(assignments).args([previously_defined_vars])
Source code in src/bloqade/builder/pragmas.py
def assign(self, **assignments) -> \"Assign\":\n    \"\"\"\n    Assign values to variables declared previously in the program.\n\n    This is reserved for variables that should only take single values OR\n    for spatial modulations that were created with `.scale(str)` in which case\n    you can pass in a list. This is the ONLY circumstance in which multiple\n    values are allowed.\n\n    ### Usage Examples:\n    ```\n    # define geometry\n    >>> reg = bloqade.start\n    ...       .add_position([(0,0),(1,1),(2,2),(3,3)])\n    # define variables in program\n    >>> seq = reg.rydberg.detuning.uniform\n    ...       .linear(start=\"ival\",stop=1,duration=\"span_time\")\n    # assign values to variables\n    >>> seq = seq.assign(span_time = 0.5, ival = 0.0)\n    ```\n\n    - You can now:\n        - `...assign(assignments).bloqade`: select the bloqade local\n            emulator backend\n        - `...assign(assignments).braket`: select braket local emulator or\n            QuEra hardware\n        - `...assign(assignments).device(specifier_string)`: select backend\n            by specifying a string\n    - Assign multiple values to a single variable for a parameter sweep:\n        - `...assign(assignments).batch_assign(assignments)`:\n    - Parallelize the program register, duplicating the geometry and waveform\n        sequence to take advantage of all available\n      space/qubits on the QPU:\n        - `...assign(assignments).parallelize(cluster_spacing)`\n    - Defer value assignment of certain variables to runtime:\n        - `...assign(assignments).args([previously_defined_vars])`\n\n    \"\"\"\n    from bloqade.builder.assign import Assign\n\n    return Assign(assignments, parent=self)\n
"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.BatchAssignable","title":"BatchAssignable","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.BatchAssignable.batch_assign","title":"batch_assign","text":"
batch_assign(__batch_params=[], **assignments)\n

Assign multiple values to a single variable to create a parameter sweep.

Bloqade automatically handles the multiple programs this would generate and treats it as object with unified results for easy post-processing.

NOTE: if you assign multiple values to multiple variables in your program, the values must be of the same length. Bloqade will NOT do a Cartesian product (e.g. if \"var1\" is assigned [1,2,3] and \"var2\" is assigned [4,5,6] then the resulting programs will have assignments [1,4], [2,5], [3,6]).

"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.BatchAssignable.batch_assign--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (0, \"atom_distance\")])\n>>> prog = reg.rydberg.rabi.amplitude.uniform.constant(\"value\", 5.0)\n>>> var_assigned_prog = prog.batch_assign(value = [1.0, 2.0, 3.0],\natom_distance = [1.0, 2.0, 3.0])\n
  • Next steps are:
    • ...batch_assign(assignments).bloqade: select the bloqade local emulator backend
    • ...batch_assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...batch_assign(assignments).device(specifier_string): select backend by specifying a string
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...batch_assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...batch_assign(assignments).args([previously_defined_vars])
Source code in src/bloqade/builder/pragmas.py
def batch_assign(\n    self,\n    __batch_params: List[Dict[str, ParamType]] = [],\n    **assignments: List[ParamType],\n) -> Union[\"BatchAssign\", \"ListAssign\"]:\n    \"\"\"\n\n    Assign multiple values to a single variable to create a parameter sweep.\n\n    Bloqade automatically handles the multiple programs this would generate\n    and treats it as object with unified results for easy post-processing.\n\n    NOTE: if you assign multiple values to multiple variables in your program,\n    the values must be of the same length. Bloqade will NOT do a Cartesian product\n    (e.g. if \"var1\" is assigned [1,2,3] and \"var2\" is assigned [4,5,6] then the\n    resulting programs will have assignments [1,4], [2,5], [3,6]).\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (0, \"atom_distance\")])\n    >>> prog = reg.rydberg.rabi.amplitude.uniform.constant(\"value\", 5.0)\n    >>> var_assigned_prog = prog.batch_assign(value = [1.0, 2.0, 3.0],\n    atom_distance = [1.0, 2.0, 3.0])\n    ```\n\n    - Next steps are:\n        - `...batch_assign(assignments).bloqade`: select the bloqade\n            local emulator backend\n        - `...batch_assign(assignments).braket`: select braket local\n        emulator or QuEra hardware\n        - `...batch_assign(assignments).device(specifier_string)`: select\n        backend by specifying a string\n    - Parallelize the program register, duplicating the geometry and waveform\n      sequence to take advantage of all available\n      space/qubits on the QPU:\n        - `...batch_assign(assignments).parallelize(cluster_spacing)`\n    - Defer value assignment of certain variables to runtime:\n        - `...batch_assign(assignments).args([previously_defined_vars])`\n\n    \"\"\"\n    from bloqade.builder.assign import BatchAssign, ListAssign\n\n    if len(__batch_params) > 0 and assignments:\n        raise ValueError(\"batch_params and assignments cannot be used together.\")\n\n    if len(__batch_params) > 0:\n        return ListAssign(__batch_params, parent=self)\n    else:\n        return BatchAssign(assignments, parent=self)\n
"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Parallelizable","title":"Parallelizable","text":""},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Parallelizable.parallelize","title":"parallelize","text":"
parallelize(cluster_spacing)\n

Parallelize the current problem (register and sequence) by duplicating the geometry to take advantage of all available space/qubits on hardware.

The singular argument lets you specify how far apart the clusters should be in micrometers.

"},{"location":"reference/bloqade/builder/pragmas/#bloqade.builder.pragmas.Parallelizable.parallelize--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position((0,0)).rydberg.rabi.uniform.amplitude\n.constant(1.0, 1.0)\n# copy-paste the geometry and waveforms\n>>> parallelized_prog = reg.parallelize(24)\n
  • Your next steps are: ...parallelize(cluster_spacing).bloqade: select the bloqade local emulator backend ...parallelize(cluster_spacing).braket: select braket local emulator or QuEra hardware on the cloud ...parallelize(cluster_spacing).device(specifier_string): select backend by specifying a string
Source code in src/bloqade/builder/pragmas.py
def parallelize(self, cluster_spacing: LiteralType) -> \"Parallelize\":\n    \"\"\"\n\n    Parallelize the current problem (register and sequence) by duplicating\n    the geometry to take advantage of all available space/qubits on hardware.\n\n    The singular argument lets you specify how far apart the clusters\n    should be in micrometers.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position((0,0)).rydberg.rabi.uniform.amplitude\n    .constant(1.0, 1.0)\n    # copy-paste the geometry and waveforms\n    >>> parallelized_prog = reg.parallelize(24)\n    ```\n\n    - Your next steps are:\n         `...parallelize(cluster_spacing).bloqade`: select the bloqade\n            local emulator backend\n         `...parallelize(cluster_spacing).braket`: select braket\n            local emulator or QuEra hardware on the cloud\n         `...parallelize(cluster_spacing).device(specifier_string)`: select\n            backend by specifying a string\n\n    \"\"\"\n    from bloqade.builder.parallelize import Parallelize\n\n    return Parallelize(cluster_spacing, self)\n
"},{"location":"reference/bloqade/builder/route/","title":"Route","text":""},{"location":"reference/bloqade/builder/sequence_builder/","title":"Sequence builder","text":""},{"location":"reference/bloqade/builder/spatial/","title":"Spatial","text":""},{"location":"reference/bloqade/builder/spatial/#bloqade.builder.spatial.Location","title":"Location","text":"
Location(labels, scales, parent=None)\n

Bases: SpatialModulation

Source code in src/bloqade/builder/spatial.py
@beartype\ndef __init__(\n    self,\n    labels: List[int],\n    scales: List[ScalarType],\n    parent: Optional[Builder] = None,\n) -> None:\n    from bloqade.ir.scalar import cast\n    from bloqade.ir.control.field import Location\n\n    super().__init__(parent)\n    self._scaled_locations = {\n        Location(label): cast(scale) for label, scale in zip(labels, scales)\n    }\n
"},{"location":"reference/bloqade/builder/spatial/#bloqade.builder.spatial.Uniform","title":"Uniform","text":"
Uniform(parent=None)\n

Bases: SpatialModulation

The node specify a uniform spacial modulation. Which is ready to apply waveform (See Waveform for available waveform options)

Examples:

- To hit this node from the start node:\n\n>>> reg = bloqade.start.add_position([(0,0),(1,1),(2,2),(3,3)])\n>>> loc = reg.rydberg.detuning.uniform\n\n- Apply Linear waveform:\n\n>>> wv = bloqade.ir.Linear(start=0,stop=1,duration=0.5)\n>>> reg = bloqade.start.add_position([(0,0),(1,1),(2,2),(3,3)])\n>>> loc = reg.rydberg.detuning.uniform.apply(wv)\n
Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/start/","title":"Start","text":""},{"location":"reference/bloqade/builder/start/#bloqade.builder.start.ProgramStart","title":"ProgramStart","text":"
ProgramStart(parent=None)\n

Bases: Drive, Builder

ProgramStart is the base class for a starting/entry node for building a program.

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/start/#bloqade.builder.start.ProgramStart.apply","title":"apply","text":"
apply(sequence)\n

Apply a pre-built sequence to a program.

This allows you to build a program independent of any geometry and then apply the program to said geometry. Or, if you have a program you would like to try on multiple geometries you can trivially do so with this.

Example Usage:

>>> from numpy import pi\n>>> seq = start.rydberg.rabi.amplitude.constant(2.0 * pi, 4.5)\n# choose a geometry of interest to apply the program on\n>>> from bloqade.atom_arrangement import Chain, Kagome\n>>> complete_program = Chain(10).apply(seq)\n# you can .apply to as many geometries as you like\n>>> another_complete_program = Kagome(3).apply(seq)\n

  • From here you can now do:
    • ...assign(assignments).bloqade: select the bloqade local emulator backend
    • ...assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...assign(assignments).device(specifier_string): select backend by specifying a string
  • Assign multiple values to a single variable for a parameter sweep:
    • ...assign(assignments).batch_assign(assignments):
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...assign(assignments).args([previously_defined_vars])
Source code in src/bloqade/builder/start.py
@beartype\ndef apply(self, sequence: SequenceExpr) -> SequenceBuilder:\n    \"\"\"\n    Apply a pre-built sequence to a program.\n\n    This allows you to build a program independent of any geometry\n    and then `apply` the program to said geometry. Or, if you have a\n    program you would like to try on multiple geometries you can\n    trivially do so with this.\n\n    Example Usage:\n    ```\n    >>> from numpy import pi\n    >>> seq = start.rydberg.rabi.amplitude.constant(2.0 * pi, 4.5)\n    # choose a geometry of interest to apply the program on\n    >>> from bloqade.atom_arrangement import Chain, Kagome\n    >>> complete_program = Chain(10).apply(seq)\n    # you can .apply to as many geometries as you like\n    >>> another_complete_program = Kagome(3).apply(seq)\n    ```\n\n    - From here you can now do:\n        - `...assign(assignments).bloqade`: select the bloqade\n            local emulator backend\n        - `...assign(assignments).braket`: select braket\n            local emulator or QuEra hardware\n        - `...assign(assignments).device(specifier_string)`: select\n            backend by specifying a string\n    - Assign multiple values to a single variable for a parameter sweep:\n        - `...assign(assignments).batch_assign(assignments)`:\n    - Parallelize the program register, duplicating the geometry and waveform\n        sequence to take advantage of all available\n      space/qubits on the QPU:\n        - `...assign(assignments).parallelize(cluster_spacing)`\n    - Defer value assignment of certain variables to runtime:\n        - `...assign(assignments).args([previously_defined_vars])`\n\n    \"\"\"\n    return SequenceBuilder(sequence, self)\n
"},{"location":"reference/bloqade/builder/typing/","title":"Typing","text":""},{"location":"reference/bloqade/builder/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Recordable","title":"Recordable","text":""},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Recordable.record","title":"record","text":"
record(name)\n

Copy or \"record\" the value at the end of the waveform into a variable so that it can be used in another place.

A common design pattern is to couple this with .slice() considering you may not know exactly what the end value of a .slice() is, especially in parameter sweeps where it becomes cumbersome to handle.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Recordable.record--usage-example","title":"Usage Example:","text":"
# define program of interest\n>>> from bloqade import start\n>>> prog = start.rydberg.rabi.amplitude.uniform\n>>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n# We now slice the piecewise_linear from above and record the\n# value at the end of that slice. We then use that value\n# to construct a new waveform that can be appended to the previous\n# one without introducing discontinuity (refer to the\n# \"Quantum Scar Dynamics\" tutorial for how this could be handy)\n>>> prog_with_record = prog_with_wf.slice(0.0, 1.0).record(\"end_of_wf\")\n>>> record_applied_prog = prog_with_record.linear(start=\"end_of_wf\"\n, stop=0.0, duration=0.3)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...slice(start, stop).linear(start, stop, duration): to append another linear waveform
    • ...slice(start, stop).constant(value, duration): to append a constant waveform
    • ...slice(start, stop).piecewise_linear(): to append a piecewise linear waveform
    • ...slice(start, stop).piecewise_constant(): to append a piecewise constant waveform
    • ...slice(start, stop).poly([coefficients], duration): to append a polynomial waveform
    • ...slice(start, stop).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...slilce(start, stop).fn(f(t,...)): to append a waveform defined by a python function
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...slice(start, stop).uniform: To address all atoms in the field
    • ...slice(start, stop).location(int): To address an atom at a specific location via index
    • ...slice(start, stop).scale(str)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...slice(start, stop).assign(variable_name = value): to assign a single value to a variable
    • ...slice(start, stop) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...slice(start, stop).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...slice(start, stop).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...slice(start, stop).bloqade: to run on the Bloqade local emulator
    • ...slice(start, stop).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...slice(start, stop).parallelize(spacing)
  • Start targeting another level coupling
    • ...slice(start, stop).rydberg: to target the Rydberg level coupling
    • ...slice(start, stop).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...slice(start, stop).amplitude: to target the real-valued Rabi Amplitude field
    • ...slice(start, stop).phase: to target the real-valued Rabi Phase field
    • ...slice(start, stop).detuning: to target the Detuning field
    • ...slice(start, stop).rabi: to target the complex-valued Rabi field ```
Source code in src/bloqade/builder/waveform.py
@beartype\ndef record(self, name: str) -> \"Record\":\n    \"\"\"\n    Copy or \"record\" the value at the end of the waveform into a variable\n    so that it can be used in another place.\n\n    A common design pattern is to couple this with `.slice()` considering\n    you may not know exactly what the end value of a `.slice()` is,\n    especially in parameter sweeps where it becomes cumbersome to handle.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    # define program of interest\n    >>> from bloqade import start\n    >>> prog = start.rydberg.rabi.amplitude.uniform\n    >>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    # We now slice the piecewise_linear from above and record the\n    # value at the end of that slice. We then use that value\n    # to construct a new waveform that can be appended to the previous\n    # one without introducing discontinuity (refer to the\n    # \"Quantum Scar Dynamics\" tutorial for how this could be handy)\n    >>> prog_with_record = prog_with_wf.slice(0.0, 1.0).record(\"end_of_wf\")\n    >>> record_applied_prog = prog_with_record.linear(start=\"end_of_wf\"\n    , stop=0.0, duration=0.3)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...slice(start, stop).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...slice(start, stop).constant(value, duration)`:\n            to append a constant waveform\n        - `...slice(start, stop).piecewise_linear()`:\n            to append a piecewise linear waveform\n        - `...slice(start, stop).piecewise_constant()`:\n            to append a piecewise constant waveform\n        - `...slice(start, stop).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...slice(start, stop).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...slilce(start, stop).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...slice(start, stop).uniform`:\n            To address all atoms in the field\n        - `...slice(start, stop).location(int)`:\n            To address an atom at a specific location via index\n        - `...slice(start, stop).scale(str)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...slice(start, stop).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...slice(start, stop)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...slice(start, stop).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...slice(start, stop).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...slice(start, stop).bloqade`:\n            to run on the Bloqade local emulator\n        - `...slice(start, stop).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...slice(start, stop).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...slice(start, stop).rydberg`:\n            to target the Rydberg level coupling\n        - `...slice(start, stop).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...slice(start, stop).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...slice(start, stop).phase`:\n            to target the real-valued Rabi Phase field\n        - `...slice(start, stop).detuning`:\n            to target the Detuning field\n        - `...slice(start, stop).rabi`:\n            to target the complex-valued Rabi field\n    ```\n    \"\"\"\n    return Record(name, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Sliceable","title":"Sliceable","text":""},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Sliceable.slice","title":"slice","text":"
slice(start=None, stop=None)\n

Indicate that you only want a portion of your waveform to be used in the program.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.Sliceable.slice--usage-example","title":"Usage Example:","text":"
# define a program with a waveform of interest\n>>> from bloqade import start\n>>> prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform\n>>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n# instead of using the full waveform we opt to only take the first 1 us\n>>> prog_with_slice = prog_with_wf.slice(0.0, 1.0)\n# you may use variables as well\n>>> prog_with_slice = prog_with_wf.slice(\"start\", \"end\")\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...slice(start, stop).linear(start, stop, duration): to append another linear waveform
    • ...slice(start, stop).constant(value, duration): to append a constant waveform
    • ...slice(start, stop).piecewise_linear(): to append a piecewise linear waveform
    • ...slice(start, stop).piecewise_constant(): to append a piecewise constant waveform
    • ...slice(start, stop).poly([coefficients], duration): to append a polynomial waveform
    • ...slice(start, stop).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...slilce(start, stop).fn(f(t,...)): to append a waveform defined by a python function
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...slice(start, stop).uniform: To address all atoms in the field
    • ...slice(start, stop).location(int): To address an atom at a specific location via index
    • ...slice(start, stop).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...slice(start, stop).assign(variable_name = value): to assign a single value to a variable
    • ...slice(start, stop) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...slice(start, stop).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...slice(start, stop).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...slice(start, stop).bloqade: to run on the Bloqade local emulator
    • ...slice(start, stop).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...slice(start, stop).parallelize(spacing)
  • Start targeting another level coupling
    • ...slice(start, stop).rydberg: to target the Rydberg level coupling
    • ...slice(start, stop).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...slice(start, stop).amplitude: to target the real-valued Rabi Amplitude field
    • ...slice(start, stop).phase: to target the real-valued Rabi Phase field
    • ...slice(start, stop).detuning: to target the Detuning field
    • ...slice(start, stop).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef slice(\n    self,\n    start: Optional[ScalarType] = None,\n    stop: Optional[ScalarType] = None,\n) -> \"Slice\":\n    \"\"\"\n    Indicate that you only want a portion of your waveform to be used in\n    the program.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n\n    ### Usage Example:\n    ```\n    # define a program with a waveform of interest\n    >>> from bloqade import start\n    >>> prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform\n    >>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    # instead of using the full waveform we opt to only take the first 1 us\n    >>> prog_with_slice = prog_with_wf.slice(0.0, 1.0)\n    # you may use variables as well\n    >>> prog_with_slice = prog_with_wf.slice(\"start\", \"end\")\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...slice(start, stop).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...slice(start, stop).constant(value, duration)`:\n            to append a constant waveform\n        - `...slice(start, stop).piecewise_linear()`:\n            to append a piecewise linear waveform\n        - `...slice(start, stop).piecewise_constant()`:\n            to append a piecewise constant waveform\n        - `...slice(start, stop).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...slice(start, stop).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...slilce(start, stop).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...slice(start, stop).uniform`:\n            To address all atoms in the field\n        - `...slice(start, stop).location(int)`:\n            To address an atom at a specific location via index\n        - `...slice(start, stop).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...slice(start, stop).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...slice(start, stop)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...slice(start, stop).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...slice(start, stop).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...slice(start, stop).bloqade`:\n            to run on the Bloqade local emulator\n        - `...slice(start, stop).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...slice(start, stop).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...slice(start, stop).rydberg`:\n            to target the Rydberg level coupling\n        - `...slice(start, stop).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...slice(start, stop).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...slice(start, stop).phase`:\n            to target the real-valued Rabi Phase field\n        - `...slice(start, stop).detuning`:\n            to target the Detuning field\n        - `...slice(start, stop).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return Slice(start, stop, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable","title":"WaveformAttachable","text":"
WaveformAttachable(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.apply","title":"apply","text":"
apply(wf)\n

Apply a Waveform built previously to current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.apply--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# build our waveform independently of the main program\n>>> from bloqade import piecewise_linear\n>>> wf = piecewise_linear(durations=[0.3, 2.5, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n>>> prog.apply(wf)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...apply(waveform).linear(start, stop, duration): to append another linear waveform
    • ...apply(waveform).constant(value, duration): to append a constant waveform
    • ...apply(waveform).piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...apply(waveform).piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...apply(waveform).poly([coefficients], duration): to append a polynomial waveform
    • ...apply(waveform).apply(waveform): to append a pre-defined waveform
    • ...apply(waveform).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...apply(waveform).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...apply(waveform).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...apply(waveform).uniform: To address all atoms in the field
    • ...apply(waveform).location(int): To address an atom at a specific location via index
    • ...apply(waveform).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...apply(waveform).assign(variable_name = value): to assign a single value to a variable
    • ...apply(waveform).batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...apply(waveform).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...apply(waveform).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...apply(waveform).bloqade: to run on the Bloqade local emulator
    • ...apply(waveform).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...apply(waveform).parallelize(spacing)
  • Start targeting another level coupling
    • ...apply(waveform).rydberg: to target the Rydberg level coupling
    • ...apply(waveform).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...apply(waveform).amplitude: to target the real-valued Rabi Amplitude field
    • ...apply(waveform).phase: to target the real-valued Rabi Phase field
    • ...apply(waveform).detuning: to target the Detuning field
    • ...apply(waveform).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef apply(self, wf: ir.Waveform) -> \"Apply\":\n    \"\"\"\n    Apply a [`Waveform`][bloqade.ir.control.Waveform] built previously to\n    current location(s).\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # build our waveform independently of the main program\n    >>> from bloqade import piecewise_linear\n    >>> wf = piecewise_linear(durations=[0.3, 2.5, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    >>> prog.apply(wf)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...apply(waveform).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...apply(waveform).constant(value, duration)`:\n            to append a constant waveform\n        - `...apply(waveform).piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...apply(waveform).piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...apply(waveform).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...apply(waveform).apply(waveform)`:\n            to append a pre-defined waveform\n        - `...apply(waveform).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...apply(waveform).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...apply(waveform).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...apply(waveform).uniform`: To address all atoms in the field\n        - `...apply(waveform).location(int)`:\n            To address an atom at a specific location via index\n        - `...apply(waveform).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying a\n                single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...apply(waveform).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...apply(waveform).batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...apply(waveform).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...apply(waveform).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...apply(waveform).bloqade`:\n            to run on the Bloqade local emulator\n        - `...apply(waveform).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...apply(waveform).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...apply(waveform).rydberg`: to target the Rydberg level coupling\n        - `...apply(waveform).hyperfine`: to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...apply(waveform).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...apply(waveform).phase`:\n            to target the real-valued Rabi Phase field\n        - `...apply(waveform).detuning`:\n            to target the Detuning field\n        - `...apply(waveform).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return Apply(wf, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.constant","title":"constant","text":"
constant(value, duration)\n

Append or assign a constant waveform to the current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.constant--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# apply a constant waveform of 1.9 radians/us for 0.5 us\n>>> prog.constant(value=1.9,duration=0.5)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...constant(value, duration).linear(start, stop, duration): to append another linear waveform
    • ...constant(value, duration).constant(value, duration): to append a constant waveform
    • ...constant(value, duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...constant(value, duration) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...constant(value, duration).poly([coefficients], duration): to append a polynomial waveform
    • ...constant(value, duration).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...constant(value, duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...constant(value, duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...constant(value, duration).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...constant(value, duration).uniform: To address all atoms in the field
    • ...constant(value, duration).scale(...): To address an atom at a specific location via index
    • ...constant(value, duration).location(int)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...constant(value, duration).assign(variable_name = value): to assign a single value to a variable
    • ...constant(value, duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...constant(value, duration).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...constant(value, duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...constant(value, duration).bloqade: to run on the Bloqade local emulator
    • ...constant(value, duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...constant(start, stop, duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...constant(value, duration).rydberg: to target the Rydberg level coupling
    • ...constant(value, duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...constant(value, duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...constant(value, duration).phase: to target the real-valued Rabi Phase field
    • ...constant(value, duration).detuning: to target the Detuning field
    • ...constant(value, duration).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef constant(self, value: ScalarType, duration: ScalarType) -> \"Constant\":\n    \"\"\"\n    Append or assign a constant waveform to the current location(s).\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # apply a constant waveform of 1.9 radians/us for 0.5 us\n    >>> prog.constant(value=1.9,duration=0.5)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...constant(value, duration).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...constant(value, duration).constant(value, duration)`:\n            to append a constant waveform\n        - `...constant(value, duration)\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...constant(value, duration)\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...constant(value, duration).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...constant(value, duration).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...constant(value, duration).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...constant(value, duration).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...constant(value, duration).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...constant(value, duration).uniform`:\n            To address all atoms in the field\n        - `...constant(value, duration).scale(...)`:\n            To address an atom at a specific location via index\n        - `...constant(value, duration).location(int)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...constant(value, duration).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...constant(value, duration)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...constant(value, duration).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...constant(value, duration).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...constant(value, duration).bloqade`:\n            to run on the Bloqade local emulator\n        - `...constant(value, duration).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...constant(start, stop, duration).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...constant(value, duration).rydberg`:\n            to target the Rydberg level coupling\n        - `...constant(value, duration).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current\n      level coupling (previously selected as `rydberg` or `hyperfine`):\n        - `...constant(value, duration).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...constant(value, duration).phase`:\n            to target the real-valued Rabi Phase field\n        - `...constant(value, duration).detuning`:\n            to target the Detuning field\n        - `...constant(value, duration).rabi`:\n            to target the complex-valued Rabi field\n\n    \"\"\"\n    return Constant(value, duration, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.fn","title":"fn","text":"
fn(fn, duration)\n

Append or assign a custom function as a waveform.

The function must have its first argument be that of time but can also have other arguments which are treated as variables. You can assign values to later in the program via .assign or .batch_assign.

The function must also return a singular float value.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.fn--usage-examples","title":"### Usage Examples:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# define our custom waveform. It must have one argument\n# be time followed by any other number of arguments that can\n# be assigned a value later in the program via `.assign` or `.batch_assign`\n>>> def custom_waveform_function(t, arg1, arg2):\n        return arg1*t + arg2\n>>> prog = prog.fn(custom_waveform_function, duration = 0.5)\n# assign values\n>>> assigned_vars_prog = prog.assign(arg1 = 1.0, arg2 = 2.0)\n# or go for batching!\n>>> assigned_vars_batch_prog = prog.assign(arg1 = 1.0, arg2 = [1.0, 2.0, 3.0])\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...fn(f(t,...)) .linear(start, stop, duration): to append another linear waveform
    • ...fn(f(t,...)) .constant(value, duration): to append a constant waveform
    • ...fn(f(t,...)) .piecewise_linear(durations, values): to append a piecewise linear waveform
    • ...fn(f(t,...)) .piecewise_constant(durations, values): to append a piecewise constant waveform
    • ...fn(f(t,...)) .poly([coefficients], duration): to append a polynomial waveform
    • ...fn(f(t,...)) .apply(waveform): to append a pre-defined waveform
    • ...fn(f(t,...)) .fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...fn(f(t,...)).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...fn(f(t,...)).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...fn(f(t,...)).uniform: To address all atoms in the field
    • ...fn(f(t,...)).scale(...): To address an atom at a specific location via index
    • ...fn(f(t,...)).location(int)`
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...fn(f(t,...)) .assign(variable_name = value): to assign a single value to a variable
    • ...fn(f(t,...)) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...fn(f(t,...)) .args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...fn(f(t,...)).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...fn(f(t,...)).bloqade: to run on the Bloqade local emulator
    • ...fn(f(t,...)).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...fn(f(t,...)).parallelize(spacing)
  • Start targeting another level coupling
    • ...fn(f(t,...)).rydberg: to target the Rydberg level coupling
    • ...fn(f(t,...)).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...fn(f(t,...)).amplitude: to target the real-valued Rabi Amplitude field
    • ...fn(f(t,...)).phase: to target the real-valued Rabi Phase field
    • ...fn(f(t,...)).detuning: to target the Detuning field
    • ...fn(f(t,...)).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef fn(self, fn: Callable, duration: ScalarType) -> \"Fn\":\n    \"\"\"\n    Append or assign a custom function as a waveform.\n\n    The function must have its first argument be that of time but\n    can also have other arguments which are treated as variables.\n    You can assign values to later in the program via `.assign` or `.batch_assign`.\n\n    The function must also return a singular float value.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### ### Usage Examples:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # define our custom waveform. It must have one argument\n    # be time followed by any other number of arguments that can\n    # be assigned a value later in the program via `.assign` or `.batch_assign`\n    >>> def custom_waveform_function(t, arg1, arg2):\n            return arg1*t + arg2\n    >>> prog = prog.fn(custom_waveform_function, duration = 0.5)\n    # assign values\n    >>> assigned_vars_prog = prog.assign(arg1 = 1.0, arg2 = 2.0)\n    # or go for batching!\n    >>> assigned_vars_batch_prog = prog.assign(arg1 = 1.0, arg2 = [1.0, 2.0, 3.0])\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...fn(f(t,...))\n            .linear(start, stop, duration)`: to append another linear waveform\n        - `...fn(f(t,...))\n            .constant(value, duration)`: to append a constant waveform\n        - `...fn(f(t,...))\n            .piecewise_linear(durations, values)`:\n            to append a piecewise linear waveform\n        - `...fn(f(t,...))\n            .piecewise_constant(durations, values)`:\n            to append a piecewise constant waveform\n        - `...fn(f(t,...))\n            .poly([coefficients], duration)`: to append a polynomial waveform\n        - `...fn(f(t,...))\n            .apply(waveform)`: to append a pre-defined waveform\n        - `...fn(f(t,...))\n            .fn(f(t,...))`: to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...fn(f(t,...)).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...fn(f(t,...)).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...fn(f(t,...)).uniform`:\n            To address all atoms in the field\n        - `...fn(f(t,...)).scale(...)`:\n            To address an atom at a specific location via index\n        - ...fn(f(t,...)).location(int)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning it a\n                list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...fn(f(t,...))\n            .assign(variable_name = value)`: to assign a single value to a variable\n        - `...fn(f(t,...))\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...fn(f(t,...))\n            .args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...fn(f(t,...)).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...fn(f(t,...)).bloqade`:\n            to run on the Bloqade local emulator\n        - `...fn(f(t,...)).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...fn(f(t,...)).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...fn(f(t,...)).rydberg`:\n            to target the Rydberg level coupling\n        - `...fn(f(t,...)).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...fn(f(t,...)).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...fn(f(t,...)).phase`:\n            to target the real-valued Rabi Phase field\n        - `...fn(f(t,...)).detuning`:\n            to target the Detuning field\n        - `...fn(f(t,...)).rabi`:\n            to target the complex-valued Rabi field\n\n    \"\"\"\n    return Fn(fn, duration, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.linear","title":"linear","text":"
linear(start, stop, duration)\n

Append or assign a linear waveform to the current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.linear--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# apply a linear waveform that goes from 0 to 1 radians/us in 0.5 us\n>>> prog.linear(start=0,stop=1,duration=0.5)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...linear(start, stop, duration).linear(start, stop, duration): to append another linear waveform
    • ...linear(start, stop, duration).constant(value, duration): to append a constant waveform
    • ...linear(start, stop, duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...linear(start, stop, duration) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...linear(start, stop, duration).poly([coefficients], duration): to append a polynomial waveform
    • ...linear(start, stop, duration).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...linear(start, stop, duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...linear(start, stop, duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...linear(start, stop, duration).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...linear(start, stop, duration).uniform: To address all atoms in the field
    • ...linear(start, stop, duration).location(int): To address atoms at specific location with scaling
    • ...linear(start, stop, duration).scale(...)
      • To address atoms at specific location with scaling
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...linear(start, stop, duration).assign(variable_name = value): to assign a single value to a variable
    • ...linear(start, stop, duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...linear(start, stop, duration).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...linear(start, stop, duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...linear(start, stop, duration).bloqade: to run on the Bloqade local emulator
    • ...linear(start, stop, duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...linear(start, stop, duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...linear(start, stop, duration).rydberg: to target the Rydberg level coupling
    • ...linear(start, stop, duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...linear(start, stop, duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...linear(start, stop, duration).phase: to target the real-valued Rabi Phase field
    • ...linear(start, stop, duration).detuning: to target the Detuning field
    • ...linear(start, stop, duration).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef linear(\n    self, start: ScalarType, stop: ScalarType, duration: ScalarType\n) -> \"Linear\":\n    \"\"\"\n\n    Append or assign a linear waveform to the current location(s).\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # apply a linear waveform that goes from 0 to 1 radians/us in 0.5 us\n    >>> prog.linear(start=0,stop=1,duration=0.5)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...linear(start, stop, duration).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...linear(start, stop, duration).constant(value, duration)`:\n            to append a constant waveform\n        - `...linear(start, stop, duration)\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...linear(start, stop, duration)\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...linear(start, stop, duration).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...linear(start, stop, duration).apply(wf:bloqade.ir.Waveform)`:\n            to append a pre-defined waveform\n        - `...linear(start, stop, duration).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...linear(start, stop, duration).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...linear(start, stop, duration).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n        (this drive will be summed to the one you just created):\n        - `...linear(start, stop, duration).uniform`:\n            To address all atoms in the field\n        - `...linear(start, stop, duration).location(int)`:\n            To address atoms at specific location with scaling\n        - `...linear(start, stop, duration).scale(...)`\n            - To address atoms at specific location with scaling\n            - To address multiple atoms at specific locations by specifying\n                a single variable and then assigning it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...linear(start, stop, duration).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...linear(start, stop, duration)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...linear(start, stop, duration).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...linear(start, stop, duration).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...linear(start, stop, duration).bloqade`:\n            to run on the Bloqade local emulator\n        - `...linear(start, stop, duration).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...linear(start, stop, duration).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...linear(start, stop, duration).rydberg`:\n            to target the Rydberg level coupling\n        - `...linear(start, stop, duration).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...linear(start, stop, duration).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...linear(start, stop, duration).phase`:\n            to target the real-valued Rabi Phase field\n        - `...linear(start, stop, duration).detuning`:\n            to target the Detuning field\n        - `...linear(start, stop, duration).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n\n    return Linear(start, stop, duration, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_constant","title":"piecewise_constant","text":"
piecewise_constant(durations, values)\n

Append or assign a piecewise constant waveform to current location(s).

The durations argument should have number of elements = len(values). durations should be the duration PER section of the waveform, NON-CUMULATIVE.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_constant--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.rabi.phase.uniform\n# create a staircase, we hold 0.0 rad/us for 1.0 us, then\n# to 1.0 rad/us for 0.5 us before stopping at 0.8 rad/us for 0.9 us.\n>>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3], values=[1.0, 0.5, 0.9])\n
  • Your next steps including:
  • Continue building your waveform via:
    • ...piecewise_constant([durations], [values]) .linear(start, stop, duration): to append another linear waveform
    • ...piecewise_constant([durations], [values]) .constant(value, duration): to append a constant waveform
    • ...piecewise_constant([durations], [values]) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...piecewise_constant([durations], [values]) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...piecewise_constant([durations], [values]) .poly([coefficients], duration): to append a polynomial waveform
    • ...piecewise_constant([durations], [values]) .apply(waveform): to append a pre-defined waveform
    • ...piecewise_constant([durations], [values]).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...piecewise_constant([durations], [values]) .slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...piecewise_constant([durations], [values]) .record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...piecewise_constant([durations], [values]).uniform: To address all atoms in the field
    • ...piecewise_constant([durations], [values]).location(int): To address an atom at a specific location via index
    • ...piecewise_constant([durations], [values]).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...piecewise_constant([durations], [values]) .assign(variable_name = value): to assign a single value to a variable
    • ...piecewise_constant([durations], [values]) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...piecewise_constant([durations], [values]) .args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...piecewise_constant([durations], [values]).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...piecewise_constant([durations], [values]).bloqade: to run on the Bloqade local emulator
    • ...piecewise_constant([durations], [values]).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...piecewise_constat([durations], [values]).parallelize(spacing)
  • Start targeting another level coupling
    • ...piecewise_constant([durations], [values]).rydberg: to target the Rydberg level coupling
    • ...piecewise_constant([durations], [values]).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...piecewise_constant(durations, values).amplitude: to target the real-valued Rabi Amplitude field
    • ...piecewise_constant([durations], [values]).phase: to target the real-valued Rabi Phase field
    • ...piecewise_constant([durations], [values]).detuning: to target the Detuning field
    • ...piecewise_constant([durations], [values]).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef piecewise_constant(\n    self, durations: List[ScalarType], values: List[ScalarType]\n) -> \"PiecewiseConstant\":\n    \"\"\"\n    Append or assign a piecewise constant waveform to current location(s).\n\n    The `durations` argument should have number of elements = len(values).\n    `durations` should be the duration PER section of the waveform,\n    NON-CUMULATIVE.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.rabi.phase.uniform\n    # create a staircase, we hold 0.0 rad/us for 1.0 us, then\n    # to 1.0 rad/us for 0.5 us before stopping at 0.8 rad/us for 0.9 us.\n    >>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3], values=[1.0, 0.5, 0.9])\n    ```\n\n    - Your next steps including:\n    - Continue building your waveform via:\n        - `...piecewise_constant([durations], [values])\n            .linear(start, stop, duration)`: to append another linear waveform\n        - `...piecewise_constant([durations], [values])\n            .constant(value, duration)`: to append a constant waveform\n        - `...piecewise_constant([durations], [values])\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...piecewise_constant([durations], [values])\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...piecewise_constant([durations], [values])\n            .poly([coefficients], duration)`: to append a polynomial waveform\n        - `...piecewise_constant([durations], [values])\n            .apply(waveform)`: to append a pre-defined waveform\n        - `...piecewise_constant([durations], [values]).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...piecewise_constant([durations], [values])\n            .slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...piecewise_constant([durations], [values])\n            .record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...piecewise_constant([durations], [values]).uniform`:\n            To address all atoms in the field\n        - `...piecewise_constant([durations], [values]).location(int)`:\n            To address an atom at a specific location via index\n        - `...piecewise_constant([durations], [values]).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning it a\n                list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...piecewise_constant([durations], [values])\n            .assign(variable_name = value)`: to assign a single value to a variable\n        - `...piecewise_constant([durations], [values])\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...piecewise_constant([durations], [values])\n            .args([\"previously_defined_var\"])`: to defer assignment\n            of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...piecewise_constant([durations], [values]).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...piecewise_constant([durations], [values]).bloqade`:\n            to run on the Bloqade local emulator\n        - `...piecewise_constant([durations], [values]).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...piecewise_constat([durations], [values]).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...piecewise_constant([durations], [values]).rydberg`:\n            to target the Rydberg level coupling\n        - `...piecewise_constant([durations], [values]).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...piecewise_constant(durations, values).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...piecewise_constant([durations], [values]).phase`:\n            to target the real-valued Rabi Phase field\n        - `...piecewise_constant([durations], [values]).detuning`:\n            to target the Detuning field\n        - `...piecewise_constant([durations], [values]).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return PiecewiseConstant(durations, values, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_linear","title":"piecewise_linear","text":"
piecewise_linear(durations, values)\n

Append or assign a piecewise linear waveform to current location(s), where the waveform is formed by connecting values[i], values[i+1] with linear segments.

The durations argument should have # of elements = len(values) - 1. durations should be the duration PER section of the waveform, NON-CUMULATIVE.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.piecewise_linear--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n# ramp our waveform up to a certain value, hold it\n# then ramp down. In this case, we ramp up to 2.0 rad/us in 0.3 us,\n# then hold it for 1.5 us before ramping down in 0.3 us back to 0.0 rad/us.\n>>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\nvalues=[0.0, 2.0, 2.0, 0.0])\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...piecewise_linear([durations], [values]) .linear(start, stop, duration): to append another linear waveform
    • ...piecewise_linear([durations], [values]).constant(value, duration): to append a constant waveform
    • ...piecewise_linear([durations], [values]) .piecewise_linear(durations, values): to append a piecewise linear waveform
    • ...piecewise_linear([durations], [values]) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...piecewise_linear([durations], [values]) .poly([coefficients], duration): to append a polynomial waveform
    • ...piecewise_linear([durations], [values]).apply(waveform): to append a pre-defined waveform
    • ...piecewise_linear([durations], [values]).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...piecewise_linear([durations], [values]) .slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...piecewise_linear([durations], [values]) .record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...piecewise_linear([durations], [values]).uniform: To address all atoms in the field
    • ...piecewise_linear([durations], [values]).scale(...): To address an atom at a specific location via index
    • ...piecewise_linear([durations], [values]).location(int)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...piecewise_linear([durations], [values]) .assign(variable_name = value): to assign a single value to a variable
    • ...piecewise_linear([durations], [values]) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...piecewise_linear([durations], [values]) .args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...piecewise_linear([durations], [values]).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...piecewise_linear([durations], [values]).bloqade: to run on the Bloqade local emulator
    • ...piecewise_linear([durations], [values]).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...piecewise_linear([durations], [values]).parallelize(spacing)
  • Start targeting another level coupling
    • ...piecewise_linear([durations], [values]).rydberg: to target the Rydberg level coupling
    • ...piecewise_linear([durations], [values]).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...piecewise_linear([durations], [values]).amplitude: to target the real-valued Rabi Amplitude field
    • ...piecewise_linear([durations], [values]).phase: to target the real-valued Rabi Phase field
    • ...piecewise_linear([durations], [values]).detuning: to target the Detuning field
    • ....rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef piecewise_linear(\n    self, durations: List[ScalarType], values: List[ScalarType]\n) -> \"PiecewiseLinear\":\n    \"\"\"\n    Append or assign a piecewise linear waveform to current location(s),\n    where the waveform is formed by connecting `values[i], values[i+1]`\n    with linear segments.\n\n    The `durations` argument should have # of elements = len(values) - 1.\n    `durations` should be the duration PER section of the waveform, NON-CUMULATIVE.\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    # ramp our waveform up to a certain value, hold it\n    # then ramp down. In this case, we ramp up to 2.0 rad/us in 0.3 us,\n    # then hold it for 1.5 us before ramping down in 0.3 us back to 0.0 rad/us.\n    >>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3],\n    values=[0.0, 2.0, 2.0, 0.0])\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...piecewise_linear([durations], [values])\n            .linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...piecewise_linear([durations], [values]).constant(value, duration)`:\n            to append a constant waveform\n        - `...piecewise_linear([durations], [values])\n            .piecewise_linear(durations, values)`:\n            to append a piecewise linear waveform\n        - `...piecewise_linear([durations], [values])\n            .piecewise_constant([durations], [values])`:\n            to append a piecewise constant waveform\n        - `...piecewise_linear([durations], [values])\n            .poly([coefficients], duration)`: to append a polynomial waveform\n        - `...piecewise_linear([durations], [values]).apply(waveform)`:\n            to append a pre-defined waveform\n        - `...piecewise_linear([durations], [values]).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...piecewise_linear([durations], [values])\n            .slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...piecewise_linear([durations], [values])\n            .record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...piecewise_linear([durations], [values]).uniform`:\n            To address all atoms in the field\n        - `...piecewise_linear([durations], [values]).scale(...)`:\n            To address an atom at a specific location via index\n        - `...piecewise_linear([durations], [values]).location(int)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning it a\n                list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...piecewise_linear([durations], [values])\n            .assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...piecewise_linear([durations], [values])\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...piecewise_linear([durations], [values])\n            .args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...piecewise_linear([durations], [values]).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...piecewise_linear([durations], [values]).bloqade`:\n            to run on the Bloqade local emulator\n        - `...piecewise_linear([durations], [values]).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...piecewise_linear([durations], [values]).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...piecewise_linear([durations], [values]).rydberg`:\n            to target the Rydberg level coupling\n        - `...piecewise_linear([durations], [values]).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level coupling\n      (previously selected as `rydberg` or `hyperfine`):\n        - `...piecewise_linear([durations], [values]).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...piecewise_linear([durations], [values]).phase`:\n            to target the real-valued Rabi Phase field\n        - `...piecewise_linear([durations], [values]).detuning`:\n            to target the Detuning field\n        - `....rabi`: to target the complex-valued Rabi field\n    \"\"\"\n    return PiecewiseLinear(durations, values, self)\n
"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.poly","title":"poly","text":"
poly(coeffs, duration)\n

Append or assign a waveform with a polynomial profile to current location(s).

You pass in a list of coefficients and a duration to this method which obeys the following expression:

wv(t) = coeffs[0] + coeffs[1]*t + coeffs[2]*t^2 + ... + coeffs[n]*t^n

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a \"drive\", one or a sum of drives creating a \"field\" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

"},{"location":"reference/bloqade/builder/waveform/#bloqade.builder.waveform.WaveformAttachable.poly--usage-example","title":"Usage Example:","text":"
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n>>> coeffs = [-1, 0.5, 1.2]\n# resulting polynomial is:\n# f(t) = -1 + 0.5*t + 1.2*t^2 with duration of\n# 0.5 us\n>>> prog.poly(coeffs, duration=0.5)\n
  • Your next steps include:
  • Continue building your waveform via:
    • ...poly([coeffs], duration).linear(start, stop, duration): to append another linear waveform
    • ...poly([coeffs], duration).constant(value, duration): to append a constant waveform
    • ...poly([coeffs], duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...poly([coeffs], duration) .piecewise_constant([durations],[values]): to append a piecewise constant waveform
    • ...poly([coeffs], duration).poly([coefficients], duration): to append a polynomial waveform
    • ...poly([coeffs], duration).apply(waveform): to append a pre-defined waveform
    • ...poly([coeffs], duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...poly([coeffs], duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...poly([coeffs], duration).record(\"you_variable_here\")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...poly([coeffs], duration).uniform: To address all atoms in the field
    • ...poly([coeffs], duration).location(int): To address an atom at a specific location via index
    • ...poly([coeffs], duration).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...poly([coeffs], duration).assign(variable_name = value): to assign a single value to a variable
    • ...poly([coeffs], duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...poly([coeffs], duration).args([\"previously_defined_var\"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...poly([coeffs], duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...poly([coeffs], duration).bloqade: to run on the Bloqade local emulator
    • ...poly([coeffs], duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...poly([coeffs], duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...poly([coeffs], duration).rydberg: to target the Rydberg level coupling
    • ...poly([coeffs], duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...poly([coeffs], duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...poly([coeffs], duration).phase: to target the real-valued Rabi Phase field
    • ...poly([coeffs], duration).detuning: to target the Detuning field
    • ...poly([coeffs], duration).rabi: to target the complex-valued Rabi field
Source code in src/bloqade/builder/waveform.py
@beartype\ndef poly(self, coeffs: List[ScalarType], duration: ScalarType) -> \"Poly\":\n    \"\"\"\n    Append or assign a waveform with a polynomial profile to current location(s).\n\n    You pass in a list of coefficients and a duration to this method which obeys\n    the following expression:\n\n    `\n    wv(t) = coeffs[0] + coeffs[1]*t + coeffs[2]*t^2 + ... + coeffs[n]*t^n\n    `\n\n    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)\n    previously without a waveform you will now have completed the construction\n    of a \"drive\", one or a sum of drives creating a \"field\"\n    (e.g. Real-valued Rabi Amplitude/Phase).\n\n    If you have already specified a waveform previously you will now be appending\n    this waveform to that previous waveform.\n\n    ### Usage Example:\n    ```\n    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform\n    >>> coeffs = [-1, 0.5, 1.2]\n    # resulting polynomial is:\n    # f(t) = -1 + 0.5*t + 1.2*t^2 with duration of\n    # 0.5 us\n    >>> prog.poly(coeffs, duration=0.5)\n    ```\n\n    - Your next steps include:\n    - Continue building your waveform via:\n        - `...poly([coeffs], duration).linear(start, stop, duration)`:\n            to append another linear waveform\n        - `...poly([coeffs], duration).constant(value, duration)`:\n            to append a constant waveform\n        - `...poly([coeffs], duration)\n            .piecewise_linear([durations], [values])`:\n            to append a piecewise linear waveform\n        - `...poly([coeffs], duration)\n            .piecewise_constant([durations],[values])`:\n            to append a piecewise constant waveform\n        - `...poly([coeffs], duration).poly([coefficients], duration)`:\n            to append a polynomial waveform\n        - `...poly([coeffs], duration).apply(waveform)`:\n            to append a pre-defined waveform\n        - `...poly([coeffs], duration).fn(f(t,...))`:\n            to append a waveform defined by a python function\n    - Slice a portion of the waveform to be used:\n        - `...poly([coeffs], duration).slice(start, stop, duration)`\n    - Save the ending value of your waveform to be reused elsewhere\n        - `...poly([coeffs], duration).record(\"you_variable_here\")`\n    - Begin constructing another drive by starting a new spatial modulation\n      (this drive will be summed to the one you just created):\n        - `...poly([coeffs], duration).uniform`:\n            To address all atoms in the field\n        - `...poly([coeffs], duration).location(int)`:\n            To address an atom at a specific location via index\n        - `...poly([coeffs], duration).scale(...)`\n            - To address an atom at a specific location via variable\n            - To address multiple atoms at specific locations by\n                specifying a single variable and then assigning\n                it a list of coordinates\n    - Assign values to pre-existing variables via:\n        - `...poly([coeffs], duration).assign(variable_name = value)`:\n            to assign a single value to a variable\n        - `...poly([coeffs], duration)\n            .batch_assign(variable_name = [value1, ...])`:\n            to assign multiple values to a variable\n        - `...poly([coeffs], duration).args([\"previously_defined_var\"])`:\n            to defer assignment of a variable to execution time\n    - Select the backend you want your program to run on via:\n        - `...poly([coeffs], duration).braket`:\n            to run on Braket local emulator or QuEra hardware remotely\n        - `...poly([coeffs], duration).bloqade`:\n            to run on the Bloqade local emulator\n        - `...poly([coeffs], duration).device`:\n            to specify the backend via string\n    - Choose to parallelize your atom geometry,\n      duplicating it to fill the whole space:\n        - `...poly([coeffs], duration).parallelize(spacing)`\n    - Start targeting another level coupling\n        - `...poly([coeffs], duration).rydberg`:\n            to target the Rydberg level coupling\n        - `...poly([coeffs], duration).hyperfine`:\n            to target the Hyperfine level coupling\n    - Start targeting other fields within your current level\n      coupling (previously selected as `rydberg` or `hyperfine`):\n        - `...poly([coeffs], duration).amplitude`:\n            to target the real-valued Rabi Amplitude field\n        - `...poly([coeffs], duration).phase`:\n            to target the real-valued Rabi Phase field\n        - `...poly([coeffs], duration).detuning`:\n            to target the Detuning field\n        - `...poly([coeffs], duration).rabi`:\n            to target the complex-valued Rabi field\n    \"\"\"\n    return Poly(coeffs, duration, self)\n
"},{"location":"reference/bloqade/builder/backend/","title":"Index","text":""},{"location":"reference/bloqade/builder/backend/#bloqade.builder.backend.BackendRoute","title":"BackendRoute","text":"
BackendRoute(parent=None)\n

Bases: QuEraService, BraketService, BloqadeService

Specify the backend to run your program on via a string (versus more formal builder syntax) of specifying the vendor/product first (Bloqade/Braket) and narrowing it down (e.g: ...device(\"quera.aquila\") versus ...quera.aquila()) - You can pass the following arguments: - \"braket.aquila\" - \"braket.local_emulator\" - \"bloqade.python\" - \"bloqade.julia\"

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/bloqade/","title":"Bloqade","text":""},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeDeviceRoute","title":"BloqadeDeviceRoute","text":"
BloqadeDeviceRoute(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeDeviceRoute.python","title":"python","text":"
python()\n

Specify the Bloqade Python backend.

  • Possible Next Steps:
    • ...python().run(shots): to submit to the python emulator and await results
Source code in src/bloqade/builder/backend/bloqade.py
def python(self):\n    \"\"\"\n    Specify the Bloqade Python backend.\n\n    - Possible Next Steps:\n        - `...python().run(shots)`:\n            to submit to the python emulator and await results\n    \"\"\"\n    return self.parse().bloqade.python()\n
"},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeService","title":"BloqadeService","text":"
BloqadeService(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeService.bloqade","title":"bloqade property","text":"
bloqade\n

Specify the Bloqade backend.

  • Possible Next Steps:
    • ...bloqade.python(): target submission to the Bloqade python backend
    • ...bloqade.julia(): (CURRENTLY NOT IMPLEMENTED!)target submission to the Bloqade.jl backend
"},{"location":"reference/bloqade/builder/backend/braket/","title":"Braket","text":""},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute","title":"BraketDeviceRoute","text":"
BraketDeviceRoute(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute.aquila","title":"aquila","text":"
aquila()\n

Specify QuEra's Aquila QPU on Braket to submit your program to.

The number of shots you specify in the subsequent .run method will either: - dictate the number of times your program is run - dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep

  • Possible next steps are:
    • ...aquila().run(shots): To submit to hardware and WAIT for results (blocking)
    • ...aquila().run_async(shots): To submit to hardware and immediately allow for other operations to occur
Source code in src/bloqade/builder/backend/braket.py
def aquila(self) -> \"BraketHardwareRoutine\":\n    \"\"\"\n    Specify QuEra's Aquila QPU on Braket to submit your program to.\n\n    The number of shots you specify in the subsequent `.run` method will either:\n        - dictate the number of times your program is run\n        - dictate the number of times per parameter your program is run if\n          you have a variable with batch assignments/intend to conduct\n          a parameter sweep\n\n\n    - Possible next steps are:\n        - `...aquila().run(shots)`: To submit to hardware and WAIT for\n            results (blocking)\n        - `...aquila().run_async(shots)`: To submit to hardware and immediately\n            allow for other operations to occur\n    \"\"\"\n    return self.parse().braket.aquila()\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute.device","title":"device","text":"
device(device_arn)\n

Specify QPU based on the device ARN on Braket to submit your program to.

The number of shots you specify in the subsequent .run method will either: - dictate the number of times your program is run - dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep

  • Possible next steps are:
    • ...device(arn).run(shots): To submit to hardware and WAIT for results (blocking)
    • ...device(arn).run_async(shots): To submit to hardware and immediately allow for other operations to occur
Source code in src/bloqade/builder/backend/braket.py
def device(self, device_arn) -> \"BraketHardwareRoutine\":\n    \"\"\"\n    Specify QPU based on the device ARN on Braket to submit your program to.\n\n    The number of shots you specify in the subsequent `.run` method will either:\n        - dictate the number of times your program is run\n        - dictate the number of times per parameter your program is run if\n            you have a variable with batch assignments/intend to conduct\n            a parameter sweep\n\n\n    - Possible next steps are:\n        - `...device(arn).run(shots)`: To submit to hardware and WAIT for\n            results (blocking)\n        - `...device(arn).run_async(shots)`: To submit to hardware and immediately\n            allow for other operations to occur\n    \"\"\"\n    return self.parse().braket.device(device_arn)\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute.local_emulator","title":"local_emulator","text":"
local_emulator()\n

Specify the Braket local emulator to submit your program to.

  • The number of shots you specify in the subsequent .run method will either:
    • dictate the number of times your program is run
    • dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep
  • Possible next steps are:
    • ...local_emulator().run(shots): to submit to the emulator and await results
Source code in src/bloqade/builder/backend/braket.py
def local_emulator(self) -> \"BraketLocalEmulatorRoutine\":\n    \"\"\"\n    Specify the Braket local emulator to submit your program to.\n\n    - The number of shots you specify in the subsequent `.run` method will either:\n        - dictate the number of times your program is run\n        - dictate the number of times per parameter your program is run if\n          you have a variable with batch assignments/intend to\n          conduct a parameter sweep\n    - Possible next steps are:\n        - `...local_emulator().run(shots)`: to submit to the emulator\n            and await results\n\n    \"\"\"\n    return self.parse().braket.local_emulator()\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketService","title":"BraketService","text":"
BraketService(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketService.braket","title":"braket property","text":"
braket\n

Specify the Braket backend. This allows you to access the AWS Braket local emulator OR go submit things to QuEra hardware on AWS Braket service.

  • Possible Next Steps are:
    • ...braket.aquila(): target submission to the QuEra Aquila QPU
    • ...braket.local_emulator(): target submission to the Braket local emulator
"},{"location":"reference/bloqade/builder/backend/quera/","title":"Quera","text":""},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute","title":"QuEraDeviceRoute","text":"
QuEraDeviceRoute(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.aquila","title":"aquila","text":"
aquila()\n

Specify QuEra's Aquila QPU

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def aquila(self):\n    \"\"\"\n    Specify QuEra's Aquila QPU\n\n    Return:\n        QuEraHardwareRoutine\n\n\n    - Possible Next:\n\n        -> `...aquila().submit`\n            :: submit aync remote job\n\n        -> `...aquila().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...aquila().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n    \"\"\"\n    return self.parse().quera.aquila()\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.cloud_mock","title":"cloud_mock","text":"
cloud_mock()\n

Specify QuEra's Remote Mock QPU

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def cloud_mock(self):\n    \"\"\"\n    Specify QuEra's Remote Mock QPU\n\n    Return:\n        QuEraHardwareRoutine\n\n    - Possible Next:\n\n        -> `...aquila().submit`\n            :: submit aync remote job\n\n        -> `...aquila().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...aquila().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n\n    \"\"\"\n    return self.parse().quera.cloud_mock()\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.device","title":"device","text":"
device(config_file=None, **api_config)\n

Specify QuEra's QPU device,

Parameters:

Name Type Description Default config_file str

file that speficy the target hardware

None Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...device().submit :: submit aync remote job

    -> ...device().run :: submit job and wait until job finished and results returned

    -> ...device().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def device(self, config_file: Optional[str] = None, **api_config):\n    \"\"\"\n    Specify QuEra's QPU device,\n\n    Args:\n        config_file (str): file that speficy the target hardware\n\n    Return:\n        QuEraHardwareRoutine\n\n    - Possible Next:\n\n        -> `...device().submit`\n            :: submit aync remote job\n\n        -> `...device().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...device().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n    \"\"\"\n    return self.parse().quera.device(config_file, **api_config)\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute.mock","title":"mock","text":"
mock(state_file='.mock_state.txt', submission_error=False)\n

Specify mock, testing locally.

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in src/bloqade/builder/backend/quera.py
def mock(self, state_file: str = \".mock_state.txt\", submission_error: bool = False):\n    \"\"\"\n    Specify mock, testing locally.\n\n    Return:\n        QuEraHardwareRoutine\n\n    - Possible Next:\n\n        -> `...aquila().submit`\n            :: submit aync remote job\n\n        -> `...aquila().run`\n            :: submit job and wait until job finished\n            and results returned\n\n        -> `...aquila().__callable__`\n            :: submit job and wait until job finished\n            and results returned\n\n\n\n    \"\"\"\n    return self.parse().quera.mock(\n        state_file=state_file, submission_error=submission_error\n    )\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraService","title":"QuEraService","text":"
QuEraService(parent=None)\n

Bases: Builder

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraService.quera","title":"quera property","text":"
quera\n
  • Specify Quera backend
  • Possible Next:

    -> ...quera.aquila :: Aquila QPU

    -> ...quera.mock :: mock backend, meant for testings

    -> ...quera.device :: QuEra QPU, specifiy by config_file

"},{"location":"reference/bloqade/builder/parse/","title":"Index","text":""},{"location":"reference/bloqade/builder/parse/builder/","title":"Builder","text":""},{"location":"reference/bloqade/builder/parse/stream/","title":"Stream","text":""},{"location":"reference/bloqade/builder/parse/stream/#bloqade.builder.parse.stream.BuilderStream","title":"BuilderStream dataclass","text":"

Represents a stream of builder nodes.

"},{"location":"reference/bloqade/builder/parse/stream/#bloqade.builder.parse.stream.BuilderStream.eat","title":"eat","text":"
eat(types, skips=None)\n

Scan the stream until a node of type in types or skips is found.

Parameters:

Name Type Description Default types List[Type[Builder]]

List of types to move the stream pointer to

required skips List[Type[Builder]] | None

List of types to end the

None

Returns:

Name Type Description BuilderNode BuilderNode

The beginning of the stream which matches a type in types.

Source code in src/bloqade/builder/parse/stream.py
def eat(\n    self, types: List[Type[Builder]], skips: Optional[List[Type[Builder]]] = None\n) -> BuilderNode:\n    \"\"\"Scan the stream until a node of type in `types` or `skips` is found.\n\n    Args:\n        types (List[Type[Builder]]): List of types to move the stream pointer to\n        skips (List[Type[Builder]] | None, optional): List of types to end the\n        stream scan\n\n    Returns:\n        BuilderNode: The beginning of the stream which matches a type in `types`.\n    \"\"\"\n    head = self.read_next(types)\n    curr = head\n    while curr is not None:\n        if type(curr.node) not in types:\n            if skips and type(curr.node) not in skips:\n                break\n        curr = curr.next\n    self.curr = curr\n    return head\n
"},{"location":"reference/bloqade/builder/parse/trait/","title":"Trait","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.Parse","title":"Parse","text":"

Bases: ParseRegister, ParseSequence, ParseCircuit, ParseRoutine

"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.Parse.n_atoms","title":"n_atoms property","text":"
n_atoms\n

Return the number of atoms in the program.

"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseCircuit","title":"ParseCircuit","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseCircuit.parse_circuit","title":"parse_circuit","text":"
parse_circuit()\n

Parse the analog circuit from the program.

Source code in src/bloqade/builder/parse/trait.py
def parse_circuit(self: \"Builder\") -> \"AnalogCircuit\":\n    \"\"\"Parse the analog circuit from the program.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse_circuit(self)\n
"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRegister","title":"ParseRegister","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRegister.parse_register","title":"parse_register","text":"
parse_register()\n

Parse the arrangement of atoms of the program.

Source code in src/bloqade/builder/parse/trait.py
def parse_register(self: \"Builder\") -> Union[\"AtomArrangement\", \"ParallelRegister\"]:\n    \"\"\"Parse the arrangement of atoms of the program.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse_register(self)\n
"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRoutine","title":"ParseRoutine","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseRoutine.parse","title":"parse","text":"
parse()\n

Parse the program to return a Routine object.

Source code in src/bloqade/builder/parse/trait.py
def parse(self: \"Builder\") -> \"Routine\":\n    \"\"\"Parse the program to return a Routine object.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse(self)\n
"},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseSequence","title":"ParseSequence","text":""},{"location":"reference/bloqade/builder/parse/trait/#bloqade.builder.parse.trait.ParseSequence.parse_sequence","title":"parse_sequence","text":"
parse_sequence()\n

Parse the pulse sequence part of the program.

Source code in src/bloqade/builder/parse/trait.py
def parse_sequence(self: \"Builder\") -> \"Sequence\":\n    \"\"\"Parse the pulse sequence part of the program.\"\"\"\n    from bloqade.builder.parse.builder import Parser\n\n    return Parser().parse_sequence(self)\n
"},{"location":"reference/bloqade/compiler/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/common/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/common/assignment_scan/","title":"Assignment scan","text":""},{"location":"reference/bloqade/compiler/analysis/common/check_slices/","title":"Check slices","text":""},{"location":"reference/bloqade/compiler/analysis/common/is_constant/","title":"Is constant","text":""},{"location":"reference/bloqade/compiler/analysis/common/is_hyperfine/","title":"Is hyperfine","text":""},{"location":"reference/bloqade/compiler/analysis/common/scan_channels/","title":"Scan channels","text":""},{"location":"reference/bloqade/compiler/analysis/common/scan_variables/","title":"Scan variables","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/#bloqade.compiler.analysis.hardware.BasicLatticeValidation","title":"BasicLatticeValidation","text":"
BasicLatticeValidation(capabilities)\n

Bases: BloqadeIRVisitor

This visitor checks that the AtomArrangement is within the bounds of the lattice and that the number of sites is within the maximum number of sites.

Source code in src/bloqade/compiler/analysis/hardware/lattice.py
def __init__(self, capabilities: QuEraCapabilities):\n    self.capabilities = capabilities\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/#bloqade.compiler.analysis.hardware.ValidateChannels","title":"ValidateChannels","text":"
ValidateChannels()\n

Bases: BloqadeIRVisitor

Checks to make sure the given sequence can be compiled to hardware.

This check looks at the spatial modulations and the level coupling to determine if the sequence can be compiled to hardware.

Source code in src/bloqade/compiler/analysis/hardware/channels.py
def __init__(self):\n    self.field_name = None\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/channels/","title":"Channels","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/channels/#bloqade.compiler.analysis.hardware.channels.ValidateChannels","title":"ValidateChannels","text":"
ValidateChannels()\n

Bases: BloqadeIRVisitor

Checks to make sure the given sequence can be compiled to hardware.

This check looks at the spatial modulations and the level coupling to determine if the sequence can be compiled to hardware.

Source code in src/bloqade/compiler/analysis/hardware/channels.py
def __init__(self):\n    self.field_name = None\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/lattice/","title":"Lattice","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/lattice/#bloqade.compiler.analysis.hardware.lattice.BasicLatticeValidation","title":"BasicLatticeValidation","text":"
BasicLatticeValidation(capabilities)\n

Bases: BloqadeIRVisitor

This visitor checks that the AtomArrangement is within the bounds of the lattice and that the number of sites is within the maximum number of sites.

Source code in src/bloqade/compiler/analysis/hardware/lattice.py
def __init__(self, capabilities: QuEraCapabilities):\n    self.capabilities = capabilities\n
"},{"location":"reference/bloqade/compiler/analysis/hardware/piecewise_constant/","title":"Piecewise constant","text":""},{"location":"reference/bloqade/compiler/analysis/hardware/piecewise_linear/","title":"Piecewise linear","text":""},{"location":"reference/bloqade/compiler/analysis/python/","title":"Index","text":""},{"location":"reference/bloqade/compiler/analysis/python/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/compiler/passes/","title":"Index","text":""},{"location":"reference/bloqade/compiler/passes/emulator/","title":"Emulator","text":""},{"location":"reference/bloqade/compiler/passes/hardware/","title":"Index","text":""},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.analyze_channels","title":"analyze_channels","text":"
analyze_channels(circuit)\n
  1. Scan channels

This pass checks to make sure that: * There is no hyperfine coupling in the sequence * There are no non-uniform spatial modulation for rabi phase and amplitude * there is no more than one non-uniform spatial modulation for detuning

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to analyze

required

Returns:

Name Type Description level_couplings Dict

Dictionary containing the required channels for the sequence. Note that this will insert a uniform field for any missing channels.

Raises:

Type Description ValueError

If there is hyperfine coupling in the sequence.

ValueError

If there is more than one non-uniform spatial modulation for detuning.

ValueError

If there are non-uniform spatial modulations for rabi phase and amplitude.

Source code in src/bloqade/compiler/passes/hardware/define.py
def analyze_channels(circuit: analog_circuit.AnalogCircuit) -> Dict:\n    \"\"\"1. Scan channels\n\n    This pass checks to make sure that:\n    * There is no hyperfine coupling in the sequence\n    * There are no non-uniform spatial modulation for rabi phase and amplitude\n    * there is no more than one non-uniform spatial modulation for detuning\n\n    Args:\n        circuit: AnalogCircuit to analyze\n\n    Returns:\n        level_couplings: Dictionary containing the required channels for the\n            sequence. Note that this will insert a uniform field for any missing\n            channels.\n\n    Raises:\n        ValueError: If there is hyperfine coupling in the sequence.\n        ValueError: If there is more than one non-uniform spatial modulation for\n            detuning.\n        ValueError: If there are non-uniform spatial modulations for rabi phase\n            and amplitude.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import ValidateChannels\n    from bloqade.compiler.analysis.common import ScanChannels\n\n    ValidateChannels().scan(circuit)\n    level_couplings = ScanChannels().scan(circuit)\n\n    # add missing channels\n    fields = level_couplings[sequence.rydberg]\n    # detuning, phase and amplitude are required\n    # to have at least a uniform field\n    updated_fields = {\n        field_name: fields.get(field_name, {field.Uniform}).union({field.Uniform})\n        for field_name in [pulse.detuning, pulse.rabi.amplitude, pulse.rabi.phase]\n    }\n\n    return {sequence.rydberg: updated_fields}\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.assign_circuit","title":"assign_circuit","text":"
assign_circuit(circuit, assignments)\n
  1. Assign variables and validate assignment

This pass assigns variables to the circuit and validates that all variables have been assigned.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to assign variables to

required assignments Dict[str, ParamType]

Dictionary containing the assignments for the variables in the circuit.

required

Returns:

Name Type Description assigned_circuit Tuple[AnalogCircuit, Dict]

AnalogCircuit with variables assigned.

Raises:

Type Description ValueError

If there are any variables that have not been assigned.

Source code in src/bloqade/compiler/passes/hardware/define.py
def assign_circuit(\n    circuit: analog_circuit.AnalogCircuit, assignments: Dict[str, ParamType]\n) -> Tuple[analog_circuit.AnalogCircuit, Dict]:\n    \"\"\"3. Assign variables and validate assignment\n\n    This pass assigns variables to the circuit and validates that all variables\n    have been assigned.\n\n    Args:\n        circuit: AnalogCircuit to assign variables to\n        assignments: Dictionary containing the assignments for the variables in\n            the circuit.\n\n    Returns:\n        assigned_circuit: AnalogCircuit with variables assigned.\n\n    Raises:\n        ValueError: If there are any variables that have not been assigned.\n\n    \"\"\"\n    from bloqade.compiler.analysis.common import AssignmentScan, ScanVariables\n    from bloqade.compiler.rewrite.common import AssignBloqadeIR\n\n    final_assignments = AssignmentScan(assignments).scan(circuit)\n\n    assigned_circuit = AssignBloqadeIR(final_assignments).visit(circuit)\n\n    assignment_analysis = ScanVariables().scan(assigned_circuit)\n\n    if not assignment_analysis.is_assigned:\n        missing_vars = assignment_analysis.scalar_vars.union(\n            assignment_analysis.vector_vars\n        )\n        raise ValueError(\n            \"Missing assignments for variables:\\n\"\n            + (\"\\n\".join(f\"{var}\" for var in missing_vars))\n            + \"\\n\"\n        )\n\n    return assigned_circuit, final_assignments\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.canonicalize_circuit","title":"canonicalize_circuit","text":"
canonicalize_circuit(circuit, level_couplings)\n
  1. Insert zero waveform in the explicit time intervals missing a waveform

This pass inserts a zero waveform in the explicit time intervals missing a waveform. This is required for later analysis passes to check that the waveforms are compatible with the hardware.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to add padding to

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Return circuit: AnalogCircuit with zero waveforms inserted in the explicit time intervals missing a waveform.

Source code in src/bloqade/compiler/passes/hardware/define.py
def canonicalize_circuit(\n    circuit: analog_circuit.AnalogCircuit, level_couplings: Dict\n) -> analog_circuit.AnalogCircuit:\n    \"\"\"2. Insert zero waveform in the explicit time intervals missing a waveform\n\n    This pass inserts a zero waveform in the explicit time intervals missing a\n    waveform. This is required for later analysis passes to check that the\n    waveforms are compatible with the hardware.\n\n    Args:\n        circuit: AnalogCircuit to add padding to\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Return\n        circuit: AnalogCircuit with zero waveforms inserted in the explicit time\n            intervals missing a waveform.\n\n    \"\"\"\n    from bloqade.compiler.rewrite.common import (\n        AddPadding,\n        AssignToLiteral,\n        Canonicalizer,\n    )\n\n    circuit = AddPadding(level_couplings).visit(circuit)\n    # these two passes are equivalent to a constant propagation pass\n    circuit = AssignToLiteral().visit(circuit)\n    circuit = Canonicalizer().visit(circuit)\n\n    return circuit\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.generate_ahs_code","title":"generate_ahs_code","text":"
generate_ahs_code(capabilities, level_couplings, circuit)\n
  1. generate ahs code

Generates the AHS code for the given circuit. This includes generating the lattice data, global detuning, global amplitude, global phase, local detuning and lattice site coefficients (if applicable).

Parameters:

Name Type Description Default capabilities QuEraCapabilities | None

Capabilities of the hardware.

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required circuit AnalogCircuit

AnalogCircuit to generate AHS code for.

required

Returns:

Name Type Description ahs_components AHSComponents

A collection of the AHS components generated for the given circuit. Can be used to generate the QuEra and Braket IR.

Raises:

Type Description ValueError

If the capabilities are not provided but the circuit has a ParallelRegister. This is because the ParallelRegister requires the capabilities to generate the lattice data.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_ahs_code(\n    capabilities: Optional[QuEraCapabilities],\n    level_couplings: Dict,\n    circuit: analog_circuit.AnalogCircuit,\n) -> AHSComponents:\n    \"\"\"5. generate ahs code\n\n    Generates the AHS code for the given circuit. This includes generating the\n    lattice data, global detuning, global amplitude, global phase, local\n    detuning and lattice site coefficients (if applicable).\n\n    Args:\n        capabilities (QuEraCapabilities | None): Capabilities of the hardware.\n        level_couplings (Dict): Dictionary containing the given channels for the\n            sequence.\n        circuit (AnalogCircuit): AnalogCircuit to generate AHS code for.\n\n    Returns:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit. Can be used to generate the QuEra\n            and Braket IR.\n\n    Raises:\n        ValueError: If the capabilities are not provided but the circuit has\n            a ParallelRegister. This is because the ParallelRegister requires\n            the capabilities to generate the lattice data.\n\n    \"\"\"\n    from bloqade.compiler.codegen.hardware import (\n        GenerateLattice,\n        GenerateLatticeSiteCoefficients,\n        GeneratePiecewiseLinearChannel,\n        GeneratePiecewiseConstantChannel,\n    )\n    from bloqade.compiler.analysis.hardware import BasicLatticeValidation\n\n    if capabilities is not None:\n        # only validate the lattice if capabilities are provided\n        BasicLatticeValidation(capabilities).visit(circuit)\n\n    ahs_lattice_data = GenerateLattice(capabilities).emit(circuit)\n\n    global_detuning = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.detuning, field.Uniform\n    ).visit(circuit)\n\n    global_amplitude = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.rabi.amplitude, field.Uniform\n    ).visit(circuit)\n\n    global_phase = GeneratePiecewiseConstantChannel(\n        sequence.rydberg, pulse.rabi.phase, field.Uniform\n    ).visit(circuit)\n\n    local_detuning = None\n    lattice_site_coefficients = None\n\n    extra_sm = set(level_couplings[sequence.rydberg][pulse.detuning]) - {field.Uniform}\n\n    if extra_sm:\n        if capabilities is not None and capabilities.capabilities.rydberg.local is None:\n            raise ValueError(\n                \"Device does not support local detuning, but the program has a \"\n                \"non-uniform spatial modulation for detuning.\"\n            )\n\n        sm = extra_sm.pop()\n\n        lattice_site_coefficients = GenerateLatticeSiteCoefficients(\n            parallel_decoder=ahs_lattice_data.parallel_decoder\n        ).emit(circuit)\n\n        local_detuning = GeneratePiecewiseLinearChannel(\n            sequence.rydberg, pulse.detuning, sm\n        ).visit(circuit)\n\n    return AHSComponents(\n        lattice_data=ahs_lattice_data,\n        global_detuning=global_detuning,\n        global_amplitude=global_amplitude,\n        global_phase=global_phase,\n        local_detuning=local_detuning,\n        lattice_site_coefficients=lattice_site_coefficients,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.generate_braket_ir","title":"generate_braket_ir","text":"
generate_braket_ir(ahs_components, shots)\n
  1. generate braket ir

This pass takes the AHS components and generates the Braket IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification BraketTaskSpecification

Braket IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_braket_ir(\n    ahs_components: AHSComponents, shots: int\n) -> BraketTaskSpecification:\n    \"\"\"7. generate braket ir\n\n    This pass takes the AHS components and generates the Braket IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (BraketTaskSpecification): Braket IR for the given\n            circuit.\n\n    \"\"\"\n    import braket.ir.ahs as ahs\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    ahs_register = ahs.AtomArrangement(\n        sites=list(map(convert_coordinate_units, ahs_components.lattice_data.sites)),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning_time_series = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning_time_series = ahs.TimeSeries(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n        )\n\n    amplitude_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    phase_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = ahs.PhysicalField(\n        time_series=global_detuning_time_series,\n        pattern=\"uniform\",\n    )\n\n    amplitude = ahs.PhysicalField(\n        time_series=amplitude_time_series,\n        pattern=\"uniform\",\n    )\n\n    phase = ahs.PhysicalField(\n        time_series=phase_time_series,\n        pattern=\"uniform\",\n    )\n\n    local_detuning = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = ahs.PhysicalField(\n            time_series=local_detuning_time_series,\n            pattern=ahs_components.lattice_site_coefficients,\n        )\n\n    driving_field = ahs.DrivingField(\n        detuning=detuning,\n        amplitude=amplitude,\n        phase=phase,\n    )\n\n    shiftingFields = []\n    if ahs_components.lattice_site_coefficients is not None:\n        shiftingFields = [ahs.ShiftingField(magnitude=local_detuning)]\n\n    program = ahs.Program(\n        setup=ahs.Setup(ahs_register=ahs_register),\n        hamiltonian=ahs.Hamiltonian(\n            drivingFields=[driving_field],\n            shiftingFields=shiftingFields,\n        ),\n    )\n\n    return BraketTaskSpecification(nshots=shots, program=program)\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.generate_quera_ir","title":"generate_quera_ir","text":"
generate_quera_ir(ahs_components, shots)\n
  1. generate quera ir

This pass takes the AHS components and generates the QuEra IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification QuEraTaskSpecification

QuEra IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_quera_ir(\n    ahs_components: AHSComponents, shots: int\n) -> QuEraTaskSpecification:\n    \"\"\"7. generate quera ir\n\n    This pass takes the AHS components and generates the QuEra IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (QuEraTaskSpecification): QuEra IR for the given\n            circuit.\n\n    \"\"\"\n    import bloqade.submission.ir.task_specification as task_spec\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    lattice = task_spec.Lattice(\n        sites=list(\n            map(\n                convert_coordinate_units,\n                ahs_components.lattice_data.sites,\n            )\n        ),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning = None\n\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = task_spec.LocalField(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n            lattice_site_coefficients=ahs_components.lattice_site_coefficients,\n        )\n\n    rabi_frequency_amplitude_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    rabi_frequency_phase_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = task_spec.Detuning(\n        global_=global_detuning,\n        local=local_detuning,\n    )\n\n    rabi_frequency_amplitude = task_spec.RabiFrequencyAmplitude(\n        global_=rabi_frequency_amplitude_field,\n    )\n\n    rabi_frequency_phase = task_spec.RabiFrequencyPhase(\n        global_=rabi_frequency_phase_field,\n    )\n\n    rydberg = task_spec.RydbergHamiltonian(\n        rabi_frequency_amplitude=rabi_frequency_amplitude,\n        rabi_frequency_phase=rabi_frequency_phase,\n        detuning=detuning,\n    )\n\n    effective_hamiltonian = task_spec.EffectiveHamiltonian(\n        rydberg=rydberg,\n    )\n\n    return task_spec.QuEraTaskSpecification(\n        nshots=shots,\n        lattice=lattice,\n        effective_hamiltonian=effective_hamiltonian,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/#bloqade.compiler.passes.hardware.validate_waveforms","title":"validate_waveforms","text":"
validate_waveforms(level_couplings, circuit)\n
  1. validate piecewise linear and piecewise constant pieces of pulses

This pass check to make sure that the waveforms are compatible with the hardware. This includes checking that the waveforms are piecewise linear or piecewise constant. It also checks that the waveforms are compatible with the given channels.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to validate waveforms for

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Raises:

Type Description ValueError

If the waveforms are not piecewise linear or piecewise constant, e.g. the waveform is not continuous.

ValueError

If a waveform segment is not compatible with the given channels.

Source code in src/bloqade/compiler/passes/hardware/define.py
def validate_waveforms(\n    level_couplings: Dict, circuit: analog_circuit.AnalogCircuit\n) -> None:\n    \"\"\"4. validate piecewise linear and piecewise constant pieces of pulses\n\n    This pass check to make sure that the waveforms are compatible with the\n    hardware. This includes checking that the waveforms are piecewise linear or\n    piecewise constant. It also checks that the waveforms are compatible with\n    the given channels.\n\n    Args:\n        circuit: AnalogCircuit to validate waveforms for\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Raises:\n        ValueError: If the waveforms are not piecewise linear or piecewise\n            constant, e.g. the waveform is not continuous.\n        ValueError: If a waveform segment is not compatible with the given\n            channels.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import (\n        ValidatePiecewiseConstantChannel,\n        ValidatePiecewiseLinearChannel,\n    )\n    from bloqade.compiler.analysis.common import CheckSlices\n\n    channel_iter = (\n        (level_coupling, field_name, sm)\n        for level_coupling, fields in level_couplings.items()\n        for field_name, spatial_modulations in fields.items()\n        for sm in spatial_modulations\n    )\n    for channel in channel_iter:\n        if channel[1] in [pulse.detuning, pulse.rabi.amplitude]:\n            ValidatePiecewiseLinearChannel(*channel).visit(circuit)\n        else:\n            ValidatePiecewiseConstantChannel(*channel).visit(circuit)\n\n    CheckSlices().visit(circuit)\n\n    if circuit.sequence.duration() == 0:\n        raise ValueError(\"Circuit Duration must be be non-zero\")\n
"},{"location":"reference/bloqade/compiler/passes/hardware/components/","title":"Components","text":""},{"location":"reference/bloqade/compiler/passes/hardware/define/","title":"Define","text":""},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.analyze_channels","title":"analyze_channels","text":"
analyze_channels(circuit)\n
  1. Scan channels

This pass checks to make sure that: * There is no hyperfine coupling in the sequence * There are no non-uniform spatial modulation for rabi phase and amplitude * there is no more than one non-uniform spatial modulation for detuning

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to analyze

required

Returns:

Name Type Description level_couplings Dict

Dictionary containing the required channels for the sequence. Note that this will insert a uniform field for any missing channels.

Raises:

Type Description ValueError

If there is hyperfine coupling in the sequence.

ValueError

If there is more than one non-uniform spatial modulation for detuning.

ValueError

If there are non-uniform spatial modulations for rabi phase and amplitude.

Source code in src/bloqade/compiler/passes/hardware/define.py
def analyze_channels(circuit: analog_circuit.AnalogCircuit) -> Dict:\n    \"\"\"1. Scan channels\n\n    This pass checks to make sure that:\n    * There is no hyperfine coupling in the sequence\n    * There are no non-uniform spatial modulation for rabi phase and amplitude\n    * there is no more than one non-uniform spatial modulation for detuning\n\n    Args:\n        circuit: AnalogCircuit to analyze\n\n    Returns:\n        level_couplings: Dictionary containing the required channels for the\n            sequence. Note that this will insert a uniform field for any missing\n            channels.\n\n    Raises:\n        ValueError: If there is hyperfine coupling in the sequence.\n        ValueError: If there is more than one non-uniform spatial modulation for\n            detuning.\n        ValueError: If there are non-uniform spatial modulations for rabi phase\n            and amplitude.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import ValidateChannels\n    from bloqade.compiler.analysis.common import ScanChannels\n\n    ValidateChannels().scan(circuit)\n    level_couplings = ScanChannels().scan(circuit)\n\n    # add missing channels\n    fields = level_couplings[sequence.rydberg]\n    # detuning, phase and amplitude are required\n    # to have at least a uniform field\n    updated_fields = {\n        field_name: fields.get(field_name, {field.Uniform}).union({field.Uniform})\n        for field_name in [pulse.detuning, pulse.rabi.amplitude, pulse.rabi.phase]\n    }\n\n    return {sequence.rydberg: updated_fields}\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.assign_circuit","title":"assign_circuit","text":"
assign_circuit(circuit, assignments)\n
  1. Assign variables and validate assignment

This pass assigns variables to the circuit and validates that all variables have been assigned.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to assign variables to

required assignments Dict[str, ParamType]

Dictionary containing the assignments for the variables in the circuit.

required

Returns:

Name Type Description assigned_circuit Tuple[AnalogCircuit, Dict]

AnalogCircuit with variables assigned.

Raises:

Type Description ValueError

If there are any variables that have not been assigned.

Source code in src/bloqade/compiler/passes/hardware/define.py
def assign_circuit(\n    circuit: analog_circuit.AnalogCircuit, assignments: Dict[str, ParamType]\n) -> Tuple[analog_circuit.AnalogCircuit, Dict]:\n    \"\"\"3. Assign variables and validate assignment\n\n    This pass assigns variables to the circuit and validates that all variables\n    have been assigned.\n\n    Args:\n        circuit: AnalogCircuit to assign variables to\n        assignments: Dictionary containing the assignments for the variables in\n            the circuit.\n\n    Returns:\n        assigned_circuit: AnalogCircuit with variables assigned.\n\n    Raises:\n        ValueError: If there are any variables that have not been assigned.\n\n    \"\"\"\n    from bloqade.compiler.analysis.common import AssignmentScan, ScanVariables\n    from bloqade.compiler.rewrite.common import AssignBloqadeIR\n\n    final_assignments = AssignmentScan(assignments).scan(circuit)\n\n    assigned_circuit = AssignBloqadeIR(final_assignments).visit(circuit)\n\n    assignment_analysis = ScanVariables().scan(assigned_circuit)\n\n    if not assignment_analysis.is_assigned:\n        missing_vars = assignment_analysis.scalar_vars.union(\n            assignment_analysis.vector_vars\n        )\n        raise ValueError(\n            \"Missing assignments for variables:\\n\"\n            + (\"\\n\".join(f\"{var}\" for var in missing_vars))\n            + \"\\n\"\n        )\n\n    return assigned_circuit, final_assignments\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.canonicalize_circuit","title":"canonicalize_circuit","text":"
canonicalize_circuit(circuit, level_couplings)\n
  1. Insert zero waveform in the explicit time intervals missing a waveform

This pass inserts a zero waveform in the explicit time intervals missing a waveform. This is required for later analysis passes to check that the waveforms are compatible with the hardware.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to add padding to

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Return circuit: AnalogCircuit with zero waveforms inserted in the explicit time intervals missing a waveform.

Source code in src/bloqade/compiler/passes/hardware/define.py
def canonicalize_circuit(\n    circuit: analog_circuit.AnalogCircuit, level_couplings: Dict\n) -> analog_circuit.AnalogCircuit:\n    \"\"\"2. Insert zero waveform in the explicit time intervals missing a waveform\n\n    This pass inserts a zero waveform in the explicit time intervals missing a\n    waveform. This is required for later analysis passes to check that the\n    waveforms are compatible with the hardware.\n\n    Args:\n        circuit: AnalogCircuit to add padding to\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Return\n        circuit: AnalogCircuit with zero waveforms inserted in the explicit time\n            intervals missing a waveform.\n\n    \"\"\"\n    from bloqade.compiler.rewrite.common import (\n        AddPadding,\n        AssignToLiteral,\n        Canonicalizer,\n    )\n\n    circuit = AddPadding(level_couplings).visit(circuit)\n    # these two passes are equivalent to a constant propagation pass\n    circuit = AssignToLiteral().visit(circuit)\n    circuit = Canonicalizer().visit(circuit)\n\n    return circuit\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.generate_ahs_code","title":"generate_ahs_code","text":"
generate_ahs_code(capabilities, level_couplings, circuit)\n
  1. generate ahs code

Generates the AHS code for the given circuit. This includes generating the lattice data, global detuning, global amplitude, global phase, local detuning and lattice site coefficients (if applicable).

Parameters:

Name Type Description Default capabilities QuEraCapabilities | None

Capabilities of the hardware.

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required circuit AnalogCircuit

AnalogCircuit to generate AHS code for.

required

Returns:

Name Type Description ahs_components AHSComponents

A collection of the AHS components generated for the given circuit. Can be used to generate the QuEra and Braket IR.

Raises:

Type Description ValueError

If the capabilities are not provided but the circuit has a ParallelRegister. This is because the ParallelRegister requires the capabilities to generate the lattice data.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_ahs_code(\n    capabilities: Optional[QuEraCapabilities],\n    level_couplings: Dict,\n    circuit: analog_circuit.AnalogCircuit,\n) -> AHSComponents:\n    \"\"\"5. generate ahs code\n\n    Generates the AHS code for the given circuit. This includes generating the\n    lattice data, global detuning, global amplitude, global phase, local\n    detuning and lattice site coefficients (if applicable).\n\n    Args:\n        capabilities (QuEraCapabilities | None): Capabilities of the hardware.\n        level_couplings (Dict): Dictionary containing the given channels for the\n            sequence.\n        circuit (AnalogCircuit): AnalogCircuit to generate AHS code for.\n\n    Returns:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit. Can be used to generate the QuEra\n            and Braket IR.\n\n    Raises:\n        ValueError: If the capabilities are not provided but the circuit has\n            a ParallelRegister. This is because the ParallelRegister requires\n            the capabilities to generate the lattice data.\n\n    \"\"\"\n    from bloqade.compiler.codegen.hardware import (\n        GenerateLattice,\n        GenerateLatticeSiteCoefficients,\n        GeneratePiecewiseLinearChannel,\n        GeneratePiecewiseConstantChannel,\n    )\n    from bloqade.compiler.analysis.hardware import BasicLatticeValidation\n\n    if capabilities is not None:\n        # only validate the lattice if capabilities are provided\n        BasicLatticeValidation(capabilities).visit(circuit)\n\n    ahs_lattice_data = GenerateLattice(capabilities).emit(circuit)\n\n    global_detuning = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.detuning, field.Uniform\n    ).visit(circuit)\n\n    global_amplitude = GeneratePiecewiseLinearChannel(\n        sequence.rydberg, pulse.rabi.amplitude, field.Uniform\n    ).visit(circuit)\n\n    global_phase = GeneratePiecewiseConstantChannel(\n        sequence.rydberg, pulse.rabi.phase, field.Uniform\n    ).visit(circuit)\n\n    local_detuning = None\n    lattice_site_coefficients = None\n\n    extra_sm = set(level_couplings[sequence.rydberg][pulse.detuning]) - {field.Uniform}\n\n    if extra_sm:\n        if capabilities is not None and capabilities.capabilities.rydberg.local is None:\n            raise ValueError(\n                \"Device does not support local detuning, but the program has a \"\n                \"non-uniform spatial modulation for detuning.\"\n            )\n\n        sm = extra_sm.pop()\n\n        lattice_site_coefficients = GenerateLatticeSiteCoefficients(\n            parallel_decoder=ahs_lattice_data.parallel_decoder\n        ).emit(circuit)\n\n        local_detuning = GeneratePiecewiseLinearChannel(\n            sequence.rydberg, pulse.detuning, sm\n        ).visit(circuit)\n\n    return AHSComponents(\n        lattice_data=ahs_lattice_data,\n        global_detuning=global_detuning,\n        global_amplitude=global_amplitude,\n        global_phase=global_phase,\n        local_detuning=local_detuning,\n        lattice_site_coefficients=lattice_site_coefficients,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.generate_braket_ir","title":"generate_braket_ir","text":"
generate_braket_ir(ahs_components, shots)\n
  1. generate braket ir

This pass takes the AHS components and generates the Braket IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification BraketTaskSpecification

Braket IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_braket_ir(\n    ahs_components: AHSComponents, shots: int\n) -> BraketTaskSpecification:\n    \"\"\"7. generate braket ir\n\n    This pass takes the AHS components and generates the Braket IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (BraketTaskSpecification): Braket IR for the given\n            circuit.\n\n    \"\"\"\n    import braket.ir.ahs as ahs\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    ahs_register = ahs.AtomArrangement(\n        sites=list(map(convert_coordinate_units, ahs_components.lattice_data.sites)),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning_time_series = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning_time_series = ahs.TimeSeries(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n        )\n\n    amplitude_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    phase_time_series = ahs.TimeSeries(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = ahs.PhysicalField(\n        time_series=global_detuning_time_series,\n        pattern=\"uniform\",\n    )\n\n    amplitude = ahs.PhysicalField(\n        time_series=amplitude_time_series,\n        pattern=\"uniform\",\n    )\n\n    phase = ahs.PhysicalField(\n        time_series=phase_time_series,\n        pattern=\"uniform\",\n    )\n\n    local_detuning = None\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = ahs.PhysicalField(\n            time_series=local_detuning_time_series,\n            pattern=ahs_components.lattice_site_coefficients,\n        )\n\n    driving_field = ahs.DrivingField(\n        detuning=detuning,\n        amplitude=amplitude,\n        phase=phase,\n    )\n\n    shiftingFields = []\n    if ahs_components.lattice_site_coefficients is not None:\n        shiftingFields = [ahs.ShiftingField(magnitude=local_detuning)]\n\n    program = ahs.Program(\n        setup=ahs.Setup(ahs_register=ahs_register),\n        hamiltonian=ahs.Hamiltonian(\n            drivingFields=[driving_field],\n            shiftingFields=shiftingFields,\n        ),\n    )\n\n    return BraketTaskSpecification(nshots=shots, program=program)\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.generate_quera_ir","title":"generate_quera_ir","text":"
generate_quera_ir(ahs_components, shots)\n
  1. generate quera ir

This pass takes the AHS components and generates the QuEra IR.

Parameters:

Name Type Description Default ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description task_specification QuEraTaskSpecification

QuEra IR for the given circuit.

Source code in src/bloqade/compiler/passes/hardware/define.py
def generate_quera_ir(\n    ahs_components: AHSComponents, shots: int\n) -> QuEraTaskSpecification:\n    \"\"\"7. generate quera ir\n\n    This pass takes the AHS components and generates the QuEra IR.\n\n    Args:\n        ahs_components (AHSComponents): A collection of the AHS components\n            generated for the given circuit.\n        shots (int): Number of shots to run the circuit for.\n\n    Returns:\n        task_specification (QuEraTaskSpecification): QuEra IR for the given\n            circuit.\n\n    \"\"\"\n    import bloqade.submission.ir.task_specification as task_spec\n    from bloqade.compiler.passes.hardware.units import (\n        convert_time_units,\n        convert_energy_units,\n        convert_coordinate_units,\n    )\n\n    lattice = task_spec.Lattice(\n        sites=list(\n            map(\n                convert_coordinate_units,\n                ahs_components.lattice_data.sites,\n            )\n        ),\n        filling=ahs_components.lattice_data.filling,\n    )\n\n    global_detuning = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_detuning.times)),\n        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),\n    )\n\n    local_detuning = None\n\n    if ahs_components.lattice_site_coefficients is not None:\n        local_detuning = task_spec.LocalField(\n            times=list(map(convert_time_units, ahs_components.local_detuning.times)),\n            values=list(\n                map(convert_energy_units, ahs_components.local_detuning.values)\n            ),\n            lattice_site_coefficients=ahs_components.lattice_site_coefficients,\n        )\n\n    rabi_frequency_amplitude_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),\n        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),\n    )\n\n    rabi_frequency_phase_field = task_spec.GlobalField(\n        times=list(map(convert_time_units, ahs_components.global_phase.times)),\n        values=ahs_components.global_phase.values,\n    )\n\n    detuning = task_spec.Detuning(\n        global_=global_detuning,\n        local=local_detuning,\n    )\n\n    rabi_frequency_amplitude = task_spec.RabiFrequencyAmplitude(\n        global_=rabi_frequency_amplitude_field,\n    )\n\n    rabi_frequency_phase = task_spec.RabiFrequencyPhase(\n        global_=rabi_frequency_phase_field,\n    )\n\n    rydberg = task_spec.RydbergHamiltonian(\n        rabi_frequency_amplitude=rabi_frequency_amplitude,\n        rabi_frequency_phase=rabi_frequency_phase,\n        detuning=detuning,\n    )\n\n    effective_hamiltonian = task_spec.EffectiveHamiltonian(\n        rydberg=rydberg,\n    )\n\n    return task_spec.QuEraTaskSpecification(\n        nshots=shots,\n        lattice=lattice,\n        effective_hamiltonian=effective_hamiltonian,\n    )\n
"},{"location":"reference/bloqade/compiler/passes/hardware/define/#bloqade.compiler.passes.hardware.define.validate_waveforms","title":"validate_waveforms","text":"
validate_waveforms(level_couplings, circuit)\n
  1. validate piecewise linear and piecewise constant pieces of pulses

This pass check to make sure that the waveforms are compatible with the hardware. This includes checking that the waveforms are piecewise linear or piecewise constant. It also checks that the waveforms are compatible with the given channels.

Parameters:

Name Type Description Default circuit AnalogCircuit

AnalogCircuit to validate waveforms for

required level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Raises:

Type Description ValueError

If the waveforms are not piecewise linear or piecewise constant, e.g. the waveform is not continuous.

ValueError

If a waveform segment is not compatible with the given channels.

Source code in src/bloqade/compiler/passes/hardware/define.py
def validate_waveforms(\n    level_couplings: Dict, circuit: analog_circuit.AnalogCircuit\n) -> None:\n    \"\"\"4. validate piecewise linear and piecewise constant pieces of pulses\n\n    This pass check to make sure that the waveforms are compatible with the\n    hardware. This includes checking that the waveforms are piecewise linear or\n    piecewise constant. It also checks that the waveforms are compatible with\n    the given channels.\n\n    Args:\n        circuit: AnalogCircuit to validate waveforms for\n        level_couplings: Dictionary containing the given channels for the\n            sequence.\n\n    Raises:\n        ValueError: If the waveforms are not piecewise linear or piecewise\n            constant, e.g. the waveform is not continuous.\n        ValueError: If a waveform segment is not compatible with the given\n            channels.\n\n    \"\"\"\n    from bloqade.compiler.analysis.hardware import (\n        ValidatePiecewiseConstantChannel,\n        ValidatePiecewiseLinearChannel,\n    )\n    from bloqade.compiler.analysis.common import CheckSlices\n\n    channel_iter = (\n        (level_coupling, field_name, sm)\n        for level_coupling, fields in level_couplings.items()\n        for field_name, spatial_modulations in fields.items()\n        for sm in spatial_modulations\n    )\n    for channel in channel_iter:\n        if channel[1] in [pulse.detuning, pulse.rabi.amplitude]:\n            ValidatePiecewiseLinearChannel(*channel).visit(circuit)\n        else:\n            ValidatePiecewiseConstantChannel(*channel).visit(circuit)\n\n    CheckSlices().visit(circuit)\n\n    if circuit.sequence.duration() == 0:\n        raise ValueError(\"Circuit Duration must be be non-zero\")\n
"},{"location":"reference/bloqade/compiler/passes/hardware/units/","title":"Units","text":""},{"location":"reference/bloqade/compiler/rewrite/","title":"Index","text":""},{"location":"reference/bloqade/compiler/rewrite/common/","title":"Index","text":""},{"location":"reference/bloqade/compiler/rewrite/common/#bloqade.compiler.rewrite.common.AssignToLiteral","title":"AssignToLiteral","text":"

Bases: BloqadeIRTransformer

Transform all assigned variables to literals.

"},{"location":"reference/bloqade/compiler/rewrite/common/add_padding/","title":"Add padding","text":""},{"location":"reference/bloqade/compiler/rewrite/common/assign_to_literal/","title":"Assign to literal","text":""},{"location":"reference/bloqade/compiler/rewrite/common/assign_to_literal/#bloqade.compiler.rewrite.common.assign_to_literal.AssignToLiteral","title":"AssignToLiteral","text":"

Bases: BloqadeIRTransformer

Transform all assigned variables to literals.

"},{"location":"reference/bloqade/compiler/rewrite/common/assign_variables/","title":"Assign variables","text":""},{"location":"reference/bloqade/compiler/rewrite/common/canonicalize/","title":"Canonicalize","text":""},{"location":"reference/bloqade/compiler/rewrite/common/flatten/","title":"Flatten","text":""},{"location":"reference/bloqade/compiler/rewrite/python/","title":"Index","text":""},{"location":"reference/bloqade/compiler/rewrite/python/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/emulate/","title":"Index","text":""},{"location":"reference/bloqade/emulate/sparse_operator/","title":"Sparse operator","text":""},{"location":"reference/bloqade/emulate/ir/","title":"Index","text":""},{"location":"reference/bloqade/emulate/ir/atom_type/","title":"Atom type","text":""},{"location":"reference/bloqade/emulate/ir/emulator/","title":"Emulator","text":""},{"location":"reference/bloqade/emulate/ir/emulator/#bloqade.emulate.ir.emulator.Register","title":"Register dataclass","text":"

This class represents the of the atoms in the system.

"},{"location":"reference/bloqade/emulate/ir/space/","title":"Space","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/","title":"State vector","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.AnalogGate","title":"AnalogGate dataclass","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.AnalogGate.run","title":"run","text":"
run(\n    shots=1,\n    solver_name=\"dop853\",\n    atol=1e-14,\n    rtol=1e-07,\n    nsteps=2147483647,\n    interaction_picture=False,\n    project_hyperfine=True,\n)\n

Run the emulation with all atoms in the ground state, sampling the final state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef run(\n    self,\n    shots: int = 1,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-14,\n    rtol: float = 1e-7,\n    nsteps: int = 2_147_483_647,\n    interaction_picture: bool = False,\n    project_hyperfine: bool = True,\n) -> NDArray[np.uint8]:\n    \"\"\"Run the emulation with all atoms in the ground state,\n    sampling the final state vector.\"\"\"\n\n    options = dict(\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n\n    state = self.hamiltonian.space.zero_state()\n    (result,) = self.apply(state, **options)\n    result.normalize()\n\n    return result.sample(shots, project_hyperfine=project_hyperfine)\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian","title":"RydbergHamiltonian dataclass","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.average","title":"average","text":"
average(register, time=None)\n

Get energy average from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default register StateVector

The state vector to take average with

required time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description float float

average energy at time time

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef average(\n    self,\n    register: StateVector,\n    time: Optional[float] = None,\n) -> float:\n    \"\"\"Get energy average from RydbergHamiltonian object at time `time` with\n    register `register`\n\n    Args:\n        register (StateVector): The state vector to take average with\n        time (Optional[float], optional): Time value to evaluate average at.\n        Defaults to duration of RydbergHamiltonian.\n\n    Returns:\n        float: average energy at time `time`\n    \"\"\"\n    return np.vdot(register.data, self._apply(register.data, time)).real\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.average_and_variance","title":"average_and_variance","text":"
average_and_variance(register, time=None)\n

Get energy average and variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default register StateVector

The state vector to take average and variance with

required time Optional[float]

Time value to evaluate average at.

None

Returns:

Type Description float

Tuple[float, float]: average and variance of energy at time time

float

respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef average_and_variance(\n    self,\n    register: StateVector,\n    time: Optional[float] = None,\n) -> Tuple[float, float]:\n    \"\"\"Get energy average and variance from RydbergHamiltonian object at time `time`\n    with register `register`\n\n    Args:\n        register (StateVector): The state vector to take average and variance with\n        time (Optional[float], optional): Time value to evaluate average at.\n        Defaults to duration of RydbergHamiltonian.\n\n    Returns:\n        Tuple[float, float]: average and variance of energy at time `time`\n        respectively.\n    \"\"\"\n    H_register_data = self._apply(register.data, time)\n\n    average = np.vdot(register.data, H_register_data).real\n    square_average = np.vdot(H_register_data, H_register_data).real\n\n    return average, square_average - average**2\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.tocsr","title":"tocsr","text":"
tocsr(time)\n

Return the Hamiltonian as a csr matrix at time time.

Parameters:

Name Type Description Default time float

time to evaluate the Hamiltonian at.

required

Returns:

Name Type Description csr_matrix csr_matrix

The Hamiltonian as a csr matrix.

Source code in src/bloqade/emulate/ir/state_vector.py
def tocsr(self, time: float) -> csr_matrix:\n    \"\"\"Return the Hamiltonian as a csr matrix at time `time`.\n\n    Args:\n        time (float): time to evaluate the Hamiltonian at.\n\n    Returns:\n        csr_matrix: The Hamiltonian as a csr matrix.\n\n    \"\"\"\n    diagonal = sum(\n        (detuning.get_diagonal(time) for detuning in self.detuning_ops),\n        start=self.rydberg,\n    )\n\n    hamiltonian = diags(diagonal).tocsr()\n    for rabi_op in self.rabi_ops:\n        hamiltonian = hamiltonian + rabi_op.tocsr(time)\n\n    return hamiltonian\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.RydbergHamiltonian.variance","title":"variance","text":"
variance(register, time=None)\n

Get the energy variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default register StateVector

The state vector to take variance with

required time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description complex float

variance of energy at time time respectively.

Source code in src/bloqade/emulate/ir/state_vector.py
@beartype\ndef variance(\n    self,\n    register: StateVector,\n    time: Optional[float] = None,\n) -> float:\n    \"\"\"Get the energy variance from RydbergHamiltonian object at\n    time `time` with register `register`\n\n    Args:\n        register (StateVector): The state vector to take variance with\n        time (Optional[float], optional): Time value to evaluate average at.\n        Defaults to duration of RydbergHamiltonian.\n\n    Returns:\n        complex: variance of energy at time `time` respectively.\n    \"\"\"\n\n    _, var = self.average_and_variance(register, time)\n    return var\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector","title":"StateVector dataclass","text":""},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector.local_trace","title":"local_trace","text":"
local_trace(matrix, site_index)\n

return trace of an operator over the StateVector.

Parameters:

Name Type Description Default matrix ndarray

Square matrix representing operator in the local hilbert space.

required site_index int | Tuple[int, int]

sites to apply one body operator to.

required

Returns:

Name Type Description complex complex

the trace of the operator over the state-vector.

Raises:

Type Description ValueError

Error is raised when the dimension of operator is not

ValueError

Error is raised when the site argument is out of bounds.

Source code in src/bloqade/emulate/ir/state_vector.py
@plum.dispatch\ndef local_trace(  # noqa: F811\n    self, matrix: np.ndarray, site_index: Union[int, Tuple[int, int]]\n) -> complex:  # noqa: F811\n    \"\"\"return trace of an operator over the StateVector.\n\n    Args:\n        matrix (np.ndarray): Square matrix representing operator in the local\n            hilbert space.\n        site_index (int | Tuple[int, int]): sites to apply one body operator to.\n\n    Returns:\n        complex: the trace of the operator over the state-vector.\n\n    Raises:\n        ValueError: Error is raised when the dimension of `operator` is not\n        consistent with `site` argument. The size of the operator must fit\n        the size of the local hilbert space of `site` depending on the number\n        of sites and the number of levels inside each atom, e.g. for two site\n        expectation value with a three level atom the operator must be a 9 by\n        9 array.\n\n        ValueError: Error is raised when the `site` argument is out of bounds.\n\n    \"\"\"\n    ...\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector.norm","title":"norm","text":"
norm()\n

Return the norm of the state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
def norm(self) -> float:\n    \"\"\"Return the norm of the state vector.\"\"\"\n    return np.linalg.norm(self.data)\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector.normalize","title":"normalize","text":"
normalize()\n

Normalize the state vector.

Source code in src/bloqade/emulate/ir/state_vector.py
def normalize(self) -> None:\n    \"\"\"Normalize the state vector.\"\"\"\n    data = self.data\n    data /= np.linalg.norm(data)\n
"},{"location":"reference/bloqade/emulate/ir/state_vector/#bloqade.emulate.ir.state_vector.StateVector.sample","title":"sample","text":"
sample(shots, project_hyperfine=True)\n

Sample the state vector and return bitstrings.

Source code in src/bloqade/emulate/ir/state_vector.py
def sample(self, shots: int, project_hyperfine: bool = True) -> NDArray:\n    \"\"\"Sample the state vector and return bitstrings.\"\"\"\n    return self.space.sample_state_vector(\n        self.data, shots, project_hyperfine=project_hyperfine\n    )\n
"},{"location":"reference/bloqade/ir/","title":"Index","text":""},{"location":"reference/bloqade/ir/#bloqade.ir.start","title":"start module-attribute","text":"
start = ListOfLocations()\n

A Program starting point, alias of empty ListOfLocations.

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for Rydberg Level coupling
    • start.hyperfine: for Hyperfine Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): (\"x\", \"y\")
        • Scalar objects: (2*cast(\"x\"), 5+cast(\"y\"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms
"},{"location":"reference/bloqade/ir/#bloqade.ir.AlignedWaveform","title":"AlignedWaveform","text":"

Bases: Waveform

<padded waveform> ::= <waveform> | <waveform> <alignment> <value>\n\n<alignment> ::= 'left aligned' | 'right aligned'\n<value> ::= 'left value' | 'right value' | <scalar expr>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AnalogCircuit","title":"AnalogCircuit","text":"

AnalogCircuit is a dummy type that bundle register and sequence together.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AnalogCircuit.register","title":"register property","text":"
register\n

Get the register of the program.

Returns:

Type Description

register (Union[\"AtomArrangement\", \"ParallelRegister\"])

Note

If the program is built with parallelize(), The the register will be a ParallelRegister. Otherwise it will be a AtomArrangement.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AnalogCircuit.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the program

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the program

{} Source code in src/bloqade/ir/analog_circuit.py
def show(self, **assignments):\n    \"\"\"Interactive visualization of the program\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the program\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/ir/#bloqade.ir.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais","title":"BoundedBravais","text":"
BoundedBravais(parent=None)\n

Bases: AtomArrangement

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.__match_args__","title":"__match_args__ class-attribute instance-attribute","text":"
__match_args__ = ('shape', 'lattice_spacing')\n

Base classe for Bravais lattices AtomArrangement.

  • Square
  • Chain
  • Honeycomb
  • Triangular
  • Lieb
  • Kagome
  • Rectangular
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.n_dims","title":"n_dims property","text":"
n_dims\n

dimension of the lattice

Returns:

Name Type Description int

dimension of the lattice

"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.coordinates","title":"coordinates","text":"
coordinates(index)\n

calculate the coordinates of a cell in the lattice given the cell index.

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef coordinates(self, index: List[int]) -> NDArray:\n    \"\"\"calculate the coordinates of a cell in the lattice\n    given the cell index.\n    \"\"\"\n    # damn! this is like stone age broadcasting\n    vectors = np.array(self.cell_vectors())\n    index = np.array(index)\n    pos = np.sum(vectors.T * index, axis=1)\n    return pos + np.array(self.cell_atoms())\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.BoundedBravais.scale","title":"scale","text":"
scale(factor)\n

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef scale(self, factor: ScalarType) -> \"BoundedBravais\":\n    \"\"\"Scale the current location with a factor.\n\n    (x,y) -> factor*(x,y)\n\n    Args:\n        factor (str | Real | Decimal | Scalar): scale factor\n\n    Returns:\n        BoundedBravais: The lattice with the scaled locations\n    \"\"\"\n    factor = cast(factor)\n    obj = self.__new__(type(self))\n    for f in fields(self):\n        if f.name == \"lattice_spacing\":\n            obj.lattice_spacing = factor * self.lattice_spacing\n        else:\n            setattr(obj, f.name, getattr(self, f.name))\n    return obj\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Constant","title":"Constant","text":"
Constant(value, duration)\n

Bases: Instruction

<constant> ::= 'constant' <scalar expr>\n

f(t=0:duration) = value

Parameters:

Name Type Description Default value Scalar

the constant value

required duration Scalar

the time span of the constant waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, value: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"value\", cast(value))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Field","title":"Field","text":"

Bases: FieldExpr

Field node in the IR. Which contains collection(s) of Waveform

<field> ::= ('field' <spatial modulation>  <padded waveform>)*\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Field.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Field

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Field

{} Source code in src/bloqade/ir/control/field.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Field\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Field\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Linear","title":"Linear","text":"
Linear(start, stop, duration)\n

Bases: Instruction

<linear> ::= 'linear' <scalar expr> <scalar expr>\n

f(t=0:duration) = start + (stop-start)/duration * t

Parameters:

Name Type Description Default start Scalar

start value

required stop Scalar

stop value

required duration Scalar

the time span of the linear waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, start: ScalarType, stop: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"start\", cast(start))\n    object.__setattr__(self, \"stop\", cast(stop))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Literal","title":"Literal","text":"

Bases: Real

"},{"location":"reference/bloqade/ir/#bloqade.ir.Literal.value","title":"value instance-attribute","text":"
value\n

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default value Decimal

decimal value instance

required"},{"location":"reference/bloqade/ir/#bloqade.ir.Poly","title":"Poly","text":"
Poly(coeffs, duration)\n

Bases: Instruction

<poly> ::= <scalar>+\n

f(t=0:duration) = c[0] + c[1]t + c[2]t^2 + ... + c[n-1]t^n-1 + c[n]t^n

Parameters:

Name Type Description Default coeffs Tuple[Scalar]

the coefficients c[] of the polynomial.

required duration Scalar

the time span of the waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, coeffs: Container[ScalarType], duration: ScalarType):\n    object.__setattr__(self, \"coeffs\", tuple(map(cast, coeffs)))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Pulse","title":"Pulse","text":"

Bases: PulseExpr

<pulse> ::= (<field name> <field>)+\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Pulse.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Pulse

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Pulse

{} Source code in src/bloqade/ir/control/pulse.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Pulse\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Pulse\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.PythonFn","title":"PythonFn","text":"

Bases: Instruction

<python-fn> ::= 'python-fn' <python function def> <scalar expr>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Record","title":"Record","text":"

Bases: Waveform

<record> ::= 'record' <waveform> <var> <side>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Sample","title":"Sample","text":"

Bases: Waveform

<sample> ::= 'sample' <waveform> <interpolation> <scalar>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Scalar","title":"Scalar","text":"

Base class for all scalar expressions.

<scalar> ::= <literal>\n| <variable>\n| <default>\n| <negative>\n| <add>\n| <mul>\n| <min>\n| <max>\n| <slice>\n| <inverval>\n\n<mul> ::= <scalar> '*' <scalar>\n<add> ::= <scalar> '+' <scalar>\n<min> ::= 'min' <scalar>+\n<max> ::= 'max' <scalar>+\n<slice> ::= <scalar expr> '[' <interval> ']'\n<interval> ::= <scalar expr> '..' <scalar expr>\n<real> ::= <literal> | <var>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Sequence","title":"Sequence","text":"

Bases: SequenceExpr

Sequence of a program, which includes pulses informations.

"},{"location":"reference/bloqade/ir/#bloqade.ir.Sequence.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Sequence

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Sequence

{} Source code in src/bloqade/ir/control/sequence.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Sequence\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Sequence\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Variable","title":"Variable","text":"

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default name str

variable instance.

required"},{"location":"reference/bloqade/ir/#bloqade.ir.Waveform","title":"Waveform","text":"

Bases: HashTrait, CanonicalizeTrait

Waveform node in the IR.

  • <instruction>
  • <smooth>
  • <slice>
  • <apppend>
  • <negative>
  • <scale>
  • <add>
  • <record>
  • <sample>
<waveform> ::= <instruction>\n    | <smooth>\n    | <slice>\n    | <append>\n    | <negative>\n    | <scale>\n    | <add>\n    | <record>\n    | <sample>\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.Waveform.figure","title":"figure","text":"
figure(**assignments)\n

get figure of the plotting the waveform.

Returns:

Name Type Description figure

a bokeh figure

Source code in src/bloqade/ir/control/waveform.py
def figure(self, **assignments):\n    \"\"\"get figure of the plotting the waveform.\n\n    Returns:\n        figure: a bokeh figure\n    \"\"\"\n    return get_ir_figure(self, **assignments)\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.cast","title":"cast","text":"
cast(py)\n
  1. cast Real number (or list/tuple of Real numbers) to Scalar Literal.

  2. cast str (or list/tuple of Real numbers) to Scalar Variable.

Parameters:

Name Type Description Default py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description Scalar

Scalar

Source code in src/bloqade/ir/scalar.py
def cast(py) -> \"Scalar\":\n    \"\"\"\n    1. cast Real number (or list/tuple of Real numbers)\n    to [`Scalar Literal`][bloqade.ir.scalar.Literal].\n\n    2. cast str (or list/tuple of Real numbers)\n    to [`Scalar Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast\n\n    Returns:\n        Scalar\n    \"\"\"\n    ret = trycast(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Scalar Literal\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/#bloqade.ir.var","title":"var","text":"
var(py)\n

cast string (or list/tuple of strings) to Variable.

Parameters:

Name Type Description Default py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description Variable

Union[Variable]

Source code in src/bloqade/ir/scalar.py
def var(py: str) -> \"Variable\":\n    \"\"\"cast string (or list/tuple of strings)\n    to [`Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str, List[str]]): a string or list/tuple of strings\n\n    Returns:\n       Union[Variable]\n    \"\"\"\n    ret = tryvar(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Variable\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/analog_circuit/","title":"Analog circuit","text":""},{"location":"reference/bloqade/ir/analog_circuit/#bloqade.ir.analog_circuit.AnalogCircuit","title":"AnalogCircuit","text":"

AnalogCircuit is a dummy type that bundle register and sequence together.

"},{"location":"reference/bloqade/ir/analog_circuit/#bloqade.ir.analog_circuit.AnalogCircuit.register","title":"register property","text":"
register\n

Get the register of the program.

Returns:

Type Description

register (Union[\"AtomArrangement\", \"ParallelRegister\"])

Note

If the program is built with parallelize(), The the register will be a ParallelRegister. Otherwise it will be a AtomArrangement.

"},{"location":"reference/bloqade/ir/analog_circuit/#bloqade.ir.analog_circuit.AnalogCircuit.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the program

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the program

{} Source code in src/bloqade/ir/analog_circuit.py
def show(self, **assignments):\n    \"\"\"Interactive visualization of the program\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the program\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/scalar/","title":"Scalar","text":""},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Literal","title":"Literal","text":"

Bases: Real

"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Literal.value","title":"value instance-attribute","text":"
value\n

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default value Decimal

decimal value instance

required"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Scalar","title":"Scalar","text":"

Base class for all scalar expressions.

<scalar> ::= <literal>\n| <variable>\n| <default>\n| <negative>\n| <add>\n| <mul>\n| <min>\n| <max>\n| <slice>\n| <inverval>\n\n<mul> ::= <scalar> '*' <scalar>\n<add> ::= <scalar> '+' <scalar>\n<min> ::= 'min' <scalar>+\n<max> ::= 'max' <scalar>+\n<slice> ::= <scalar expr> '[' <interval> ']'\n<interval> ::= <scalar expr> '..' <scalar expr>\n<real> ::= <literal> | <var>\n
"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.Variable","title":"Variable","text":"

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default name str

variable instance.

required"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.cast","title":"cast","text":"
cast(py)\n
  1. cast Real number (or list/tuple of Real numbers) to Scalar Literal.

  2. cast str (or list/tuple of Real numbers) to Scalar Variable.

Parameters:

Name Type Description Default py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description Scalar

Scalar

Source code in src/bloqade/ir/scalar.py
def cast(py) -> \"Scalar\":\n    \"\"\"\n    1. cast Real number (or list/tuple of Real numbers)\n    to [`Scalar Literal`][bloqade.ir.scalar.Literal].\n\n    2. cast str (or list/tuple of Real numbers)\n    to [`Scalar Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast\n\n    Returns:\n        Scalar\n    \"\"\"\n    ret = trycast(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Scalar Literal\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/scalar/#bloqade.ir.scalar.var","title":"var","text":"
var(py)\n

cast string (or list/tuple of strings) to Variable.

Parameters:

Name Type Description Default py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description Variable

Union[Variable]

Source code in src/bloqade/ir/scalar.py
def var(py: str) -> \"Variable\":\n    \"\"\"cast string (or list/tuple of strings)\n    to [`Variable`][bloqade.ir.scalar.Variable].\n\n    Args:\n        py (Union[str, List[str]]): a string or list/tuple of strings\n\n    Returns:\n       Union[Variable]\n    \"\"\"\n    ret = tryvar(py)\n    if ret is None:\n        raise TypeError(f\"Cannot cast {type(py)} to Variable\")\n\n    return ret\n
"},{"location":"reference/bloqade/ir/control/","title":"Index","text":""},{"location":"reference/bloqade/ir/control/field/","title":"Field","text":""},{"location":"reference/bloqade/ir/control/field/#bloqade.ir.control.field.Field","title":"Field","text":"

Bases: FieldExpr

Field node in the IR. Which contains collection(s) of Waveform

<field> ::= ('field' <spatial modulation>  <padded waveform>)*\n
"},{"location":"reference/bloqade/ir/control/field/#bloqade.ir.control.field.Field.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Field

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Field

{} Source code in src/bloqade/ir/control/field.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Field\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Field\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/control/pulse/","title":"Pulse","text":""},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.Append","title":"Append","text":"

Bases: AppendTrait, PulseExpr

<append> ::= <expr>+\n
"},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.Pulse","title":"Pulse","text":"

Bases: PulseExpr

<pulse> ::= (<field name> <field>)+\n
"},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.Pulse.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Pulse

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Pulse

{} Source code in src/bloqade/ir/control/pulse.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Pulse\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Pulse\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/control/pulse/#bloqade.ir.control.pulse.PulseExpr","title":"PulseExpr","text":"

Bases: HashTrait, CanonicalizeTrait

<expr> ::= <pulse>\n  | <append>\n  | <slice>\n  | <named>\n
"},{"location":"reference/bloqade/ir/control/sequence/","title":"Sequence","text":""},{"location":"reference/bloqade/ir/control/sequence/#bloqade.ir.control.sequence.Sequence","title":"Sequence","text":"

Bases: SequenceExpr

Sequence of a program, which includes pulses informations.

"},{"location":"reference/bloqade/ir/control/sequence/#bloqade.ir.control.sequence.Sequence.show","title":"show","text":"
show(**assignments)\n

Interactive visualization of the Sequence

Parameters:

Name Type Description Default **assignments

assigning the instance value (literal) to the existing variables in the Sequence

{} Source code in src/bloqade/ir/control/sequence.py
def show(self, **assignments):\n    \"\"\"\n    Interactive visualization of the Sequence\n\n    Args:\n        **assignments: assigning the instance value (literal) to the\n            existing variables in the Sequence\n\n    \"\"\"\n    display_ir(self, assignments)\n
"},{"location":"reference/bloqade/ir/control/waveform/","title":"Waveform","text":""},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Add","title":"Add","text":"

Bases: Waveform

<add> ::= <waveform> '+' <waveform>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.AlignedWaveform","title":"AlignedWaveform","text":"

Bases: Waveform

<padded waveform> ::= <waveform> | <waveform> <alignment> <value>\n\n<alignment> ::= 'left aligned' | 'right aligned'\n<value> ::= 'left value' | 'right value' | <scalar expr>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Append","title":"Append","text":"

Bases: AppendTrait, Waveform

<append> ::= <waveform>+\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Constant","title":"Constant","text":"
Constant(value, duration)\n

Bases: Instruction

<constant> ::= 'constant' <scalar expr>\n

f(t=0:duration) = value

Parameters:

Name Type Description Default value Scalar

the constant value

required duration Scalar

the time span of the constant waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, value: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"value\", cast(value))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Instruction","title":"Instruction","text":"

Bases: Waveform

Instruction node in the IR.

  • <linear>
  • <constant>
  • <poly>
  • <python-fn>
<instruction> ::= <linear>\n    | <constant>\n    | <poly>\n    | <python-fn>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Linear","title":"Linear","text":"
Linear(start, stop, duration)\n

Bases: Instruction

<linear> ::= 'linear' <scalar expr> <scalar expr>\n

f(t=0:duration) = start + (stop-start)/duration * t

Parameters:

Name Type Description Default start Scalar

start value

required stop Scalar

stop value

required duration Scalar

the time span of the linear waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, start: ScalarType, stop: ScalarType, duration: ScalarType):\n    object.__setattr__(self, \"start\", cast(start))\n    object.__setattr__(self, \"stop\", cast(stop))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Negative","title":"Negative","text":"

Bases: Waveform

<negative> ::= '-' <waveform>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Poly","title":"Poly","text":"
Poly(coeffs, duration)\n

Bases: Instruction

<poly> ::= <scalar>+\n

f(t=0:duration) = c[0] + c[1]t + c[2]t^2 + ... + c[n-1]t^n-1 + c[n]t^n

Parameters:

Name Type Description Default coeffs Tuple[Scalar]

the coefficients c[] of the polynomial.

required duration Scalar

the time span of the waveform.

required Source code in src/bloqade/ir/control/waveform.py
@beartype\ndef __init__(self, coeffs: Container[ScalarType], duration: ScalarType):\n    object.__setattr__(self, \"coeffs\", tuple(map(cast, coeffs)))\n    object.__setattr__(self, \"duration\", cast(duration))\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.PythonFn","title":"PythonFn","text":"

Bases: Instruction

<python-fn> ::= 'python-fn' <python function def> <scalar expr>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Record","title":"Record","text":"

Bases: Waveform

<record> ::= 'record' <waveform> <var> <side>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Sample","title":"Sample","text":"

Bases: Waveform

<sample> ::= 'sample' <waveform> <interpolation> <scalar>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Scale","title":"Scale","text":"
Scale(scalar, waveform)\n

Bases: Waveform

<scale> ::= <scalar expr> '*' <waveform>\n
Source code in src/bloqade/ir/control/waveform.py
def __init__(self, scalar, waveform: Waveform):\n    object.__setattr__(self, \"scalar\", cast(scalar))\n    object.__setattr__(self, \"waveform\", waveform)\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Slice","title":"Slice","text":"

Bases: SliceTrait, Waveform

<slice> ::= <waveform> <scalar.interval>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Smooth","title":"Smooth","text":"
Smooth(radius, kernel, waveform)\n

Bases: Waveform

<smooth> ::= 'smooth' <kernel> <waveform>\n
Source code in src/bloqade/ir/control/waveform.py
def __init__(self, radius, kernel, waveform):\n    if isinstance(kernel, str):\n        if kernel == \"Gaussian\":\n            kernel = GaussianKernel\n        elif kernel == \"Logistic\":\n            kernel = LogisticKernel\n        elif kernel == \"Sigmoid\":\n            kernel = SigmoidKernel\n        elif kernel == \"Triangle\":\n            kernel = TriangleKernel\n        elif kernel == \"Uniform\":\n            kernel = UniformKernel\n        elif kernel == \"Parabolic\":\n            kernel = ParabolicKernel\n        elif kernel == \"Biweight\":\n            kernel = BiweightKernel\n        elif kernel == \"Triweight\":\n            kernel = TriweightKernel\n        elif kernel == \"Tricube\":\n            kernel = TricubeKernel\n        elif kernel == \"Cosine\":\n            kernel = CosineKernel\n        else:\n            raise ValueError(f\"Invalid kernel: {kernel}\")\n\n    object.__setattr__(self, \"radius\", cast(radius))\n    object.__setattr__(self, \"kernel\", kernel)\n    object.__setattr__(self, \"waveform\", waveform)\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Waveform","title":"Waveform","text":"

Bases: HashTrait, CanonicalizeTrait

Waveform node in the IR.

  • <instruction>
  • <smooth>
  • <slice>
  • <apppend>
  • <negative>
  • <scale>
  • <add>
  • <record>
  • <sample>
<waveform> ::= <instruction>\n    | <smooth>\n    | <slice>\n    | <append>\n    | <negative>\n    | <scale>\n    | <add>\n    | <record>\n    | <sample>\n
"},{"location":"reference/bloqade/ir/control/waveform/#bloqade.ir.control.waveform.Waveform.figure","title":"figure","text":"
figure(**assignments)\n

get figure of the plotting the waveform.

Returns:

Name Type Description figure

a bokeh figure

Source code in src/bloqade/ir/control/waveform.py
def figure(self, **assignments):\n    \"\"\"get figure of the plotting the waveform.\n\n    Returns:\n        figure: a bokeh figure\n    \"\"\"\n    return get_ir_figure(self, **assignments)\n
"},{"location":"reference/bloqade/ir/control/traits/","title":"Index","text":""},{"location":"reference/bloqade/ir/control/traits/#bloqade.ir.control.traits.SliceTrait","title":"SliceTrait","text":""},{"location":"reference/bloqade/ir/control/traits/#bloqade.ir.control.traits.SliceTrait.start","title":"start cached property","text":"
start\n

Start time of the sliced object

Returns:

Name Type Description Scalar Scalar

The starting time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/control/traits/#bloqade.ir.control.traits.SliceTrait.stop","title":"stop cached property","text":"
stop\n

Stop time of the sliced object

Returns:

Name Type Description Scalar Scalar

The stopping time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/control/traits/append/","title":"Append","text":""},{"location":"reference/bloqade/ir/control/traits/canonicalize/","title":"Canonicalize","text":""},{"location":"reference/bloqade/ir/control/traits/hash/","title":"Hash","text":""},{"location":"reference/bloqade/ir/control/traits/slice/","title":"Slice","text":""},{"location":"reference/bloqade/ir/control/traits/slice/#bloqade.ir.control.traits.slice.SliceTrait","title":"SliceTrait","text":""},{"location":"reference/bloqade/ir/control/traits/slice/#bloqade.ir.control.traits.slice.SliceTrait.start","title":"start cached property","text":"
start\n

Start time of the sliced object

Returns:

Name Type Description Scalar Scalar

The starting time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/control/traits/slice/#bloqade.ir.control.traits.slice.SliceTrait.stop","title":"stop cached property","text":"
stop\n

Stop time of the sliced object

Returns:

Name Type Description Scalar Scalar

The stopping time of the sliced object as a

Scalar

Scalar Expression

"},{"location":"reference/bloqade/ir/location/","title":"Index","text":""},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.start","title":"start module-attribute","text":"
start = ListOfLocations()\n

A Program starting point, alias of empty ListOfLocations.

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for Rydberg Level coupling
    • start.hyperfine: for Hyperfine Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): (\"x\", \"y\")
        • Scalar objects: (2*cast(\"x\"), 5+cast(\"y\"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais","title":"BoundedBravais","text":"
BoundedBravais(parent=None)\n

Bases: AtomArrangement

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.__match_args__","title":"__match_args__ class-attribute instance-attribute","text":"
__match_args__ = ('shape', 'lattice_spacing')\n

Base classe for Bravais lattices AtomArrangement.

  • Square
  • Chain
  • Honeycomb
  • Triangular
  • Lieb
  • Kagome
  • Rectangular
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.n_dims","title":"n_dims property","text":"
n_dims\n

dimension of the lattice

Returns:

Name Type Description int

dimension of the lattice

"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.coordinates","title":"coordinates","text":"
coordinates(index)\n

calculate the coordinates of a cell in the lattice given the cell index.

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef coordinates(self, index: List[int]) -> NDArray:\n    \"\"\"calculate the coordinates of a cell in the lattice\n    given the cell index.\n    \"\"\"\n    # damn! this is like stone age broadcasting\n    vectors = np.array(self.cell_vectors())\n    index = np.array(index)\n    pos = np.sum(vectors.T * index, axis=1)\n    return pos + np.array(self.cell_atoms())\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.BoundedBravais.scale","title":"scale","text":"
scale(factor)\n

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef scale(self, factor: ScalarType) -> \"BoundedBravais\":\n    \"\"\"Scale the current location with a factor.\n\n    (x,y) -> factor*(x,y)\n\n    Args:\n        factor (str | Real | Decimal | Scalar): scale factor\n\n    Returns:\n        BoundedBravais: The lattice with the scaled locations\n    \"\"\"\n    factor = cast(factor)\n    obj = self.__new__(type(self))\n    for f in fields(self):\n        if f.name == \"lattice_spacing\":\n            obj.lattice_spacing = factor * self.lattice_spacing\n        else:\n            setattr(obj, f.name, getattr(self, f.name))\n    return obj\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/#bloqade.ir.location.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/","title":"Bravais","text":""},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais","title":"BoundedBravais","text":"
BoundedBravais(parent=None)\n

Bases: AtomArrangement

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.__match_args__","title":"__match_args__ class-attribute instance-attribute","text":"
__match_args__ = ('shape', 'lattice_spacing')\n

Base classe for Bravais lattices AtomArrangement.

  • Square
  • Chain
  • Honeycomb
  • Triangular
  • Lieb
  • Kagome
  • Rectangular
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.n_dims","title":"n_dims property","text":"
n_dims\n

dimension of the lattice

Returns:

Name Type Description int

dimension of the lattice

"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.coordinates","title":"coordinates","text":"
coordinates(index)\n

calculate the coordinates of a cell in the lattice given the cell index.

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef coordinates(self, index: List[int]) -> NDArray:\n    \"\"\"calculate the coordinates of a cell in the lattice\n    given the cell index.\n    \"\"\"\n    # damn! this is like stone age broadcasting\n    vectors = np.array(self.cell_vectors())\n    index = np.array(index)\n    pos = np.sum(vectors.T * index, axis=1)\n    return pos + np.array(self.cell_atoms())\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.BoundedBravais.scale","title":"scale","text":"
scale(factor)\n

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef scale(self, factor: ScalarType) -> \"BoundedBravais\":\n    \"\"\"Scale the current location with a factor.\n\n    (x,y) -> factor*(x,y)\n\n    Args:\n        factor (str | Real | Decimal | Scalar): scale factor\n\n    Returns:\n        BoundedBravais: The lattice with the scaled locations\n    \"\"\"\n    factor = cast(factor)\n    obj = self.__new__(type(self))\n    for f in fields(self):\n        if f.name == \"lattice_spacing\":\n            obj.lattice_spacing = factor * self.lattice_spacing\n        else:\n            setattr(obj, f.name, getattr(self, f.name))\n    return obj\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Chain","title":"Chain","text":"
Chain(L, *, lattice_spacing=1.0, vertical_chain=False)\n

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L int

number of sites in the chain

required lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False\n):\n    self.L = L\n    self.lattice_spacing = cast(lattice_spacing)\n    self.vertical_chain = vertical_chain\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Honeycomb","title":"Honeycomb","text":"
Honeycomb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (\u00bd, 1/(2*sqrt(3))

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Kagome","title":"Kagome","text":"
Kagome(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Lieb","title":"Lieb","text":"
Lieb(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Rectangular","title":"Rectangular","text":"
Rectangular(\n    width,\n    height,\n    *,\n    lattice_spacing_x=1.0,\n    lattice_spacing_y=1.0\n)\n

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default width int

number of sites in x direction.

required height int

number of sites in y direction.

required lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0 lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self,\n    width: int,\n    height: int,\n    *,\n    lattice_spacing_x: ScalarType = 1.0,\n    lattice_spacing_y: ScalarType = 1.0,\n):\n    self.width = width\n    self.height = height\n    self.lattice_spacing_x = cast(lattice_spacing_x)\n    self.lattice_spacing_y = (\n        cast(lattice_spacing_y)\n        if lattice_spacing_y is not None\n        else self.lattice_spacing_x\n    )\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Square","title":"Square","text":"
Square(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/bravais/#bloqade.ir.location.bravais.Triangular","title":"Triangular","text":"
Triangular(L1, L2=None, *, lattice_spacing=1.0)\n

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (\u00bd, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default L int

number of sites in linear direction. n_atoms = L * L.

required L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in src/bloqade/ir/location/bravais.py
@beartype\ndef __init__(\n    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0\n):\n    if L2 is None:\n        L2 = L1\n    self.L1 = L1\n    self.L2 = L2\n    self.lattice_spacing = cast(lattice_spacing)\n\n    super().__init__()\n
"},{"location":"reference/bloqade/ir/location/location/","title":"Location","text":""},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement","title":"AtomArrangement","text":"
AtomArrangement(parent=None)\n

Bases: ProgramStart

Source code in src/bloqade/builder/base.py
def __init__(\n    self,\n    parent: Optional[\"Builder\"] = None,\n) -> None:\n    self.__parent__ = parent\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_atoms","title":"n_atoms property","text":"
n_atoms\n

number of atoms (filled sites) in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_dims","title":"n_dims property","text":"
n_dims\n

number of dimensions in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_sites","title":"n_sites property","text":"
n_sites\n

number of sites in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.n_vacant","title":"n_vacant property","text":"
n_vacant\n

number of vacant sites in the register.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.add_position","title":"add_position","text":"
add_position(position, filling=None)\n

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom \"filling\" (whether or not at a specified coordinate an atom should be present).

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.add_position--usage-example","title":"Usage Example:","text":"
# single coordinate\n>>> reg = start.add_position((0,0))\n# you may chain add_position calls\n>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n# you can add variables anywhere a value may be present\n>>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n# and specify your atom fillings\n>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n[True, False])\n# alternatively you could use one boolean to specify\n# all coordinates should be empty/filled\n>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n(5.2, 2.2)], False)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
def add_position(\n    self,\n    position: Union[\n        PositionArray,\n        List[Tuple[ScalarType, ScalarType]],\n        Tuple[ScalarType, ScalarType],\n    ],\n    filling: Optional[Union[BoolArray, List[bool], bool]] = None,\n) -> \"ListOfLocations\":\n    \"\"\"\n    Add a position or multiple positions to a pre-existing geometry.\n\n    `add_position` is capable of accepting:\n    - A single tuple for one atom coordinate: `(1.0, 2.5)`\n    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]\n    - A numpy array of shape (N, 2) where N is the number of atoms\n\n    You may also intersperse variables anywhere a value may be present.\n\n    You can also pass in an optional argument which determines the atom \"filling\"\n    (whether or not at a specified coordinate an atom should be present).\n\n    ### Usage Example:\n    ```\n    # single coordinate\n    >>> reg = start.add_position((0,0))\n    # you may chain add_position calls\n    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])\n    # you can add variables anywhere a value may be present\n    >>> reg_with_var = reg_plus_two.add_position((\"x\", \"y\"))\n    # and specify your atom fillings\n    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],\n    [True, False])\n    # alternatively you could use one boolean to specify\n    # all coordinates should be empty/filled\n    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),\n    (5.2, 2.2)], False)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`: to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    if is_bearable(position, PositionArray) and is_bearable(\n        filling, Optional[BoolArray]\n    ):\n        return self.add_position_ndarray(position, filling)\n    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(\n        filling, Optional[List[bool]]\n    ):\n        return self.add_position_list_tuples(position, filling)\n    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(\n        filling, Optional[bool]\n    ):\n        return self.add_position_single_tupe(position, filling)\n    else:\n        raise TypeError(\"Invalid input types for add_position provided!\")\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_count","title":"apply_defect_count","text":"
apply_defect_count(n_defects, rng=np.random.default_rng())\n

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_count--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_count(2, custom_rng)\n# you may also chain apply_defect_count calls\n>>> reg.apply_defect_count(2, custom_rng)\n# you can also use apply_defect_count on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_count(\n    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()\n):\n    \"\"\"\n    Drop `n_defects` atoms from the geometry randomly. Internally this occurs\n    by setting certain sites to have a SiteFilling set to false indicating\n    no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_count(2, custom_rng)\n    # you may also chain apply_defect_count calls\n    >>> reg.apply_defect_count(2, custom_rng)\n    # you can also use apply_defect_count on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n            to add more positions\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_count(n_defects)`: to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n            .apply_defect_density(defect_probability)`:\n            to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n            to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`: to specify\n            Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n            to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n            shows your geometry in your web browser\n    \"\"\"\n\n    location_list = []\n    for location_info in self.enumerate():\n        location_list.append(location_info)\n\n    filled_sites = []\n\n    for index, location_info in enumerate(location_list):\n        if location_info.filling is SiteFilling.filled:\n            filled_sites.append(index)\n\n    if n_defects >= len(filled_sites):\n        raise ValueError(\n            f\"n_defects {n_defects} must be less than the number of filled sites \"\n            f\"({len(filled_sites)})\"\n        )\n\n    for _ in range(n_defects):\n        index = rng.choice(filled_sites)\n        location_list[index] = LocationInfo.create(\n            location_list[index].position,\n            (False if location_list[index].filling is SiteFilling.filled else True),\n        )\n        filled_sites.remove(index)\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_density","title":"apply_defect_density","text":"
apply_defect_density(\n    defect_probability, rng=np.random.default_rng()\n)\n

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.apply_defect_density--usage-example","title":"Usage Example:","text":"
>>> from bloqade.atom_arrangement import Chain\n>>> import numpy as np\n# set a custom seed for a numpy-based RNG\n>>> custom_rng = np.random.default_rng(888)\n# randomly remove two atoms from the geometry\n>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n# you may also chain apply_defect_density calls\n>>> reg.apply_defect_count(0.1, custom_rng)\n# you can also use apply_defect_density on custom geometries\n>>> from bloqade import start\n>>> start.add_position([(0,0), (1,1)])\n.apply_defect_density(0.5, custom_rng)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef apply_defect_density(\n    self,\n    defect_probability: float,\n    rng: np.random.Generator = np.random.default_rng(),\n):\n    \"\"\"\n    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).\n    Internally this occurs by setting certain sites to have a SiteFilling\n    set to false indicating no atom is present at the coordinate.\n\n    A default numpy-based Random Number Generator is used but you can\n    explicitly override this by passing in your own.\n\n    ### Usage Example:\n\n    ```\n    >>> from bloqade.atom_arrangement import Chain\n    >>> import numpy as np\n    # set a custom seed for a numpy-based RNG\n    >>> custom_rng = np.random.default_rng(888)\n    # randomly remove two atoms from the geometry\n    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)\n    # you may also chain apply_defect_density calls\n    >>> reg.apply_defect_count(0.1, custom_rng)\n    # you can also use apply_defect_density on custom geometries\n    >>> from bloqade import start\n    >>> start.add_position([(0,0), (1,1)])\n    .apply_defect_density(0.5, custom_rng)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...apply_defect_count(defect_counts).add_position(positions)`:\n        to add more positions\n        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...apply_defect_count(defect_counts)\n        .apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...apply_defect_count(defect_counts).scale(scale)`:\n        to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...apply_defect_count(defect_counts).rydberg`:\n        to specify Rydberg coupling\n        - `...apply_defect_count(defect_counts).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...apply_defect_count(defect_counts).show()`:\n        shows your geometry in your web browser\n    \"\"\"\n\n    p = min(1, max(0, defect_probability))\n    location_list = []\n\n    for location_info in self.enumerate():\n        if rng.random() < p:\n            location_list.append(\n                LocationInfo.create(\n                    location_info.position,\n                    (\n                        False\n                        if location_info.filling is SiteFilling.filled\n                        else True\n                    ),\n                )\n            )\n        else:\n            location_list.append(location_info)\n\n    return ListOfLocations(location_list=location_list)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.enumerate","title":"enumerate","text":"
enumerate()\n

enumerate all locations in the register.

Source code in src/bloqade/ir/location/location.py
def enumerate(self) -> Generator[LocationInfo, None, None]:\n    \"\"\"enumerate all locations in the register.\"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.figure","title":"figure","text":"
figure(fig_kwargs=None, **assignments)\n

obtain a figure object from the atom arrangement.

Source code in src/bloqade/ir/location/location.py
def figure(self, fig_kwargs=None, **assignments):\n    \"\"\"obtain a figure object from the atom arrangement.\"\"\"\n    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.rydberg_interaction","title":"rydberg_interaction","text":"
rydberg_interaction(**assignments)\n

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default **assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in src/bloqade/ir/location/location.py
def rydberg_interaction(self, **assignments) -> NDArray:\n    \"\"\"calculate the Rydberg interaction matrix.\n\n    Args:\n        **assignments: the values to assign to the variables in the register.\n\n    Returns:\n        NDArray: the Rydberg interaction matrix in the lower triangular form.\n\n    \"\"\"\n\n    from bloqade.constants import RB_C6\n\n    # calculate the Interaction matrix\n    V_ij = np.zeros((self.n_sites, self.n_sites))\n    for i, site_i in enumerate(self.enumerate()):\n        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])\n\n        for j, site_j in enumerate(self.enumerate()):\n            if j >= i:\n                break  # enforce lower triangular form\n\n            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])\n            r_ij = np.linalg.norm(pos_i - pos_j)\n\n            V_ij[i, j] = RB_C6 / r_ij**6\n\n    return V_ij\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.scale","title":"scale","text":"
scale(scale)\n

Scale the geometry of your atoms.

"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.AtomArrangement.scale--usage-example","title":"Usage Example:","text":"
>>> reg = start.add_position([(0,0), (1,1)])\n# atom positions are now (0,0), (2,2)\n>>> new_reg = reg.scale(2)\n# you may also use scale on pre-defined geometries\n>>> from bloqade.atom_arrangement import Chain\n# atoms in the chain will now be 2 um apart versus\n# the default 1 um\n>>> Chain(11).scale(2)\n
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in src/bloqade/ir/location/location.py
@beartype\ndef scale(self, scale: ScalarType):\n    \"\"\"\n    Scale the geometry of your atoms.\n\n    ### Usage Example:\n    ```\n    >>> reg = start.add_position([(0,0), (1,1)])\n    # atom positions are now (0,0), (2,2)\n    >>> new_reg = reg.scale(2)\n    # you may also use scale on pre-defined geometries\n    >>> from bloqade.atom_arrangement import Chain\n    # atoms in the chain will now be 2 um apart versus\n    # the default 1 um\n    >>> Chain(11).scale(2)\n    ```\n\n    - Next possible steps are:\n    - Continuing to build your geometry via:\n        - `...add_position(positions).add_position(positions)`:\n            to add more positions\n        - `...add_position(positions).apply_defect_count(n_defects)`:\n        to randomly drop out n_atoms\n        - `...add_position(positions).apply_defect_density(defect_probability)`:\n        to drop out atoms with a certain probability\n        - `...add_position(positions).scale(scale)`: to scale the geometry\n    - Targeting a level coupling once you're done with the atom geometry:\n        - `...add_position(positions).rydberg`:\n        to specify Rydberg coupling\n        - `...add_position(positions).hyperfine`:\n        to specify Hyperfine coupling\n    - Visualizing your atom geometry:\n        - `...add_position(positions).show()`:\n        shows your geometry in your web browser\n\n    \"\"\"\n\n    scale = cast(scale)\n    location_list = []\n    for location_info in self.enumerate():\n        x, y = location_info.position\n        new_position = (scale * x, scale * y)\n        location_list.append(\n            LocationInfo.create(new_position, bool(location_info.filling.value))\n        )\n\n    return ListOfLocations(location_list)\n
"},{"location":"reference/bloqade/ir/location/location/#bloqade.ir.location.location.ParallelRegisterInfo","title":"ParallelRegisterInfo","text":"
ParallelRegisterInfo(parallel_register)\n

ParallelRegisterInfo

Source code in src/bloqade/ir/location/location.py
def __init__(self, parallel_register: ParallelRegister):\n    atom_arrangement = parallel_register.atom_arrangement\n    cluster_spacing = parallel_register.cluster_spacing\n\n    if atom_arrangement.n_atoms > 0:\n        # calculate bounding box\n        # of this register\n        location_iter = atom_arrangement.enumerate()\n        (x, y) = next(location_iter).position\n        x_min = x\n        x_max = x\n        y_min = y\n        y_max = y\n\n        for location_info in location_iter:\n            (x, y) = location_info.position\n            x_min = x.min(x_min)\n            x_max = x.max(x_max)\n            y_min = y.min(y_min)\n            y_max = y.max(y_max)\n\n        shift_x = (x_max - x_min) + cluster_spacing\n        shift_y = (y_max - y_min) + cluster_spacing\n\n        register_locations = [\n            list(location_info.position)\n            for location_info in atom_arrangement.enumerate()\n        ]\n        register_filling = [\n            location_info.filling.value\n            for location_info in atom_arrangement.enumerate()\n        ]\n        shift_vectors = [[shift_x, cast(0)], [cast(0), shift_y]]\n    else:\n        raise ValueError(\"No locations to parallelize.\")\n\n    self.register_locations = register_locations\n    self.register_filling = register_filling\n    self.shift_vectors = shift_vectors\n
"},{"location":"reference/bloqade/ir/routine/","title":"Index","text":""},{"location":"reference/bloqade/ir/routine/base/","title":"Base","text":""},{"location":"reference/bloqade/ir/routine/base/#bloqade.ir.routine.base.Routine","title":"Routine","text":"

Bases: RoutineBase

Result of parsing a completed Builder string.

"},{"location":"reference/bloqade/ir/routine/base/#bloqade.ir.routine.base.RoutineShow","title":"RoutineShow","text":"

Bases: Show

"},{"location":"reference/bloqade/ir/routine/base/#bloqade.ir.routine.base.RoutineShow.show","title":"show","text":"
show(*args, batch_index=0)\n

Show an interactive plot of the routine.

int

which parameter set out of the batch to use. Default is 0. If there are no batch parameters, use 0.

*args: Any Specify the parameters that are defined in the .args([...]) build step.

Source code in src/bloqade/ir/routine/base.py
def show(self: \"RoutineBase\", *args, batch_index: int = 0):\n    \"\"\"Show an interactive plot of the routine.\n\n    batch_index: int\n        which parameter set out of the batch to use. Default is 0.\n        If there are no batch parameters, use 0.\n\n    *args: Any\n        Specify the parameters that are defined in the `.args([...])` build step.\n\n    \"\"\"\n    if self.source is None:\n        raise ValueError(\"Cannot show a routine without a source Builder.\")\n\n    return self.source.show(*args, batch_id=batch_index)\n
"},{"location":"reference/bloqade/ir/routine/bloqade/","title":"Bloqade","text":""},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadeEmulation","title":"BloqadeEmulation dataclass","text":"

Data class to hold the Hamiltonian and metadata for a given set of parameters

"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadeEmulation.hamiltonian","title":"hamiltonian property","text":"
hamiltonian\n

Return the Hamiltonian object for the given task data.

"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadeEmulation.metadata","title":"metadata property","text":"
metadata\n

The metadata for the given task data.

"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadeEmulation.evolve","title":"evolve","text":"
evolve(\n    state=None,\n    solver_name=\"dop853\",\n    atol=1e-07,\n    rtol=1e-14,\n    nsteps=2147483647,\n    times=(),\n    interaction_picture=False,\n)\n

Evolve an initial state vector using the Hamiltonian

Parameters:

Name Type Description Default state Optional[StateVector]

The initial state vector to

None solver_name str

Which SciPy Solver to use. Defaults to

'dop853' atol float

Absolute tolerance for ODE solver. Defaults

1e-07 rtol float

Relative tolerance for adaptive step in

1e-14 nsteps int

Maximum number of steps allowed per integration

2147483647 times Sequence[float]

The times to evaluate the state vector

() interaction_picture bool

Use the interaction picture when

False

Returns:

Type Description Iterator[StateVector]

Iterator[StateVector]: An iterator of the state vectors at each time step.

Source code in src/bloqade/ir/routine/bloqade.py
def evolve(\n    self,\n    state: Optional[StateVector] = None,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-7,\n    rtol: float = 1e-14,\n    nsteps: int = 2147483647,\n    times: Sequence[float] = (),\n    interaction_picture: bool = False,\n) -> Iterator[StateVector]:\n    \"\"\"Evolve an initial state vector using the Hamiltonian\n\n    Args:\n        state (Optional[StateVector], optional): The initial state vector to\n        evolve. if not provided, the zero state will be used. Defaults to None.\n        solver_name (str, optional): Which SciPy Solver to use. Defaults to\n        \"dop853\".\n        atol (float, optional): Absolute tolerance for ODE solver. Defaults\n        to 1e-14.\n        rtol (float, optional): Relative tolerance for adaptive step in\n        ODE solver. Defaults to 1e-7.\n        nsteps (int, optional): Maximum number of steps allowed per integration\n        step. Defaults to 2147483647.\n        times (Sequence[float], optional): The times to evaluate the state vector\n        at. Defaults to (). If not provided the state will be evaluated at\n        the end of the bloqade program.\n        interaction_picture (bool, optional): Use the interaction picture when\n        solving schrodinger equation. Defaults to False.\n\n    Returns:\n        Iterator[StateVector]: An iterator of the state vectors at each time step.\n\n    \"\"\"\n    state = self.zero_state(np.complex128) if state is None else state\n\n    U = AnalogGate(self.hamiltonian)\n\n    return U.apply(\n        state,\n        times=times,\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadeEmulation.fock_state","title":"fock_state","text":"
fock_state(fock_state_str, dtype=np.float64)\n

Return the fock state for the given Hamiltonian.

Source code in src/bloqade/ir/routine/bloqade.py
def fock_state(\n    self, fock_state_str: str, dtype: np.dtype = np.float64\n) -> StateVector:\n    \"\"\"Return the fock state for the given Hamiltonian.\"\"\"\n    index = self.hamiltonian.space.fock_state_to_index(fock_state_str)\n    data = np.zeros(self.hamiltonian.space.size, dtype=dtype)\n    data[index] = 1\n    return StateVector(data, self.hamiltonian.space)\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadeEmulation.zero_state","title":"zero_state","text":"
zero_state(dtype=np.float64)\n

Return the zero state for the given Hamiltonian.

Source code in src/bloqade/ir/routine/bloqade.py
def zero_state(self, dtype: np.dtype = np.float64) -> StateVector:\n    \"\"\"Return the zero state for the given Hamiltonian.\"\"\"\n    return self.hamiltonian.space.zero_state(dtype)\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadePythonRoutine","title":"BloqadePythonRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadePythonRoutine.run","title":"run","text":"
run(\n    shots,\n    args=(),\n    name=None,\n    blockade_radius=0.0,\n    waveform_runtime=\"interpret\",\n    interaction_picture=False,\n    cache_matrices=False,\n    multiprocessing=False,\n    num_workers=None,\n    solver_name=\"dop853\",\n    atol=1e-07,\n    rtol=1e-14,\n    nsteps=2147483647,\n)\n

Run the current program using bloqade python backend

Parameters:

Name Type Description Default shots int

number of shots after running state vector simulation

required args Tuple[LiteralType, ...]

The values for parameters defined

() name Optional[str]

Name to give this run. Defaults to None.

None blockade_radius float

Use the Blockade subspace given a

0.0 waveform_runtime str

(bool, optional): Use Numba to compile the waveforms,

'interpret' interaction_picture bool

Use the interaction picture when

False cache_matrices bool

Reuse previously evaluated matrcies when

False multiprocessing bool

Use multiple processes to process the

False num_workers Optional[int]

Number of processes to run with

None solver_name str

Which SciPy Solver to use. Defaults to

'dop853' atol float

Absolute tolerance for ODE solver. Defaults to

1e-07 rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14 nsteps int

Maximum number of steps allowed per integration

2147483647

Raises:

Type Description ValueError

Cannot use multiprocessing and cache_matrices at the same time.

Returns:

Name Type Description LocalBatch LocalBatch

Batch of local tasks that have been executed.

Source code in src/bloqade/ir/routine/bloqade.py
@beartype\ndef run(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    blockade_radius: float = 0.0,\n    waveform_runtime: str = \"interpret\",\n    interaction_picture: bool = False,\n    cache_matrices: bool = False,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-7,\n    rtol: float = 1e-14,\n    nsteps: int = 2_147_483_647,\n) -> LocalBatch:\n    \"\"\"Run the current program using bloqade python backend\n\n    Args:\n        shots (int): number of shots after running state vector simulation\n        args (Tuple[LiteralType, ...], optional): The values for parameters defined\n        in `args`. Defaults to ().\n        name (Optional[str], optional): Name to give this run. Defaults to None.\n        blockade_radius (float, optional): Use the Blockade subspace given a\n        particular radius. Defaults to 0.0.\n        waveform_runtime: (bool, optional): Use Numba to compile the waveforms,\n        Defaults to False.\n        interaction_picture (bool, optional): Use the interaction picture when\n        solving schrodinger equation. Defaults to False.\n        cache_matrices (bool, optional): Reuse previously evaluated matrcies when\n        possible. Defaults to False.\n        multiprocessing (bool, optional): Use multiple processes to process the\n        batches. Defaults to False.\n        num_workers (Optional[int], optional): Number of processes to run with\n        multiprocessing. Defaults to None.\n        solver_name (str, optional): Which SciPy Solver to use. Defaults to\n        \"dop853\".\n        atol (float, optional): Absolute tolerance for ODE solver. Defaults to\n        1e-14.\n        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.\n        Defaults to 1e-7.\n        nsteps (int, optional): Maximum number of steps allowed per integration\n        step. Defaults to 2_147_483_647, the maximum value.\n\n    Raises:\n        ValueError: Cannot use multiprocessing and cache_matrices at the same time.\n\n    Returns:\n        LocalBatch: Batch of local tasks that have been executed.\n    \"\"\"\n    if multiprocessing and cache_matrices:\n        raise ValueError(\n            \"Cannot use multiprocessing and cache_matrices at the same time.\"\n        )\n\n    compile_options = dict(\n        shots=shots,\n        args=args,\n        name=name,\n        blockade_radius=blockade_radius,\n        cache_matrices=cache_matrices,\n        waveform_runtime=waveform_runtime,\n    )\n\n    solver_options = dict(\n        multiprocessing=multiprocessing,\n        num_workers=num_workers,\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n\n    batch = self._compile(**compile_options)\n    batch._run(**solver_options)\n\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.BloqadePythonRoutine.run_callback","title":"run_callback","text":"
run_callback(\n    callback,\n    program_args=(),\n    callback_args=(),\n    ignore_exceptions=False,\n    blockade_radius=0.0,\n    waveform_runtime=\"interpret\",\n    interaction_picture=False,\n    cache_matrices=False,\n    multiprocessing=False,\n    num_workers=None,\n    solver_name=\"dop853\",\n    atol=1e-07,\n    rtol=1e-14,\n    nsteps=2147483647,\n    use_hyperfine=False,\n)\n

Run state-vector simulation with a callback to access full state-vector from emulator

Parameters:

Name Type Description Default callback Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any] required program_args Tuple[LiteralType, ...]

The values for parameters

() callback_args Tuple[Any, ...]

Extra arguments to pass into

() ignore_exceptions bool

(bool, optional) If True any exception raised during

False blockade_radius float

Use the Blockade subspace given a

0.0 waveform_runtime str

(str, optional): Specify which runtime to use for

'interpret' interaction_picture bool

Use the interaction picture when

False cache_matrices bool

Reuse previously evaluated matrcies when

False multiprocessing bool

Use multiple processes to process the

False num_workers Optional[int]

Number of processes to run with

None solver_name str

Which SciPy Solver to use. Defaults to

'dop853' atol float

Absolute tolerance for ODE solver. Defaults to

1e-07 rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14 nsteps int

Maximum number of steps allowed per integration

2147483647

Returns:

Name Type Description List List

List of resulting outputs from the callbacks

Raises:

Type Description RuntimeError

Raises the first error that occurs, only if

Note

For the callback function, first argument is the many-body wavefunction as a 1D complex numpy array, the second argument is of type Metadata which is a Named Tuple where the fields correspond to the parameters of that given task, RydbergHamiltonian is the object that contains the Hamiltonian used to generate the evolution for that task, Finally any optional positional arguments are allowed after that. The return value can be anything, the results will be collected in a list for each task in the batch.

Source code in src/bloqade/ir/routine/bloqade.py
@beartype\ndef run_callback(\n    self,\n    callback: Callable[[StateVector, NamedTuple, RydbergHamiltonian, Any], Any],\n    program_args: Tuple[LiteralType, ...] = (),\n    callback_args: Tuple = (),\n    ignore_exceptions: bool = False,\n    blockade_radius: float = 0.0,\n    waveform_runtime: str = \"interpret\",\n    interaction_picture: bool = False,\n    cache_matrices: bool = False,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    solver_name: str = \"dop853\",\n    atol: float = 1e-7,\n    rtol: float = 1e-14,\n    nsteps: int = 2_147_483_647,\n    use_hyperfine: bool = False,\n) -> List:\n    \"\"\"Run state-vector simulation with a callback to access full state-vector from\n    emulator\n\n    Args:\n        callback (Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]):\n        The callback function to run for each task in batch. See note below for more\n        details about the signature of the function.\n        program_args (Tuple[LiteralType, ...], optional): The values for parameters\n        defined in `args`. Defaults to ().\n        callback_args (Tuple[Any,...], optional): Extra arguments to pass into\n        ignore_exceptions: (bool, optional) If `True` any exception raised during\n        a task will be saved instead of the resulting output of the callback,\n        otherwise the first exception by task number will be raised after *all*\n        tasks have executed. Defaults to False.\n        blockade_radius (float, optional): Use the Blockade subspace given a\n        particular radius. Defaults to 0.0.\n        waveform_runtime: (str, optional): Specify which runtime to use for\n        waveforms. Defaults to \"interpret\".\n        interaction_picture (bool, optional): Use the interaction picture when\n        solving schrodinger equation. Defaults to False.\n        cache_matrices (bool, optional): Reuse previously evaluated matrcies when\n        possible. Defaults to False.\n        multiprocessing (bool, optional): Use multiple processes to process the\n        batches. Defaults to False.\n        num_workers (Optional[int], optional): Number of processes to run with\n        multiprocessing. Defaults to None.\n        solver_name (str, optional): Which SciPy Solver to use. Defaults to\n        \"dop853\".\n        atol (float, optional): Absolute tolerance for ODE solver. Defaults to\n        1e-14.\n        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.\n        Defaults to 1e-7.\n        nsteps (int, optional): Maximum number of steps allowed per integration\n        step. Defaults to 2_147_483_647, the maximum value.\n\n    Returns:\n        List: List of resulting outputs from the callbacks\n\n    Raises:\n        RuntimeError: Raises the first error that occurs, only if\n        `ignore_exceptions=False`.\n\n    Note:\n        For the `callback` function, first argument is the many-body wavefunction\n        as a 1D complex numpy array, the second argument is of type `Metadata` which\n        is a Named Tuple where the fields correspond to the parameters of that given\n        task, RydbergHamiltonian is the object that contains the Hamiltonian used to\n        generate the evolution for that task, Finally any optional positional\n        arguments are allowed after that. The return value can be anything, the\n        results will be collected in a list for each task in the batch.\n\n\n    \"\"\"\n    if multiprocessing:\n        from multiprocessing import Process, Queue, cpu_count\n    else:\n        from queue import Queue\n\n    if cache_matrices:\n        compile_cache = CompileCache()\n    else:\n        compile_cache = None\n\n    solver_args = dict(\n        solver_name=solver_name,\n        atol=atol,\n        rtol=rtol,\n        nsteps=nsteps,\n        interaction_picture=interaction_picture,\n    )\n\n    runner = self.EmuRunner(\n        compile_cache=compile_cache,\n        solver_args=solver_args,\n        callback=callback,\n        callback_args=callback_args,\n    )\n\n    tasks = Queue()\n    results = Queue()\n\n    total_tasks = 0\n    ir_iter = self._generate_ir(\n        program_args, blockade_radius, waveform_runtime, use_hyperfine\n    )\n    for task_data in ir_iter:\n        task_number = task_data.task_id\n        emulator_ir = task_data.emulator_ir\n        metadata = task_data.metadata_dict\n        total_tasks += 1\n        tasks.put((task_number, (emulator_ir, metadata)))\n\n    workers = []\n    if multiprocessing:\n        num_workers = max(int(num_workers or cpu_count()), 1)\n        num_workers = min(total_tasks, num_workers)\n\n        for _ in range(num_workers):\n            worker = Process(\n                target=BloqadePythonRoutine.process_tasks,\n                args=(runner, tasks, results),\n            )\n            worker.start()\n\n            workers.append(worker)\n    else:\n        self.process_tasks(runner, tasks, results)\n\n    # blocks until all\n    # results have been fetched\n    # from the id_results Queue\n    id_results = []\n    for i in range(total_tasks):\n        id_results.append(results.get())\n\n    if workers:\n        for worker in workers:\n            worker.join()\n\n        tasks.close()\n        results.close()\n\n    id_results.sort(key=lambda x: x[0])\n    results = []\n\n    for task_id, result in id_results:\n        if not ignore_exceptions and isinstance(result, BaseException):\n            try:\n                raise result\n            except BaseException:\n                raise RuntimeError(\n                    f\"{result.__class__.__name__} occured during child process \"\n                    f\"running for task number {task_id}:\\n{traceback.format_exc()}\"\n                )\n\n        results.append(result)\n\n    return results\n
"},{"location":"reference/bloqade/ir/routine/bloqade/#bloqade.ir.routine.bloqade.TaskData","title":"TaskData dataclass","text":"

Data class to hold the program ir and metadata for a given set of parameters

"},{"location":"reference/bloqade/ir/routine/braket/","title":"Braket","text":""},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine","title":"BraketHardwareRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine.__call__","title":"__call__","text":"
__call__(\n    *args, shots=1, name=None, shuffle=False, **kwargs\n)\n

Compile to a RemoteBatch, which contain Braket backend specific tasks, run_async to Braket, and wait until the results are coming back.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

1 args LiteralType

additional arguments for args variables.

() name str

custom name of the batch

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef __call__(\n    self,\n    *args: LiteralType,\n    shots: int = 1,\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n):\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n    Braket backend specific tasks, run_async to Braket,\n    and wait until the results are coming back.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args: additional arguments for args variables.\n        name (str): custom name of the batch\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    return self.run(shots, args, name, shuffle, **kwargs)\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine.run","title":"run","text":"
run(shots, args=(), name=None, shuffle=False, **kwargs)\n

Compile to a RemoteBatch, which contain Braket backend specific tasks, run_async to Braket, and wait until the results are coming back.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple

additional arguments

() name str

custom name of the batch

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef run(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n) -> RemoteBatch:\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n    Braket backend specific tasks, run_async to Braket,\n    and wait until the results are coming back.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args (Tuple): additional arguments\n        name (str): custom name of the batch\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n\n    batch = self.run_async(shots, args, name, shuffle, **kwargs)\n    batch.pull()\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketHardwareRoutine.run_async","title":"run_async","text":"
run_async(\n    shots, args=(), name=None, shuffle=False, **kwargs\n)\n

Compile to a RemoteBatch, which contain Braket backend specific tasks, and run_async to Braket.

Note

This is async.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple

Values of the parameter defined in args, defaults to ()

() name str | None

custom name of the batch, defaults to None

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef run_async(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n) -> RemoteBatch:\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n    Braket backend specific tasks, and run_async to Braket.\n\n    Note:\n        This is async.\n\n    Args:\n        shots (int): number of shots\n        args (Tuple): Values of the parameter defined in `args`, defaults to ()\n        name (str | None): custom name of the batch, defaults to None\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n\n    batch = self._compile(shots, args, name)\n    batch._submit(shuffle, **kwargs)\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketLocalEmulatorRoutine","title":"BraketLocalEmulatorRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketLocalEmulatorRoutine.__call__","title":"__call__","text":"
__call__(\n    *args,\n    shots=1,\n    name=None,\n    multiprocessing=False,\n    num_workers=None,\n    **kwargs\n)\n

Compile to a LocalBatch, and run. The LocalBatch contain tasks to run on local emulator.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

1 args LiteralType

additional arguments for args variables.

() multiprocessing bool

enable multi-process

False num_workers int

number of workers to run the emulator

None Return

LocalBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef __call__(\n    self,\n    *args: LiteralType,\n    shots: int = 1,\n    name: Optional[str] = None,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    **kwargs,\n):\n    \"\"\"\n    Compile to a LocalBatch, and run.\n    The LocalBatch contain tasks to run on local emulator.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args: additional arguments for args variables.\n        multiprocessing (bool): enable multi-process\n        num_workers (int): number of workers to run the emulator\n\n    Return:\n        LocalBatch\n\n    \"\"\"\n    return self.run(\n        shots,\n        args,\n        name,\n        multiprocessing=multiprocessing,\n        num_workers=num_workers,\n        **kwargs,\n    )\n
"},{"location":"reference/bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketLocalEmulatorRoutine.run","title":"run","text":"
run(\n    shots,\n    args=(),\n    name=None,\n    multiprocessing=False,\n    num_workers=None,\n    **kwargs\n)\n

Compile to a LocalBatch, and run. The LocalBatch contain tasks to run on local emulator.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple[LiteralType, ...]

additional arguments for args variables.

() multiprocessing bool

enable multi-process

False num_workers int

number of workers to run the emulator

None Return

LocalBatch

Source code in src/bloqade/ir/routine/braket.py
@beartype\ndef run(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    multiprocessing: bool = False,\n    num_workers: Optional[int] = None,\n    **kwargs,\n) -> LocalBatch:\n    \"\"\"\n    Compile to a LocalBatch, and run.\n    The LocalBatch contain tasks to run on local emulator.\n\n    Note:\n        This is sync, and will wait until remote results\n        finished.\n\n    Args:\n        shots (int): number of shots\n        args: additional arguments for args variables.\n        multiprocessing (bool): enable multi-process\n        num_workers (int): number of workers to run the emulator\n\n    Return:\n        LocalBatch\n\n    \"\"\"\n\n    batch = self._compile(shots, args, name)\n    batch._run(multiprocessing=multiprocessing, num_workers=num_workers, **kwargs)\n    return batch\n
"},{"location":"reference/bloqade/ir/routine/params/","title":"Params","text":""},{"location":"reference/bloqade/ir/routine/quera/","title":"Quera","text":""},{"location":"reference/bloqade/ir/routine/quera/#bloqade.ir.routine.quera.QuEraHardwareRoutine","title":"QuEraHardwareRoutine","text":"

Bases: RoutineBase

"},{"location":"reference/bloqade/ir/routine/quera/#bloqade.ir.routine.quera.QuEraHardwareRoutine.run_async","title":"run_async","text":"
run_async(\n    shots, args=(), name=None, shuffle=False, **kwargs\n)\n

Compile to a RemoteBatch, which contain QuEra backend specific tasks, and run_async through QuEra service.

Parameters:

Name Type Description Default shots int

number of shots

required args Tuple

additional arguments

() name str

custom name of the batch

None shuffle bool

shuffle the order of jobs

False Return

RemoteBatch

Source code in src/bloqade/ir/routine/quera.py
@beartype\ndef run_async(\n    self,\n    shots: int,\n    args: Tuple[LiteralType, ...] = (),\n    name: Optional[str] = None,\n    shuffle: bool = False,\n    **kwargs,\n) -> RemoteBatch:\n    \"\"\"\n    Compile to a RemoteBatch, which contain\n        QuEra backend specific tasks,\n        and run_async through QuEra service.\n\n    Args:\n        shots (int): number of shots\n        args (Tuple): additional arguments\n        name (str): custom name of the batch\n        shuffle (bool): shuffle the order of jobs\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    batch = self._compile(shots, args, name)\n    batch._submit(shuffle, **kwargs)\n    return batch\n
"},{"location":"reference/bloqade/submission/","title":"Index","text":""},{"location":"reference/bloqade/submission/base/","title":"Base","text":""},{"location":"reference/bloqade/submission/braket/","title":"Braket","text":""},{"location":"reference/bloqade/submission/load_config/","title":"Load config","text":""},{"location":"reference/bloqade/submission/mock/","title":"Mock","text":""},{"location":"reference/bloqade/submission/quera/","title":"Quera","text":""},{"location":"reference/bloqade/submission/ir/","title":"Index","text":""},{"location":"reference/bloqade/submission/ir/braket/","title":"Braket","text":""},{"location":"reference/bloqade/submission/ir/capabilities/","title":"Capabilities","text":""},{"location":"reference/bloqade/submission/ir/parallel/","title":"Parallel","text":""},{"location":"reference/bloqade/submission/ir/parallel/#bloqade.submission.ir.parallel.ClusterLocationInfo","title":"ClusterLocationInfo","text":"

Bases: BaseModel

Class that stores the mapping of batched jobs.

Parameters:

Name Type Description Default cluster_index int

the index of the cluster a site belongs to

required global_location_index int

the index of the site in the multplexed system

required cluster_location_index int

the index of the site in the original system

required"},{"location":"reference/bloqade/submission/ir/task_results/","title":"Task results","text":""},{"location":"reference/bloqade/submission/ir/task_results/#bloqade.submission.ir.task_results.QuEraTaskResults","title":"QuEraTaskResults","text":"

Bases: BaseModel

"},{"location":"reference/bloqade/submission/ir/task_results/#bloqade.submission.ir.task_results.QuEraTaskResults.export_as_probabilities","title":"export_as_probabilities","text":"
export_as_probabilities()\n

converts from shot results to probabilities

Returns:

Name Type Description TaskProbabilities TaskProbabilities

The task results as probabilties

Source code in src/bloqade/submission/ir/task_results.py
def export_as_probabilities(self) -> TaskProbabilities:\n    \"\"\"converts from shot results to probabilities\n\n    Returns:\n        TaskProbabilities: The task results as probabilties\n    \"\"\"\n    counts = dict()\n    nshots = len(self.shot_outputs)\n    for shot_result in self.shot_outputs:\n        pre_sequence_str = \"\".join(str(bit) for bit in shot_result.pre_sequence)\n\n        post_sequence_str = \"\".join(str(bit) for bit in shot_result.post_sequence)\n\n        configuration = (pre_sequence_str, post_sequence_str)\n        # iterative average\n        current_count = counts.get(configuration, 0)\n        counts[configuration] = current_count + 1\n\n    probabilities = [(config, count / nshots) for config, count in counts.items()]\n    return TaskProbabilities(probabilities=probabilities)\n
"},{"location":"reference/bloqade/submission/ir/task_specification/","title":"Task specification","text":""},{"location":"reference/bloqade/task/","title":"Index","text":""},{"location":"reference/bloqade/task/base/","title":"Base","text":""},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report","title":"Report","text":"
Report(data, metas, geos, name='')\n
Source code in src/bloqade/task/base.py
def __init__(self, data, metas, geos, name=\"\") -> None:\n    self.dataframe = data  # df\n    self._bitstrings = None  # bitstring cache\n    self._counts = None  # counts cache\n    self.metas = metas\n    self.geos = geos\n    self.name = name + \" \" + str(datetime.datetime.now())\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.bitstrings","title":"bitstrings","text":"
bitstrings(filter_perfect_filling=True, clusters=[])\n

Get the bitstrings from the data.

Parameters:

Name Type Description Default filter_perfect_filling bool

whether return will

True clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]):

[]

Returns:

Name Type Description bitstrings list of ndarray

list corresponding to each

List[NDArray]

task in the report. Each element is an ndarray of shape

List[NDArray]

(nshots, nsites) where nshots is the number of shots for

List[NDArray]

the task and nsites is the number of sites in the task.

Note

Note that nshots may vary between tasks if filter_perfect_filling is set to True.

Source code in src/bloqade/task/base.py
@beartype\ndef bitstrings(\n    self,\n    filter_perfect_filling: bool = True,\n    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],\n) -> List[NDArray]:\n    \"\"\"Get the bitstrings from the data.\n\n    Args:\n        filter_perfect_filling (bool): whether return will\n        only contain perfect filling shots. Defaults to True.\n        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):\n        cluster index to filter shots from. If none are provided\n        all clusters are used, defaults to [].\n\n    Returns:\n        bitstrings (list of ndarray): list corresponding to each\n        task in the report. Each element is an ndarray of shape\n        (nshots, nsites) where nshots is the number of shots for\n        the task and nsites is the number of sites in the task.\n\n    Note:\n        Note that nshots may vary between tasks if filter_perfect_filling\n        is set to True.\n\n    \"\"\"\n\n    task_numbers = self.dataframe.index.get_level_values(\"task_number\").unique()\n\n    bitstrings = []\n    for task_number in task_numbers:\n        mask = self._filter(\n            task_number=task_number,\n            filter_perfect_filling=filter_perfect_filling,\n            clusters=clusters,\n        )\n        if np.any(mask):\n            bitstrings.append(self.dataframe.loc[mask].to_numpy())\n        else:\n            bitstrings.append(\n                np.zeros((0, self.dataframe.shape[1]), dtype=np.uint8)\n            )\n\n    return bitstrings\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.counts","title":"counts","text":"
counts(filter_perfect_filling=True, clusters=[])\n

Get the counts of unique bit strings.

Parameters:

Name Type Description Default filter_perfect_filling bool

whether return will

True clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]):

[]

Returns:

Name Type Description bitstrings list of ndarray

list corresponding to each

List[OrderedDict[str, int]]

task in the report. Each element is an ndarray of shape

List[OrderedDict[str, int]]

(nshots, nsites) where nshots is the number of shots for

List[OrderedDict[str, int]]

the task and nsites is the number of sites in the task.

Note

Note that nshots may vary between tasks if filter_perfect_filling is set to True.

Source code in src/bloqade/task/base.py
def counts(\n    self,\n    filter_perfect_filling: bool = True,\n    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],\n) -> List[OrderedDict[str, int]]:\n    \"\"\"Get the counts of unique bit strings.\n\n    Args:\n        filter_perfect_filling (bool): whether return will\n        only contain perfect filling shots. Defaults to True.\n        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):\n        cluster index to filter shots from. If none are provided\n        all clusters are used, defaults to [].\n\n    Returns:\n        bitstrings (list of ndarray): list corresponding to each\n        task in the report. Each element is an ndarray of shape\n        (nshots, nsites) where nshots is the number of shots for\n        the task and nsites is the number of sites in the task.\n\n    Note:\n        Note that nshots may vary between tasks if filter_perfect_filling\n        is set to True.\n\n    \"\"\"\n\n    def generate_counts(bitstring):\n        output = np.unique(bitstring, axis=0, return_counts=True)\n\n        count_list = [\n            (\"\".join(map(str, bitstring)), int(count))\n            for bitstring, count in zip(*output)\n        ]\n        count_list.sort(key=lambda x: x[1], reverse=True)\n        count = OrderedDict(count_list)\n\n        return count\n\n    return list(\n        map(generate_counts, self.bitstrings(filter_perfect_filling, clusters))\n    )\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.list_param","title":"list_param","text":"
list_param(field_name)\n

List the parameters associate with the given variable field_name for each tasks.

Parameters:

Name Type Description Default field_name str

variable name

required Source code in src/bloqade/task/base.py
def list_param(self, field_name: str) -> List[Union[Number, None]]:\n    \"\"\"\n    List the parameters associate with the given variable field_name\n    for each tasks.\n\n    Args:\n        field_name (str): variable name\n\n    \"\"\"\n\n    def cast(x):\n        if x is None:\n            return None\n        elif isinstance(x, (list, tuple, np.ndarray)):\n            return list(map(cast, x))\n        else:\n            return float(x)\n\n    return list(map(cast, (meta.get(field_name) for meta in self.metas)))\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.rydberg_densities","title":"rydberg_densities","text":"
rydberg_densities(filter_perfect_filling=True, clusters=[])\n

Get rydberg density for each task.

Parameters:

Name Type Description Default filter_perfect_filling bool

whether return will

True Return

per-site rydberg density for each task as a pandas DataFrame or Series.

Source code in src/bloqade/task/base.py
@beartype\ndef rydberg_densities(\n    self,\n    filter_perfect_filling: bool = True,\n    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],\n) -> Union[pd.Series, pd.DataFrame]:\n    \"\"\"Get rydberg density for each task.\n\n    Args:\n        filter_perfect_filling (bool, optional): whether return will\n        only contain perfect filling shots. Defaults to True.\n\n    Return:\n        per-site rydberg density for each task as a pandas DataFrame or Series.\n\n    \"\"\"\n    mask = self._filter(\n        filter_perfect_filling=filter_perfect_filling, clusters=clusters\n    )\n    df = self.dataframe[mask]\n    return 1 - (df.groupby(\"task_number\").mean())\n
"},{"location":"reference/bloqade/task/base/#bloqade.task.base.Report.show","title":"show","text":"
show()\n

Interactive Visualization of the Report

Source code in src/bloqade/task/base.py
def show(self):\n    \"\"\"\n    Interactive Visualization of the Report\n\n    \"\"\"\n    display_report(self)\n
"},{"location":"reference/bloqade/task/batch/","title":"Batch","text":""},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Filter","title":"Filter","text":""},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Filter.filter_metadata","title":"filter_metadata","text":"
filter_metadata(__match_any__=False, **metadata)\n

Create a Batch object that has tasks filtered based on the values of metadata.

Parameters:

Name Type Description Default __match_any__ bool

if True, then a task will be included if it matches any of the metadata filters. If False, then a task will be included only if it matches all of the metadata filters. Defaults to False.

False **metadata MetadataFilterType

the metadata to filter on. The keys are the metadata names and the values (as a set) are the values to filter on. The elements in the set can be Real, Decimal, Tuple[Real], or Tuple[Decimal].

{} Return

type(self): a Batch object with the filtered tasks, either LocalBatch or RemoteBatch depending on the type of self

Source code in src/bloqade/task/batch.py
@beartype\ndef filter_metadata(\n    self, __match_any__: bool = False, **metadata: MetadataFilterType\n) -> Union[\"LocalBatch\", \"RemoteBatch\"]:\n    \"\"\"Create a Batch object that has tasks filtered based on the\n    values of metadata.\n\n    Args:\n        __match_any__: if True, then a task will be included if it\n            matches any of the metadata filters. If False, then a\n            task will be included only if it matches all of the\n            metadata filters. Defaults to False.\n\n        **metadata: the metadata to filter on. The keys are the metadata\n            names and the values (as a set) are the values to filter on.\n            The elements in the set can be Real, Decimal, Tuple[Real], or\n            Tuple[Decimal].\n\n    Return:\n        type(self): a Batch object with the filtered tasks, either\n            LocalBatch or RemoteBatch depending on the type of self\n\n    \"\"\"\n\n    def convert_to_decimal(element):\n        if isinstance(element, list):\n            return list(map(convert_to_decimal, element))\n        elif isinstance(element, (Real, Decimal)):\n            return Decimal(str(element))\n        else:\n            raise ValueError(\n                f\"Invalid value {element} for metadata filter. \"\n                \"Only Real, Decimal, List[Real], and List[Decimal] \"\n                \"are supported.\"\n            )\n\n    def metadata_match_all(task):\n        return all(\n            task.metadata.get(key) in value for key, value in metadata.items()\n        )\n\n    def metadata_match_any(task):\n        return any(\n            task.metadata.get(key) in value for key, value in metadata.items()\n        )\n\n    metadata = {k: list(map(convert_to_decimal, v)) for k, v in metadata.items()}\n\n    metadata_filter = metadata_match_any if __match_any__ else metadata_match_all\n\n    new_tasks = OrderedDict(\n        [(k, v) for k, v in self.tasks.items() if metadata_filter(v)]\n    )\n\n    kw = dict(self.__dict__)\n    kw[\"tasks\"] = new_tasks\n\n    return self.__class__(**kw)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.LocalBatch","title":"LocalBatch dataclass","text":"

Bases: Serializable, Filter

"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.LocalBatch.report","title":"report","text":"
report()\n

Generate analysis report base on currently completed tasks in the LocalBatch.

Return

Report

Source code in src/bloqade/task/batch.py
def report(self) -> Report:\n    \"\"\"\n    Generate analysis report base on currently\n    completed tasks in the LocalBatch.\n\n    Return:\n        Report\n\n    \"\"\"\n\n    ## this potentially can be specialize/disatch\n    ## offline\n    index = []\n    data = []\n    metas = []\n    geos = []\n\n    for task_number, task in self.tasks.items():\n        geometry = task.geometry\n        perfect_sorting = \"\".join(map(str, geometry.filling))\n        parallel_decoder = geometry.parallel_decoder\n\n        if parallel_decoder:\n            cluster_indices = parallel_decoder.get_cluster_indices()\n        else:\n            cluster_indices = {(0, 0): list(range(len(perfect_sorting)))}\n\n        shot_iter = filter(\n            lambda shot: shot.shot_status == QuEraShotStatusCode.Completed,\n            task.result().shot_outputs,\n        )\n\n        for shot, (cluster_coordinate, cluster_index) in product(\n            shot_iter, cluster_indices.items()\n        ):\n            pre_sequence = \"\".join(\n                map(\n                    str,\n                    (shot.pre_sequence[index] for index in cluster_index),\n                )\n            )\n\n            post_sequence = np.asarray(\n                [shot.post_sequence[index] for index in cluster_index],\n                dtype=np.int8,\n            )\n\n            pfc_sorting = \"\".join(\n                [perfect_sorting[index] for index in cluster_index]\n            )\n\n            key = (\n                task_number,\n                cluster_coordinate,\n                pfc_sorting,\n                pre_sequence,\n            )\n\n            index.append(key)\n            data.append(post_sequence)\n\n        metas.append(task.metadata)\n        geos.append(task.geometry)\n\n    index = pd.MultiIndex.from_tuples(\n        index, names=[\"task_number\", \"cluster\", \"perfect_sorting\", \"pre_sequence\"]\n    )\n\n    df = pd.DataFrame(data, index=index)\n    df.sort_index(axis=\"index\")\n\n    rept = None\n    if self.name is None:\n        rept = Report(df, metas, geos, \"Local\")\n    else:\n        rept = Report(df, metas, geos, self.name)\n\n    return rept\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.LocalBatch.rerun","title":"rerun","text":"
rerun(multiprocessing=False, num_workers=None, **kwargs)\n

Rerun all the tasks in the LocalBatch.

Return

Report

Source code in src/bloqade/task/batch.py
@beartype\ndef rerun(\n    self, multiprocessing: bool = False, num_workers: Optional[int] = None, **kwargs\n):\n    \"\"\"\n    Rerun all the tasks in the LocalBatch.\n\n    Return:\n        Report\n\n    \"\"\"\n\n    return self._run(\n        multiprocessing=multiprocessing, num_workers=num_workers, **kwargs\n    )\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch","title":"RemoteBatch dataclass","text":"

Bases: Serializable, Filter

"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.total_nshots","title":"total_nshots property","text":"
total_nshots\n

Total number of shots of all tasks in the RemoteBatch

Return

number of shots

"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.cancel","title":"cancel","text":"
cancel()\n

Cancel all the tasks in the Batch.

Return

self

Source code in src/bloqade/task/batch.py
def cancel(self) -> \"RemoteBatch\":\n    \"\"\"\n    Cancel all the tasks in the Batch.\n\n    Return:\n        self\n\n    \"\"\"\n    # cancel all jobs\n    for task in self.tasks.values():\n        task.cancel()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.fetch","title":"fetch","text":"
fetch()\n

Fetch the tasks in the Batch.

Note

Fetching will update the status of tasks, and only pull the results for those tasks that have completed.

Return

self

Source code in src/bloqade/task/batch.py
def fetch(self) -> \"RemoteBatch\":\n    \"\"\"\n    Fetch the tasks in the Batch.\n\n    Note:\n        Fetching will update the status of tasks,\n        and only pull the results for those tasks\n        that have completed.\n\n    Return:\n        self\n\n    \"\"\"\n    # online, non-blocking\n    # pull the results only when its ready\n    for task in self.tasks.values():\n        task.fetch()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_completed_tasks","title":"get_completed_tasks","text":"
get_completed_tasks()\n

Create a RemoteBatch object that contain completed tasks from current Batch.

Tasks consider completed with following status codes:

  1. Completed
  2. Partial
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def get_completed_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain completed tasks from current Batch.\n\n    Tasks consider completed with following status codes:\n\n    1. Completed\n    2. Partial\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    statuses = [\n        \"Completed\",\n        \"Partial\",\n    ]\n    return self.get_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_failed_tasks","title":"get_failed_tasks","text":"
get_failed_tasks()\n

Create a RemoteBatch object that contain failed tasks from current Batch.

failed tasks with following status codes:

  1. Failed
  2. Unaccepted
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def get_failed_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain failed tasks from current Batch.\n\n    failed tasks with following status codes:\n\n    1. Failed\n    2. Unaccepted\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # statuses that are in a state that are\n    # completed because of an error\n    statuses = [\"Failed\", \"Unaccepted\"]\n    return self.get_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_finished_tasks","title":"get_finished_tasks","text":"
get_finished_tasks()\n

Create a RemoteBatch object that contain finished tasks from current Batch.

Tasks consider finished with following status codes:

  1. Failed
  2. Unaccepted
  3. Completed
  4. Partial
  5. Cancelled
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def get_finished_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain finished tasks from current Batch.\n\n    Tasks consider finished with following status codes:\n\n    1. Failed\n    2. Unaccepted\n    3. Completed\n    4. Partial\n    5. Cancelled\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # statuses that are in a state that will\n    # not run going forward for any reason\n    statuses = [\"Completed\", \"Failed\", \"Unaccepted\", \"Partial\", \"Cancelled\"]\n    return self.get_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.get_tasks","title":"get_tasks","text":"
get_tasks(*status_codes)\n

Get Tasks with specify status_codes.

Return

RemoteBatch

Source code in src/bloqade/task/batch.py
@beartype\ndef get_tasks(self, *status_codes: str) -> \"RemoteBatch\":\n    \"\"\"\n    Get Tasks with specify status_codes.\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # offline:\n    st_codes = [QuEraTaskStatusCode(x) for x in status_codes]\n\n    new_task_results = OrderedDict()\n    for task_number, task in self.tasks.items():\n        if task.task_result_ir.task_status in st_codes:\n            new_task_results[task_number] = task\n\n    return RemoteBatch(self.source, new_task_results, name=self.name)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.pull","title":"pull","text":"
pull()\n

Pull results of the tasks in the Batch.

Note

Pulling will pull the results for the tasks. If a given task(s) has not been completed, wait until it finished.

Return

self

Source code in src/bloqade/task/batch.py
def pull(self) -> \"RemoteBatch\":\n    \"\"\"\n    Pull results of the tasks in the Batch.\n\n    Note:\n        Pulling will pull the results for the tasks.\n        If a given task(s) has not been completed, wait\n        until it finished.\n\n    Return:\n        self\n    \"\"\"\n    # online, blocking\n    # pull the results. if its not ready, hanging\n    for task in self.tasks.values():\n        task.pull()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.remove_failed_tasks","title":"remove_failed_tasks","text":"
remove_failed_tasks()\n

Create a RemoteBatch object that contain tasks from current Batch, with failed tasks removed.

failed tasks with following status codes:

  1. Failed
  2. Unaccepted
Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def remove_failed_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain tasks from current Batch,\n    with failed tasks removed.\n\n    failed tasks with following status codes:\n\n    1. Failed\n    2. Unaccepted\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # statuses that are in a state that will\n    # not run going forward because of an error\n    statuses = [\"Failed\", \"Unaccepted\"]\n    return self.remove_tasks(*statuses)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.remove_invalid_tasks","title":"remove_invalid_tasks","text":"
remove_invalid_tasks()\n

Create a RemoteBatch object that contain tasks from current Batch, with all Unaccepted tasks removed.

Return

RemoteBatch

Source code in src/bloqade/task/batch.py
def remove_invalid_tasks(self) -> \"RemoteBatch\":\n    \"\"\"\n    Create a RemoteBatch object that\n    contain tasks from current Batch,\n    with all Unaccepted tasks removed.\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    return self.remove_tasks(\"Unaccepted\")\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.remove_tasks","title":"remove_tasks","text":"
remove_tasks(*status_codes)\n

Remove Tasks with specify status_codes.

Return

RemoteBatch

Source code in src/bloqade/task/batch.py
@beartype\ndef remove_tasks(self, *status_codes: str) -> \"RemoteBatch\":\n    \"\"\"\n    Remove Tasks with specify status_codes.\n\n    Return:\n        RemoteBatch\n\n    \"\"\"\n    # offline:\n\n    st_codes = [QuEraTaskStatusCode(x) for x in status_codes]\n\n    new_results = OrderedDict()\n    for task_number, task in self.tasks.items():\n        if task.task_result_ir.task_status in st_codes:\n            continue\n\n        new_results[task_number] = task\n\n    return RemoteBatch(self.source, new_results, self.name)\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.report","title":"report","text":"
report()\n

Generate analysis report base on currently completed tasks in the RemoteBatch.

Return

Report

Source code in src/bloqade/task/batch.py
def report(self) -> \"Report\":\n    \"\"\"\n    Generate analysis report base on currently\n    completed tasks in the RemoteBatch.\n\n    Return:\n        Report\n\n    \"\"\"\n    ## this potentially can be specialize/disatch\n    ## offline\n    index = []\n    data = []\n    metas = []\n    geos = []\n\n    for task_number, task in self.tasks.items():\n        ## fliter not existing results tasks:\n        if (task.task_id is None) or (not task._result_exists()):\n            continue\n\n        ## filter has result but is not correctly completed.\n        if task.task_result_ir.task_status not in [\n            QuEraTaskStatusCode.Completed,\n            QuEraTaskStatusCode.Partial,\n        ]:\n            continue\n\n        geometry = task.geometry\n        perfect_sorting = \"\".join(map(str, geometry.filling))\n        parallel_decoder = geometry.parallel_decoder\n\n        if parallel_decoder:\n            cluster_indices = parallel_decoder.get_cluster_indices()\n        else:\n            cluster_indices = {(0, 0): list(range(len(perfect_sorting)))}\n\n        shot_iter = filter(\n            lambda shot: shot.shot_status == QuEraShotStatusCode.Completed,\n            task.result().shot_outputs,\n        )\n\n        for shot, (cluster_coordinate, cluster_index) in product(\n            shot_iter, cluster_indices.items()\n        ):\n            pre_sequence = \"\".join(\n                map(\n                    str,\n                    (shot.pre_sequence[index] for index in cluster_index),\n                )\n            )\n\n            post_sequence = np.asarray(\n                [shot.post_sequence[index] for index in cluster_index],\n                dtype=np.int8,\n            )\n\n            pfc_sorting = \"\".join(\n                [perfect_sorting[index] for index in cluster_index]\n            )\n\n            key = (\n                task_number,\n                cluster_coordinate,\n                pfc_sorting,\n                pre_sequence,\n            )\n\n            index.append(key)\n            data.append(post_sequence)\n\n        metas.append(task.metadata)\n        geos.append(task.geometry)\n\n    index = pd.MultiIndex.from_tuples(\n        index, names=[\"task_number\", \"cluster\", \"perfect_sorting\", \"pre_sequence\"]\n    )\n\n    df = pd.DataFrame(data, index=index)\n    df.sort_index(axis=\"index\")\n\n    rept = None\n    if self.name is None:\n        rept = Report(df, metas, geos, \"Remote\")\n    else:\n        rept = Report(df, metas, geos, self.name)\n\n    return rept\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.resubmit","title":"resubmit","text":"
resubmit(shuffle_submit_order=True)\n

Resubmit all the tasks in the RemoteBatch

Return

self

Source code in src/bloqade/task/batch.py
@beartype\ndef resubmit(self, shuffle_submit_order: bool = True) -> \"RemoteBatch\":\n    \"\"\"\n    Resubmit all the tasks in the RemoteBatch\n\n    Return:\n        self\n\n    \"\"\"\n    # online, non-blocking\n    self._submit(shuffle_submit_order, force=True)\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.retrieve","title":"retrieve","text":"
retrieve()\n

Retrieve missing task results.

Note

Retrieve will update the status of tasks, and only pull the results for those tasks that have completed.

Return

self

Source code in src/bloqade/task/batch.py
def retrieve(self) -> \"RemoteBatch\":\n    \"\"\"Retrieve missing task results.\n\n    Note:\n        Retrieve will update the status of tasks,\n        and only pull the results for those tasks\n        that have completed.\n\n    Return:\n        self\n\n    \"\"\"\n    # partially online, sometimes blocking\n    # pull the results for tasks that have\n    # not been pulled already.\n    for task in self.tasks.values():\n        if not task._result_exists():\n            task.pull()\n\n    return self\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.RemoteBatch.tasks_metric","title":"tasks_metric","text":"
tasks_metric()\n

Get current tasks status metric

Return

dataframe with [\"task id\", \"status\", \"shots\"]

Source code in src/bloqade/task/batch.py
def tasks_metric(self) -> pd.DataFrame:\n    \"\"\"\n    Get current tasks status metric\n\n    Return:\n        dataframe with [\"task id\", \"status\", \"shots\"]\n\n    \"\"\"\n    # [TODO] more info on current status\n    # offline, non-blocking\n    tid = []\n    data = []\n    for int, task in self.tasks.items():\n        tid.append(int)\n\n        dat = [None, None, None]\n        dat[0] = task.task_id\n        if task.task_result_ir is not None:\n            dat[1] = task.task_result_ir.task_status.name\n        dat[2] = task.task_ir.nshots\n        data.append(dat)\n\n    return pd.DataFrame(data, index=tid, columns=[\"task ID\", \"status\", \"shots\"])\n
"},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Serializable","title":"Serializable","text":""},{"location":"reference/bloqade/task/batch/#bloqade.task.batch.Serializable.json","title":"json","text":"
json(**options)\n

Serialize the object to JSON string.

Return

JSON string

Source code in src/bloqade/task/batch.py
def json(self, **options) -> str:\n    \"\"\"\n    Serialize the object to JSON string.\n\n    Return:\n        JSON string\n\n    \"\"\"\n    from bloqade import dumps\n\n    return dumps(self, **options)\n
"},{"location":"reference/bloqade/task/bloqade/","title":"Bloqade","text":""},{"location":"reference/bloqade/task/braket/","title":"Braket","text":""},{"location":"reference/bloqade/task/braket_simulator/","title":"Braket simulator","text":""},{"location":"reference/bloqade/task/quera/","title":"Quera","text":""}]} \ No newline at end of file diff --git a/dev/sitemap.xml b/dev/sitemap.xml index 5de0cbc05..a5f1a4bf3 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,637 +2,637 @@ https://bloqade.quera.com/dev/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/blog/2023/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/blog/2023/posts/bloqade-release/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/asking-a-question/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/code-of-conduct/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/community-slack/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/design-philosophy-and-architecture/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/developing-bloqade/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/documentation-issues/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/feature-requests/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/providing-feedback/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/contributing/reporting-a-bug/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/drafts/aws_hybrid_execution/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/home/advanced_usage/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/home/getting_started/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/home/gotchas/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/home/quick_start/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/hardware-capabilities/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/overview/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/standard/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/atom_arrangement/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/constants/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/factory/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/serialize/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/args/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/assign/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/coupling/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/drive/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/field/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/parallelize/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/pragmas/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/route/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/sequence_builder/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/spatial/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/start/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/typing/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/waveform/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/backend/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/backend/bloqade/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/backend/braket/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/backend/quera/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/parse/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/parse/builder/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/parse/stream/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/builder/parse/trait/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/assignment_scan/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/check_slices/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/is_constant/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/is_hyperfine/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/scan_channels/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/common/scan_variables/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/hardware/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/hardware/channels/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/hardware/lattice/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/hardware/piecewise_constant/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/hardware/piecewise_linear/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/python/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/analysis/python/waveform/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/passes/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/passes/emulator/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/passes/hardware/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/passes/hardware/components/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/passes/hardware/define/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/passes/hardware/units/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/common/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/common/add_padding/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/common/assign_to_literal/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/common/assign_variables/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/common/canonicalize/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/common/flatten/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/python/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/compiler/rewrite/python/waveform/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/sparse_operator/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/ir/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/ir/atom_type/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/ir/emulator/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/ir/space/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/emulate/ir/state_vector/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/analog_circuit/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/scalar/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/field/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/pulse/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/sequence/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/waveform/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/traits/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/traits/append/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/traits/canonicalize/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/traits/hash/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/control/traits/slice/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/location/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/location/bravais/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/location/location/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/routine/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/routine/base/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/routine/bloqade/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/routine/braket/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/routine/params/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/ir/routine/quera/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/base/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/braket/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/load_config/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/mock/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/quera/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ir/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ir/braket/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ir/capabilities/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ir/parallel/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ir/task_results/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/submission/ir/task_specification/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/base/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/batch/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/bloqade/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/braket/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/braket_simulator/ - 2024-05-24 + 2024-05-29 daily https://bloqade.quera.com/dev/reference/bloqade/task/quera/ - 2024-05-24 + 2024-05-29 daily \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index 884623e66458a610cd043b2c436def47cb848d6e..0e5d3f20baad9c5c2a83350b01559d2270a3d646 100644 GIT binary patch delta 94 zcmV-k0HObg35W>?ABzYG0B}~32Oa~o1-g+aM*+5xa49wO?)d2;{p1^5p3Of8>(nvd zMbd3*>t0Fo{&nO)d*%94^Hd(n+-CU}lGr?ABzYGfY?uw2Oa}-3$&3aM*+H#a49vn-SN{y`pGxAJez+G)~RE@ zi=^As*1eME{p-kq_R968=BYfCxy|w|B(Zn;`1JVnxp@4gvCgss2&+e?)SO)a0C<5i A>i_@%